Back to index

tor  0.2.3.18-rc
geoip.c
Go to the documentation of this file.
00001 /* Copyright (c) 2007-2012, The Tor Project, Inc. */
00002 /* See LICENSE for licensing information */
00003 
00012 #define GEOIP_PRIVATE
00013 #include "or.h"
00014 #include "ht.h"
00015 #include "config.h"
00016 #include "control.h"
00017 #include "dnsserv.h"
00018 #include "geoip.h"
00019 #include "routerlist.h"
00020 
00021 static void clear_geoip_db(void);
00022 static void init_geoip_countries(void);
00023 
00025 typedef struct geoip_entry_t {
00026   uint32_t ip_low; 
00027   uint32_t ip_high; 
00028   intptr_t country; 
00029 } geoip_entry_t;
00030 
00032 typedef struct geoip_country_t {
00033   char countrycode[3];
00034   uint32_t n_v2_ns_requests;
00035   uint32_t n_v3_ns_requests;
00036 } geoip_country_t;
00037 
00039 static smartlist_t *geoip_countries = NULL;
00043 static strmap_t *country_idxplus1_by_lc_code = NULL;
00045 static smartlist_t *geoip_entries = NULL;
00046 
00048 static char geoip_digest[DIGEST_LEN];
00049 
00053 country_t
00054 geoip_get_country(const char *country)
00055 {
00056   void *_idxplus1;
00057   intptr_t idx;
00058 
00059   _idxplus1 = strmap_get_lc(country_idxplus1_by_lc_code, country);
00060   if (!_idxplus1)
00061     return -1;
00062 
00063   idx = ((uintptr_t)_idxplus1)-1;
00064   return (country_t)idx;
00065 }
00066 
00070 static void
00071 geoip_add_entry(uint32_t low, uint32_t high, const char *country)
00072 {
00073   intptr_t idx;
00074   geoip_entry_t *ent;
00075   void *_idxplus1;
00076 
00077   if (high < low)
00078     return;
00079 
00080   _idxplus1 = strmap_get_lc(country_idxplus1_by_lc_code, country);
00081 
00082   if (!_idxplus1) {
00083     geoip_country_t *c = tor_malloc_zero(sizeof(geoip_country_t));
00084     strlcpy(c->countrycode, country, sizeof(c->countrycode));
00085     tor_strlower(c->countrycode);
00086     smartlist_add(geoip_countries, c);
00087     idx = smartlist_len(geoip_countries) - 1;
00088     strmap_set_lc(country_idxplus1_by_lc_code, country, (void*)(idx+1));
00089   } else {
00090     idx = ((uintptr_t)_idxplus1)-1;
00091   }
00092   {
00093     geoip_country_t *c = smartlist_get(geoip_countries, idx);
00094     tor_assert(!strcasecmp(c->countrycode, country));
00095   }
00096   ent = tor_malloc_zero(sizeof(geoip_entry_t));
00097   ent->ip_low = low;
00098   ent->ip_high = high;
00099   ent->country = idx;
00100   smartlist_add(geoip_entries, ent);
00101 }
00102 
00105 /*private*/ int
00106 geoip_parse_entry(const char *line)
00107 {
00108   unsigned int low, high;
00109   char b[3];
00110   if (!geoip_countries)
00111     init_geoip_countries();
00112   if (!geoip_entries)
00113     geoip_entries = smartlist_new();
00114 
00115   while (TOR_ISSPACE(*line))
00116     ++line;
00117   if (*line == '#')
00118     return 0;
00119   if (tor_sscanf(line,"%u,%u,%2s", &low, &high, b) == 3) {
00120     geoip_add_entry(low, high, b);
00121     return 0;
00122   } else if (tor_sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, b) == 3) {
00123     geoip_add_entry(low, high, b);
00124     return 0;
00125   } else {
00126     log_warn(LD_GENERAL, "Unable to parse line from GEOIP file: %s",
00127              escaped(line));
00128     return -1;
00129   }
00130 }
00131 
00134 static int
00135 _geoip_compare_entries(const void **_a, const void **_b)
00136 {
00137   const geoip_entry_t *a = *_a, *b = *_b;
00138   if (a->ip_low < b->ip_low)
00139     return -1;
00140   else if (a->ip_low > b->ip_low)
00141     return 1;
00142   else
00143     return 0;
00144 }
00145 
00148 static int
00149 _geoip_compare_key_to_entry(const void *_key, const void **_member)
00150 {
00151   /* No alignment issue here, since _key really is a pointer to uint32_t */
00152   const uint32_t addr = *(uint32_t *)_key;
00153   const geoip_entry_t *entry = *_member;
00154   if (addr < entry->ip_low)
00155     return -1;
00156   else if (addr > entry->ip_high)
00157     return 1;
00158   else
00159     return 0;
00160 }
00161 
00164 int
00165 should_record_bridge_info(const or_options_t *options)
00166 {
00167   return options->BridgeRelay && options->BridgeRecordUsageByCountry;
00168 }
00169 
00173 static void
00174 init_geoip_countries(void)
00175 {
00176   geoip_country_t *geoip_unresolved;
00177   geoip_countries = smartlist_new();
00178   /* Add a geoip_country_t for requests that could not be resolved to a
00179    * country as first element (index 0) to geoip_countries. */
00180   geoip_unresolved = tor_malloc_zero(sizeof(geoip_country_t));
00181   strlcpy(geoip_unresolved->countrycode, "??",
00182           sizeof(geoip_unresolved->countrycode));
00183   smartlist_add(geoip_countries, geoip_unresolved);
00184   country_idxplus1_by_lc_code = strmap_new();
00185   strmap_set_lc(country_idxplus1_by_lc_code, "??", (void*)(1));
00186 }
00187 
00201 int
00202 geoip_load_file(const char *filename, const or_options_t *options)
00203 {
00204   FILE *f;
00205   const char *msg = "";
00206   int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO;
00207   crypto_digest_t *geoip_digest_env = NULL;
00208   clear_geoip_db();
00209   if (!(f = tor_fopen_cloexec(filename, "r"))) {
00210     log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s.  %s",
00211            filename, msg);
00212     return -1;
00213   }
00214   if (!geoip_countries)
00215     init_geoip_countries();
00216   if (geoip_entries) {
00217     SMARTLIST_FOREACH(geoip_entries, geoip_entry_t *, e, tor_free(e));
00218     smartlist_free(geoip_entries);
00219   }
00220   geoip_entries = smartlist_new();
00221   geoip_digest_env = crypto_digest_new();
00222   log_notice(LD_GENERAL, "Parsing GEOIP file %s.", filename);
00223   while (!feof(f)) {
00224     char buf[512];
00225     if (fgets(buf, (int)sizeof(buf), f) == NULL)
00226       break;
00227     crypto_digest_add_bytes(geoip_digest_env, buf, strlen(buf));
00228     /* FFFF track full country name. */
00229     geoip_parse_entry(buf);
00230   }
00231   /*XXXX abort and return -1 if no entries/illformed?*/
00232   fclose(f);
00233 
00234   smartlist_sort(geoip_entries, _geoip_compare_entries);
00235 
00236   /* Okay, now we need to maybe change our mind about what is in which
00237    * country. */
00238   refresh_all_country_info();
00239 
00240   /* Remember file digest so that we can include it in our extra-info
00241    * descriptors. */
00242   crypto_digest_get_digest(geoip_digest_env, geoip_digest, DIGEST_LEN);
00243   crypto_digest_free(geoip_digest_env);
00244 
00245   return 0;
00246 }
00247 
00254 int
00255 geoip_get_country_by_ip(uint32_t ipaddr)
00256 {
00257   geoip_entry_t *ent;
00258   if (!geoip_entries)
00259     return -1;
00260   ent = smartlist_bsearch(geoip_entries, &ipaddr, _geoip_compare_key_to_entry);
00261   return ent ? (int)ent->country : 0;
00262 }
00263 
00269 int
00270 geoip_get_country_by_addr(const tor_addr_t *addr)
00271 {
00272   if (tor_addr_family(addr) != AF_INET) {
00273     /*XXXX IP6 support ipv6 geoip.*/
00274     return -1;
00275   }
00276   return geoip_get_country_by_ip(tor_addr_to_ipv4h(addr));
00277 }
00278 
00280 int
00281 geoip_get_n_countries(void)
00282 {
00283   if (!geoip_countries)
00284     init_geoip_countries();
00285   return (int) smartlist_len(geoip_countries);
00286 }
00287 
00290 const char *
00291 geoip_get_country_name(country_t num)
00292 {
00293   if (geoip_countries && num >= 0 && num < smartlist_len(geoip_countries)) {
00294     geoip_country_t *c = smartlist_get(geoip_countries, num);
00295     return c->countrycode;
00296   } else
00297     return "??";
00298 }
00299 
00301 int
00302 geoip_is_loaded(void)
00303 {
00304   return geoip_countries != NULL && geoip_entries != NULL;
00305 }
00306 
00310 const char *
00311 geoip_db_digest(void)
00312 {
00313   return hex_str(geoip_digest, DIGEST_LEN);
00314 }
00315 
00319 typedef struct clientmap_entry_t {
00320   HT_ENTRY(clientmap_entry_t) node;
00321   tor_addr_t addr;
00326   unsigned int last_seen_in_minutes:30;
00327   unsigned int action:2;
00328 } clientmap_entry_t;
00329 
00333 #define MAX_LAST_SEEN_IN_MINUTES 0X3FFFFFFFu
00334 
00336 static HT_HEAD(clientmap, clientmap_entry_t) client_history =
00337      HT_INITIALIZER();
00338 
00340 static INLINE unsigned
00341 clientmap_entry_hash(const clientmap_entry_t *a)
00342 {
00343   return ht_improve_hash(tor_addr_hash(&a->addr));
00344 }
00346 static INLINE int
00347 clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
00348 {
00349   return !tor_addr_compare(&a->addr, &b->addr, CMP_EXACT) &&
00350          a->action == b->action;
00351 }
00352 
00353 HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
00354              clientmap_entries_eq);
00355 HT_GENERATE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
00356             clientmap_entries_eq, 0.6, malloc, realloc, free);
00357 
00359 static void
00360 client_history_clear(void)
00361 {
00362   clientmap_entry_t **ent, **next, *this;
00363   for (ent = HT_START(clientmap, &client_history); ent != NULL;
00364        ent = next) {
00365     if ((*ent)->action == GEOIP_CLIENT_CONNECT) {
00366       this = *ent;
00367       next = HT_NEXT_RMV(clientmap, &client_history, ent);
00368       tor_free(this);
00369     } else {
00370       next = HT_NEXT(clientmap, &client_history, ent);
00371     }
00372   }
00373 }
00374 
00380 #define REQUEST_SHARE_INTERVAL (15 * 60)
00381 
00384 static time_t last_time_determined_shares = 0;
00385 
00388 static double v2_share_times_seconds;
00389 
00392 static double v3_share_times_seconds;
00393 
00395 static int share_seconds;
00396 
00400 static void
00401 geoip_determine_shares(time_t now)
00402 {
00403   double v2_share = 0.0, v3_share = 0.0;
00404   if (router_get_my_share_of_directory_requests(&v2_share, &v3_share) < 0)
00405     return;
00406   if (last_time_determined_shares) {
00407     v2_share_times_seconds += v2_share *
00408         ((double) (now - last_time_determined_shares));
00409     v3_share_times_seconds += v3_share *
00410         ((double) (now - last_time_determined_shares));
00411     share_seconds += (int)(now - last_time_determined_shares);
00412   }
00413   last_time_determined_shares = now;
00414 }
00415 
00422 static int
00423 geoip_get_mean_shares(time_t now, double *v2_share_out,
00424                          double *v3_share_out)
00425 {
00426   geoip_determine_shares(now);
00427   if (!share_seconds)
00428     return -1;
00429   *v2_share_out = v2_share_times_seconds / ((double) share_seconds);
00430   *v3_share_out = v3_share_times_seconds / ((double) share_seconds);
00431   v2_share_times_seconds = v3_share_times_seconds = 0.0;
00432   share_seconds = 0;
00433   return 0;
00434 }
00435 
00439 void
00440 geoip_note_client_seen(geoip_client_action_t action,
00441                        const tor_addr_t *addr, time_t now)
00442 {
00443   const or_options_t *options = get_options();
00444   clientmap_entry_t lookup, *ent;
00445   if (action == GEOIP_CLIENT_CONNECT) {
00446     /* Only remember statistics as entry guard or as bridge. */
00447     if (!options->EntryStatistics &&
00448         (!(options->BridgeRelay && options->BridgeRecordUsageByCountry)))
00449       return;
00450   } else {
00451     if (options->BridgeRelay || options->BridgeAuthoritativeDir ||
00452         !options->DirReqStatistics)
00453       return;
00454   }
00455 
00456   tor_addr_copy(&lookup.addr, addr);
00457   lookup.action = (int)action;
00458   ent = HT_FIND(clientmap, &client_history, &lookup);
00459   if (! ent) {
00460     ent = tor_malloc_zero(sizeof(clientmap_entry_t));
00461     tor_addr_copy(&ent->addr, addr);
00462     ent->action = (int)action;
00463     HT_INSERT(clientmap, &client_history, ent);
00464   }
00465   if (now / 60 <= (int)MAX_LAST_SEEN_IN_MINUTES && now >= 0)
00466     ent->last_seen_in_minutes = (unsigned)(now/60);
00467   else
00468     ent->last_seen_in_minutes = 0;
00469 
00470   if (action == GEOIP_CLIENT_NETWORKSTATUS ||
00471       action == GEOIP_CLIENT_NETWORKSTATUS_V2) {
00472     int country_idx = geoip_get_country_by_addr(addr);
00473     if (country_idx < 0)
00474       country_idx = 0; 
00475     if (country_idx >= 0 && country_idx < smartlist_len(geoip_countries)) {
00476       geoip_country_t *country = smartlist_get(geoip_countries, country_idx);
00477       if (action == GEOIP_CLIENT_NETWORKSTATUS)
00478         ++country->n_v3_ns_requests;
00479       else
00480         ++country->n_v2_ns_requests;
00481     }
00482 
00483     /* Periodically determine share of requests that we should see */
00484     if (last_time_determined_shares + REQUEST_SHARE_INTERVAL < now)
00485       geoip_determine_shares(now);
00486   }
00487 }
00488 
00491 static int
00492 _remove_old_client_helper(struct clientmap_entry_t *ent, void *_cutoff)
00493 {
00494   time_t cutoff = *(time_t*)_cutoff / 60;
00495   if (ent->last_seen_in_minutes < cutoff) {
00496     tor_free(ent);
00497     return 1;
00498   } else {
00499     return 0;
00500   }
00501 }
00502 
00504 void
00505 geoip_remove_old_clients(time_t cutoff)
00506 {
00507   clientmap_HT_FOREACH_FN(&client_history,
00508                           _remove_old_client_helper,
00509                           &cutoff);
00510 }
00511 
00514 static uint32_t ns_v2_responses[GEOIP_NS_RESPONSE_NUM];
00515 
00518 static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
00519 
00523 void
00524 geoip_note_ns_response(geoip_client_action_t action,
00525                        geoip_ns_response_t response)
00526 {
00527   static int arrays_initialized = 0;
00528   if (!get_options()->DirReqStatistics)
00529     return;
00530   if (!arrays_initialized) {
00531     memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
00532     memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
00533     arrays_initialized = 1;
00534   }
00535   tor_assert(action == GEOIP_CLIENT_NETWORKSTATUS ||
00536              action == GEOIP_CLIENT_NETWORKSTATUS_V2);
00537   tor_assert(response < GEOIP_NS_RESPONSE_NUM);
00538   if (action == GEOIP_CLIENT_NETWORKSTATUS)
00539     ns_v3_responses[response]++;
00540   else
00541     ns_v2_responses[response]++;
00542 }
00543 
00547 #define MIN_IPS_TO_NOTE_COUNTRY 1
00548 
00550 #define MIN_IPS_TO_NOTE_ANYTHING 1
00551 
00553 #define IP_GRANULARITY 8
00554 
00556 typedef struct c_hist_t {
00557   char country[3]; 
00558   unsigned total; 
00559 } c_hist_t;
00560 
00564 static int
00565 _c_hist_compare(const void **_a, const void **_b)
00566 {
00567   const c_hist_t *a = *_a, *b = *_b;
00568   if (a->total > b->total)
00569     return -1;
00570   else if (a->total < b->total)
00571     return 1;
00572   else
00573     return strcmp(a->country, b->country);
00574 }
00575 
00579 #define DIRREQ_TIMEOUT (10*60)
00580 
00586 typedef struct dirreq_map_entry_t {
00587   HT_ENTRY(dirreq_map_entry_t) node;
00592   uint64_t dirreq_id;
00593   unsigned int state:3; 
00594   unsigned int type:1; 
00595   unsigned int completed:1; 
00596   unsigned int action:2; 
00598   struct timeval request_time;
00599   size_t response_size; 
00600   struct timeval completion_time; 
00601 } dirreq_map_entry_t;
00602 
00606 static HT_HEAD(dirreqmap, dirreq_map_entry_t) dirreq_map =
00607      HT_INITIALIZER();
00608 
00609 static int
00610 dirreq_map_ent_eq(const dirreq_map_entry_t *a,
00611                   const dirreq_map_entry_t *b)
00612 {
00613   return a->dirreq_id == b->dirreq_id && a->type == b->type;
00614 }
00615 
00616 /* DOCDOC dirreq_map_ent_hash */
00617 static unsigned
00618 dirreq_map_ent_hash(const dirreq_map_entry_t *entry)
00619 {
00620   unsigned u = (unsigned) entry->dirreq_id;
00621   u += entry->type << 20;
00622   return u;
00623 }
00624 
00625 HT_PROTOTYPE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash,
00626              dirreq_map_ent_eq);
00627 HT_GENERATE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash,
00628             dirreq_map_ent_eq, 0.6, malloc, realloc, free);
00629 
00633 static void
00634 _dirreq_map_put(dirreq_map_entry_t *entry, dirreq_type_t type,
00635                uint64_t dirreq_id)
00636 {
00637   dirreq_map_entry_t *old_ent;
00638   tor_assert(entry->type == type);
00639   tor_assert(entry->dirreq_id == dirreq_id);
00640 
00641   /* XXXX we could switch this to HT_INSERT some time, since it seems that
00642    * this bug doesn't happen. But since this function doesn't seem to be
00643    * critical-path, it's sane to leave it alone. */
00644   old_ent = HT_REPLACE(dirreqmap, &dirreq_map, entry);
00645   if (old_ent && old_ent != entry) {
00646     log_warn(LD_BUG, "Error when putting directory request into local "
00647              "map. There was already an entry for the same identifier.");
00648     return;
00649   }
00650 }
00651 
00655 static dirreq_map_entry_t *
00656 _dirreq_map_get(dirreq_type_t type, uint64_t dirreq_id)
00657 {
00658   dirreq_map_entry_t lookup;
00659   lookup.type = type;
00660   lookup.dirreq_id = dirreq_id;
00661   return HT_FIND(dirreqmap, &dirreq_map, &lookup);
00662 }
00663 
00668 void
00669 geoip_start_dirreq(uint64_t dirreq_id, size_t response_size,
00670                    geoip_client_action_t action, dirreq_type_t type)
00671 {
00672   dirreq_map_entry_t *ent;
00673   if (!get_options()->DirReqStatistics)
00674     return;
00675   ent = tor_malloc_zero(sizeof(dirreq_map_entry_t));
00676   ent->dirreq_id = dirreq_id;
00677   tor_gettimeofday(&ent->request_time);
00678   ent->response_size = response_size;
00679   ent->action = action;
00680   ent->type = type;
00681   _dirreq_map_put(ent, type, dirreq_id);
00682 }
00683 
00690 void
00691 geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
00692                           dirreq_state_t new_state)
00693 {
00694   dirreq_map_entry_t *ent;
00695   if (!get_options()->DirReqStatistics)
00696     return;
00697   ent = _dirreq_map_get(type, dirreq_id);
00698   if (!ent)
00699     return;
00700   if (new_state == DIRREQ_IS_FOR_NETWORK_STATUS)
00701     return;
00702   if (new_state - 1 != ent->state)
00703     return;
00704   ent->state = new_state;
00705   if ((type == DIRREQ_DIRECT &&
00706          new_state == DIRREQ_FLUSHING_DIR_CONN_FINISHED) ||
00707       (type == DIRREQ_TUNNELED &&
00708          new_state == DIRREQ_OR_CONN_BUFFER_FLUSHED)) {
00709     tor_gettimeofday(&ent->completion_time);
00710     ent->completed = 1;
00711   }
00712 }
00713 
00719 static char *
00720 geoip_get_dirreq_history(geoip_client_action_t action,
00721                            dirreq_type_t type)
00722 {
00723   char *result = NULL;
00724   smartlist_t *dirreq_completed = NULL;
00725   uint32_t complete = 0, timeouts = 0, running = 0;
00726   int bufsize = 1024, written;
00727   dirreq_map_entry_t **ptr, **next, *ent;
00728   struct timeval now;
00729 
00730   tor_gettimeofday(&now);
00731   if (action != GEOIP_CLIENT_NETWORKSTATUS &&
00732       action != GEOIP_CLIENT_NETWORKSTATUS_V2)
00733     return NULL;
00734   dirreq_completed = smartlist_new();
00735   for (ptr = HT_START(dirreqmap, &dirreq_map); ptr; ptr = next) {
00736     ent = *ptr;
00737     if (ent->action != action || ent->type != type) {
00738       next = HT_NEXT(dirreqmap, &dirreq_map, ptr);
00739       continue;
00740     } else {
00741       if (ent->completed) {
00742         smartlist_add(dirreq_completed, ent);
00743         complete++;
00744         next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
00745       } else {
00746         if (tv_mdiff(&ent->request_time, &now) / 1000 > DIRREQ_TIMEOUT)
00747           timeouts++;
00748         else
00749           running++;
00750         next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
00751         tor_free(ent);
00752       }
00753     }
00754   }
00755 #define DIR_REQ_GRANULARITY 4
00756   complete = round_uint32_to_next_multiple_of(complete,
00757                                               DIR_REQ_GRANULARITY);
00758   timeouts = round_uint32_to_next_multiple_of(timeouts,
00759                                               DIR_REQ_GRANULARITY);
00760   running = round_uint32_to_next_multiple_of(running,
00761                                              DIR_REQ_GRANULARITY);
00762   result = tor_malloc_zero(bufsize);
00763   written = tor_snprintf(result, bufsize, "complete=%u,timeout=%u,"
00764                          "running=%u", complete, timeouts, running);
00765   if (written < 0) {
00766     tor_free(result);
00767     goto done;
00768   }
00769 
00770 #define MIN_DIR_REQ_RESPONSES 16
00771   if (complete >= MIN_DIR_REQ_RESPONSES) {
00772     uint32_t *dltimes;
00773     /* We may have rounded 'completed' up.  Here we want to use the
00774      * real value. */
00775     complete = smartlist_len(dirreq_completed);
00776     dltimes = tor_malloc_zero(sizeof(uint32_t) * complete);
00777     SMARTLIST_FOREACH_BEGIN(dirreq_completed, dirreq_map_entry_t *, ent) {
00778       uint32_t bytes_per_second;
00779       uint32_t time_diff = (uint32_t) tv_mdiff(&ent->request_time,
00780                                                &ent->completion_time);
00781       if (time_diff == 0)
00782         time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible
00783                         * by law of nature or something, but a milisecond
00784                         * is a bit greater than "instantly" */
00785       bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff);
00786       dltimes[ent_sl_idx] = bytes_per_second;
00787     } SMARTLIST_FOREACH_END(ent);
00788     median_uint32(dltimes, complete); /* sorts as a side effect. */
00789     written = tor_snprintf(result + written, bufsize - written,
00790                            ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u,"
00791                            "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u",
00792                            dltimes[0],
00793                            dltimes[1*complete/10-1],
00794                            dltimes[2*complete/10-1],
00795                            dltimes[1*complete/4-1],
00796                            dltimes[3*complete/10-1],
00797                            dltimes[4*complete/10-1],
00798                            dltimes[5*complete/10-1],
00799                            dltimes[6*complete/10-1],
00800                            dltimes[7*complete/10-1],
00801                            dltimes[3*complete/4-1],
00802                            dltimes[8*complete/10-1],
00803                            dltimes[9*complete/10-1],
00804                            dltimes[complete-1]);
00805     if (written<0)
00806       tor_free(result);
00807     tor_free(dltimes);
00808   }
00809  done:
00810   SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent,
00811                     tor_free(ent));
00812   smartlist_free(dirreq_completed);
00813   return result;
00814 }
00815 
00822 char *
00823 geoip_get_client_history(geoip_client_action_t action)
00824 {
00825   char *result = NULL;
00826   unsigned granularity = IP_GRANULARITY;
00827   smartlist_t *chunks = NULL;
00828   smartlist_t *entries = NULL;
00829   int n_countries = geoip_get_n_countries();
00830   int i;
00831   clientmap_entry_t **ent;
00832   unsigned *counts = NULL;
00833   unsigned total = 0;
00834 
00835   if (!geoip_is_loaded())
00836     return NULL;
00837 
00838   counts = tor_malloc_zero(sizeof(unsigned)*n_countries);
00839   HT_FOREACH(ent, clientmap, &client_history) {
00840     int country;
00841     if ((*ent)->action != (int)action)
00842       continue;
00843     country = geoip_get_country_by_addr(&(*ent)->addr);
00844     if (country < 0)
00845       country = 0; 
00846     tor_assert(0 <= country && country < n_countries);
00847     ++counts[country];
00848     ++total;
00849   }
00850   /* Don't record anything if we haven't seen enough IPs. */
00851   if (total < MIN_IPS_TO_NOTE_ANYTHING)
00852     goto done;
00853   /* Make a list of c_hist_t */
00854   entries = smartlist_new();
00855   for (i = 0; i < n_countries; ++i) {
00856     unsigned c = counts[i];
00857     const char *countrycode;
00858     c_hist_t *ent;
00859     /* Only report a country if it has a minimum number of IPs. */
00860     if (c >= MIN_IPS_TO_NOTE_COUNTRY) {
00861       c = round_to_next_multiple_of(c, granularity);
00862       countrycode = geoip_get_country_name(i);
00863       ent = tor_malloc(sizeof(c_hist_t));
00864       strlcpy(ent->country, countrycode, sizeof(ent->country));
00865       ent->total = c;
00866       smartlist_add(entries, ent);
00867     }
00868   }
00869   /* Sort entries. Note that we must do this _AFTER_ rounding, or else
00870    * the sort order could leak info. */
00871   smartlist_sort(entries, _c_hist_compare);
00872 
00873   /* Build the result. */
00874   chunks = smartlist_new();
00875   SMARTLIST_FOREACH(entries, c_hist_t *, ch, {
00876       smartlist_add_asprintf(chunks, "%s=%u", ch->country, ch->total);
00877   });
00878   result = smartlist_join_strings(chunks, ",", 0, NULL);
00879  done:
00880   tor_free(counts);
00881   if (chunks) {
00882     SMARTLIST_FOREACH(chunks, char *, c, tor_free(c));
00883     smartlist_free(chunks);
00884   }
00885   if (entries) {
00886     SMARTLIST_FOREACH(entries, c_hist_t *, c, tor_free(c));
00887     smartlist_free(entries);
00888   }
00889   return result;
00890 }
00891 
00895 char *
00896 geoip_get_request_history(geoip_client_action_t action)
00897 {
00898   smartlist_t *entries, *strings;
00899   char *result;
00900   unsigned granularity = IP_GRANULARITY;
00901 
00902   if (action != GEOIP_CLIENT_NETWORKSTATUS &&
00903       action != GEOIP_CLIENT_NETWORKSTATUS_V2)
00904     return NULL;
00905   if (!geoip_countries)
00906     return NULL;
00907 
00908   entries = smartlist_new();
00909   SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
00910       uint32_t tot = 0;
00911       c_hist_t *ent;
00912       tot = (action == GEOIP_CLIENT_NETWORKSTATUS) ?
00913             c->n_v3_ns_requests : c->n_v2_ns_requests;
00914       if (!tot)
00915         continue;
00916       ent = tor_malloc_zero(sizeof(c_hist_t));
00917       strlcpy(ent->country, c->countrycode, sizeof(ent->country));
00918       ent->total = round_to_next_multiple_of(tot, granularity);
00919       smartlist_add(entries, ent);
00920   });
00921   smartlist_sort(entries, _c_hist_compare);
00922 
00923   strings = smartlist_new();
00924   SMARTLIST_FOREACH(entries, c_hist_t *, ent, {
00925       smartlist_add_asprintf(strings, "%s=%u", ent->country, ent->total);
00926   });
00927   result = smartlist_join_strings(strings, ",", 0, NULL);
00928   SMARTLIST_FOREACH(strings, char *, cp, tor_free(cp));
00929   SMARTLIST_FOREACH(entries, c_hist_t *, ent, tor_free(ent));
00930   smartlist_free(strings);
00931   smartlist_free(entries);
00932   return result;
00933 }
00934 
00937 static time_t start_of_dirreq_stats_interval;
00938 
00940 void
00941 geoip_dirreq_stats_init(time_t now)
00942 {
00943   start_of_dirreq_stats_interval = now;
00944 }
00945 
00947 void
00948 geoip_reset_dirreq_stats(time_t now)
00949 {
00950   SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
00951       c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
00952   });
00953   {
00954     clientmap_entry_t **ent, **next, *this;
00955     for (ent = HT_START(clientmap, &client_history); ent != NULL;
00956          ent = next) {
00957       if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS ||
00958           (*ent)->action == GEOIP_CLIENT_NETWORKSTATUS_V2) {
00959         this = *ent;
00960         next = HT_NEXT_RMV(clientmap, &client_history, ent);
00961         tor_free(this);
00962       } else {
00963         next = HT_NEXT(clientmap, &client_history, ent);
00964       }
00965     }
00966   }
00967   v2_share_times_seconds = v3_share_times_seconds = 0.0;
00968   last_time_determined_shares = 0;
00969   share_seconds = 0;
00970   memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
00971   memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
00972   {
00973     dirreq_map_entry_t **ent, **next, *this;
00974     for (ent = HT_START(dirreqmap, &dirreq_map); ent != NULL; ent = next) {
00975       this = *ent;
00976       next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ent);
00977       tor_free(this);
00978     }
00979   }
00980   start_of_dirreq_stats_interval = now;
00981 }
00982 
00985 void
00986 geoip_dirreq_stats_term(void)
00987 {
00988   geoip_reset_dirreq_stats(0);
00989 }
00990 
00994 char *
00995 geoip_format_dirreq_stats(time_t now)
00996 {
00997   char t[ISO_TIME_LEN+1];
00998   double v2_share = 0.0, v3_share = 0.0;
00999   int i;
01000   char *v3_ips_string, *v2_ips_string, *v3_reqs_string, *v2_reqs_string,
01001        *v2_share_string = NULL, *v3_share_string = NULL,
01002        *v3_direct_dl_string, *v2_direct_dl_string,
01003        *v3_tunneled_dl_string, *v2_tunneled_dl_string;
01004   char *result;
01005 
01006   if (!start_of_dirreq_stats_interval)
01007     return NULL; /* Not initialized. */
01008 
01009   tor_assert(now >= start_of_dirreq_stats_interval);
01010 
01011   format_iso_time(t, now);
01012   v2_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
01013   v3_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS);
01014   v2_reqs_string = geoip_get_request_history(
01015                    GEOIP_CLIENT_NETWORKSTATUS_V2);
01016   v3_reqs_string = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
01017 
01018 #define RESPONSE_GRANULARITY 8
01019   for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
01020     ns_v2_responses[i] = round_uint32_to_next_multiple_of(
01021                                ns_v2_responses[i], RESPONSE_GRANULARITY);
01022     ns_v3_responses[i] = round_uint32_to_next_multiple_of(
01023                                ns_v3_responses[i], RESPONSE_GRANULARITY);
01024   }
01025 #undef RESPONSE_GRANULARITY
01026 
01027   if (!geoip_get_mean_shares(now, &v2_share, &v3_share)) {
01028     tor_asprintf(&v2_share_string, "dirreq-v2-share %0.2f%%\n",
01029                  v2_share*100);
01030     tor_asprintf(&v3_share_string, "dirreq-v3-share %0.2f%%\n",
01031                  v3_share*100);
01032   }
01033 
01034   v2_direct_dl_string = geoip_get_dirreq_history(
01035                         GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_DIRECT);
01036   v3_direct_dl_string = geoip_get_dirreq_history(
01037                         GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_DIRECT);
01038 
01039   v2_tunneled_dl_string = geoip_get_dirreq_history(
01040                           GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_TUNNELED);
01041   v3_tunneled_dl_string = geoip_get_dirreq_history(
01042                           GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_TUNNELED);
01043 
01044   /* Put everything together into a single string. */
01045   tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n"
01046               "dirreq-v3-ips %s\n"
01047               "dirreq-v2-ips %s\n"
01048               "dirreq-v3-reqs %s\n"
01049               "dirreq-v2-reqs %s\n"
01050               "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
01051                    "not-found=%u,not-modified=%u,busy=%u\n"
01052               "dirreq-v2-resp ok=%u,unavailable=%u,"
01053                    "not-found=%u,not-modified=%u,busy=%u\n"
01054               "%s"
01055               "%s"
01056               "dirreq-v3-direct-dl %s\n"
01057               "dirreq-v2-direct-dl %s\n"
01058               "dirreq-v3-tunneled-dl %s\n"
01059               "dirreq-v2-tunneled-dl %s\n",
01060               t,
01061               (unsigned) (now - start_of_dirreq_stats_interval),
01062               v3_ips_string ? v3_ips_string : "",
01063               v2_ips_string ? v2_ips_string : "",
01064               v3_reqs_string ? v3_reqs_string : "",
01065               v2_reqs_string ? v2_reqs_string : "",
01066               ns_v3_responses[GEOIP_SUCCESS],
01067               ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
01068               ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
01069               ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
01070               ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
01071               ns_v3_responses[GEOIP_REJECT_BUSY],
01072               ns_v2_responses[GEOIP_SUCCESS],
01073               ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
01074               ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
01075               ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
01076               ns_v2_responses[GEOIP_REJECT_BUSY],
01077               v2_share_string ? v2_share_string : "",
01078               v3_share_string ? v3_share_string : "",
01079               v3_direct_dl_string ? v3_direct_dl_string : "",
01080               v2_direct_dl_string ? v2_direct_dl_string : "",
01081               v3_tunneled_dl_string ? v3_tunneled_dl_string : "",
01082               v2_tunneled_dl_string ? v2_tunneled_dl_string : "");
01083 
01084   /* Free partial strings. */
01085   tor_free(v3_ips_string);
01086   tor_free(v2_ips_string);
01087   tor_free(v3_reqs_string);
01088   tor_free(v2_reqs_string);
01089   tor_free(v2_share_string);
01090   tor_free(v3_share_string);
01091   tor_free(v3_direct_dl_string);
01092   tor_free(v2_direct_dl_string);
01093   tor_free(v3_tunneled_dl_string);
01094   tor_free(v2_tunneled_dl_string);
01095 
01096   return result;
01097 }
01098 
01104 time_t
01105 geoip_dirreq_stats_write(time_t now)
01106 {
01107   char *statsdir = NULL, *filename = NULL, *str = NULL;
01108 
01109   if (!start_of_dirreq_stats_interval)
01110     return 0; /* Not initialized. */
01111   if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now)
01112     goto done; /* Not ready to write. */
01113 
01114   /* Discard all items in the client history that are too old. */
01115   geoip_remove_old_clients(start_of_dirreq_stats_interval);
01116 
01117   /* Generate history string .*/
01118   str = geoip_format_dirreq_stats(now);
01119 
01120   /* Write dirreq-stats string to disk. */
01121   statsdir = get_datadir_fname("stats");
01122   if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
01123     log_warn(LD_HIST, "Unable to create stats/ directory!");
01124     goto done;
01125   }
01126   filename = get_datadir_fname2("stats", "dirreq-stats");
01127   if (write_str_to_file(filename, str, 0) < 0)
01128     log_warn(LD_HIST, "Unable to write dirreq statistics to disk!");
01129 
01130   /* Reset measurement interval start. */
01131   geoip_reset_dirreq_stats(now);
01132 
01133  done:
01134   tor_free(statsdir);
01135   tor_free(filename);
01136   tor_free(str);
01137   return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL;
01138 }
01139 
01142 static time_t start_of_bridge_stats_interval;
01143 
01145 void
01146 geoip_bridge_stats_init(time_t now)
01147 {
01148   start_of_bridge_stats_interval = now;
01149 }
01150 
01153 void
01154 geoip_bridge_stats_term(void)
01155 {
01156   client_history_clear();
01157   start_of_bridge_stats_interval = 0;
01158 }
01159 
01163 static int
01164 validate_bridge_stats(const char *stats_str, time_t now)
01165 {
01166   char stats_end_str[ISO_TIME_LEN+1], stats_start_str[ISO_TIME_LEN+1],
01167        *eos;
01168 
01169   const char *BRIDGE_STATS_END = "bridge-stats-end ";
01170   const char *BRIDGE_IPS = "bridge-ips ";
01171   const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
01172   const char *tmp;
01173   time_t stats_end_time;
01174   int seconds;
01175   tor_assert(stats_str);
01176 
01177   /* Parse timestamp and number of seconds from
01178     "bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */
01179   tmp = find_str_at_start_of_line(stats_str, BRIDGE_STATS_END);
01180   if (!tmp)
01181     return 0;
01182   tmp += strlen(BRIDGE_STATS_END);
01183 
01184   if (strlen(tmp) < ISO_TIME_LEN + 6)
01185     return 0;
01186   strlcpy(stats_end_str, tmp, sizeof(stats_end_str));
01187   if (parse_iso_time(stats_end_str, &stats_end_time) < 0)
01188     return 0;
01189   if (stats_end_time < now - (25*60*60) ||
01190       stats_end_time > now + (1*60*60))
01191     return 0;
01192   seconds = (int)strtol(tmp + ISO_TIME_LEN + 2, &eos, 10);
01193   if (!eos || seconds < 23*60*60)
01194     return 0;
01195   format_iso_time(stats_start_str, stats_end_time - seconds);
01196 
01197   /* Parse: "bridge-ips CC=N,CC=N,..." */
01198   tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS);
01199   if (!tmp) {
01200     /* Look if there is an empty "bridge-ips" line */
01201     tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS_EMPTY_LINE);
01202     if (!tmp)
01203       return 0;
01204   }
01205 
01206   return 1;
01207 }
01208 
01211 static char *bridge_stats_extrainfo = NULL;
01212 
01216 char *
01217 geoip_format_bridge_stats(time_t now)
01218 {
01219   char *out = NULL, *data = NULL;
01220   long duration = now - start_of_bridge_stats_interval;
01221   char written[ISO_TIME_LEN+1];
01222 
01223   if (duration < 0)
01224     return NULL;
01225   if (!start_of_bridge_stats_interval)
01226     return NULL; /* Not initialized. */
01227 
01228   format_iso_time(written, now);
01229   data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
01230 
01231   tor_asprintf(&out,
01232                "bridge-stats-end %s (%ld s)\n"
01233                "bridge-ips %s\n",
01234                written, duration,
01235                data ? data : "");
01236   tor_free(data);
01237 
01238   return out;
01239 }
01240 
01244 static char *
01245 format_bridge_stats_controller(time_t now)
01246 {
01247   char *out = NULL, *data = NULL;
01248   char started[ISO_TIME_LEN+1];
01249   (void) now;
01250 
01251   format_iso_time(started, start_of_bridge_stats_interval);
01252   data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
01253 
01254   tor_asprintf(&out,
01255                "TimeStarted=\"%s\" CountrySummary=%s",
01256                started, data ? data : "");
01257   tor_free(data);
01258   return out;
01259 }
01260 
01263 time_t
01264 geoip_bridge_stats_write(time_t now)
01265 {
01266   char *filename = NULL, *val = NULL, *statsdir = NULL;
01267 
01268   /* Check if 24 hours have passed since starting measurements. */
01269   if (now < start_of_bridge_stats_interval + WRITE_STATS_INTERVAL)
01270     return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL;
01271 
01272   /* Discard all items in the client history that are too old. */
01273   geoip_remove_old_clients(start_of_bridge_stats_interval);
01274 
01275   /* Generate formatted string */
01276   val = geoip_format_bridge_stats(now);
01277   if (val == NULL)
01278     goto done;
01279 
01280   /* Update the stored value. */
01281   tor_free(bridge_stats_extrainfo);
01282   bridge_stats_extrainfo = val;
01283   start_of_bridge_stats_interval = now;
01284 
01285   /* Write it to disk. */
01286   statsdir = get_datadir_fname("stats");
01287   if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
01288     goto done;
01289   filename = get_datadir_fname2("stats", "bridge-stats");
01290 
01291   write_str_to_file(filename, bridge_stats_extrainfo, 0);
01292 
01293   /* Tell the controller, "hey, there are clients!" */
01294   {
01295     char *controller_str = format_bridge_stats_controller(now);
01296     if (controller_str)
01297       control_event_clients_seen(controller_str);
01298     tor_free(controller_str);
01299   }
01300  done:
01301   tor_free(filename);
01302   tor_free(statsdir);
01303 
01304   return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL;
01305 }
01306 
01310 static void
01311 load_bridge_stats(time_t now)
01312 {
01313   char *fname, *contents;
01314   if (bridge_stats_extrainfo)
01315     return;
01316 
01317   fname = get_datadir_fname2("stats", "bridge-stats");
01318   contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
01319   if (contents && validate_bridge_stats(contents, now))
01320     bridge_stats_extrainfo = contents;
01321 
01322   tor_free(fname);
01323 }
01324 
01327 const char *
01328 geoip_get_bridge_stats_extrainfo(time_t now)
01329 {
01330   load_bridge_stats(now);
01331   return bridge_stats_extrainfo;
01332 }
01333 
01336 char *
01337 geoip_get_bridge_stats_controller(time_t now)
01338 {
01339   return format_bridge_stats_controller(now);
01340 }
01341 
01344 static time_t start_of_entry_stats_interval;
01345 
01347 void
01348 geoip_entry_stats_init(time_t now)
01349 {
01350   start_of_entry_stats_interval = now;
01351 }
01352 
01354 void
01355 geoip_reset_entry_stats(time_t now)
01356 {
01357   client_history_clear();
01358   start_of_entry_stats_interval = now;
01359 }
01360 
01363 void
01364 geoip_entry_stats_term(void)
01365 {
01366   geoip_reset_entry_stats(0);
01367 }
01368 
01372 char *
01373 geoip_format_entry_stats(time_t now)
01374 {
01375   char t[ISO_TIME_LEN+1];
01376   char *data = NULL;
01377   char *result;
01378 
01379   if (!start_of_entry_stats_interval)
01380     return NULL; /* Not initialized. */
01381 
01382   tor_assert(now >= start_of_entry_stats_interval);
01383 
01384   data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
01385   format_iso_time(t, now);
01386   tor_asprintf(&result, "entry-stats-end %s (%u s)\nentry-ips %s\n",
01387               t, (unsigned) (now - start_of_entry_stats_interval),
01388               data ? data : "");
01389   tor_free(data);
01390   return result;
01391 }
01392 
01397 time_t
01398 geoip_entry_stats_write(time_t now)
01399 {
01400   char *statsdir = NULL, *filename = NULL, *str = NULL;
01401 
01402   if (!start_of_entry_stats_interval)
01403     return 0; /* Not initialized. */
01404   if (start_of_entry_stats_interval + WRITE_STATS_INTERVAL > now)
01405     goto done; /* Not ready to write. */
01406 
01407   /* Discard all items in the client history that are too old. */
01408   geoip_remove_old_clients(start_of_entry_stats_interval);
01409 
01410   /* Generate history string .*/
01411   str = geoip_format_entry_stats(now);
01412 
01413   /* Write entry-stats string to disk. */
01414   statsdir = get_datadir_fname("stats");
01415   if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
01416     log_warn(LD_HIST, "Unable to create stats/ directory!");
01417     goto done;
01418   }
01419   filename = get_datadir_fname2("stats", "entry-stats");
01420   if (write_str_to_file(filename, str, 0) < 0)
01421     log_warn(LD_HIST, "Unable to write entry statistics to disk!");
01422 
01423   /* Reset measurement interval start. */
01424   geoip_reset_entry_stats(now);
01425 
01426  done:
01427   tor_free(statsdir);
01428   tor_free(filename);
01429   tor_free(str);
01430   return start_of_entry_stats_interval + WRITE_STATS_INTERVAL;
01431 }
01432 
01434 int
01435 getinfo_helper_geoip(control_connection_t *control_conn,
01436                      const char *question, char **answer,
01437                      const char **errmsg)
01438 {
01439   (void)control_conn;
01440   if (!geoip_is_loaded()) {
01441     *errmsg = "GeoIP data not loaded";
01442     return -1;
01443   }
01444   if (!strcmpstart(question, "ip-to-country/")) {
01445     int c;
01446     uint32_t ip;
01447     struct in_addr in;
01448     question += strlen("ip-to-country/");
01449     if (tor_inet_aton(question, &in) != 0) {
01450       ip = ntohl(in.s_addr);
01451       c = geoip_get_country_by_ip(ip);
01452       *answer = tor_strdup(geoip_get_country_name(c));
01453     }
01454   }
01455   return 0;
01456 }
01457 
01459 static void
01460 clear_geoip_db(void)
01461 {
01462   if (geoip_countries) {
01463     SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, tor_free(c));
01464     smartlist_free(geoip_countries);
01465   }
01466 
01467   strmap_free(country_idxplus1_by_lc_code, NULL);
01468   if (geoip_entries) {
01469     SMARTLIST_FOREACH(geoip_entries, geoip_entry_t *, ent, tor_free(ent));
01470     smartlist_free(geoip_entries);
01471   }
01472   geoip_countries = NULL;
01473   country_idxplus1_by_lc_code = NULL;
01474   geoip_entries = NULL;
01475 }
01476 
01478 void
01479 geoip_free_all(void)
01480 {
01481   {
01482     clientmap_entry_t **ent, **next, *this;
01483     for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) {
01484       this = *ent;
01485       next = HT_NEXT_RMV(clientmap, &client_history, ent);
01486       tor_free(this);
01487     }
01488     HT_CLEAR(clientmap, &client_history);
01489   }
01490   {
01491     dirreq_map_entry_t **ent, **next, *this;
01492     for (ent = HT_START(dirreqmap, &dirreq_map); ent != NULL; ent = next) {
01493       this = *ent;
01494       next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ent);
01495       tor_free(this);
01496     }
01497     HT_CLEAR(dirreqmap, &dirreq_map);
01498   }
01499 
01500   clear_geoip_db();
01501 }
01502