Fix compile warning on windows
[tor.git] / src / or / geoip.c
blob5bb2410a750d141dd33ad7736f47cada012a909b
1 /* Copyright (c) 2007-2011, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file geoip.c
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
9 * requests.
12 #define GEOIP_PRIVATE
13 #include "or.h"
14 #include "ht.h"
15 #include "config.h"
16 #include "control.h"
17 #include "dnsserv.h"
18 #include "geoip.h"
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 */
29 } geoip_entry_t;
31 /** A per-country record for GeoIP request history. */
32 typedef struct geoip_country_t {
33 char countrycode[3];
34 uint32_t n_v2_ns_requests;
35 uint32_t n_v3_ns_requests;
36 } geoip_country_t;
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
42 * not found. */
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.
50 country_t
51 geoip_get_country(const char *country)
53 void *_idxplus1;
54 intptr_t idx;
56 _idxplus1 = strmap_get_lc(country_idxplus1_by_lc_code, country);
57 if (!_idxplus1)
58 return -1;
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>.
67 static void
68 geoip_add_entry(uint32_t low, uint32_t high, const char *country)
70 intptr_t idx;
71 geoip_entry_t *ent;
72 void *_idxplus1;
74 if (high < low)
75 return;
77 _idxplus1 = strmap_get_lc(country_idxplus1_by_lc_code, country);
79 if (!_idxplus1) {
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));
86 } else {
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));
94 ent->ip_low = low;
95 ent->ip_high = high;
96 ent->country = idx;
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(). */
102 /*private*/ int
103 geoip_parse_entry(const char *line)
105 unsigned int low, high;
106 char b[3];
107 if (!geoip_countries)
108 init_geoip_countries();
109 if (!geoip_entries)
110 geoip_entries = smartlist_create();
112 while (TOR_ISSPACE(*line))
113 ++line;
114 if (*line == '#')
115 return 0;
116 if (sscanf(line,"%u,%u,%2s", &low, &high, b) == 3) {
117 geoip_add_entry(low, high, b);
118 return 0;
119 } else if (sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, b) == 3) {
120 geoip_add_entry(low, high, b);
121 return 0;
122 } else {
123 log_warn(LD_GENERAL, "Unable to parse line from GEOIP file: %s",
124 escaped(line));
125 return -1;
129 /** Sorting helper: return -1, 1, or 0 based on comparison of two
130 * geoip_entry_t */
131 static int
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)
136 return -1;
137 else if (a->ip_low > b->ip_low)
138 return 1;
139 else
140 return 0;
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 */
145 static int
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)
152 return -1;
153 else if (addr > entry->ip_high)
154 return 1;
155 else
156 return 0;
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.
170 static void
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
190 * and
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)
201 FILE *f;
202 const char *msg = "";
203 int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO;
204 clear_geoip_db();
205 if (!(f = fopen(filename, "r"))) {
206 log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s. %s",
207 filename, msg);
208 return -1;
210 if (!geoip_countries)
211 init_geoip_countries();
212 if (geoip_entries) {
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);
218 while (!feof(f)) {
219 char buf[512];
220 if (fgets(buf, (int)sizeof(buf), f) == NULL)
221 break;
222 /* FFFF track full country name. */
223 geoip_parse_entry(buf);
225 /*XXXX abort and return -1 if no entries/illformed?*/
226 fclose(f);
228 smartlist_sort(geoip_entries, _geoip_compare_entries);
230 /* Okay, now we need to maybe change our mind about what is in which
231 * country. */
232 refresh_all_country_info();
234 return 0;
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)
246 geoip_entry_t *ent;
247 if (!geoip_entries)
248 return -1;
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. */
264 const char *
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;
270 } else
271 return "??";
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;
286 uint32_t ipaddr;
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;
293 } clientmap_entry_t;
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 =
302 HT_INITIALIZER();
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. */
311 static INLINE int
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. */
323 static void
324 client_history_clear(void)
326 clientmap_entry_t **ent, **next, *this;
327 for (ent = HT_START(clientmap, &client_history); ent != NULL;
328 ent = next) {
329 if ((*ent)->action == GEOIP_CLIENT_CONNECT) {
330 this = *ent;
331 next = HT_NEXT_RMV(clientmap, &client_history, ent);
332 tor_free(this);
333 } else {
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
347 * is sent to us? */
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. */
364 static void
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)
369 return;
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).*/
386 static int
387 geoip_get_mean_shares(time_t now, double *v2_share_out,
388 double *v3_share_out)
390 geoip_determine_shares(now);
391 if (!share_seconds)
392 return -1;
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;
396 share_seconds = 0;
397 return 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. */
403 void
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)))
413 return;
414 } else {
415 if (options->BridgeRelay || options->BridgeAuthoritativeDir ||
416 !options->DirReqStatistics)
417 return;
420 lookup.ipaddr = addr;
421 lookup.action = (int)action;
422 ent = HT_FIND(clientmap, &client_history, &lookup);
423 if (! ent) {
424 ent = tor_malloc_zero(sizeof(clientmap_entry_t));
425 ent->ipaddr = addr;
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);
431 else
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);
437 if (country_idx < 0)
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;
443 else
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. */
455 static int
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) {
460 tor_free(ent);
461 return 1;
462 } else {
463 return 0;
467 /** Forget about all clients that haven't connected since <b>cutoff</b>. */
468 void
469 geoip_remove_old_clients(time_t cutoff)
471 clientmap_HT_FOREACH_FN(&client_history,
472 _remove_old_client_helper,
473 &cutoff);
476 /** How many responses are we giving to clients requesting v2 network
477 * statuses? */
478 static uint32_t ns_v2_responses[GEOIP_NS_RESPONSE_NUM];
480 /** How many responses are we giving to clients requesting v3 network
481 * statuses? */
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
486 * <b>now</b>. */
487 void
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)
493 return;
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]++;
504 else
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. */
523 } c_hist_t;
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
527 * code. */
528 static int
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)
533 return -1;
534 else if (a->total < b->total)
535 return 1;
536 else
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
549 * bandwidths. */
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. */
556 uint64_t dirreq_id;
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 =
571 HT_INITIALIZER();
573 static int
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;
580 static unsigned
581 dirreq_map_ent_hash(const dirreq_map_entry_t *entry)
583 unsigned u = (unsigned) entry->dirreq_id;
584 u += entry->type << 20;
585 return u;
588 HT_PROTOTYPE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash,
589 dirreq_map_ent_eq);
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. */
596 static void
597 _dirreq_map_put(dirreq_map_entry_t *entry, dirreq_type_t type,
598 uint64_t dirreq_id)
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.");
611 return;
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;
622 lookup.type = type;
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
630 * started. */
631 void
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)
637 return;
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;
643 ent->type = type;
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. */
653 void
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)
659 return;
660 ent = _dirreq_map_get(type, dirreq_id);
661 if (!ent)
662 return;
663 if (new_state == DIRREQ_IS_FOR_NETWORK_STATUS)
664 return;
665 if (new_state - 1 != ent->state)
666 return;
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);
673 ent->completed = 1;
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. */
682 static char *
683 geoip_get_dirreq_history(geoip_client_action_t action,
684 dirreq_type_t type)
686 char *result = NULL;
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;
691 struct timeval now;
693 tor_gettimeofday(&now);
694 if (action != GEOIP_CLIENT_NETWORKSTATUS &&
695 action != GEOIP_CLIENT_NETWORKSTATUS_V2)
696 return NULL;
697 dirreq_completed = smartlist_create();
698 for (ptr = HT_START(dirreqmap, &dirreq_map); ptr; ptr = next) {
699 ent = *ptr;
700 if (ent->action != action || ent->type != type) {
701 next = HT_NEXT(dirreqmap, &dirreq_map, ptr);
702 continue;
703 } else {
704 if (ent->completed) {
705 smartlist_add(dirreq_completed, ent);
706 complete++;
707 next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
708 } else {
709 if (tv_mdiff(&ent->request_time, &now) / 1000 > DIRREQ_TIMEOUT)
710 timeouts++;
711 else
712 running++;
713 next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ptr);
714 tor_free(ent);
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);
728 if (written < 0) {
729 tor_free(result);
730 goto done;
733 #define MIN_DIR_REQ_RESPONSES 16
734 if (complete >= MIN_DIR_REQ_RESPONSES) {
735 uint32_t *dltimes;
736 /* We may have rounded 'completed' up. Here we want to use the
737 * real value. */
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);
744 if (time_diff == 0)
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",
755 dltimes[0],
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]);
768 if (written<0)
769 tor_free(result);
770 tor_free(dltimes);
772 done:
773 SMARTLIST_FOREACH(dirreq_completed, dirreq_map_entry_t *, ent,
774 tor_free(ent));
775 smartlist_free(dirreq_completed);
776 return result;
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. */
785 char *
786 geoip_get_client_history(geoip_client_action_t action)
788 char *result = NULL;
789 unsigned granularity = IP_GRANULARITY;
790 smartlist_t *chunks = NULL;
791 smartlist_t *entries = NULL;
792 int n_countries = geoip_get_n_countries();
793 int i;
794 clientmap_entry_t **ent;
795 unsigned *counts = NULL;
796 unsigned total = 0;
798 if (!geoip_is_loaded())
799 return NULL;
801 counts = tor_malloc_zero(sizeof(unsigned)*n_countries);
802 HT_FOREACH(ent, clientmap, &client_history) {
803 int country;
804 if ((*ent)->action != (int)action)
805 continue;
806 country = geoip_get_country_by_ip((*ent)->ipaddr);
807 if (country < 0)
808 country = 0; /** unresolved requests are stored at index 0. */
809 tor_assert(0 <= country && country < n_countries);
810 ++counts[country];
811 ++total;
813 /* Don't record anything if we haven't seen enough IPs. */
814 if (total < MIN_IPS_TO_NOTE_ANYTHING)
815 goto done;
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;
821 c_hist_t *ent;
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));
828 ent->total = c;
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, {
839 char *buf=NULL;
840 tor_asprintf(&buf, "%s=%u", ch->country, ch->total);
841 smartlist_add(chunks, buf);
843 result = smartlist_join_strings(chunks, ",", 0, NULL);
844 done:
845 tor_free(counts);
846 if (chunks) {
847 SMARTLIST_FOREACH(chunks, char *, c, tor_free(c));
848 smartlist_free(chunks);
850 if (entries) {
851 SMARTLIST_FOREACH(entries, c_hist_t *, c, tor_free(c));
852 smartlist_free(entries);
854 return result;
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
859 * on failure. */
860 char *
861 geoip_get_request_history(geoip_client_action_t action)
863 smartlist_t *entries, *strings;
864 char *result;
865 unsigned granularity = IP_GRANULARITY;
867 if (action != GEOIP_CLIENT_NETWORKSTATUS &&
868 action != GEOIP_CLIENT_NETWORKSTATUS_V2)
869 return NULL;
870 if (!geoip_countries)
871 return NULL;
873 entries = smartlist_create();
874 SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
875 uint32_t tot = 0;
876 c_hist_t *ent;
877 tot = (action == GEOIP_CLIENT_NETWORKSTATUS) ?
878 c->n_v3_ns_requests : c->n_v2_ns_requests;
879 if (!tot)
880 continue;
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, {
890 char *buf = NULL;
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);
899 return result;
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. */
907 void
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(). */
915 void
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;
924 ent = next) {
925 if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS ||
926 (*ent)->action == GEOIP_CLIENT_NETWORKSTATUS_V2) {
927 this = *ent;
928 next = HT_NEXT_RMV(clientmap, &client_history, ent);
929 tor_free(this);
930 } else {
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;
937 share_seconds = 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) {
943 this = *ent;
944 next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ent);
945 tor_free(this);
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. */
953 time_t
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;
961 FILE *out;
962 int i;
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)
974 goto done;
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,
980 0600, &open_file);
981 if (!out)
982 goto done;
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)
987 goto done;
988 tor_free(data_v2);
989 tor_free(data_v3);
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)
995 goto done;
996 tor_free(data_v2);
997 tor_free(data_v3);
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)
1017 goto done;
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)
1025 goto done;
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)
1030 goto done;
1031 if (fprintf(out, "dirreq-v3-share %0.2lf%%\n", v3_share*100) < 0)
1032 goto done;
1035 data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2,
1036 DIRREQ_DIRECT);
1037 data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
1038 DIRREQ_DIRECT);
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)
1041 goto done;
1042 tor_free(data_v2);
1043 tor_free(data_v3);
1044 data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2,
1045 DIRREQ_TUNNELED);
1046 data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
1047 DIRREQ_TUNNELED);
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)
1050 goto done;
1052 finish_writing_to_file(open_file);
1053 open_file = NULL;
1055 start_of_dirreq_stats_interval = now;
1057 done:
1058 if (open_file)
1059 abort_writing_to_file(open_file);
1060 tor_free(filename);
1061 tor_free(statsdir);
1062 tor_free(data_v2);
1063 tor_free(data_v3);
1064 return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL;
1067 /** Start time of bridge stats or 0 if we're not collecting bridge
1068 * statistics. */
1069 static time_t start_of_bridge_stats_interval;
1071 /** Initialize bridge stats. */
1072 void
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(). */
1080 void
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. */
1090 static int
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],
1094 *eos;
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";
1099 const char *tmp;
1100 time_t stats_end_time;
1101 int seconds;
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);
1107 if (!tmp)
1108 return 0;
1109 tmp += strlen(BRIDGE_STATS_END);
1111 if (strlen(tmp) < ISO_TIME_LEN + 6)
1112 return 0;
1113 strlcpy(stats_end_str, tmp, sizeof(stats_end_str));
1114 if (parse_iso_time(stats_end_str, &stats_end_time) < 0)
1115 return 0;
1116 if (stats_end_time < now - (25*60*60) ||
1117 stats_end_time > now + (1*60*60))
1118 return 0;
1119 seconds = (int)strtol(tmp + ISO_TIME_LEN + 2, &eos, 10);
1120 if (!eos || seconds < 23*60*60)
1121 return 0;
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);
1126 if (!tmp) {
1127 /* Look if there is an empty "bridge-ips" line */
1128 tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS_EMPTY_LINE);
1129 if (!tmp)
1130 return 0;
1133 return 1;
1136 /** Most recent bridge statistics formatted to be written to extra-info
1137 * descriptors. */
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
1142 * failure. */
1143 static char *
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];
1150 if (duration < 0)
1151 return NULL;
1153 format_iso_time(written, now);
1154 data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
1156 tor_asprintf(&out,
1157 "bridge-stats-end %s (%ld s)\n"
1158 "bridge-ips %s\n",
1159 written, duration,
1160 data ? data : "");
1161 tor_free(data);
1163 return out;
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
1168 * failure. */
1169 static char *
1170 format_bridge_stats_controller(time_t now)
1172 char *out = NULL, *data = NULL;
1173 char started[ISO_TIME_LEN+1];
1174 (void) now;
1176 format_iso_time(started, start_of_bridge_stats_interval);
1177 data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
1179 tor_asprintf(&out,
1180 "TimeStarted=\"%s\" CountrySummary=%s",
1181 started, data ? data : "");
1182 tor_free(data);
1183 return out;
1186 /** Write bridge statistics to $DATADIR/stats/bridge-stats and return
1187 * when we should next try to write statistics. */
1188 time_t
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);
1202 if (val == NULL)
1203 goto done;
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)
1213 goto done;
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);
1221 if (controller_str)
1222 control_event_clients_seen(controller_str);
1223 tor_free(controller_str);
1225 done:
1226 tor_free(filename);
1227 tor_free(statsdir);
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. */
1235 static void
1236 load_bridge_stats(time_t now)
1238 char *fname, *contents;
1239 if (bridge_stats_extrainfo)
1240 return;
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;
1247 tor_free(fname);
1250 /** Return most recent bridge statistics for inclusion in extra-info
1251 * descriptors, or NULL if we don't have recent bridge statistics. */
1252 const char *
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. */
1261 char *
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
1268 * statistics. */
1269 static time_t start_of_entry_stats_interval;
1271 /** Initialize entry stats. */
1272 void
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(). */
1280 void
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. */
1289 time_t
1290 geoip_entry_stats_write(time_t now)
1292 char *statsdir = NULL, *filename = NULL;
1293 char *data = NULL;
1294 char written[ISO_TIME_LEN+1];
1295 open_file_t *open_file = NULL;
1296 FILE *out;
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)
1308 goto done;
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,
1313 0600, &open_file);
1314 if (!out)
1315 goto done;
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)
1319 goto done;
1321 start_of_entry_stats_interval = now;
1323 finish_writing_to_file(open_file);
1324 open_file = NULL;
1325 done:
1326 if (open_file)
1327 abort_writing_to_file(open_file);
1328 tor_free(filename);
1329 tor_free(statsdir);
1330 tor_free(data);
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)
1340 (void)control_conn;
1341 if (!geoip_is_loaded()) {
1342 *errmsg = "GeoIP data not loaded";
1343 return -1;
1345 if (!strcmpstart(question, "ip-to-country/")) {
1346 int c;
1347 uint32_t ip;
1348 struct in_addr in;
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));
1356 return 0;
1359 /** Release all storage held by the GeoIP database. */
1360 static void
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. */
1379 void
1380 geoip_free_all(void)
1383 clientmap_entry_t **ent, **next, *this;
1384 for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) {
1385 this = *ent;
1386 next = HT_NEXT_RMV(clientmap, &client_history, ent);
1387 tor_free(this);
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) {
1394 this = *ent;
1395 next = HT_NEXT_RMV(dirreqmap, &dirreq_map, ent);
1396 tor_free(this);
1398 HT_CLEAR(dirreqmap, &dirreq_map);
1401 clear_geoip_db();