mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 04:13:28 +01:00
Merge remote-tracking branch 'linus/bug5053-bug5055'
Conflicts: src/or/geoip.c
This commit is contained in:
commit
626a8b60d7
4
changes/5053
Normal file
4
changes/5053
Normal file
@ -0,0 +1,4 @@
|
||||
o Minor features (IPv6):
|
||||
- Bridge clients connecting over IPv6 are now counted in bridge
|
||||
statistics. Implementation by shkoo, addressing ticket 5053.
|
||||
Included in 0.2.3.14-alpha.
|
9
changes/5055
Normal file
9
changes/5055
Normal file
@ -0,0 +1,9 @@
|
||||
o Minor features (IPv6):
|
||||
- Add GeoIP database for IPv6 addresses and use it. The new config
|
||||
option is GeoIPv6File.
|
||||
Bridge clients connecting over IPv6 are now counted in bridge
|
||||
statistics. Bridge statistics files now list "bridge-ip-versions"
|
||||
and extra-info documents list "geoip6-db-digest".
|
||||
The control protocol has been extended to support more IPv6
|
||||
("CLIENTS_SEEN" and "ip-to-country").
|
||||
Initial implementation by shkoo, addressing ticket 5055.
|
@ -1458,7 +1458,10 @@ is non-zero):
|
||||
does on behalf of clients. (Default: 1)
|
||||
|
||||
**GeoIPFile** __filename__::
|
||||
A filename containing GeoIP data, for use with BridgeRecordUsageByCountry.
|
||||
A filename containing IPv4 GeoIP data, for use with by-country statistics.
|
||||
|
||||
**GeoIPv6File** __filename__::
|
||||
A filename containing IPv6 GeoIP data, for use with by-country statistics.
|
||||
|
||||
**CellStatistics** **0**|**1**::
|
||||
When this option is enabled, Tor writes statistics on the mean time that
|
||||
|
11638
src/config/geoip6
Normal file
11638
src/config/geoip6
Normal file
File diff suppressed because it is too large
Load Diff
@ -244,9 +244,12 @@ static config_var_t option_vars_[] = {
|
||||
V(FetchV2Networkstatus, BOOL, "0"),
|
||||
#ifdef _WIN32
|
||||
V(GeoIPFile, FILENAME, "<default>"),
|
||||
V(GeoIPv6File, FILENAME, "<default>"),
|
||||
#else
|
||||
V(GeoIPFile, FILENAME,
|
||||
SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"),
|
||||
V(GeoIPv6File, FILENAME,
|
||||
SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip6"),
|
||||
#endif
|
||||
OBSOLETE("GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays"),
|
||||
OBSOLETE("Group"),
|
||||
@ -480,6 +483,8 @@ static void init_libevent(const or_options_t *options);
|
||||
static int opt_streq(const char *s1, const char *s2);
|
||||
static int parse_outbound_addresses(or_options_t *options, int validate_only,
|
||||
char **msg);
|
||||
static void config_maybe_load_geoip_files_(const or_options_t *options,
|
||||
const or_options_t *old_options);
|
||||
|
||||
/** Magic value for or_options_t. */
|
||||
#define OR_OPTIONS_MAGIC 9090909
|
||||
@ -1510,24 +1515,7 @@ options_act(const or_options_t *old_options)
|
||||
connection_or_update_token_buckets(get_connection_array(), options);
|
||||
}
|
||||
|
||||
/* Maybe load geoip file */
|
||||
if (options->GeoIPFile &&
|
||||
((!old_options || !opt_streq(old_options->GeoIPFile, options->GeoIPFile))
|
||||
|| !geoip_is_loaded())) {
|
||||
/* XXXX Don't use this "<default>" junk; make our filename options
|
||||
* understand prefixes somehow. -NM */
|
||||
/* XXXX024 Reload GeoIPFile on SIGHUP. -NM */
|
||||
char *actual_fname = tor_strdup(options->GeoIPFile);
|
||||
#ifdef _WIN32
|
||||
if (!strcmp(actual_fname, "<default>")) {
|
||||
const char *conf_root = get_windows_conf_root();
|
||||
tor_free(actual_fname);
|
||||
tor_asprintf(&actual_fname, "%s\\geoip", conf_root);
|
||||
}
|
||||
#endif
|
||||
geoip_load_file(actual_fname, options);
|
||||
tor_free(actual_fname);
|
||||
}
|
||||
config_maybe_load_geoip_files_(options, old_options);
|
||||
|
||||
if (options->CellStatistics || options->DirReqStatistics ||
|
||||
options->EntryStatistics || options->ExitPortStatistics ||
|
||||
@ -1551,7 +1539,7 @@ options_act(const or_options_t *old_options)
|
||||
}
|
||||
if ((!old_options || !old_options->DirReqStatistics) &&
|
||||
options->DirReqStatistics) {
|
||||
if (geoip_is_loaded()) {
|
||||
if (geoip_is_loaded(AF_INET)) {
|
||||
geoip_dirreq_stats_init(now);
|
||||
print_notice = 1;
|
||||
} else {
|
||||
@ -1566,7 +1554,7 @@ options_act(const or_options_t *old_options)
|
||||
}
|
||||
if ((!old_options || !old_options->EntryStatistics) &&
|
||||
options->EntryStatistics && !should_record_bridge_info(options)) {
|
||||
if (geoip_is_loaded()) {
|
||||
if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) {
|
||||
geoip_entry_stats_init(now);
|
||||
print_notice = 1;
|
||||
} else {
|
||||
@ -5577,3 +5565,44 @@ parse_outbound_addresses(or_options_t *options, int validate_only, char **msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Load one of the geoip files, <a>family</a> determining which
|
||||
* one. Note that <a>fname</a> will be freed by this
|
||||
* function. <a>default_fname</a> is used if on Windows and
|
||||
* <a>fname</a> equals "<default>". */
|
||||
static void
|
||||
config_load_geoip_file_(sa_family_t family,
|
||||
char *fname, /* will be freed */
|
||||
const char *default_fname)
|
||||
{
|
||||
(void)default_fname;
|
||||
/* XXXX Don't use this "<default>" junk; make our filename options
|
||||
* understand prefixes somehow. -NM */
|
||||
/* XXXX024 Reload GeoIPFile on SIGHUP. -NM */
|
||||
#ifdef _WIN32
|
||||
if (!strcmp(fname, "<default>")) {
|
||||
const char *conf_root = get_windows_conf_root();
|
||||
tor_free(fname);
|
||||
tor_asprintf(&fname, "%s\\%s", conf_root, default_fname);
|
||||
}
|
||||
#endif
|
||||
geoip_load_file(family, fname);
|
||||
tor_free(fname);
|
||||
}
|
||||
|
||||
/** Load geoip files for IPv4 and IPv6 if <a>options</a> and
|
||||
* <a>old_options</a> indicate we should. */
|
||||
static void
|
||||
config_maybe_load_geoip_files_(const or_options_t *options,
|
||||
const or_options_t *old_options)
|
||||
{
|
||||
if (options->GeoIPFile &&
|
||||
((!old_options || !opt_streq(old_options->GeoIPFile,
|
||||
options->GeoIPFile))
|
||||
|| !geoip_is_loaded(AF_INET)))
|
||||
config_load_geoip_file_(AF_INET, tor_strdup(options->GeoIPFile), "geoip");
|
||||
if (options->GeoIPv6File &&
|
||||
((!old_options || !opt_streq(old_options->GeoIPv6File,
|
||||
options->GeoIPv6File))
|
||||
|| !geoip_is_loaded(AF_INET6)))
|
||||
config_load_geoip_file_(AF_INET6, tor_strdup(options->GeoIPv6File), "geoip6");
|
||||
}
|
||||
|
465
src/or/geoip.c
465
src/or/geoip.c
@ -21,12 +21,19 @@
|
||||
static void clear_geoip_db(void);
|
||||
static void init_geoip_countries(void);
|
||||
|
||||
/** An entry from the GeoIP file: maps an IP range to a country. */
|
||||
typedef struct geoip_entry_t {
|
||||
/** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */
|
||||
typedef struct geoip_ipv4_entry_t {
|
||||
uint32_t ip_low; /**< The lowest IP in the range, in host order */
|
||||
uint32_t ip_high; /**< The highest IP in the range, in host order */
|
||||
intptr_t country; /**< An index into geoip_countries */
|
||||
} geoip_entry_t;
|
||||
} geoip_ipv4_entry_t;
|
||||
|
||||
/** An entry from the GeoIP IPv6 file: maps an IPv6 range to a country. */
|
||||
typedef struct geoip_ipv6_entry_t {
|
||||
struct in6_addr ip_low; /**< The lowest IP in the range, in host order */
|
||||
struct in6_addr ip_high; /**< The highest IP in the range, in host order */
|
||||
intptr_t country; /**< An index into geoip_countries */
|
||||
} geoip_ipv6_entry_t;
|
||||
|
||||
/** A per-country record for GeoIP request history. */
|
||||
typedef struct geoip_country_t {
|
||||
@ -41,15 +48,17 @@ static smartlist_t *geoip_countries = NULL;
|
||||
* The index is encoded in the pointer, and 1 is added so that NULL can mean
|
||||
* not found. */
|
||||
static strmap_t *country_idxplus1_by_lc_code = NULL;
|
||||
/** A list of all known geoip_entry_t, sorted by ip_low. */
|
||||
static smartlist_t *geoip_entries = NULL;
|
||||
/** Lists of all known geoip_ipv4_entry_t and geoip_ipv6_entry_t, sorted
|
||||
* by their respective ip_low. */
|
||||
static smartlist_t *geoip_ipv4_entries = NULL, *geoip_ipv6_entries = NULL;
|
||||
|
||||
/** SHA1 digest of the GeoIP file to include in extra-info descriptors. */
|
||||
/** SHA1 digest of the GeoIP files to include in extra-info descriptors. */
|
||||
static char geoip_digest[DIGEST_LEN];
|
||||
static char geoip6_digest[DIGEST_LEN];
|
||||
|
||||
/** Return the index of the <b>country</b>'s entry in the GeoIP DB
|
||||
* if it is a valid 2-letter country code, otherwise return -1.
|
||||
*/
|
||||
/** Return the index of the <b>country</b>'s entry in the GeoIP
|
||||
* country list if it is a valid 2-letter country code, otherwise
|
||||
* return -1. */
|
||||
country_t
|
||||
geoip_get_country(const char *country)
|
||||
{
|
||||
@ -64,17 +73,18 @@ geoip_get_country(const char *country)
|
||||
return (country_t)idx;
|
||||
}
|
||||
|
||||
/** Add an entry to the GeoIP table, mapping all IPs between <b>low</b> and
|
||||
* <b>high</b>, inclusive, to the 2-letter country code <b>country</b>.
|
||||
*/
|
||||
/** Add an entry to a GeoIP table, mapping all IP addresses between <b>low</b> and
|
||||
* <b>high</b>, inclusive, to the 2-letter country code <b>country</b>. */
|
||||
static void
|
||||
geoip_add_entry(uint32_t low, uint32_t high, const char *country)
|
||||
geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high,
|
||||
const char *country)
|
||||
{
|
||||
intptr_t idx;
|
||||
geoip_entry_t *ent;
|
||||
void *idxplus1_;
|
||||
|
||||
if (high < low)
|
||||
if (tor_addr_family(low) != tor_addr_family(high))
|
||||
return;
|
||||
if (tor_addr_compare(high, low, CMP_EXACT) < 0)
|
||||
return;
|
||||
|
||||
idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country);
|
||||
@ -93,48 +103,97 @@ geoip_add_entry(uint32_t low, uint32_t high, const char *country)
|
||||
geoip_country_t *c = smartlist_get(geoip_countries, idx);
|
||||
tor_assert(!strcasecmp(c->countrycode, country));
|
||||
}
|
||||
ent = tor_malloc_zero(sizeof(geoip_entry_t));
|
||||
ent->ip_low = low;
|
||||
ent->ip_high = high;
|
||||
ent->country = idx;
|
||||
smartlist_add(geoip_entries, ent);
|
||||
|
||||
if (tor_addr_family(low) == AF_INET) {
|
||||
geoip_ipv4_entry_t *ent = tor_malloc_zero(sizeof(geoip_ipv4_entry_t));
|
||||
ent->ip_low = tor_addr_to_ipv4h(low);
|
||||
ent->ip_high = tor_addr_to_ipv4h(high);
|
||||
ent->country = idx;
|
||||
smartlist_add(geoip_ipv4_entries, ent);
|
||||
} else if (tor_addr_family(low) == AF_INET6) {
|
||||
geoip_ipv6_entry_t *ent = tor_malloc_zero(sizeof(geoip_ipv6_entry_t));
|
||||
ent->ip_low = *tor_addr_to_in6(low);
|
||||
ent->ip_high = *tor_addr_to_in6(high);
|
||||
ent->country = idx;
|
||||
smartlist_add(geoip_ipv6_entries, ent);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add an entry to the GeoIP table, parsing it from <b>line</b>. The
|
||||
* format is as for geoip_load_file(). */
|
||||
/** Add an entry to the GeoIP table indicated by <b>family</b>,
|
||||
* parsing it from <b>line</b>. The format is as for geoip_load_file(). */
|
||||
/*private*/ int
|
||||
geoip_parse_entry(const char *line)
|
||||
geoip_parse_entry(const char *line, sa_family_t family)
|
||||
{
|
||||
unsigned int low, high;
|
||||
char b[3];
|
||||
tor_addr_t low_addr, high_addr;
|
||||
char c[3];
|
||||
char *country = NULL;
|
||||
|
||||
if (!geoip_countries)
|
||||
init_geoip_countries();
|
||||
if (!geoip_entries)
|
||||
geoip_entries = smartlist_new();
|
||||
if (family == AF_INET) {
|
||||
if (!geoip_ipv4_entries)
|
||||
geoip_ipv4_entries = smartlist_new();
|
||||
} else if (family == AF_INET6) {
|
||||
if (!geoip_ipv6_entries)
|
||||
geoip_ipv6_entries = smartlist_new();
|
||||
} else {
|
||||
log_warn(LD_GENERAL, "Unsupported family: %d", family);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (TOR_ISSPACE(*line))
|
||||
++line;
|
||||
if (*line == '#')
|
||||
return 0;
|
||||
if (tor_sscanf(line,"%u,%u,%2s", &low, &high, b) == 3) {
|
||||
geoip_add_entry(low, high, b);
|
||||
return 0;
|
||||
} else if (tor_sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, b) == 3) {
|
||||
geoip_add_entry(low, high, b);
|
||||
return 0;
|
||||
} else {
|
||||
log_warn(LD_GENERAL, "Unable to parse line from GEOIP file: %s",
|
||||
escaped(line));
|
||||
return -1;
|
||||
|
||||
if (family == AF_INET) {
|
||||
unsigned int low, high;
|
||||
if (tor_sscanf(line,"%u,%u,%2s", &low, &high, c) == 3 ||
|
||||
tor_sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, c) == 3) {
|
||||
tor_addr_from_ipv4h(&low_addr, low);
|
||||
tor_addr_from_ipv4h(&high_addr, high);
|
||||
} else
|
||||
goto fail;
|
||||
country = c;
|
||||
} else { /* AF_INET6 */
|
||||
char buf[512];
|
||||
char *low_str, *high_str;
|
||||
struct in6_addr low, high;
|
||||
char *strtok_state;
|
||||
strlcpy(buf, line, sizeof(buf));
|
||||
low_str = tor_strtok_r(buf, ",", &strtok_state);
|
||||
if (!low_str)
|
||||
goto fail;
|
||||
high_str = tor_strtok_r(NULL, ",", &strtok_state);
|
||||
if (!high_str)
|
||||
goto fail;
|
||||
country = tor_strtok_r(NULL, "\n", &strtok_state);
|
||||
if (!country)
|
||||
goto fail;
|
||||
if (strlen(country) != 2)
|
||||
goto fail;
|
||||
if (tor_inet_pton(AF_INET6, low_str, &low) <= 0)
|
||||
goto fail;
|
||||
tor_addr_from_in6(&low_addr, &low);
|
||||
if (tor_inet_pton(AF_INET6, high_str, &high) <= 0)
|
||||
goto fail;
|
||||
tor_addr_from_in6(&high_addr, &high);
|
||||
}
|
||||
geoip_add_entry(&low_addr, &high_addr, country);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
log_warn(LD_GENERAL, "Unable to parse line from GEOIP %s file: %s",
|
||||
family == AF_INET ? "IPv4" : "IPv6", escaped(line));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Sorting helper: return -1, 1, or 0 based on comparison of two
|
||||
* geoip_entry_t */
|
||||
* geoip_ipv4_entry_t */
|
||||
static int
|
||||
geoip_compare_entries_(const void **_a, const void **_b)
|
||||
geoip_ipv4_compare_entries_(const void **_a, const void **_b)
|
||||
{
|
||||
const geoip_entry_t *a = *_a, *b = *_b;
|
||||
const geoip_ipv4_entry_t *a = *_a, *b = *_b;
|
||||
if (a->ip_low < b->ip_low)
|
||||
return -1;
|
||||
else if (a->ip_low > b->ip_low)
|
||||
@ -144,13 +203,13 @@ geoip_compare_entries_(const void **_a, const void **_b)
|
||||
}
|
||||
|
||||
/** bsearch helper: return -1, 1, or 0 based on comparison of an IP (a pointer
|
||||
* to a uint32_t in host order) to a geoip_entry_t */
|
||||
* to a uint32_t in host order) to a geoip_ipv4_entry_t */
|
||||
static int
|
||||
geoip_compare_key_to_entry_(const void *_key, const void **_member)
|
||||
geoip_ipv4_compare_key_to_entry_(const void *_key, const void **_member)
|
||||
{
|
||||
/* No alignment issue here, since _key really is a pointer to uint32_t */
|
||||
const uint32_t addr = *(uint32_t *)_key;
|
||||
const geoip_entry_t *entry = *_member;
|
||||
const geoip_ipv4_entry_t *entry = *_member;
|
||||
if (addr < entry->ip_low)
|
||||
return -1;
|
||||
else if (addr > entry->ip_high)
|
||||
@ -159,6 +218,33 @@ geoip_compare_key_to_entry_(const void *_key, const void **_member)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Sorting helper: return -1, 1, or 0 based on comparison of two
|
||||
* geoip_ipv6_entry_t */
|
||||
static int
|
||||
geoip_ipv6_compare_entries_(const void **_a, const void **_b)
|
||||
{
|
||||
const geoip_ipv6_entry_t *a = *_a, *b = *_b;
|
||||
return memcmp(a->ip_low.s6_addr, b->ip_low.s6_addr, sizeof(struct in6_addr));
|
||||
}
|
||||
|
||||
/** bsearch helper: return -1, 1, or 0 based on comparison of an IPv6
|
||||
* (a pointer to a in6_addr) to a geoip_ipv6_entry_t */
|
||||
static int
|
||||
geoip_ipv6_compare_key_to_entry_(const void *_key, const void **_member)
|
||||
{
|
||||
const struct in6_addr *addr = (struct in6_addr *)_key;
|
||||
const geoip_ipv6_entry_t *entry = *_member;
|
||||
|
||||
if (memcmp(addr->s6_addr, entry->ip_low.s6_addr,
|
||||
sizeof(struct in6_addr)) < 0)
|
||||
return -1;
|
||||
else if (memcmp(addr->s6_addr, entry->ip_high.s6_addr,
|
||||
sizeof(struct in6_addr)) > 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return 1 if we should collect geoip stats on bridge users, and
|
||||
* include them in our extrainfo descriptor. Else return 0. */
|
||||
int
|
||||
@ -185,27 +271,35 @@ init_geoip_countries(void)
|
||||
strmap_set_lc(country_idxplus1_by_lc_code, "??", (void*)(1));
|
||||
}
|
||||
|
||||
/** Clear the GeoIP database and reload it from the file
|
||||
* <b>filename</b>. Return 0 on success, -1 on failure.
|
||||
/** 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 are:
|
||||
* 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(const char *filename, const or_options_t *options)
|
||||
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;
|
||||
clear_geoip_db();
|
||||
|
||||
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);
|
||||
@ -213,33 +307,51 @@ geoip_load_file(const char *filename, const or_options_t *options)
|
||||
}
|
||||
if (!geoip_countries)
|
||||
init_geoip_countries();
|
||||
if (geoip_entries) {
|
||||
SMARTLIST_FOREACH(geoip_entries, geoip_entry_t *, e, tor_free(e));
|
||||
smartlist_free(geoip_entries);
|
||||
|
||||
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_entries = smartlist_new();
|
||||
geoip_digest_env = crypto_digest_new();
|
||||
log_notice(LD_GENERAL, "Parsing GEOIP file %s.", filename);
|
||||
|
||||
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);
|
||||
geoip_parse_entry(buf, family);
|
||||
}
|
||||
/*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);
|
||||
/* 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;
|
||||
@ -252,12 +364,30 @@ geoip_load_file(const char *filename, const or_options_t *options)
|
||||
* geoip_get_country_name().
|
||||
*/
|
||||
int
|
||||
geoip_get_country_by_ip(uint32_t ipaddr)
|
||||
geoip_get_country_by_ipv4(uint32_t ipaddr)
|
||||
{
|
||||
geoip_entry_t *ent;
|
||||
if (!geoip_entries)
|
||||
geoip_ipv4_entry_t *ent;
|
||||
if (!geoip_ipv4_entries)
|
||||
return -1;
|
||||
ent = smartlist_bsearch(geoip_entries, &ipaddr, geoip_compare_key_to_entry_);
|
||||
ent = smartlist_bsearch(geoip_ipv4_entries, &ipaddr,
|
||||
geoip_ipv4_compare_key_to_entry_);
|
||||
return ent ? (int)ent->country : 0;
|
||||
}
|
||||
|
||||
/** Given an IPv6 address, return a number representing the country to
|
||||
* which that address belongs, -1 for "No geoip information available", or
|
||||
* 0 for the 'unknown country'. The return value will always be less than
|
||||
* geoip_get_n_countries(). To decode it, call geoip_get_country_name().
|
||||
*/
|
||||
int
|
||||
geoip_get_country_by_ipv6(const struct in6_addr *addr)
|
||||
{
|
||||
geoip_ipv6_entry_t *ent;
|
||||
|
||||
if (!geoip_ipv6_entries)
|
||||
return -1;
|
||||
ent = smartlist_bsearch(geoip_ipv6_entries, addr,
|
||||
geoip_ipv6_compare_key_to_entry_);
|
||||
return ent ? (int)ent->country : 0;
|
||||
}
|
||||
|
||||
@ -269,14 +399,16 @@ geoip_get_country_by_ip(uint32_t ipaddr)
|
||||
int
|
||||
geoip_get_country_by_addr(const tor_addr_t *addr)
|
||||
{
|
||||
if (tor_addr_family(addr) != AF_INET) {
|
||||
/*XXXX IP6 support ipv6 geoip.*/
|
||||
if (tor_addr_family(addr) == AF_INET) {
|
||||
return geoip_get_country_by_ipv4(tor_addr_to_ipv4h(addr));
|
||||
} else if (tor_addr_family(addr) == AF_INET6) {
|
||||
return geoip_get_country_by_ipv6(tor_addr_to_in6(addr));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return geoip_get_country_by_ip(tor_addr_to_ipv4h(addr));
|
||||
}
|
||||
|
||||
/** Return the number of countries recognized by the GeoIP database. */
|
||||
/** Return the number of countries recognized by the GeoIP country list. */
|
||||
int
|
||||
geoip_get_n_countries(void)
|
||||
{
|
||||
@ -299,18 +431,28 @@ geoip_get_country_name(country_t num)
|
||||
|
||||
/** Return true iff we have loaded a GeoIP database.*/
|
||||
int
|
||||
geoip_is_loaded(void)
|
||||
geoip_is_loaded(sa_family_t family)
|
||||
{
|
||||
return geoip_countries != NULL && geoip_entries != NULL;
|
||||
tor_assert(family == AF_INET || family == AF_INET6);
|
||||
if (geoip_countries == NULL)
|
||||
return 0;
|
||||
if (family == AF_INET)
|
||||
return geoip_ipv4_entries != NULL;
|
||||
else /* AF_INET6 */
|
||||
return geoip_ipv6_entries != NULL;
|
||||
}
|
||||
|
||||
/** Return the hex-encoded SHA1 digest of the loaded GeoIP file. The
|
||||
* result does not need to be deallocated, but will be overwritten by the
|
||||
* next call of hex_str(). */
|
||||
const char *
|
||||
geoip_db_digest(void)
|
||||
geoip_db_digest(sa_family_t family)
|
||||
{
|
||||
return hex_str(geoip_digest, DIGEST_LEN);
|
||||
tor_assert(family == AF_INET || family == AF_INET6);
|
||||
if (family == AF_INET)
|
||||
return hex_str(geoip_digest, DIGEST_LEN);
|
||||
else /* AF_INET6 */
|
||||
return hex_str(geoip6_digest, DIGEST_LEN);
|
||||
}
|
||||
|
||||
/** Entry in a map from IP address to the last time we've seen an incoming
|
||||
@ -559,7 +701,7 @@ typedef struct c_hist_t {
|
||||
} c_hist_t;
|
||||
|
||||
/** Sorting helper: return -1, 1, or 0 based on comparison of two
|
||||
* geoip_entry_t. Sort in descending order of total, and then by country
|
||||
* geoip_ipv4_entry_t. Sort in descending order of total, and then by country
|
||||
* code. */
|
||||
static int
|
||||
c_hist_compare_(const void **_a, const void **_b)
|
||||
@ -813,27 +955,35 @@ geoip_get_dirreq_history(geoip_client_action_t action,
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Return a newly allocated comma-separated string containing entries for
|
||||
* all the countries from which we've seen enough clients connect as a
|
||||
* bridge, directory server, or entry guard. The entry format is cc=num
|
||||
* where num is the number of IPs we've seen connecting from that country,
|
||||
* and cc is a lowercased country code. Returns NULL if we don't want
|
||||
* to export geoip data yet. */
|
||||
char *
|
||||
geoip_get_client_history(geoip_client_action_t action)
|
||||
/** Store a newly allocated comma-separated string in
|
||||
* *<a>country_str</a> containing entries for all the countries from
|
||||
* which we've seen enough clients connect as a bridge, directory
|
||||
* server, or entry guard. The entry format is cc=num where num is the
|
||||
* number of IPs we've seen connecting from that country, and cc is a
|
||||
* lowercased country code. *<a>country_str</a> is set to NULL if
|
||||
* we're not ready to export per country data yet.
|
||||
*
|
||||
* Store a newly allocated comma-separated string in <a>ipver_str</a>
|
||||
* containing entries for clients connecting over IPv4 and IPv6. The
|
||||
* format is family=num where num is the nubmer of IPs we've seen
|
||||
* connecting over that protocol family, and family is 'v4' or 'v6'.
|
||||
*
|
||||
* Return 0 on success and -1 if we're missing geoip data. */
|
||||
int
|
||||
geoip_get_client_history(geoip_client_action_t action,
|
||||
char **country_str, char **ipver_str)
|
||||
{
|
||||
char *result = NULL;
|
||||
unsigned granularity = IP_GRANULARITY;
|
||||
smartlist_t *chunks = NULL;
|
||||
smartlist_t *entries = NULL;
|
||||
int n_countries = geoip_get_n_countries();
|
||||
int i;
|
||||
clientmap_entry_t **ent;
|
||||
unsigned *counts = NULL;
|
||||
unsigned total = 0;
|
||||
unsigned ipv4_count = 0, ipv6_count = 0;
|
||||
|
||||
if (!geoip_is_loaded())
|
||||
return NULL;
|
||||
if (!geoip_is_loaded(AF_INET) && !geoip_is_loaded(AF_INET6))
|
||||
return -1;
|
||||
|
||||
counts = tor_malloc_zero(sizeof(unsigned)*n_countries);
|
||||
HT_FOREACH(ent, clientmap, &client_history) {
|
||||
@ -846,10 +996,34 @@ geoip_get_client_history(geoip_client_action_t action)
|
||||
tor_assert(0 <= country && country < n_countries);
|
||||
++counts[country];
|
||||
++total;
|
||||
switch (tor_addr_family(&(*ent)->addr)) {
|
||||
case AF_INET:
|
||||
ipv4_count++;
|
||||
break;
|
||||
case AF_INET6:
|
||||
ipv6_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Don't record anything if we haven't seen enough IPs. */
|
||||
if (total < MIN_IPS_TO_NOTE_ANYTHING)
|
||||
goto done;
|
||||
if (ipver_str) {
|
||||
smartlist_t *chunks = smartlist_new();
|
||||
smartlist_add_asprintf(chunks, "v4=%u",
|
||||
round_to_next_multiple_of(ipv4_count, granularity));
|
||||
smartlist_add_asprintf(chunks, "v6=%u",
|
||||
round_to_next_multiple_of(ipv6_count, granularity));
|
||||
*ipver_str = smartlist_join_strings(chunks, ",", 0, NULL);
|
||||
SMARTLIST_FOREACH(chunks, char *, c, tor_free(c));
|
||||
smartlist_free(chunks);
|
||||
}
|
||||
|
||||
/* Don't record per country data if we haven't seen enough IPs. */
|
||||
if (total < MIN_IPS_TO_NOTE_ANYTHING) {
|
||||
tor_free(counts);
|
||||
if (country_str)
|
||||
*country_str = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make a list of c_hist_t */
|
||||
entries = smartlist_new();
|
||||
for (i = 0; i < n_countries; ++i) {
|
||||
@ -870,23 +1044,21 @@ geoip_get_client_history(geoip_client_action_t action)
|
||||
* the sort order could leak info. */
|
||||
smartlist_sort(entries, c_hist_compare_);
|
||||
|
||||
/* Build the result. */
|
||||
chunks = smartlist_new();
|
||||
SMARTLIST_FOREACH(entries, c_hist_t *, ch, {
|
||||
smartlist_add_asprintf(chunks, "%s=%u", ch->country, ch->total);
|
||||
});
|
||||
result = smartlist_join_strings(chunks, ",", 0, NULL);
|
||||
done:
|
||||
tor_free(counts);
|
||||
if (chunks) {
|
||||
if (country_str) {
|
||||
smartlist_t *chunks = smartlist_new();
|
||||
SMARTLIST_FOREACH(entries, c_hist_t *, ch, {
|
||||
smartlist_add_asprintf(chunks, "%s=%u", ch->country, ch->total);
|
||||
});
|
||||
*country_str = smartlist_join_strings(chunks, ",", 0, NULL);
|
||||
SMARTLIST_FOREACH(chunks, char *, c, tor_free(c));
|
||||
smartlist_free(chunks);
|
||||
}
|
||||
if (entries) {
|
||||
SMARTLIST_FOREACH(entries, c_hist_t *, c, tor_free(c));
|
||||
smartlist_free(entries);
|
||||
}
|
||||
return result;
|
||||
|
||||
SMARTLIST_FOREACH(entries, c_hist_t *, c, tor_free(c));
|
||||
smartlist_free(entries);
|
||||
tor_free(counts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return a newly allocated string holding the per-country request history
|
||||
@ -1009,8 +1181,9 @@ geoip_format_dirreq_stats(time_t now)
|
||||
tor_assert(now >= start_of_dirreq_stats_interval);
|
||||
|
||||
format_iso_time(t, now);
|
||||
v2_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
|
||||
v3_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS);
|
||||
geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2, &v2_ips_string,
|
||||
NULL);
|
||||
geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS, &v3_ips_string, NULL);
|
||||
v2_reqs_string = geoip_get_request_history(
|
||||
GEOIP_CLIENT_NETWORKSTATUS_V2);
|
||||
v3_reqs_string = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
|
||||
@ -1216,7 +1389,7 @@ static char *bridge_stats_extrainfo = NULL;
|
||||
char *
|
||||
geoip_format_bridge_stats(time_t now)
|
||||
{
|
||||
char *out = NULL, *data = NULL;
|
||||
char *out = NULL, *country_data = NULL, *ipver_data = NULL;
|
||||
long duration = now - start_of_bridge_stats_interval;
|
||||
char written[ISO_TIME_LEN+1];
|
||||
|
||||
@ -1226,14 +1399,17 @@ geoip_format_bridge_stats(time_t now)
|
||||
return NULL; /* Not initialized. */
|
||||
|
||||
format_iso_time(written, now);
|
||||
data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
|
||||
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data);
|
||||
|
||||
tor_asprintf(&out,
|
||||
"bridge-stats-end %s (%ld s)\n"
|
||||
"bridge-ips %s\n",
|
||||
"bridge-ips %s\n"
|
||||
"bridge-ip-versions %s\n",
|
||||
written, duration,
|
||||
data ? data : "");
|
||||
tor_free(data);
|
||||
country_data ? country_data : "",
|
||||
ipver_data ? ipver_data : "");
|
||||
tor_free(country_data);
|
||||
tor_free(ipver_data);
|
||||
|
||||
return out;
|
||||
}
|
||||
@ -1244,17 +1420,20 @@ geoip_format_bridge_stats(time_t now)
|
||||
static char *
|
||||
format_bridge_stats_controller(time_t now)
|
||||
{
|
||||
char *out = NULL, *data = NULL;
|
||||
char *out = NULL, *country_data = NULL, *ipver_data = NULL;
|
||||
char started[ISO_TIME_LEN+1];
|
||||
(void) now;
|
||||
|
||||
format_iso_time(started, start_of_bridge_stats_interval);
|
||||
data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
|
||||
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data);
|
||||
|
||||
tor_asprintf(&out,
|
||||
"TimeStarted=\"%s\" CountrySummary=%s",
|
||||
started, data ? data : "");
|
||||
tor_free(data);
|
||||
"TimeStarted=\"%s\" CountrySummary=%s IPVersions=%s",
|
||||
started,
|
||||
country_data ? country_data : "",
|
||||
ipver_data ? ipver_data : "");
|
||||
tor_free(country_data);
|
||||
tor_free(ipver_data);
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -1381,11 +1560,13 @@ geoip_format_entry_stats(time_t now)
|
||||
|
||||
tor_assert(now >= start_of_entry_stats_interval);
|
||||
|
||||
data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
|
||||
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &data, NULL);
|
||||
format_iso_time(t, now);
|
||||
tor_asprintf(&result, "entry-stats-end %s (%u s)\nentry-ips %s\n",
|
||||
t, (unsigned) (now - start_of_entry_stats_interval),
|
||||
data ? data : "");
|
||||
tor_asprintf(&result,
|
||||
"entry-stats-end %s (%u s)\n"
|
||||
"entry-ips %s\n",
|
||||
t, (unsigned) (now - start_of_entry_stats_interval),
|
||||
data ? data : "");
|
||||
tor_free(data);
|
||||
return result;
|
||||
}
|
||||
@ -1437,25 +1618,30 @@ getinfo_helper_geoip(control_connection_t *control_conn,
|
||||
const char **errmsg)
|
||||
{
|
||||
(void)control_conn;
|
||||
if (!geoip_is_loaded()) {
|
||||
*errmsg = "GeoIP data not loaded";
|
||||
return -1;
|
||||
}
|
||||
if (!strcmpstart(question, "ip-to-country/")) {
|
||||
int c;
|
||||
uint32_t ip;
|
||||
struct in_addr in;
|
||||
sa_family_t family;
|
||||
tor_addr_t addr;
|
||||
question += strlen("ip-to-country/");
|
||||
if (tor_inet_aton(question, &in) != 0) {
|
||||
ip = ntohl(in.s_addr);
|
||||
c = geoip_get_country_by_ip(ip);
|
||||
*answer = tor_strdup(geoip_get_country_name(c));
|
||||
family = tor_addr_parse(&addr, question);
|
||||
if (family != AF_INET && family != AF_INET6) {
|
||||
*errmsg = "Invalid address family";
|
||||
return -1;
|
||||
}
|
||||
if (!geoip_is_loaded(family)) {
|
||||
*errmsg = "GeoIP data not loaded";
|
||||
return -1;
|
||||
}
|
||||
if (family == AF_INET)
|
||||
c = geoip_get_country_by_ipv4(tor_addr_to_ipv4h(&addr));
|
||||
else /* AF_INET6 */
|
||||
c = geoip_get_country_by_ipv6(tor_addr_to_in6(&addr));
|
||||
*answer = tor_strdup(geoip_get_country_name(c));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Release all storage held by the GeoIP database. */
|
||||
/** Release all storage held by the GeoIP databases and country list. */
|
||||
static void
|
||||
clear_geoip_db(void)
|
||||
{
|
||||
@ -1465,13 +1651,20 @@ clear_geoip_db(void)
|
||||
}
|
||||
|
||||
strmap_free(country_idxplus1_by_lc_code, NULL);
|
||||
if (geoip_entries) {
|
||||
SMARTLIST_FOREACH(geoip_entries, geoip_entry_t *, ent, tor_free(ent));
|
||||
smartlist_free(geoip_entries);
|
||||
if (geoip_ipv4_entries) {
|
||||
SMARTLIST_FOREACH(geoip_ipv4_entries, geoip_ipv4_entry_t *, ent,
|
||||
tor_free(ent));
|
||||
smartlist_free(geoip_ipv4_entries);
|
||||
}
|
||||
if (geoip_ipv6_entries) {
|
||||
SMARTLIST_FOREACH(geoip_ipv6_entries, geoip_ipv6_entry_t *, ent,
|
||||
tor_free(ent));
|
||||
smartlist_free(geoip_ipv6_entries);
|
||||
}
|
||||
geoip_countries = NULL;
|
||||
country_idxplus1_by_lc_code = NULL;
|
||||
geoip_entries = NULL;
|
||||
geoip_ipv4_entries = NULL;
|
||||
geoip_ipv6_entries = NULL;
|
||||
}
|
||||
|
||||
/** Release all storage held in this file. */
|
||||
|
@ -13,16 +13,17 @@
|
||||
#define TOR_GEOIP_H
|
||||
|
||||
#ifdef GEOIP_PRIVATE
|
||||
int geoip_parse_entry(const char *line);
|
||||
int geoip_parse_entry(const char *line, sa_family_t family);
|
||||
int geoip_get_country_by_ipv4(uint32_t ipaddr);
|
||||
int geoip_get_country_by_ipv6(const struct in6_addr *addr);
|
||||
#endif
|
||||
int should_record_bridge_info(const or_options_t *options);
|
||||
int geoip_load_file(const char *filename, const or_options_t *options);
|
||||
int geoip_get_country_by_ip(uint32_t ipaddr);
|
||||
int geoip_load_file(sa_family_t family, const char *filename);
|
||||
int geoip_get_country_by_addr(const tor_addr_t *addr);
|
||||
int geoip_get_n_countries(void);
|
||||
const char *geoip_get_country_name(country_t num);
|
||||
int geoip_is_loaded(void);
|
||||
const char *geoip_db_digest(void);
|
||||
int geoip_is_loaded(sa_family_t family);
|
||||
const char *geoip_db_digest(sa_family_t family);
|
||||
country_t geoip_get_country(const char *countrycode);
|
||||
|
||||
void geoip_note_client_seen(geoip_client_action_t action,
|
||||
@ -31,7 +32,8 @@ void geoip_remove_old_clients(time_t cutoff);
|
||||
|
||||
void geoip_note_ns_response(geoip_client_action_t action,
|
||||
geoip_ns_response_t response);
|
||||
char *geoip_get_client_history(geoip_client_action_t action);
|
||||
int geoip_get_client_history(geoip_client_action_t action,
|
||||
char **country_str, char **ipver_str);
|
||||
char *geoip_get_request_history(geoip_client_action_t action);
|
||||
int getinfo_helper_geoip(control_connection_t *control_conn,
|
||||
const char *question, char **answer,
|
||||
|
@ -921,12 +921,15 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out)
|
||||
void
|
||||
node_set_country(node_t *node)
|
||||
{
|
||||
tor_addr_t addr = TOR_ADDR_NULL;
|
||||
|
||||
/* XXXXipv6 */
|
||||
if (node->rs)
|
||||
node->country = geoip_get_country_by_ip(node->rs->addr);
|
||||
tor_addr_from_ipv4h(&addr, node->rs->addr);
|
||||
else if (node->ri)
|
||||
node->country = geoip_get_country_by_ip(node->ri->addr);
|
||||
else
|
||||
node->country = -1;
|
||||
tor_addr_from_ipv4h(&addr, node->ri->addr);
|
||||
|
||||
node->country = geoip_get_country_by_addr(&addr);
|
||||
}
|
||||
|
||||
/** Set the country code of all routers in the routerlist. */
|
||||
|
@ -3657,8 +3657,9 @@ typedef struct {
|
||||
* the bridge authority guess which countries have blocked access to us. */
|
||||
int BridgeRecordUsageByCountry;
|
||||
|
||||
/** Optionally, a file with GeoIP data. */
|
||||
/** Optionally, IPv4 and IPv6 GeoIP data. */
|
||||
char *GeoIPFile;
|
||||
char *GeoIPv6File;
|
||||
|
||||
/** If true, SIGHUP should reload the torrc. Sometimes controllers want
|
||||
* to make this false. */
|
||||
|
@ -319,9 +319,13 @@ addr_is_in_cc_list(uint32_t addr, const smartlist_t *cc_list)
|
||||
{
|
||||
country_t country;
|
||||
const char *name;
|
||||
tor_addr_t tar;
|
||||
|
||||
if (!cc_list)
|
||||
return 0;
|
||||
country = geoip_get_country_by_ip(addr);
|
||||
/* XXXXipv6 */
|
||||
tor_addr_from_ipv4h(&tar, addr);
|
||||
country = geoip_get_country_by_addr(&tar);
|
||||
name = geoip_get_country_name(country);
|
||||
return smartlist_string_isin_case(cc_list, name);
|
||||
}
|
||||
|
@ -2313,9 +2313,12 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
|
||||
tor_free(bandwidth_usage);
|
||||
smartlist_add(chunks, pre);
|
||||
|
||||
if (geoip_is_loaded()) {
|
||||
smartlist_add_asprintf(chunks, "geoip-db-digest %s\n", geoip_db_digest());
|
||||
}
|
||||
if (geoip_is_loaded(AF_INET))
|
||||
smartlist_add_asprintf(chunks, "geoip-db-digest %s\n",
|
||||
geoip_db_digest(AF_INET));
|
||||
if (geoip_is_loaded(AF_INET6))
|
||||
smartlist_add_asprintf(chunks, "geoip6-db-digest %s\n",
|
||||
geoip_db_digest(AF_INET6));
|
||||
|
||||
if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
|
||||
log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
|
||||
|
@ -4791,7 +4791,7 @@ routers_sort_by_identity(smartlist_t *routers)
|
||||
smartlist_sort(routers, compare_routerinfo_by_id_digest_);
|
||||
}
|
||||
|
||||
/** Called when we change a node set, or when we reload the geoip list:
|
||||
/** Called when we change a node set, or when we reload the geoip IPv4 list:
|
||||
* recompute all country info in all configuration node sets and in the
|
||||
* routerlist. */
|
||||
void
|
||||
|
@ -74,7 +74,7 @@ routerset_get_countryname(const char *c)
|
||||
}
|
||||
|
||||
/** Update the routerset's <b>countries</b> bitarray_t. Called whenever
|
||||
* the GeoIP database is reloaded.
|
||||
* the GeoIP IPv4 database is reloaded.
|
||||
*/
|
||||
void
|
||||
routerset_refresh_countries(routerset_t *target)
|
||||
@ -82,7 +82,7 @@ routerset_refresh_countries(routerset_t *target)
|
||||
int cc;
|
||||
bitarray_free(target->countries);
|
||||
|
||||
if (!geoip_is_loaded()) {
|
||||
if (!geoip_is_loaded(AF_INET)) {
|
||||
target->countries = NULL;
|
||||
target->n_countries = 0;
|
||||
return;
|
||||
@ -216,7 +216,7 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr,
|
||||
return 3;
|
||||
if (set->countries) {
|
||||
if (country < 0 && addr)
|
||||
country = geoip_get_country_by_ip(tor_addr_to_ipv4h(addr));
|
||||
country = geoip_get_country_by_addr(addr);
|
||||
|
||||
if (country >= 0 && country < set->n_countries &&
|
||||
bitarray_is_set(set->countries, country))
|
||||
|
113
src/test/test.c
113
src/test/test.c
@ -1454,10 +1454,11 @@ test_geoip(void)
|
||||
{
|
||||
int i, j;
|
||||
time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
|
||||
char *s = NULL;
|
||||
char *s = NULL, *v = NULL;
|
||||
const char *bridge_stats_1 =
|
||||
"bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
|
||||
"bridge-ips zz=24,xy=8\n",
|
||||
"bridge-ips zz=24,xy=8\n"
|
||||
"bridge-ip-versions v4=16,v6=16\n",
|
||||
*dirreq_stats_1 =
|
||||
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
|
||||
"dirreq-v3-ips ab=8\n"
|
||||
@ -1521,61 +1522,106 @@ test_geoip(void)
|
||||
"entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
|
||||
"entry-ips \n";
|
||||
tor_addr_t addr;
|
||||
struct in6_addr in6;
|
||||
|
||||
/* Populate the DB a bit. Add these in order, since we can't do the final
|
||||
* 'sort' step. These aren't very good IP addresses, but they're perfectly
|
||||
* fine uint32_t values. */
|
||||
test_eq(0, geoip_parse_entry("10,50,AB"));
|
||||
test_eq(0, geoip_parse_entry("52,90,XY"));
|
||||
test_eq(0, geoip_parse_entry("95,100,AB"));
|
||||
test_eq(0, geoip_parse_entry("\"105\",\"140\",\"ZZ\""));
|
||||
test_eq(0, geoip_parse_entry("\"150\",\"190\",\"XY\""));
|
||||
test_eq(0, geoip_parse_entry("\"200\",\"250\",\"AB\""));
|
||||
test_eq(0, geoip_parse_entry("10,50,AB", AF_INET));
|
||||
test_eq(0, geoip_parse_entry("52,90,XY", AF_INET));
|
||||
test_eq(0, geoip_parse_entry("95,100,AB", AF_INET));
|
||||
test_eq(0, geoip_parse_entry("\"105\",\"140\",\"ZZ\"", AF_INET));
|
||||
test_eq(0, geoip_parse_entry("\"150\",\"190\",\"XY\"", AF_INET));
|
||||
test_eq(0, geoip_parse_entry("\"200\",\"250\",\"AB\"", AF_INET));
|
||||
|
||||
/* Populate the IPv6 DB equivalently with fake IPs in the same range */
|
||||
test_eq(0, geoip_parse_entry("::a,::32,AB", AF_INET6));
|
||||
test_eq(0, geoip_parse_entry("::34,::5a,XY", AF_INET6));
|
||||
test_eq(0, geoip_parse_entry("::5f,::64,AB", AF_INET6));
|
||||
test_eq(0, geoip_parse_entry("::69,::8c,ZZ", AF_INET6));
|
||||
test_eq(0, geoip_parse_entry("::96,::be,XY", AF_INET6));
|
||||
test_eq(0, geoip_parse_entry("::c8,::fa,AB", AF_INET6));
|
||||
|
||||
/* We should have 4 countries: ??, ab, xy, zz. */
|
||||
test_eq(4, geoip_get_n_countries());
|
||||
memset(&in6, 0, sizeof(in6));
|
||||
|
||||
/* Make sure that country ID actually works. */
|
||||
#define NAMEFOR(x) geoip_get_country_name(geoip_get_country_by_ip(x))
|
||||
test_streq("??", NAMEFOR(3));
|
||||
test_eq(0, geoip_get_country_by_ip(3));
|
||||
test_streq("ab", NAMEFOR(32));
|
||||
test_streq("??", NAMEFOR(5));
|
||||
test_streq("??", NAMEFOR(51));
|
||||
test_streq("xy", NAMEFOR(150));
|
||||
test_streq("xy", NAMEFOR(190));
|
||||
test_streq("??", NAMEFOR(2000));
|
||||
#undef NAMEFOR
|
||||
#define SET_TEST_IPV6(i) in6.s6_addr32[3] = htonl((uint32_t) i)
|
||||
#define CHECK_COUNTRY(country, val) do { \
|
||||
/* test ipv4 country lookup */ \
|
||||
test_streq(country, \
|
||||
geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
|
||||
/* test ipv6 country lookup */ \
|
||||
SET_TEST_IPV6(val); \
|
||||
test_streq(country, \
|
||||
geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
|
||||
} while (0)
|
||||
|
||||
CHECK_COUNTRY("??", 3);
|
||||
CHECK_COUNTRY("ab", 32);
|
||||
CHECK_COUNTRY("??", 5);
|
||||
CHECK_COUNTRY("??", 51);
|
||||
CHECK_COUNTRY("xy", 150);
|
||||
CHECK_COUNTRY("xy", 190);
|
||||
CHECK_COUNTRY("??", 2000);
|
||||
|
||||
test_eq(0, geoip_get_country_by_ipv4(3));
|
||||
SET_TEST_IPV6(3);
|
||||
test_eq(0, geoip_get_country_by_ipv6(&in6));
|
||||
|
||||
#undef CHECK_COUNTRY
|
||||
|
||||
/* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
|
||||
* using ipv4. Since our fake geoip database is the same between
|
||||
* ipv4 and ipv6, we should get the same result no matter which
|
||||
* address family we pick for each IP. */
|
||||
#define SET_TEST_ADDRESS(i) do { \
|
||||
if ((i) & 1) { \
|
||||
SET_TEST_IPV6(i); \
|
||||
tor_addr_from_in6(&addr, &in6); \
|
||||
} else { \
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) i); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
get_options_mutable()->BridgeRelay = 1;
|
||||
get_options_mutable()->BridgeRecordUsageByCountry = 1;
|
||||
/* Put 9 observations in AB... */
|
||||
for (i=32; i < 40; ++i) {
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) i);
|
||||
SET_TEST_ADDRESS(i);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
|
||||
}
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 225);
|
||||
SET_TEST_ADDRESS(225);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
|
||||
/* and 3 observations in XY, several times. */
|
||||
for (j=0; j < 10; ++j)
|
||||
for (i=52; i < 55; ++i) {
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) i);
|
||||
SET_TEST_ADDRESS(i);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-3600);
|
||||
}
|
||||
/* and 17 observations in ZZ... */
|
||||
for (i=110; i < 127; ++i) {
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) i);
|
||||
SET_TEST_ADDRESS(i);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
|
||||
}
|
||||
s = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
|
||||
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &s, &v);
|
||||
test_assert(s);
|
||||
test_assert(v);
|
||||
test_streq("zz=24,ab=16,xy=8", s);
|
||||
test_streq("v4=16,v6=16", v);
|
||||
tor_free(s);
|
||||
tor_free(v);
|
||||
|
||||
/* Now clear out all the AB observations. */
|
||||
geoip_remove_old_clients(now-6000);
|
||||
s = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
|
||||
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &s, &v);
|
||||
test_assert(s);
|
||||
test_assert(v);
|
||||
test_streq("zz=24,xy=8", s);
|
||||
test_streq("v4=16,v6=16", v);
|
||||
tor_free(s);
|
||||
tor_free(v);
|
||||
|
||||
/* Start testing bridge statistics by making sure that we don't output
|
||||
* bridge stats without initializing them. */
|
||||
@ -1604,7 +1650,7 @@ test_geoip(void)
|
||||
|
||||
/* Start testing dirreq statistics by making sure that we don't collect
|
||||
* dirreq stats without initializing them. */
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 100);
|
||||
SET_TEST_ADDRESS(100);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
|
||||
s = geoip_format_dirreq_stats(now + 86400);
|
||||
test_assert(!s);
|
||||
@ -1612,7 +1658,7 @@ test_geoip(void)
|
||||
/* Initialize stats, note one connecting client, and generate the
|
||||
* dirreq-stats history string. */
|
||||
geoip_dirreq_stats_init(now);
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 100);
|
||||
SET_TEST_ADDRESS(100);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
|
||||
s = geoip_format_dirreq_stats(now + 86400);
|
||||
test_streq(dirreq_stats_1, s);
|
||||
@ -1621,7 +1667,7 @@ test_geoip(void)
|
||||
/* Stop collecting stats, add another connecting client, and ensure we
|
||||
* don't generate a history string. */
|
||||
geoip_dirreq_stats_term();
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 101);
|
||||
SET_TEST_ADDRESS(101);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
|
||||
s = geoip_format_dirreq_stats(now + 86400);
|
||||
test_assert(!s);
|
||||
@ -1629,7 +1675,7 @@ test_geoip(void)
|
||||
/* Re-start stats, add a connecting client, reset stats, and make sure
|
||||
* that we get an all empty history string. */
|
||||
geoip_dirreq_stats_init(now);
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 100);
|
||||
SET_TEST_ADDRESS(100);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
|
||||
geoip_reset_dirreq_stats(now);
|
||||
s = geoip_format_dirreq_stats(now + 86400);
|
||||
@ -1657,7 +1703,7 @@ test_geoip(void)
|
||||
|
||||
/* Start testing entry statistics by making sure that we don't collect
|
||||
* anything without initializing entry stats. */
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 100);
|
||||
SET_TEST_ADDRESS(100);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
|
||||
s = geoip_format_entry_stats(now + 86400);
|
||||
test_assert(!s);
|
||||
@ -1665,7 +1711,7 @@ test_geoip(void)
|
||||
/* Initialize stats, note one connecting client, and generate the
|
||||
* entry-stats history string. */
|
||||
geoip_entry_stats_init(now);
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 100);
|
||||
SET_TEST_ADDRESS(100);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
|
||||
s = geoip_format_entry_stats(now + 86400);
|
||||
test_streq(entry_stats_1, s);
|
||||
@ -1674,7 +1720,7 @@ test_geoip(void)
|
||||
/* Stop collecting stats, add another connecting client, and ensure we
|
||||
* don't generate a history string. */
|
||||
geoip_entry_stats_term();
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 101);
|
||||
SET_TEST_ADDRESS(101);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
|
||||
s = geoip_format_entry_stats(now + 86400);
|
||||
test_assert(!s);
|
||||
@ -1682,13 +1728,16 @@ test_geoip(void)
|
||||
/* Re-start stats, add a connecting client, reset stats, and make sure
|
||||
* that we get an all empty history string. */
|
||||
geoip_entry_stats_init(now);
|
||||
tor_addr_from_ipv4h(&addr, (uint32_t) 100);
|
||||
SET_TEST_ADDRESS(100);
|
||||
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
|
||||
geoip_reset_entry_stats(now);
|
||||
s = geoip_format_entry_stats(now + 86400);
|
||||
test_streq(entry_stats_2, s);
|
||||
tor_free(s);
|
||||
|
||||
#undef SET_TEST_ADDRESS
|
||||
#undef SET_TEST_IPV6
|
||||
|
||||
/* Stop collecting entry statistics. */
|
||||
geoip_entry_stats_term();
|
||||
get_options_mutable()->EntryStatistics = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user