/** Clear the GeoIP database and reload it from the file * <b>filename</b>. Return 0 on success, -1 on failure. * * Recognized line formats are: * INTIPLOW,INTIPHIGH,CC * and * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME" * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned * integers, and CC is a country code. * * It also recognizes, and skips over, blank lines and lines that start * with '#' (comments). */ int geoip_load_file(const char *filename, const or_options_t *options) { FILE *f; const char *msg = ""; int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO; crypto_digest_t *geoip_digest_env = NULL; clear_geoip_db(); if (!(f = tor_fopen_cloexec(filename, "r"))) { log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s. %s", filename, msg); return -1; } if (!geoip_countries) init_geoip_countries(); if (geoip_entries) { SMARTLIST_FOREACH(geoip_entries, geoip_entry_t *, e, tor_free(e)); smartlist_free(geoip_entries); } geoip_entries = smartlist_new(); geoip_digest_env = crypto_digest_new(); log_notice(LD_GENERAL, "Parsing GEOIP file %s.", filename); while (!feof(f)) { char buf[512]; if (fgets(buf, (int)sizeof(buf), f) == NULL) break; crypto_digest_add_bytes(geoip_digest_env, buf, strlen(buf)); /* FFFF track full country name. */ geoip_parse_entry(buf); } /*XXXX abort and return -1 if no entries/illformed?*/ fclose(f); smartlist_sort(geoip_entries, geoip_compare_entries_); /* Okay, now we need to maybe change our mind about what is in which * country. */ refresh_all_country_info(); /* Remember file digest so that we can include it in our extra-info * descriptors. */ crypto_digest_get_digest(geoip_digest_env, geoip_digest, DIGEST_LEN); crypto_digest_free(geoip_digest_env); return 0; }
/** Clear the GeoIP database and reload it from the file * <b>filename</b>. Return 0 on success, -1 on failure. * * Recognized line formats are: * INTIPLOW,INTIPHIGH,CC * and * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME" * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned * integers, and CC is a country code. * * It also recognizes, and skips over, blank lines and lines that start * with '#' (comments). */ int geoip_load_file(const char *filename, or_options_t *options) { FILE *f; const char *msg = ""; int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO; clear_geoip_db(); if (!(f = fopen(filename, "r"))) { log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s. %s", filename, msg); return -1; } if (!geoip_countries) { geoip_countries = smartlist_create(); country_idxplus1_by_lc_code = strmap_new(); } if (geoip_entries) { SMARTLIST_FOREACH(geoip_entries, geoip_entry_t *, e, tor_free(e)); smartlist_free(geoip_entries); } geoip_entries = smartlist_create(); log_notice(LD_GENERAL, "Parsing GEOIP file."); while (!feof(f)) { char buf[512]; if (fgets(buf, (int)sizeof(buf), f) == NULL) break; /* FFFF track full country name. */ geoip_parse_entry(buf); } /*XXXX abort and return -1 if no entries/illformed?*/ fclose(f); smartlist_sort(geoip_entries, _geoip_compare_entries); /* Okay, now we need to maybe change our mind about what is in which * country. */ refresh_all_country_info(); return 0; }
/** Run unit tests for smartlist-of-strings functionality. */ static void test_container_smartlist_strings(void) { smartlist_t *sl = smartlist_new(); char *cp=NULL, *cp_alloc=NULL; size_t sz; /* Test split and join */ test_eq(0, smartlist_len(sl)); smartlist_split_string(sl, "abc", ":", 0, 0); test_eq(1, smartlist_len(sl)); test_streq("abc", smartlist_get(sl, 0)); smartlist_split_string(sl, "a::bc::", "::", 0, 0); test_eq(4, smartlist_len(sl)); test_streq("a", smartlist_get(sl, 1)); test_streq("bc", smartlist_get(sl, 2)); test_streq("", smartlist_get(sl, 3)); cp_alloc = smartlist_join_strings(sl, "", 0, NULL); test_streq(cp_alloc, "abcabc"); tor_free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "!", 0, NULL); test_streq(cp_alloc, "abc!a!bc!"); tor_free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); test_streq(cp_alloc, "abcXYaXYbcXY"); tor_free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL); test_streq(cp_alloc, "abcXYaXYbcXYXY"); tor_free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "", 1, NULL); test_streq(cp_alloc, "abcabc"); tor_free(cp_alloc); smartlist_split_string(sl, "/def/ /ghijk", "/", 0, 0); test_eq(8, smartlist_len(sl)); test_streq("", smartlist_get(sl, 4)); test_streq("def", smartlist_get(sl, 5)); test_streq(" ", smartlist_get(sl, 6)); test_streq("ghijk", smartlist_get(sl, 7)); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); smartlist_split_string(sl, "a,bbd,cdef", ",", SPLIT_SKIP_SPACE, 0); test_eq(3, smartlist_len(sl)); test_streq("a", smartlist_get(sl,0)); test_streq("bbd", smartlist_get(sl,1)); test_streq("cdef", smartlist_get(sl,2)); smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", SPLIT_SKIP_SPACE, 0); test_eq(8, smartlist_len(sl)); test_streq("z", smartlist_get(sl,3)); test_streq("zhasd", smartlist_get(sl,4)); test_streq("", smartlist_get(sl,5)); test_streq("bnud", smartlist_get(sl,6)); test_streq("", smartlist_get(sl,7)); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); smartlist_split_string(sl, " ab\tc \td ef ", NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); test_eq(4, smartlist_len(sl)); test_streq("ab", smartlist_get(sl,0)); test_streq("c", smartlist_get(sl,1)); test_streq("d", smartlist_get(sl,2)); test_streq("ef", smartlist_get(sl,3)); smartlist_split_string(sl, "ghi\tj", NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); test_eq(6, smartlist_len(sl)); test_streq("ghi", smartlist_get(sl,4)); test_streq("j", smartlist_get(sl,5)); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); test_streq(cp_alloc, ""); tor_free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL); test_streq(cp_alloc, "XY"); tor_free(cp_alloc); smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); test_eq(3, smartlist_len(sl)); test_streq("z", smartlist_get(sl, 0)); test_streq("zhasd", smartlist_get(sl, 1)); test_streq("bnud", smartlist_get(sl, 2)); smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); test_eq(5, smartlist_len(sl)); test_streq("z", smartlist_get(sl, 3)); test_streq("zhasd <> <> bnud<>", smartlist_get(sl, 4)); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); smartlist_split_string(sl, "abcd\n", "\n", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); test_eq(1, smartlist_len(sl)); test_streq("abcd", smartlist_get(sl, 0)); smartlist_split_string(sl, "efgh", "\n", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); test_eq(2, smartlist_len(sl)); test_streq("efgh", smartlist_get(sl, 1)); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); /* Test swapping, shuffling, and sorting. */ smartlist_split_string(sl, "the,onion,router,by,arma,and,nickm", ",", 0, 0); test_eq(7, smartlist_len(sl)); smartlist_sort(sl, compare_strs_); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); test_streq(cp_alloc,"and,arma,by,nickm,onion,router,the"); tor_free(cp_alloc); smartlist_swap(sl, 1, 5); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); test_streq(cp_alloc,"and,router,by,nickm,onion,arma,the"); tor_free(cp_alloc); smartlist_shuffle(sl); test_eq(7, smartlist_len(sl)); test_assert(smartlist_contains_string(sl, "and")); test_assert(smartlist_contains_string(sl, "router")); test_assert(smartlist_contains_string(sl, "by")); test_assert(smartlist_contains_string(sl, "nickm")); test_assert(smartlist_contains_string(sl, "onion")); test_assert(smartlist_contains_string(sl, "arma")); test_assert(smartlist_contains_string(sl, "the")); /* Test bsearch. */ smartlist_sort(sl, compare_strs_); test_streq("nickm", smartlist_bsearch(sl, "zNicKM", compare_without_first_ch_)); test_streq("and", smartlist_bsearch(sl, " AND", compare_without_first_ch_)); test_eq_ptr(NULL, smartlist_bsearch(sl, " ANz", compare_without_first_ch_)); /* Test bsearch_idx */ { int f; smartlist_t *tmp = NULL; test_eq(0, smartlist_bsearch_idx(sl," aaa",compare_without_first_ch_,&f)); test_eq(f, 0); test_eq(0, smartlist_bsearch_idx(sl," and",compare_without_first_ch_,&f)); test_eq(f, 1); test_eq(1, smartlist_bsearch_idx(sl," arm",compare_without_first_ch_,&f)); test_eq(f, 0); test_eq(1, smartlist_bsearch_idx(sl," arma",compare_without_first_ch_,&f)); test_eq(f, 1); test_eq(2, smartlist_bsearch_idx(sl," armb",compare_without_first_ch_,&f)); test_eq(f, 0); test_eq(7, smartlist_bsearch_idx(sl," zzzz",compare_without_first_ch_,&f)); test_eq(f, 0); /* Test trivial cases for list of length 0 or 1 */ tmp = smartlist_new(); test_eq(0, smartlist_bsearch_idx(tmp, "foo", compare_strs_for_bsearch_, &f)); test_eq(f, 0); smartlist_insert(tmp, 0, (void *)("bar")); test_eq(1, smartlist_bsearch_idx(tmp, "foo", compare_strs_for_bsearch_, &f)); test_eq(f, 0); test_eq(0, smartlist_bsearch_idx(tmp, "aaa", compare_strs_for_bsearch_, &f)); test_eq(f, 0); test_eq(0, smartlist_bsearch_idx(tmp, "bar", compare_strs_for_bsearch_, &f)); test_eq(f, 1); /* ... and one for length 2 */ smartlist_insert(tmp, 1, (void *)("foo")); test_eq(1, smartlist_bsearch_idx(tmp, "foo", compare_strs_for_bsearch_, &f)); test_eq(f, 1); test_eq(2, smartlist_bsearch_idx(tmp, "goo", compare_strs_for_bsearch_, &f)); test_eq(f, 0); smartlist_free(tmp); } /* Test reverse() and pop_last() */ smartlist_reverse(sl); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); test_streq(cp_alloc,"the,router,onion,nickm,by,arma,and"); tor_free(cp_alloc); cp_alloc = smartlist_pop_last(sl); test_streq(cp_alloc, "and"); tor_free(cp_alloc); test_eq(smartlist_len(sl), 6); SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); cp_alloc = smartlist_pop_last(sl); test_eq_ptr(cp_alloc, NULL); /* Test uniq() */ smartlist_split_string(sl, "50,noon,radar,a,man,a,plan,a,canal,panama,radar,noon,50", ",", 0, 0); smartlist_sort(sl, compare_strs_); smartlist_uniq(sl, compare_strs_, tor_free_); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); test_streq(cp_alloc, "50,a,canal,man,noon,panama,plan,radar"); tor_free(cp_alloc); /* Test contains_string, contains_string_case and contains_int_as_string */ test_assert(smartlist_contains_string(sl, "noon")); test_assert(!smartlist_contains_string(sl, "noonoon")); test_assert(smartlist_contains_string_case(sl, "nOOn")); test_assert(!smartlist_contains_string_case(sl, "nooNooN")); test_assert(smartlist_contains_int_as_string(sl, 50)); test_assert(!smartlist_contains_int_as_string(sl, 60)); /* Test smartlist_choose */ { int i; int allsame = 1; int allin = 1; void *first = smartlist_choose(sl); test_assert(smartlist_contains(sl, first)); for (i = 0; i < 100; ++i) { void *second = smartlist_choose(sl); if (second != first) allsame = 0; if (!smartlist_contains(sl, second)) allin = 0; } test_assert(!allsame); test_assert(allin); } SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_clear(sl); /* Test string_remove and remove and join_strings2 */ smartlist_split_string(sl, "Some say the Earth will end in ice and some in fire", " ", 0, 0); cp = smartlist_get(sl, 4); test_streq(cp, "will"); smartlist_add(sl, cp); smartlist_remove(sl, cp); tor_free(cp); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); test_streq(cp_alloc, "Some,say,the,Earth,fire,end,in,ice,and,some,in"); tor_free(cp_alloc); smartlist_string_remove(sl, "in"); cp_alloc = smartlist_join_strings2(sl, "+XX", 1, 0, &sz); test_streq(cp_alloc, "Some+say+the+Earth+fire+end+some+ice+and"); test_eq((int)sz, 40); done: SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); smartlist_free(sl); tor_free(cp_alloc); }
/** Sort the list of DIGEST_LEN-byte digests into ascending order. */ void smartlist_sort_digests(smartlist_t *sl) { smartlist_sort(sl, _compare_digests); }
/** Sort a smartlist <b>sl</b> containing strings into lexically ascending * order. */ void smartlist_sort_strings(smartlist_t *sl) { smartlist_sort(sl, _compare_string_ptrs); }
/** Run unit tests for smartlist-of-strings functionality. */ static void test_container_smartlist_strings(void *unused) { smartlist_t *sl = smartlist_create(); char *cp=NULL, *cp_alloc=NULL; size_t sz; /* Test split and join */ tt_int_op(smartlist_len(sl), ==, 0); smartlist_split_string(sl, "abc", ":", 0, 0); tt_int_op(smartlist_len(sl), ==, 1); tt_str_op(smartlist_get(sl, 0), ==, "abc"); smartlist_split_string(sl, "a::bc::", "::", 0, 0); tt_int_op(smartlist_len(sl), ==, 4); tt_str_op(smartlist_get(sl, 1), ==, "a"); tt_str_op(smartlist_get(sl, 2), ==, "bc"); tt_str_op(smartlist_get(sl, 3), ==, ""); cp_alloc = smartlist_join_strings(sl, "", 0, NULL); tt_str_op(cp_alloc, ==, "abcabc"); free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "!", 0, NULL); tt_str_op(cp_alloc, ==, "abc!a!bc!"); free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); tt_str_op(cp_alloc, ==, "abcXYaXYbcXY"); free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL); tt_str_op(cp_alloc, ==, "abcXYaXYbcXYXY"); free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "", 1, NULL); tt_str_op(cp_alloc, ==, "abcabc"); free(cp_alloc); smartlist_split_string(sl, "/def/ /ghijk", "/", 0, 0); tt_int_op(smartlist_len(sl), ==, 8); tt_str_op(smartlist_get(sl, 4), ==, ""); tt_str_op(smartlist_get(sl, 5), ==, "def"); tt_str_op(smartlist_get(sl, 6), ==, " "); tt_str_op(smartlist_get(sl, 7), ==, "ghijk"); SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); smartlist_split_string(sl, "a,bbd,cdef", ",", SPLIT_SKIP_SPACE, 0); tt_int_op(smartlist_len(sl), ==, 3); tt_str_op(smartlist_get(sl,0), ==, "a"); tt_str_op(smartlist_get(sl,1), ==, "bbd"); tt_str_op(smartlist_get(sl,2), ==, "cdef"); smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", SPLIT_SKIP_SPACE, 0); tt_int_op(smartlist_len(sl), ==, 8); tt_str_op(smartlist_get(sl,3), ==, "z"); tt_str_op(smartlist_get(sl,4), ==, "zhasd"); tt_str_op(smartlist_get(sl,5), ==, ""); tt_str_op(smartlist_get(sl,6), ==, "bnud"); tt_str_op(smartlist_get(sl,7), ==, ""); SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); smartlist_split_string(sl, " ab\tc \td ef ", NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); tt_int_op(smartlist_len(sl), ==, 4); tt_str_op(smartlist_get(sl,0), ==, "ab"); tt_str_op(smartlist_get(sl,1), ==, "c"); tt_str_op(smartlist_get(sl,2), ==, "d"); tt_str_op(smartlist_get(sl,3), ==, "ef"); smartlist_split_string(sl, "ghi\tj", NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); tt_int_op(smartlist_len(sl), ==, 6); tt_str_op(smartlist_get(sl,4), ==, "ghi"); tt_str_op(smartlist_get(sl,5), ==, "j"); SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL); tt_str_op(cp_alloc, ==, ""); free(cp_alloc); cp_alloc = smartlist_join_strings(sl, "XY", 1, NULL); tt_str_op(cp_alloc, ==, "XY"); free(cp_alloc); smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); tt_int_op(smartlist_len(sl), ==, 3); tt_str_op(smartlist_get(sl, 0), ==, "z"); tt_str_op(smartlist_get(sl, 1), ==, "zhasd"); tt_str_op(smartlist_get(sl, 2), ==, "bnud"); smartlist_split_string(sl, " z <> zhasd <> <> bnud<> ", "<>", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); tt_int_op(smartlist_len(sl), ==, 5); tt_str_op(smartlist_get(sl, 3), ==, "z"); tt_str_op(smartlist_get(sl, 4), ==, "zhasd <> <> bnud<>"); SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); smartlist_split_string(sl, "abcd\n", "\n", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); tt_int_op(smartlist_len(sl), ==, 1); tt_str_op(smartlist_get(sl, 0), ==, "abcd"); smartlist_split_string(sl, "efgh", "\n", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); tt_int_op(smartlist_len(sl), ==, 2); tt_str_op(smartlist_get(sl, 1), ==, "efgh"); SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); /* Test swapping, shuffling, and sorting. */ smartlist_split_string(sl, "the,onion,router,by,arma,and,nickm", ",", 0, 0); tt_int_op(smartlist_len(sl), ==, 7); smartlist_sort(sl, _compare_strs); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); tt_str_op(cp_alloc, ==, "and,arma,by,nickm,onion,router,the"); free(cp_alloc); smartlist_swap(sl, 1, 5); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); tt_str_op(cp_alloc, ==, "and,router,by,nickm,onion,arma,the"); free(cp_alloc); smartlist_shuffle(sl); tt_int_op(smartlist_len(sl), ==, 7); tt_assert(smartlist_string_isin(sl, "and")); tt_assert(smartlist_string_isin(sl, "router")); tt_assert(smartlist_string_isin(sl, "by")); tt_assert(smartlist_string_isin(sl, "nickm")); tt_assert(smartlist_string_isin(sl, "onion")); tt_assert(smartlist_string_isin(sl, "arma")); tt_assert(smartlist_string_isin(sl, "the")); /* Test bsearch. */ smartlist_sort(sl, _compare_strs); tt_str_op(smartlist_bsearch(sl, "zNicKM", _compare_without_first_ch), ==, "nickm"); tt_str_op(smartlist_bsearch(sl, " AND", _compare_without_first_ch), ==, "and"); tt_ptr_op(smartlist_bsearch(sl, " ANz", _compare_without_first_ch), ==, NULL); /* Test bsearch_idx */ { int f; tt_int_op(smartlist_bsearch_idx(sl," aaa",_compare_without_first_ch,&f), ==, 0); tt_int_op(f, ==, 0); tt_int_op(smartlist_bsearch_idx(sl," and",_compare_without_first_ch,&f), ==, 0); tt_int_op(f, ==, 1); tt_int_op(smartlist_bsearch_idx(sl," arm",_compare_without_first_ch,&f), ==, 1); tt_int_op(f, ==, 0); tt_int_op(smartlist_bsearch_idx(sl," arma",_compare_without_first_ch,&f), ==, 1); tt_int_op(f, ==, 1); tt_int_op(smartlist_bsearch_idx(sl," armb",_compare_without_first_ch,&f), ==, 2); tt_int_op(f, ==, 0); tt_int_op(smartlist_bsearch_idx(sl," zzzz",_compare_without_first_ch,&f), ==, 7); tt_int_op(f, ==, 0); } /* Test reverse() and pop_last() */ smartlist_reverse(sl); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); tt_str_op(cp_alloc, ==, "the,router,onion,nickm,by,arma,and"); free(cp_alloc); cp_alloc = smartlist_pop_last(sl); tt_str_op(cp_alloc, ==, "and"); free(cp_alloc); tt_int_op(smartlist_len(sl), ==, 6); SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); cp_alloc = smartlist_pop_last(sl); tt_ptr_op(cp_alloc, ==, NULL); /* Test uniq() */ smartlist_split_string(sl, "50,noon,radar,a,man,a,plan,a,canal,panama,radar,noon,50", ",", 0, 0); smartlist_sort(sl, _compare_strs); smartlist_uniq(sl, _compare_strs, free); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); tt_str_op(cp_alloc, ==, "50,a,canal,man,noon,panama,plan,radar"); free(cp_alloc); /* Test string_isin and isin_case and num_isin */ tt_assert(smartlist_string_isin(sl, "noon")); tt_assert(!smartlist_string_isin(sl, "noonoon")); tt_assert(smartlist_string_isin_case(sl, "nOOn")); tt_assert(!smartlist_string_isin_case(sl, "nooNooN")); tt_assert(smartlist_string_num_isin(sl, 50)); tt_assert(!smartlist_string_num_isin(sl, 60)); /* Test smartlist_choose */ { int i; int allsame = 1; int allin = 1; void *first = smartlist_choose(sl); tt_assert(smartlist_isin(sl, first)); for (i = 0; i < 100; ++i) { void *second = smartlist_choose(sl); if (second != first) allsame = 0; if (!smartlist_isin(sl, second)) allin = 0; } tt_assert(!allsame); tt_assert(allin); } SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_clear(sl); /* Test string_remove and remove and join_strings2 */ smartlist_split_string(sl, "Some say the Earth will end in ice and some in fire", " ", 0, 0); cp = smartlist_get(sl, 4); tt_str_op(cp, ==, "will"); smartlist_add(sl, cp); smartlist_remove(sl, cp); free(cp); cp_alloc = smartlist_join_strings(sl, ",", 0, NULL); tt_str_op(cp_alloc, ==, "Some,say,the,Earth,fire,end,in,ice,and,some,in"); free(cp_alloc); smartlist_string_remove(sl, "in"); cp_alloc = smartlist_join_strings2(sl, "+XX", 1, 0, &sz); tt_str_op(cp_alloc, ==, "Some+say+the+Earth+fire+end+some+ice+and"); tt_int_op((int)sz, ==, 40); end: SMARTLIST_FOREACH(sl, char *, cp, free(cp)); smartlist_free(sl); free(cp_alloc); }
/** Clear appropriate GeoIP database, based on <b>family</b>, and * reload it from the file <b>filename</b>. Return 0 on success, -1 on * failure. * * Recognized line formats for IPv4 are: * INTIPLOW,INTIPHIGH,CC * and * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME" * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned * integers, and CC is a country code. * * Recognized line format for IPv6 is: * IPV6LOW,IPV6HIGH,CC * where IPV6LOW and IPV6HIGH are IPv6 addresses and CC is a country code. * * It also recognizes, and skips over, blank lines and lines that start * with '#' (comments). */ int geoip_load_file(sa_family_t family, const char *filename) { FILE *f; const char *msg = ""; const or_options_t *options = get_options(); int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO; crypto_digest_t *geoip_digest_env = NULL; tor_assert(family == AF_INET || family == AF_INET6); if (!(f = tor_fopen_cloexec(filename, "r"))) { log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s. %s", filename, msg); return -1; } if (!geoip_countries) init_geoip_countries(); if (family == AF_INET) { if (geoip_ipv4_entries) { SMARTLIST_FOREACH(geoip_ipv4_entries, geoip_ipv4_entry_t *, e, tor_free(e)); smartlist_free(geoip_ipv4_entries); } geoip_ipv4_entries = smartlist_new(); } else { /* AF_INET6 */ if (geoip_ipv6_entries) { SMARTLIST_FOREACH(geoip_ipv6_entries, geoip_ipv6_entry_t *, e, tor_free(e)); smartlist_free(geoip_ipv6_entries); } geoip_ipv6_entries = smartlist_new(); } geoip_digest_env = crypto_digest_new(); log_notice(LD_GENERAL, "Parsing GEOIP %s file %s.", (family == AF_INET) ? "IPv4" : "IPv6", filename); while (!feof(f)) { char buf[512]; if (fgets(buf, (int)sizeof(buf), f) == NULL) break; crypto_digest_add_bytes(geoip_digest_env, buf, strlen(buf)); /* FFFF track full country name. */ geoip_parse_entry(buf, family); } /*XXXX abort and return -1 if no entries/illformed?*/ fclose(f); /* Sort list and remember file digests so that we can include it in * our extra-info descriptors. */ if (family == AF_INET) { smartlist_sort(geoip_ipv4_entries, geoip_ipv4_compare_entries_); /* Okay, now we need to maybe change our mind about what is in * which country. We do this for IPv4 only since that's what we * store in node->country. */ refresh_all_country_info(); crypto_digest_get_digest(geoip_digest_env, geoip_digest, DIGEST_LEN); } else { /* AF_INET6 */ smartlist_sort(geoip_ipv6_entries, geoip_ipv6_compare_entries_); crypto_digest_get_digest(geoip_digest_env, geoip6_digest, DIGEST_LEN); } crypto_digest_free(geoip_digest_env); return 0; }
/** * Read the measured bandwidth list <b>from_file</b>: * - store all the headers in <b>bw_file_headers</b>, * - apply bandwidth lines to the list of vote_routerstatus_t in * <b>routerstatuses</b>, * - cache bandwidth lines for dirserv_get_bandwidth_for_router(), * - expire old entries in the measured bandwidth cache, and * - store the DIGEST_SHA256 of the contents of the file in <b>digest_out</b>. * * Returns -1 on error, 0 otherwise. * * If the file can't be read, or is empty: * - <b>bw_file_headers</b> is empty, * - <b>routerstatuses</b> is not modified, * - the measured bandwidth cache is not modified, and * - <b>digest_out</b> is the zero-byte digest. * * Otherwise, if there is an error later in the file: * - <b>bw_file_headers</b> contains all the headers up to the error, * - <b>routerstatuses</b> is updated with all the relay lines up to the error, * - the measured bandwidth cache is updated with all the relay lines up to * the error, * - if the timestamp is valid and recent, old entries in the measured * bandwidth cache are expired, and * - <b>digest_out</b> is the digest up to the first read error (if any). * The digest is taken over all the readable file contents, even if the * file is outdated or unparseable. */ int dirserv_read_measured_bandwidths(const char *from_file, smartlist_t *routerstatuses, smartlist_t *bw_file_headers, uint8_t *digest_out) { FILE *fp = tor_fopen_cloexec(from_file, "r"); int applied_lines = 0; time_t file_time, now; int ok; /* This flag will be 1 only when the first successful bw measurement line * has been encountered, so that measured_bw_line_parse don't give warnings * if there are additional header lines, as introduced in Bandwidth List spec * version 1.1.0 */ int line_is_after_headers = 0; int rv = -1; char *line = NULL; size_t n = 0; crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA256); if (fp == NULL) { log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s", from_file); goto err; } if (tor_getline(&line,&n,fp) <= 0) { log_warn(LD_DIRSERV, "Empty bandwidth file"); goto err; } /* If the line could be gotten, add it to the digest */ crypto_digest_add_bytes(digest, (const char *) line, strlen(line)); if (!strlen(line) || line[strlen(line)-1] != '\n') { log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s", escaped(line)); /* Continue adding lines to the digest. */ goto continue_digest; } line[strlen(line)-1] = '\0'; file_time = (time_t)tor_parse_ulong(line, 10, 0, ULONG_MAX, &ok, NULL); if (!ok) { log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s", escaped(line)); goto continue_digest; } now = approx_time(); if ((now - file_time) > MAX_MEASUREMENT_AGE) { log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u", (unsigned)(time(NULL) - file_time)); goto continue_digest; } /* If timestamp was correct and bw_file_headers is not NULL, * add timestamp to bw_file_headers */ if (bw_file_headers) smartlist_add_asprintf(bw_file_headers, "timestamp=%lu", (unsigned long)file_time); if (routerstatuses) smartlist_sort(routerstatuses, compare_vote_routerstatus_entries); while (!feof(fp)) { measured_bw_line_t parsed_line; if (tor_getline(&line, &n, fp) >= 0) { crypto_digest_add_bytes(digest, (const char *) line, strlen(line)); if (measured_bw_line_parse(&parsed_line, line, line_is_after_headers) != -1) { /* This condition will be true when the first complete valid bw line * has been encountered, which means the end of the header lines. */ line_is_after_headers = 1; /* Also cache the line for dirserv_get_bandwidth_for_router() */ dirserv_cache_measured_bw(&parsed_line, file_time); if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0) applied_lines++; /* if the terminator is found, it is the end of header lines, set the * flag but do not store anything */ } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) { line_is_after_headers = 1; /* if the line was not a correct relay line nor the terminator and * the end of the header lines has not been detected yet * and it is key_value and bw_file_headers did not reach the maximum * number of headers, * then assume this line is a header and add it to bw_file_headers */ } else if (bw_file_headers && (line_is_after_headers == 0) && string_is_key_value(LOG_DEBUG, line) && !strchr(line, ' ') && (smartlist_len(bw_file_headers) < MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) { line[strlen(line)-1] = '\0'; smartlist_add_strdup(bw_file_headers, line); }; } } /* Now would be a nice time to clean the cache, too */ dirserv_expire_measured_bw_cache(now); log_info(LD_DIRSERV, "Bandwidth measurement file successfully read. " "Applied %d measurements.", applied_lines); rv = 0; continue_digest: /* Continue parsing lines to return the digest of the Bandwidth File. */ while (!feof(fp)) { if (tor_getline(&line, &n, fp) >= 0) { crypto_digest_add_bytes(digest, (const char *) line, strlen(line)); } } err: if (line) { // we need to raw_free this buffer because we got it from tor_getdelim() raw_free(line); } if (fp) fclose(fp); if (digest_out) crypto_digest_get_digest(digest, (char *) digest_out, DIGEST256_LEN); crypto_digest_free(digest); return rv; }