smartlist_t *port_cfgs = smartlist_new();
int discard_pk = 0;
int detach = 0;
+ int max_streams = 0;
+ int max_streams_close_circuit = 0;
for (size_t i = 1; i < arg_len; i++) {
static const char *port_prefix = "Port=";
static const char *flags_prefix = "Flags=";
+ static const char *max_s_prefix = "MaxStreams=";
const char *arg = smartlist_get(args, i);
if (!strcasecmpstart(arg, port_prefix)) {
goto out;
}
smartlist_add(port_cfgs, cfg);
+ } else if (!strcasecmpstart(arg, max_s_prefix)) {
+ /* "MaxStreams=[0..65535]". */
+ const char *max_s_str = arg + strlen(max_s_prefix);
+ int ok = 0;
+ max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n");
+ goto out;
+ }
} else if (!strcasecmpstart(arg, flags_prefix)) {
/* "Flags=Flag[,Flag]", where Flag can be:
* * 'DiscardPK' - If tor generates the keypair, do not include it in
* the response.
* * 'Detach' - Do not tie this onion service to any particular control
* connection.
+ * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
+ * exceeded.
*/
static const char *discard_flag = "DiscardPK";
static const char *detach_flag = "Detach";
+ static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
smartlist_t *flags = smartlist_new();
int bad = 0;
discard_pk = 1;
} else if (!strcasecmp(flag, detach_flag)) {
detach = 1;
+ } else if (!strcasecmp(flag, max_s_close_flag)) {
+ max_streams_close_circuit = 1;
} else {
connection_printf_to_buf(conn,
"512 Invalid 'Flags' argument: %s\r\n",
* regardless of success/failure.
*/
char *service_id = NULL;
- int ret = rend_service_add_ephemeral(pk, port_cfgs, &service_id);
+ int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
+ max_streams_close_circuit,
+ &service_id);
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
switch (ret) {
case RSAE_OKAY:
service->intro_nodes = smartlist_new();
+ if (service->max_streams_per_circuit < 0) {
+ log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max "
+ "streams per circuit; ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
+
+ if (service->max_streams_close_circuit < 0 ||
+ service->max_streams_close_circuit > 1) {
+ log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid "
+ "max streams handling; ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
+
if (service->auth_type != REND_NO_AUTH &&
smartlist_len(service->clients) == 0) {
log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
return 0;
}
-/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible.
+/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with
+ * <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit,
+ * and circuit closure on max streams being exceeded set by
+ * <b>max_streams_close_circuit</b>.
*
* Regardless of sucess/failure, callers should not touch pk/ports after
* calling this routine, and may assume that correct cleanup has been done
rend_service_add_ephemeral_status_t
rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
+ int max_streams_per_circuit,
+ int max_streams_close_circuit,
char **service_id_out)
{
*service_id_out = NULL;
s->ports = ports;
s->intro_period_started = time(NULL);
s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
+ s->max_streams_per_circuit = max_streams_per_circuit;
+ s->max_streams_close_circuit = max_streams_close_circuit;
if (rend_service_derive_key_digests(s) < 0) {
rend_service_free(s);
return RSAE_BADPRIVKEY;