tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

commit c8f2a6d2fe17fb1c02116fddaa93ded0ce655af9
parent fa32574bdb7381f02abc68ad917ad79f354938a4
Author: Nick Mathewson <nickm@torproject.org>
Date:   Thu, 27 Sep 2018 10:26:01 -0400

Extract the non-stats part of geoip into a new src/lib/geoip.

Diffstat:
M.gitignore | 2++
MMakefile.am | 2++
Msrc/app/config/config.c | 2+-
Msrc/app/main/main.c | 2+-
Msrc/core/include.am | 2--
Msrc/core/mainloop/connection.c | 2+-
Msrc/core/or/connection_or.c | 2+-
Msrc/core/or/or.h | 4+---
Msrc/core/or/policies.c | 2+-
Msrc/feature/control/getinfo_geoip.c | 2+-
Msrc/feature/nodelist/nodelist.c | 2+-
Msrc/feature/nodelist/routerset.c | 2+-
Msrc/feature/relay/router.c | 2+-
Dsrc/feature/stats/geoip.c | 516-------------------------------------------------------------------------------
Dsrc/feature/stats/geoip.h | 48------------------------------------------------
Msrc/feature/stats/geoip_stats.c | 2+-
Msrc/include.am | 1+
Asrc/lib/geoip/.may_include | 12++++++++++++
Asrc/lib/geoip/country.h | 14++++++++++++++
Asrc/lib/geoip/geoip.c | 510+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/geoip/geoip.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/geoip/include.am | 17+++++++++++++++++
Msrc/test/test_config.c | 2+-
Msrc/test/test_dir_handle_get.c | 2+-
Msrc/test/test_geoip.c | 2+-
Msrc/test/test_options.c | 2+-
Msrc/test/test_routerset.c | 2+-
27 files changed, 622 insertions(+), 584 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -173,6 +173,8 @@ uptime-*.json /src/lib/libtor-fdio-testing.a /src/lib/libtor-fs.a /src/lib/libtor-fs-testing.a +/src/lib/libtor-geoip.a +/src/lib/libtor-geoip-testing.a /src/lib/libtor-intmath.a /src/lib/libtor-intmath-testing.a /src/lib/libtor-lock.a diff --git a/Makefile.am b/Makefile.am @@ -40,6 +40,7 @@ endif # "Common" libraries used to link tor's utility code. TOR_UTIL_LIBS = \ + src/lib/libtor-geoip.a \ src/lib/libtor-process.a \ src/lib/libtor-time.a \ src/lib/libtor-fs.a \ @@ -68,6 +69,7 @@ TOR_UTIL_LIBS = \ # and tests) if UNITTESTS_ENABLED TOR_UTIL_TESTING_LIBS = \ + src/lib/libtor-geoip-testing.a \ src/lib/libtor-process-testing.a \ src/lib/libtor-time-testing.a \ src/lib/libtor-fs-testing.a \ diff --git a/src/app/config/config.c b/src/app/config/config.c @@ -103,7 +103,7 @@ #include "feature/relay/routermode.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendservice.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" #include "feature/stats/rephist.h" diff --git a/src/app/main/main.c b/src/app/main/main.c @@ -61,7 +61,7 @@ #include "feature/rend/rendcache.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendservice.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" #include "feature/stats/rephist.h" diff --git a/src/core/include.am b/src/core/include.am @@ -117,7 +117,6 @@ LIBTOR_APP_A_SOURCES = \ src/feature/rend/rendcommon.c \ src/feature/rend/rendmid.c \ src/feature/rend/rendservice.c \ - src/feature/stats/geoip.c \ src/feature/stats/geoip_stats.c \ src/feature/stats/rephist.c \ src/feature/stats/predict_ports.c @@ -350,7 +349,6 @@ noinst_HEADERS += \ src/feature/rend/rendcommon.h \ src/feature/rend/rendmid.h \ src/feature/rend/rendservice.h \ - src/feature/stats/geoip.h \ src/feature/stats/geoip_stats.h \ src/feature/stats/rephist.h \ src/feature/stats/predict_ports.h diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c @@ -102,7 +102,7 @@ #include "feature/relay/routermode.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendcommon.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_util.h" diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c @@ -43,7 +43,7 @@ #include "lib/crypt_ops/crypto_util.h" #include "feature/dirauth/reachability.h" #include "feature/client/entrynodes.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "core/mainloop/mainloop.h" #include "trunnel/link_handshake.h" #include "feature/nodelist/microdesc.h" diff --git a/src/core/or/or.h b/src/core/or/or.h @@ -41,6 +41,7 @@ #include "lib/fs/mmap.h" #include "lib/fs/path.h" #include "lib/fs/userdb.h" +#include "lib/geoip/country.h" #include "lib/intmath/addsub.h" #include "lib/intmath/bits.h" #include "lib/intmath/cmp.h" @@ -798,9 +799,6 @@ typedef struct download_status_t download_status_t; typedef struct signed_descriptor_t signed_descriptor_t; -/** A signed integer representing a country code. */ -typedef int16_t country_t; - /** Flags used to summarize the declared protocol versions of a relay, * so we don't need to parse them again and again. */ typedef struct protover_summary_flags_t { diff --git a/src/core/or/policies.c b/src/core/or/policies.c @@ -27,7 +27,7 @@ #include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/nodelist/routerparse.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "ht.h" #include "lib/encoding/confline.h" diff --git a/src/feature/control/getinfo_geoip.c b/src/feature/control/getinfo_geoip.c @@ -3,7 +3,7 @@ #include "core/mainloop/connection.h" #include "feature/control/control.h" #include "feature/control/getinfo_geoip.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" /** Helper used to implement GETINFO ip-to-country/... controller command. */ int diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c @@ -65,7 +65,7 @@ #include "feature/nodelist/routerset.h" #include "feature/nodelist/torcert.h" #include "feature/rend/rendservice.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "lib/net/address.h" #include <string.h> diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c @@ -34,7 +34,7 @@ n * Copyright (c) 2001-2004, Roger Dingledine. #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerparse.h" #include "feature/nodelist/routerset.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "core/or/addr_policy_st.h" #include "core/or/extend_info_st.h" diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c @@ -36,7 +36,7 @@ #include "feature/relay/routerkeys.h" #include "feature/relay/routermode.h" #include "feature/relay/selftest.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_ed25519.h" diff --git a/src/feature/stats/geoip.c b/src/feature/stats/geoip.c @@ -1,516 +0,0 @@ -/* Copyright (c) 2007-2018, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file geoip.c - * \brief Functions related to maintaining an IP-to-country database; - * to summarizing client connections by country to entry guards, bridges, - * and directory servers; and for statistics on answering network status - * requests. - * - * There are two main kinds of functions in this module: geoip functions, - * which map groups of IPv4 and IPv6 addresses to country codes, and - * statistical functions, which collect statistics about different kinds of - * per-country usage. - * - * The geoip lookup tables are implemented as sorted lists of disjoint address - * ranges, each mapping to a singleton geoip_country_t. These country objects - * are also indexed by their names in a hashtable. - * - * The tables are populated from disk at startup by the geoip_load_file() - * function. For more information on the file format they read, see that - * function. See the scripts and the README file in src/config for more - * information about how those files are generated. - * - * Tor uses GeoIP information in order to implement user requests (such as - * ExcludeNodes {cc}), and to keep track of how much usage relays are getting - * for each country. - */ - -#define GEOIP_PRIVATE - -#include "lib/cc/torint.h" -/** A signed integer representing a country code. */ -typedef int16_t country_t; // XXXX duplicate in or.h - -#include "feature/stats/geoip.h" -#include "lib/container/map.h" -#include "lib/container/order.h" -#include "lib/container/smartlist.h" -#include "lib/crypt_ops/crypto_digest.h" -#include "lib/ctime/di_ops.h" -#include "lib/encoding/binascii.h" -#include "lib/fs/files.h" -#include "lib/log/escape.h" -#include "lib/malloc/malloc.h" -#include "lib/net/address.h" //???? -#include "lib/net/inaddr.h" -#include "lib/string/compat_ctype.h" -#include "lib/string/compat_string.h" -#include "lib/string/scanf.h" -#include "lib/string/util_string.h" -#include "lib/time/tvdiff.h" - -#include <stdio.h> -#include <string.h> - -static void init_geoip_countries(void); - -/** 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_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 list of geoip_country_t */ -static smartlist_t *geoip_countries = NULL; -/** A map from lowercased country codes to their position in geoip_countries. - * 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; -/** 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 files to include in extra-info descriptors. */ -static char geoip_digest[DIGEST_LEN]; -static char geoip6_digest[DIGEST_LEN]; - -/** Return a list of geoip_country_t for all known countries. */ -const smartlist_t * -geoip_get_countries(void) -{ - if (geoip_countries == NULL) { - init_geoip_countries(); - } - return geoip_countries; -} - -/** 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. */ -MOCK_IMPL(country_t, -geoip_get_country,(const char *country)) -{ - void *idxplus1_; - intptr_t idx; - - idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country); - if (!idxplus1_) - return -1; - - idx = ((uintptr_t)idxplus1_)-1; - return (country_t)idx; -} - -/** 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(const tor_addr_t *low, const tor_addr_t *high, - const char *country) -{ - intptr_t idx; - void *idxplus1_; - - IF_BUG_ONCE(tor_addr_family(low) != tor_addr_family(high)) - return; - IF_BUG_ONCE(tor_addr_compare(high, low, CMP_EXACT) < 0) - return; - - idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country); - - if (!idxplus1_) { - geoip_country_t *c = tor_malloc_zero(sizeof(geoip_country_t)); - strlcpy(c->countrycode, country, sizeof(c->countrycode)); - tor_strlower(c->countrycode); - smartlist_add(geoip_countries, c); - idx = smartlist_len(geoip_countries) - 1; - strmap_set_lc(country_idxplus1_by_lc_code, country, (void*)(idx+1)); - } else { - idx = ((uintptr_t)idxplus1_)-1; - } - { - geoip_country_t *c = smartlist_get(geoip_countries, (int)idx); - tor_assert(!strcasecmp(c->countrycode, country)); - } - - 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_assert(low); - ent->ip_high = *tor_addr_to_in6_assert(high); - ent->country = idx; - smartlist_add(geoip_ipv6_entries, ent); - } -} - -/** 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(). */ -STATIC int -geoip_parse_entry(const char *line, sa_family_t family) -{ - tor_addr_t low_addr, high_addr; - char c[3]; - char *country = NULL; - - if (!geoip_countries) - init_geoip_countries(); - 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; - - char buf[512]; - 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 *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_ipv4_entry_t */ -static int -geoip_ipv4_compare_entries_(const void **_a, const void **_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) - return 1; - else - return 0; -} - -/** 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_ipv4_entry_t */ -static int -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_ipv4_entry_t *entry = *_member; - if (addr < entry->ip_low) - return -1; - else if (addr > entry->ip_high) - return 1; - else - 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 fast_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 (fast_memcmp(addr->s6_addr, entry->ip_low.s6_addr, - sizeof(struct in6_addr)) < 0) - return -1; - else if (fast_memcmp(addr->s6_addr, entry->ip_high.s6_addr, - sizeof(struct in6_addr)) > 0) - return 1; - else - return 0; -} - -/** Set up a new list of geoip countries with no countries (yet) set in it, - * except for the unknown country. - */ -static void -init_geoip_countries(void) -{ - geoip_country_t *geoip_unresolved; - geoip_countries = smartlist_new(); - /* Add a geoip_country_t for requests that could not be resolved to a - * country as first element (index 0) to geoip_countries. */ - geoip_unresolved = tor_malloc_zero(sizeof(geoip_country_t)); - strlcpy(geoip_unresolved->countrycode, "??", - sizeof(geoip_unresolved->countrycode)); - smartlist_add(geoip_countries, geoip_unresolved); - country_idxplus1_by_lc_code = strmap_new(); - strmap_set_lc(country_idxplus1_by_lc_code, "??", (void*)(1)); -} - -/** 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, int severity) -{ - FILE *f; - 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.", - filename); - 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_); - 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; -} - -/** Given an IP address in host order, 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_ipv4(uint32_t ipaddr) -{ - geoip_ipv4_entry_t *ent; - if (!geoip_ipv4_entries) - return -1; - 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; -} - -/** Given an IP 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(). - */ -MOCK_IMPL(int, -geoip_get_country_by_addr,(const tor_addr_t *addr)) -{ - 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 the number of countries recognized by the GeoIP country list. */ -MOCK_IMPL(int, -geoip_get_n_countries,(void)) -{ - if (!geoip_countries) - init_geoip_countries(); - return (int) smartlist_len(geoip_countries); -} - -/** Return the two-letter country code associated with the number <b>num</b>, - * or "??" for an unknown value. */ -const char * -geoip_get_country_name(country_t num) -{ - if (geoip_countries && num >= 0 && num < smartlist_len(geoip_countries)) { - geoip_country_t *c = smartlist_get(geoip_countries, num); - return c->countrycode; - } else - return "??"; -} - -/** Return true iff we have loaded a GeoIP database.*/ -MOCK_IMPL(int, -geoip_is_loaded,(sa_family_t family)) -{ - 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(sa_family_t family) -{ - 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); -} - -/** Release all storage held by the GeoIP databases and country list. */ -STATIC void -clear_geoip_db(void) -{ - if (geoip_countries) { - SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, tor_free(c)); - smartlist_free(geoip_countries); - } - - strmap_free(country_idxplus1_by_lc_code, NULL); - 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_ipv4_entries = NULL; - geoip_ipv6_entries = NULL; -} - -/** Release all storage held in this file. */ -void -geoip_free_all(void) -{ - clear_geoip_db(); - - memset(geoip_digest, 0, sizeof(geoip_digest)); - memset(geoip6_digest, 0, sizeof(geoip6_digest)); -} diff --git a/src/feature/stats/geoip.h b/src/feature/stats/geoip.h @@ -1,48 +0,0 @@ -/* Copyright (c) 2001 Matej Pfajfar. - * Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2018, The Tor Project, Inc. */ -/* See LICENSE for licensing information */ - -/** - * \file geoip.h - * \brief Header file for geoip.c. - **/ - -#ifndef TOR_GEOIP_H -#define TOR_GEOIP_H - -#include "lib/net/nettypes.h" -#include "lib/testsupport/testsupport.h" -#include "lib/cc/torint.h" - -#ifdef GEOIP_PRIVATE -STATIC int geoip_parse_entry(const char *line, sa_family_t family); -STATIC void clear_geoip_db(void); -#endif /* defined(GEOIP_PRIVATE) */ - -struct in6_addr; -struct tor_addr_t; - -int geoip_get_country_by_ipv4(uint32_t ipaddr); -int geoip_get_country_by_ipv6(const struct in6_addr *addr); - -/** A per-country GeoIP record. */ -typedef struct geoip_country_t { - char countrycode[3]; -} geoip_country_t; - -struct smartlist_t; -const struct smartlist_t *geoip_get_countries(void); - -int geoip_load_file(sa_family_t family, const char *filename, int severity); -MOCK_DECL(int, geoip_get_country_by_addr, (const struct tor_addr_t *addr)); -MOCK_DECL(int, geoip_get_n_countries, (void)); -const char *geoip_get_country_name(country_t num); -MOCK_DECL(int, geoip_is_loaded, (sa_family_t family)); -const char *geoip_db_digest(sa_family_t family); -MOCK_DECL(country_t, geoip_get_country, (const char *countrycode)); - -void geoip_free_all(void); - -#endif /* !defined(TOR_GEOIP_H) */ diff --git a/src/feature/stats/geoip_stats.c b/src/feature/stats/geoip_stats.c @@ -35,7 +35,7 @@ #include "feature/control/control.h" #include "feature/client/dnsserv.h" #include "core/or/dos.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" #include "feature/nodelist/routerlist.h" diff --git a/src/include.am b/src/include.am @@ -11,6 +11,7 @@ include src/lib/encoding/include.am include src/lib/evloop/include.am include src/lib/fdio/include.am include src/lib/fs/include.am +include src/lib/geoip/include.am include src/lib/include.libdonna.am include src/lib/intmath/include.am include src/lib/lock/include.am diff --git a/src/lib/geoip/.may_include b/src/lib/geoip/.may_include @@ -0,0 +1,12 @@ +lib/cc/*.h +lib/container/*.h +lib/crypt_ops/*.h +lib/ctime/*.h +lib/encoding/*.h +lib/fs/*.h +lib/geoip/*.h +lib/log/*.h +lib/malloc/*.h +lib/net/*.h +lib/string/*.h +lib/testsupport/*.h diff --git a/src/lib/geoip/country.h b/src/lib/geoip/country.h @@ -0,0 +1,14 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_COUNTRY_H +#define TOR_COUNTRY_H + +#include "lib/cc/torint.h" +/** A signed integer representing a country code. */ +typedef int16_t country_t; + +#endif diff --git a/src/lib/geoip/geoip.c b/src/lib/geoip/geoip.c @@ -0,0 +1,510 @@ +/* Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file geoip.c + * \brief Functions related to maintaining an IP-to-country database; + * to summarizing client connections by country to entry guards, bridges, + * and directory servers; and for statistics on answering network status + * requests. + * + * There are two main kinds of functions in this module: geoip functions, + * which map groups of IPv4 and IPv6 addresses to country codes, and + * statistical functions, which collect statistics about different kinds of + * per-country usage. + * + * The geoip lookup tables are implemented as sorted lists of disjoint address + * ranges, each mapping to a singleton geoip_country_t. These country objects + * are also indexed by their names in a hashtable. + * + * The tables are populated from disk at startup by the geoip_load_file() + * function. For more information on the file format they read, see that + * function. See the scripts and the README file in src/config for more + * information about how those files are generated. + * + * Tor uses GeoIP information in order to implement user requests (such as + * ExcludeNodes {cc}), and to keep track of how much usage relays are getting + * for each country. + */ + +#define GEOIP_PRIVATE +#include "lib/geoip/geoip.h" +#include "lib/container/map.h" +#include "lib/container/order.h" +#include "lib/container/smartlist.h" +#include "lib/crypt_ops/crypto_digest.h" +#include "lib/ctime/di_ops.h" +#include "lib/encoding/binascii.h" +#include "lib/fs/files.h" +#include "lib/log/escape.h" +#include "lib/malloc/malloc.h" +#include "lib/net/address.h" //???? +#include "lib/net/inaddr.h" +#include "lib/string/compat_ctype.h" +#include "lib/string/compat_string.h" +#include "lib/string/scanf.h" +#include "lib/string/util_string.h" + +#include <stdio.h> +#include <string.h> + +static void init_geoip_countries(void); + +/** 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_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 list of geoip_country_t */ +static smartlist_t *geoip_countries = NULL; +/** A map from lowercased country codes to their position in geoip_countries. + * 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; +/** 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 files to include in extra-info descriptors. */ +static char geoip_digest[DIGEST_LEN]; +static char geoip6_digest[DIGEST_LEN]; + +/** Return a list of geoip_country_t for all known countries. */ +const smartlist_t * +geoip_get_countries(void) +{ + if (geoip_countries == NULL) { + init_geoip_countries(); + } + return geoip_countries; +} + +/** 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. */ +MOCK_IMPL(country_t, +geoip_get_country,(const char *country)) +{ + void *idxplus1_; + intptr_t idx; + + idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country); + if (!idxplus1_) + return -1; + + idx = ((uintptr_t)idxplus1_)-1; + return (country_t)idx; +} + +/** 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(const tor_addr_t *low, const tor_addr_t *high, + const char *country) +{ + intptr_t idx; + void *idxplus1_; + + IF_BUG_ONCE(tor_addr_family(low) != tor_addr_family(high)) + return; + IF_BUG_ONCE(tor_addr_compare(high, low, CMP_EXACT) < 0) + return; + + idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country); + + if (!idxplus1_) { + geoip_country_t *c = tor_malloc_zero(sizeof(geoip_country_t)); + strlcpy(c->countrycode, country, sizeof(c->countrycode)); + tor_strlower(c->countrycode); + smartlist_add(geoip_countries, c); + idx = smartlist_len(geoip_countries) - 1; + strmap_set_lc(country_idxplus1_by_lc_code, country, (void*)(idx+1)); + } else { + idx = ((uintptr_t)idxplus1_)-1; + } + { + geoip_country_t *c = smartlist_get(geoip_countries, (int)idx); + tor_assert(!strcasecmp(c->countrycode, country)); + } + + 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_assert(low); + ent->ip_high = *tor_addr_to_in6_assert(high); + ent->country = idx; + smartlist_add(geoip_ipv6_entries, ent); + } +} + +/** 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(). */ +STATIC int +geoip_parse_entry(const char *line, sa_family_t family) +{ + tor_addr_t low_addr, high_addr; + char c[3]; + char *country = NULL; + + if (!geoip_countries) + init_geoip_countries(); + 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; + + char buf[512]; + 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 *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_ipv4_entry_t */ +static int +geoip_ipv4_compare_entries_(const void **_a, const void **_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) + return 1; + else + return 0; +} + +/** 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_ipv4_entry_t */ +static int +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_ipv4_entry_t *entry = *_member; + if (addr < entry->ip_low) + return -1; + else if (addr > entry->ip_high) + return 1; + else + 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 fast_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 (fast_memcmp(addr->s6_addr, entry->ip_low.s6_addr, + sizeof(struct in6_addr)) < 0) + return -1; + else if (fast_memcmp(addr->s6_addr, entry->ip_high.s6_addr, + sizeof(struct in6_addr)) > 0) + return 1; + else + return 0; +} + +/** Set up a new list of geoip countries with no countries (yet) set in it, + * except for the unknown country. + */ +static void +init_geoip_countries(void) +{ + geoip_country_t *geoip_unresolved; + geoip_countries = smartlist_new(); + /* Add a geoip_country_t for requests that could not be resolved to a + * country as first element (index 0) to geoip_countries. */ + geoip_unresolved = tor_malloc_zero(sizeof(geoip_country_t)); + strlcpy(geoip_unresolved->countrycode, "??", + sizeof(geoip_unresolved->countrycode)); + smartlist_add(geoip_countries, geoip_unresolved); + country_idxplus1_by_lc_code = strmap_new(); + strmap_set_lc(country_idxplus1_by_lc_code, "??", (void*)(1)); +} + +/** 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, int severity) +{ + FILE *f; + 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.", + filename); + 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_); + 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; +} + +/** Given an IP address in host order, 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_ipv4(uint32_t ipaddr) +{ + geoip_ipv4_entry_t *ent; + if (!geoip_ipv4_entries) + return -1; + 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; +} + +/** Given an IP 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(). + */ +MOCK_IMPL(int, +geoip_get_country_by_addr,(const tor_addr_t *addr)) +{ + 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 the number of countries recognized by the GeoIP country list. */ +MOCK_IMPL(int, +geoip_get_n_countries,(void)) +{ + if (!geoip_countries) + init_geoip_countries(); + return (int) smartlist_len(geoip_countries); +} + +/** Return the two-letter country code associated with the number <b>num</b>, + * or "??" for an unknown value. */ +const char * +geoip_get_country_name(country_t num) +{ + if (geoip_countries && num >= 0 && num < smartlist_len(geoip_countries)) { + geoip_country_t *c = smartlist_get(geoip_countries, num); + return c->countrycode; + } else + return "??"; +} + +/** Return true iff we have loaded a GeoIP database.*/ +MOCK_IMPL(int, +geoip_is_loaded,(sa_family_t family)) +{ + 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(sa_family_t family) +{ + 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); +} + +/** Release all storage held by the GeoIP databases and country list. */ +STATIC void +clear_geoip_db(void) +{ + if (geoip_countries) { + SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, tor_free(c)); + smartlist_free(geoip_countries); + } + + strmap_free(country_idxplus1_by_lc_code, NULL); + 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_ipv4_entries = NULL; + geoip_ipv6_entries = NULL; +} + +/** Release all storage held in this file. */ +void +geoip_free_all(void) +{ + clear_geoip_db(); + + memset(geoip_digest, 0, sizeof(geoip_digest)); + memset(geoip6_digest, 0, sizeof(geoip6_digest)); +} diff --git a/src/lib/geoip/geoip.h b/src/lib/geoip/geoip.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file geoip.h + * \brief Header file for geoip.c. + **/ + +#ifndef TOR_GEOIP_H +#define TOR_GEOIP_H + +#include "lib/net/nettypes.h" +#include "lib/testsupport/testsupport.h" +#include "lib/geoip/country.h" + +#ifdef GEOIP_PRIVATE +STATIC int geoip_parse_entry(const char *line, sa_family_t family); +STATIC void clear_geoip_db(void); +#endif /* defined(GEOIP_PRIVATE) */ + +struct in6_addr; +struct tor_addr_t; + +int geoip_get_country_by_ipv4(uint32_t ipaddr); +int geoip_get_country_by_ipv6(const struct in6_addr *addr); + +/** A per-country GeoIP record. */ +typedef struct geoip_country_t { + char countrycode[3]; +} geoip_country_t; + +struct smartlist_t; +const struct smartlist_t *geoip_get_countries(void); + +int geoip_load_file(sa_family_t family, const char *filename, int severity); +MOCK_DECL(int, geoip_get_country_by_addr, (const struct tor_addr_t *addr)); +MOCK_DECL(int, geoip_get_n_countries, (void)); +const char *geoip_get_country_name(country_t num); +MOCK_DECL(int, geoip_is_loaded, (sa_family_t family)); +const char *geoip_db_digest(sa_family_t family); +MOCK_DECL(country_t, geoip_get_country, (const char *countrycode)); + +void geoip_free_all(void); + +#endif /* !defined(TOR_GEOIP_H) */ diff --git a/src/lib/geoip/include.am b/src/lib/geoip/include.am @@ -0,0 +1,17 @@ +noinst_LIBRARIES += src/lib/libtor-geoip.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-geoip-testing.a +endif + +src_lib_libtor_geoip_a_SOURCES = \ + src/lib/geoip/geoip.c + +src_lib_libtor_geoip_testing_a_SOURCES = \ + $(src_lib_libtor_geoip_a_SOURCES) +src_lib_libtor_geoip_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_geoip_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +noinst_HEADERS += \ + src/lib/geoip/geoip.h \ + src/lib/geoip/country.h diff --git a/src/test/test_config.c b/src/test/test_config.c @@ -29,7 +29,7 @@ #include "feature/client/entrynodes.h" #include "feature/client/transports.h" #include "feature/relay/ext_orport.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/hibernate/hibernate.h" #include "core/mainloop/mainloop.h" #include "feature/nodelist/networkstatus.h" diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c @@ -32,7 +32,7 @@ #include "feature/nodelist/routerparse.h" #include "feature/nodelist/networkstatus.h" #include "core/proto/proto_http.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" #include "feature/dircache/dirserv.h" #include "feature/dirauth/dirvote.h" diff --git a/src/test/test_geoip.c b/src/test/test_geoip.c @@ -10,7 +10,7 @@ #define GEOIP_PRIVATE #include "core/or/or.h" #include "app/config/config.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" #include "test/test.h" diff --git a/src/test/test_options.c b/src/test/test_options.c @@ -8,7 +8,7 @@ #include "app/config/confparse.h" #include "app/config/config.h" #include "test/test.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #define ROUTERSET_PRIVATE #include "feature/nodelist/routerset.h" diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c @@ -4,7 +4,7 @@ #define ROUTERSET_PRIVATE #include "core/or/or.h" -#include "feature/stats/geoip.h" +#include "lib/geoip/geoip.h" #include "feature/nodelist/routerset.h" #include "feature/nodelist/routerparse.h" #include "core/or/policies.h"