diff --git a/changes/ticket33072 b/changes/ticket33072
new file mode 100644
index 00000000000..ad427bd0493
--- /dev/null
+++ b/changes/ticket33072
@@ -0,0 +1,4 @@
+ o Minor bugfix (directory authority):
+ - Always allow compressed directory requests and introduce option
+ AuthDirRejectUncompressedRequests to control that behavior. Fixes bug
+ 33072; bugfix on 0.1.2.5-alpha.
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index c7c41e78419..7f05e15ab96 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -2931,6 +2931,10 @@ on the public Tor network.
bandwidth pressure (reaching the configured limit if any). Relays will
always tried to be answered even if this is on. (Default: 1)
+[[AuthDirRejectUncompressedRequests]] **AuthDirRejectUncompressedRequests** **0**|**1**::
+ If set, the directory authority will reject every uncompressed directory
+ requests that is requests not ending with ".z" and lacking
+ "Accept-Encoding". (Default: 1)
HIDDEN SERVICE OPTIONS
----------------------
diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt
index 7b15b37f8c5..a8ca61c2360 100644
--- a/scripts/maint/practracker/exceptions.txt
+++ b/scripts/maint/practracker/exceptions.txt
@@ -56,7 +56,7 @@ problem dependency-violation /src/core/crypto/onion_crypto.c 5
problem dependency-violation /src/core/crypto/onion_fast.c 1
problem dependency-violation /src/core/crypto/onion_tap.c 3
problem dependency-violation /src/core/crypto/relay_crypto.c 9
-problem file-size /src/core/mainloop/connection.c 5569
+problem file-size /src/core/mainloop/connection.c 5694
problem include-count /src/core/mainloop/connection.c 62
problem function-size /src/core/mainloop/connection.c:connection_free_minimal() 185
problem function-size /src/core/mainloop/connection.c:connection_listener_new() 324
diff --git a/src/app/config/config.c b/src/app/config/config.c
index 8c635ab848b..b4fa96ee238 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -672,6 +672,7 @@ static const config_var_t option_vars_[] = {
V(User, STRING, NULL),
OBSOLETE("UserspaceIOCPBuffers"),
V(AuthDirRejectRequestsUnderLoad, BOOL, "1"),
+ V(AuthDirRejectUncompressedRequests, BOOL, "1"),
V(AuthDirSharedRandomness, BOOL, "1"),
V(AuthDirTestEd25519LinkKeys, BOOL, "1"),
OBSOLETE("V1AuthoritativeDirectory"),
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index e6be797017a..3932588232d 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -1015,6 +1015,10 @@ struct or_options_t {
* regardless of bandwidth pressure or not. */
int AuthDirRejectRequestsUnderLoad;
+ /** Bool (default: 1) Reject uncompressed directory requests. A 503 error
+ * code is returned. */
+ int AuthDirRejectUncompressedRequests;
+
/** Bool (default: 1): Switch for the shared random protocol. Only
* relevant to a directory authority. If off, the authority won't
* participate in the protocol. If on (default), a flag is added to the
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 50cd3810a46..8bb3da3f29c 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -3190,8 +3190,11 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
* shouldn't send attempt bytes of low-priority directory stuff
* out to conn.
*
- * If we are a directory authority, always answer dir requests thus true is
- * always returned.
+ * If we are a directory authority, false is returned (indicating that we
+ * should answer the request) if one of these conditions is met:
+ * - Connection is from a known relay (address is looked up).
+ * - AuthDirRejectRequestsUnderLoad is set to 0.
+ * - Compression is being used for the request (looking at c_method).
*
* Note: There are a lot of parameters we could use here:
* - global_relayed_write_bucket. Low is bad.
@@ -3206,7 +3209,8 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
* that's harder to quantify and harder to keep track of.
*/
bool
-connection_dir_is_global_write_low(const connection_t *conn, size_t attempt)
+connection_dir_is_global_write_low(const connection_t *conn, size_t attempt,
+ const compress_method_t c_method)
{
size_t smaller_bucket =
MIN(token_bucket_rw_get_write(&global_bucket),
@@ -3214,6 +3218,16 @@ connection_dir_is_global_write_low(const connection_t *conn, size_t attempt)
/* Special case for authorities (directory only). */
if (authdir_mode_v3(get_options())) {
+ /* If this requests is uncompressed and we are configured to reject those,
+ * indicate that have reached the limit thus deny answering. */
+ if (c_method == NO_METHOD &&
+ get_options()->AuthDirRejectUncompressedRequests) {
+ return true;
+ }
+ if (c_method != NO_METHOD) {
+ /* Always answer compressed request. */
+ return false;
+ }
/* Are we configured to possibly reject requests under load? */
if (!get_options()->AuthDirRejectRequestsUnderLoad) {
/* Answer request no matter what. */
diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h
index 668c7400426..b5f67c37dec 100644
--- a/src/core/mainloop/connection.h
+++ b/src/core/mainloop/connection.h
@@ -12,6 +12,8 @@
#ifndef TOR_CONNECTION_H
#define TOR_CONNECTION_H
+#include "lib/compress/compress.h"
+
listener_connection_t *TO_LISTENER_CONN(connection_t *);
struct buf_t;
@@ -197,7 +199,8 @@ void connection_mark_all_noncontrol_connections(void);
ssize_t connection_bucket_write_limit(struct connection_t *conn, time_t now);
bool connection_dir_is_global_write_low(const struct connection_t *conn,
- size_t attempt);
+ size_t attempt,
+ const compress_method_t c_method);
void connection_bucket_init(void);
void connection_bucket_adjust(const or_options_t *options);
void connection_bucket_refill_all(time_t now,
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index 59cdcc5e022..19768d1f085 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -951,7 +951,8 @@ handle_get_current_consensus(dir_connection_t *conn,
goto done;
}
- if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess,
+ compress_method)) {
log_debug(LD_DIRSERV,
"Client asked for network status lists, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
@@ -1060,7 +1061,8 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
}
});
- if (connection_dir_is_global_write_low(TO_CONN(conn), estimated_len)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), estimated_len,
+ compress_method)) {
write_short_http_response(conn, 503, "Directory busy, try again later");
goto vote_done;
}
@@ -1119,7 +1121,8 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
write_short_http_response(conn, 404, "Not found");
goto done;
}
- if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess,
+ compress_method)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
@@ -1217,7 +1220,8 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
msg = "Not found";
write_short_http_response(conn, 404, msg);
} else {
- if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess,
+ compress_method)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
@@ -1314,7 +1318,8 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
len += c->cache_info.signed_descriptor_len);
if (connection_dir_is_global_write_low(TO_CONN(conn),
- compress_method != NO_METHOD ? len/2 : len)) {
+ compress_method != NO_METHOD ? len/2 : len,
+ compress_method)) {
write_short_http_response(conn, 503, "Directory busy, try again later");
goto keys_done;
}
diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c
index e6f028ed745..97062ef915e 100644
--- a/src/test/test_bwmgt.c
+++ b/src/test/test_bwmgt.c
@@ -336,6 +336,8 @@ test_bwmgt_dir_conn_global_write_low(void *arg)
mock_options.AuthoritativeDir = 1;
mock_options.V3AuthoritativeDir = 1;
mock_options.UseDefaultFallbackDirs = 0;
+ mock_options.AuthDirRejectUncompressedRequests = 0;
+ mock_options.AuthDirRejectRequestsUnderLoad = 0;
/* This will set our global bucket to 1 byte and thus we will hit the
* banwdith limit in our test. */
@@ -359,7 +361,7 @@ test_bwmgt_dir_conn_global_write_low(void *arg)
* that our limit is _not_ low. */
addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
tt_int_op(addr_family, OP_EQ, AF_INET);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 0);
/* Now, we will reject requests under load so try again a non authority non
@@ -369,46 +371,55 @@ test_bwmgt_dir_conn_global_write_low(void *arg)
addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
tt_int_op(addr_family, OP_EQ, AF_INET);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 1);
/* Now, lets try with a connection address from moria1. It should always
* pass even though our limit is too low. */
addr_family = tor_addr_parse(&conn->addr, "128.31.0.39");
tt_int_op(addr_family, OP_EQ, AF_INET);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 0);
/* IPv6 testing of gabelmoo. */
addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]");
tt_int_op(addr_family, OP_EQ, AF_INET6);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 0);
/* Lets retry with a known relay address. It should pass. Possible due to
* our consensus setting above. */
memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t));
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 0);
/* Lets retry with a random IP that is not an authority nor a relay. */
addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
tt_int_op(addr_family, OP_EQ, AF_INET);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 0);
+ /* Compress method should always let us through. */
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, ZLIB_METHOD);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Lets configure ourselves to reject uncompressed requests. */
+ mock_options.AuthDirRejectUncompressedRequests = 1;
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
+ tt_int_op(ret, OP_EQ, 1);
+
/* Finally, just make sure it still denies an IP if we are _not_ a v3
* directory authority. */
mock_options.V3AuthoritativeDir = 0;
addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
tt_int_op(addr_family, OP_EQ, AF_INET);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 1);
/* Random IPv6 should not be allowed. */
addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]");
tt_int_op(addr_family, OP_EQ, AF_INET6);
- ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX, NO_METHOD);
tt_int_op(ret, OP_EQ, 1);
done: