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);
15 static void dump_geoip_stats(void);
16 static void dump_entry_stats(void);
18 /** An entry from the GeoIP file: maps an IP range to a country. */
19 typedef struct geoip_entry_t
{
20 uint32_t ip_low
; /**< The lowest IP in the range, in host order */
21 uint32_t ip_high
; /**< The highest IP in the range, in host order */
22 intptr_t country
; /**< An index into geoip_countries */
25 /** For how many periods should we remember per-country request history? */
26 #define REQUEST_HIST_LEN 1
27 /** How long are the periods for which we should remember request history? */
28 #define REQUEST_HIST_PERIOD (24*60*60)
30 /** A per-country record for GeoIP request history. */
31 typedef struct geoip_country_t
{
33 uint32_t n_v2_ns_requests
[REQUEST_HIST_LEN
];
34 uint32_t n_v3_ns_requests
[REQUEST_HIST_LEN
];
37 /** A list of geoip_country_t */
38 static smartlist_t
*geoip_countries
= NULL
;
39 /** A map from lowercased country codes to their position in geoip_countries.
40 * The index is encoded in the pointer, and 1 is added so that NULL can mean
42 static strmap_t
*country_idxplus1_by_lc_code
= NULL
;
43 /** A list of all known geoip_entry_t, sorted by ip_low. */
44 static smartlist_t
*geoip_entries
= NULL
;
46 /** Return the index of the <b>country</b>'s entry in the GeoIP DB
47 * if it is a valid 2-letter country code, otherwise return -1.
50 geoip_get_country(const char *country
)
55 _idxplus1
= strmap_get_lc(country_idxplus1_by_lc_code
, country
);
59 idx
= ((uintptr_t)_idxplus1
)-1;
60 return (country_t
)idx
;
63 /** Add an entry to the GeoIP table, mapping all IPs between <b>low</b> and
64 * <b>high</b>, inclusive, to the 2-letter country code <b>country</b>.
67 geoip_add_entry(uint32_t low
, uint32_t high
, const char *country
)
76 _idxplus1
= strmap_get_lc(country_idxplus1_by_lc_code
, country
);
79 geoip_country_t
*c
= tor_malloc_zero(sizeof(geoip_country_t
));
80 strlcpy(c
->countrycode
, country
, sizeof(c
->countrycode
));
81 tor_strlower(c
->countrycode
);
82 smartlist_add(geoip_countries
, c
);
83 idx
= smartlist_len(geoip_countries
) - 1;
84 strmap_set_lc(country_idxplus1_by_lc_code
, country
, (void*)(idx
+1));
86 idx
= ((uintptr_t)_idxplus1
)-1;
89 geoip_country_t
*c
= smartlist_get(geoip_countries
, idx
);
90 tor_assert(!strcasecmp(c
->countrycode
, country
));
92 ent
= tor_malloc_zero(sizeof(geoip_entry_t
));
96 smartlist_add(geoip_entries
, ent
);
99 /** Add an entry to the GeoIP table, parsing it from <b>line</b>. The
100 * format is as for geoip_load_file(). */
102 geoip_parse_entry(const char *line
)
104 unsigned int low
, high
;
106 if (!geoip_countries
) {
107 geoip_countries
= smartlist_create();
108 geoip_entries
= smartlist_create();
109 country_idxplus1_by_lc_code
= strmap_new();
111 while (TOR_ISSPACE(*line
))
115 if (sscanf(line
,"%u,%u,%2s", &low
, &high
, b
) == 3) {
116 geoip_add_entry(low
, high
, b
);
118 } else if (sscanf(line
,"\"%u\",\"%u\",\"%2s\",", &low
, &high
, b
) == 3) {
119 geoip_add_entry(low
, high
, b
);
122 log_warn(LD_GENERAL
, "Unable to parse line from GEOIP file: %s",
128 /** Sorting helper: return -1, 1, or 0 based on comparison of two
131 _geoip_compare_entries(const void **_a
, const void **_b
)
133 const geoip_entry_t
*a
= *_a
, *b
= *_b
;
134 if (a
->ip_low
< b
->ip_low
)
136 else if (a
->ip_low
> b
->ip_low
)
142 /** bsearch helper: return -1, 1, or 0 based on comparison of an IP (a pointer
143 * to a uint32_t in host order) to a geoip_entry_t */
145 _geoip_compare_key_to_entry(const void *_key
, const void **_member
)
147 const uint32_t addr
= *(uint32_t *)_key
;
148 const geoip_entry_t
*entry
= *_member
;
149 if (addr
< entry
->ip_low
)
151 else if (addr
> entry
->ip_high
)
157 /** Return 1 if we should collect geoip stats on bridge users, and
158 * include them in our extrainfo descriptor. Else return 0. */
160 should_record_bridge_info(or_options_t
*options
)
162 return options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
;
165 /** Clear the GeoIP database and reload it from the file
166 * <b>filename</b>. Return 0 on success, -1 on failure.
168 * Recognized line formats are:
169 * INTIPLOW,INTIPHIGH,CC
171 * "INTIPLOW","INTIPHIGH","CC","CC3","COUNTRY NAME"
172 * where INTIPLOW and INTIPHIGH are IPv4 addresses encoded as 4-byte unsigned
173 * integers, and CC is a country code.
175 * It also recognizes, and skips over, blank lines and lines that start
176 * with '#' (comments).
179 geoip_load_file(const char *filename
, or_options_t
*options
)
182 const char *msg
= "";
183 int severity
= options_need_geoip_info(options
, &msg
) ? LOG_WARN
: LOG_INFO
;
185 if (!(f
= fopen(filename
, "r"))) {
186 log_fn(severity
, LD_GENERAL
, "Failed to open GEOIP file %s. %s",
190 if (!geoip_countries
) {
191 geoip_country_t
*geoip_unresolved
;
192 geoip_countries
= smartlist_create();
193 /* Add a geoip_country_t for requests that could not be resolved to a
194 * country as first element (index 0) to geoip_countries. */
195 geoip_unresolved
= tor_malloc_zero(sizeof(geoip_country_t
));
196 strlcpy(geoip_unresolved
->countrycode
, "??",
197 sizeof(geoip_unresolved
->countrycode
));
198 smartlist_add(geoip_countries
, geoip_unresolved
);
199 country_idxplus1_by_lc_code
= strmap_new();
202 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, e
, tor_free(e
));
203 smartlist_free(geoip_entries
);
205 geoip_entries
= smartlist_create();
206 log_notice(LD_GENERAL
, "Parsing GEOIP file.");
209 if (fgets(buf
, (int)sizeof(buf
), f
) == NULL
)
211 /* FFFF track full country name. */
212 geoip_parse_entry(buf
);
214 /*XXXX abort and return -1 if no entries/illformed?*/
217 smartlist_sort(geoip_entries
, _geoip_compare_entries
);
219 /* Okay, now we need to maybe change our mind about what is in which
221 refresh_all_country_info();
226 /** Given an IP address in host order, return a number representing the
227 * country to which that address belongs, or -1 for unknown. The return value
228 * will always be less than geoip_get_n_countries(). To decode it,
229 * call geoip_get_country_name().
232 geoip_get_country_by_ip(uint32_t ipaddr
)
237 ent
= smartlist_bsearch(geoip_entries
, &ipaddr
, _geoip_compare_key_to_entry
);
238 return ent
? (int)ent
->country
: -1;
241 /** Return the number of countries recognized by the GeoIP database. */
243 geoip_get_n_countries(void)
245 return (int) smartlist_len(geoip_countries
);
248 /** Return the two-letter country code associated with the number <b>num</b>,
249 * or "??" for an unknown value. */
251 geoip_get_country_name(country_t num
)
253 if (geoip_countries
&& num
>= 0 && num
< smartlist_len(geoip_countries
)) {
254 geoip_country_t
*c
= smartlist_get(geoip_countries
, num
);
255 return c
->countrycode
;
260 /** Return true iff we have loaded a GeoIP database.*/
262 geoip_is_loaded(void)
264 return geoip_countries
!= NULL
&& geoip_entries
!= NULL
;
267 /** Entry in a map from IP address to the last time we've seen an incoming
268 * connection from that IP address. Used by bridges only, to track which
269 * countries have them blocked. */
270 typedef struct clientmap_entry_t
{
271 HT_ENTRY(clientmap_entry_t
) node
;
273 unsigned int last_seen_in_minutes
:30;
274 unsigned int action
:2;
277 #define ACTION_MASK 3
279 /** Map from client IP address to last time seen. */
280 static HT_HEAD(clientmap
, clientmap_entry_t
) client_history
=
282 /** Time at which we started tracking client IP history. */
283 static time_t client_history_starts
= 0;
285 /** When did the current period of checking per-country request history
287 static time_t current_request_period_starts
= 0;
288 /** How many older request periods are we remembering? */
289 static int n_old_request_periods
= 0;
291 /** Hashtable helper: compute a hash of a clientmap_entry_t. */
292 static INLINE
unsigned
293 clientmap_entry_hash(const clientmap_entry_t
*a
)
295 return ht_improve_hash((unsigned) a
->ipaddr
);
297 /** Hashtable helper: compare two clientmap_entry_t values for equality. */
299 clientmap_entries_eq(const clientmap_entry_t
*a
, const clientmap_entry_t
*b
)
301 return a
->ipaddr
== b
->ipaddr
&& a
->action
== b
->action
;
304 HT_PROTOTYPE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
305 clientmap_entries_eq
);
306 HT_GENERATE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
307 clientmap_entries_eq
, 0.6, malloc
, realloc
, free
);
309 /** Note that we've seen a client connect from the IP <b>addr</b> (host order)
310 * at time <b>now</b>. Ignored by all but bridges and directories if
311 * configured accordingly. */
313 geoip_note_client_seen(geoip_client_action_t action
,
314 uint32_t addr
, time_t now
)
316 or_options_t
*options
= get_options();
317 clientmap_entry_t lookup
, *ent
;
318 if (action
== GEOIP_CLIENT_CONNECT
) {
319 #ifdef ENABLE_ENTRY_STATS
320 if (!options
->EntryStatistics
)
323 if (!(options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
))
326 /* Did we recently switch from bridge to relay or back? */
327 if (client_history_starts
> now
)
330 #ifndef ENABLE_GEOIP_STATS
333 if (options
->BridgeRelay
|| options
->BridgeAuthoritativeDir
)
338 /* Rotate the current request period. */
339 while (current_request_period_starts
+ REQUEST_HIST_PERIOD
< now
) {
340 if (!geoip_countries
)
341 geoip_countries
= smartlist_create();
342 if (!current_request_period_starts
) {
343 current_request_period_starts
= now
;
346 /* Also discard all items in the client history that are too old.
347 * (This only works here because bridge and directory stats are
348 * independent. Otherwise, we'd only want to discard those items
349 * with action GEOIP_CLIENT_NETWORKSTATUS{_V2}.) */
350 geoip_remove_old_clients(current_request_period_starts
);
351 /* Before rotating, write the current stats to disk. */
353 if (get_options()->EntryStatistics
)
355 /* Now rotate request period */
356 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
357 memmove(&c
->n_v2_ns_requests
[0], &c
->n_v2_ns_requests
[1],
358 sizeof(uint32_t)*(REQUEST_HIST_LEN
-1));
359 memmove(&c
->n_v3_ns_requests
[0], &c
->n_v3_ns_requests
[1],
360 sizeof(uint32_t)*(REQUEST_HIST_LEN
-1));
361 c
->n_v2_ns_requests
[REQUEST_HIST_LEN
-1] = 0;
362 c
->n_v3_ns_requests
[REQUEST_HIST_LEN
-1] = 0;
364 current_request_period_starts
+= REQUEST_HIST_PERIOD
;
365 if (n_old_request_periods
< REQUEST_HIST_LEN
-1)
366 ++n_old_request_periods
;
369 lookup
.ipaddr
= addr
;
370 lookup
.action
= (int)action
;
371 ent
= HT_FIND(clientmap
, &client_history
, &lookup
);
373 ent
->last_seen_in_minutes
= now
/ 60;
375 ent
= tor_malloc_zero(sizeof(clientmap_entry_t
));
377 ent
->last_seen_in_minutes
= now
/ 60;
378 ent
->action
= (int)action
;
379 HT_INSERT(clientmap
, &client_history
, ent
);
382 if (action
== GEOIP_CLIENT_NETWORKSTATUS
||
383 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
) {
384 int country_idx
= geoip_get_country_by_ip(addr
);
386 country_idx
= 0; /** unresolved requests are stored at index 0. */
387 if (country_idx
>= 0 && country_idx
< smartlist_len(geoip_countries
)) {
388 geoip_country_t
*country
= smartlist_get(geoip_countries
, country_idx
);
389 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
390 ++country
->n_v3_ns_requests
[REQUEST_HIST_LEN
-1];
392 ++country
->n_v2_ns_requests
[REQUEST_HIST_LEN
-1];
396 if (!client_history_starts
) {
397 client_history_starts
= now
;
398 current_request_period_starts
= now
;
402 /** HT_FOREACH helper: remove a clientmap_entry_t from the hashtable if it's
403 * older than a certain time. */
405 _remove_old_client_helper(struct clientmap_entry_t
*ent
, void *_cutoff
)
407 time_t cutoff
= *(time_t*)_cutoff
/ 60;
408 if (ent
->last_seen_in_minutes
< cutoff
) {
416 /** Forget about all clients that haven't connected since <b>cutoff</b>.
417 * If <b>cutoff</b> is in the future, clients won't be added to the history
418 * until this time is reached. This is useful to prevent relays that switch
419 * to bridges from reporting unbelievable numbers of clients. */
421 geoip_remove_old_clients(time_t cutoff
)
423 clientmap_HT_FOREACH_FN(&client_history
,
424 _remove_old_client_helper
,
426 if (client_history_starts
< cutoff
)
427 client_history_starts
= cutoff
;
430 /** Do not mention any country from which fewer than this number of IPs have
431 * connected. This conceivably avoids reporting information that could
432 * deanonymize users, though analysis is lacking. */
433 #define MIN_IPS_TO_NOTE_COUNTRY 1
434 /** Do not report any geoip data at all if we have fewer than this number of
435 * IPs to report about. */
436 #define MIN_IPS_TO_NOTE_ANYTHING 1
437 /** When reporting geoip data about countries, round up to the nearest
438 * multiple of this value. */
439 #define IP_GRANULARITY 8
441 /** Return the time at which we started recording geoip data. */
443 geoip_get_history_start(void)
445 return client_history_starts
;
448 /** Helper type: used to sort per-country totals by value. */
449 typedef struct c_hist_t
{
450 char country
[3]; /**< Two-letter country code. */
451 unsigned total
; /**< Total IP addresses seen in this country. */
454 /** Sorting helper: return -1, 1, or 0 based on comparison of two
455 * geoip_entry_t. Sort in descending order of total, and then by country
458 _c_hist_compare(const void **_a
, const void **_b
)
460 const c_hist_t
*a
= *_a
, *b
= *_b
;
461 if (a
->total
> b
->total
)
463 else if (a
->total
< b
->total
)
466 return strcmp(a
->country
, b
->country
);
469 /** How long do we have to have observed per-country request history before we
470 * are willing to talk about it? */
471 #define GEOIP_MIN_OBSERVATION_TIME (12*60*60)
473 /** Return the lowest x such that x is at least <b>number</b>, and x modulo
474 * <b>divisor</b> == 0. */
475 static INLINE
unsigned
476 round_to_next_multiple_of(unsigned number
, unsigned divisor
)
478 number
+= divisor
- 1;
479 number
-= number
% divisor
;
483 /** Return a newly allocated comma-separated string containing entries for all
484 * the countries from which we've seen enough clients connect. The entry
485 * format is cc=num where num is the number of IPs we've seen connecting from
486 * that country, and cc is a lowercased country code. Returns NULL if we don't
487 * want to export geoip data yet. */
489 geoip_get_client_history(time_t now
, geoip_client_action_t action
)
492 int min_observation_time
= GEOIP_MIN_OBSERVATION_TIME
;
493 #ifdef ENABLE_GEOIP_STATS
494 min_observation_time
= DIR_RECORD_USAGE_MIN_OBSERVATION_TIME
;
496 if (!geoip_is_loaded())
498 if (client_history_starts
< (now
- min_observation_time
)) {
500 smartlist_t
*chunks
= NULL
;
501 smartlist_t
*entries
= NULL
;
502 int n_countries
= geoip_get_n_countries();
504 clientmap_entry_t
**ent
;
505 unsigned *counts
= tor_malloc_zero(sizeof(unsigned)*n_countries
);
507 unsigned granularity
= IP_GRANULARITY
;
508 #ifdef ENABLE_GEOIP_STATS
509 granularity
= DIR_RECORD_USAGE_GRANULARITY
;
511 HT_FOREACH(ent
, clientmap
, &client_history
) {
513 if ((*ent
)->action
!= (int)action
)
515 country
= geoip_get_country_by_ip((*ent
)->ipaddr
);
517 country
= 0; /** unresolved requests are stored at index 0. */
518 tor_assert(0 <= country
&& country
< n_countries
);
522 /* Don't record anything if we haven't seen enough IPs. */
523 if (total
< MIN_IPS_TO_NOTE_ANYTHING
)
525 /* Make a list of c_hist_t */
526 entries
= smartlist_create();
527 for (i
= 0; i
< n_countries
; ++i
) {
528 unsigned c
= counts
[i
];
529 const char *countrycode
;
531 /* Only report a country if it has a minimum number of IPs. */
532 if (c
>= MIN_IPS_TO_NOTE_COUNTRY
) {
533 c
= round_to_next_multiple_of(c
, granularity
);
534 countrycode
= geoip_get_country_name(i
);
535 ent
= tor_malloc(sizeof(c_hist_t
));
536 strlcpy(ent
->country
, countrycode
, sizeof(ent
->country
));
538 smartlist_add(entries
, ent
);
541 /* Sort entries. Note that we must do this _AFTER_ rounding, or else
542 * the sort order could leak info. */
543 smartlist_sort(entries
, _c_hist_compare
);
545 /* Build the result. */
546 chunks
= smartlist_create();
547 SMARTLIST_FOREACH(entries
, c_hist_t
*, ch
, {
548 tor_snprintf(buf
, sizeof(buf
), "%s=%u", ch
->country
, ch
->total
);
549 smartlist_add(chunks
, tor_strdup(buf
));
551 result
= smartlist_join_strings(chunks
, ",", 0, NULL
);
555 SMARTLIST_FOREACH(chunks
, char *, c
, tor_free(c
));
556 smartlist_free(chunks
);
559 SMARTLIST_FOREACH(entries
, c_hist_t
*, c
, tor_free(c
));
560 smartlist_free(entries
);
566 /** Return a newly allocated string holding the per-country request history
567 * for <b>action</b> in a format suitable for an extra-info document, or NULL
570 geoip_get_request_history(time_t now
, geoip_client_action_t action
)
572 smartlist_t
*entries
, *strings
;
574 unsigned granularity
= IP_GRANULARITY
;
575 int min_observation_time
= GEOIP_MIN_OBSERVATION_TIME
;
576 #ifdef ENABLE_GEOIP_STATS
577 granularity
= DIR_RECORD_USAGE_GRANULARITY
;
578 min_observation_time
= DIR_RECORD_USAGE_MIN_OBSERVATION_TIME
;
581 if (client_history_starts
>= (now
- min_observation_time
))
583 if (action
!= GEOIP_CLIENT_NETWORKSTATUS
&&
584 action
!= GEOIP_CLIENT_NETWORKSTATUS_V2
)
586 if (!geoip_countries
)
589 entries
= smartlist_create();
590 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
591 uint32_t *n
= (action
== GEOIP_CLIENT_NETWORKSTATUS
)
592 ? c
->n_v3_ns_requests
: c
->n_v2_ns_requests
;
596 for (i
=0; i
< REQUEST_HIST_LEN
; ++i
)
600 ent
= tor_malloc_zero(sizeof(c_hist_t
));
601 strlcpy(ent
->country
, c
->countrycode
, sizeof(ent
->country
));
602 ent
->total
= round_to_next_multiple_of(tot
, granularity
);
603 smartlist_add(entries
, ent
);
605 smartlist_sort(entries
, _c_hist_compare
);
607 strings
= smartlist_create();
608 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, {
610 tor_snprintf(buf
, sizeof(buf
), "%s=%u", ent
->country
, ent
->total
);
611 smartlist_add(strings
, tor_strdup(buf
));
613 result
= smartlist_join_strings(strings
, ",", 0, NULL
);
614 SMARTLIST_FOREACH(strings
, char *, cp
, tor_free(cp
));
615 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, tor_free(ent
));
616 smartlist_free(strings
);
617 smartlist_free(entries
);
621 /** Store all our geoip statistics into $DATADIR/geoip-stats. */
623 dump_geoip_stats(void)
625 #ifdef ENABLE_GEOIP_STATS
626 time_t now
= time(NULL
);
627 time_t request_start
;
628 char *filename
= get_datadir_fname("geoip-stats");
629 char *data_v2
= NULL
, *data_v3
= NULL
;
630 char since
[ISO_TIME_LEN
+1], written
[ISO_TIME_LEN
+1];
631 open_file_t
*open_file
= NULL
;
632 double v2_share
= 0.0, v3_share
= 0.0;
635 data_v2
= geoip_get_client_history(now
, GEOIP_CLIENT_NETWORKSTATUS_V2
);
636 data_v3
= geoip_get_client_history(now
, GEOIP_CLIENT_NETWORKSTATUS
);
637 format_iso_time(since
, geoip_get_history_start());
638 format_iso_time(written
, now
);
639 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
643 if (fprintf(out
, "written %s\nstarted-at %s\nns-ips %s\nns-v2-ips %s\n",
645 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
650 request_start
= current_request_period_starts
-
651 (n_old_request_periods
* REQUEST_HIST_PERIOD
);
652 format_iso_time(since
, request_start
);
653 data_v2
= geoip_get_request_history(now
, GEOIP_CLIENT_NETWORKSTATUS_V2
);
654 data_v3
= geoip_get_request_history(now
, GEOIP_CLIENT_NETWORKSTATUS
);
655 if (fprintf(out
, "requests-start %s\nn-ns-reqs %s\nn-v2-ns-reqs %s\n",
657 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
659 if (!router_get_my_share_of_directory_requests(&v2_share
, &v3_share
)) {
660 if (fprintf(out
, "v2-ns-share %0.2lf%%\n", v2_share
*100) < 0)
662 if (fprintf(out
, "v3-ns-share %0.2lf%%\n", v3_share
*100) < 0)
666 finish_writing_to_file(open_file
);
670 abort_writing_to_file(open_file
);
677 /** Store all our geoip statistics as entry guards into
678 * $DATADIR/entry-stats. */
680 dump_entry_stats(void)
682 #ifdef ENABLE_ENTRY_STATS
683 time_t now
= time(NULL
);
684 char *filename
= get_datadir_fname("entry-stats");
686 char since
[ISO_TIME_LEN
+1], written
[ISO_TIME_LEN
+1];
687 open_file_t
*open_file
= NULL
;
690 data
= geoip_get_client_history(now
, GEOIP_CLIENT_CONNECT
);
691 format_iso_time(since
, geoip_get_history_start());
692 format_iso_time(written
, now
);
693 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
697 if (fprintf(out
, "written %s\nstarted-at %s\nips %s\n",
698 written
, since
, data
? data
: "") < 0)
701 finish_writing_to_file(open_file
);
705 abort_writing_to_file(open_file
);
711 /** Helper used to implement GETINFO ip-to-country/... controller command. */
713 getinfo_helper_geoip(control_connection_t
*control_conn
,
714 const char *question
, char **answer
)
717 if (geoip_is_loaded() && !strcmpstart(question
, "ip-to-country/")) {
721 question
+= strlen("ip-to-country/");
722 if (tor_inet_aton(question
, &in
) != 0) {
723 ip
= ntohl(in
.s_addr
);
724 c
= geoip_get_country_by_ip(ip
);
725 *answer
= tor_strdup(geoip_get_country_name(c
));
731 /** Release all storage held by the GeoIP database. */
735 if (geoip_countries
) {
736 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, tor_free(c
));
737 smartlist_free(geoip_countries
);
739 if (country_idxplus1_by_lc_code
)
740 strmap_free(country_idxplus1_by_lc_code
, NULL
);
742 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, ent
, tor_free(ent
));
743 smartlist_free(geoip_entries
);
745 geoip_countries
= NULL
;
746 country_idxplus1_by_lc_code
= NULL
;
747 geoip_entries
= NULL
;
750 /** Release all storage held in this file. */
754 clientmap_entry_t
**ent
, **next
, *this;
755 for (ent
= HT_START(clientmap
, &client_history
); ent
!= NULL
; ent
= next
) {
757 next
= HT_NEXT_RMV(clientmap
, &client_history
, ent
);
760 HT_CLEAR(clientmap
, &client_history
);