|
@@ -294,6 +294,27 @@ geoip_get_history_start(void)
|
|
|
return client_history_starts;
|
|
|
}
|
|
|
|
|
|
+/* Helper type: used to sort results by value. */
|
|
|
+typedef struct c_hist_t {
|
|
|
+ char country[3];
|
|
|
+ unsigned total;
|
|
|
+} c_hist_t;
|
|
|
+
|
|
|
+/** Sorting helper: return -1, 1, or 0 based on comparison of two
|
|
|
+ * geoip_entry_t. Sort in descending order of total, and then by country
|
|
|
+ * code. */
|
|
|
+static int
|
|
|
+_c_hist_compare(const void **_a, const void **_b)
|
|
|
+{
|
|
|
+ const c_hist_t *a = *_a, *b = *_b;
|
|
|
+ if (a->total > b->total)
|
|
|
+ return -1;
|
|
|
+ else if (a->total < b->total)
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return strcmp(a->country, b->country);
|
|
|
+}
|
|
|
+
|
|
|
/** Return a newly allocated comma-separated string containing entries for all
|
|
|
* the countries from which we've seen enough clients connect. The entry
|
|
|
* format is cc=num where num is the number of IPs we've seen connecting from
|
|
@@ -308,6 +329,7 @@ geoip_get_client_history(time_t now)
|
|
|
if (client_history_starts < (now - 12*60*60)) {
|
|
|
char buf[32];
|
|
|
smartlist_t *chunks = NULL;
|
|
|
+ smartlist_t *entries = NULL;
|
|
|
int n_countries = geoip_get_n_countries();
|
|
|
int i;
|
|
|
clientmap_entry_t **ent;
|
|
@@ -321,19 +343,35 @@ geoip_get_client_history(time_t now)
|
|
|
++counts[country];
|
|
|
++total;
|
|
|
}
|
|
|
+ /* Don't record anything if we haven't seen enough IPs. */
|
|
|
if (total < MIN_IPS_TO_NOTE_ANYTHING)
|
|
|
goto done;
|
|
|
- chunks = smartlist_create();
|
|
|
+ /* Make a list of c_hist_t */
|
|
|
+ entries = smartlist_create();
|
|
|
for (i = 0; i < n_countries; ++i) {
|
|
|
unsigned c = counts[i];
|
|
|
const char *countrycode;
|
|
|
+ c_hist_t *ent;
|
|
|
+ /* Only report a country if it has a minimum number of IPs. */
|
|
|
if (c >= MIN_IPS_TO_NOTE_COUNTRY) {
|
|
|
c -= c % IP_GRANULARITY;
|
|
|
countrycode = geoip_get_country_name(i);
|
|
|
- tor_snprintf(buf, sizeof(buf), "%s=%u", countrycode, c);
|
|
|
- smartlist_add(chunks, tor_strdup(buf));
|
|
|
+ ent = tor_malloc(sizeof(c_hist_t));
|
|
|
+ strlcpy(ent->country, countrycode, sizeof(ent->country));
|
|
|
+ ent->total = c;
|
|
|
+ smartlist_add(entries, ent);
|
|
|
}
|
|
|
}
|
|
|
+ /* Sort entries. Note that we must do this _AFTER_ rounding, or else
|
|
|
+ * the sort order could leak info. */
|
|
|
+ smartlist_sort(entries, _c_hist_compare);
|
|
|
+
|
|
|
+ /* Build the result. */
|
|
|
+ chunks = smartlist_create();
|
|
|
+ SMARTLIST_FOREACH(entries, c_hist_t *, ch, {
|
|
|
+ tor_snprintf(buf, sizeof(buf), "%s=%u", ch->country, ch->total);
|
|
|
+ smartlist_add(chunks, tor_strdup(buf));
|
|
|
+ });
|
|
|
result = smartlist_join_strings(chunks, ",", 0, NULL);
|
|
|
done:
|
|
|
tor_free(counts);
|
|
@@ -341,6 +379,10 @@ geoip_get_client_history(time_t now)
|
|
|
SMARTLIST_FOREACH(chunks, char *, c, tor_free(c));
|
|
|
smartlist_free(chunks);
|
|
|
}
|
|
|
+ if (entries) {
|
|
|
+ SMARTLIST_FOREACH(entries, c_hist_t *, c, tor_free(c));
|
|
|
+ smartlist_free(entries);
|
|
|
+ }
|
|
|
}
|
|
|
return result;
|
|
|
}
|