aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYawning Angel <yawning@schwanenlied.me>2015-05-20 17:33:59 +0000
committerYawning Angel <yawning@schwanenlied.me>2015-05-20 17:33:59 +0000
commitdb7bde08be59398488624bc377d1d5318182ee45 (patch)
treeea94749bcaf9952e55bbe74e80188bf2b3dc002e /src
parent32bd533ddac5bd7d594a81119449b3d30206eedd (diff)
downloadtor-db7bde08be59398488624bc377d1d5318182ee45.tar.gz
tor-db7bde08be59398488624bc377d1d5318182ee45.zip
Add "HiddenServiceMaxStreams" as a per-HS tunable.
When set, this limits the maximum number of simultaneous streams per rendezvous circuit on the server side of a HS, with further RELAY_BEGIN cells being silently ignored. This can be modified via "HiddenServiceMaxStreamsCloseCircuit", which if set will cause offending rendezvous circuits to be torn down instead. Addresses part of #16052.
Diffstat (limited to 'src')
-rw-r--r--src/or/circuituse.c27
-rw-r--r--src/or/config.c2
-rw-r--r--src/or/connection_edge.c2
-rw-r--r--src/or/or.h3
-rw-r--r--src/or/rendservice.c53
5 files changed, 79 insertions, 8 deletions
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index b54a4d2a7f..a429a7d053 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ int removed = 0;
if (conn == origin_circ->p_streams) {
origin_circ->p_streams = conn->next_stream;
- return;
+ removed = 1;
+ } else {
+ for (prevconn = origin_circ->p_streams;
+ prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+ prevconn = prevconn->next_stream)
+ ;
+ if (prevconn && prevconn->next_stream) {
+ prevconn->next_stream = conn->next_stream;
+ removed = 1;
+ }
}
-
- for (prevconn = origin_circ->p_streams;
- prevconn && prevconn->next_stream && prevconn->next_stream != conn;
- prevconn = prevconn->next_stream)
- ;
- if (prevconn && prevconn->next_stream) {
- prevconn->next_stream = conn->next_stream;
+ if (removed) {
+ /* If the stream was removed, and it was a rend stream, decrement the
+ * number of streams on the circuit associated with the rend service.
+ */
+ if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+ tor_assert(origin_circ->rend_data);
+ origin_circ->rend_data->nr_streams--;
+ }
return;
}
} else {
diff --git a/src/or/config.c b/src/or/config.c
index 10304482e8..1c04578893 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -286,6 +286,8 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
V(HiddenServiceStatistics, BOOL, "0"),
V(HidServAuth, LINELIST, NULL),
V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index cc6e3d7c58..c63c350fd8 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -2860,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
origin_circ->p_streams = n_stream;
assert_circuit_ok(circ);
+ origin_circ->rend_data->nr_streams++;
+
connection_exit_connect(n_stream);
/* For path bias: This circuit was used successfully */
diff --git a/src/or/or.h b/src/or/or.h
index 5bb080fdc5..af3496765e 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -818,6 +818,9 @@ typedef struct rend_data_t {
/** List of HSDir fingerprints on which this request has been sent to.
* This contains binary identity digest of the directory. */
smartlist_t *hsdirs_fp;
+
+ /** Number of streams associated with this rendezvous circuit. */
+ int nr_streams;
} rend_data_t;
/** Time interval for tracking replays of DH public keys received in
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index daca4ccda4..5d2225ec56 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -147,6 +147,13 @@ typedef struct rend_service_t {
/** If true, we don't close circuits for making requests to unsupported
* ports. */
int allow_unknown_ports;
+ /** The maximum number of simultanious streams-per-circuit that are allowed
+ * to be established, or 0 if no limit is set.
+ */
+ int max_streams_per_circuit;
+ /** If true, we close circuits that exceed the max_streams_per_circuit
+ * limit. */
+ int max_streams_close_circuit;
} rend_service_t;
/** Returns a escaped string representation of the service, <b>s</b>.
@@ -539,6 +546,33 @@ rend_config_services(const or_options_t *options, int validate_only)
log_info(LD_CONFIG,
"HiddenServiceDirGroupReadable=%d for %s",
service->dir_group_readable, service->directory);
+ } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
+ service->max_streams_per_circuit = (int)tor_parse_long(line->value,
+ 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreams should be between 0 and %d, not %s",
+ 65535, line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreams=%d for %s",
+ service->max_streams_per_circuit, service->directory);
+ } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
+ service->max_streams_close_circuit = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit=%d for %s",
+ (int)service->max_streams_close_circuit, service->directory);
+
} else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
/* Parse auth type and comma-separated list of client names and add a
* rend_authorized_client_t for each client to the service's list
@@ -3795,6 +3829,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
serviceid, (unsigned)circ->base_.n_circ_id);
return -2;
}
+ if (service->max_streams_per_circuit > 0) {
+ /* Enforce the streams-per-circuit limit, and refuse to provide a
+ * mapping if this circuit will exceed the limit. */
+#define MAX_STREAM_WARN_INTERVAL 600
+ static struct ratelim_t stream_ratelim =
+ RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
+ if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) {
+ log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
+ "Maximum streams per circuit limit reached on rendezvous "
+ "circuit %u; %s. Circuit has %d out of %d streams.",
+ (unsigned)circ->base_.n_circ_id,
+ service->max_streams_close_circuit ?
+ "closing circuit" :
+ "ignoring open stream request",
+ circ->rend_data->nr_streams,
+ service->max_streams_per_circuit);
+ return service->max_streams_close_circuit ? -2 : -1;
+ }
+ }
matching_ports = smartlist_new();
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
{