commit 1b490c2030daae89025794592dab11fdaaa3a48f
parent dba00ebf3435848d95ca58f78078b2e0378f8af1
Author: David Goulet <dgoulet@torproject.org>
Date: Tue, 13 Jan 2026 11:58:36 -0500
Merge branch 'maint-0.4.8'
Diffstat:
8 files changed, 52 insertions(+), 13 deletions(-)
diff --git a/changes/bug41192 b/changes/bug41192
@@ -0,0 +1,9 @@
+ o Major bugfixes (directory servers):
+ - Don't count networkstatus serves until they finish. When we started
+ serving a consensus document but the client didn't receive all of
+ it, we were still counting that as a success in our stats. This
+ mistake, which can be triggered for example by obsolete clients
+ or by DPI-based censorship, led to wildly inflated user counts
+ because we estimate total users in the world based on successful
+ consensus fetches. Fixes bug 41192; bugfix on 0.2.1.1-alpha.
+
diff --git a/changes/bug41192b b/changes/bug41192b
@@ -0,0 +1,4 @@
+ o Minor features (directory servers):
+ - Track how many times directory servers begin serving networkstatus
+ documents, so we can compare it to the number of times we finish
+ serving them. Motivated by the fixes in ticket 41192.
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
@@ -968,12 +968,12 @@ handle_get_current_consensus(dir_connection_t *conn,
goto done;
}
+ /* Success: we are going to try serving it. */
+ geoip_note_ns_response(GEOIP_SERVED);
+ conn->should_count_geoip_when_finished = 1;
+
tor_addr_t addr;
if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) {
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
- &addr, NULL,
- time(NULL));
- geoip_note_ns_response(GEOIP_SUCCESS);
/* Note that a request for a network status has started, so that we
* can measure the download time later on. */
if (conn->dirreq_id)
diff --git a/src/feature/dircache/dirserv.c b/src/feature/dircache/dirserv.c
@@ -15,6 +15,7 @@
#include "feature/nodelist/routerlist.h"
#include "feature/relay/router.h"
#include "feature/relay/routermode.h"
+#include "feature/stats/geoip_stats.h"
#include "feature/stats/predict_ports.h"
#include "feature/dircache/cached_dir_st.h"
@@ -778,6 +779,17 @@ connection_dirserv_flushed_some(dir_connection_t *conn)
tor_compress_free(conn->compress_state);
conn->compress_state = NULL;
}
+ if (conn->should_count_geoip_when_finished) {
+ /* only count successfully networkstatus serves when the spool runs dry */
+ tor_addr_t addr;
+ if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) {
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
+ &addr, NULL,
+ time(NULL));
+ }
+ geoip_note_ns_response(GEOIP_SUCCESS);
+ conn->should_count_geoip_when_finished = 0;
+ }
return 0;
}
diff --git a/src/feature/dircommon/dir_connection_st.h b/src/feature/dircommon/dir_connection_st.h
@@ -61,6 +61,10 @@ struct dir_connection_t {
* needs this for the incoming side, so it's moved here. */
uint64_t dirreq_id;
+ /** 0 normally, 1 if we're serving a consensus and we're delaying counting
+ * geoip until we've served the final bytes. */
+ bool should_count_geoip_when_finished;
+
#ifdef MEASUREMENTS_21206
/** Number of RELAY_DATA cells received. */
uint32_t data_cells_received;
diff --git a/src/feature/stats/geoip_stats.c b/src/feature/stats/geoip_stats.c
@@ -390,8 +390,8 @@ geoip_client_cache_total_allocation(void)
* statuses? */
static uint32_t ns_v3_responses[GEOIP_NS_RESPONSE_NUM];
-/** Note that we've rejected a client's request for a v3 network status
- * for reason <b>reason</b> at time <b>now</b>. */
+/** Note how we have handled a client's request for a v3 network status:
+ * with <b>reason</b> at time <b>now</b>. */
void
geoip_note_ns_response(geoip_ns_response_t response)
{
@@ -997,7 +997,8 @@ geoip_format_dirreq_stats(time_t now)
tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n"
"dirreq-v3-ips %s\n"
"dirreq-v3-reqs %s\n"
- "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
+ "dirreq-v3-resp "
+ "served=%u,ok=%u,not-enough-sigs=%u,unavailable=%u,"
"not-found=%u,not-modified=%u,busy=%u\n"
"dirreq-v3-direct-dl %s\n"
"dirreq-v3-tunneled-dl %s\n",
@@ -1005,6 +1006,7 @@ geoip_format_dirreq_stats(time_t now)
(unsigned) (now - start_of_dirreq_stats_interval),
v3_ips_string ? v3_ips_string : "",
v3_reqs_string ? v3_reqs_string : "",
+ ns_v3_responses[GEOIP_SERVED],
ns_v3_responses[GEOIP_SUCCESS],
ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
diff --git a/src/feature/stats/geoip_stats.h b/src/feature/stats/geoip_stats.h
@@ -25,7 +25,7 @@ typedef enum {
/** We've served a networkstatus consensus as a directory server. */
GEOIP_CLIENT_NETWORKSTATUS = 1,
} geoip_client_action_t;
-/** Indicates either a positive reply or a reason for rejectng a network
+/** Indicates either a positive reply or a reason for rejecting a network
* status request that will be included in geoip statistics. */
typedef enum {
/** Request is answered successfully. */
@@ -41,8 +41,11 @@ typedef enum {
GEOIP_REJECT_NOT_MODIFIED = 4,
/** Directory is busy. */
GEOIP_REJECT_BUSY = 5,
+ /** We began to serve the request, and when we feel we have finished
+ * serving it we will note this with a GEOIP_SUCCESS call too. */
+ GEOIP_SERVED = 6,
} geoip_ns_response_t;
-#define GEOIP_NS_RESPONSE_NUM 6
+#define GEOIP_NS_RESPONSE_NUM 7
/** Directory requests that we are measuring can be either direct or
* tunneled. */
diff --git a/src/test/test_geoip.c b/src/test/test_geoip.c
@@ -58,7 +58,8 @@ test_geoip(void *arg)
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"dirreq-v3-ips ab=8\n"
"dirreq-v3-reqs ab=8\n"
- "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "dirreq-v3-resp "
+ "served=0,ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
"not-modified=0,busy=0\n"
"dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
"dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n",
@@ -66,7 +67,8 @@ test_geoip(void *arg)
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"dirreq-v3-ips \n"
"dirreq-v3-reqs \n"
- "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "dirreq-v3-resp "
+ "served=0,ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
"not-modified=0,busy=0\n"
"dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
"dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n",
@@ -74,7 +76,8 @@ test_geoip(void *arg)
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"dirreq-v3-ips \n"
"dirreq-v3-reqs \n"
- "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "dirreq-v3-resp "
+ "served=8,ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
"not-modified=0,busy=0\n"
"dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
"dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n",
@@ -82,7 +85,8 @@ test_geoip(void *arg)
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"dirreq-v3-ips \n"
"dirreq-v3-reqs \n"
- "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "dirreq-v3-resp "
+ "served=8,ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
"not-modified=0,busy=0\n"
"dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
"dirreq-v3-tunneled-dl complete=0,timeout=0,running=4\n",
@@ -230,6 +234,7 @@ test_geoip(void *arg)
/* Note a successful network status response and make sure that it
* appears in the history string. */
+ geoip_note_ns_response(GEOIP_SERVED);
geoip_note_ns_response(GEOIP_SUCCESS);
s = geoip_format_dirreq_stats(now + 86400);
tt_str_op(dirreq_stats_3,OP_EQ, s);