1 /* Copyright (c) 2007-2011, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * \brief Functions related to maintaining an IP-to-country database;
7 * to summarizing client connections by country to entry guards, bridges,
8 * and directory servers; and for statistics on answering network status
19 #include "routerlist.h"
21 static void clear_geoip_db(void);
22 static void init_geoip_countries(void);
24 /** An entry from the GeoIP file: maps an IP range to a country. */
25 typedef struct geoip_entry_t
{
26 uint32_t ip_low
; /**< The lowest IP in the range, in host order */
27 uint32_t ip_high
; /**< The highest IP in the range, in host order */
28 intptr_t country
; /**< An index into geoip_countries */
31 /** A per-country record for GeoIP request history. */
32 typedef struct geoip_country_t
{
34 uint32_t n_v2_ns_requests
;
35 uint32_t n_v3_ns_requests
;
38 /** A list of geoip_country_t */
39 static smartlist_t
*geoip_countries
= NULL
;
40 /** A map from lowercased country codes to their position in geoip_countries.
41 * The index is encoded in the pointer, and 1 is added so that NULL can mean
43 static strmap_t
*country_idxplus1_by_lc_code
= NULL
;
44 /** A list of all known geoip_entry_t, sorted by ip_low. */
45 static smartlist_t
*geoip_entries
= NULL
;
47 /** Return the index of the <b>country</b>'s entry in the GeoIP DB
48 * if it is a valid 2-letter country code, otherwise return -1.
51 geoip_get_country(const char *country
)
56 _idxplus1
= strmap_get_lc(country_idxplus1_by_lc_code
, country
);
60 idx
= ((uintptr_t)_idxplus1
)-1;
61 return (country_t
)idx
;
64 /** Add an entry to the GeoIP table, mapping all IPs between <b>low</b> and
65 * <b>high</b>, inclusive, to the 2-letter country code <b>country</b>.
68 geoip_add_entry(uint32_t low
, uint32_t high
, const char *country
)
77 _idxplus1
= strmap_get_lc(country_idxplus1_by_lc_code
, country
);
80 geoip_country_t
*c
= tor_malloc_zero(sizeof(geoip_country_t
));
81 strlcpy(c
->countrycode
, country
, sizeof(c
->countrycode
));
82 tor_strlower(c
->countrycode
);
83 smartlist_add(geoip_countries
, c
);
84 idx
= smartlist_len(geoip_countries
) - 1;
85 strmap_set_lc(country_idxplus1_by_lc_code
, country
, (void*)(idx
+1));
87 idx
= ((uintptr_t)_idxplus1
)-1;
90 geoip_country_t
*c
= smartlist_get(geoip_countries
, idx
);
91 tor_assert(!strcasecmp(c
->countrycode
, country
));
93 ent
= tor_malloc_zero(sizeof(geoip_entry_t
));
97 smartlist_add(geoip_entries
, ent
);
100 /** Add an entry to the GeoIP table, parsing it from <b>line</b>. The
101 * format is as for geoip_load_file(). */
103 geoip_parse_entry(const char *line
)
105 unsigned int low
, high
;
107 if (!geoip_countries
)
108 init_geoip_countries();
110 geoip_entries
= smartlist_create();
112 while (TOR_ISSPACE(*line
))
116 if (sscanf(line
,"%u,%u,%2s", &low
, &high
, b
) == 3) {
117 geoip_add_entry(low
, high
, b
);
119 } else if (sscanf(line
,"\"%u\",\"%u\",\"%2s\",", &low
, &high
, b
) == 3) {
120 geoip_add_entry(low
, high
, b
);
123 log_warn(LD_GENERAL
, "Unable to parse line from GEOIP file: %s",
129 /** Sorting helper: return -1, 1, or 0 based on comparison of two
132 _geoip_compare_entries(const void **_a
, const void **_b
)
134 const geoip_entry_t
*a
= *_a
, *b
= *_b
;
135 if (a
->ip_low
< b
->ip_low
)
137 else if (a
->ip_low
> b
->ip_low
)
143 /** bsearch helper: return -1, 1, or 0 based on comparison of an IP (a pointer
144 * to a uint32_t in host order) to a geoip_entry_t */
146 _geoip_compare_key_to_entry(const void *_key
, const void **_member
)
148 /* No alignment issue here, since _key really is a pointer to uint32_t */
149 const uint32_t addr
= *(uint32_t *)_key
;
150 const geoip_entry_t
*entry
= *_member
;
151 if (addr
< entry
->ip_low
)
153 else if (addr
> entry
->ip_high
)
159 /** Return 1 if we should collect geoip stats on bridge users, and
160 * include them in our extrainfo descriptor. Else return 0. */
162 should_record_bridge_info(or_options_t
*options
)
164 return options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
;
167 /** Set up a new list of geoip countries with no countries (yet) set in it,
168 * except for the unknown country.
171 init_geoip_countries(void)
173 geoip_country_t
*geoip_unresolved
;
174 geoip_countries
= smartlist_create();
175 /* Add a geoip_country_t for requests that could not be resolved to a
176 * country as first element (index 0) to geoip_countries. */
177 geoip_unresolved
= tor_malloc_zero(sizeof(geoip_country_t
));
178 strlcpy(geoip_unresolved
->countrycode
, "??",
179 sizeof(geoip_unresolved
->countrycode
));
180 smartlist_add(geoip_countries
, geoip_unresolved
);
181 country_idxplus1_by_lc_code
= strmap_new();
182 strmap_set_lc(country_idxplus1_by_lc_code
, "??", (void*)(1));
185 /** Clear the GeoIP database and reload it from the file
186 * <b>filename</b>. Return 0 on success, -1 on failure.
188 * Recognized line formats are:
189 * INTIPLOW,INTIPHIGH,CC
191 * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME"
192 * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned
193 * integers, and CC is a country code.
195 * It also recognizes, and skips over, blank lines and lines that start
196 * with '#' (comments).
199 geoip_load_file(const char *filename
, or_options_t
*options
)
202 const char *msg
= "";
203 int severity
= options_need_geoip_info(options
, &msg
) ? LOG_WARN
: LOG_INFO
;
205 if (!(f
= fopen(filename
, "r"))) {
206 log_fn(severity
, LD_GENERAL
, "Failed to open GEOIP file %s. %s",
210 if (!geoip_countries
)
211 init_geoip_countries();
213 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, e
, tor_free(e
));
214 smartlist_free(geoip_entries
);
216 geoip_entries
= smartlist_create();
217 log_notice(LD_GENERAL
, "Parsing GEOIP file %s.", filename
);
220 if (fgets(buf
, (int)sizeof(buf
), f
) == NULL
)
222 /* FFFF track full country name. */
223 geoip_parse_entry(buf
);
225 /*XXXX abort and return -1 if no entries/illformed?*/
228 smartlist_sort(geoip_entries
, _geoip_compare_entries
);
230 /* Okay, now we need to maybe change our mind about what is in which
232 refresh_all_country_info();
237 /** Given an IP address in host order, return a number representing the
238 * country to which that address belongs, -1 for "No geoip information
239 * available", or 0 for the 'unknown country'. The return value will always
240 * be less than geoip_get_n_countries(). To decode it, call
241 * geoip_get_country_name().
244 geoip_get_country_by_ip(uint32_t ipaddr
)
249 ent
= smartlist_bsearch(geoip_entries
, &ipaddr
, _geoip_compare_key_to_entry
);
250 return ent
? (int)ent
->country
: 0;
253 /** Return the number of countries recognized by the GeoIP database. */
255 geoip_get_n_countries(void)
257 if (!geoip_countries
)
258 init_geoip_countries();
259 return (int) smartlist_len(geoip_countries
);
262 /** Return the two-letter country code associated with the number <b>num</b>,
263 * or "??" for an unknown value. */
265 geoip_get_country_name(country_t num
)
267 if (geoip_countries
&& num
>= 0 && num
< smartlist_len(geoip_countries
)) {
268 geoip_country_t
*c
= smartlist_get(geoip_countries
, num
);
269 return c
->countrycode
;
274 /** Return true iff we have loaded a GeoIP database.*/
276 geoip_is_loaded(void)
278 return geoip_countries
!= NULL
&& geoip_entries
!= NULL
;
281 /** Entry in a map from IP address to the last time we've seen an incoming
282 * connection from that IP address. Used by bridges only, to track which
283 * countries have them blocked. */
284 typedef struct clientmap_entry_t
{
285 HT_ENTRY(clientmap_entry_t
) node
;
287 /** Time when we last saw this IP address, in MINUTES since the epoch.
289 * (This will run out of space around 4011 CE. If Tor is still in use around
290 * 4000 CE, please remember to add more bits to last_seen_in_minutes.) */
291 unsigned int last_seen_in_minutes
:30;
292 unsigned int action
:2;
295 /** Largest allowable value for last_seen_in_minutes. (It's a 30-bit field,
296 * so it can hold up to (1u<<30)-1, or 0x3fffffffu.
298 #define MAX_LAST_SEEN_IN_MINUTES 0X3FFFFFFFu
300 /** Map from client IP address to last time seen. */
301 static HT_HEAD(clientmap
, clientmap_entry_t
) client_history
=
304 /** Hashtable helper: compute a hash of a clientmap_entry_t. */
305 static INLINE
unsigned
306 clientmap_entry_hash(const clientmap_entry_t
*a
)
308 return ht_improve_hash((unsigned) a
->ipaddr
);
310 /** Hashtable helper: compare two clientmap_entry_t values for equality. */
312 clientmap_entries_eq(const clientmap_entry_t
*a
, const clientmap_entry_t
*b
)
314 return a
->ipaddr
== b
->ipaddr
&& a
->action
== b
->action
;
317 HT_PROTOTYPE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
318 clientmap_entries_eq
);
319 HT_GENERATE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
320 clientmap_entries_eq
, 0.6, malloc
, realloc
, free
);
322 /** Clear history of connecting clients used by entry and bridge stats. */
324 client_history_clear(void)
326 clientmap_entry_t
**ent
, **next
, *this;
327 for (ent
= HT_START(clientmap
, &client_history
); ent
!= NULL
;
329 if ((*ent
)->action
== GEOIP_CLIENT_CONNECT
) {
331 next
= HT_NEXT_RMV(clientmap
, &client_history
, ent
);
334 next
= HT_NEXT(clientmap
, &client_history
, ent
);
339 /** How often do we update our estimate which share of v2 and v3 directory
340 * requests is sent to us? We could as well trigger updates of shares from
341 * network status updates, but that means adding a lot of calls into code
342 * that is independent from geoip stats (and keeping them up-to-date). We
343 * are perfectly fine with an approximation of 15-minute granularity. */
344 #define REQUEST_SHARE_INTERVAL (15 * 60)
346 /** When did we last determine which share of v2 and v3 directory requests
348 static time_t last_time_determined_shares
= 0;
350 /** Sum of products of v2 shares times the number of seconds for which we
351 * consider these shares as valid. */
352 static double v2_share_times_seconds
;
354 /** Sum of products of v3 shares times the number of seconds for which we
355 * consider these shares as valid. */
356 static double v3_share_times_seconds
;
358 /** Number of seconds we are determining v2 and v3 shares. */
359 static int share_seconds
;
361 /** Try to determine which fraction of v2 and v3 directory requests aimed at
362 * caches will be sent to us at time <b>now</b> and store that value in
363 * order to take a mean value later on. */
365 geoip_determine_shares(time_t now
)
367 double v2_share
= 0.0, v3_share
= 0.0;
368 if (router_get_my_share_of_directory_requests(&v2_share
, &v3_share
) < 0)
370 if (last_time_determined_shares
) {
371 v2_share_times_seconds
+= v2_share
*
372 ((double) (now
- last_time_determined_shares
));
373 v3_share_times_seconds
+= v3_share
*
374 ((double) (now
- last_time_determined_shares
));
375 share_seconds
+= (int)(now
- last_time_determined_shares
);
377 last_time_determined_shares
= now
;
380 /** Calculate which fraction of v2 and v3 directory requests aimed at caches
381 * have been sent to us since the last call of this function up to time
382 * <b>now</b>. Set *<b>v2_share_out</b> and *<b>v3_share_out</b> to the
383 * fractions of v2 and v3 protocol shares we expect to have seen. Reset
384 * counters afterwards. Return 0 on success, -1 on failure (e.g. when zero
385 * seconds have passed since the last call).*/
387 geoip_get_mean_shares(time_t now
, double *v2_share_out
,
388 double *v3_share_out
)
390 geoip_determine_shares(now
);
393 *v2_share_out
= v2_share_times_seconds
/ ((double) share_seconds
);
394 *v3_share_out
= v3_share_times_seconds
/ ((double) share_seconds
);
395 v2_share_times_seconds
= v3_share_times_seconds
= 0.0;
400 /** Note that we've seen a client connect from the IP <b>addr</b> (host order)
401 * at time <b>now</b>. Ignored by all but bridges and directories if
402 * configured accordingly. */
404 geoip_note_client_seen(geoip_client_action_t action
,
405 uint32_t addr
, time_t now
)
407 or_options_t
*options
= get_options();
408 clientmap_entry_t lookup
, *ent
;
409 if (action
== GEOIP_CLIENT_CONNECT
) {
410 /* Only remember statistics as entry guard or as bridge. */
411 if (!options
->EntryStatistics
&&
412 (!(options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
)))
415 if (options
->BridgeRelay
|| options
->BridgeAuthoritativeDir
||
416 !options
->DirReqStatistics
)
420 lookup
.ipaddr
= addr
;
421 lookup
.action
= (int)action
;
422 ent
= HT_FIND(clientmap
, &client_history
, &lookup
);
424 ent
= tor_malloc_zero(sizeof(clientmap_entry_t
));
426 ent
->action
= (int)action
;
427 HT_INSERT(clientmap
, &client_history
, ent
);
429 if (now
/ 60 <= (int)MAX_LAST_SEEN_IN_MINUTES
&& now
>= 0)
430 ent
->last_seen_in_minutes
= (unsigned)(now
/60);
432 ent
->last_seen_in_minutes
= 0;
434 if (action
== GEOIP_CLIENT_NETWORKSTATUS
||
435 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
) {
436 int country_idx
= geoip_get_country_by_ip(addr
);
438 country_idx
= 0; /** unresolved requests are stored at index 0. */
439 if (country_idx
>= 0 && country_idx
< smartlist_len(geoip_countries
)) {
440 geoip_country_t
*country
= smartlist_get(geoip_countries
, country_idx
);
441 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
442 ++country
->n_v3_ns_requests
;
444 ++country
->n_v2_ns_requests
;
447 /* Periodically determine share of requests that we should see */
448 if (last_time_determined_shares
+ REQUEST_SHARE_INTERVAL
< now
)
449 geoip_determine_shares(now
);
453 /** HT_FOREACH helper: remove a clientmap_entry_t from the hashtable if it's
454 * older than a certain time. */
456 _remove_old_client_helper(struct clientmap_entry_t
*ent
, void *_cutoff
)
458 time_t cutoff
= *(time_t*)_cutoff
/ 60;
459 if (ent
->last_seen_in_minutes
< cutoff
) {
467 /** Forget about all clients that haven't connected since <b>cutoff</b>. */
469 geoip_remove_old_clients(time_t cutoff
)
471 clientmap_HT_FOREACH_FN(&client_history
,
472 _remove_old_client_helper
,
476 /** How many responses are we giving to clients requesting v2 network
478 static uint32_t ns_v2_responses
[GEOIP_NS_RESPONSE_NUM
];
480 /** How many responses are we giving to clients requesting v3 network
482 static uint32_t ns_v3_responses
[GEOIP_NS_RESPONSE_NUM
];
484 /** Note that we've rejected a client's request for a v2 or v3 network
485 * status, encoded in <b>action</b> for reason <b>reason</b> at time
488 geoip_note_ns_response(geoip_client_action_t action
,
489 geoip_ns_response_t response
)
491 static int arrays_initialized
= 0;
492 if (!get_options()->DirReqStatistics
)
494 if (!arrays_initialized
) {
495 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
496 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
497 arrays_initialized
= 1;
499 tor_assert(action
== GEOIP_CLIENT_NETWORKSTATUS
||
500 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
);
501 tor_assert(response
< GEOIP_NS_RESPONSE_NUM
);
502 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
503 ns_v3_responses
[response
]++;
505 ns_v2_responses
[response
]++;
508 /** Do not mention any country from which fewer than this number of IPs have
509 * connected. This conceivably avoids reporting information that could
510 * deanonymize users, though analysis is lacking. */
511 #define MIN_IPS_TO_NOTE_COUNTRY 1
512 /** Do not report any geoip data at all if we have fewer than this number of
513 * IPs to report about. */
514 #define MIN_IPS_TO_NOTE_ANYTHING 1
515 /** When reporting geoip data about countries, round up to the nearest
516 * multiple of this value. */
517 #define IP_GRANULARITY 8
519 /** Helper type: used to sort per-country totals by value. */
520 typedef struct c_hist_t
{
521 char country
[3]; /**< Two-letter country code. */
522 unsigned total
; /**< Total IP addresses seen in this country. */
525 /** Sorting helper: return -1, 1, or 0 based on comparison of two
526 * geoip_entry_t. Sort in descending order of total, and then by country
529 _c_hist_compare(const void **_a
, const void **_b
)
531 const c_hist_t
*a
= *_a
, *b
= *_b
;
532 if (a
->total
> b
->total
)
534 else if (a
->total
< b
->total
)
537 return strcmp(a
->country
, b
->country
);
540 /** When there are incomplete directory requests at the end of a 24-hour
541 * period, consider those requests running for longer than this timeout as
542 * failed, the others as still running. */
543 #define DIRREQ_TIMEOUT (10*60)
545 /** Entry in a map from either conn->global_identifier for direct requests
546 * or a unique circuit identifier for tunneled requests to request time,
547 * response size, and completion time of a network status request. Used to
548 * measure download times of requests to derive average client
550 typedef struct dirreq_map_entry_t
{
551 HT_ENTRY(dirreq_map_entry_t
) node
;
552 /** Unique identifier for this network status request; this is either the
553 * conn->global_identifier of the dir conn (direct request) or a new
554 * locally unique identifier of a circuit (tunneled request). This ID is
555 * only unique among other direct or tunneled requests, respectively. */
557 unsigned int state
:3; /**< State of this directory request. */
558 unsigned int type
:1; /**< Is this a direct or a tunneled request? */
559 unsigned int completed
:1; /**< Is this request complete? */
560 unsigned int action
:2; /**< Is this a v2 or v3 request? */
561 /** When did we receive the request and started sending the response? */
562 struct timeval request_time
;
563 size_t response_size
; /**< What is the size of the response in bytes? */
564 struct timeval completion_time
; /**< When did the request succeed? */
565 } dirreq_map_entry_t
;
567 /** Map of all directory requests asking for v2 or v3 network statuses in
568 * the current geoip-stats interval. Values are
569 * of type *<b>dirreq_map_entry_t</b>. */
570 static HT_HEAD(dirreqmap
, dirreq_map_entry_t
) dirreq_map
=
574 dirreq_map_ent_eq(const dirreq_map_entry_t
*a
,
575 const dirreq_map_entry_t
*b
)
577 return a
->dirreq_id
== b
->dirreq_id
&& a
->type
== b
->type
;
581 dirreq_map_ent_hash(const dirreq_map_entry_t
*entry
)
583 unsigned u
= (unsigned) entry
->dirreq_id
;
584 u
+= entry
->type
<< 20;
588 HT_PROTOTYPE(dirreqmap
, dirreq_map_entry_t
, node
, dirreq_map_ent_hash
,
590 HT_GENERATE(dirreqmap
, dirreq_map_entry_t
, node
, dirreq_map_ent_hash
,
591 dirreq_map_ent_eq
, 0.6, malloc
, realloc
, free
);
593 /** Helper: Put <b>entry</b> into map of directory requests using
594 * <b>type</b> and <b>dirreq_id</b> as key parts. If there is
595 * already an entry for that key, print out a BUG warning and return. */
597 _dirreq_map_put(dirreq_map_entry_t
*entry
, dirreq_type_t type
,
600 dirreq_map_entry_t
*old_ent
;
601 tor_assert(entry
->type
== type
);
602 tor_assert(entry
->dirreq_id
== dirreq_id
);
604 /* XXXX we could switch this to HT_INSERT some time, since it seems that
605 * this bug doesn't happen. But since this function doesn't seem to be
606 * critical-path, it's sane to leave it alone. */
607 old_ent
= HT_REPLACE(dirreqmap
, &dirreq_map
, entry
);
608 if (old_ent
&& old_ent
!= entry
) {
609 log_warn(LD_BUG
, "Error when putting directory request into local "
610 "map. There was already an entry for the same identifier.");
615 /** Helper: Look up and return an entry in the map of directory requests
616 * using <b>type</b> and <b>dirreq_id</b> as key parts. If there
617 * is no such entry, return NULL. */
618 static dirreq_map_entry_t
*
619 _dirreq_map_get(dirreq_type_t type
, uint64_t dirreq_id
)
621 dirreq_map_entry_t lookup
;
623 lookup
.dirreq_id
= dirreq_id
;
624 return HT_FIND(dirreqmap
, &dirreq_map
, &lookup
);
627 /** Note that an either direct or tunneled (see <b>type</b>) directory
628 * request for a network status with unique ID <b>dirreq_id</b> of size
629 * <b>response_size</b> and action <b>action</b> (either v2 or v3) has
632 geoip_start_dirreq(uint64_t dirreq_id
, size_t response_size
,
633 geoip_client_action_t action
, dirreq_type_t type
)
635 dirreq_map_entry_t
*ent
;
636 if (!get_options()->DirReqStatistics
)
638 ent
= tor_malloc_zero(sizeof(dirreq_map_entry_t
));
639 ent
->dirreq_id
= dirreq_id
;
640 tor_gettimeofday(&ent
->request_time
);
641 ent
->response_size
= response_size
;
642 ent
->action
= action
;
644 _dirreq_map_put(ent
, type
, dirreq_id
);
647 /** Change the state of the either direct or tunneled (see <b>type</b>)
648 * directory request with <b>dirreq_id</b> to <b>new_state</b> and
649 * possibly mark it as completed. If no entry can be found for the given
650 * key parts (e.g., if this is a directory request that we are not
651 * measuring, or one that was started in the previous measurement period),
652 * or if the state cannot be advanced to <b>new_state</b>, do nothing. */
654 geoip_change_dirreq_state(uint64_t dirreq_id
, dirreq_type_t type
,
655 dirreq_state_t new_state
)
657 dirreq_map_entry_t
*ent
;
658 if (!get_options()->DirReqStatistics
)
660 ent
= _dirreq_map_get(type
, dirreq_id
);
663 if (new_state
== DIRREQ_IS_FOR_NETWORK_STATUS
)
665 if (new_state
- 1 != ent
->state
)
667 ent
->state
= new_state
;
668 if ((type
== DIRREQ_DIRECT
&&
669 new_state
== DIRREQ_FLUSHING_DIR_CONN_FINISHED
) ||
670 (type
== DIRREQ_TUNNELED
&&
671 new_state
== DIRREQ_OR_CONN_BUFFER_FLUSHED
)) {
672 tor_gettimeofday(&ent
->completion_time
);
677 /** Return a newly allocated comma-separated string containing statistics
678 * on network status downloads. The string contains the number of completed
679 * requests, timeouts, and still running requests as well as the download
680 * times by deciles and quartiles. Return NULL if we have not observed
681 * requests for long enough. */
683 geoip_get_dirreq_history(geoip_client_action_t action
,
687 smartlist_t
*dirreq_completed
= NULL
;
688 uint32_t complete
= 0, timeouts
= 0, running
= 0;
689 int bufsize
= 1024, written
;
690 dirreq_map_entry_t
**ptr
, **next
, *ent
;
693 tor_gettimeofday(&now
);
694 if (action
!= GEOIP_CLIENT_NETWORKSTATUS
&&
695 action
!= GEOIP_CLIENT_NETWORKSTATUS_V2
)
697 dirreq_completed
= smartlist_create();
698 for (ptr
= HT_START(dirreqmap
, &dirreq_map
); ptr
; ptr
= next
) {
700 if (ent
->action
!= action
|| ent
->type
!= type
) {
701 next
= HT_NEXT(dirreqmap
, &dirreq_map
, ptr
);
704 if (ent
->completed
) {
705 smartlist_add(dirreq_completed
, ent
);
707 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ptr
);
709 if (tv_mdiff(&ent
->request_time
, &now
) / 1000 > DIRREQ_TIMEOUT
)
713 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ptr
);
718 #define DIR_REQ_GRANULARITY 4
719 complete
= round_uint32_to_next_multiple_of(complete
,
720 DIR_REQ_GRANULARITY
);
721 timeouts
= round_uint32_to_next_multiple_of(timeouts
,
722 DIR_REQ_GRANULARITY
);
723 running
= round_uint32_to_next_multiple_of(running
,
724 DIR_REQ_GRANULARITY
);
725 result
= tor_malloc_zero(bufsize
);
726 written
= tor_snprintf(result
, bufsize
, "complete=%u,timeout=%u,"
727 "running=%u", complete
, timeouts
, running
);
733 #define MIN_DIR_REQ_RESPONSES 16
734 if (complete
>= MIN_DIR_REQ_RESPONSES
) {
736 /* We may have rounded 'completed' up. Here we want to use the
738 complete
= smartlist_len(dirreq_completed
);
739 dltimes
= tor_malloc_zero(sizeof(uint32_t) * complete
);
740 SMARTLIST_FOREACH_BEGIN(dirreq_completed
, dirreq_map_entry_t
*, ent
) {
741 uint32_t bytes_per_second
;
742 uint32_t time_diff
= (uint32_t) tv_mdiff(&ent
->request_time
,
743 &ent
->completion_time
);
745 time_diff
= 1; /* Avoid DIV/0; "instant" answers are impossible
746 * by law of nature or something, but a milisecond
747 * is a bit greater than "instantly" */
748 bytes_per_second
= (uint32_t)(1000 * ent
->response_size
/ time_diff
);
749 dltimes
[ent_sl_idx
] = bytes_per_second
;
750 } SMARTLIST_FOREACH_END(ent
);
751 median_uint32(dltimes
, complete
); /* sorts as a side effect. */
752 written
= tor_snprintf(result
+ written
, bufsize
- written
,
753 ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u,"
754 "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u",
756 dltimes
[1*complete
/10-1],
757 dltimes
[2*complete
/10-1],
758 dltimes
[1*complete
/4-1],
759 dltimes
[3*complete
/10-1],
760 dltimes
[4*complete
/10-1],
761 dltimes
[5*complete
/10-1],
762 dltimes
[6*complete
/10-1],
763 dltimes
[7*complete
/10-1],
764 dltimes
[3*complete
/4-1],
765 dltimes
[8*complete
/10-1],
766 dltimes
[9*complete
/10-1],
767 dltimes
[complete
-1]);
773 SMARTLIST_FOREACH(dirreq_completed
, dirreq_map_entry_t
*, ent
,
775 smartlist_free(dirreq_completed
);
779 /** Return a newly allocated comma-separated string containing entries for
780 * all the countries from which we've seen enough clients connect as a
781 * bridge, directory server, or entry guard. The entry format is cc=num
782 * where num is the number of IPs we've seen connecting from that country,
783 * and cc is a lowercased country code. Returns NULL if we don't want
784 * to export geoip data yet. */
786 geoip_get_client_history(geoip_client_action_t action
)
789 unsigned granularity
= IP_GRANULARITY
;
790 smartlist_t
*chunks
= NULL
;
791 smartlist_t
*entries
= NULL
;
792 int n_countries
= geoip_get_n_countries();
794 clientmap_entry_t
**ent
;
795 unsigned *counts
= NULL
;
798 if (!geoip_is_loaded())
801 counts
= tor_malloc_zero(sizeof(unsigned)*n_countries
);
802 HT_FOREACH(ent
, clientmap
, &client_history
) {
804 if ((*ent
)->action
!= (int)action
)
806 country
= geoip_get_country_by_ip((*ent
)->ipaddr
);
808 country
= 0; /** unresolved requests are stored at index 0. */
809 tor_assert(0 <= country
&& country
< n_countries
);
813 /* Don't record anything if we haven't seen enough IPs. */
814 if (total
< MIN_IPS_TO_NOTE_ANYTHING
)
816 /* Make a list of c_hist_t */
817 entries
= smartlist_create();
818 for (i
= 0; i
< n_countries
; ++i
) {
819 unsigned c
= counts
[i
];
820 const char *countrycode
;
822 /* Only report a country if it has a minimum number of IPs. */
823 if (c
>= MIN_IPS_TO_NOTE_COUNTRY
) {
824 c
= round_to_next_multiple_of(c
, granularity
);
825 countrycode
= geoip_get_country_name(i
);
826 ent
= tor_malloc(sizeof(c_hist_t
));
827 strlcpy(ent
->country
, countrycode
, sizeof(ent
->country
));
829 smartlist_add(entries
, ent
);
832 /* Sort entries. Note that we must do this _AFTER_ rounding, or else
833 * the sort order could leak info. */
834 smartlist_sort(entries
, _c_hist_compare
);
836 /* Build the result. */
837 chunks
= smartlist_create();
838 SMARTLIST_FOREACH(entries
, c_hist_t
*, ch
, {
840 tor_asprintf(&buf
, "%s=%u", ch
->country
, ch
->total
);
841 smartlist_add(chunks
, buf
);
843 result
= smartlist_join_strings(chunks
, ",", 0, NULL
);
847 SMARTLIST_FOREACH(chunks
, char *, c
, tor_free(c
));
848 smartlist_free(chunks
);
851 SMARTLIST_FOREACH(entries
, c_hist_t
*, c
, tor_free(c
));
852 smartlist_free(entries
);
857 /** Return a newly allocated string holding the per-country request history
858 * for <b>action</b> in a format suitable for an extra-info document, or NULL
861 geoip_get_request_history(geoip_client_action_t action
)
863 smartlist_t
*entries
, *strings
;
865 unsigned granularity
= IP_GRANULARITY
;
867 if (action
!= GEOIP_CLIENT_NETWORKSTATUS
&&
868 action
!= GEOIP_CLIENT_NETWORKSTATUS_V2
)
870 if (!geoip_countries
)
873 entries
= smartlist_create();
874 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
877 tot
= (action
== GEOIP_CLIENT_NETWORKSTATUS
) ?
878 c
->n_v3_ns_requests
: c
->n_v2_ns_requests
;
881 ent
= tor_malloc_zero(sizeof(c_hist_t
));
882 strlcpy(ent
->country
, c
->countrycode
, sizeof(ent
->country
));
883 ent
->total
= round_to_next_multiple_of(tot
, granularity
);
884 smartlist_add(entries
, ent
);
886 smartlist_sort(entries
, _c_hist_compare
);
888 strings
= smartlist_create();
889 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, {
891 tor_asprintf(&buf
, "%s=%u", ent
->country
, ent
->total
);
892 smartlist_add(strings
, buf
);
894 result
= smartlist_join_strings(strings
, ",", 0, NULL
);
895 SMARTLIST_FOREACH(strings
, char *, cp
, tor_free(cp
));
896 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, tor_free(ent
));
897 smartlist_free(strings
);
898 smartlist_free(entries
);
902 /** Start time of directory request stats or 0 if we're not collecting
903 * directory request statistics. */
904 static time_t start_of_dirreq_stats_interval
;
906 /** Initialize directory request stats. */
908 geoip_dirreq_stats_init(time_t now
)
910 start_of_dirreq_stats_interval
= now
;
913 /** Stop collecting directory request stats in a way that we can re-start
914 * doing so in geoip_dirreq_stats_init(). */
916 geoip_dirreq_stats_term(void)
918 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
919 c
->n_v2_ns_requests
= c
->n_v3_ns_requests
= 0;
922 clientmap_entry_t
**ent
, **next
, *this;
923 for (ent
= HT_START(clientmap
, &client_history
); ent
!= NULL
;
925 if ((*ent
)->action
== GEOIP_CLIENT_NETWORKSTATUS
||
926 (*ent
)->action
== GEOIP_CLIENT_NETWORKSTATUS_V2
) {
928 next
= HT_NEXT_RMV(clientmap
, &client_history
, ent
);
931 next
= HT_NEXT(clientmap
, &client_history
, ent
);
935 v2_share_times_seconds
= v3_share_times_seconds
= 0.0;
936 last_time_determined_shares
= 0;
938 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
939 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
941 dirreq_map_entry_t
**ent
, **next
, *this;
942 for (ent
= HT_START(dirreqmap
, &dirreq_map
); ent
!= NULL
; ent
= next
) {
944 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ent
);
948 start_of_dirreq_stats_interval
= 0;
951 /** Write dirreq statistics to $DATADIR/stats/dirreq-stats and return when
952 * we would next want to write. */
954 geoip_dirreq_stats_write(time_t now
)
956 char *statsdir
= NULL
, *filename
= NULL
;
957 char *data_v2
= NULL
, *data_v3
= NULL
;
958 char written
[ISO_TIME_LEN
+1];
959 open_file_t
*open_file
= NULL
;
960 double v2_share
= 0.0, v3_share
= 0.0;
964 if (!start_of_dirreq_stats_interval
)
965 return 0; /* Not initialized. */
966 if (start_of_dirreq_stats_interval
+ WRITE_STATS_INTERVAL
> now
)
967 goto done
; /* Not ready to write. */
969 /* Discard all items in the client history that are too old. */
970 geoip_remove_old_clients(start_of_dirreq_stats_interval
);
972 statsdir
= get_datadir_fname("stats");
973 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
975 filename
= get_datadir_fname2("stats", "dirreq-stats");
976 data_v2
= geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2
);
977 data_v3
= geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS
);
978 format_iso_time(written
, now
);
979 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
983 if (fprintf(out
, "dirreq-stats-end %s (%d s)\ndirreq-v3-ips %s\n"
984 "dirreq-v2-ips %s\n", written
,
985 (unsigned) (now
- start_of_dirreq_stats_interval
),
986 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
991 data_v2
= geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS_V2
);
992 data_v3
= geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS
);
993 if (fprintf(out
, "dirreq-v3-reqs %s\ndirreq-v2-reqs %s\n",
994 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
998 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
999 c
->n_v2_ns_requests
= c
->n_v3_ns_requests
= 0;
1001 #define RESPONSE_GRANULARITY 8
1002 for (i
= 0; i
< GEOIP_NS_RESPONSE_NUM
; i
++) {
1003 ns_v2_responses
[i
] = round_uint32_to_next_multiple_of(
1004 ns_v2_responses
[i
], RESPONSE_GRANULARITY
);
1005 ns_v3_responses
[i
] = round_uint32_to_next_multiple_of(
1006 ns_v3_responses
[i
], RESPONSE_GRANULARITY
);
1008 #undef RESPONSE_GRANULARITY
1009 if (fprintf(out
, "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
1010 "not-found=%u,not-modified=%u,busy=%u\n",
1011 ns_v3_responses
[GEOIP_SUCCESS
],
1012 ns_v3_responses
[GEOIP_REJECT_NOT_ENOUGH_SIGS
],
1013 ns_v3_responses
[GEOIP_REJECT_UNAVAILABLE
],
1014 ns_v3_responses
[GEOIP_REJECT_NOT_FOUND
],
1015 ns_v3_responses
[GEOIP_REJECT_NOT_MODIFIED
],
1016 ns_v3_responses
[GEOIP_REJECT_BUSY
]) < 0)
1018 if (fprintf(out
, "dirreq-v2-resp ok=%u,unavailable=%u,"
1019 "not-found=%u,not-modified=%u,busy=%u\n",
1020 ns_v2_responses
[GEOIP_SUCCESS
],
1021 ns_v2_responses
[GEOIP_REJECT_UNAVAILABLE
],
1022 ns_v2_responses
[GEOIP_REJECT_NOT_FOUND
],
1023 ns_v2_responses
[GEOIP_REJECT_NOT_MODIFIED
],
1024 ns_v2_responses
[GEOIP_REJECT_BUSY
]) < 0)
1026 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
1027 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
1028 if (!geoip_get_mean_shares(now
, &v2_share
, &v3_share
)) {
1029 if (fprintf(out
, "dirreq-v2-share %0.2lf%%\n", v2_share
*100) < 0)
1031 if (fprintf(out
, "dirreq-v3-share %0.2lf%%\n", v3_share
*100) < 0)
1035 data_v2
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2
,
1037 data_v3
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS
,
1039 if (fprintf(out
, "dirreq-v3-direct-dl %s\ndirreq-v2-direct-dl %s\n",
1040 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
1044 data_v2
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2
,
1046 data_v3
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS
,
1048 if (fprintf(out
, "dirreq-v3-tunneled-dl %s\ndirreq-v2-tunneled-dl %s\n",
1049 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
1052 finish_writing_to_file(open_file
);
1055 start_of_dirreq_stats_interval
= now
;
1059 abort_writing_to_file(open_file
);
1064 return start_of_dirreq_stats_interval
+ WRITE_STATS_INTERVAL
;
1067 /** Start time of bridge stats or 0 if we're not collecting bridge
1069 static time_t start_of_bridge_stats_interval
;
1071 /** Initialize bridge stats. */
1073 geoip_bridge_stats_init(time_t now
)
1075 start_of_bridge_stats_interval
= now
;
1078 /** Stop collecting bridge stats in a way that we can re-start doing so in
1079 * geoip_bridge_stats_init(). */
1081 geoip_bridge_stats_term(void)
1083 client_history_clear();
1084 start_of_bridge_stats_interval
= 0;
1087 /** Validate a bridge statistics string as it would be written to a
1088 * current extra-info descriptor. Return 1 if the string is valid and
1089 * recent enough, or 0 otherwise. */
1091 validate_bridge_stats(const char *stats_str
, time_t now
)
1093 char stats_end_str
[ISO_TIME_LEN
+1], stats_start_str
[ISO_TIME_LEN
+1],
1096 const char *BRIDGE_STATS_END
= "bridge-stats-end ";
1097 const char *BRIDGE_IPS
= "bridge-ips ";
1098 const char *BRIDGE_IPS_EMPTY_LINE
= "bridge-ips\n";
1100 time_t stats_end_time
;
1102 tor_assert(stats_str
);
1104 /* Parse timestamp and number of seconds from
1105 "bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */
1106 tmp
= find_str_at_start_of_line(stats_str
, BRIDGE_STATS_END
);
1109 tmp
+= strlen(BRIDGE_STATS_END
);
1111 if (strlen(tmp
) < ISO_TIME_LEN
+ 6)
1113 strlcpy(stats_end_str
, tmp
, sizeof(stats_end_str
));
1114 if (parse_iso_time(stats_end_str
, &stats_end_time
) < 0)
1116 if (stats_end_time
< now
- (25*60*60) ||
1117 stats_end_time
> now
+ (1*60*60))
1119 seconds
= (int)strtol(tmp
+ ISO_TIME_LEN
+ 2, &eos
, 10);
1120 if (!eos
|| seconds
< 23*60*60)
1122 format_iso_time(stats_start_str
, stats_end_time
- seconds
);
1124 /* Parse: "bridge-ips CC=N,CC=N,..." */
1125 tmp
= find_str_at_start_of_line(stats_str
, BRIDGE_IPS
);
1127 /* Look if there is an empty "bridge-ips" line */
1128 tmp
= find_str_at_start_of_line(stats_str
, BRIDGE_IPS_EMPTY_LINE
);
1136 /** Most recent bridge statistics formatted to be written to extra-info
1138 static char *bridge_stats_extrainfo
= NULL
;
1140 /** Return a newly allocated string holding our bridge usage stats by country
1141 * in a format suitable for inclusion in an extrainfo document. Return NULL on
1144 format_bridge_stats_extrainfo(time_t now
)
1146 char *out
= NULL
, *data
= NULL
;
1147 long duration
= now
- start_of_bridge_stats_interval
;
1148 char written
[ISO_TIME_LEN
+1];
1153 format_iso_time(written
, now
);
1154 data
= geoip_get_client_history(GEOIP_CLIENT_CONNECT
);
1157 "bridge-stats-end %s (%ld s)\n"
1166 /** Return a newly allocated string holding our bridge usage stats by country
1167 * in a format suitable for the answer to a controller request. Return NULL on
1170 format_bridge_stats_controller(time_t now
)
1172 char *out
= NULL
, *data
= NULL
;
1173 char started
[ISO_TIME_LEN
+1];
1176 format_iso_time(started
, start_of_bridge_stats_interval
);
1177 data
= geoip_get_client_history(GEOIP_CLIENT_CONNECT
);
1180 "TimeStarted=\"%s\" CountrySummary=%s",
1181 started
, data
? data
: "");
1186 /** Write bridge statistics to $DATADIR/stats/bridge-stats and return
1187 * when we should next try to write statistics. */
1189 geoip_bridge_stats_write(time_t now
)
1191 char *filename
= NULL
, *val
= NULL
, *statsdir
= NULL
;
1193 /* Check if 24 hours have passed since starting measurements. */
1194 if (now
< start_of_bridge_stats_interval
+ WRITE_STATS_INTERVAL
)
1195 return start_of_bridge_stats_interval
+ WRITE_STATS_INTERVAL
;
1197 /* Discard all items in the client history that are too old. */
1198 geoip_remove_old_clients(start_of_bridge_stats_interval
);
1200 /* Generate formatted string */
1201 val
= format_bridge_stats_extrainfo(now
);
1205 /* Update the stored value. */
1206 tor_free(bridge_stats_extrainfo
);
1207 bridge_stats_extrainfo
= val
;
1208 start_of_bridge_stats_interval
= now
;
1210 /* Write it to disk. */
1211 statsdir
= get_datadir_fname("stats");
1212 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
1214 filename
= get_datadir_fname2("stats", "bridge-stats");
1216 write_str_to_file(filename
, bridge_stats_extrainfo
, 0);
1218 /* Tell the controller, "hey, there are clients!" */
1220 char *controller_str
= format_bridge_stats_controller(now
);
1222 control_event_clients_seen(controller_str
);
1223 tor_free(controller_str
);
1229 return start_of_bridge_stats_interval
+ WRITE_STATS_INTERVAL
;
1232 /** Try to load the most recent bridge statistics from disk, unless we
1233 * have finished a measurement interval lately, and check whether they
1234 * are still recent enough. */
1236 load_bridge_stats(time_t now
)
1238 char *fname
, *contents
;
1239 if (bridge_stats_extrainfo
)
1242 fname
= get_datadir_fname2("stats", "bridge-stats");
1243 contents
= read_file_to_str(fname
, RFTS_IGNORE_MISSING
, NULL
);
1244 if (contents
&& validate_bridge_stats(contents
, now
))
1245 bridge_stats_extrainfo
= contents
;
1250 /** Return most recent bridge statistics for inclusion in extra-info
1251 * descriptors, or NULL if we don't have recent bridge statistics. */
1253 geoip_get_bridge_stats_extrainfo(time_t now
)
1255 load_bridge_stats(now
);
1256 return bridge_stats_extrainfo
;
1259 /** Return a new string containing the recent bridge statistics to be returned
1260 * to controller clients, or NULL if we don't have any bridge statistics. */
1262 geoip_get_bridge_stats_controller(time_t now
)
1264 return format_bridge_stats_controller(now
);
1267 /** Start time of entry stats or 0 if we're not collecting entry
1269 static time_t start_of_entry_stats_interval
;
1271 /** Initialize entry stats. */
1273 geoip_entry_stats_init(time_t now
)
1275 start_of_entry_stats_interval
= now
;
1278 /** Stop collecting entry stats in a way that we can re-start doing so in
1279 * geoip_entry_stats_init(). */
1281 geoip_entry_stats_term(void)
1283 client_history_clear();
1284 start_of_entry_stats_interval
= 0;
1287 /** Write entry statistics to $DATADIR/stats/entry-stats and return time
1288 * when we would next want to write. */
1290 geoip_entry_stats_write(time_t now
)
1292 char *statsdir
= NULL
, *filename
= NULL
;
1294 char written
[ISO_TIME_LEN
+1];
1295 open_file_t
*open_file
= NULL
;
1298 if (!start_of_entry_stats_interval
)
1299 return 0; /* Not initialized. */
1300 if (start_of_entry_stats_interval
+ WRITE_STATS_INTERVAL
> now
)
1301 goto done
; /* Not ready to write. */
1303 /* Discard all items in the client history that are too old. */
1304 geoip_remove_old_clients(start_of_entry_stats_interval
);
1306 statsdir
= get_datadir_fname("stats");
1307 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
1309 filename
= get_datadir_fname2("stats", "entry-stats");
1310 data
= geoip_get_client_history(GEOIP_CLIENT_CONNECT
);
1311 format_iso_time(written
, now
);
1312 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
1316 if (fprintf(out
, "entry-stats-end %s (%u s)\nentry-ips %s\n",
1317 written
, (unsigned) (now
- start_of_entry_stats_interval
),
1318 data
? data
: "") < 0)
1321 start_of_entry_stats_interval
= now
;
1323 finish_writing_to_file(open_file
);
1327 abort_writing_to_file(open_file
);
1331 return start_of_entry_stats_interval
+ WRITE_STATS_INTERVAL
;
1334 /** Helper used to implement GETINFO ip-to-country/... controller command. */
1336 getinfo_helper_geoip(control_connection_t
*control_conn
,
1337 const char *question
, char **answer
,
1338 const char **errmsg
)
1341 if (!geoip_is_loaded()) {
1342 *errmsg
= "GeoIP data not loaded";
1345 if (!strcmpstart(question
, "ip-to-country/")) {
1349 question
+= strlen("ip-to-country/");
1350 if (tor_inet_aton(question
, &in
) != 0) {
1351 ip
= ntohl(in
.s_addr
);
1352 c
= geoip_get_country_by_ip(ip
);
1353 *answer
= tor_strdup(geoip_get_country_name(c
));
1359 /** Release all storage held by the GeoIP database. */
1361 clear_geoip_db(void)
1363 if (geoip_countries
) {
1364 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, tor_free(c
));
1365 smartlist_free(geoip_countries
);
1368 strmap_free(country_idxplus1_by_lc_code
, NULL
);
1369 if (geoip_entries
) {
1370 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, ent
, tor_free(ent
));
1371 smartlist_free(geoip_entries
);
1373 geoip_countries
= NULL
;
1374 country_idxplus1_by_lc_code
= NULL
;
1375 geoip_entries
= NULL
;
1378 /** Release all storage held in this file. */
1380 geoip_free_all(void)
1383 clientmap_entry_t
**ent
, **next
, *this;
1384 for (ent
= HT_START(clientmap
, &client_history
); ent
!= NULL
; ent
= next
) {
1386 next
= HT_NEXT_RMV(clientmap
, &client_history
, ent
);
1389 HT_CLEAR(clientmap
, &client_history
);
1392 dirreq_map_entry_t
**ent
, **next
, *this;
1393 for (ent
= HT_START(dirreqmap
, &dirreq_map
); ent
!= NULL
; ent
= next
) {
1395 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ent
);
1398 HT_CLEAR(dirreqmap
, &dirreq_map
);