1 /* Copyright (c) 2007-2009, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
6 * \brief Functions related to maintaining an IP-to-country database and to
7 * summarizing client connections by country.
14 static void clear_geoip_db(void);
16 /** An entry from the GeoIP file: maps an IP range to a country. */
17 typedef struct geoip_entry_t
{
18 uint32_t ip_low
; /**< The lowest IP in the range, in host order */
19 uint32_t ip_high
; /**< The highest IP in the range, in host order */
20 intptr_t country
; /**< An index into geoip_countries */
23 /** For how many periods should we remember per-country request history? */
24 #define REQUEST_HIST_LEN 1
25 /** How long are the periods for which we should remember request history? */
26 #define REQUEST_HIST_PERIOD (24*60*60)
28 /** A per-country record for GeoIP request history. */
29 typedef struct geoip_country_t
{
31 uint32_t n_v2_ns_requests
[REQUEST_HIST_LEN
];
32 uint32_t n_v3_ns_requests
[REQUEST_HIST_LEN
];
35 /** A list of geoip_country_t */
36 static smartlist_t
*geoip_countries
= NULL
;
37 /** A map from lowercased country codes to their position in geoip_countries.
38 * The index is encoded in the pointer, and 1 is added so that NULL can mean
40 static strmap_t
*country_idxplus1_by_lc_code
= NULL
;
41 /** A list of all known geoip_entry_t, sorted by ip_low. */
42 static smartlist_t
*geoip_entries
= NULL
;
44 /** Return the index of the <b>country</b>'s entry in the GeoIP DB
45 * if it is a valid 2-letter country code, otherwise return -1.
48 geoip_get_country(const char *country
)
53 _idxplus1
= strmap_get_lc(country_idxplus1_by_lc_code
, country
);
57 idx
= ((uintptr_t)_idxplus1
)-1;
58 return (country_t
)idx
;
61 /** Add an entry to the GeoIP table, mapping all IPs between <b>low</b> and
62 * <b>high</b>, inclusive, to the 2-letter country code <b>country</b>.
65 geoip_add_entry(uint32_t low
, uint32_t high
, const char *country
)
74 _idxplus1
= strmap_get_lc(country_idxplus1_by_lc_code
, country
);
77 geoip_country_t
*c
= tor_malloc_zero(sizeof(geoip_country_t
));
78 strlcpy(c
->countrycode
, country
, sizeof(c
->countrycode
));
79 tor_strlower(c
->countrycode
);
80 smartlist_add(geoip_countries
, c
);
81 idx
= smartlist_len(geoip_countries
) - 1;
82 strmap_set_lc(country_idxplus1_by_lc_code
, country
, (void*)(idx
+1));
84 idx
= ((uintptr_t)_idxplus1
)-1;
87 geoip_country_t
*c
= smartlist_get(geoip_countries
, idx
);
88 tor_assert(!strcasecmp(c
->countrycode
, country
));
90 ent
= tor_malloc_zero(sizeof(geoip_entry_t
));
94 smartlist_add(geoip_entries
, ent
);
97 /** Add an entry to the GeoIP table, parsing it from <b>line</b>. The
98 * format is as for geoip_load_file(). */
100 geoip_parse_entry(const char *line
)
102 unsigned int low
, high
;
104 if (!geoip_countries
) {
105 geoip_countries
= smartlist_create();
106 geoip_entries
= smartlist_create();
107 country_idxplus1_by_lc_code
= strmap_new();
109 while (TOR_ISSPACE(*line
))
113 if (sscanf(line
,"%u,%u,%2s", &low
, &high
, b
) == 3) {
114 geoip_add_entry(low
, high
, b
);
116 } else if (sscanf(line
,"\"%u\",\"%u\",\"%2s\",", &low
, &high
, b
) == 3) {
117 geoip_add_entry(low
, high
, b
);
120 log_warn(LD_GENERAL
, "Unable to parse line from GEOIP file: %s",
126 /** Sorting helper: return -1, 1, or 0 based on comparison of two
129 _geoip_compare_entries(const void **_a
, const void **_b
)
131 const geoip_entry_t
*a
= *_a
, *b
= *_b
;
132 if (a
->ip_low
< b
->ip_low
)
134 else if (a
->ip_low
> b
->ip_low
)
140 /** bsearch helper: return -1, 1, or 0 based on comparison of an IP (a pointer
141 * to a uint32_t in host order) to a geoip_entry_t */
143 _geoip_compare_key_to_entry(const void *_key
, const void **_member
)
145 const uint32_t addr
= *(uint32_t *)_key
;
146 const geoip_entry_t
*entry
= *_member
;
147 if (addr
< entry
->ip_low
)
149 else if (addr
> entry
->ip_high
)
155 /** Return 1 if we should collect geoip stats on bridge users, and
156 * include them in our extrainfo descriptor. Else return 0. */
158 should_record_bridge_info(or_options_t
*options
)
160 return options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
;
163 /** Clear the GeoIP database and reload it from the file
164 * <b>filename</b>. Return 0 on success, -1 on failure.
166 * Recognized line formats are:
167 * INTIPLOW,INTIPHIGH,CC
169 * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME"
170 * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned
171 * integers, and CC is a country code.
173 * It also recognizes, and skips over, blank lines and lines that start
174 * with '#' (comments).
177 geoip_load_file(const char *filename
, or_options_t
*options
)
180 const char *msg
= "";
181 int severity
= options_need_geoip_info(options
, &msg
) ? LOG_WARN
: LOG_INFO
;
183 if (!(f
= fopen(filename
, "r"))) {
184 log_fn(severity
, LD_GENERAL
, "Failed to open GEOIP file %s. %s",
188 if (!geoip_countries
) {
189 geoip_country_t
*geoip_unresolved
;
190 geoip_countries
= smartlist_create();
191 /* Add a geoip_country_t for requests that could not be resolved to a
192 * country as first element (index 0) to geoip_countries. */
193 geoip_unresolved
= tor_malloc_zero(sizeof(geoip_country_t
));
194 strlcpy(geoip_unresolved
->countrycode
, "??",
195 sizeof(geoip_unresolved
->countrycode
));
196 smartlist_add(geoip_countries
, geoip_unresolved
);
197 country_idxplus1_by_lc_code
= strmap_new();
200 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, e
, tor_free(e
));
201 smartlist_free(geoip_entries
);
203 geoip_entries
= smartlist_create();
204 log_notice(LD_GENERAL
, "Parsing GEOIP file.");
207 if (fgets(buf
, (int)sizeof(buf
), f
) == NULL
)
209 /* FFFF track full country name. */
210 geoip_parse_entry(buf
);
212 /*XXXX abort and return -1 if no entries/illformed?*/
215 smartlist_sort(geoip_entries
, _geoip_compare_entries
);
217 /* Okay, now we need to maybe change our mind about what is in which
219 refresh_all_country_info();
224 /** Given an IP address in host order, return a number representing the
225 * country to which that address belongs, or -1 for unknown. The return value
226 * will always be less than geoip_get_n_countries(). To decode it,
227 * call geoip_get_country_name().
230 geoip_get_country_by_ip(uint32_t ipaddr
)
235 ent
= smartlist_bsearch(geoip_entries
, &ipaddr
, _geoip_compare_key_to_entry
);
236 return ent
? (int)ent
->country
: -1;
239 /** Return the number of countries recognized by the GeoIP database. */
241 geoip_get_n_countries(void)
243 return (int) smartlist_len(geoip_countries
);
246 /** Return the two-letter country code associated with the number <b>num</b>,
247 * or "??" for an unknown value. */
249 geoip_get_country_name(country_t num
)
251 if (geoip_countries
&& num
>= 0 && num
< smartlist_len(geoip_countries
)) {
252 geoip_country_t
*c
= smartlist_get(geoip_countries
, num
);
253 return c
->countrycode
;
258 /** Return true iff we have loaded a GeoIP database.*/
260 geoip_is_loaded(void)
262 return geoip_countries
!= NULL
&& geoip_entries
!= NULL
;
265 /** Entry in a map from IP address to the last time we've seen an incoming
266 * connection from that IP address. Used by bridges only, to track which
267 * countries have them blocked. */
268 typedef struct clientmap_entry_t
{
269 HT_ENTRY(clientmap_entry_t
) node
;
271 unsigned int last_seen_in_minutes
:30;
272 unsigned int action
:2;
275 #define ACTION_MASK 3
277 /** Map from client IP address to last time seen. */
278 static HT_HEAD(clientmap
, clientmap_entry_t
) client_history
=
280 /** Time at which we started tracking client IP history. */
281 static time_t client_history_starts
= 0;
283 /** When did the current period of checking per-country request history
285 static time_t current_request_period_starts
= 0;
286 /** How many older request periods are we remembering? */
287 static int n_old_request_periods
= 0;
289 /** Hashtable helper: compute a hash of a clientmap_entry_t. */
290 static INLINE
unsigned
291 clientmap_entry_hash(const clientmap_entry_t
*a
)
293 return ht_improve_hash((unsigned) a
->ipaddr
);
295 /** Hashtable helper: compare two clientmap_entry_t values for equality. */
297 clientmap_entries_eq(const clientmap_entry_t
*a
, const clientmap_entry_t
*b
)
299 return a
->ipaddr
== b
->ipaddr
&& a
->action
== b
->action
;
302 HT_PROTOTYPE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
303 clientmap_entries_eq
);
304 HT_GENERATE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
305 clientmap_entries_eq
, 0.6, malloc
, realloc
, free
);
307 /** How often do we update our estimate which share of v2 and v3 directory
308 * requests is sent to us? We could as well trigger updates of shares from
309 * network status updates, but that means adding a lot of calls into code
310 * that is independent from geoip stats (and keeping them up-to-date). We
311 * are perfectly fine with an approximation of 15-minute granularity. */
312 #define REQUEST_SHARE_INTERVAL (15 * 60)
314 /** When did we last determine which share of v2 and v3 directory requests
316 static time_t last_time_determined_shares
= 0;
318 /** Sum of products of v2 shares times the number of seconds for which we
319 * consider these shares as valid. */
320 static double v2_share_times_seconds
;
322 /** Sum of products of v3 shares times the number of seconds for which we
323 * consider these shares as valid. */
324 static double v3_share_times_seconds
;
326 /** Number of seconds we are determining v2 and v3 shares. */
327 static int share_seconds
;
329 /** Try to determine which fraction of v2 and v3 directory requests aimed at
330 * caches will be sent to us at time <b>now</b> and store that value in
331 * order to take a mean value later on. */
333 geoip_determine_shares(time_t now
)
335 double v2_share
= 0.0, v3_share
= 0.0;
336 if (router_get_my_share_of_directory_requests(&v2_share
, &v3_share
) < 0)
338 if (last_time_determined_shares
) {
339 v2_share_times_seconds
+= v2_share
*
340 ((double) (now
- last_time_determined_shares
));
341 v3_share_times_seconds
+= v3_share
*
342 ((double) (now
- last_time_determined_shares
));
343 share_seconds
+= (int)(now
- last_time_determined_shares
);
345 last_time_determined_shares
= now
;
348 /** Calculate which fraction of v2 and v3 directory requests aimed at caches
349 * have been sent to us since the last call of this function up to time
350 * <b>now</b>. Set *<b>v2_share_out</b> and *<b>v3_share_out</b> to the
351 * fractions of v2 and v3 protocol shares we expect to have seen. Reset
352 * counters afterwards. Return 0 on success, -1 on failure (e.g. when zero
353 * seconds have passed since the last call).*/
355 geoip_get_mean_shares(time_t now
, double *v2_share_out
,
356 double *v3_share_out
)
358 geoip_determine_shares(now
);
361 *v2_share_out
= v2_share_times_seconds
/ ((double) share_seconds
);
362 *v3_share_out
= v3_share_times_seconds
/ ((double) share_seconds
);
363 v2_share_times_seconds
= v3_share_times_seconds
= 0.0;
368 /* Rotate period of v2 and v3 network status requests. */
370 rotate_request_period(void)
372 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
373 #if REQUEST_HIST_LEN > 1
374 memmove(&c
->n_v2_ns_requests
[0], &c
->n_v2_ns_requests
[1],
375 sizeof(uint32_t)*(REQUEST_HIST_LEN
-1));
376 memmove(&c
->n_v3_ns_requests
[0], &c
->n_v3_ns_requests
[1],
377 sizeof(uint32_t)*(REQUEST_HIST_LEN
-1));
379 c
->n_v2_ns_requests
[REQUEST_HIST_LEN
-1] = 0;
380 c
->n_v3_ns_requests
[REQUEST_HIST_LEN
-1] = 0;
382 current_request_period_starts
+= REQUEST_HIST_PERIOD
;
383 if (n_old_request_periods
< REQUEST_HIST_LEN
-1)
384 ++n_old_request_periods
;
387 /** Note that we've seen a client connect from the IP <b>addr</b> (host order)
388 * at time <b>now</b>. Ignored by all but bridges and directories if
389 * configured accordingly. */
391 geoip_note_client_seen(geoip_client_action_t action
,
392 uint32_t addr
, time_t now
)
394 or_options_t
*options
= get_options();
395 clientmap_entry_t lookup
, *ent
;
396 if (action
== GEOIP_CLIENT_CONNECT
) {
397 /* Only remember statistics as entry guard or as bridge. */
398 if (!options
->EntryStatistics
&&
399 (!(options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
)))
401 /* Did we recently switch from bridge to relay or back? */
402 if (client_history_starts
> now
)
405 if (options
->BridgeRelay
|| options
->BridgeAuthoritativeDir
||
406 !options
->DirReqStatistics
)
410 /* As a bridge that doesn't rotate request periods every 24 hours,
411 * possibly rotate now. */
412 if (options
->BridgeRelay
) {
413 while (current_request_period_starts
+ REQUEST_HIST_PERIOD
< now
) {
414 if (!geoip_countries
)
415 geoip_countries
= smartlist_create();
416 if (!current_request_period_starts
) {
417 current_request_period_starts
= now
;
420 /* Also discard all items in the client history that are too old.
421 * (This only works here because bridge and directory stats are
422 * independent. Otherwise, we'd only want to discard those items
423 * with action GEOIP_CLIENT_NETWORKSTATUS{_V2}.) */
424 geoip_remove_old_clients(current_request_period_starts
);
425 /* Now rotate request period */
426 rotate_request_period();
430 lookup
.ipaddr
= addr
;
431 lookup
.action
= (int)action
;
432 ent
= HT_FIND(clientmap
, &client_history
, &lookup
);
434 ent
->last_seen_in_minutes
= now
/ 60;
436 ent
= tor_malloc_zero(sizeof(clientmap_entry_t
));
438 ent
->last_seen_in_minutes
= now
/ 60;
439 ent
->action
= (int)action
;
440 HT_INSERT(clientmap
, &client_history
, ent
);
443 if (action
== GEOIP_CLIENT_NETWORKSTATUS
||
444 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
) {
445 int country_idx
= geoip_get_country_by_ip(addr
);
447 country_idx
= 0; /** unresolved requests are stored at index 0. */
448 if (country_idx
>= 0 && country_idx
< smartlist_len(geoip_countries
)) {
449 geoip_country_t
*country
= smartlist_get(geoip_countries
, country_idx
);
450 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
451 ++country
->n_v3_ns_requests
[REQUEST_HIST_LEN
-1];
453 ++country
->n_v2_ns_requests
[REQUEST_HIST_LEN
-1];
456 /* Periodically determine share of requests that we should see */
457 if (last_time_determined_shares
+ REQUEST_SHARE_INTERVAL
< now
)
458 geoip_determine_shares(now
);
461 if (!client_history_starts
) {
462 client_history_starts
= now
;
463 current_request_period_starts
= now
;
467 /** HT_FOREACH helper: remove a clientmap_entry_t from the hashtable if it's
468 * older than a certain time. */
470 _remove_old_client_helper(struct clientmap_entry_t
*ent
, void *_cutoff
)
472 time_t cutoff
= *(time_t*)_cutoff
/ 60;
473 if (ent
->last_seen_in_minutes
< cutoff
) {
481 /** Forget about all clients that haven't connected since <b>cutoff</b>.
482 * If <b>cutoff</b> is in the future, clients won't be added to the history
483 * until this time is reached. This is useful to prevent relays that switch
484 * to bridges from reporting unbelievable numbers of clients. */
486 geoip_remove_old_clients(time_t cutoff
)
488 clientmap_HT_FOREACH_FN(&client_history
,
489 _remove_old_client_helper
,
491 if (client_history_starts
< cutoff
)
492 client_history_starts
= cutoff
;
495 /** How many responses are we giving to clients requesting v2 network
497 static uint32_t ns_v2_responses
[GEOIP_NS_RESPONSE_NUM
];
499 /** How many responses are we giving to clients requesting v3 network
501 static uint32_t ns_v3_responses
[GEOIP_NS_RESPONSE_NUM
];
503 /** Note that we've rejected a client's request for a v2 or v3 network
504 * status, encoded in <b>action</b> for reason <b>reason</b> at time
507 geoip_note_ns_response(geoip_client_action_t action
,
508 geoip_ns_response_t response
)
510 static int arrays_initialized
= 0;
511 if (!get_options()->DirReqStatistics
)
513 if (!arrays_initialized
) {
514 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
515 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
516 arrays_initialized
= 1;
518 tor_assert(action
== GEOIP_CLIENT_NETWORKSTATUS
||
519 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
);
520 tor_assert(response
< GEOIP_NS_RESPONSE_NUM
);
521 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
522 ns_v3_responses
[response
]++;
524 ns_v2_responses
[response
]++;
527 /** Do not mention any country from which fewer than this number of IPs have
528 * connected. This conceivably avoids reporting information that could
529 * deanonymize users, though analysis is lacking. */
530 #define MIN_IPS_TO_NOTE_COUNTRY 1
531 /** Do not report any geoip data at all if we have fewer than this number of
532 * IPs to report about. */
533 #define MIN_IPS_TO_NOTE_ANYTHING 1
534 /** When reporting geoip data about countries, round up to the nearest
535 * multiple of this value. */
536 #define IP_GRANULARITY 8
538 /** Return the time at which we started recording geoip data. */
540 geoip_get_history_start(void)
542 return client_history_starts
;
545 /** Helper type: used to sort per-country totals by value. */
546 typedef struct c_hist_t
{
547 char country
[3]; /**< Two-letter country code. */
548 unsigned total
; /**< Total IP addresses seen in this country. */
551 /** Sorting helper: return -1, 1, or 0 based on comparison of two
552 * geoip_entry_t. Sort in descending order of total, and then by country
555 _c_hist_compare(const void **_a
, const void **_b
)
557 const c_hist_t
*a
= *_a
, *b
= *_b
;
558 if (a
->total
> b
->total
)
560 else if (a
->total
< b
->total
)
563 return strcmp(a
->country
, b
->country
);
566 /** When there are incomplete directory requests at the end of a 24-hour
567 * period, consider those requests running for longer than this timeout as
568 * failed, the others as still running. */
569 #define DIRREQ_TIMEOUT (10*60)
571 /** Entry in a map from either conn->global_identifier for direct requests
572 * or a unique circuit identifier for tunneled requests to request time,
573 * response size, and completion time of a network status request. Used to
574 * measure download times of requests to derive average client
576 typedef struct dirreq_map_entry_t
{
577 HT_ENTRY(dirreq_map_entry_t
) node
;
578 /** Unique identifier for this network status request; this is either the
579 * conn->global_identifier of the dir conn (direct request) or a new
580 * locally unique identifier of a circuit (tunneled request). This ID is
581 * only unique among other direct or tunneled requests, respectively. */
583 unsigned int state
:3; /**< State of this directory request. */
584 unsigned int type
:1; /**< Is this a direct or a tunneled request? */
585 unsigned int completed
:1; /**< Is this request complete? */
586 unsigned int action
:2; /**< Is this a v2 or v3 request? */
587 /** When did we receive the request and started sending the response? */
588 struct timeval request_time
;
589 size_t response_size
; /**< What is the size of the response in bytes? */
590 struct timeval completion_time
; /**< When did the request succeed? */
591 } dirreq_map_entry_t
;
593 /** Map of all directory requests asking for v2 or v3 network statuses in
594 * the current geoip-stats interval. Values are
595 * of type *<b>dirreq_map_entry_t</b>. */
596 static HT_HEAD(dirreqmap
, dirreq_map_entry_t
) dirreq_map
=
600 dirreq_map_ent_eq(const dirreq_map_entry_t
*a
,
601 const dirreq_map_entry_t
*b
)
603 return a
->dirreq_id
== b
->dirreq_id
&& a
->type
== b
->type
;
607 dirreq_map_ent_hash(const dirreq_map_entry_t
*entry
)
609 unsigned u
= (unsigned) entry
->dirreq_id
;
610 u
+= entry
->type
<< 20;
614 HT_PROTOTYPE(dirreqmap
, dirreq_map_entry_t
, node
, dirreq_map_ent_hash
,
616 HT_GENERATE(dirreqmap
, dirreq_map_entry_t
, node
, dirreq_map_ent_hash
,
617 dirreq_map_ent_eq
, 0.6, malloc
, realloc
, free
);
619 /** Helper: Put <b>entry</b> into map of directory requests using
620 * <b>tunneled</b> and <b>dirreq_id</b> as key parts. If there is
621 * already an entry for that key, print out a BUG warning and return. */
623 _dirreq_map_put(dirreq_map_entry_t
*entry
, dirreq_type_t type
,
626 dirreq_map_entry_t
*old_ent
;
627 tor_assert(entry
->type
== type
);
628 tor_assert(entry
->dirreq_id
== dirreq_id
);
630 /* XXXX022 once we're sure the bug case never happens, we can switch
632 old_ent
= HT_REPLACE(dirreqmap
, &dirreq_map
, entry
);
633 if (old_ent
&& old_ent
!= entry
) {
634 log_warn(LD_BUG
, "Error when putting directory request into local "
635 "map. There was already an entry for the same identifier.");
640 /** Helper: Look up and return an entry in the map of directory requests
641 * using <b>tunneled</b> and <b>dirreq_id</b> as key parts. If there
642 * is no such entry, return NULL. */
643 static dirreq_map_entry_t
*
644 _dirreq_map_get(dirreq_type_t type
, uint64_t dirreq_id
)
646 dirreq_map_entry_t lookup
;
648 lookup
.dirreq_id
= dirreq_id
;
649 return HT_FIND(dirreqmap
, &dirreq_map
, &lookup
);
652 /** Note that an either direct or tunneled (see <b>type</b>) directory
653 * request for a network status with unique ID <b>dirreq_id</b> of size
654 * <b>response_size</b> and action <b>action</b> (either v2 or v3) has
657 geoip_start_dirreq(uint64_t dirreq_id
, size_t response_size
,
658 geoip_client_action_t action
, dirreq_type_t type
)
660 dirreq_map_entry_t
*ent
;
661 if (!get_options()->DirReqStatistics
)
663 ent
= tor_malloc_zero(sizeof(dirreq_map_entry_t
));
664 ent
->dirreq_id
= dirreq_id
;
665 tor_gettimeofday(&ent
->request_time
);
666 ent
->response_size
= response_size
;
667 ent
->action
= action
;
669 _dirreq_map_put(ent
, type
, dirreq_id
);
672 /** Change the state of the either direct or tunneled (see <b>type</b>)
673 * directory request with <b>dirreq_id</b> to <b>new_state</b> and
674 * possibly mark it as completed. If no entry can be found for the given
675 * key parts (e.g., if this is a directory request that we are not
676 * measuring, or one that was started in the previous measurement period),
677 * or if the state cannot be advanced to <b>new_state</b>, do nothing. */
679 geoip_change_dirreq_state(uint64_t dirreq_id
, dirreq_type_t type
,
680 dirreq_state_t new_state
)
682 dirreq_map_entry_t
*ent
;
683 if (!get_options()->DirReqStatistics
)
685 ent
= _dirreq_map_get(type
, dirreq_id
);
688 if (new_state
== DIRREQ_IS_FOR_NETWORK_STATUS
)
690 if (new_state
- 1 != ent
->state
)
692 ent
->state
= new_state
;
693 if ((type
== DIRREQ_DIRECT
&&
694 new_state
== DIRREQ_FLUSHING_DIR_CONN_FINISHED
) ||
695 (type
== DIRREQ_TUNNELED
&&
696 new_state
== DIRREQ_OR_CONN_BUFFER_FLUSHED
)) {
697 tor_gettimeofday(&ent
->completion_time
);
702 /** Return a newly allocated comma-separated string containing statistics
703 * on network status downloads. The string contains the number of completed
704 * requests, timeouts, and still running requests as well as the download
705 * times by deciles and quartiles. Return NULL if we have not observed
706 * requests for long enough. */
708 geoip_get_dirreq_history(geoip_client_action_t action
,
712 smartlist_t
*dirreq_completed
= NULL
;
713 uint32_t complete
= 0, timeouts
= 0, running
= 0;
714 int bufsize
= 1024, written
;
715 dirreq_map_entry_t
**ptr
, **next
, *ent
;
718 tor_gettimeofday(&now
);
719 if (action
!= GEOIP_CLIENT_NETWORKSTATUS
&&
720 action
!= GEOIP_CLIENT_NETWORKSTATUS_V2
)
722 dirreq_completed
= smartlist_create();
723 for (ptr
= HT_START(dirreqmap
, &dirreq_map
); ptr
; ptr
= next
) {
725 if (ent
->action
!= action
|| ent
->type
!= type
) {
726 next
= HT_NEXT(dirreqmap
, &dirreq_map
, ptr
);
729 if (ent
->completed
) {
730 smartlist_add(dirreq_completed
, ent
);
732 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ptr
);
734 if (tv_mdiff(&ent
->request_time
, &now
) / 1000 > DIRREQ_TIMEOUT
)
738 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ptr
);
743 #define DIR_REQ_GRANULARITY 4
744 complete
= round_uint32_to_next_multiple_of(complete
,
745 DIR_REQ_GRANULARITY
);
746 timeouts
= round_uint32_to_next_multiple_of(timeouts
,
747 DIR_REQ_GRANULARITY
);
748 running
= round_uint32_to_next_multiple_of(running
,
749 DIR_REQ_GRANULARITY
);
750 result
= tor_malloc_zero(bufsize
);
751 written
= tor_snprintf(result
, bufsize
, "complete=%u,timeout=%u,"
752 "running=%u", complete
, timeouts
, running
);
758 #define MIN_DIR_REQ_RESPONSES 16
759 if (complete
>= MIN_DIR_REQ_RESPONSES
) {
761 /* We may have rounded 'completed' up. Here we want to use the
763 complete
= smartlist_len(dirreq_completed
);
764 dltimes
= tor_malloc_zero(sizeof(uint32_t) * complete
);
765 SMARTLIST_FOREACH_BEGIN(dirreq_completed
, dirreq_map_entry_t
*, ent
) {
766 uint32_t bytes_per_second
;
767 uint32_t time_diff
= (uint32_t) tv_mdiff(&ent
->request_time
,
768 &ent
->completion_time
);
770 time_diff
= 1; /* Avoid DIV/0; "instant" answers are impossible
771 * by law of nature or something, but a milisecond
772 * is a bit greater than "instantly" */
773 bytes_per_second
= (uint32_t)(1000 * ent
->response_size
/ time_diff
);
774 dltimes
[ent_sl_idx
] = bytes_per_second
;
775 } SMARTLIST_FOREACH_END(ent
);
776 median_uint32(dltimes
, complete
); /* sorts as a side effect. */
777 written
= tor_snprintf(result
+ written
, bufsize
- written
,
778 ",min=%u,d1=%u,d2=%u,q1=%u,d3=%u,d4=%u,md=%u,"
779 "d6=%u,d7=%u,q3=%u,d8=%u,d9=%u,max=%u",
781 dltimes
[1*complete
/10-1],
782 dltimes
[2*complete
/10-1],
783 dltimes
[1*complete
/4-1],
784 dltimes
[3*complete
/10-1],
785 dltimes
[4*complete
/10-1],
786 dltimes
[5*complete
/10-1],
787 dltimes
[6*complete
/10-1],
788 dltimes
[7*complete
/10-1],
789 dltimes
[3*complete
/4-1],
790 dltimes
[8*complete
/10-1],
791 dltimes
[9*complete
/10-1],
792 dltimes
[complete
-1]);
798 SMARTLIST_FOREACH(dirreq_completed
, dirreq_map_entry_t
*, ent
,
800 smartlist_free(dirreq_completed
);
804 /** How long do we have to have observed per-country request history before we
805 * are willing to talk about it? */
806 #define GEOIP_MIN_OBSERVATION_TIME (12*60*60)
808 /** Helper for geoip_get_client_history_dirreq() and
809 * geoip_get_client_history_bridge(). */
811 geoip_get_client_history(time_t now
, geoip_client_action_t action
,
812 int min_observation_time
, unsigned granularity
)
815 if (!geoip_is_loaded())
817 if (client_history_starts
< (now
- min_observation_time
)) {
819 smartlist_t
*chunks
= NULL
;
820 smartlist_t
*entries
= NULL
;
821 int n_countries
= geoip_get_n_countries();
823 clientmap_entry_t
**ent
;
824 unsigned *counts
= tor_malloc_zero(sizeof(unsigned)*n_countries
);
826 HT_FOREACH(ent
, clientmap
, &client_history
) {
828 if ((*ent
)->action
!= (int)action
)
830 country
= geoip_get_country_by_ip((*ent
)->ipaddr
);
832 country
= 0; /** unresolved requests are stored at index 0. */
833 tor_assert(0 <= country
&& country
< n_countries
);
837 /* Don't record anything if we haven't seen enough IPs. */
838 if (total
< MIN_IPS_TO_NOTE_ANYTHING
)
840 /* Make a list of c_hist_t */
841 entries
= smartlist_create();
842 for (i
= 0; i
< n_countries
; ++i
) {
843 unsigned c
= counts
[i
];
844 const char *countrycode
;
846 /* Only report a country if it has a minimum number of IPs. */
847 if (c
>= MIN_IPS_TO_NOTE_COUNTRY
) {
848 c
= round_to_next_multiple_of(c
, granularity
);
849 countrycode
= geoip_get_country_name(i
);
850 ent
= tor_malloc(sizeof(c_hist_t
));
851 strlcpy(ent
->country
, countrycode
, sizeof(ent
->country
));
853 smartlist_add(entries
, ent
);
856 /* Sort entries. Note that we must do this _AFTER_ rounding, or else
857 * the sort order could leak info. */
858 smartlist_sort(entries
, _c_hist_compare
);
860 /* Build the result. */
861 chunks
= smartlist_create();
862 SMARTLIST_FOREACH(entries
, c_hist_t
*, ch
, {
863 tor_snprintf(buf
, sizeof(buf
), "%s=%u", ch
->country
, ch
->total
);
864 smartlist_add(chunks
, tor_strdup(buf
));
866 result
= smartlist_join_strings(chunks
, ",", 0, NULL
);
870 SMARTLIST_FOREACH(chunks
, char *, c
, tor_free(c
));
871 smartlist_free(chunks
);
874 SMARTLIST_FOREACH(entries
, c_hist_t
*, c
, tor_free(c
));
875 smartlist_free(entries
);
881 /** Return a newly allocated comma-separated string containing entries for
882 * all the countries from which we've seen enough clients connect as a
883 * directory. The entry format is cc=num where num is the number of IPs
884 * we've seen connecting from that country, and cc is a lowercased country
885 * code. Returns NULL if we don't want to export geoip data yet. */
887 geoip_get_client_history_dirreq(time_t now
,
888 geoip_client_action_t action
)
890 return geoip_get_client_history(now
, action
,
891 DIR_RECORD_USAGE_MIN_OBSERVATION_TIME
,
892 DIR_RECORD_USAGE_GRANULARITY
);
895 /** Return a newly allocated comma-separated string containing entries for
896 * all the countries from which we've seen enough clients connect as a
897 * bridge. The entry format is cc=num where num is the number of IPs
898 * we've seen connecting from that country, and cc is a lowercased country
899 * code. Returns NULL if we don't want to export geoip data yet. */
901 geoip_get_client_history_bridge(time_t now
,
902 geoip_client_action_t action
)
904 return geoip_get_client_history(now
, action
,
905 GEOIP_MIN_OBSERVATION_TIME
,
909 /** Return a newly allocated string holding the per-country request history
910 * for <b>action</b> in a format suitable for an extra-info document, or NULL
913 geoip_get_request_history(time_t now
, geoip_client_action_t action
)
915 smartlist_t
*entries
, *strings
;
917 unsigned granularity
= IP_GRANULARITY
;
918 int min_observation_time
= GEOIP_MIN_OBSERVATION_TIME
;
920 if (client_history_starts
>= (now
- min_observation_time
))
922 if (action
!= GEOIP_CLIENT_NETWORKSTATUS
&&
923 action
!= GEOIP_CLIENT_NETWORKSTATUS_V2
)
925 if (!geoip_countries
)
928 entries
= smartlist_create();
929 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
930 uint32_t *n
= (action
== GEOIP_CLIENT_NETWORKSTATUS
)
931 ? c
->n_v3_ns_requests
: c
->n_v2_ns_requests
;
935 for (i
=0; i
< REQUEST_HIST_LEN
; ++i
)
939 ent
= tor_malloc_zero(sizeof(c_hist_t
));
940 strlcpy(ent
->country
, c
->countrycode
, sizeof(ent
->country
));
941 ent
->total
= round_to_next_multiple_of(tot
, granularity
);
942 smartlist_add(entries
, ent
);
944 smartlist_sort(entries
, _c_hist_compare
);
946 strings
= smartlist_create();
947 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, {
949 tor_snprintf(buf
, sizeof(buf
), "%s=%u", ent
->country
, ent
->total
);
950 smartlist_add(strings
, tor_strdup(buf
));
952 result
= smartlist_join_strings(strings
, ",", 0, NULL
);
953 SMARTLIST_FOREACH(strings
, char *, cp
, tor_free(cp
));
954 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, tor_free(ent
));
955 smartlist_free(strings
);
956 smartlist_free(entries
);
960 /** Start time of directory request stats. */
961 static time_t start_of_dirreq_stats_interval
;
963 /** Initialize directory request stats. */
965 geoip_dirreq_stats_init(time_t now
)
967 start_of_dirreq_stats_interval
= now
;
970 /** Write dirreq statistics to $DATADIR/stats/dirreq-stats. */
972 geoip_dirreq_stats_write(time_t now
)
974 char *statsdir
= NULL
, *filename
= NULL
;
975 char *data_v2
= NULL
, *data_v3
= NULL
;
976 char written
[ISO_TIME_LEN
+1];
977 open_file_t
*open_file
= NULL
;
978 double v2_share
= 0.0, v3_share
= 0.0;
982 if (!get_options()->DirReqStatistics
)
985 /* Discard all items in the client history that are too old. */
986 geoip_remove_old_clients(start_of_dirreq_stats_interval
);
988 statsdir
= get_datadir_fname("stats");
989 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
991 filename
= get_datadir_fname2("stats", "dirreq-stats");
992 data_v2
= geoip_get_client_history_dirreq(now
,
993 GEOIP_CLIENT_NETWORKSTATUS_V2
);
994 data_v3
= geoip_get_client_history_dirreq(now
,
995 GEOIP_CLIENT_NETWORKSTATUS
);
996 format_iso_time(written
, now
);
997 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
1001 if (fprintf(out
, "dirreq-stats-end %s (%d s)\ndirreq-v3-ips %s\n"
1002 "dirreq-v2-ips %s\n", written
,
1003 (unsigned) (now
- start_of_dirreq_stats_interval
),
1004 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
1009 data_v2
= geoip_get_request_history(now
, GEOIP_CLIENT_NETWORKSTATUS_V2
);
1010 data_v3
= geoip_get_request_history(now
, GEOIP_CLIENT_NETWORKSTATUS
);
1011 if (fprintf(out
, "dirreq-v3-reqs %s\ndirreq-v2-reqs %s\n",
1012 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
1016 #define RESPONSE_GRANULARITY 8
1017 for (i
= 0; i
< GEOIP_NS_RESPONSE_NUM
; i
++) {
1018 ns_v2_responses
[i
] = round_uint32_to_next_multiple_of(
1019 ns_v2_responses
[i
], RESPONSE_GRANULARITY
);
1020 ns_v3_responses
[i
] = round_uint32_to_next_multiple_of(
1021 ns_v3_responses
[i
], RESPONSE_GRANULARITY
);
1023 #undef RESPONSE_GRANULARITY
1024 if (fprintf(out
, "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
1025 "not-found=%u,not-modified=%u,busy=%u\n",
1026 ns_v3_responses
[GEOIP_SUCCESS
],
1027 ns_v3_responses
[GEOIP_REJECT_NOT_ENOUGH_SIGS
],
1028 ns_v3_responses
[GEOIP_REJECT_UNAVAILABLE
],
1029 ns_v3_responses
[GEOIP_REJECT_NOT_FOUND
],
1030 ns_v3_responses
[GEOIP_REJECT_NOT_MODIFIED
],
1031 ns_v3_responses
[GEOIP_REJECT_BUSY
]) < 0)
1033 if (fprintf(out
, "dirreq-v2-resp ok=%u,unavailable=%u,"
1034 "not-found=%u,not-modified=%u,busy=%u\n",
1035 ns_v2_responses
[GEOIP_SUCCESS
],
1036 ns_v2_responses
[GEOIP_REJECT_UNAVAILABLE
],
1037 ns_v2_responses
[GEOIP_REJECT_NOT_FOUND
],
1038 ns_v2_responses
[GEOIP_REJECT_NOT_MODIFIED
],
1039 ns_v2_responses
[GEOIP_REJECT_BUSY
]) < 0)
1041 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
1042 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
1043 if (!geoip_get_mean_shares(now
, &v2_share
, &v3_share
)) {
1044 if (fprintf(out
, "dirreq-v2-share %0.2lf%%\n", v2_share
*100) < 0)
1046 if (fprintf(out
, "dirreq-v3-share %0.2lf%%\n", v3_share
*100) < 0)
1050 data_v2
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2
,
1052 data_v3
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS
,
1054 if (fprintf(out
, "dirreq-v3-direct-dl %s\ndirreq-v2-direct-dl %s\n",
1055 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
1059 data_v2
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2
,
1061 data_v3
= geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS
,
1063 if (fprintf(out
, "dirreq-v3-tunneled-dl %s\ndirreq-v2-tunneled-dl %s\n",
1064 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
1067 finish_writing_to_file(open_file
);
1070 /* Rotate request period */
1071 rotate_request_period();
1073 start_of_dirreq_stats_interval
= now
;
1077 abort_writing_to_file(open_file
);
1084 /** Start time of bridge stats. */
1085 static time_t start_of_bridge_stats_interval
;
1087 /** Initialize bridge stats. */
1089 geoip_bridge_stats_init(time_t now
)
1091 start_of_bridge_stats_interval
= now
;
1094 /** Parse the bridge statistics as they are written to extra-info
1095 * descriptors for being returned to controller clients. Return the
1096 * controller string if successful, or NULL otherwise. */
1098 parse_bridge_stats_controller(const char *stats_str
, time_t now
)
1100 char stats_end_str
[ISO_TIME_LEN
+1], stats_start_str
[ISO_TIME_LEN
+1],
1101 *controller_str
, *eos
, *eol
, *summary
;
1103 const char *BRIDGE_STATS_END
= "bridge-stats-end ";
1104 const char *BRIDGE_IPS
= "bridge-ips ";
1105 const char *BRIDGE_IPS_EMPTY_LINE
= "bridge-ips\n";
1107 time_t stats_end_time
;
1108 size_t controller_len
;
1110 tor_assert(stats_str
);
1112 /* Parse timestamp and number of seconds from
1113 "bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */
1114 tmp
= find_str_at_start_of_line(stats_str
, BRIDGE_STATS_END
);
1117 tmp
+= strlen(BRIDGE_STATS_END
);
1119 if (strlen(tmp
) < ISO_TIME_LEN
+ 6)
1121 strlcpy(stats_end_str
, tmp
, sizeof(stats_end_str
));
1122 if (parse_iso_time(stats_end_str
, &stats_end_time
) < 0)
1124 if (stats_end_time
< now
- (25*60*60) ||
1125 stats_end_time
> now
+ (1*60*60))
1127 seconds
= (int)strtol(tmp
+ ISO_TIME_LEN
+ 2, &eos
, 10);
1128 if (!eos
|| seconds
< 23*60*60)
1130 format_iso_time(stats_start_str
, stats_end_time
- seconds
);
1132 /* Parse: "bridge-ips CC=N,CC=N,..." */
1133 tmp
= find_str_at_start_of_line(stats_str
, BRIDGE_IPS
);
1135 tmp
+= strlen(BRIDGE_IPS
);
1136 tmp
= eat_whitespace_no_nl(tmp
);
1137 eol
= strchr(tmp
, '\n');
1139 summary
= tor_strndup(tmp
, eol
-tmp
);
1141 summary
= tor_strdup(tmp
);
1143 /* Look if there is an empty "bridge-ips" line */
1144 tmp
= find_str_at_start_of_line(stats_str
, BRIDGE_IPS_EMPTY_LINE
);
1147 summary
= tor_strdup("");
1150 controller_len
= strlen("TimeStarted=\"\" CountrySummary=") +
1151 strlen(summary
) + 42;
1152 controller_str
= tor_malloc(controller_len
);
1153 if (tor_snprintf(controller_str
, controller_len
,
1154 "TimeStarted=\"%s\" CountrySummary=%s",
1155 stats_start_str
, summary
) < 0) {
1156 tor_free(controller_str
);
1161 return controller_str
;
1164 /** Most recent bridge statistics formatted to be written to extra-info
1166 static char *bridge_stats_extrainfo
= NULL
;
1168 /** Most recent bridge statistics formatted to be returned to controller
1170 static char *bridge_stats_controller
= NULL
;
1172 /** Write bridge statistics to $DATADIR/stats/bridge-stats and return
1173 * when we should next try to write statistics. */
1175 geoip_bridge_stats_write(time_t now
)
1177 char *statsdir
= NULL
, *filename
= NULL
, *data
= NULL
,
1178 written
[ISO_TIME_LEN
+1], *out
= NULL
, *controller_str
;
1181 /* If we changed from relay to bridge recently, adapt starting time
1182 * of current measurements. */
1183 if (start_of_bridge_stats_interval
< client_history_starts
)
1184 start_of_bridge_stats_interval
= client_history_starts
;
1186 /* Check if 24 hours have passed since starting measurements. */
1187 if (now
< start_of_bridge_stats_interval
+
1188 DIR_ENTRY_RECORD_USAGE_RETAIN_IPS
)
1189 return start_of_bridge_stats_interval
+
1190 DIR_ENTRY_RECORD_USAGE_RETAIN_IPS
;
1192 /* Discard all items in the client history that are too old. */
1193 geoip_remove_old_clients(start_of_bridge_stats_interval
);
1195 statsdir
= get_datadir_fname("stats");
1196 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
1198 filename
= get_datadir_fname2("stats", "bridge-stats");
1199 data
= geoip_get_client_history_bridge(now
, GEOIP_CLIENT_CONNECT
);
1200 format_iso_time(written
, now
);
1201 len
= strlen("bridge-stats-end (999999 s)\nbridge-ips \n") +
1202 ISO_TIME_LEN
+ (data
? strlen(data
) : 0) + 42;
1203 out
= tor_malloc(len
);
1204 if (tor_snprintf(out
, len
, "bridge-stats-end %s (%u s)\nbridge-ips %s\n",
1205 written
, (unsigned) (now
- start_of_bridge_stats_interval
),
1206 data
? data
: "") < 0)
1208 write_str_to_file(filename
, out
, 0);
1209 controller_str
= parse_bridge_stats_controller(out
, now
);
1210 if (!controller_str
)
1212 start_of_bridge_stats_interval
= now
;
1213 tor_free(bridge_stats_extrainfo
);
1214 tor_free(bridge_stats_controller
);
1215 bridge_stats_extrainfo
= out
;
1217 bridge_stats_controller
= controller_str
;
1218 control_event_clients_seen(controller_str
);
1224 return start_of_bridge_stats_interval
+
1225 DIR_ENTRY_RECORD_USAGE_RETAIN_IPS
;
1228 /** Try to load the most recent bridge statistics from disk, unless we
1229 * have finished a measurement interval lately. */
1231 load_bridge_stats(time_t now
)
1233 char *statsdir
, *fname
=NULL
, *contents
, *controller_str
;
1234 if (bridge_stats_extrainfo
)
1236 statsdir
= get_datadir_fname("stats");
1237 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
1239 fname
= get_datadir_fname2("stats", "bridge-stats");
1240 contents
= read_file_to_str(fname
, RFTS_IGNORE_MISSING
, NULL
);
1242 controller_str
= parse_bridge_stats_controller(contents
, now
);
1243 if (controller_str
) {
1244 bridge_stats_extrainfo
= contents
;
1245 bridge_stats_controller
= controller_str
;
1255 /** Return most recent bridge statistics for inclusion in extra-info
1256 * descriptors, or NULL if we don't have recent bridge statistics. */
1258 geoip_get_bridge_stats_extrainfo(time_t now
)
1260 load_bridge_stats(now
);
1261 return bridge_stats_extrainfo
;
1264 /** Return most recent bridge statistics to be returned to controller
1265 * clients, or NULL if we don't have recent bridge statistics. */
1267 geoip_get_bridge_stats_controller(time_t now
)
1269 load_bridge_stats(now
);
1270 return bridge_stats_controller
;
1273 /** Start time of entry stats. */
1274 static time_t start_of_entry_stats_interval
;
1276 /** Initialize entry stats. */
1278 geoip_entry_stats_init(time_t now
)
1280 start_of_entry_stats_interval
= now
;
1283 /** Write entry statistics to $DATADIR/stats/entry-stats. */
1285 geoip_entry_stats_write(time_t now
)
1287 char *statsdir
= NULL
, *filename
= NULL
;
1289 char written
[ISO_TIME_LEN
+1];
1290 open_file_t
*open_file
= NULL
;
1293 if (!get_options()->EntryStatistics
)
1296 /* Discard all items in the client history that are too old. */
1297 geoip_remove_old_clients(start_of_entry_stats_interval
);
1299 statsdir
= get_datadir_fname("stats");
1300 if (check_private_dir(statsdir
, CPD_CREATE
) < 0)
1302 filename
= get_datadir_fname2("stats", "entry-stats");
1303 data
= geoip_get_client_history_dirreq(now
, GEOIP_CLIENT_CONNECT
);
1304 format_iso_time(written
, now
);
1305 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
1309 if (fprintf(out
, "entry-stats-end %s (%u s)\nentry-ips %s\n",
1310 written
, (unsigned) (now
- start_of_entry_stats_interval
),
1311 data
? data
: "") < 0)
1314 start_of_entry_stats_interval
= now
;
1316 finish_writing_to_file(open_file
);
1320 abort_writing_to_file(open_file
);
1326 /** Helper used to implement GETINFO ip-to-country/... controller command. */
1328 getinfo_helper_geoip(control_connection_t
*control_conn
,
1329 const char *question
, char **answer
)
1332 if (geoip_is_loaded() && !strcmpstart(question
, "ip-to-country/")) {
1336 question
+= strlen("ip-to-country/");
1337 if (tor_inet_aton(question
, &in
) != 0) {
1338 ip
= ntohl(in
.s_addr
);
1339 c
= geoip_get_country_by_ip(ip
);
1340 *answer
= tor_strdup(geoip_get_country_name(c
));
1346 /** Release all storage held by the GeoIP database. */
1348 clear_geoip_db(void)
1350 if (geoip_countries
) {
1351 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, tor_free(c
));
1352 smartlist_free(geoip_countries
);
1355 strmap_free(country_idxplus1_by_lc_code
, NULL
);
1356 if (geoip_entries
) {
1357 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, ent
, tor_free(ent
));
1358 smartlist_free(geoip_entries
);
1360 geoip_countries
= NULL
;
1361 country_idxplus1_by_lc_code
= NULL
;
1362 geoip_entries
= NULL
;
1365 /** Release all storage held in this file. */
1367 geoip_free_all(void)
1370 clientmap_entry_t
**ent
, **next
, *this;
1371 for (ent
= HT_START(clientmap
, &client_history
); ent
!= NULL
; ent
= next
) {
1373 next
= HT_NEXT_RMV(clientmap
, &client_history
, ent
);
1376 HT_CLEAR(clientmap
, &client_history
);
1379 dirreq_map_entry_t
**ent
, **next
, *this;
1380 for (ent
= HT_START(dirreqmap
, &dirreq_map
); ent
!= NULL
; ent
= next
) {
1382 next
= HT_NEXT_RMV(dirreqmap
, &dirreq_map
, ent
);
1385 HT_CLEAR(dirreqmap
, &dirreq_map
);