Merge branch 'ticket30924_042_04_squashed_merged'

This commit is contained in:
Nick Mathewson
2019-09-09 12:35:27 -04:00
32 changed files with 1702 additions and 187 deletions
+5
View File
@@ -496,6 +496,11 @@ static const config_var_t option_vars_[] = {
VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceExportCircuitID", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceEnableIntroDoSDefense", LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceEnableIntroDoSRatePerSec",
LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceEnableIntroDoSBurstPerSec",
LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
V(HidServAuth, LINELIST, NULL),
V(ClientOnionAuthDir, FILENAME, NULL),
+4
View File
@@ -843,6 +843,10 @@ typedef struct protover_summary_flags_t {
/** True iff this router has a protocol list that allows clients to
* negotiate hs circuit setup padding. Requires Padding>=2. */
unsigned int supports_hs_setup_padding : 1;
/** True iff this router has a protocol list that allows it to support the
* ESTABLISH_INTRO DoS cell extension. Requires HSIntro>=5. */
unsigned int supports_establish_intro_dos_extension : 1;
} protover_summary_flags_t;
typedef struct routerinfo_t routerinfo_t;
+4
View File
@@ -72,6 +72,10 @@ struct or_circuit_t {
* buffer stats to disk. */
uint64_t total_cell_waiting_time;
/** If set, the DoS defenses are enabled on this circuit meaning that the
* introduce2_bucket is initialized and used. */
unsigned int introduce2_dos_defense_enabled : 1;
/** INTRODUCE2 cell bucket controlling how much can go on this circuit. Only
* used if this is a service introduction circuit at the intro point
* (purpose = CIRCUIT_PURPOSE_INTRO_POINT). */
+1 -1
View File
@@ -392,7 +392,7 @@ protover_get_supported_protocols(void)
"Desc=1-2 "
"DirCache=1-2 "
"HSDir=1-2 "
"HSIntro=3-4 "
"HSIntro=3-5 "
"HSRend=1-2 "
"Link=1-5 "
#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS
+3 -1
View File
@@ -450,7 +450,9 @@ memoize_protover_summary(protover_summary_flags_t *out,
PROTOVER_HS_RENDEZVOUS_POINT_V3);
out->supports_hs_setup_padding =
protocol_list_supports_protocol(protocols, PRT_PADDING,
PROTOVER_HS_SETUP_PADDING);
PROTOVER_HS_SETUP_PADDING);
out->supports_establish_intro_dos_extension =
protocol_list_supports_protocol(protocols, PRT_HSINTRO, 5);
protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
cached = strmap_set(protover_summary_map, protocols, new_cached);
+107 -4
View File
@@ -473,10 +473,110 @@ introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
}
}
/* Build and add to the given DoS cell extension the given parameter type and
* value. */
static void
build_establish_intro_dos_param(trn_cell_extension_dos_t *dos_ext,
uint8_t param_type, uint64_t param_value)
{
trn_cell_extension_dos_param_t *dos_param =
trn_cell_extension_dos_param_new();
/* Extra safety. We should never send an unknown parameter type. */
tor_assert(param_type == TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC ||
param_type == TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
trn_cell_extension_dos_param_set_type(dos_param, param_type);
trn_cell_extension_dos_param_set_value(dos_param, param_value);
trn_cell_extension_dos_add_params(dos_ext, dos_param);
/* Not freeing the trunnel object because it is now owned by dos_ext. */
}
/* Build the DoS defense cell extension and put it in the given extensions
* object. This can't fail. */
static void
build_establish_intro_dos_extension(const hs_service_config_t *service_config,
trn_cell_extension_t *extensions)
{
ssize_t ret, dos_ext_encoded_len;
uint8_t *field_array;
trn_cell_extension_field_t *field;
trn_cell_extension_dos_t *dos_ext;
tor_assert(service_config);
tor_assert(extensions);
/* We are creating a cell extension field of the type DoS. */
field = trn_cell_extension_field_new();
trn_cell_extension_field_set_field_type(field,
TRUNNEL_CELL_EXTENSION_TYPE_DOS);
/* Build DoS extension field. We will put in two parameters. */
dos_ext = trn_cell_extension_dos_new();
trn_cell_extension_dos_set_n_params(dos_ext, 2);
/* Build DoS parameter INTRO2 rate per second. */
build_establish_intro_dos_param(dos_ext,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC,
service_config->intro_dos_rate_per_sec);
/* Build DoS parameter INTRO2 burst per second. */
build_establish_intro_dos_param(dos_ext,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC,
service_config->intro_dos_burst_per_sec);
/* Set the field with the encoded DoS extension. */
dos_ext_encoded_len = trn_cell_extension_dos_encoded_len(dos_ext);
/* Set length field and the field array size length. */
trn_cell_extension_field_set_field_len(field, dos_ext_encoded_len);
trn_cell_extension_field_setlen_field(field, dos_ext_encoded_len);
/* Encode the DoS extension into the cell extension field. */
field_array = trn_cell_extension_field_getarray_field(field);
ret = trn_cell_extension_dos_encode(field_array,
trn_cell_extension_field_getlen_field(field), dos_ext);
tor_assert(ret == dos_ext_encoded_len);
/* Finally, encode field into the cell extension. */
trn_cell_extension_add_fields(extensions, field);
/* We've just add an extension field to the cell extensions so increment the
* total number. */
trn_cell_extension_set_num(extensions,
trn_cell_extension_get_num(extensions) + 1);
/* Cleanup. DoS extension has been encoded at this point. */
trn_cell_extension_dos_free(dos_ext);
}
/* ========== */
/* Public API */
/* ========== */
/* Allocate and build all the ESTABLISH_INTRO cell extension. The given
* extensions pointer is always set to a valid cell extension object. */
STATIC trn_cell_extension_t *
build_establish_intro_extensions(const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip)
{
trn_cell_extension_t *extensions;
tor_assert(service_config);
tor_assert(ip);
extensions = trn_cell_extension_new();
trn_cell_extension_set_num(extensions, 0);
/* If the defense has been enabled service side (by the operator with a
* torrc option) and the intro point does support it. */
if (service_config->has_dos_defense_enabled &&
ip->support_intro2_dos_defense) {
/* This function takes care to increment the number of extensions. */
build_establish_intro_dos_extension(service_config, extensions);
}
return extensions;
}
/* Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
* object. The encoded cell is put in cell_out that MUST at least be of the
* size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on success else
@@ -484,15 +584,17 @@ introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
* legacy cell creation. */
ssize_t
hs_cell_build_establish_intro(const char *circ_nonce,
const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip,
uint8_t *cell_out)
{
ssize_t cell_len = -1;
uint16_t sig_len = ED25519_SIG_LEN;
trn_cell_extension_t *ext;
trn_cell_establish_intro_t *cell = NULL;
trn_cell_extension_t *extensions;
tor_assert(circ_nonce);
tor_assert(service_config);
tor_assert(ip);
/* Quickly handle the legacy IP. */
@@ -505,11 +607,12 @@ hs_cell_build_establish_intro(const char *circ_nonce,
goto done;
}
/* Build the extensions, if any. */
extensions = build_establish_intro_extensions(service_config, ip);
/* Set extension data. None used here. */
ext = trn_cell_extension_new();
trn_cell_extension_set_num(ext, 0);
cell = trn_cell_establish_intro_new();
trn_cell_establish_intro_set_extensions(cell, ext);
trn_cell_establish_intro_set_extensions(cell, extensions);
/* Set signature size. Array is then allocated in the cell. We need to do
* this early so we can use trunnel API to get the signature length. */
trn_cell_establish_intro_set_sig_len(cell, sig_len);
+11
View File
@@ -79,6 +79,7 @@ typedef struct hs_cell_introduce2_data_t {
/* Build cell API. */
ssize_t hs_cell_build_establish_intro(const char *circ_nonce,
const hs_service_config_t *config,
const hs_service_intro_point_t *ip,
uint8_t *cell_out);
ssize_t hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
@@ -105,5 +106,15 @@ int hs_cell_parse_rendezvous2(const uint8_t *payload, size_t payload_len,
/* Util API. */
void hs_cell_introduce1_data_clear(hs_cell_introduce1_data_t *data);
#ifdef TOR_UNIT_TESTS
#include "trunnel/hs/cell_common.h"
STATIC trn_cell_extension_t *
build_establish_intro_extensions(const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip);
#endif /* defined(TOR_UNIT_TESTS) */
#endif /* !defined(TOR_HS_CELL_H) */
+1 -1
View File
@@ -317,7 +317,7 @@ send_establish_intro(const hs_service_t *service,
/* Encode establish intro cell. */
cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce,
ip, payload);
&service->config, ip, payload);
if (cell_len < 0) {
log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s "
"on circuit %u. Closing circuit.",
+60
View File
@@ -218,6 +218,9 @@ config_has_invalid_options(const config_line_t *line_,
const char *opts_exclude_v2[] = {
"HiddenServiceExportCircuitID",
"HiddenServiceEnableIntroDoSDefense",
"HiddenServiceEnableIntroDoSRatePerSec",
"HiddenServiceEnableIntroDoSBurstPerSec",
NULL /* End marker. */
};
@@ -276,6 +279,15 @@ config_validate_service(const hs_service_config_t *config)
goto invalid;
}
/* DoS validation values. */
if (config->has_dos_defense_enabled &&
(config->intro_dos_burst_per_sec < config->intro_dos_rate_per_sec)) {
log_warn(LD_CONFIG, "Hidden service DoS defenses burst (%" PRIu32 ") can "
"not be smaller than the rate value (%" PRIu32 ").",
config->intro_dos_burst_per_sec, config->intro_dos_rate_per_sec);
goto invalid;
}
/* Valid. */
return 0;
invalid:
@@ -296,6 +308,8 @@ config_service_v3(const config_line_t *line_,
{
int have_num_ip = 0;
bool export_circuit_id = false; /* just to detect duplicate options */
bool dos_enabled = false, dos_rate_per_sec = false;
bool dos_burst_per_sec = false;
const char *dup_opt_seen = NULL;
const config_line_t *line;
@@ -334,6 +348,52 @@ config_service_v3(const config_line_t *line_,
export_circuit_id = true;
continue;
}
if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSDefense")) {
config->has_dos_defense_enabled =
(unsigned int) helper_parse_uint64(line->key, line->value,
HS_CONFIG_V3_DOS_DEFENSE_DEFAULT,
1, &ok);
if (!ok || dos_enabled) {
if (dos_enabled) {
dup_opt_seen = line->key;
}
goto err;
}
dos_enabled = true;
continue;
}
if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSRatePerSec")) {
config->intro_dos_rate_per_sec =
(unsigned int) helper_parse_uint64(line->key, line->value,
HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN,
HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX, &ok);
if (!ok || dos_rate_per_sec) {
if (dos_rate_per_sec) {
dup_opt_seen = line->key;
}
goto err;
}
dos_rate_per_sec = true;
log_info(LD_REND, "Service INTRO2 DoS defenses rate set to: %" PRIu32,
config->intro_dos_rate_per_sec);
continue;
}
if (!strcasecmp(line->key, "HiddenServiceEnableIntroDoSBurstPerSec")) {
config->intro_dos_burst_per_sec =
(unsigned int) helper_parse_uint64(line->key, line->value,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX, &ok);
if (!ok || dos_burst_per_sec) {
if (dos_burst_per_sec) {
dup_opt_seen = line->key;
}
goto err;
}
dos_burst_per_sec = true;
log_info(LD_REND, "Service INTRO2 DoS defenses burst set to: %" PRIu32,
config->intro_dos_burst_per_sec);
continue;
}
}
/* We do not load the key material for the service at this stage. This is
+9
View File
@@ -15,6 +15,15 @@
#define HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT 65535
/* Maximum number of intro points per version 3 services. */
#define HS_CONFIG_V3_MAX_INTRO_POINTS 20
/* Default value for the introduction DoS defenses. The MIN/MAX are inclusive
* meaning they can be used as valid values. */
#define HS_CONFIG_V3_DOS_DEFENSE_DEFAULT 0
#define HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT 25
#define HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN 0
#define HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX INT32_MAX
#define HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT 200
#define HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN 0
#define HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX INT32_MAX
/* API */
+39 -27
View File
@@ -45,24 +45,26 @@
* introduction DoS defense. Disabled by default. */
#define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0
/* Consensus parameters. */
static uint32_t hs_dos_introduce_rate_per_sec =
/* Consensus parameters. The ESTABLISH_INTRO DoS cell extension have higher
* priority than these values. If no extension is sent, these are used only by
* the introduction point. */
static uint32_t consensus_param_introduce_rate_per_sec =
HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC;
static uint32_t hs_dos_introduce_burst_per_sec =
static uint32_t consensus_param_introduce_burst_per_sec =
HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC;
static uint32_t hs_dos_introduce_enabled =
static uint32_t consensus_param_introduce_defense_enabled =
HS_DOS_INTRODUCE_ENABLED_DEFAULT;
static uint32_t
get_param_intro_dos_enabled(const networkstatus_t *ns)
STATIC uint32_t
get_intro2_enable_consensus_param(const networkstatus_t *ns)
{
return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense",
HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1);
}
/* Return the parameter for the introduction rate per sec. */
static uint32_t
get_param_rate_per_sec(const networkstatus_t *ns)
STATIC uint32_t
get_intro2_rate_consensus_param(const networkstatus_t *ns)
{
return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec",
HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC,
@@ -70,8 +72,8 @@ get_param_rate_per_sec(const networkstatus_t *ns)
}
/* Return the parameter for the introduction burst per sec. */
static uint32_t
get_param_burst_per_sec(const networkstatus_t *ns)
STATIC uint32_t
get_intro2_burst_consensus_param(const networkstatus_t *ns)
{
return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec",
HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC,
@@ -88,10 +90,13 @@ update_intro_circuits(void)
smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side();
SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) {
/* Defenses might have been enabled or disabled. */
TO_OR_CIRCUIT(circ)->introduce2_dos_defense_enabled =
consensus_param_introduce_defense_enabled;
/* Adjust the rate/burst value that might have changed. */
token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket,
hs_dos_get_intro2_rate(),
hs_dos_get_intro2_burst());
consensus_param_introduce_rate_per_sec,
consensus_param_introduce_burst_per_sec);
} SMARTLIST_FOREACH_END(circ);
smartlist_free(intro_circs);
@@ -101,9 +106,12 @@ update_intro_circuits(void)
static void
set_consensus_parameters(const networkstatus_t *ns)
{
hs_dos_introduce_rate_per_sec = get_param_rate_per_sec(ns);
hs_dos_introduce_burst_per_sec = get_param_burst_per_sec(ns);
hs_dos_introduce_enabled = get_param_intro_dos_enabled(ns);
consensus_param_introduce_rate_per_sec =
get_intro2_rate_consensus_param(ns);
consensus_param_introduce_burst_per_sec =
get_intro2_burst_consensus_param(ns);
consensus_param_introduce_defense_enabled =
get_intro2_enable_consensus_param(ns);
/* The above might have changed which means we need to go through all
* introduction circuits (relay side) and update the token buckets. */
@@ -114,18 +122,20 @@ set_consensus_parameters(const networkstatus_t *ns)
* Public API.
*/
/* Return the INTRODUCE2 cell rate per second. */
uint32_t
hs_dos_get_intro2_rate(void)
/* Initialize the INTRODUCE2 token bucket for the DoS defenses using the
* consensus/default values. We might get a cell extension that changes those
* later but if we don't, the default or consensus parameters are used. */
void
hs_dos_setup_default_intro2_defenses(or_circuit_t *circ)
{
return hs_dos_introduce_rate_per_sec;
}
tor_assert(circ);
/* Return the INTRODUCE2 cell burst per second. */
uint32_t
hs_dos_get_intro2_burst(void)
{
return hs_dos_introduce_burst_per_sec;
circ->introduce2_dos_defense_enabled =
consensus_param_introduce_defense_enabled;
token_bucket_ctr_init(&circ->introduce2_bucket,
consensus_param_introduce_rate_per_sec,
consensus_param_introduce_burst_per_sec,
(uint32_t) approx_time());
}
/* Called when the consensus has changed. We might have new consensus
@@ -149,8 +159,10 @@ hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
{
tor_assert(s_intro_circ);
/* Always allowed if the defense is disabled. */
if (!hs_dos_introduce_enabled) {
/* Allow to send the cell if the DoS defenses are disabled on the circuit.
* This can be set by the consensus, the ESTABLISH_INTRO cell extension or
* the hardcoded values in tor code. */
if (!s_intro_circ->introduce2_dos_defense_enabled) {
return true;
}
+6 -4
View File
@@ -20,16 +20,18 @@ void hs_dos_init(void);
/* Consensus. */
void hs_dos_consensus_has_changed(const networkstatus_t *ns);
/* Introduction Point. */
bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ);
/* Getters. */
uint32_t hs_dos_get_intro2_rate(void);
uint32_t hs_dos_get_intro2_burst(void);
void hs_dos_setup_default_intro2_defenses(or_circuit_t *circ);
#ifdef HS_DOS_PRIVATE
#ifdef TOR_UNIT_TESTS
STATIC uint32_t get_intro2_enable_consensus_param(const networkstatus_t *ns);
STATIC uint32_t get_intro2_rate_consensus_param(const networkstatus_t *ns);
STATIC uint32_t get_intro2_burst_consensus_param(const networkstatus_t *ns);
#endif /* define(TOR_UNIT_TESTS) */
#endif /* defined(HS_DOS_PRIVATE) */
+187 -3
View File
@@ -26,6 +26,7 @@
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_descriptor.h"
#include "feature/hs/hs_dos.h"
#include "feature/hs/hs_intropoint.h"
@@ -181,6 +182,185 @@ hs_intro_send_intro_established_cell,(or_circuit_t *circ))
return ret;
}
/* Validate the cell DoS extension parameters. Return true iff they've been
* bound check and can be used. Else return false. See proposal 305 for
* details and reasons about this validation. */
STATIC bool
cell_dos_extension_parameters_are_valid(uint64_t intro2_rate_per_sec,
uint64_t intro2_burst_per_sec)
{
bool ret = false;
/* Check that received value is not below the minimum. Don't check if minimum
is set to 0, since the param is a positive value and gcc will complain. */
#if HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN > 0
if (intro2_rate_per_sec < HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Intro point DoS defenses rate per second is "
"too small. Received value: %" PRIu64, intro2_rate_per_sec);
goto end;
}
#endif
/* Check that received value is not above maximum */
if (intro2_rate_per_sec > HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Intro point DoS defenses rate per second is "
"too big. Received value: %" PRIu64, intro2_rate_per_sec);
goto end;
}
/* Check that received value is not below the minimum */
#if HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN > 0
if (intro2_burst_per_sec < HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Intro point DoS defenses burst per second is "
"too small. Received value: %" PRIu64, intro2_burst_per_sec);
goto end;
}
#endif
/* Check that received value is not above maximum */
if (intro2_burst_per_sec > HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Intro point DoS defenses burst per second is "
"too big. Received value: %" PRIu64, intro2_burst_per_sec);
goto end;
}
/* In a rate limiting scenario, burst can never be smaller than the rate. At
* best it can be equal. */
if (intro2_burst_per_sec < intro2_rate_per_sec) {
log_info(LD_REND, "Intro point DoS defenses burst is smaller than rate. "
"Rate: %" PRIu64 " vs Burst: %" PRIu64,
intro2_rate_per_sec, intro2_burst_per_sec);
goto end;
}
/* Passing validation. */
ret = true;
end:
return ret;
}
/* Parse the cell DoS extension and apply defenses on the given circuit if
* validation passes. If the cell extension is malformed or contains unusable
* values, the DoS defenses is disabled on the circuit. */
static void
handle_establish_intro_cell_dos_extension(
const trn_cell_extension_field_t *field,
or_circuit_t *circ)
{
ssize_t ret;
uint64_t intro2_rate_per_sec = 0, intro2_burst_per_sec = 0;
trn_cell_extension_dos_t *dos = NULL;
tor_assert(field);
tor_assert(circ);
ret = trn_cell_extension_dos_parse(&dos,
trn_cell_extension_field_getconstarray_field(field),
trn_cell_extension_field_getlen_field(field));
if (ret < 0) {
goto end;
}
for (size_t i = 0; i < trn_cell_extension_dos_get_n_params(dos); i++) {
const trn_cell_extension_dos_param_t *param =
trn_cell_extension_dos_getconst_params(dos, i);
if (BUG(param == NULL)) {
goto end;
}
switch (trn_cell_extension_dos_param_get_type(param)) {
case TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC:
intro2_rate_per_sec = trn_cell_extension_dos_param_get_value(param);
break;
case TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC:
intro2_burst_per_sec = trn_cell_extension_dos_param_get_value(param);
break;
default:
goto end;
}
}
/* A value of 0 is valid in the sense that we accept it but we still disable
* the defenses so return false. */
if (intro2_rate_per_sec == 0 || intro2_burst_per_sec == 0) {
log_info(LD_REND, "Intro point DoS defenses parameter set to 0. "
"Disabling INTRO2 DoS defenses on circuit id %u",
circ->p_circ_id);
circ->introduce2_dos_defense_enabled = 0;
goto end;
}
/* If invalid, we disable the defense on the circuit. */
if (!cell_dos_extension_parameters_are_valid(intro2_rate_per_sec,
intro2_burst_per_sec)) {
circ->introduce2_dos_defense_enabled = 0;
log_info(LD_REND, "Disabling INTRO2 DoS defenses on circuit id %u",
circ->p_circ_id);
goto end;
}
/* We passed validation, enable defenses and apply rate/burst. */
circ->introduce2_dos_defense_enabled = 1;
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
token_bucket_ctr_init(&circ->introduce2_bucket,
(uint32_t) intro2_rate_per_sec,
(uint32_t) intro2_burst_per_sec,
(uint32_t) approx_time());
log_info(LD_REND, "Intro point DoS defenses enabled. Rate is %" PRIu64
" and Burst is %" PRIu64,
intro2_rate_per_sec, intro2_burst_per_sec);
end:
trn_cell_extension_dos_free(dos);
return;
}
/* Parse every cell extension in the given ESTABLISH_INTRO cell. */
static void
handle_establish_intro_cell_extensions(
const trn_cell_establish_intro_t *parsed_cell,
or_circuit_t *circ)
{
const trn_cell_extension_t *extensions;
tor_assert(parsed_cell);
tor_assert(circ);
extensions = trn_cell_establish_intro_getconst_extensions(parsed_cell);
if (extensions == NULL) {
goto end;
}
/* Go over all extensions. */
for (size_t idx = 0; idx < trn_cell_extension_get_num(extensions); idx++) {
const trn_cell_extension_field_t *field =
trn_cell_extension_getconst_fields(extensions, idx);
if (BUG(field == NULL)) {
/* The number of extensions should match the number of fields. */
break;
}
switch (trn_cell_extension_field_get_field_type(field)) {
case TRUNNEL_CELL_EXTENSION_TYPE_DOS:
/* After this, the circuit should be set for DoS defenses. */
handle_establish_intro_cell_dos_extension(field, circ);
break;
default:
/* Unknown extension. Skip over. */
break;
}
}
end:
return;
}
/** We received an ESTABLISH_INTRO <b>parsed_cell</b> on <b>circ</b>. It's
* well-formed and passed our verifications. Perform appropriate actions to
* establish an intro point. */
@@ -193,6 +373,13 @@ handle_verified_establish_intro_cell(or_circuit_t *circ,
get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO,
parsed_cell);
/* Setup INTRODUCE2 defenses on the circuit. Must be done before parsing the
* cell extension that can possibly change the defenses' values. */
hs_dos_setup_default_intro2_defenses(circ);
/* Handle cell extension if any. */
handle_establish_intro_cell_extensions(parsed_cell, circ);
/* Then notify the hidden service that the intro point is established by
sending an INTRO_ESTABLISHED cell */
if (hs_intro_send_intro_established_cell(circ)) {
@@ -204,9 +391,6 @@ handle_verified_establish_intro_cell(or_circuit_t *circ,
hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key);
/* Repurpose this circuit into an intro circuit. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(),
hs_dos_get_intro2_burst(), (uint32_t) approx_time());
return 0;
}
+3
View File
@@ -57,6 +57,9 @@ STATIC int handle_introduce1(or_circuit_t *client_circ,
const uint8_t *request, size_t request_len);
STATIC int validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell);
STATIC int circuit_is_suitable_for_introduce1(const or_circuit_t *circ);
STATIC bool cell_dos_extension_parameters_are_valid(
uint64_t intro2_rate_per_sec,
uint64_t intro2_burst_per_sec);
#endif /* defined(HS_INTROPOINT_PRIVATE) */
+7
View File
@@ -242,6 +242,9 @@ set_service_default_config(hs_service_config_t *c,
c->is_single_onion = 0;
c->dir_group_readable = 0;
c->is_ephemeral = 0;
c->has_dos_defense_enabled = HS_CONFIG_V3_DOS_DEFENSE_DEFAULT;
c->intro_dos_rate_per_sec = HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT;
c->intro_dos_burst_per_sec = HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT;
}
/* From a service configuration object config, clear everything from it
@@ -489,6 +492,10 @@ service_intro_point_new(const node_t *node)
}
}
/* Flag if this intro point supports the INTRO2 dos defenses. */
ip->support_intro2_dos_defense =
node_supports_establish_intro_dos_extension(node);
/* Finally, copy onion key from the node. */
memcpy(&ip->onion_key, node_get_curve25519_onion_key(node),
sizeof(ip->onion_key));
+9
View File
@@ -76,6 +76,10 @@ typedef struct hs_service_intro_point_t {
* circuit associated with this intro point has received. This is used to
* prevent replay attacks. */
replaycache_t *replay_cache;
/* Support the INTRO2 DoS defense. If set, the DoS extension described by
* proposal 305 is sent. */
unsigned int support_intro2_dos_defense : 1;
} hs_service_intro_point_t;
/* Object handling introduction points of a service. */
@@ -241,6 +245,11 @@ typedef struct hs_service_config_t {
/* Does this service export the circuit ID of its clients? */
hs_circuit_id_protocol_t circuit_id_protocol;
/* DoS defenses. For the ESTABLISH_INTRO cell extension. */
unsigned int has_dos_defense_enabled : 1;
uint32_t intro_dos_rate_per_sec;
uint32_t intro_dos_burst_per_sec;
} hs_service_config_t;
/* Service state. */
+12 -1
View File
@@ -1106,7 +1106,7 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id)
/** Dummy object that should be unreturnable. Used to ensure that
* node_get_protover_summary_flags() always returns non-NULL. */
static const protover_summary_flags_t zero_protover_flags = {
0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0
};
/** Return the protover_summary_flags for a given node. */
@@ -1166,6 +1166,17 @@ node_supports_ed25519_hs_intro(const node_t *node)
return node_get_protover_summary_flags(node)->supports_ed25519_hs_intro;
}
/** Return true iff <b>node</b> supports the DoS ESTABLISH_INTRO cell
* extenstion. */
int
node_supports_establish_intro_dos_extension(const node_t *node)
{
tor_assert(node);
return node_get_protover_summary_flags(node)->
supports_establish_intro_dos_extension;
}
/** Return true iff <b>node</b> supports to be a rendezvous point for hidden
* service version 3 (HSRend=2). */
int
+1
View File
@@ -76,6 +76,7 @@ int node_supports_ed25519_link_authentication(const node_t *node,
int node_supports_v3_hsdir(const node_t *node);
int node_supports_ed25519_hs_intro(const node_t *node);
int node_supports_v3_rendezvous_point(const node_t *node);
int node_supports_establish_intro_dos_extension(const node_t *node);
const uint8_t *node_get_rsa_id_digest(const node_t *node);
smartlist_t *node_get_link_specifier_smartlist(const node_t *node,
bool direct_conn);
+1 -2
View File
@@ -118,8 +118,7 @@ rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request,
/* Now, set up this circuit. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
hs_circuitmap_register_intro_circ_v2_relay_side(circ, (uint8_t *)pk_digest);
token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(),
hs_dos_get_intro2_burst(), (uint32_t) approx_time());
hs_dos_setup_default_intro2_defenses(circ);
log_info(LD_REND,
"Established introduction point on circuit %u for service %s",
+94 -2
View File
@@ -20,6 +20,7 @@
#include "feature/hs/hs_service.h"
/* Trunnel. */
#include "trunnel/hs/cell_common.h"
#include "trunnel/hs/cell_establish_intro.h"
/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
@@ -38,11 +39,13 @@ test_gen_establish_intro_cell(void *arg)
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
attempt to parse it. */
{
hs_service_config_t config;
memset(&config, 0, sizeof(config));
/* We only need the auth key pair here. */
hs_service_intro_point_t *ip = service_intro_point_new(NULL);
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
ret = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
service_intro_point_free(ip);
tt_u64_op(ret, OP_GT, 0);
}
@@ -97,6 +100,9 @@ test_gen_establish_intro_cell_bad(void *arg)
trn_cell_establish_intro_t *cell = NULL;
char circ_nonce[DIGEST_LEN] = {0};
hs_service_intro_point_t *ip = NULL;
hs_service_config_t config;
memset(&config, 0, sizeof(config));
MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
@@ -108,7 +114,7 @@ test_gen_establish_intro_cell_bad(void *arg)
cell = trn_cell_establish_intro_new();
tt_assert(cell);
ip = service_intro_point_new(NULL);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, NULL);
service_intro_point_free(ip);
expect_log_msg_containing("Unable to make signature for "
"ESTABLISH_INTRO cell.");
@@ -120,11 +126,97 @@ test_gen_establish_intro_cell_bad(void *arg)
UNMOCK(ed25519_sign_prefixed);
}
static void
test_gen_establish_intro_dos_ext(void *arg)
{
ssize_t ret;
hs_service_config_t config;
hs_service_intro_point_t *ip = NULL;
trn_cell_extension_t *extensions = NULL;
trn_cell_extension_dos_t *dos = NULL;
(void) arg;
memset(&config, 0, sizeof(config));
ip = service_intro_point_new(NULL);
tt_assert(ip);
ip->support_intro2_dos_defense = 1;
/* Case 1: No DoS parameters so no extension to be built. */
extensions = build_establish_intro_extensions(&config, ip);
tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 0);
trn_cell_extension_free(extensions);
extensions = NULL;
/* Case 2: Enable the DoS extension. Parameter set to 0 should indicate to
* disable the defense on the intro point but there should be an extension
* nonetheless in the cell. */
config.has_dos_defense_enabled = 1;
extensions = build_establish_intro_extensions(&config, ip);
tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1);
/* Validate the extension. */
const trn_cell_extension_field_t *field =
trn_cell_extension_getconst_fields(extensions, 0);
tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ,
TRUNNEL_CELL_EXTENSION_TYPE_DOS);
ret = trn_cell_extension_dos_parse(&dos,
trn_cell_extension_field_getconstarray_field(field),
trn_cell_extension_field_getlen_field(field));
tt_int_op(ret, OP_EQ, 19);
/* Rate per sec param. */
const trn_cell_extension_dos_param_t *param =
trn_cell_extension_dos_getconst_params(dos, 0);
tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC);
tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0);
/* Burst per sec param. */
param = trn_cell_extension_dos_getconst_params(dos, 1);
tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0);
trn_cell_extension_dos_free(dos); dos = NULL;
trn_cell_extension_free(extensions); extensions = NULL;
/* Case 3: Enable the DoS extension. Parameter set to some normal values. */
config.has_dos_defense_enabled = 1;
config.intro_dos_rate_per_sec = 42;
config.intro_dos_burst_per_sec = 250;
extensions = build_establish_intro_extensions(&config, ip);
tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1);
/* Validate the extension. */
field = trn_cell_extension_getconst_fields(extensions, 0);
tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ,
TRUNNEL_CELL_EXTENSION_TYPE_DOS);
ret = trn_cell_extension_dos_parse(&dos,
trn_cell_extension_field_getconstarray_field(field),
trn_cell_extension_field_getlen_field(field));
tt_int_op(ret, OP_EQ, 19);
/* Rate per sec param. */
param = trn_cell_extension_dos_getconst_params(dos, 0);
tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC);
tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 42);
/* Burst per sec param. */
param = trn_cell_extension_dos_getconst_params(dos, 1);
tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 250);
trn_cell_extension_dos_free(dos); dos = NULL;
trn_cell_extension_free(extensions); extensions = NULL;
done:
service_intro_point_free(ip);
trn_cell_extension_dos_free(dos);
trn_cell_extension_free(extensions);
}
struct testcase_t hs_cell_tests[] = {
{ "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
NULL, NULL },
{ "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
NULL, NULL },
{ "gen_establish_intro_dos_ext", test_gen_establish_intro_dos_ext, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
};
+109
View File
@@ -489,6 +489,111 @@ test_staging_service_v3(void *arg)
hs_free_all();
}
static void
test_dos_parameters(void *arg)
{
int ret;
(void) arg;
hs_init();
/* Valid configuration. */
{
const char *conf =
"HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort 22 1.1.1.1:22\n"
"HiddenServiceEnableIntroDoSDefense 1\n"
"HiddenServiceEnableIntroDoSRatePerSec 42\n"
"HiddenServiceEnableIntroDoSBurstPerSec 87\n";
setup_full_capture_of_logs(LOG_INFO);
ret = helper_config_service(conf, 0);
tt_int_op(ret, OP_EQ, 0);
expect_log_msg_containing("Service INTRO2 DoS defenses rate set to: 42");
expect_log_msg_containing("Service INTRO2 DoS defenses burst set to: 87");
teardown_capture_of_logs();
}
/* Invalid rate. Value of 2^37. Max allowed is 2^31. */
{
const char *conf =
"HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort 22 1.1.1.1:22\n"
"HiddenServiceEnableIntroDoSDefense 1\n"
"HiddenServiceEnableIntroDoSRatePerSec 137438953472\n"
"HiddenServiceEnableIntroDoSBurstPerSec 87\n";
setup_full_capture_of_logs(LOG_WARN);
ret = helper_config_service(conf, 0);
tt_int_op(ret, OP_EQ, -1);
expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must "
"be between 0 and 2147483647, "
"not 137438953472");
teardown_capture_of_logs();
}
/* Invalid burst. Value of 2^38. Max allowed is 2^31. */
{
const char *conf =
"HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort 22 1.1.1.1:22\n"
"HiddenServiceEnableIntroDoSDefense 1\n"
"HiddenServiceEnableIntroDoSRatePerSec 42\n"
"HiddenServiceEnableIntroDoSBurstPerSec 274877906944\n";
setup_full_capture_of_logs(LOG_WARN);
ret = helper_config_service(conf, 0);
tt_int_op(ret, OP_EQ, -1);
expect_log_msg_containing("HiddenServiceEnableIntroDoSBurstPerSec must "
"be between 0 and 2147483647, "
"not 274877906944");
teardown_capture_of_logs();
}
/* Burst is smaller than rate. */
{
const char *conf =
"HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort 22 1.1.1.1:22\n"
"HiddenServiceEnableIntroDoSDefense 1\n"
"HiddenServiceEnableIntroDoSRatePerSec 42\n"
"HiddenServiceEnableIntroDoSBurstPerSec 27\n";
setup_full_capture_of_logs(LOG_WARN);
ret = helper_config_service(conf, 0);
tt_int_op(ret, OP_EQ, -1);
expect_log_msg_containing("Hidden service DoS defenses burst (27) can "
"not be smaller than the rate value (42).");
teardown_capture_of_logs();
}
/* Negative value. */
{
const char *conf =
"HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort 22 1.1.1.1:22\n"
"HiddenServiceEnableIntroDoSDefense 1\n"
"HiddenServiceEnableIntroDoSRatePerSec -1\n"
"HiddenServiceEnableIntroDoSBurstPerSec 42\n";
setup_full_capture_of_logs(LOG_WARN);
ret = helper_config_service(conf, 0);
tt_int_op(ret, OP_EQ, -1);
expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must be "
"between 0 and 2147483647, not -1");
teardown_capture_of_logs();
}
done:
hs_free_all();
}
struct testcase_t hs_config_tests[] = {
/* Invalid service not specific to any version. */
{ "invalid_service", test_invalid_service, TT_FORK,
@@ -512,6 +617,10 @@ struct testcase_t hs_config_tests[] = {
{ "staging_service_v3", test_staging_service_v3, TT_FORK,
NULL, NULL },
/* Test HS DoS parameters. */
{ "dos_parameters", test_dos_parameters, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
};
+52 -10
View File
@@ -8,6 +8,8 @@
#define CIRCUITLIST_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define HS_DOS_PRIVATE
#define HS_INTROPOINT_PRIVATE
#include "test/test.h"
#include "test/test_helpers.h"
@@ -20,6 +22,7 @@
#include "core/or/or_circuit_st.h"
#include "feature/hs/hs_dos.h"
#include "feature/hs/hs_intropoint.h"
#include "feature/nodelist/networkstatus.h"
static void
@@ -57,9 +60,8 @@ test_can_send_intro2(void *arg)
/* Make that circuit a service intro point. */
circuit_change_purpose(TO_CIRCUIT(or_circ), CIRCUIT_PURPOSE_INTRO_POINT);
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
token_bucket_ctr_init(&or_circ->introduce2_bucket, hs_dos_get_intro2_rate(),
hs_dos_get_intro2_burst(), now);
hs_dos_setup_default_intro2_defenses(or_circ);
or_circ->introduce2_dos_defense_enabled = 1;
/* Brand new circuit, we should be able to send INTRODUCE2 cells. */
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
@@ -71,13 +73,13 @@ test_can_send_intro2(void *arg)
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
}
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
hs_dos_get_intro2_burst() - 10);
get_intro2_burst_consensus_param(NULL) - 10);
/* Fully refill the bucket minus 1 cell. */
update_approx_time(++now);
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
hs_dos_get_intro2_burst() - 1);
get_intro2_burst_consensus_param(NULL) - 1);
/* Receive an INTRODUCE2 at each second. We should have the bucket full
* since at every second it gets refilled. */
@@ -87,18 +89,18 @@ test_can_send_intro2(void *arg)
}
/* Last check if we can send the cell decrements the bucket so minus 1. */
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
hs_dos_get_intro2_burst() - 1);
get_intro2_burst_consensus_param(NULL) - 1);
/* Manually reset bucket for next test. */
token_bucket_ctr_reset(&or_circ->introduce2_bucket, now);
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
hs_dos_get_intro2_burst());
get_intro2_burst_consensus_param(NULL));
/* Do a full burst in the current second which should empty the bucket and
* we shouldn't be allowed to send one more cell after that. We go minus 1
* cell else the very last check if we can send the INTRO2 cell returns
* false because the bucket goes down to 0. */
for (uint32_t i = 0; i < hs_dos_get_intro2_burst() - 1; i++) {
for (uint32_t i = 0; i < get_intro2_burst_consensus_param(NULL) - 1; i++) {
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
}
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 1);
@@ -116,7 +118,7 @@ test_can_send_intro2(void *arg)
update_approx_time(++now);
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
hs_dos_get_intro2_rate() - 1);
get_intro2_rate_consensus_param(NULL) - 1);
done:
circuit_free_(TO_CIRCUIT(or_circ));
@@ -125,10 +127,50 @@ test_can_send_intro2(void *arg)
free_mock_consensus();
}
static void
test_validate_dos_extension_params(void *arg)
{
bool ret;
(void) arg;
/* Validate the default values. */
ret = cell_dos_extension_parameters_are_valid(
get_intro2_rate_consensus_param(NULL),
get_intro2_burst_consensus_param(NULL));
tt_assert(ret);
/* Valid custom rate/burst. */
ret = cell_dos_extension_parameters_are_valid(17, 42);
tt_assert(ret);
ret = cell_dos_extension_parameters_are_valid(INT32_MAX, INT32_MAX);
tt_assert(ret);
/* Invalid rate. */
ret = cell_dos_extension_parameters_are_valid(UINT64_MAX, 42);
tt_assert(!ret);
/* Invalid burst. */
ret = cell_dos_extension_parameters_are_valid(42, UINT64_MAX);
tt_assert(!ret);
/* Value of 0 is valid (but should disable defenses) */
ret = cell_dos_extension_parameters_are_valid(0, 0);
tt_assert(ret);
/* Can't have burst smaller than rate. */
ret = cell_dos_extension_parameters_are_valid(42, 40);
tt_assert(!ret);
done:
return;
}
struct testcase_t hs_dos_tests[] = {
{ "can_send_intro2", test_can_send_intro2, TT_FORK,
NULL, NULL },
{ "validate_dos_extension_params", test_validate_dos_extension_params,
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
+159 -2
View File
@@ -26,6 +26,7 @@
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_dos.h"
#include "feature/hs/hs_intropoint.h"
#include "feature/hs/hs_service.h"
@@ -45,6 +46,9 @@ new_establish_intro_cell(const char *circ_nonce,
uint8_t buf[RELAY_PAYLOAD_SIZE] = {0};
trn_cell_establish_intro_t *cell = NULL;
hs_service_intro_point_t *ip = NULL;
hs_service_config_t config;
memset(&config, 0, sizeof(config));
/* Ensure that *cell_out is NULL such that we can use to check if we need to
* free `cell` in case of an error. */
@@ -54,7 +58,7 @@ new_establish_intro_cell(const char *circ_nonce,
* using this IP object. */
ip = service_intro_point_new(NULL);
tt_assert(ip);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
tt_i64_op(cell_len, OP_GT, 0);
cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf));
@@ -75,12 +79,15 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
{
ssize_t cell_len = 0;
hs_service_intro_point_t *ip = NULL;
hs_service_config_t config;
memset(&config, 0, sizeof(config));
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
ip = service_intro_point_new(NULL);
tt_assert(ip);
cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell_out);
tt_i64_op(cell_len, OP_GT, 0);
done:
@@ -903,6 +910,153 @@ test_received_introduce1_handling(void *arg)
UNMOCK(relay_send_command_from_edge_);
}
static void
test_received_establish_intro_dos_ext(void *arg)
{
int ret;
ssize_t cell_len = 0;
uint8_t cell[RELAY_PAYLOAD_SIZE] = {0};
char circ_nonce[DIGEST_LEN] = {0};
hs_service_intro_point_t *ip = NULL;
hs_service_config_t config;
or_circuit_t *intro_circ = or_circuit_new(0,NULL);
(void) arg;
MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
hs_circuitmap_init();
/* Setup. */
crypto_rand(circ_nonce, sizeof(circ_nonce));
ip = service_intro_point_new(NULL);
tt_assert(ip);
ip->support_intro2_dos_defense = 1;
memset(&config, 0, sizeof(config));
config.has_dos_defense_enabled = 1;
config.intro_dos_rate_per_sec = 13;
config.intro_dos_burst_per_sec = 42;
helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* The INTRO2 bucket should be 0 at this point. */
tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, 0);
tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, 0);
tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, 0);
tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, 0);
/* Case 1: Build encoded cell. Usable DoS parameters. */
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
tt_size_op(cell_len, OP_GT, 0);
/* Pass it to the intro point. */
ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
tt_int_op(ret, OP_EQ, 0);
/* Should be set to the burst value. */
tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, 42);
/* Validate the config of the intro2 bucket. */
tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, 13);
tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, 42);
tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, 1);
/* Need to reset the circuit in between test cases. */
circuit_free_(TO_CIRCUIT(intro_circ));
intro_circ = or_circuit_new(0,NULL);
helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Case 2: Build encoded cell. Bad DoS parameters. */
config.has_dos_defense_enabled = 1;
config.intro_dos_rate_per_sec = UINT_MAX;
config.intro_dos_burst_per_sec = 13;
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
tt_size_op(cell_len, OP_GT, 0);
/* Pass it to the intro point. */
ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
tt_int_op(ret, OP_EQ, 0);
tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
/* Need to reset the circuit in between test cases. */
circuit_free_(TO_CIRCUIT(intro_circ));
intro_circ = or_circuit_new(0,NULL);
helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Case 3: Build encoded cell. Burst is smaller than rate. Not allowed. */
config.has_dos_defense_enabled = 1;
config.intro_dos_rate_per_sec = 87;
config.intro_dos_burst_per_sec = 45;
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
tt_size_op(cell_len, OP_GT, 0);
/* Pass it to the intro point. */
ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
tt_int_op(ret, OP_EQ, 0);
tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
/* Need to reset the circuit in between test cases. */
circuit_free_(TO_CIRCUIT(intro_circ));
intro_circ = or_circuit_new(0,NULL);
helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Case 4: Build encoded cell. Rate is 0 but burst is not 0. Disables the
* defense. */
config.has_dos_defense_enabled = 1;
config.intro_dos_rate_per_sec = 0;
config.intro_dos_burst_per_sec = 45;
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
tt_size_op(cell_len, OP_GT, 0);
/* Pass it to the intro point. */
ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
tt_int_op(ret, OP_EQ, 0);
tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
/* Need to reset the circuit in between test cases. */
circuit_free_(TO_CIRCUIT(intro_circ));
intro_circ = or_circuit_new(0,NULL);
helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Case 5: Build encoded cell. Burst is 0 but rate is not 0. Disables the
* defense. */
config.has_dos_defense_enabled = 1;
config.intro_dos_rate_per_sec = 45;
config.intro_dos_burst_per_sec = 0;
cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell);
tt_size_op(cell_len, OP_GT, 0);
/* Pass it to the intro point. */
ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len);
tt_int_op(ret, OP_EQ, 0);
tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT);
tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ,
HS_CONFIG_V3_DOS_DEFENSE_DEFAULT);
done:
circuit_free_(TO_CIRCUIT(intro_circ));
service_intro_point_free(ip);
hs_circuitmap_free_all();
UNMOCK(relay_send_command_from_edge_);
}
static void *
hs_subsystem_setup_fn(const struct testcase_t *tc)
{
@@ -961,5 +1115,8 @@ struct testcase_t hs_intropoint_tests[] = {
{ "received_introduce1_handling",
test_received_introduce1_handling, TT_FORK, NULL, &test_setup},
{ "received_establish_intro_dos_ext",
test_received_establish_intro_dos_ext, TT_FORK, NULL, &test_setup},
END_OF_TESTCASES
};
+58 -58
View File
@@ -28,10 +28,10 @@ int cellcommon_deadcode_dummy__ = 0;
} \
} while (0)
trn_cell_extension_fields_t *
trn_cell_extension_fields_new(void)
trn_cell_extension_field_t *
trn_cell_extension_field_new(void)
{
trn_cell_extension_fields_t *val = trunnel_calloc(1, sizeof(trn_cell_extension_fields_t));
trn_cell_extension_field_t *val = trunnel_calloc(1, sizeof(trn_cell_extension_field_t));
if (NULL == val)
return NULL;
return val;
@@ -40,7 +40,7 @@ trn_cell_extension_fields_new(void)
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
trn_cell_extension_fields_clear(trn_cell_extension_fields_t *obj)
trn_cell_extension_field_clear(trn_cell_extension_field_t *obj)
{
(void) obj;
TRUNNEL_DYNARRAY_WIPE(&obj->field);
@@ -48,62 +48,62 @@ trn_cell_extension_fields_clear(trn_cell_extension_fields_t *obj)
}
void
trn_cell_extension_fields_free(trn_cell_extension_fields_t *obj)
trn_cell_extension_field_free(trn_cell_extension_field_t *obj)
{
if (obj == NULL)
return;
trn_cell_extension_fields_clear(obj);
trunnel_memwipe(obj, sizeof(trn_cell_extension_fields_t));
trn_cell_extension_field_clear(obj);
trunnel_memwipe(obj, sizeof(trn_cell_extension_field_t));
trunnel_free_(obj);
}
uint8_t
trn_cell_extension_fields_get_field_type(const trn_cell_extension_fields_t *inp)
trn_cell_extension_field_get_field_type(const trn_cell_extension_field_t *inp)
{
return inp->field_type;
}
int
trn_cell_extension_fields_set_field_type(trn_cell_extension_fields_t *inp, uint8_t val)
trn_cell_extension_field_set_field_type(trn_cell_extension_field_t *inp, uint8_t val)
{
inp->field_type = val;
return 0;
}
uint8_t
trn_cell_extension_fields_get_field_len(const trn_cell_extension_fields_t *inp)
trn_cell_extension_field_get_field_len(const trn_cell_extension_field_t *inp)
{
return inp->field_len;
}
int
trn_cell_extension_fields_set_field_len(trn_cell_extension_fields_t *inp, uint8_t val)
trn_cell_extension_field_set_field_len(trn_cell_extension_field_t *inp, uint8_t val)
{
inp->field_len = val;
return 0;
}
size_t
trn_cell_extension_fields_getlen_field(const trn_cell_extension_fields_t *inp)
trn_cell_extension_field_getlen_field(const trn_cell_extension_field_t *inp)
{
return TRUNNEL_DYNARRAY_LEN(&inp->field);
}
uint8_t
trn_cell_extension_fields_get_field(trn_cell_extension_fields_t *inp, size_t idx)
trn_cell_extension_field_get_field(trn_cell_extension_field_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->field, idx);
}
uint8_t
trn_cell_extension_fields_getconst_field(const trn_cell_extension_fields_t *inp, size_t idx)
trn_cell_extension_field_getconst_field(const trn_cell_extension_field_t *inp, size_t idx)
{
return trn_cell_extension_fields_get_field((trn_cell_extension_fields_t*)inp, idx);
return trn_cell_extension_field_get_field((trn_cell_extension_field_t*)inp, idx);
}
int
trn_cell_extension_fields_set_field(trn_cell_extension_fields_t *inp, size_t idx, uint8_t elt)
trn_cell_extension_field_set_field(trn_cell_extension_field_t *inp, size_t idx, uint8_t elt)
{
TRUNNEL_DYNARRAY_SET(&inp->field, idx, elt);
return 0;
}
int
trn_cell_extension_fields_add_field(trn_cell_extension_fields_t *inp, uint8_t elt)
trn_cell_extension_field_add_field(trn_cell_extension_field_t *inp, uint8_t elt)
{
#if SIZE_MAX >= UINT8_MAX
if (inp->field.n_ == UINT8_MAX)
@@ -117,17 +117,17 @@ trn_cell_extension_fields_add_field(trn_cell_extension_fields_t *inp, uint8_t el
}
uint8_t *
trn_cell_extension_fields_getarray_field(trn_cell_extension_fields_t *inp)
trn_cell_extension_field_getarray_field(trn_cell_extension_field_t *inp)
{
return inp->field.elts_;
}
const uint8_t *
trn_cell_extension_fields_getconstarray_field(const trn_cell_extension_fields_t *inp)
trn_cell_extension_field_getconstarray_field(const trn_cell_extension_field_t *inp)
{
return (const uint8_t *)trn_cell_extension_fields_getarray_field((trn_cell_extension_fields_t*)inp);
return (const uint8_t *)trn_cell_extension_field_getarray_field((trn_cell_extension_field_t*)inp);
}
int
trn_cell_extension_fields_setlen_field(trn_cell_extension_fields_t *inp, size_t newlen)
trn_cell_extension_field_setlen_field(trn_cell_extension_field_t *inp, size_t newlen)
{
uint8_t *newptr;
#if UINT8_MAX < SIZE_MAX
@@ -147,7 +147,7 @@ trn_cell_extension_fields_setlen_field(trn_cell_extension_fields_t *inp, size_t
return -1;
}
const char *
trn_cell_extension_fields_check(const trn_cell_extension_fields_t *obj)
trn_cell_extension_field_check(const trn_cell_extension_field_t *obj)
{
if (obj == NULL)
return "Object was NULL";
@@ -159,11 +159,11 @@ trn_cell_extension_fields_check(const trn_cell_extension_fields_t *obj)
}
ssize_t
trn_cell_extension_fields_encoded_len(const trn_cell_extension_fields_t *obj)
trn_cell_extension_field_encoded_len(const trn_cell_extension_field_t *obj)
{
ssize_t result = 0;
if (NULL != trn_cell_extension_fields_check(obj))
if (NULL != trn_cell_extension_field_check(obj))
return -1;
@@ -178,24 +178,24 @@ trn_cell_extension_fields_encoded_len(const trn_cell_extension_fields_t *obj)
return result;
}
int
trn_cell_extension_fields_clear_errors(trn_cell_extension_fields_t *obj)
trn_cell_extension_field_clear_errors(trn_cell_extension_field_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
trn_cell_extension_fields_encode(uint8_t *output, const size_t avail, const trn_cell_extension_fields_t *obj)
trn_cell_extension_field_encode(uint8_t *output, const size_t avail, const trn_cell_extension_field_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = trn_cell_extension_fields_encoded_len(obj);
const ssize_t encoded_len = trn_cell_extension_field_encoded_len(obj);
#endif
if (NULL != (msg = trn_cell_extension_fields_check(obj)))
if (NULL != (msg = trn_cell_extension_field_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
@@ -252,11 +252,11 @@ trn_cell_extension_fields_encode(uint8_t *output, const size_t avail, const trn_
return result;
}
/** As trn_cell_extension_fields_parse(), but do not allocate the
/** As trn_cell_extension_field_parse(), but do not allocate the
* output object.
*/
static ssize_t
trn_cell_extension_fields_parse_into(trn_cell_extension_fields_t *obj, const uint8_t *input, const size_t len_in)
trn_cell_extension_field_parse_into(trn_cell_extension_field_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
@@ -290,15 +290,15 @@ trn_cell_extension_fields_parse_into(trn_cell_extension_fields_t *obj, const uin
}
ssize_t
trn_cell_extension_fields_parse(trn_cell_extension_fields_t **output, const uint8_t *input, const size_t len_in)
trn_cell_extension_field_parse(trn_cell_extension_field_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = trn_cell_extension_fields_new();
*output = trn_cell_extension_field_new();
if (NULL == *output)
return -1;
result = trn_cell_extension_fields_parse_into(*output, input, len_in);
result = trn_cell_extension_field_parse_into(*output, input, len_in);
if (result < 0) {
trn_cell_extension_fields_free(*output);
trn_cell_extension_field_free(*output);
*output = NULL;
}
return result;
@@ -322,7 +322,7 @@ trn_cell_extension_clear(trn_cell_extension_t *obj)
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->fields); ++idx) {
trn_cell_extension_fields_free(TRUNNEL_DYNARRAY_GET(&obj->fields, idx));
trn_cell_extension_field_free(TRUNNEL_DYNARRAY_GET(&obj->fields, idx));
}
}
TRUNNEL_DYNARRAY_WIPE(&obj->fields);
@@ -356,66 +356,66 @@ trn_cell_extension_getlen_fields(const trn_cell_extension_t *inp)
return TRUNNEL_DYNARRAY_LEN(&inp->fields);
}
struct trn_cell_extension_fields_st *
struct trn_cell_extension_field_st *
trn_cell_extension_get_fields(trn_cell_extension_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->fields, idx);
}
const struct trn_cell_extension_fields_st *
const struct trn_cell_extension_field_st *
trn_cell_extension_getconst_fields(const trn_cell_extension_t *inp, size_t idx)
{
return trn_cell_extension_get_fields((trn_cell_extension_t*)inp, idx);
}
int
trn_cell_extension_set_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_fields_st * elt)
trn_cell_extension_set_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_field_st * elt)
{
trn_cell_extension_fields_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->fields, idx);
trn_cell_extension_field_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->fields, idx);
if (oldval && oldval != elt)
trn_cell_extension_fields_free(oldval);
trn_cell_extension_field_free(oldval);
return trn_cell_extension_set0_fields(inp, idx, elt);
}
int
trn_cell_extension_set0_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_fields_st * elt)
trn_cell_extension_set0_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_field_st * elt)
{
TRUNNEL_DYNARRAY_SET(&inp->fields, idx, elt);
return 0;
}
int
trn_cell_extension_add_fields(trn_cell_extension_t *inp, struct trn_cell_extension_fields_st * elt)
trn_cell_extension_add_fields(trn_cell_extension_t *inp, struct trn_cell_extension_field_st * elt)
{
#if SIZE_MAX >= UINT8_MAX
if (inp->fields.n_ == UINT8_MAX)
goto trunnel_alloc_failed;
#endif
TRUNNEL_DYNARRAY_ADD(struct trn_cell_extension_fields_st *, &inp->fields, elt, {});
TRUNNEL_DYNARRAY_ADD(struct trn_cell_extension_field_st *, &inp->fields, elt, {});
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
struct trn_cell_extension_fields_st * *
struct trn_cell_extension_field_st * *
trn_cell_extension_getarray_fields(trn_cell_extension_t *inp)
{
return inp->fields.elts_;
}
const struct trn_cell_extension_fields_st * const *
const struct trn_cell_extension_field_st * const *
trn_cell_extension_getconstarray_fields(const trn_cell_extension_t *inp)
{
return (const struct trn_cell_extension_fields_st * const *)trn_cell_extension_getarray_fields((trn_cell_extension_t*)inp);
return (const struct trn_cell_extension_field_st * const *)trn_cell_extension_getarray_fields((trn_cell_extension_t*)inp);
}
int
trn_cell_extension_setlen_fields(trn_cell_extension_t *inp, size_t newlen)
{
struct trn_cell_extension_fields_st * *newptr;
struct trn_cell_extension_field_st * *newptr;
#if UINT8_MAX < SIZE_MAX
if (newlen > UINT8_MAX)
goto trunnel_alloc_failed;
#endif
newptr = trunnel_dynarray_setlen(&inp->fields.allocated_,
&inp->fields.n_, inp->fields.elts_, newlen,
sizeof(inp->fields.elts_[0]), (trunnel_free_fn_t) trn_cell_extension_fields_free,
sizeof(inp->fields.elts_[0]), (trunnel_free_fn_t) trn_cell_extension_field_free,
&inp->trunnel_error_code_);
if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
@@ -437,7 +437,7 @@ trn_cell_extension_check(const trn_cell_extension_t *obj)
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->fields); ++idx) {
if (NULL != (msg = trn_cell_extension_fields_check(TRUNNEL_DYNARRAY_GET(&obj->fields, idx))))
if (NULL != (msg = trn_cell_extension_field_check(TRUNNEL_DYNARRAY_GET(&obj->fields, idx))))
return msg;
}
}
@@ -458,12 +458,12 @@ trn_cell_extension_encoded_len(const trn_cell_extension_t *obj)
/* Length of u8 num */
result += 1;
/* Length of struct trn_cell_extension_fields fields[num] */
/* Length of struct trn_cell_extension_field fields[num] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->fields); ++idx) {
result += trn_cell_extension_fields_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->fields, idx));
result += trn_cell_extension_field_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->fields, idx));
}
}
return result;
@@ -500,13 +500,13 @@ trn_cell_extension_encode(uint8_t *output, const size_t avail, const trn_cell_ex
trunnel_set_uint8(ptr, (obj->num));
written += 1; ptr += 1;
/* Encode struct trn_cell_extension_fields fields[num] */
/* Encode struct trn_cell_extension_field fields[num] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->fields); ++idx) {
trunnel_assert(written <= avail);
result = trn_cell_extension_fields_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->fields, idx));
result = trn_cell_extension_field_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->fields, idx));
if (result < 0)
goto fail; /* XXXXXXX !*/
written += result; ptr += result;
@@ -553,18 +553,18 @@ trn_cell_extension_parse_into(trn_cell_extension_t *obj, const uint8_t *input, c
obj->num = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse struct trn_cell_extension_fields fields[num] */
TRUNNEL_DYNARRAY_EXPAND(trn_cell_extension_fields_t *, &obj->fields, obj->num, {});
/* Parse struct trn_cell_extension_field fields[num] */
TRUNNEL_DYNARRAY_EXPAND(trn_cell_extension_field_t *, &obj->fields, obj->num, {});
{
trn_cell_extension_fields_t * elt;
trn_cell_extension_field_t * elt;
unsigned idx;
for (idx = 0; idx < obj->num; ++idx) {
result = trn_cell_extension_fields_parse(&elt, ptr, remaining);
result = trn_cell_extension_field_parse(&elt, ptr, remaining);
if (result < 0)
goto relay_fail;
trunnel_assert((size_t)result <= remaining);
remaining -= result; ptr += result;
TRUNNEL_DYNARRAY_ADD(trn_cell_extension_fields_t *, &obj->fields, elt, {trn_cell_extension_fields_free(elt);});
TRUNNEL_DYNARRAY_ADD(trn_cell_extension_field_t *, &obj->fields, elt, {trn_cell_extension_field_free(elt);});
}
}
trunnel_assert(ptr + remaining == input + len_in);
+49 -49
View File
@@ -8,112 +8,112 @@
#include <stdint.h>
#include "trunnel.h"
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_FIELDS)
struct trn_cell_extension_fields_st {
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_FIELD)
struct trn_cell_extension_field_st {
uint8_t field_type;
uint8_t field_len;
TRUNNEL_DYNARRAY_HEAD(, uint8_t) field;
uint8_t trunnel_error_code_;
};
#endif
typedef struct trn_cell_extension_fields_st trn_cell_extension_fields_t;
typedef struct trn_cell_extension_field_st trn_cell_extension_field_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION)
struct trn_cell_extension_st {
uint8_t num;
TRUNNEL_DYNARRAY_HEAD(, struct trn_cell_extension_fields_st *) fields;
TRUNNEL_DYNARRAY_HEAD(, struct trn_cell_extension_field_st *) fields;
uint8_t trunnel_error_code_;
};
#endif
typedef struct trn_cell_extension_st trn_cell_extension_t;
/** Return a newly allocated trn_cell_extension_fields with all
/** Return a newly allocated trn_cell_extension_field with all
* elements set to zero.
*/
trn_cell_extension_fields_t *trn_cell_extension_fields_new(void);
/** Release all storage held by the trn_cell_extension_fields in
trn_cell_extension_field_t *trn_cell_extension_field_new(void);
/** Release all storage held by the trn_cell_extension_field in
* 'victim'. (Do nothing if 'victim' is NULL.)
*/
void trn_cell_extension_fields_free(trn_cell_extension_fields_t *victim);
/** Try to parse a trn_cell_extension_fields from the buffer in
void trn_cell_extension_field_free(trn_cell_extension_field_t *victim);
/** Try to parse a trn_cell_extension_field from the buffer in
* 'input', using up to 'len_in' bytes from the input buffer. On
* success, return the number of bytes consumed and set *output to the
* newly allocated trn_cell_extension_fields_t. On failure, return -2
* newly allocated trn_cell_extension_field_t. On failure, return -2
* if the input appears truncated, and -1 if the input is otherwise
* invalid.
*/
ssize_t trn_cell_extension_fields_parse(trn_cell_extension_fields_t **output, const uint8_t *input, const size_t len_in);
ssize_t trn_cell_extension_field_parse(trn_cell_extension_field_t **output, const uint8_t *input, const size_t len_in);
/** Return the number of bytes we expect to need to encode the
* trn_cell_extension_fields in 'obj'. On failure, return a negative
* trn_cell_extension_field in 'obj'. On failure, return a negative
* value. Note that this value may be an overestimate, and can even be
* an underestimate for certain unencodeable objects.
*/
ssize_t trn_cell_extension_fields_encoded_len(const trn_cell_extension_fields_t *obj);
/** Try to encode the trn_cell_extension_fields from 'input' into the
ssize_t trn_cell_extension_field_encoded_len(const trn_cell_extension_field_t *obj);
/** Try to encode the trn_cell_extension_field from 'input' into the
* buffer at 'output', using up to 'avail' bytes of the output buffer.
* On success, return the number of bytes used. On failure, return -2
* if the buffer was not long enough, and -1 if the input was invalid.
*/
ssize_t trn_cell_extension_fields_encode(uint8_t *output, size_t avail, const trn_cell_extension_fields_t *input);
/** Check whether the internal state of the trn_cell_extension_fields
ssize_t trn_cell_extension_field_encode(uint8_t *output, size_t avail, const trn_cell_extension_field_t *input);
/** Check whether the internal state of the trn_cell_extension_field
* in 'obj' is consistent. Return NULL if it is, and a short message
* if it is not.
*/
const char *trn_cell_extension_fields_check(const trn_cell_extension_fields_t *obj);
const char *trn_cell_extension_field_check(const trn_cell_extension_field_t *obj);
/** Clear any errors that were set on the object 'obj' by its setter
* functions. Return true iff errors were cleared.
*/
int trn_cell_extension_fields_clear_errors(trn_cell_extension_fields_t *obj);
int trn_cell_extension_field_clear_errors(trn_cell_extension_field_t *obj);
/** Return the value of the field_type field of the
* trn_cell_extension_fields_t in 'inp'
* trn_cell_extension_field_t in 'inp'
*/
uint8_t trn_cell_extension_fields_get_field_type(const trn_cell_extension_fields_t *inp);
uint8_t trn_cell_extension_field_get_field_type(const trn_cell_extension_field_t *inp);
/** Set the value of the field_type field of the
* trn_cell_extension_fields_t in 'inp' to 'val'. Return 0 on success;
* trn_cell_extension_field_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
*/
int trn_cell_extension_fields_set_field_type(trn_cell_extension_fields_t *inp, uint8_t val);
int trn_cell_extension_field_set_field_type(trn_cell_extension_field_t *inp, uint8_t val);
/** Return the value of the field_len field of the
* trn_cell_extension_fields_t in 'inp'
* trn_cell_extension_field_t in 'inp'
*/
uint8_t trn_cell_extension_fields_get_field_len(const trn_cell_extension_fields_t *inp);
uint8_t trn_cell_extension_field_get_field_len(const trn_cell_extension_field_t *inp);
/** Set the value of the field_len field of the
* trn_cell_extension_fields_t in 'inp' to 'val'. Return 0 on success;
* trn_cell_extension_field_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
*/
int trn_cell_extension_fields_set_field_len(trn_cell_extension_fields_t *inp, uint8_t val);
int trn_cell_extension_field_set_field_len(trn_cell_extension_field_t *inp, uint8_t val);
/** Return the length of the dynamic array holding the field field of
* the trn_cell_extension_fields_t in 'inp'.
* the trn_cell_extension_field_t in 'inp'.
*/
size_t trn_cell_extension_fields_getlen_field(const trn_cell_extension_fields_t *inp);
size_t trn_cell_extension_field_getlen_field(const trn_cell_extension_field_t *inp);
/** Return the element at position 'idx' of the dynamic array field
* field of the trn_cell_extension_fields_t in 'inp'.
* field of the trn_cell_extension_field_t in 'inp'.
*/
uint8_t trn_cell_extension_fields_get_field(trn_cell_extension_fields_t *inp, size_t idx);
/** As trn_cell_extension_fields_get_field, but take and return a
* const pointer
uint8_t trn_cell_extension_field_get_field(trn_cell_extension_field_t *inp, size_t idx);
/** As trn_cell_extension_field_get_field, but take and return a const
* pointer
*/
uint8_t trn_cell_extension_fields_getconst_field(const trn_cell_extension_fields_t *inp, size_t idx);
uint8_t trn_cell_extension_field_getconst_field(const trn_cell_extension_field_t *inp, size_t idx);
/** Change the element at position 'idx' of the dynamic array field
* field of the trn_cell_extension_fields_t in 'inp', so that it will
* field of the trn_cell_extension_field_t in 'inp', so that it will
* hold the value 'elt'.
*/
int trn_cell_extension_fields_set_field(trn_cell_extension_fields_t *inp, size_t idx, uint8_t elt);
int trn_cell_extension_field_set_field(trn_cell_extension_field_t *inp, size_t idx, uint8_t elt);
/** Append a new element 'elt' to the dynamic array field field of the
* trn_cell_extension_fields_t in 'inp'.
* trn_cell_extension_field_t in 'inp'.
*/
int trn_cell_extension_fields_add_field(trn_cell_extension_fields_t *inp, uint8_t elt);
int trn_cell_extension_field_add_field(trn_cell_extension_field_t *inp, uint8_t elt);
/** Return a pointer to the variable-length array field field of
* 'inp'.
*/
uint8_t * trn_cell_extension_fields_getarray_field(trn_cell_extension_fields_t *inp);
/** As trn_cell_extension_fields_get_field, but take and return a
* const pointer
uint8_t * trn_cell_extension_field_getarray_field(trn_cell_extension_field_t *inp);
/** As trn_cell_extension_field_get_field, but take and return a const
* pointer
*/
const uint8_t * trn_cell_extension_fields_getconstarray_field(const trn_cell_extension_fields_t *inp);
const uint8_t * trn_cell_extension_field_getconstarray_field(const trn_cell_extension_field_t *inp);
/** Change the length of the variable-length array field field of
* 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
*/
int trn_cell_extension_fields_setlen_field(trn_cell_extension_fields_t *inp, size_t newlen);
int trn_cell_extension_field_setlen_field(trn_cell_extension_field_t *inp, size_t newlen);
/** Return a newly allocated trn_cell_extension with all elements set
* to zero.
*/
@@ -166,32 +166,32 @@ size_t trn_cell_extension_getlen_fields(const trn_cell_extension_t *inp);
/** Return the element at position 'idx' of the dynamic array field
* fields of the trn_cell_extension_t in 'inp'.
*/
struct trn_cell_extension_fields_st * trn_cell_extension_get_fields(trn_cell_extension_t *inp, size_t idx);
struct trn_cell_extension_field_st * trn_cell_extension_get_fields(trn_cell_extension_t *inp, size_t idx);
/** As trn_cell_extension_get_fields, but take and return a const
* pointer
*/
const struct trn_cell_extension_fields_st * trn_cell_extension_getconst_fields(const trn_cell_extension_t *inp, size_t idx);
const struct trn_cell_extension_field_st * trn_cell_extension_getconst_fields(const trn_cell_extension_t *inp, size_t idx);
/** Change the element at position 'idx' of the dynamic array field
* fields of the trn_cell_extension_t in 'inp', so that it will hold
* the value 'elt'. Free the previous value, if any.
*/
int trn_cell_extension_set_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_fields_st * elt);
int trn_cell_extension_set_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_field_st * elt);
/** As trn_cell_extension_set_fields, but does not free the previous
* value.
*/
int trn_cell_extension_set0_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_fields_st * elt);
int trn_cell_extension_set0_fields(trn_cell_extension_t *inp, size_t idx, struct trn_cell_extension_field_st * elt);
/** Append a new element 'elt' to the dynamic array field fields of
* the trn_cell_extension_t in 'inp'.
*/
int trn_cell_extension_add_fields(trn_cell_extension_t *inp, struct trn_cell_extension_fields_st * elt);
int trn_cell_extension_add_fields(trn_cell_extension_t *inp, struct trn_cell_extension_field_st * elt);
/** Return a pointer to the variable-length array field fields of
* 'inp'.
*/
struct trn_cell_extension_fields_st * * trn_cell_extension_getarray_fields(trn_cell_extension_t *inp);
struct trn_cell_extension_field_st * * trn_cell_extension_getarray_fields(trn_cell_extension_t *inp);
/** As trn_cell_extension_get_fields, but take and return a const
* pointer
*/
const struct trn_cell_extension_fields_st * const * trn_cell_extension_getconstarray_fields(const trn_cell_extension_t *inp);
const struct trn_cell_extension_field_st * const * trn_cell_extension_getconstarray_fields(const trn_cell_extension_t *inp);
/** Change the length of the variable-length array field fields of
* 'inp' to 'newlen'.Fill extra elements with NULL; free removed
* elements. Return 0 on success; return -1 and set the error code on
+2 -2
View File
@@ -1,6 +1,6 @@
/* This file contains common data structure that cells use. */
struct trn_cell_extension_fields {
struct trn_cell_extension_field {
u8 field_type;
u8 field_len;
u8 field[field_len];
@@ -8,5 +8,5 @@ struct trn_cell_extension_fields {
struct trn_cell_extension {
u8 num;
struct trn_cell_extension_fields fields[num];
struct trn_cell_extension_field fields[num];
};
+469
View File
@@ -36,6 +36,185 @@ ssize_t trn_cell_extension_encoded_len(const trn_cell_extension_t *obj);
ssize_t trn_cell_extension_encode(uint8_t *output, size_t avail, const trn_cell_extension_t *input);
const char *trn_cell_extension_check(const trn_cell_extension_t *obj);
int trn_cell_extension_clear_errors(trn_cell_extension_t *obj);
trn_cell_extension_dos_param_t *
trn_cell_extension_dos_param_new(void)
{
trn_cell_extension_dos_param_t *val = trunnel_calloc(1, sizeof(trn_cell_extension_dos_param_t));
if (NULL == val)
return NULL;
return val;
}
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
trn_cell_extension_dos_param_clear(trn_cell_extension_dos_param_t *obj)
{
(void) obj;
}
void
trn_cell_extension_dos_param_free(trn_cell_extension_dos_param_t *obj)
{
if (obj == NULL)
return;
trn_cell_extension_dos_param_clear(obj);
trunnel_memwipe(obj, sizeof(trn_cell_extension_dos_param_t));
trunnel_free_(obj);
}
uint8_t
trn_cell_extension_dos_param_get_type(const trn_cell_extension_dos_param_t *inp)
{
return inp->type;
}
int
trn_cell_extension_dos_param_set_type(trn_cell_extension_dos_param_t *inp, uint8_t val)
{
inp->type = val;
return 0;
}
uint64_t
trn_cell_extension_dos_param_get_value(const trn_cell_extension_dos_param_t *inp)
{
return inp->value;
}
int
trn_cell_extension_dos_param_set_value(trn_cell_extension_dos_param_t *inp, uint64_t val)
{
inp->value = val;
return 0;
}
const char *
trn_cell_extension_dos_param_check(const trn_cell_extension_dos_param_t *obj)
{
if (obj == NULL)
return "Object was NULL";
if (obj->trunnel_error_code_)
return "A set function failed on this object";
return NULL;
}
ssize_t
trn_cell_extension_dos_param_encoded_len(const trn_cell_extension_dos_param_t *obj)
{
ssize_t result = 0;
if (NULL != trn_cell_extension_dos_param_check(obj))
return -1;
/* Length of u8 type */
result += 1;
/* Length of u64 value */
result += 8;
return result;
}
int
trn_cell_extension_dos_param_clear_errors(trn_cell_extension_dos_param_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
trn_cell_extension_dos_param_encode(uint8_t *output, const size_t avail, const trn_cell_extension_dos_param_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = trn_cell_extension_dos_param_encoded_len(obj);
#endif
if (NULL != (msg = trn_cell_extension_dos_param_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
trunnel_assert(encoded_len >= 0);
#endif
/* Encode u8 type */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->type));
written += 1; ptr += 1;
/* Encode u64 value */
trunnel_assert(written <= avail);
if (avail - written < 8)
goto truncated;
trunnel_set_uint64(ptr, trunnel_htonll(obj->value));
written += 8; ptr += 8;
trunnel_assert(ptr == output + written);
#ifdef TRUNNEL_CHECK_ENCODED_LEN
{
trunnel_assert(encoded_len >= 0);
trunnel_assert((size_t)encoded_len == written);
}
#endif
return written;
truncated:
result = -2;
goto fail;
check_failed:
(void)msg;
result = -1;
goto fail;
fail:
trunnel_assert(result < 0);
return result;
}
/** As trn_cell_extension_dos_param_parse(), but do not allocate the
* output object.
*/
static ssize_t
trn_cell_extension_dos_param_parse_into(trn_cell_extension_dos_param_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
ssize_t result = 0;
(void)result;
/* Parse u8 type */
CHECK_REMAINING(1, truncated);
obj->type = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse u64 value */
CHECK_REMAINING(8, truncated);
obj->value = trunnel_ntohll(trunnel_get_uint64(ptr));
remaining -= 8; ptr += 8;
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
truncated:
return -2;
}
ssize_t
trn_cell_extension_dos_param_parse(trn_cell_extension_dos_param_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = trn_cell_extension_dos_param_new();
if (NULL == *output)
return -1;
result = trn_cell_extension_dos_param_parse_into(*output, input, len_in);
if (result < 0) {
trn_cell_extension_dos_param_free(*output);
*output = NULL;
}
return result;
}
trn_cell_establish_intro_t *
trn_cell_establish_intro_new(void)
{
@@ -561,6 +740,296 @@ trn_cell_establish_intro_parse(trn_cell_establish_intro_t **output, const uint8_
}
return result;
}
trn_cell_extension_dos_t *
trn_cell_extension_dos_new(void)
{
trn_cell_extension_dos_t *val = trunnel_calloc(1, sizeof(trn_cell_extension_dos_t));
if (NULL == val)
return NULL;
return val;
}
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
trn_cell_extension_dos_clear(trn_cell_extension_dos_t *obj)
{
(void) obj;
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->params); ++idx) {
trn_cell_extension_dos_param_free(TRUNNEL_DYNARRAY_GET(&obj->params, idx));
}
}
TRUNNEL_DYNARRAY_WIPE(&obj->params);
TRUNNEL_DYNARRAY_CLEAR(&obj->params);
}
void
trn_cell_extension_dos_free(trn_cell_extension_dos_t *obj)
{
if (obj == NULL)
return;
trn_cell_extension_dos_clear(obj);
trunnel_memwipe(obj, sizeof(trn_cell_extension_dos_t));
trunnel_free_(obj);
}
uint8_t
trn_cell_extension_dos_get_n_params(const trn_cell_extension_dos_t *inp)
{
return inp->n_params;
}
int
trn_cell_extension_dos_set_n_params(trn_cell_extension_dos_t *inp, uint8_t val)
{
inp->n_params = val;
return 0;
}
size_t
trn_cell_extension_dos_getlen_params(const trn_cell_extension_dos_t *inp)
{
return TRUNNEL_DYNARRAY_LEN(&inp->params);
}
struct trn_cell_extension_dos_param_st *
trn_cell_extension_dos_get_params(trn_cell_extension_dos_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->params, idx);
}
const struct trn_cell_extension_dos_param_st *
trn_cell_extension_dos_getconst_params(const trn_cell_extension_dos_t *inp, size_t idx)
{
return trn_cell_extension_dos_get_params((trn_cell_extension_dos_t*)inp, idx);
}
int
trn_cell_extension_dos_set_params(trn_cell_extension_dos_t *inp, size_t idx, struct trn_cell_extension_dos_param_st * elt)
{
trn_cell_extension_dos_param_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->params, idx);
if (oldval && oldval != elt)
trn_cell_extension_dos_param_free(oldval);
return trn_cell_extension_dos_set0_params(inp, idx, elt);
}
int
trn_cell_extension_dos_set0_params(trn_cell_extension_dos_t *inp, size_t idx, struct trn_cell_extension_dos_param_st * elt)
{
TRUNNEL_DYNARRAY_SET(&inp->params, idx, elt);
return 0;
}
int
trn_cell_extension_dos_add_params(trn_cell_extension_dos_t *inp, struct trn_cell_extension_dos_param_st * elt)
{
#if SIZE_MAX >= UINT8_MAX
if (inp->params.n_ == UINT8_MAX)
goto trunnel_alloc_failed;
#endif
TRUNNEL_DYNARRAY_ADD(struct trn_cell_extension_dos_param_st *, &inp->params, elt, {});
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
struct trn_cell_extension_dos_param_st * *
trn_cell_extension_dos_getarray_params(trn_cell_extension_dos_t *inp)
{
return inp->params.elts_;
}
const struct trn_cell_extension_dos_param_st * const *
trn_cell_extension_dos_getconstarray_params(const trn_cell_extension_dos_t *inp)
{
return (const struct trn_cell_extension_dos_param_st * const *)trn_cell_extension_dos_getarray_params((trn_cell_extension_dos_t*)inp);
}
int
trn_cell_extension_dos_setlen_params(trn_cell_extension_dos_t *inp, size_t newlen)
{
struct trn_cell_extension_dos_param_st * *newptr;
#if UINT8_MAX < SIZE_MAX
if (newlen > UINT8_MAX)
goto trunnel_alloc_failed;
#endif
newptr = trunnel_dynarray_setlen(&inp->params.allocated_,
&inp->params.n_, inp->params.elts_, newlen,
sizeof(inp->params.elts_[0]), (trunnel_free_fn_t) trn_cell_extension_dos_param_free,
&inp->trunnel_error_code_);
if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->params.elts_ = newptr;
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
const char *
trn_cell_extension_dos_check(const trn_cell_extension_dos_t *obj)
{
if (obj == NULL)
return "Object was NULL";
if (obj->trunnel_error_code_)
return "A set function failed on this object";
{
const char *msg;
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->params); ++idx) {
if (NULL != (msg = trn_cell_extension_dos_param_check(TRUNNEL_DYNARRAY_GET(&obj->params, idx))))
return msg;
}
}
if (TRUNNEL_DYNARRAY_LEN(&obj->params) != obj->n_params)
return "Length mismatch for params";
return NULL;
}
ssize_t
trn_cell_extension_dos_encoded_len(const trn_cell_extension_dos_t *obj)
{
ssize_t result = 0;
if (NULL != trn_cell_extension_dos_check(obj))
return -1;
/* Length of u8 n_params */
result += 1;
/* Length of struct trn_cell_extension_dos_param params[n_params] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->params); ++idx) {
result += trn_cell_extension_dos_param_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->params, idx));
}
}
return result;
}
int
trn_cell_extension_dos_clear_errors(trn_cell_extension_dos_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
trn_cell_extension_dos_encode(uint8_t *output, const size_t avail, const trn_cell_extension_dos_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = trn_cell_extension_dos_encoded_len(obj);
#endif
if (NULL != (msg = trn_cell_extension_dos_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
trunnel_assert(encoded_len >= 0);
#endif
/* Encode u8 n_params */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->n_params));
written += 1; ptr += 1;
/* Encode struct trn_cell_extension_dos_param params[n_params] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->params); ++idx) {
trunnel_assert(written <= avail);
result = trn_cell_extension_dos_param_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->params, idx));
if (result < 0)
goto fail; /* XXXXXXX !*/
written += result; ptr += result;
}
}
trunnel_assert(ptr == output + written);
#ifdef TRUNNEL_CHECK_ENCODED_LEN
{
trunnel_assert(encoded_len >= 0);
trunnel_assert((size_t)encoded_len == written);
}
#endif
return written;
truncated:
result = -2;
goto fail;
check_failed:
(void)msg;
result = -1;
goto fail;
fail:
trunnel_assert(result < 0);
return result;
}
/** As trn_cell_extension_dos_parse(), but do not allocate the output
* object.
*/
static ssize_t
trn_cell_extension_dos_parse_into(trn_cell_extension_dos_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
ssize_t result = 0;
(void)result;
/* Parse u8 n_params */
CHECK_REMAINING(1, truncated);
obj->n_params = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse struct trn_cell_extension_dos_param params[n_params] */
TRUNNEL_DYNARRAY_EXPAND(trn_cell_extension_dos_param_t *, &obj->params, obj->n_params, {});
{
trn_cell_extension_dos_param_t * elt;
unsigned idx;
for (idx = 0; idx < obj->n_params; ++idx) {
result = trn_cell_extension_dos_param_parse(&elt, ptr, remaining);
if (result < 0)
goto relay_fail;
trunnel_assert((size_t)result <= remaining);
remaining -= result; ptr += result;
TRUNNEL_DYNARRAY_ADD(trn_cell_extension_dos_param_t *, &obj->params, elt, {trn_cell_extension_dos_param_free(elt);});
}
}
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
truncated:
return -2;
relay_fail:
trunnel_assert(result < 0);
return result;
trunnel_alloc_failed:
return -1;
}
ssize_t
trn_cell_extension_dos_parse(trn_cell_extension_dos_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = trn_cell_extension_dos_new();
if (NULL == *output)
return -1;
result = trn_cell_extension_dos_parse_into(*output, input, len_in);
if (result < 0) {
trn_cell_extension_dos_free(*output);
*output = NULL;
}
return result;
}
trn_cell_intro_established_t *
trn_cell_intro_established_new(void)
{
+159
View File
@@ -10,6 +10,17 @@
struct trn_cell_extension_st;
#define TRUNNEL_SHA3_256_LEN 32
#define TRUNNEL_CELL_EXTENSION_TYPE_DOS 1
#define TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC 1
#define TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC 2
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_DOS_PARAM)
struct trn_cell_extension_dos_param_st {
uint8_t type;
uint64_t value;
uint8_t trunnel_error_code_;
};
#endif
typedef struct trn_cell_extension_dos_param_st trn_cell_extension_dos_param_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_ESTABLISH_INTRO)
struct trn_cell_establish_intro_st {
const uint8_t *start_cell;
@@ -26,6 +37,14 @@ struct trn_cell_establish_intro_st {
};
#endif
typedef struct trn_cell_establish_intro_st trn_cell_establish_intro_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_DOS)
struct trn_cell_extension_dos_st {
uint8_t n_params;
TRUNNEL_DYNARRAY_HEAD(, struct trn_cell_extension_dos_param_st *) params;
uint8_t trunnel_error_code_;
};
#endif
typedef struct trn_cell_extension_dos_st trn_cell_extension_dos_t;
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_INTRO_ESTABLISHED)
struct trn_cell_intro_established_st {
struct trn_cell_extension_st *extensions;
@@ -33,6 +52,62 @@ struct trn_cell_intro_established_st {
};
#endif
typedef struct trn_cell_intro_established_st trn_cell_intro_established_t;
/** Return a newly allocated trn_cell_extension_dos_param with all
* elements set to zero.
*/
trn_cell_extension_dos_param_t *trn_cell_extension_dos_param_new(void);
/** Release all storage held by the trn_cell_extension_dos_param in
* 'victim'. (Do nothing if 'victim' is NULL.)
*/
void trn_cell_extension_dos_param_free(trn_cell_extension_dos_param_t *victim);
/** Try to parse a trn_cell_extension_dos_param from the buffer in
* 'input', using up to 'len_in' bytes from the input buffer. On
* success, return the number of bytes consumed and set *output to the
* newly allocated trn_cell_extension_dos_param_t. On failure, return
* -2 if the input appears truncated, and -1 if the input is otherwise
* invalid.
*/
ssize_t trn_cell_extension_dos_param_parse(trn_cell_extension_dos_param_t **output, const uint8_t *input, const size_t len_in);
/** Return the number of bytes we expect to need to encode the
* trn_cell_extension_dos_param in 'obj'. On failure, return a
* negative value. Note that this value may be an overestimate, and
* can even be an underestimate for certain unencodeable objects.
*/
ssize_t trn_cell_extension_dos_param_encoded_len(const trn_cell_extension_dos_param_t *obj);
/** Try to encode the trn_cell_extension_dos_param from 'input' into
* the buffer at 'output', using up to 'avail' bytes of the output
* buffer. On success, return the number of bytes used. On failure,
* return -2 if the buffer was not long enough, and -1 if the input
* was invalid.
*/
ssize_t trn_cell_extension_dos_param_encode(uint8_t *output, size_t avail, const trn_cell_extension_dos_param_t *input);
/** Check whether the internal state of the
* trn_cell_extension_dos_param in 'obj' is consistent. Return NULL if
* it is, and a short message if it is not.
*/
const char *trn_cell_extension_dos_param_check(const trn_cell_extension_dos_param_t *obj);
/** Clear any errors that were set on the object 'obj' by its setter
* functions. Return true iff errors were cleared.
*/
int trn_cell_extension_dos_param_clear_errors(trn_cell_extension_dos_param_t *obj);
/** Return the value of the type field of the
* trn_cell_extension_dos_param_t in 'inp'
*/
uint8_t trn_cell_extension_dos_param_get_type(const trn_cell_extension_dos_param_t *inp);
/** Set the value of the type field of the
* trn_cell_extension_dos_param_t in 'inp' to 'val'. Return 0 on
* success; return -1 and set the error code on 'inp' on failure.
*/
int trn_cell_extension_dos_param_set_type(trn_cell_extension_dos_param_t *inp, uint8_t val);
/** Return the value of the value field of the
* trn_cell_extension_dos_param_t in 'inp'
*/
uint64_t trn_cell_extension_dos_param_get_value(const trn_cell_extension_dos_param_t *inp);
/** Set the value of the value field of the
* trn_cell_extension_dos_param_t in 'inp' to 'val'. Return 0 on
* success; return -1 and set the error code on 'inp' on failure.
*/
int trn_cell_extension_dos_param_set_value(trn_cell_extension_dos_param_t *inp, uint64_t val);
/** Return a newly allocated trn_cell_establish_intro with all
* elements set to zero.
*/
@@ -216,6 +291,90 @@ const uint8_t * trn_cell_establish_intro_getconstarray_sig(const trn_cell_estab
* -1 and set the error code on 'inp' on failure.
*/
int trn_cell_establish_intro_setlen_sig(trn_cell_establish_intro_t *inp, size_t newlen);
/** Return a newly allocated trn_cell_extension_dos with all elements
* set to zero.
*/
trn_cell_extension_dos_t *trn_cell_extension_dos_new(void);
/** Release all storage held by the trn_cell_extension_dos in
* 'victim'. (Do nothing if 'victim' is NULL.)
*/
void trn_cell_extension_dos_free(trn_cell_extension_dos_t *victim);
/** Try to parse a trn_cell_extension_dos from the buffer in 'input',
* using up to 'len_in' bytes from the input buffer. On success,
* return the number of bytes consumed and set *output to the newly
* allocated trn_cell_extension_dos_t. On failure, return -2 if the
* input appears truncated, and -1 if the input is otherwise invalid.
*/
ssize_t trn_cell_extension_dos_parse(trn_cell_extension_dos_t **output, const uint8_t *input, const size_t len_in);
/** Return the number of bytes we expect to need to encode the
* trn_cell_extension_dos in 'obj'. On failure, return a negative
* value. Note that this value may be an overestimate, and can even be
* an underestimate for certain unencodeable objects.
*/
ssize_t trn_cell_extension_dos_encoded_len(const trn_cell_extension_dos_t *obj);
/** Try to encode the trn_cell_extension_dos from 'input' into the
* buffer at 'output', using up to 'avail' bytes of the output buffer.
* On success, return the number of bytes used. On failure, return -2
* if the buffer was not long enough, and -1 if the input was invalid.
*/
ssize_t trn_cell_extension_dos_encode(uint8_t *output, size_t avail, const trn_cell_extension_dos_t *input);
/** Check whether the internal state of the trn_cell_extension_dos in
* 'obj' is consistent. Return NULL if it is, and a short message if
* it is not.
*/
const char *trn_cell_extension_dos_check(const trn_cell_extension_dos_t *obj);
/** Clear any errors that were set on the object 'obj' by its setter
* functions. Return true iff errors were cleared.
*/
int trn_cell_extension_dos_clear_errors(trn_cell_extension_dos_t *obj);
/** Return the value of the n_params field of the
* trn_cell_extension_dos_t in 'inp'
*/
uint8_t trn_cell_extension_dos_get_n_params(const trn_cell_extension_dos_t *inp);
/** Set the value of the n_params field of the
* trn_cell_extension_dos_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
*/
int trn_cell_extension_dos_set_n_params(trn_cell_extension_dos_t *inp, uint8_t val);
/** Return the length of the dynamic array holding the params field of
* the trn_cell_extension_dos_t in 'inp'.
*/
size_t trn_cell_extension_dos_getlen_params(const trn_cell_extension_dos_t *inp);
/** Return the element at position 'idx' of the dynamic array field
* params of the trn_cell_extension_dos_t in 'inp'.
*/
struct trn_cell_extension_dos_param_st * trn_cell_extension_dos_get_params(trn_cell_extension_dos_t *inp, size_t idx);
/** As trn_cell_extension_dos_get_params, but take and return a const
* pointer
*/
const struct trn_cell_extension_dos_param_st * trn_cell_extension_dos_getconst_params(const trn_cell_extension_dos_t *inp, size_t idx);
/** Change the element at position 'idx' of the dynamic array field
* params of the trn_cell_extension_dos_t in 'inp', so that it will
* hold the value 'elt'. Free the previous value, if any.
*/
int trn_cell_extension_dos_set_params(trn_cell_extension_dos_t *inp, size_t idx, struct trn_cell_extension_dos_param_st * elt);
/** As trn_cell_extension_dos_set_params, but does not free the
* previous value.
*/
int trn_cell_extension_dos_set0_params(trn_cell_extension_dos_t *inp, size_t idx, struct trn_cell_extension_dos_param_st * elt);
/** Append a new element 'elt' to the dynamic array field params of
* the trn_cell_extension_dos_t in 'inp'.
*/
int trn_cell_extension_dos_add_params(trn_cell_extension_dos_t *inp, struct trn_cell_extension_dos_param_st * elt);
/** Return a pointer to the variable-length array field params of
* 'inp'.
*/
struct trn_cell_extension_dos_param_st * * trn_cell_extension_dos_getarray_params(trn_cell_extension_dos_t *inp);
/** As trn_cell_extension_dos_get_params, but take and return a const
* pointer
*/
const struct trn_cell_extension_dos_param_st * const * trn_cell_extension_dos_getconstarray_params(const trn_cell_extension_dos_t *inp);
/** Change the length of the variable-length array field params of
* 'inp' to 'newlen'.Fill extra elements with NULL; free removed
* elements. Return 0 on success; return -1 and set the error code on
* 'inp' on failure.
*/
int trn_cell_extension_dos_setlen_params(trn_cell_extension_dos_t *inp, size_t newlen);
/** Return a newly allocated trn_cell_intro_established with all
* elements set to zero.
*/
@@ -39,3 +39,26 @@ struct trn_cell_intro_established {
/* Extension(s). Reserved fields. */
struct trn_cell_extension extensions;
};
/*
* ESTABLISH_INTRO cell extensions.
*/
const TRUNNEL_CELL_EXTENSION_TYPE_DOS = 0x01;
/* DoS Parameter types. */
const TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC = 0x01;
const TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC = 0x02;
/*
* DoS Parameters Extension. See proposal 305 for more details.
*/
struct trn_cell_extension_dos_param {
u8 type;
u64 value;
};
struct trn_cell_extension_dos {
u8 n_params;
struct trn_cell_extension_dos_param params[n_params];
};