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_countries
= smartlist_create();
192 country_idxplus1_by_lc_code
= strmap_new();
195 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, e
, tor_free(e
));
196 smartlist_free(geoip_entries
);
198 geoip_entries
= smartlist_create();
199 log_notice(LD_GENERAL
, "Parsing GEOIP file.");
202 if (fgets(buf
, (int)sizeof(buf
), f
) == NULL
)
204 /* FFFF track full country name. */
205 geoip_parse_entry(buf
);
207 /*XXXX abort and return -1 if no entries/illformed?*/
210 smartlist_sort(geoip_entries
, _geoip_compare_entries
);
212 /* Okay, now we need to maybe change our mind about what is in which
214 refresh_all_country_info();
219 /** Given an IP address in host order, return a number representing the
220 * country to which that address belongs, or -1 for unknown. The return value
221 * will always be less than geoip_get_n_countries(). To decode it,
222 * call geoip_get_country_name().
225 geoip_get_country_by_ip(uint32_t ipaddr
)
230 ent
= smartlist_bsearch(geoip_entries
, &ipaddr
, _geoip_compare_key_to_entry
);
231 return ent
? (int)ent
->country
: -1;
234 /** Return the number of countries recognized by the GeoIP database. */
236 geoip_get_n_countries(void)
238 return (int) smartlist_len(geoip_countries
);
241 /** Return the two-letter country code associated with the number <b>num</b>,
242 * or "??" for an unknown value. */
244 geoip_get_country_name(country_t num
)
246 if (geoip_countries
&& num
>= 0 && num
< smartlist_len(geoip_countries
)) {
247 geoip_country_t
*c
= smartlist_get(geoip_countries
, num
);
248 return c
->countrycode
;
253 /** Return true iff we have loaded a GeoIP database.*/
255 geoip_is_loaded(void)
257 return geoip_countries
!= NULL
&& geoip_entries
!= NULL
;
260 /** Entry in a map from IP address to the last time we've seen an incoming
261 * connection from that IP address. Used by bridges only, to track which
262 * countries have them blocked. */
263 typedef struct clientmap_entry_t
{
264 HT_ENTRY(clientmap_entry_t
) node
;
266 unsigned int last_seen_in_minutes
:30;
267 unsigned int action
:2;
270 #define ACTION_MASK 3
272 /** Map from client IP address to last time seen. */
273 static HT_HEAD(clientmap
, clientmap_entry_t
) client_history
=
275 /** Time at which we started tracking client IP history. */
276 static time_t client_history_starts
= 0;
278 /** When did the current period of checking per-country request history
280 static time_t current_request_period_starts
= 0;
281 /** How many older request periods are we remembering? */
282 static int n_old_request_periods
= 0;
284 /** Hashtable helper: compute a hash of a clientmap_entry_t. */
285 static INLINE
unsigned
286 clientmap_entry_hash(const clientmap_entry_t
*a
)
288 return ht_improve_hash((unsigned) a
->ipaddr
);
290 /** Hashtable helper: compare two clientmap_entry_t values for equality. */
292 clientmap_entries_eq(const clientmap_entry_t
*a
, const clientmap_entry_t
*b
)
294 return a
->ipaddr
== b
->ipaddr
&& a
->action
== b
->action
;
297 HT_PROTOTYPE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
298 clientmap_entries_eq
);
299 HT_GENERATE(clientmap
, clientmap_entry_t
, node
, clientmap_entry_hash
,
300 clientmap_entries_eq
, 0.6, malloc
, realloc
, free
);
302 /** Note that we've seen a client connect from the IP <b>addr</b> (host order)
303 * at time <b>now</b>. Ignored by all but bridges and directories if
304 * configured accordingly. */
306 geoip_note_client_seen(geoip_client_action_t action
,
307 uint32_t addr
, time_t now
)
309 or_options_t
*options
= get_options();
310 clientmap_entry_t lookup
, *ent
;
311 if (action
== GEOIP_CLIENT_CONNECT
) {
312 #ifdef ENABLE_ENTRY_STATS
313 if (!options
->EntryStatistics
)
316 if (!(options
->BridgeRelay
&& options
->BridgeRecordUsageByCountry
))
319 /* Did we recently switch from bridge to relay or back? */
320 if (client_history_starts
> now
)
323 #ifndef ENABLE_GEOIP_STATS
326 if (options
->BridgeRelay
|| options
->BridgeAuthoritativeDir
)
331 /* Rotate the current request period. */
332 while (current_request_period_starts
+ REQUEST_HIST_PERIOD
< now
) {
333 if (!geoip_countries
)
334 geoip_countries
= smartlist_create();
335 if (!current_request_period_starts
) {
336 current_request_period_starts
= now
;
339 /* Also discard all items in the client history that are too old.
340 * (This only works here because bridge and directory stats are
341 * independent. Otherwise, we'd only want to discard those items
342 * with action GEOIP_CLIENT_NETWORKSTATUS{_V2}.) */
343 geoip_remove_old_clients(current_request_period_starts
);
344 /* Before rotating, write the current stats to disk. */
346 if (get_options()->EntryStatistics
)
348 /* Now rotate request period */
349 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
350 memmove(&c
->n_v2_ns_requests
[0], &c
->n_v2_ns_requests
[1],
351 sizeof(uint32_t)*(REQUEST_HIST_LEN
-1));
352 memmove(&c
->n_v3_ns_requests
[0], &c
->n_v3_ns_requests
[1],
353 sizeof(uint32_t)*(REQUEST_HIST_LEN
-1));
354 c
->n_v2_ns_requests
[REQUEST_HIST_LEN
-1] = 0;
355 c
->n_v3_ns_requests
[REQUEST_HIST_LEN
-1] = 0;
357 current_request_period_starts
+= REQUEST_HIST_PERIOD
;
358 if (n_old_request_periods
< REQUEST_HIST_LEN
-1)
359 ++n_old_request_periods
;
362 lookup
.ipaddr
= addr
;
363 lookup
.action
= (int)action
;
364 ent
= HT_FIND(clientmap
, &client_history
, &lookup
);
366 ent
->last_seen_in_minutes
= now
/ 60;
368 ent
= tor_malloc_zero(sizeof(clientmap_entry_t
));
370 ent
->last_seen_in_minutes
= now
/ 60;
371 ent
->action
= (int)action
;
372 HT_INSERT(clientmap
, &client_history
, ent
);
375 if (action
== GEOIP_CLIENT_NETWORKSTATUS
||
376 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
) {
377 int country_idx
= geoip_get_country_by_ip(addr
);
378 if (country_idx
>= 0 && country_idx
< smartlist_len(geoip_countries
)) {
379 geoip_country_t
*country
= smartlist_get(geoip_countries
, country_idx
);
380 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
381 ++country
->n_v3_ns_requests
[REQUEST_HIST_LEN
-1];
383 ++country
->n_v2_ns_requests
[REQUEST_HIST_LEN
-1];
387 if (!client_history_starts
) {
388 client_history_starts
= now
;
389 current_request_period_starts
= now
;
393 /** HT_FOREACH helper: remove a clientmap_entry_t from the hashtable if it's
394 * older than a certain time. */
396 _remove_old_client_helper(struct clientmap_entry_t
*ent
, void *_cutoff
)
398 time_t cutoff
= *(time_t*)_cutoff
/ 60;
399 if (ent
->last_seen_in_minutes
< cutoff
) {
407 /** Forget about all clients that haven't connected since <b>cutoff</b>.
408 * If <b>cutoff</b> is in the future, clients won't be added to the history
409 * until this time is reached. This is useful to prevent relays that switch
410 * to bridges from reporting unbelievable numbers of clients. */
412 geoip_remove_old_clients(time_t cutoff
)
414 clientmap_HT_FOREACH_FN(&client_history
,
415 _remove_old_client_helper
,
417 if (client_history_starts
< cutoff
)
418 client_history_starts
= cutoff
;
421 #ifdef ENABLE_GEOIP_STATS
422 /** How many responses are we giving to clients requesting v2 network
424 static uint32_t ns_v2_responses
[GEOIP_NS_RESPONSE_NUM
];
426 /** How many responses are we giving to clients requesting v3 network
428 static uint32_t ns_v3_responses
[GEOIP_NS_RESPONSE_NUM
];
431 /** Note that we've rejected a client's request for a v2 or v3 network
432 * status, encoded in <b>action</b> for reason <b>reason</b> at time
435 geoip_note_ns_response(geoip_client_action_t action
,
436 geoip_ns_response_t response
)
438 #ifdef ENABLE_GEOIP_STATS
439 static int arrays_initialized
= 0;
440 if (!arrays_initialized
) {
441 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
442 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
443 arrays_initialized
= 1;
445 tor_assert(action
== GEOIP_CLIENT_NETWORKSTATUS
||
446 action
== GEOIP_CLIENT_NETWORKSTATUS_V2
);
447 tor_assert(response
< GEOIP_NS_RESPONSE_NUM
);
448 if (action
== GEOIP_CLIENT_NETWORKSTATUS
)
449 ns_v3_responses
[response
]++;
451 ns_v2_responses
[response
]++;
458 /** Do not mention any country from which fewer than this number of IPs have
459 * connected. This conceivably avoids reporting information that could
460 * deanonymize users, though analysis is lacking. */
461 #define MIN_IPS_TO_NOTE_COUNTRY 1
462 /** Do not report any geoip data at all if we have fewer than this number of
463 * IPs to report about. */
464 #define MIN_IPS_TO_NOTE_ANYTHING 1
465 /** When reporting geoip data about countries, round up to the nearest
466 * multiple of this value. */
467 #define IP_GRANULARITY 8
469 /** Return the time at which we started recording geoip data. */
471 geoip_get_history_start(void)
473 return client_history_starts
;
476 /** Helper type: used to sort per-country totals by value. */
477 typedef struct c_hist_t
{
478 char country
[3]; /**< Two-letter country code. */
479 unsigned total
; /**< Total IP addresses seen in this country. */
482 /** Sorting helper: return -1, 1, or 0 based on comparison of two
483 * geoip_entry_t. Sort in descending order of total, and then by country
486 _c_hist_compare(const void **_a
, const void **_b
)
488 const c_hist_t
*a
= *_a
, *b
= *_b
;
489 if (a
->total
> b
->total
)
491 else if (a
->total
< b
->total
)
494 return strcmp(a
->country
, b
->country
);
497 /** How long do we have to have observed per-country request history before we
498 * are willing to talk about it? */
499 #define GEOIP_MIN_OBSERVATION_TIME (12*60*60)
501 /** Return a newly allocated comma-separated string containing entries for all
502 * the countries from which we've seen enough clients connect. The entry
503 * format is cc=num where num is the number of IPs we've seen connecting from
504 * that country, and cc is a lowercased country code. Returns NULL if we don't
505 * want to export geoip data yet. */
507 geoip_get_client_history(time_t now
, geoip_client_action_t action
)
510 int min_observation_time
= GEOIP_MIN_OBSERVATION_TIME
;
511 #ifdef ENABLE_GEOIP_STATS
512 min_observation_time
= DIR_RECORD_USAGE_MIN_OBSERVATION_TIME
;
514 if (!geoip_is_loaded())
516 if (client_history_starts
< (now
- min_observation_time
)) {
518 smartlist_t
*chunks
= NULL
;
519 smartlist_t
*entries
= NULL
;
520 int n_countries
= geoip_get_n_countries();
522 clientmap_entry_t
**ent
;
523 unsigned *counts
= tor_malloc_zero(sizeof(unsigned)*n_countries
);
525 unsigned granularity
= IP_GRANULARITY
;
526 #ifdef ENABLE_GEOIP_STATS
527 granularity
= DIR_RECORD_USAGE_GRANULARITY
;
529 HT_FOREACH(ent
, clientmap
, &client_history
) {
531 if ((*ent
)->action
!= (int)action
)
533 country
= geoip_get_country_by_ip((*ent
)->ipaddr
);
536 tor_assert(0 <= country
&& country
< n_countries
);
540 /* Don't record anything if we haven't seen enough IPs. */
541 if (total
< MIN_IPS_TO_NOTE_ANYTHING
)
543 /* Make a list of c_hist_t */
544 entries
= smartlist_create();
545 for (i
= 0; i
< n_countries
; ++i
) {
546 unsigned c
= counts
[i
];
547 const char *countrycode
;
549 /* Only report a country if it has a minimum number of IPs. */
550 if (c
>= MIN_IPS_TO_NOTE_COUNTRY
) {
551 c
= round_to_next_multiple_of(c
, granularity
);
552 countrycode
= geoip_get_country_name(i
);
553 ent
= tor_malloc(sizeof(c_hist_t
));
554 strlcpy(ent
->country
, countrycode
, sizeof(ent
->country
));
556 smartlist_add(entries
, ent
);
559 /* Sort entries. Note that we must do this _AFTER_ rounding, or else
560 * the sort order could leak info. */
561 smartlist_sort(entries
, _c_hist_compare
);
563 /* Build the result. */
564 chunks
= smartlist_create();
565 SMARTLIST_FOREACH(entries
, c_hist_t
*, ch
, {
566 tor_snprintf(buf
, sizeof(buf
), "%s=%u", ch
->country
, ch
->total
);
567 smartlist_add(chunks
, tor_strdup(buf
));
569 result
= smartlist_join_strings(chunks
, ",", 0, NULL
);
573 SMARTLIST_FOREACH(chunks
, char *, c
, tor_free(c
));
574 smartlist_free(chunks
);
577 SMARTLIST_FOREACH(entries
, c_hist_t
*, c
, tor_free(c
));
578 smartlist_free(entries
);
584 /** Return a newly allocated string holding the per-country request history
585 * for <b>action</b> in a format suitable for an extra-info document, or NULL
588 geoip_get_request_history(time_t now
, geoip_client_action_t action
)
590 smartlist_t
*entries
, *strings
;
592 unsigned granularity
= IP_GRANULARITY
;
593 int min_observation_time
= GEOIP_MIN_OBSERVATION_TIME
;
594 #ifdef ENABLE_GEOIP_STATS
595 granularity
= DIR_RECORD_USAGE_GRANULARITY
;
596 min_observation_time
= DIR_RECORD_USAGE_MIN_OBSERVATION_TIME
;
599 if (client_history_starts
>= (now
- min_observation_time
))
601 if (action
!= GEOIP_CLIENT_NETWORKSTATUS
&&
602 action
!= GEOIP_CLIENT_NETWORKSTATUS_V2
)
604 if (!geoip_countries
)
607 entries
= smartlist_create();
608 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, {
609 uint32_t *n
= (action
== GEOIP_CLIENT_NETWORKSTATUS
)
610 ? c
->n_v3_ns_requests
: c
->n_v2_ns_requests
;
614 for (i
=0; i
< REQUEST_HIST_LEN
; ++i
)
618 ent
= tor_malloc_zero(sizeof(c_hist_t
));
619 strlcpy(ent
->country
, c
->countrycode
, sizeof(ent
->country
));
620 ent
->total
= round_to_next_multiple_of(tot
, granularity
);
621 smartlist_add(entries
, ent
);
623 smartlist_sort(entries
, _c_hist_compare
);
625 strings
= smartlist_create();
626 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, {
628 tor_snprintf(buf
, sizeof(buf
), "%s=%u", ent
->country
, ent
->total
);
629 smartlist_add(strings
, tor_strdup(buf
));
631 result
= smartlist_join_strings(strings
, ",", 0, NULL
);
632 SMARTLIST_FOREACH(strings
, char *, cp
, tor_free(cp
));
633 SMARTLIST_FOREACH(entries
, c_hist_t
*, ent
, tor_free(ent
));
634 smartlist_free(strings
);
635 smartlist_free(entries
);
639 /** Store all our geoip statistics into $DATADIR/geoip-stats. */
641 dump_geoip_stats(void)
643 #ifdef ENABLE_GEOIP_STATS
644 time_t now
= time(NULL
);
645 time_t request_start
;
646 char *filename
= get_datadir_fname("geoip-stats");
647 char *data_v2
= NULL
, *data_v3
= NULL
;
648 char since
[ISO_TIME_LEN
+1], written
[ISO_TIME_LEN
+1];
649 open_file_t
*open_file
= NULL
;
650 double v2_share
= 0.0, v3_share
= 0.0;
654 data_v2
= geoip_get_client_history(now
, GEOIP_CLIENT_NETWORKSTATUS_V2
);
655 data_v3
= geoip_get_client_history(now
, GEOIP_CLIENT_NETWORKSTATUS
);
656 format_iso_time(since
, geoip_get_history_start());
657 format_iso_time(written
, now
);
658 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
662 if (fprintf(out
, "written %s\nstarted-at %s\nns-ips %s\nns-v2-ips %s\n",
664 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
669 request_start
= current_request_period_starts
-
670 (n_old_request_periods
* REQUEST_HIST_PERIOD
);
671 format_iso_time(since
, request_start
);
672 data_v2
= geoip_get_request_history(now
, GEOIP_CLIENT_NETWORKSTATUS_V2
);
673 data_v3
= geoip_get_request_history(now
, GEOIP_CLIENT_NETWORKSTATUS
);
674 if (fprintf(out
, "requests-start %s\nn-ns-reqs %s\nn-v2-ns-reqs %s\n",
676 data_v3
? data_v3
: "", data_v2
? data_v2
: "") < 0)
678 #define RESPONSE_GRANULARITY 8
679 for (i
= 0; i
< GEOIP_NS_RESPONSE_NUM
; i
++) {
680 ns_v2_responses
[i
] = round_to_next_multiple_of(ns_v2_responses
[i
],
681 RESPONSE_GRANULARITY
);
682 ns_v3_responses
[i
] = round_to_next_multiple_of(ns_v3_responses
[i
],
683 RESPONSE_GRANULARITY
);
685 #undef RESPONSE_GRANULARITY
686 if (fprintf(out
, "n-ns-resp ok=%d,not-enough-sigs=%d,unavailable=%d,"
687 "not-found=%d,not-modified=%d,busy=%d\n",
688 ns_v3_responses
[GEOIP_SUCCESS
],
689 ns_v3_responses
[GEOIP_REJECT_NOT_ENOUGH_SIGS
],
690 ns_v3_responses
[GEOIP_REJECT_UNAVAILABLE
],
691 ns_v3_responses
[GEOIP_REJECT_NOT_FOUND
],
692 ns_v3_responses
[GEOIP_REJECT_NOT_MODIFIED
],
693 ns_v3_responses
[GEOIP_REJECT_BUSY
]) < 0)
695 if (fprintf(out
, "n-v2-ns-resp ok=%d,unavailable=%d,"
696 "not-found=%d,not-modified=%d,busy=%d\n",
697 ns_v2_responses
[GEOIP_SUCCESS
],
698 ns_v2_responses
[GEOIP_REJECT_UNAVAILABLE
],
699 ns_v2_responses
[GEOIP_REJECT_NOT_FOUND
],
700 ns_v2_responses
[GEOIP_REJECT_NOT_MODIFIED
],
701 ns_v2_responses
[GEOIP_REJECT_BUSY
]) < 0)
703 memset(ns_v2_responses
, 0, sizeof(ns_v2_responses
));
704 memset(ns_v3_responses
, 0, sizeof(ns_v3_responses
));
705 if (!router_get_my_share_of_directory_requests(&v2_share
, &v3_share
)) {
706 if (fprintf(out
, "v2-ns-share %0.2lf%%\n", v2_share
*100) < 0)
708 if (fprintf(out
, "v3-ns-share %0.2lf%%\n", v3_share
*100) < 0)
712 finish_writing_to_file(open_file
);
716 abort_writing_to_file(open_file
);
723 /** Store all our geoip statistics as entry guards into
724 * $DATADIR/entry-stats. */
726 dump_entry_stats(void)
728 #ifdef ENABLE_ENTRY_STATS
729 time_t now
= time(NULL
);
730 char *filename
= get_datadir_fname("entry-stats");
732 char since
[ISO_TIME_LEN
+1], written
[ISO_TIME_LEN
+1];
733 open_file_t
*open_file
= NULL
;
736 data
= geoip_get_client_history(now
, GEOIP_CLIENT_CONNECT
);
737 format_iso_time(since
, geoip_get_history_start());
738 format_iso_time(written
, now
);
739 out
= start_writing_to_stdio_file(filename
, OPEN_FLAGS_APPEND
,
743 if (fprintf(out
, "written %s\nstarted-at %s\nips %s\n",
744 written
, since
, data
? data
: "") < 0)
747 finish_writing_to_file(open_file
);
751 abort_writing_to_file(open_file
);
757 /** Helper used to implement GETINFO ip-to-country/... controller command. */
759 getinfo_helper_geoip(control_connection_t
*control_conn
,
760 const char *question
, char **answer
)
763 if (geoip_is_loaded() && !strcmpstart(question
, "ip-to-country/")) {
767 question
+= strlen("ip-to-country/");
768 if (tor_inet_aton(question
, &in
) != 0) {
769 ip
= ntohl(in
.s_addr
);
770 c
= geoip_get_country_by_ip(ip
);
771 *answer
= tor_strdup(geoip_get_country_name(c
));
777 /** Release all storage held by the GeoIP database. */
781 if (geoip_countries
) {
782 SMARTLIST_FOREACH(geoip_countries
, geoip_country_t
*, c
, tor_free(c
));
783 smartlist_free(geoip_countries
);
785 if (country_idxplus1_by_lc_code
)
786 strmap_free(country_idxplus1_by_lc_code
, NULL
);
788 SMARTLIST_FOREACH(geoip_entries
, geoip_entry_t
*, ent
, tor_free(ent
));
789 smartlist_free(geoip_entries
);
791 geoip_countries
= NULL
;
792 country_idxplus1_by_lc_code
= NULL
;
793 geoip_entries
= NULL
;
796 /** Release all storage held in this file. */
800 clientmap_entry_t
**ent
, **next
, *this;
801 for (ent
= HT_START(clientmap
, &client_history
); ent
!= NULL
; ent
= next
) {
803 next
= HT_NEXT_RMV(clientmap
, &client_history
, ent
);
806 HT_CLEAR(clientmap
, &client_history
);