diff options
Diffstat (limited to 'src')
116 files changed, 4686 insertions, 1565 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index a061871748..7908007051 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -317,7 +317,7 @@ static config_var_t option_vars_[] = { OBSOLETE("AuthDirRejectUnlisted"), OBSOLETE("AuthDirListBadDirs"), V(AuthDirListBadExits, BOOL, "0"), - V(AuthDirMaxServersPerAddr, UINT, "2"), + V(AuthDirMaxServersPerAddr, POSINT, "2"), OBSOLETE("AuthDirMaxServersPerAuthAddr"), V(AuthDirHasIPv6Connectivity, BOOL, "0"), VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), @@ -352,7 +352,7 @@ static config_var_t option_vars_[] = { V(ClientUseIPv6, BOOL, "0"), V(ClientUseIPv4, BOOL, "1"), V(ConsensusParams, STRING, NULL), - V(ConnLimit, UINT, "1000"), + V(ConnLimit, POSINT, "1000"), V(ConnDirectionStatistics, BOOL, "0"), V(ConstrainedSockets, BOOL, "0"), V(ConstrainedSockSize, MEMUNIT, "8192"), @@ -402,14 +402,14 @@ static config_var_t option_vars_[] = { V(DormantCanceledByStartup, BOOL, "0"), /* DoS circuit creation options. */ V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"), - V(DoSCircuitCreationMinConnections, UINT, "0"), - V(DoSCircuitCreationRate, UINT, "0"), - V(DoSCircuitCreationBurst, UINT, "0"), + V(DoSCircuitCreationMinConnections, POSINT, "0"), + V(DoSCircuitCreationRate, POSINT, "0"), + V(DoSCircuitCreationBurst, POSINT, "0"), V(DoSCircuitCreationDefenseType, INT, "0"), V(DoSCircuitCreationDefenseTimePeriod, INTERVAL, "0"), /* DoS connection options. */ V(DoSConnectionEnabled, AUTOBOOL, "auto"), - V(DoSConnectionMaxConcurrentCount, UINT, "0"), + V(DoSConnectionMaxConcurrentCount, POSINT, "0"), V(DoSConnectionDefenseType, INT, "0"), /* DoS single hop client options. */ V(DoSRefuseSingleHopClientRendezvous, AUTOBOOL, "auto"), @@ -522,7 +522,7 @@ static config_var_t option_vars_[] = { VAR("MapAddress", LINELIST, AddressMap, NULL), V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"), V(MaxCircuitDirtiness, INTERVAL, "10 minutes"), - V(MaxClientCircuitsPending, UINT, "32"), + V(MaxClientCircuitsPending, POSINT, "32"), V(MaxConsensusAgeForDiffs, INTERVAL, "0 seconds"), VAR("MaxMemInQueues", MEMUNIT, MaxMemInQueues_raw, "0"), OBSOLETE("MaxOnionsPending"), @@ -539,10 +539,10 @@ static config_var_t option_vars_[] = { OBSOLETE("WarnUnsafeSocks"), VAR("NodeFamily", LINELIST, NodeFamilies, NULL), V(NoExec, BOOL, "0"), - V(NumCPUs, UINT, "0"), - V(NumDirectoryGuards, UINT, "0"), - V(NumEntryGuards, UINT, "0"), - V(NumPrimaryGuards, UINT, "0"), + V(NumCPUs, POSINT, "0"), + V(NumDirectoryGuards, POSINT, "0"), + V(NumEntryGuards, POSINT, "0"), + V(NumPrimaryGuards, POSINT, "0"), V(OfflineMasterKey, BOOL, "0"), OBSOLETE("ORListenAddress"), VPORT(ORPort), @@ -593,7 +593,7 @@ static config_var_t option_vars_[] = { V(RecommendedVersions, LINELIST, NULL), V(RecommendedClientVersions, LINELIST, NULL), V(RecommendedServerVersions, LINELIST, NULL), - V(RecommendedPackages, LINELIST, NULL), + OBSOLETE("RecommendedPackages"), V(ReducedConnectionPadding, BOOL, "0"), V(ConnectionPadding, AUTOBOOL, "auto"), V(RefuseUnknownExits, AUTOBOOL, "auto"), @@ -666,7 +666,7 @@ static config_var_t option_vars_[] = { V(V3AuthVotingInterval, INTERVAL, "1 hour"), V(V3AuthVoteDelay, INTERVAL, "5 minutes"), V(V3AuthDistDelay, INTERVAL, "5 minutes"), - V(V3AuthNIntervalsValid, UINT, "3"), + V(V3AuthNIntervalsValid, POSINT, "3"), V(V3AuthUseLegacyKey, BOOL, "0"), V(V3BandwidthsFile, FILENAME, NULL), V(GuardfractionFile, FILENAME, NULL), @@ -715,7 +715,7 @@ static config_var_t option_vars_[] = { * blocked), but we also don't want to fail if only some mirrors are * blackholed. Clients will try 3 directories simultaneously. * (Relays never use simultaneous connections.) */ - V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), + V(ClientBootstrapConsensusMaxInProgressTries, POSINT, "3"), /* When a client has any running bridges, check each bridge occasionally, * whether or not that bridge is actually up. */ V(TestingBridgeDownloadInitialDelay, CSV_INTERVAL,"10800"), @@ -749,7 +749,7 @@ static const config_var_t testing_tor_network_defaults[] = { V(DirAllowPrivateAddresses, BOOL, "1"), V(EnforceDistinctSubnets, BOOL, "0"), V(AssumeReachable, BOOL, "1"), - V(AuthDirMaxServersPerAddr, UINT, "0"), + V(AuthDirMaxServersPerAddr, POSINT, "0"), V(ClientBootstrapConsensusAuthorityDownloadInitialDelay, CSV_INTERVAL, "0"), V(ClientBootstrapConsensusFallbackDownloadInitialDelay, CSV_INTERVAL, "0"), V(ClientBootstrapConsensusAuthorityOnlyDownloadInitialDelay, CSV_INTERVAL, @@ -3522,13 +3522,6 @@ options_validate(or_options_t *old_options, or_options_t *options, "features to be broken in unpredictable ways."); } - for (cl = options->RecommendedPackages; cl; cl = cl->next) { - if (! validate_recommended_package_line(cl->value)) { - log_warn(LD_CONFIG, "Invalid RecommendedPackage line %s will be ignored", - escaped(cl->value)); - } - } - if (options->AuthoritativeDir) { if (!options->ContactInfo && !options->TestingTorNetwork) REJECT("Authoritative directory servers must set ContactInfo"); @@ -8184,10 +8177,9 @@ getinfo_helper_config(control_connection_t *conn, switch (var->type) { case CONFIG_TYPE_STRING: type = "String"; break; case CONFIG_TYPE_FILENAME: type = "Filename"; break; - case CONFIG_TYPE_UINT: type = "Integer"; break; + case CONFIG_TYPE_POSINT: type = "Integer"; break; case CONFIG_TYPE_UINT64: type = "Integer"; break; case CONFIG_TYPE_INT: type = "SignedInteger"; break; - case CONFIG_TYPE_PORT: type = "Port"; break; case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break; case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break; case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break; diff --git a/src/app/config/confparse.c b/src/app/config/confparse.c index 729e7a4478..bc2ab24e4f 100644 --- a/src/app/config/confparse.c +++ b/src/app/config/confparse.c @@ -21,16 +21,17 @@ * specified, and a linked list of key-value pairs. */ +#define CONFPARSE_PRIVATE #include "core/or/or.h" #include "app/config/confparse.h" #include "feature/nodelist/routerset.h" +#include "lib/confmgt/unitparse.h" #include "lib/container/bitarray.h" #include "lib/encoding/confline.h" -static uint64_t config_parse_memunit(const char *s, int *ok); -static int config_parse_msec_interval(const char *s, int *ok); -static int config_parse_interval(const char *s, int *ok); +#include "lib/confmgt/typedvar.h" + static void config_reset(const config_format_t *fmt, void *options, const config_var_t *var, int use_defaults); @@ -87,7 +88,7 @@ const char * config_find_deprecation(const config_format_t *fmt, const char *key) { if (BUG(fmt == NULL) || BUG(key == NULL)) - return NULL; + return NULL; // LCOV_EXCL_LINE if (fmt->deprecations == NULL) return NULL; @@ -161,7 +162,6 @@ static int config_assign_value(const config_format_t *fmt, void *options, config_line_t *c, char **msg) { - int i, ok; const config_var_t *var; void *lvalue; @@ -169,150 +169,14 @@ config_assign_value(const config_format_t *fmt, void *options, var = config_find_option(fmt, c->key); tor_assert(var); + tor_assert(!strcmp(c->key, var->name)); lvalue = STRUCT_VAR_P(options, var->var_offset); - switch (var->type) { - - case CONFIG_TYPE_PORT: - if (!strcasecmp(c->value, "auto")) { - *(int *)lvalue = CFG_AUTO_PORT; - break; - } - /* fall through */ - case CONFIG_TYPE_INT: - case CONFIG_TYPE_UINT: - i = (int)tor_parse_long(c->value, 10, - var->type==CONFIG_TYPE_INT ? INT_MIN : 0, - var->type==CONFIG_TYPE_PORT ? 65535 : INT_MAX, - &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "Int keyword '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - - case CONFIG_TYPE_UINT64: { - uint64_t u64 = tor_parse_uint64(c->value, 10, - 0, UINT64_MAX, &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "uint64 keyword '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(uint64_t *)lvalue = u64; - break; - } - - case CONFIG_TYPE_CSV_INTERVAL: { - /* We used to have entire smartlists here. But now that all of our - * download schedules use exponential backoff, only the first part - * matters. */ - const char *comma = strchr(c->value, ','); - const char *val = c->value; - char *tmp = NULL; - if (comma) { - tmp = tor_strndup(c->value, comma - c->value); - val = tmp; - } + if (var->type == CONFIG_TYPE_ROUTERSET) { + // XXXX make the backend extensible so that we don't have to + // XXXX handle ROUTERSET specially. - i = config_parse_interval(val, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - tor_free(tmp); - return -1; - } - *(int *)lvalue = i; - tor_free(tmp); - break; - } - - case CONFIG_TYPE_INTERVAL: { - i = config_parse_interval(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - } - - case CONFIG_TYPE_MSEC_INTERVAL: { - i = config_parse_msec_interval(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Msec interval '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - } - - case CONFIG_TYPE_MEMUNIT: { - uint64_t u64 = config_parse_memunit(c->value, &ok); - if (!ok) { - tor_asprintf(msg, - "Value '%s %s' is malformed or out of bounds.", - c->key, c->value); - return -1; - } - *(uint64_t *)lvalue = u64; - break; - } - - case CONFIG_TYPE_BOOL: - i = (int)tor_parse_long(c->value, 10, 0, 1, &ok, NULL); - if (!ok) { - tor_asprintf(msg, - "Boolean '%s %s' expects 0 or 1.", - c->key, c->value); - return -1; - } - *(int *)lvalue = i; - break; - - case CONFIG_TYPE_AUTOBOOL: - if (!strcasecmp(c->value, "auto")) - *(int *)lvalue = -1; - else if (!strcmp(c->value, "0")) - *(int *)lvalue = 0; - else if (!strcmp(c->value, "1")) - *(int *)lvalue = 1; - else { - tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.", - c->key, c->value); - return -1; - } - break; - - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - tor_free(*(char **)lvalue); - *(char **)lvalue = tor_strdup(c->value); - break; - - case CONFIG_TYPE_DOUBLE: - *(double *)lvalue = atof(c->value); - break; - - case CONFIG_TYPE_ISOTIME: - if (parse_iso_time(c->value, (time_t *)lvalue)) { - tor_asprintf(msg, - "Invalid time '%s' for keyword '%s'", c->value, c->key); - return -1; - } - break; - - case CONFIG_TYPE_ROUTERSET: if (*(routerset_t**)lvalue) { routerset_free(*(routerset_t**)lvalue); } @@ -322,48 +186,10 @@ config_assign_value(const config_format_t *fmt, void *options, c->value, c->key); return -1; } - break; - - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp)); - smartlist_clear(*(smartlist_t**)lvalue); - } else { - *(smartlist_t**)lvalue = smartlist_new(); - } - - smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - break; - - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - { - config_line_t *lastval = *(config_line_t**)lvalue; - if (lastval && lastval->fragile) { - if (c->command != CONFIG_LINE_APPEND) { - config_free_lines(lastval); - *(config_line_t**)lvalue = NULL; - } else { - lastval->fragile = 0; - } - } - - config_line_append((config_line_t**)lvalue, c->key, c->value); - } - break; - case CONFIG_TYPE_OBSOLETE: - log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); - break; - case CONFIG_TYPE_LINELIST_V: - tor_asprintf(msg, - "You may not provide a value for virtual option '%s'", c->key); - return -1; - default: - tor_assert(0); - break; + return 0; } - return 0; + + return typed_var_kvassign(lvalue, c, msg, var->type); } /** Mark every linelist in <b>options</b> "fragile", so that fresh assignments @@ -462,7 +288,9 @@ config_assign_line(const config_format_t *fmt, void *options, } return 0; } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { - config_reset(fmt, options, var, use_defaults); + // XXXX This is unreachable, since a CLEAR line always has an + // XXXX empty value. + config_reset(fmt, options, var, use_defaults); // LCOV_EXCL_LINE } if (options_seen && (var->type != CONFIG_TYPE_LINELIST && @@ -484,7 +312,7 @@ config_assign_line(const config_format_t *fmt, void *options, /** Restore the option named <b>key</b> in options to its default value. * Called from config_assign(). */ -static void +STATIC void config_reset_line(const config_format_t *fmt, void *options, const char *key, int use_defaults) { @@ -547,105 +375,15 @@ config_get_assigned_option(const config_format_t *fmt, const void *options, } value = STRUCT_VAR_P(options, var->var_offset); - result = tor_malloc_zero(sizeof(config_line_t)); - result->key = tor_strdup(var->name); - switch (var->type) - { - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - if (*(char**)value) { - result->value = tor_strdup(*(char**)value); - } else { - tor_free(result->key); - tor_free(result); - return NULL; - } - break; - case CONFIG_TYPE_ISOTIME: - if (*(time_t*)value) { - result->value = tor_malloc(ISO_TIME_LEN+1); - format_iso_time(result->value, *(time_t*)value); - } else { - tor_free(result->key); - tor_free(result); - } - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_PORT: - if (*(int*)value == CFG_AUTO_PORT) { - result->value = tor_strdup("auto"); - escape_val = 0; - break; - } - /* fall through */ - case CONFIG_TYPE_CSV_INTERVAL: - case CONFIG_TYPE_INTERVAL: - case CONFIG_TYPE_MSEC_INTERVAL: - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_INT: - /* This means every or_options_t uint or bool element - * needs to be an int. Not, say, a uint16_t or char. */ - tor_asprintf(&result->value, "%d", *(int*)value); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_UINT64: /* Fall through */ - case CONFIG_TYPE_MEMUNIT: - tor_asprintf(&result->value, "%"PRIu64, - (*(uint64_t*)value)); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_DOUBLE: - tor_asprintf(&result->value, "%f", *(double*)value); - escape_val = 0; /* Can't need escape. */ - break; - - case CONFIG_TYPE_AUTOBOOL: - if (*(int*)value == -1) { - result->value = tor_strdup("auto"); - escape_val = 0; - break; - } - /* fall through */ - case CONFIG_TYPE_BOOL: - result->value = tor_strdup(*(int*)value ? "1" : "0"); - escape_val = 0; /* Can't need escape. */ - break; - case CONFIG_TYPE_ROUTERSET: - result->value = routerset_to_string(*(routerset_t**)value); - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)value) - result->value = - smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL); - else - result->value = tor_strdup(""); - break; - case CONFIG_TYPE_OBSOLETE: - log_fn(LOG_INFO, LD_CONFIG, - "You asked me for the value of an obsolete config option '%s'.", - key); - tor_free(result->key); - tor_free(result); - return NULL; - case CONFIG_TYPE_LINELIST_S: - tor_free(result->key); - tor_free(result); - result = config_lines_dup_and_filter(*(const config_line_t **)value, - key); - break; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_V: - tor_free(result->key); - tor_free(result); - result = config_lines_dup(*(const config_line_t**)value); - break; - default: - tor_free(result->key); - tor_free(result); - log_warn(LD_BUG,"Unknown type %d for known key '%s'", - var->type, key); - return NULL; - } + if (var->type == CONFIG_TYPE_ROUTERSET) { + // XXXX make the backend extensible so that we don't have to + // XXXX handle ROUTERSET specially. + result = tor_malloc_zero(sizeof(config_line_t)); + result->key = tor_strdup(var->name); + result->value = routerset_to_string(*(routerset_t**)value); + } else { + result = typed_var_kvencode(var->name, value, var->type); + } if (escape_val) { config_line_t *line; @@ -773,57 +511,17 @@ config_clear(const config_format_t *fmt, void *options, { void *lvalue = STRUCT_VAR_P(options, var->var_offset); (void)fmt; /* unused */ - switch (var->type) { - case CONFIG_TYPE_STRING: - case CONFIG_TYPE_FILENAME: - tor_free(*(char**)lvalue); - break; - case CONFIG_TYPE_DOUBLE: - *(double*)lvalue = 0.0; - break; - case CONFIG_TYPE_ISOTIME: - *(time_t*)lvalue = 0; - break; - case CONFIG_TYPE_CSV_INTERVAL: - case CONFIG_TYPE_INTERVAL: - case CONFIG_TYPE_MSEC_INTERVAL: - case CONFIG_TYPE_UINT: - case CONFIG_TYPE_INT: - case CONFIG_TYPE_PORT: - case CONFIG_TYPE_BOOL: - *(int*)lvalue = 0; - break; - case CONFIG_TYPE_AUTOBOOL: - *(int*)lvalue = -1; - break; - case CONFIG_TYPE_UINT64: - case CONFIG_TYPE_MEMUNIT: - *(uint64_t*)lvalue = 0; - break; - case CONFIG_TYPE_ROUTERSET: - if (*(routerset_t**)lvalue) { - routerset_free(*(routerset_t**)lvalue); - *(routerset_t**)lvalue = NULL; - } - break; - case CONFIG_TYPE_CSV: - if (*(smartlist_t**)lvalue) { - SMARTLIST_FOREACH(*(smartlist_t **)lvalue, char *, cp, tor_free(cp)); - smartlist_free(*(smartlist_t **)lvalue); - *(smartlist_t **)lvalue = NULL; - } - break; - case CONFIG_TYPE_LINELIST: - case CONFIG_TYPE_LINELIST_S: - config_free_lines(*(config_line_t **)lvalue); - *(config_line_t **)lvalue = NULL; - break; - case CONFIG_TYPE_LINELIST_V: - /* handled by linelist_s. */ - break; - case CONFIG_TYPE_OBSOLETE: - break; + if (var->type == CONFIG_TYPE_ROUTERSET) { + // XXXX make the backend extensible so that we don't have to + // XXXX handle ROUTERSET specially. + if (*(routerset_t**)lvalue) { + routerset_free(*(routerset_t**)lvalue); + *(routerset_t**)lvalue = NULL; + } + return; } + + typed_var_free(lvalue, var->type); } /** Clear the option indexed by <b>var</b> in <b>options</b>. Then if @@ -844,8 +542,10 @@ config_reset(const config_format_t *fmt, void *options, c->key = tor_strdup(var->name); c->value = tor_strdup(var->initvalue); if (config_assign_value(fmt, options, c, &msg) < 0) { + // LCOV_EXCL_START log_warn(LD_BUG, "Failed to assign default: %s", msg); tor_free(msg); /* if this happens it's a bug */ + // LCOV_EXCL_STOP } config_free_lines(c); } @@ -911,10 +611,12 @@ config_dup(const config_format_t *fmt, const void *old) if (line) { char *msg = NULL; if (config_assign(fmt, newopts, line, 0, &msg) < 0) { + // LCOV_EXCL_START log_err(LD_BUG, "config_get_assigned_option() generated " "something we couldn't config_assign(): %s", msg); tor_free(msg); tor_assert(0); + // LCOV_EXCL_STOP } } config_free_lines(line); @@ -963,9 +665,11 @@ config_dump(const config_format_t *fmt, const void *default_options, /* XXX use a 1 here so we don't add a new log line while dumping */ if (default_options == NULL) { if (fmt->validate_fn(NULL, defaults_tmp, defaults_tmp, 1, &msg) < 0) { + // LCOV_EXCL_START log_err(LD_BUG, "Failed to validate default config: %s", msg); tor_free(msg); tor_assert(0); + // LCOV_EXCL_STOP } } @@ -1015,193 +719,3 @@ config_dump(const config_format_t *fmt, const void *default_options, } return result; } - -/** Mapping from a unit name to a multiplier for converting that unit into a - * base unit. Used by config_parse_unit. */ -struct unit_table_t { - const char *unit; /**< The name of the unit */ - uint64_t multiplier; /**< How many of the base unit appear in this unit */ -}; - -/** Table to map the names of memory units to the number of bytes they - * contain. */ -static struct unit_table_t memory_units[] = { - { "", 1 }, - { "b", 1<< 0 }, - { "byte", 1<< 0 }, - { "bytes", 1<< 0 }, - { "kb", 1<<10 }, - { "kbyte", 1<<10 }, - { "kbytes", 1<<10 }, - { "kilobyte", 1<<10 }, - { "kilobytes", 1<<10 }, - { "kilobits", 1<<7 }, - { "kilobit", 1<<7 }, - { "kbits", 1<<7 }, - { "kbit", 1<<7 }, - { "m", 1<<20 }, - { "mb", 1<<20 }, - { "mbyte", 1<<20 }, - { "mbytes", 1<<20 }, - { "megabyte", 1<<20 }, - { "megabytes", 1<<20 }, - { "megabits", 1<<17 }, - { "megabit", 1<<17 }, - { "mbits", 1<<17 }, - { "mbit", 1<<17 }, - { "gb", 1<<30 }, - { "gbyte", 1<<30 }, - { "gbytes", 1<<30 }, - { "gigabyte", 1<<30 }, - { "gigabytes", 1<<30 }, - { "gigabits", 1<<27 }, - { "gigabit", 1<<27 }, - { "gbits", 1<<27 }, - { "gbit", 1<<27 }, - { "tb", UINT64_C(1)<<40 }, - { "tbyte", UINT64_C(1)<<40 }, - { "tbytes", UINT64_C(1)<<40 }, - { "terabyte", UINT64_C(1)<<40 }, - { "terabytes", UINT64_C(1)<<40 }, - { "terabits", UINT64_C(1)<<37 }, - { "terabit", UINT64_C(1)<<37 }, - { "tbits", UINT64_C(1)<<37 }, - { "tbit", UINT64_C(1)<<37 }, - { NULL, 0 }, -}; - -/** Table to map the names of time units to the number of seconds they - * contain. */ -static struct unit_table_t time_units[] = { - { "", 1 }, - { "second", 1 }, - { "seconds", 1 }, - { "minute", 60 }, - { "minutes", 60 }, - { "hour", 60*60 }, - { "hours", 60*60 }, - { "day", 24*60*60 }, - { "days", 24*60*60 }, - { "week", 7*24*60*60 }, - { "weeks", 7*24*60*60 }, - { "month", 2629728, }, /* about 30.437 days */ - { "months", 2629728, }, - { NULL, 0 }, -}; - -/** Table to map the names of time units to the number of milliseconds - * they contain. */ -static struct unit_table_t time_msec_units[] = { - { "", 1 }, - { "msec", 1 }, - { "millisecond", 1 }, - { "milliseconds", 1 }, - { "second", 1000 }, - { "seconds", 1000 }, - { "minute", 60*1000 }, - { "minutes", 60*1000 }, - { "hour", 60*60*1000 }, - { "hours", 60*60*1000 }, - { "day", 24*60*60*1000 }, - { "days", 24*60*60*1000 }, - { "week", 7*24*60*60*1000 }, - { "weeks", 7*24*60*60*1000 }, - { NULL, 0 }, -}; - -/** Parse a string <b>val</b> containing a number, zero or more - * spaces, and an optional unit string. If the unit appears in the - * table <b>u</b>, then multiply the number by the unit multiplier. - * On success, set *<b>ok</b> to 1 and return this product. - * Otherwise, set *<b>ok</b> to 0. - */ -static uint64_t -config_parse_units(const char *val, struct unit_table_t *u, int *ok) -{ - uint64_t v = 0; - double d = 0; - int use_float = 0; - char *cp; - - tor_assert(ok); - - v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); - if (!*ok || (cp && *cp == '.')) { - d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp); - if (!*ok) - goto done; - use_float = 1; - } - - if (!cp) { - *ok = 1; - v = use_float ? ((uint64_t)d) : v; - goto done; - } - - cp = (char*) eat_whitespace(cp); - - for ( ;u->unit;++u) { - if (!strcasecmp(u->unit, cp)) { - if (use_float) - v = (uint64_t)(u->multiplier * d); - else - v *= u->multiplier; - *ok = 1; - goto done; - } - } - log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); - *ok = 0; - done: - - if (*ok) - return v; - else - return 0; -} - -/** Parse a string in the format "number unit", where unit is a unit of - * information (byte, KB, M, etc). On success, set *<b>ok</b> to true - * and return the number of bytes specified. Otherwise, set - * *<b>ok</b> to false and return 0. */ -static uint64_t -config_parse_memunit(const char *s, int *ok) -{ - uint64_t u = config_parse_units(s, memory_units, ok); - return u; -} - -/** Parse a string in the format "number unit", where unit is a unit of - * time in milliseconds. On success, set *<b>ok</b> to true and return - * the number of milliseconds in the provided interval. Otherwise, set - * *<b>ok</b> to 0 and return -1. */ -static int -config_parse_msec_interval(const char *s, int *ok) -{ - uint64_t r; - r = config_parse_units(s, time_msec_units, ok); - if (r > INT_MAX) { - log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); - *ok = 0; - return -1; - } - return (int)r; -} - -/** Parse a string in the format "number unit", where unit is a unit of time. - * On success, set *<b>ok</b> to true and return the number of seconds in - * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1. - */ -static int -config_parse_interval(const char *s, int *ok) -{ - uint64_t r; - r = config_parse_units(s, time_units, ok); - if (r > INT_MAX) { - log_warn(LD_CONFIG, "Interval '%s' is too long", s); - *ok = 0; - return -1; - } - return (int)r; -} diff --git a/src/app/config/confparse.h b/src/app/config/confparse.h index 57f1ec1762..bd06a4a0d0 100644 --- a/src/app/config/confparse.h +++ b/src/app/config/confparse.h @@ -13,72 +13,7 @@ #ifndef TOR_CONFPARSE_H #define TOR_CONFPARSE_H -/** Enumeration of types which option values can take */ -typedef enum config_type_t { - CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ - CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ - CONFIG_TYPE_UINT, /**< A non-negative integer less than MAX_INT */ - CONFIG_TYPE_INT, /**< Any integer. */ - CONFIG_TYPE_UINT64, /**< A value in range 0..UINT64_MAX */ - CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or - * "auto". */ - CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ - CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional - * units */ - CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ - CONFIG_TYPE_DOUBLE, /**< A floating-point value */ - CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ - CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false, - * 1 for true, and -1 for auto */ - CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ - CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and - * optional whitespace. */ - CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and - * optional whitespace, representing intervals in - * seconds, with optional units. We allow - * multiple values here for legacy reasons, but - * ignore every value after the first. */ - CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ - CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, - * mixed with other keywords. */ - CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize - * context-sensitive config lines when fetching. - */ - CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps, - * parsed into a routerset_t. */ - CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */ -} config_type_t; - -#ifdef TOR_UNIT_TESTS -/** - * Union used when building in test mode typechecking the members of a type - * used with confparse.c. See CONF_CHECK_VAR_TYPE for a description of how - * it is used. */ -typedef union { - char **STRING; - char **FILENAME; - int *UINT; /* yes, really: Even though the confparse type is called - * "UINT", it still uses the C int type -- it just enforces that - * the values are in range [0,INT_MAX]. - */ - uint64_t *UINT64; - int *INT; - int *PORT; - int *INTERVAL; - int *MSEC_INTERVAL; - uint64_t *MEMUNIT; - double *DOUBLE; - int *BOOL; - int *AUTOBOOL; - time_t *ISOTIME; - smartlist_t **CSV; - int *CSV_INTERVAL; - struct config_line_t **LINELIST; - struct config_line_t **LINELIST_S; - struct config_line_t **LINELIST_V; - routerset_t **ROUTERSET; -} confparse_dummy_values_t; -#endif /* defined(TOR_UNIT_TESTS) */ +#include "lib/conf/conftypes.h" /** An abbreviation for a configuration option allowed on the command line. */ typedef struct config_abbrev_t { @@ -230,4 +165,9 @@ void warn_deprecated_option(const char *what, const char *why); #define CFG_EQ_LINELIST(a,b,opt) config_lines_eq((a)->opt, (b)->opt) #define CFG_EQ_ROUTERSET(a,b,opt) routerset_equal((a)->opt, (b)->opt) +#ifdef CONFPARSE_PRIVATE +STATIC void config_reset_line(const config_format_t *fmt, void *options, + const char *key, int use_defaults); +#endif + #endif /* !defined(TOR_CONFPARSE_H) */ diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 2ee2d15674..8156d2ca11 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -121,7 +121,6 @@ struct or_options_t { struct config_line_t *RecommendedVersions; struct config_line_t *RecommendedClientVersions; struct config_line_t *RecommendedServerVersions; - struct config_line_t *RecommendedPackages; /** Whether dirservers allow router descriptors with private IPs. */ int DirAllowPrivateAddresses; /** Whether routers accept EXTEND cells to routers with private IPs. */ diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index fdfd68b244..c6c5ec14f5 100644 --- a/src/app/config/statefile.c +++ b/src/app/config/statefile.c @@ -105,19 +105,19 @@ static config_var_t state_vars_[] = { V(HidServRevCounter, LINELIST, NULL), V(BWHistoryReadEnds, ISOTIME, NULL), - V(BWHistoryReadInterval, UINT, "900"), + V(BWHistoryReadInterval, POSINT, "900"), V(BWHistoryReadValues, CSV, ""), V(BWHistoryReadMaxima, CSV, ""), V(BWHistoryWriteEnds, ISOTIME, NULL), - V(BWHistoryWriteInterval, UINT, "900"), + V(BWHistoryWriteInterval, POSINT, "900"), V(BWHistoryWriteValues, CSV, ""), V(BWHistoryWriteMaxima, CSV, ""), V(BWHistoryDirReadEnds, ISOTIME, NULL), - V(BWHistoryDirReadInterval, UINT, "900"), + V(BWHistoryDirReadInterval, POSINT, "900"), V(BWHistoryDirReadValues, CSV, ""), V(BWHistoryDirReadMaxima, CSV, ""), V(BWHistoryDirWriteEnds, ISOTIME, NULL), - V(BWHistoryDirWriteInterval, UINT, "900"), + V(BWHistoryDirWriteInterval, POSINT, "900"), V(BWHistoryDirWriteValues, CSV, ""), V(BWHistoryDirWriteMaxima, CSV, ""), @@ -128,12 +128,12 @@ static config_var_t state_vars_[] = { V(LastRotatedOnionKey, ISOTIME, NULL), V(LastWritten, ISOTIME, NULL), - V(TotalBuildTimes, UINT, NULL), - V(CircuitBuildAbandonedCount, UINT, "0"), + V(TotalBuildTimes, POSINT, NULL), + V(CircuitBuildAbandonedCount, POSINT, "0"), VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL), VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL), - V(MinutesSinceUserActivity, UINT, NULL), + V(MinutesSinceUserActivity, POSINT, NULL), V(Dormant, AUTOBOOL, "auto"), END_OF_CONFIG_VARS diff --git a/src/app/main/main.c b/src/app/main/main.c index 6e325f0b10..31cee37637 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -653,10 +653,6 @@ tor_init(int argc, char *argv[]) return -1; } - if (tor_init_libevent_rng() < 0) { - log_warn(LD_NET, "Problem initializing libevent RNG."); - } - /* Scan/clean unparseable descriptors; after reading config */ routerparse_init(); @@ -1256,6 +1252,8 @@ pubsub_connect(void) /* XXXX For each pubsub channel, its delivery strategy should be set at * this XXXX point, using tor_mainloop_set_delivery_strategy(). */ + tor_mainloop_set_delivery_strategy("orconn", DELIV_IMMEDIATE); + tor_mainloop_set_delivery_strategy("ocirc", DELIV_IMMEDIATE); } } diff --git a/src/app/main/ntmain.c b/src/app/main/ntmain.c index f00b712702..a2de5bb87e 100644 --- a/src/app/main/ntmain.c +++ b/src/app/main/ntmain.c @@ -608,6 +608,7 @@ nt_service_install(int argc, char **argv) &sidUse) == 0) { /* XXXX For some reason, the above test segfaults. Fix that. */ printf("User \"%s\" doesn't seem to exist.\n", user_acct); + tor_free(command); return -1; } else { printf("Will try to install service as user \"%s\".\n", user_acct); diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c index cc0091a9ab..93d6351d1b 100644 --- a/src/app/main/shutdown.c +++ b/src/app/main/shutdown.c @@ -160,8 +160,6 @@ tor_free_all(int postfork) subsystems_shutdown(); - tor_libevent_free_all(); - /* Stuff in util.c and address.c*/ if (!postfork) { esc_router_info(NULL); diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c index f595796232..95d96f78d2 100644 --- a/src/app/main/subsystem_list.c +++ b/src/app/main/subsystem_list.c @@ -25,6 +25,7 @@ #include "lib/time/time_sys.h" #include "lib/tls/tortls_sys.h" #include "lib/wallclock/wallclock_sys.h" +#include "lib/evloop/evloop_sys.h" #include "feature/dirauth/dirauth_sys.h" @@ -50,6 +51,8 @@ const subsys_fns_t *tor_subsystems[] = { &sys_ocirc_event, /* -32 */ &sys_btrack, /* -30 */ + &sys_evloop, /* -20 */ + &sys_mainloop, /* 5 */ &sys_or, /* 20 */ diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 3a4e729429..ff809c01cf 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -522,14 +522,13 @@ origin_circuit_get_guard_state(origin_circuit_t *circ) static void circuit_chan_publish(const origin_circuit_t *circ, const channel_t *chan) { - ocirc_event_msg_t msg; + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = OCIRC_MSGTYPE_CHAN; - msg.u.chan.gid = circ->global_identifier; - msg.u.chan.chan = chan->global_identifier; - msg.u.chan.onehop = circ->build_state->onehop_tunnel; + msg->gid = circ->global_identifier; + msg->chan = chan->global_identifier; + msg->onehop = circ->build_state->onehop_tunnel; - ocirc_event_publish(&msg); + ocirc_chan_publish(msg); } /** Start establishing the first hop of our circuit. Figure out what diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index ebbe7f0824..9ee9f93c99 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -496,17 +496,16 @@ int circuit_event_status(origin_circuit_t *circ, circuit_status_event_t tp, int reason_code) { - ocirc_event_msg_t msg; + ocirc_cevent_msg_t *msg = tor_malloc(sizeof(*msg)); tor_assert(circ); - msg.type = OCIRC_MSGTYPE_CEVENT; - msg.u.cevent.gid = circ->global_identifier; - msg.u.cevent.evtype = tp; - msg.u.cevent.reason = reason_code; - msg.u.cevent.onehop = circ->build_state->onehop_tunnel; + msg->gid = circ->global_identifier; + msg->evtype = tp; + msg->reason = reason_code; + msg->onehop = circ->build_state->onehop_tunnel; - ocirc_event_publish(&msg); + ocirc_cevent_publish(msg); return control_event_circuit_status(circ, tp, reason_code); } @@ -514,26 +513,25 @@ circuit_event_status(origin_circuit_t *circ, circuit_status_event_t tp, * Helper function to publish a state change message * * circuit_set_state() calls this to notify subscribers about a change - * of the state of an origin circuit. + * of the state of an origin circuit. @a circ must be an origin + * circuit. **/ static void circuit_state_publish(const circuit_t *circ) { - ocirc_event_msg_t msg; + ocirc_state_msg_t *msg = tor_malloc(sizeof(*msg)); const origin_circuit_t *ocirc; - if (!CIRCUIT_IS_ORIGIN(circ)) - return; + tor_assert(CIRCUIT_IS_ORIGIN(circ)); ocirc = CONST_TO_ORIGIN_CIRCUIT(circ); /* Only inbound OR circuits can be in this state, not origin circuits. */ tor_assert(circ->state != CIRCUIT_STATE_ONIONSKIN_PENDING); - msg.type = OCIRC_MSGTYPE_STATE; - msg.u.state.gid = ocirc->global_identifier; - msg.u.state.state = circ->state; - msg.u.state.onehop = ocirc->build_state->onehop_tunnel; + msg->gid = ocirc->global_identifier; + msg->state = circ->state; + msg->onehop = ocirc->build_state->onehop_tunnel; - ocirc_event_publish(&msg); + ocirc_state_publish(msg); } /** Change the state of <b>circ</b> to <b>state</b>, adding it to or removing @@ -565,7 +563,8 @@ circuit_set_state(circuit_t *circ, uint8_t state) if (state == CIRCUIT_STATE_GUARD_WAIT || state == CIRCUIT_STATE_OPEN) tor_assert(!circ->n_chan_create_cell); circ->state = state; - circuit_state_publish(circ); + if (CIRCUIT_IS_ORIGIN(circ)) + circuit_state_publish(circ); } /** Append to <b>out</b> all circuits in state CHAN_WAIT waiting for diff --git a/src/core/or/circuitpadding.c b/src/core/or/circuitpadding.c index c4670bbc2f..460f72c17e 100644 --- a/src/core/or/circuitpadding.c +++ b/src/core/or/circuitpadding.c @@ -17,7 +17,7 @@ * Each padding type is described by a state machine (circpad_machine_spec_t), * which is also referred as a "padding machine" in this file. Currently, * these state machines are hardcoded in the source code (e.g. see - * circpad_circ_client_machine_init()), but in the future we will be able to + * circpad_machines_init()), but in the future we will be able to * serialize them in the torrc or the consensus. * * As specified by prop#254, clients can negotiate padding with relays by using @@ -210,7 +210,7 @@ circpad_marked_circuit_for_padding(circuit_t *circ, int reason) } log_info(LD_CIRC, "Circuit %d is not marked for close because of a " - " pending padding machine.", CIRCUIT_IS_ORIGIN(circ) ? + "pending padding machine.", CIRCUIT_IS_ORIGIN(circ) ? TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0); /* If the machine has had no network events at all within the @@ -222,7 +222,7 @@ circpad_marked_circuit_for_padding(circuit_t *circ, int reason) if (circ->padding_info[i]->last_cell_time_sec + (time_t)CIRCPAD_DELAY_MAX_SECS < approx_time()) { log_notice(LD_BUG, "Circuit %d was not marked for close because of a " - " pending padding machine for over an hour. Circuit is a %s", + "pending padding machine for over an hour. Circuit is a %s", CIRCUIT_IS_ORIGIN(circ) ? TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0, circuit_purpose_to_string(circ->purpose)); @@ -555,11 +555,12 @@ circpad_distribution_sample_iat_delay(const circpad_state_t *state, } /** - * Sample an expected time-until-next-packet delay from the histogram. + * Sample an expected time-until-next-packet delay from the histogram or + * probability distribution. * - * The bin is chosen with probability proportional to the number - * of tokens in each bin, and then a time value is chosen uniformly from - * that bin's [start,end) time range. + * A bin of the histogram is chosen with probability proportional to the number + * of tokens in each bin, and then a time value is chosen uniformly from that + * bin's [start,end) time range. */ STATIC circpad_delay_t circpad_machine_sample_delay(circpad_machine_runtime_t *mi) @@ -658,12 +659,7 @@ circpad_machine_sample_delay(circpad_machine_runtime_t *mi) /** * Sample a value from the specified probability distribution. * - * This performs inverse transform sampling - * (https://en.wikipedia.org/wiki/Inverse_transform_sampling). - * - * XXX: These formulas were taken verbatim. Need a floating wizard - * to check them for catastropic cancellation and other issues (teor?). - * Also: is 32bits of double from [0.0,1.0) enough? + * Uses functions from src/lib/math/prob_distr.c . */ static double circpad_distribution_sample(circpad_distribution_t dist) @@ -747,6 +743,8 @@ circpad_distribution_sample(circpad_distribution_t dist) /** * Find the index of the first bin whose upper bound is * greater than the target, and that has tokens remaining. + * + * Used for histograms with token removal. */ static circpad_hist_index_t circpad_machine_first_higher_index(const circpad_machine_runtime_t *mi, @@ -769,6 +767,8 @@ circpad_machine_first_higher_index(const circpad_machine_runtime_t *mi, /** * Find the index of the first bin whose lower bound is lower or equal to * <b>target_bin_usec</b>, and that still has tokens remaining. + * + * Used for histograms with token removal. */ static circpad_hist_index_t circpad_machine_first_lower_index(const circpad_machine_runtime_t *mi, @@ -790,6 +790,8 @@ circpad_machine_first_lower_index(const circpad_machine_runtime_t *mi, /** * Remove a token from the first non-empty bin whose upper bound is * greater than the target. + * + * Used for histograms with token removal. */ STATIC void circpad_machine_remove_higher_token(circpad_machine_runtime_t *mi, @@ -811,6 +813,8 @@ circpad_machine_remove_higher_token(circpad_machine_runtime_t *mi, /** * Remove a token from the first non-empty bin whose upper bound is * lower than the target. + * + * Used for histograms with token removal. */ STATIC void circpad_machine_remove_lower_token(circpad_machine_runtime_t *mi, @@ -840,6 +844,8 @@ circpad_machine_remove_lower_token(circpad_machine_runtime_t *mi, * midpoint. * * If it is false, use bin index distance only. + * + * Used for histograms with token removal. */ STATIC void circpad_machine_remove_closest_token(circpad_machine_runtime_t *mi, @@ -922,6 +928,8 @@ circpad_machine_remove_closest_token(circpad_machine_runtime_t *mi, * Remove a token from the exact bin corresponding to the target. * * If it is empty, do nothing. + * + * Used for histograms with token removal. */ static void circpad_machine_remove_exact(circpad_machine_runtime_t *mi, @@ -1354,7 +1362,7 @@ circpad_machine_reached_padding_limit(circpad_machine_runtime_t *mi) /* If circpad_max_global_padding_pct is non-zero, and we've * sent more than the global padding cell limit, then check our - * gloabl tor process percentage limit on padding. */ + * global tor process percentage limit on padding. */ if (circpad_global_max_padding_percent && circpad_global_padding_sent >= circpad_global_allowed_cells) { uint64_t total_cells = circpad_global_padding_sent + @@ -1494,7 +1502,7 @@ circpad_machine_schedule_padding,(circpad_machine_runtime_t *mi)) /** * If the machine transitioned to the END state, we need * to check to see if it wants us to shut it down immediately. - * If it does, then we need to send the appropate negotation commands + * If it does, then we need to send the appropiate negotiation commands * depending on which side it is. * * After this function is called, mi may point to freed memory. Do @@ -1511,7 +1519,7 @@ circpad_machine_spec_transitioned_to_end(circpad_machine_runtime_t *mi) * we can handle the case where this machine started while it was * the only machine that matched conditions, but *since* then more * "higher ranking" machines now match the conditions, and would - * be given a chance to take precidence over this one in + * be given a chance to take precedence over this one in * circpad_add_matching_machines(). * * Returning to START or waiting forever in END would not give those @@ -1638,7 +1646,7 @@ circpad_estimate_circ_rtt_on_received(circuit_t *circ, if (CIRCUIT_IS_ORIGIN(circ) || mi->stop_rtt_update) return; - /* If we already have a last receieved packet time, that means we + /* If we already have a last received packet time, that means we * did not get a response before this packet. The RTT estimate * only makes sense if we do not have multiple packets on the * wire, so stop estimating if this is the second packet @@ -2305,7 +2313,7 @@ circpad_deliver_sent_relay_cell_events(circuit_t *circ, /* Optimization: The event for RELAY_COMMAND_DROP is sent directly * from circpad_send_padding_cell_for_callback(). This is to avoid * putting a cell_t and a relay_header_t on the stack repeatedly - * if we decide to send a long train of padidng cells back-to-back + * if we decide to send a long train of padding cells back-to-back * with 0 delay. So we do nothing here. */ return; } else { diff --git a/src/core/or/circuitpadding.h b/src/core/or/circuitpadding.h index 3cf40e11db..fc2e595c0a 100644 --- a/src/core/or/circuitpadding.h +++ b/src/core/or/circuitpadding.h @@ -51,7 +51,7 @@ typedef enum { CIRCPAD_EVENT_INFINITY = 4, /* All histogram bins are empty (we are out of tokens) */ CIRCPAD_EVENT_BINS_EMPTY = 5, - /* just a counter of the events above */ + /* This state has used up its cell count */ CIRCPAD_EVENT_LENGTH_COUNT = 6 } circpad_event_t; #define CIRCPAD_NUM_EVENTS ((int)CIRCPAD_EVENT_LENGTH_COUNT+1) @@ -79,7 +79,7 @@ typedef uint32_t circpad_delay_t; * An infinite padding cell delay means don't schedule any padding -- * simply wait until a different event triggers a transition. * - * This means that the maximum delay we can scedule is UINT32_MAX-1 + * This means that the maximum delay we can schedule is UINT32_MAX-1 * microseconds, or about 4300 seconds (1.25 hours). * XXX: Is this enough if we want to simulate light, intermittent * activity on an onion service? @@ -106,8 +106,8 @@ typedef uint32_t circpad_delay_t; * * If any of these elements is set, then the circuit will be tested against * that specific condition. If an element is unset, then we don't test it. - * (E.g. If neither NO_STREAMS or STREAMS are set, then we will not care - * whether a circuit has streams attached when we apply a state machine) + * (E.g., if neither NO_STREAMS or STREAMS are set, then we will not care + * whether a circuit has streams attached when we apply a state machine.) * * The helper function circpad_circuit_state() converts circuit state * flags into this more compact representation. @@ -255,8 +255,9 @@ typedef struct circpad_distribution_t { typedef uint16_t circpad_statenum_t; #define CIRCPAD_STATENUM_MAX (UINT16_MAX) -/** A histogram is used to sample padding delays given a machine state. This - * constant defines the maximum histogram width (i.e. the max number of bins). +/** A histogram can be used to sample padding delays given a machine state. + * This constant defines the maximum histogram width (i.e. the max number of + * bins). * * The current limit is arbitrary and could be raised if there is a need, * however too many bins will be hard to serialize in the future. @@ -275,10 +276,10 @@ typedef uint16_t circpad_statenum_t; * happen. The mutable information that gets updated in runtime are carried in * a circpad_machine_runtime_t. * - * This struct describes the histograms and parameters of a single - * state in the adaptive padding machine. Instances of this struct - * exist in global circpad machine definitions that come from torrc - * or the consensus. + * This struct describes the histograms and/or probability distributions, as + * well as parameters of a single state in the adaptive padding machine. + * Instances of this struct exist in global circpad machine definitions that + * come from torrc or the consensus. */ typedef struct circpad_state_t { /** diff --git a/src/core/or/circuitpadding_machines.c b/src/core/or/circuitpadding_machines.c index 75d2614aca..7220d657fc 100644 --- a/src/core/or/circuitpadding_machines.c +++ b/src/core/or/circuitpadding_machines.c @@ -155,7 +155,6 @@ circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl) relay_machine->name = "relay_ip_circ"; relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; - relay_machine->target_hopnum = 2; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; @@ -387,7 +386,6 @@ circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl) /* Only pad after the circuit has been built and pad to the middle */ relay_machine->conditions.min_hops = 2; relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; - relay_machine->target_hopnum = 2; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; @@ -408,7 +406,7 @@ circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl) /* OBFUSCATE_CIRC_SETUP -> END transition when we send our first * padding packet and/or hit the state length (the state length is 1). */ relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP]. - next_state[CIRCPAD_EVENT_PADDING_RECV] = CIRCPAD_STATE_END; + next_state[CIRCPAD_EVENT_PADDING_SENT] = CIRCPAD_STATE_END; relay_machine->states[CIRCPAD_STATE_OBFUSCATE_CIRC_SETUP]. next_state[CIRCPAD_EVENT_LENGTH_COUNT] = CIRCPAD_STATE_END; diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index c08d2a9ff5..091d9c9b09 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -3833,6 +3833,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) if (! bcell.is_begindir) { /* Steal reference */ + tor_assert(bcell.address); address = bcell.address; port = bcell.port; diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 830e09fd54..4c93351e31 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -414,13 +414,12 @@ void connection_or_event_status(or_connection_t *conn, or_conn_status_event_t tp, int reason) { - orconn_event_msg_t msg; + orconn_status_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = ORCONN_MSGTYPE_STATUS; - msg.u.status.gid = conn->base_.global_identifier; - msg.u.status.status = tp; - msg.u.status.reason = reason; - orconn_event_publish(&msg); + msg->gid = conn->base_.global_identifier; + msg->status = tp; + msg->reason = reason; + orconn_status_publish(msg); control_event_or_conn_status(conn, tp, reason); } @@ -433,26 +432,25 @@ connection_or_event_status(or_connection_t *conn, or_conn_status_event_t tp, static void connection_or_state_publish(const or_connection_t *conn, uint8_t state) { - orconn_event_msg_t msg; + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = ORCONN_MSGTYPE_STATE; - msg.u.state.gid = conn->base_.global_identifier; + msg->gid = conn->base_.global_identifier; if (conn->is_pt) { /* Do extra decoding because conn->proxy_type indicates the proxy * protocol that tor uses to talk with the transport plugin, * instead of PROXY_PLUGGABLE. */ tor_assert_nonfatal(conn->proxy_type != PROXY_NONE); - msg.u.state.proxy_type = PROXY_PLUGGABLE; + msg->proxy_type = PROXY_PLUGGABLE; } else { - msg.u.state.proxy_type = conn->proxy_type; + msg->proxy_type = conn->proxy_type; } - msg.u.state.state = state; + msg->state = state; if (conn->chan) { - msg.u.state.chan = TLS_CHAN_TO_BASE(conn->chan)->global_identifier; + msg->chan = TLS_CHAN_TO_BASE(conn->chan)->global_identifier; } else { - msg.u.state.chan = 0; + msg->chan = 0; } - orconn_event_publish(&msg); + orconn_state_publish(msg); } /** Call this to change or_connection_t states, so the owning channel_tls_t can diff --git a/src/core/or/ocirc_event.c b/src/core/or/ocirc_event.c index 4a6fc748c9..3cb9147134 100644 --- a/src/core/or/ocirc_event.c +++ b/src/core/or/ocirc_event.c @@ -26,59 +26,103 @@ #include "core/or/origin_circuit_st.h" #include "lib/subsys/subsys.h" -/** List of subscribers */ -static smartlist_t *ocirc_event_rcvrs; +DECLARE_PUBLISH(ocirc_state); +DECLARE_PUBLISH(ocirc_chan); +DECLARE_PUBLISH(ocirc_cevent); + +static void +ocirc_event_free(msg_aux_data_t u) +{ + tor_free_(u.ptr); +} + +static char * +ocirc_state_fmt(msg_aux_data_t u) +{ + ocirc_state_msg_t *msg = (ocirc_state_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu32" state=%d onehop=%d>", + msg->gid, msg->state, msg->onehop); + return s; +} + +static char * +ocirc_chan_fmt(msg_aux_data_t u) +{ + ocirc_chan_msg_t *msg = (ocirc_chan_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu32" chan=%"PRIu64" onehop=%d>", + msg->gid, msg->chan, msg->onehop); + return s; +} + +static char * +ocirc_cevent_fmt(msg_aux_data_t u) +{ + ocirc_cevent_msg_t *msg = (ocirc_cevent_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu32" evtype=%d reason=%d onehop=%d>", + msg->gid, msg->evtype, msg->reason, msg->onehop); + return s; +} + +static dispatch_typefns_t ocirc_state_fns = { + .free_fn = ocirc_event_free, + .fmt_fn = ocirc_state_fmt, +}; + +static dispatch_typefns_t ocirc_chan_fns = { + .free_fn = ocirc_event_free, + .fmt_fn = ocirc_chan_fmt, +}; + +static dispatch_typefns_t ocirc_cevent_fns = { + .free_fn = ocirc_event_free, + .fmt_fn = ocirc_cevent_fmt, +}; -/** Initialize subscriber list */ static int -ocirc_event_init(void) +ocirc_add_pubsub(struct pubsub_connector_t *connector) { - ocirc_event_rcvrs = smartlist_new(); + if (DISPATCH_REGISTER_TYPE(connector, ocirc_state, ô_state_fns)) + return -1; + if (DISPATCH_REGISTER_TYPE(connector, ocirc_chan, ô_chan_fns)) + return -1; + if (DISPATCH_REGISTER_TYPE(connector, ocirc_cevent, ô_cevent_fns)) + return -1; + if (DISPATCH_ADD_PUB(connector, ocirc, ocirc_state)) + return -1; + if (DISPATCH_ADD_PUB(connector, ocirc, ocirc_chan)) + return -1; + if (DISPATCH_ADD_PUB(connector, ocirc, ocirc_cevent)) + return -1; return 0; } -/** Free subscriber list */ -static void -ocirc_event_fini(void) +void +ocirc_state_publish(ocirc_state_msg_t *msg) { - smartlist_free(ocirc_event_rcvrs); + PUBLISH(ocirc_state, msg); } -/** - * Subscribe to messages about origin circuit events - * - * Register a callback function to receive messages about origin - * circuits. The publisher calls this function synchronously. - **/ void -ocirc_event_subscribe(ocirc_event_rcvr_t fn) +ocirc_chan_publish(ocirc_chan_msg_t *msg) { - tor_assert(fn); - /* Don't duplicate subscriptions. */ - if (smartlist_contains(ocirc_event_rcvrs, fn)) - return; - - smartlist_add(ocirc_event_rcvrs, fn); + PUBLISH(ocirc_chan, msg); } -/** - * Publish a message about OR connection events - * - * This calls the subscriber receiver function synchronously. - **/ void -ocirc_event_publish(const ocirc_event_msg_t *msg) +ocirc_cevent_publish(ocirc_cevent_msg_t *msg) { - SMARTLIST_FOREACH_BEGIN(ocirc_event_rcvrs, ocirc_event_rcvr_t, fn) { - tor_assert(fn); - (*fn)(msg); - } SMARTLIST_FOREACH_END(fn); + PUBLISH(ocirc_cevent, msg); } const subsys_fns_t sys_ocirc_event = { .name = "ocirc_event", .supported = true, .level = -32, - .initialize = ocirc_event_init, - .shutdown = ocirc_event_fini, + .add_pubsub = ocirc_add_pubsub, }; diff --git a/src/core/or/ocirc_event.h b/src/core/or/ocirc_event.h index 59ec9e27cb..8e9494874f 100644 --- a/src/core/or/ocirc_event.h +++ b/src/core/or/ocirc_event.h @@ -12,6 +12,7 @@ #include <stdbool.h> #include "lib/cc/torint.h" +#include "lib/pubsub/pubsub.h" /** Used to indicate the type of a circuit event passed to the controller. * The various types are defined in control-spec.txt */ @@ -30,6 +31,8 @@ typedef struct ocirc_state_msg_t { bool onehop; /**< one-hop circuit? */ } ocirc_state_msg_t; +DECLARE_MESSAGE(ocirc_state, ocirc_state, ocirc_state_msg_t *); + /** * Message when a channel gets associated to a circuit. * @@ -44,6 +47,8 @@ typedef struct ocirc_chan_msg_t { bool onehop; /**< one-hop circuit? */ } ocirc_chan_msg_t; +DECLARE_MESSAGE(ocirc_chan, ocirc_chan, ocirc_chan_msg_t *); + /** * Message for origin circuit status event * @@ -56,34 +61,12 @@ typedef struct ocirc_cevent_msg_t { bool onehop; /**< one-hop circuit? */ } ocirc_cevent_msg_t; -/** Discriminant values for origin circuit event message */ -typedef enum ocirc_msgtype_t { - OCIRC_MSGTYPE_STATE, - OCIRC_MSGTYPE_CHAN, - OCIRC_MSGTYPE_CEVENT, -} ocirc_msgtype_t; - -/** Discriminated union for the actual message */ -typedef struct ocirc_event_msg_t { - int type; - union { - ocirc_state_msg_t state; - ocirc_chan_msg_t chan; - ocirc_cevent_msg_t cevent; - } u; -} ocirc_event_msg_t; - -/** - * Receiver function pointer for origin circuit subscribers - * - * This function gets called synchronously by the publisher. - **/ -typedef void (*ocirc_event_rcvr_t)(const ocirc_event_msg_t *); - -void ocirc_event_subscribe(ocirc_event_rcvr_t fn); +DECLARE_MESSAGE(ocirc_cevent, ocirc_cevent, ocirc_cevent_msg_t *); #ifdef OCIRC_EVENT_PRIVATE -void ocirc_event_publish(const ocirc_event_msg_t *msg); +void ocirc_state_publish(ocirc_state_msg_t *msg); +void ocirc_chan_publish(ocirc_chan_msg_t *msg); +void ocirc_cevent_publish(ocirc_cevent_msg_t *msg); #endif #endif /* !defined(TOR_OCIRC_EVENT_H) */ diff --git a/src/core/or/orconn_event.c b/src/core/or/orconn_event.c index 9fb34bd1ff..86f112fc09 100644 --- a/src/core/or/orconn_event.c +++ b/src/core/or/orconn_event.c @@ -17,65 +17,83 @@ **/ #include "core/or/or.h" +#include "lib/pubsub/pubsub.h" #include "lib/subsys/subsys.h" #define ORCONN_EVENT_PRIVATE #include "core/or/orconn_event.h" #include "core/or/orconn_event_sys.h" -/** List of subscribers */ -static smartlist_t *orconn_event_rcvrs; +DECLARE_PUBLISH(orconn_state); +DECLARE_PUBLISH(orconn_status); -/** Initialize subscriber list */ -static int -orconn_event_init(void) +static void +orconn_event_free(msg_aux_data_t u) { - orconn_event_rcvrs = smartlist_new(); - return 0; + tor_free_(u.ptr); } -/** Free subscriber list */ -static void -orconn_event_fini(void) +static char * +orconn_state_fmt(msg_aux_data_t u) { - smartlist_free(orconn_event_rcvrs); + orconn_state_msg_t *msg = (orconn_state_msg_t *)u.ptr; + char *s = NULL; + + tor_asprintf(&s, "<gid=%"PRIu64" chan=%"PRIu64" proxy_type=%d state=%d>", + msg->gid, msg->chan, msg->proxy_type, msg->state); + return s; } -/** - * Subscribe to messages about OR connection events - * - * Register a callback function to receive messages about ORCONNs. - * The publisher calls this function synchronously. - **/ -void -orconn_event_subscribe(orconn_event_rcvr_t fn) +static char * +orconn_status_fmt(msg_aux_data_t u) { - tor_assert(fn); - /* Don't duplicate subscriptions. */ - if (smartlist_contains(orconn_event_rcvrs, fn)) - return; + orconn_status_msg_t *msg = (orconn_status_msg_t *)u.ptr; + char *s = NULL; - smartlist_add(orconn_event_rcvrs, fn); + tor_asprintf(&s, "<gid=%"PRIu64" status=%d reason=%d>", + msg->gid, msg->status, msg->reason); + return s; +} + +static dispatch_typefns_t orconn_state_fns = { + .free_fn = orconn_event_free, + .fmt_fn = orconn_state_fmt, +}; + +static dispatch_typefns_t orconn_status_fns = { + .free_fn = orconn_event_free, + .fmt_fn = orconn_status_fmt, +}; + +static int +orconn_add_pubsub(struct pubsub_connector_t *connector) +{ + if (DISPATCH_REGISTER_TYPE(connector, orconn_state, &orconn_state_fns)) + return -1; + if (DISPATCH_REGISTER_TYPE(connector, orconn_status, &orconn_status_fns)) + return -1; + if (DISPATCH_ADD_PUB(connector, orconn, orconn_state) != 0) + return -1; + if (DISPATCH_ADD_PUB(connector, orconn, orconn_status) != 0) + return -1; + return 0; +} + +void +orconn_state_publish(orconn_state_msg_t *msg) +{ + PUBLISH(orconn_state, msg); } -/** - * Publish a message about OR connection events - * - * This calls the subscriber receiver function synchronously. - **/ void -orconn_event_publish(const orconn_event_msg_t *msg) +orconn_status_publish(orconn_status_msg_t *msg) { - SMARTLIST_FOREACH_BEGIN(orconn_event_rcvrs, orconn_event_rcvr_t, fn) { - tor_assert(fn); - (*fn)(msg); - } SMARTLIST_FOREACH_END(fn); + PUBLISH(orconn_status, msg); } const subsys_fns_t sys_orconn_event = { .name = "orconn_event", .supported = true, .level = -33, - .initialize = orconn_event_init, - .shutdown = orconn_event_fini, + .add_pubsub = orconn_add_pubsub, }; diff --git a/src/core/or/orconn_event.h b/src/core/or/orconn_event.h index d6635793db..fb67a7d183 100644 --- a/src/core/or/orconn_event.h +++ b/src/core/or/orconn_event.h @@ -16,6 +16,8 @@ #ifndef TOR_ORCONN_EVENT_H #define TOR_ORCONN_EVENT_H +#include "lib/pubsub/pubsub.h" + /** * @name States of OR connections * @@ -62,12 +64,6 @@ typedef enum or_conn_status_event_t { OR_CONN_EVENT_NEW = 4, } or_conn_status_event_t; -/** Discriminant values for orconn event message */ -typedef enum orconn_msgtype_t { - ORCONN_MSGTYPE_STATE, - ORCONN_MSGTYPE_STATUS, -} orconn_msgtype_t; - /** * Message for orconn state update * @@ -83,6 +79,8 @@ typedef struct orconn_state_msg_t { uint8_t state; /**< new connection state */ } orconn_state_msg_t; +DECLARE_MESSAGE(orconn_state, orconn_state, orconn_state_msg_t *); + /** * Message for orconn status event * @@ -95,26 +93,11 @@ typedef struct orconn_status_msg_t { int reason; /**< reason */ } orconn_status_msg_t; -/** Discriminated union for the actual message */ -typedef struct orconn_event_msg_t { - int type; - union { - orconn_state_msg_t state; - orconn_status_msg_t status; - } u; -} orconn_event_msg_t; - -/** - * Receiver function pointer for OR subscribers - * - * This function gets called synchronously by the publisher. - **/ -typedef void (*orconn_event_rcvr_t)(const orconn_event_msg_t *); - -void orconn_event_subscribe(orconn_event_rcvr_t); +DECLARE_MESSAGE(orconn_status, orconn_status, orconn_status_msg_t *); #ifdef ORCONN_EVENT_PRIVATE -void orconn_event_publish(const orconn_event_msg_t *); +void orconn_state_publish(orconn_state_msg_t *); +void orconn_status_publish(orconn_status_msg_t *); #endif #endif /* !defined(TOR_ORCONN_EVENT_H) */ diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c index 47ac95f3cf..0757ce3d52 100644 --- a/src/core/or/sendme.c +++ b/src/core/or/sendme.c @@ -23,7 +23,7 @@ #include "core/or/sendme.h" #include "feature/nodelist/networkstatus.h" #include "lib/ctime/di_ops.h" -#include "trunnel/sendme.h" +#include "trunnel/sendme_cell.h" /* Return the minimum version given by the consensus (if any) that should be * used when emitting a SENDME cell. */ diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c index af8559a476..faa52ae4e1 100644 --- a/src/ext/csiphash.c +++ b/src/ext/csiphash.c @@ -87,6 +87,13 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k v0 ^= mi; } +#ifdef __COVERITY__ + { + uint64_t mi = 0; + memcpy(&mi, m+i, (src_sz-blocks)); + last7 = _le64toh(mi) | (uint64_t)(src_sz & 0xff) << 56; + } +#else switch (src_sz - blocks) { case 7: last7 |= (uint64_t)m[i + 6] << 48; /* Falls through. */ case 6: last7 |= (uint64_t)m[i + 5] << 40; /* Falls through. */ @@ -98,6 +105,7 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k case 0: default:; } +#endif v3 ^= last7; DOUBLE_ROUND(v0,v1,v2,v3); v0 ^= last7; diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h index 15d1c8633e..52afa9ccd4 100644 --- a/src/ext/trunnel/trunnel-impl.h +++ b/src/ext/trunnel/trunnel-impl.h @@ -1,4 +1,4 @@ -/* trunnel-impl.h -- copied from Trunnel v1.5.2 +/* trunnel-impl.h -- copied from Trunnel v1.5.3 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c index 3ae3fe02c8..01a55c5bec 100644 --- a/src/ext/trunnel/trunnel.c +++ b/src/ext/trunnel/trunnel.c @@ -1,4 +1,4 @@ -/* trunnel.c -- copied from Trunnel v1.5.2 +/* trunnel.c -- copied from Trunnel v1.5.3 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h index 9b708437b8..87c75f4ec3 100644 --- a/src/ext/trunnel/trunnel.h +++ b/src/ext/trunnel/trunnel.h @@ -1,4 +1,4 @@ -/* trunnel.h -- copied from Trunnel v1.5.2 +/* trunnel.h -- copied from Trunnel v1.5.3 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/feature/control/btrack.c b/src/feature/control/btrack.c index d3d12cb2b7..3ce97dc855 100644 --- a/src/feature/control/btrack.c +++ b/src/feature/control/btrack.c @@ -24,6 +24,7 @@ #include "feature/control/btrack_circuit.h" #include "feature/control/btrack_orconn.h" #include "feature/control/btrack_sys.h" +#include "lib/pubsub/pubsub.h" #include "lib/subsys/subsys.h" static int @@ -31,8 +32,6 @@ btrack_init(void) { if (btrack_orconn_init()) return -1; - if (btrack_circ_init()) - return -1; return 0; } @@ -44,10 +43,22 @@ btrack_fini(void) btrack_circ_fini(); } +static int +btrack_add_pubsub(pubsub_connector_t *connector) +{ + if (btrack_orconn_add_pubsub(connector)) + return -1; + if (btrack_circ_add_pubsub(connector)) + return -1; + + return 0; +} + const subsys_fns_t sys_btrack = { .name = "btrack", .supported = true, .level = -30, .initialize = btrack_init, .shutdown = btrack_fini, + .add_pubsub = btrack_add_pubsub, }; diff --git a/src/feature/control/btrack_circuit.c b/src/feature/control/btrack_circuit.c index dcee9e460e..2980c77ddc 100644 --- a/src/feature/control/btrack_circuit.c +++ b/src/feature/control/btrack_circuit.c @@ -109,51 +109,53 @@ btc_update_evtype(const ocirc_cevent_msg_t *msg, btc_best_t *best, return false; } +DECLARE_SUBSCRIBE(ocirc_state, btc_state_rcvr); +DECLARE_SUBSCRIBE(ocirc_cevent, btc_cevent_rcvr); +DECLARE_SUBSCRIBE(ocirc_chan, btc_chan_rcvr); + static void -btc_state_rcvr(const ocirc_state_msg_t *msg) +btc_state_rcvr(const msg_t *msg, const ocirc_state_msg_t *arg) { + (void)msg; log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" state=%d onehop=%d", - msg->gid, msg->state, msg->onehop); + arg->gid, arg->state, arg->onehop); - btc_update_state(msg, &best_any_state, "ANY"); - if (msg->onehop) + btc_update_state(arg, &best_any_state, "ANY"); + if (arg->onehop) return; - btc_update_state(msg, &best_ap_state, "AP"); + btc_update_state(arg, &best_ap_state, "AP"); } static void -btc_cevent_rcvr(const ocirc_cevent_msg_t *msg) +btc_cevent_rcvr(const msg_t *msg, const ocirc_cevent_msg_t *arg) { + (void)msg; log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" evtype=%d reason=%d onehop=%d", - msg->gid, msg->evtype, msg->reason, msg->onehop); + arg->gid, arg->evtype, arg->reason, arg->onehop); - btc_update_evtype(msg, &best_any_evtype, "ANY"); - if (msg->onehop) + btc_update_evtype(arg, &best_any_evtype, "ANY"); + if (arg->onehop) return; - btc_update_evtype(msg, &best_ap_evtype, "AP"); + btc_update_evtype(arg, &best_ap_evtype, "AP"); } static void -btc_event_rcvr(const ocirc_event_msg_t *msg) +btc_chan_rcvr(const msg_t *msg, const ocirc_chan_msg_t *arg) { - switch (msg->type) { - case OCIRC_MSGTYPE_STATE: - return btc_state_rcvr(&msg->u.state); - case OCIRC_MSGTYPE_CHAN: - log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" chan=%"PRIu64" onehop=%d", - msg->u.chan.gid, msg->u.chan.chan, msg->u.chan.onehop); - break; - case OCIRC_MSGTYPE_CEVENT: - return btc_cevent_rcvr(&msg->u.cevent); - default: - break; - } + (void)msg; + log_debug(LD_BTRACK, "CIRC gid=%"PRIu32" chan=%"PRIu64" onehop=%d", + arg->gid, arg->chan, arg->onehop); } int -btrack_circ_init(void) +btrack_circ_add_pubsub(pubsub_connector_t *connector) { - ocirc_event_subscribe(btc_event_rcvr); + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_chan)) + return -1; + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_cevent)) + return -1; + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_state)) + return -1; return 0; } diff --git a/src/feature/control/btrack_circuit.h b/src/feature/control/btrack_circuit.h index 9e06fefb07..b326c22ccf 100644 --- a/src/feature/control/btrack_circuit.h +++ b/src/feature/control/btrack_circuit.h @@ -9,7 +9,10 @@ #ifndef TOR_BTRACK_CIRCUIT_H #define TOR_BTRACK_CIRCUIT_H +#include "lib/pubsub/pubsub.h" + int btrack_circ_init(void); void btrack_circ_fini(void); +int btrack_circ_add_pubsub(pubsub_connector_t *); #endif /* !defined(TOR_BTRACK_CIRCUIT_H) */ diff --git a/src/feature/control/btrack_orconn.c b/src/feature/control/btrack_orconn.c index 93ebe8d9cc..922b542a0c 100644 --- a/src/feature/control/btrack_orconn.c +++ b/src/feature/control/btrack_orconn.c @@ -45,6 +45,11 @@ #include "feature/control/btrack_orconn_cevent.h" #include "feature/control/btrack_orconn_maps.h" #include "lib/log/log.h" +#include "lib/pubsub/pubsub.h" + +DECLARE_SUBSCRIBE(orconn_state, bto_state_rcvr); +DECLARE_SUBSCRIBE(orconn_status, bto_status_rcvr); +DECLARE_SUBSCRIBE(ocirc_chan, bto_chan_rcvr); /** Pair of a best ORCONN GID and with its state */ typedef struct bto_best_t { @@ -110,16 +115,17 @@ bto_reset_bests(void) * message comes from code in connection_or.c. **/ static void -bto_state_rcvr(const orconn_state_msg_t *msg) +bto_state_rcvr(const msg_t *msg, const orconn_state_msg_t *arg) { bt_orconn_t *bto; - bto = bto_find_or_new(msg->gid, msg->chan); + (void)msg; + bto = bto_find_or_new(arg->gid, arg->chan); log_debug(LD_BTRACK, "ORCONN gid=%"PRIu64" chan=%"PRIu64 " proxy_type=%d state=%d", - msg->gid, msg->chan, msg->proxy_type, msg->state); - bto->proxy_type = msg->proxy_type; - bto->state = msg->state; + arg->gid, arg->chan, arg->proxy_type, arg->state); + bto->proxy_type = arg->proxy_type; + bto->state = arg->state; if (bto->is_orig) bto_update_bests(bto); } @@ -130,54 +136,38 @@ bto_state_rcvr(const orconn_state_msg_t *msg) * control.c. **/ static void -bto_status_rcvr(const orconn_status_msg_t *msg) +bto_status_rcvr(const msg_t *msg, const orconn_status_msg_t *arg) { - switch (msg->status) { + (void)msg; + switch (arg->status) { case OR_CONN_EVENT_FAILED: case OR_CONN_EVENT_CLOSED: log_info(LD_BTRACK, "ORCONN DELETE gid=%"PRIu64" status=%d reason=%d", - msg->gid, msg->status, msg->reason); - return bto_delete(msg->gid); + arg->gid, arg->status, arg->reason); + return bto_delete(arg->gid); default: break; } } -/** Dispatch to individual ORCONN message handlers */ -static void -bto_event_rcvr(const orconn_event_msg_t *msg) -{ - switch (msg->type) { - case ORCONN_MSGTYPE_STATE: - return bto_state_rcvr(&msg->u.state); - case ORCONN_MSGTYPE_STATUS: - return bto_status_rcvr(&msg->u.status); - default: - tor_assert(false); - } -} - /** * Create or update a cached ORCONN state for a newly launched * connection, including whether it's launched by an origin circuit * and whether it's a one-hop circuit. **/ static void -bto_chan_rcvr(const ocirc_event_msg_t *msg) +bto_chan_rcvr(const msg_t *msg, const ocirc_chan_msg_t *arg) { bt_orconn_t *bto; - /* Ignore other kinds of origin circuit events; we don't need them */ - if (msg->type != OCIRC_MSGTYPE_CHAN) - return; - - bto = bto_find_or_new(0, msg->u.chan.chan); - if (!bto->is_orig || (bto->is_onehop && !msg->u.chan.onehop)) { + (void)msg; + bto = bto_find_or_new(0, arg->chan); + if (!bto->is_orig || (bto->is_onehop && !arg->onehop)) { log_debug(LD_BTRACK, "ORCONN LAUNCH chan=%"PRIu64" onehop=%d", - msg->u.chan.chan, msg->u.chan.onehop); + arg->chan, arg->onehop); } bto->is_orig = true; - if (!msg->u.chan.onehop) + if (!arg->onehop) bto->is_onehop = false; bto_update_bests(bto); } @@ -190,12 +180,22 @@ int btrack_orconn_init(void) { bto_init_maps(); - orconn_event_subscribe(bto_event_rcvr); - ocirc_event_subscribe(bto_chan_rcvr); return 0; } +int +btrack_orconn_add_pubsub(pubsub_connector_t *connector) +{ + if (DISPATCH_ADD_SUB(connector, orconn, orconn_state)) + return -1; + if (DISPATCH_ADD_SUB(connector, orconn, orconn_status)) + return -1; + if (DISPATCH_ADD_SUB(connector, ocirc, ocirc_chan)) + return -1; + return 0; +} + /** Clear the hash maps and reset the "best" states */ void btrack_orconn_fini(void) diff --git a/src/feature/control/btrack_orconn.h b/src/feature/control/btrack_orconn.h index f8f5c1096c..07b1b755f3 100644 --- a/src/feature/control/btrack_orconn.h +++ b/src/feature/control/btrack_orconn.h @@ -9,6 +9,8 @@ #ifndef TOR_BTRACK_ORCONN_H #define TOR_BTRACK_ORCONN_H +#include "lib/pubsub/pubsub.h" + #ifdef BTRACK_ORCONN_PRIVATE #include "ht.h" @@ -33,6 +35,7 @@ typedef struct bt_orconn_t { #endif /* defined(BTRACK_ORCONN_PRIVATE) */ int btrack_orconn_init(void); +int btrack_orconn_add_pubsub(pubsub_connector_t *); void btrack_orconn_fini(void); #endif /* !defined(TOR_BTRACK_ORCONN_H) */ diff --git a/src/feature/control/control_auth.c b/src/feature/control/control_auth.c index 49d4d415c6..a574d07b33 100644 --- a/src/feature/control/control_auth.c +++ b/src/feature/control/control_auth.c @@ -151,12 +151,8 @@ handle_control_authchallenge(control_connection_t *conn, goto fail; } if (args->kwargs == NULL || args->kwargs->next != NULL) { - /* connection_write_str_to_buf("512 AUTHCHALLENGE requires exactly " - "2 arguments.\r\n", conn); - */ - control_printf_endreply(conn, 512, - "AUTHCHALLENGE dislikes argument list %s", - escaped(args->raw_body)); + control_write_endreply(conn, 512, + "Wrong number of arguments for AUTHCHALLENGE"); goto fail; } if (strcmp(args->kwargs->key, "")) { diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index abb579bd43..ad4a4ef0af 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -703,9 +703,8 @@ handle_control_mapaddress(control_connection_t *conn, connection_buf_add(r, sz, TO_CONN(conn)); tor_free(r); } else { - const char *response = - "512 syntax error: not enough arguments to mapaddress.\r\n"; - connection_buf_add(response, strlen(response), TO_CONN(conn)); + control_write_endreply(conn, 512, "syntax error: " + "not enough arguments to mapaddress."); } SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp)); @@ -845,7 +844,7 @@ handle_control_extendcircuit(control_connection_t *conn, "addresses that are allowed by the firewall configuration; " "circuit marked for closing."); circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED); - connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn); + control_write_endreply(conn, 551, "Couldn't start circuit"); goto done; } circuit_append_new_exit(circ, info); @@ -1744,16 +1743,10 @@ handle_control_add_onion(control_connection_t *conn, goto out; } else if (!strcasecmp(arg->key, "ClientAuth")) { - char *err_msg = NULL; int created = 0; rend_authorized_client_t *client = - add_onion_helper_clientauth(arg->value, - &created, &err_msg); + add_onion_helper_clientauth(arg->value, &created, conn); if (!client) { - if (err_msg) { - connection_write_str_to_buf(err_msg, conn); - tor_free(err_msg); - } goto out; } @@ -1818,19 +1811,13 @@ handle_control_add_onion(control_connection_t *conn, add_onion_secret_key_t pk = { NULL }; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; const char *onionkey = smartlist_get(args->args, 0); if (add_onion_helper_keyarg(onionkey, discard_pk, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg) < 0) { - if (err_msg) { - connection_write_str_to_buf(err_msg, conn); - tor_free(err_msg); - } + conn) < 0) { goto out; } - tor_assert(!err_msg); /* Hidden service version 3 don't have client authentication support so if * ClientAuth was given, send back an error. */ @@ -1876,8 +1863,8 @@ handle_control_add_onion(control_connection_t *conn, char *encoded = rend_auth_encode_cookie(ac->descriptor_cookie, auth_type); tor_assert(encoded); - connection_printf_to_buf(conn, "250-ClientAuth=%s:%s\r\n", - ac->client_name, encoded); + control_printf_midreply(conn, 250, "ClientAuth=%s:%s", + ac->client_name, encoded); memwipe(encoded, 0, strlen(encoded)); tor_free(encoded); }); @@ -1930,27 +1917,30 @@ handle_control_add_onion(control_connection_t *conn, * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated * and the private key not discarded, the algorithm and serialized private key, * or NULL and an optional control protocol error message on failure. The - * caller is responsible for freeing the returned key_new_blob and err_msg. + * caller is responsible for freeing the returned key_new_blob. * * Note: The error messages returned are deliberately vague to avoid echoing * key material. + * + * Note: conn is only used for writing control replies. For testing + * purposes, it can be NULL if control_write_reply() is appropriately + * mocked. */ STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, add_onion_secret_key_t *decoded_key, int *hs_version, - char **err_msg_out) + control_connection_t *conn) { smartlist_t *key_args = smartlist_new(); crypto_pk_t *pk = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; int ret = -1; smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { - err_msg = tor_strdup("512 Invalid key type/blob\r\n"); + control_write_endreply(conn, 512, "Invalid key type/blob"); goto err; } @@ -1967,12 +1957,12 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */ pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob)); if (!pk) { - err_msg = tor_strdup("512 Failed to decode RSA key\r\n"); + control_write_endreply(conn, 512, "Failed to decode RSA key"); goto err; } if (crypto_pk_num_bits(pk) != PK_BYTES*8) { crypto_pk_free(pk); - err_msg = tor_strdup("512 Invalid RSA key size\r\n"); + control_write_endreply(conn, 512, "Invalid RSA key size"); goto err; } decoded_key->v2 = pk; @@ -1983,7 +1973,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, strlen(key_blob)) != sizeof(sk->seckey)) { tor_free(sk); - err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n"); + control_write_endreply(conn, 512, "Failed to decode ED25519-V3 key"); goto err; } decoded_key->v3 = sk; @@ -1995,15 +1985,15 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */ pk = crypto_pk_new(); if (crypto_pk_generate_key(pk)) { - tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", - key_type_rsa1024); + control_printf_endreply(conn, 551, "Failed to generate %s key", + key_type_rsa1024); goto err; } if (!discard_pk) { if (crypto_pk_base64_encode_private(pk, &key_new_blob)) { crypto_pk_free(pk); - tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", - key_type_rsa1024); + control_printf_endreply(conn, 551, "Failed to encode %s key", + key_type_rsa1024); goto err; } key_new_alg = key_type_rsa1024; @@ -2014,8 +2004,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); if (ed25519_secret_key_generate(sk, 1) < 0) { tor_free(sk); - tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", - key_type_ed25519_v3); + control_printf_endreply(conn, 551, "Failed to generate %s key", + key_type_ed25519_v3); goto err; } if (!discard_pk) { @@ -2025,8 +2015,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, sizeof(sk->seckey), 0) != (len - 1)) { tor_free(sk); tor_free(key_new_blob); - tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", - key_type_ed25519_v3); + control_printf_endreply(conn, 551, "Failed to encode %s key", + key_type_ed25519_v3); goto err; } key_new_alg = key_type_ed25519_v3; @@ -2034,11 +2024,11 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, decoded_key->v3 = sk; *hs_version = HS_VERSION_THREE; } else { - err_msg = tor_strdup("513 Invalid key type\r\n"); + control_write_endreply(conn, 513, "Invalid key type"); goto err; } } else { - err_msg = tor_strdup("513 Invalid key type\r\n"); + control_write_endreply(conn, 513, "Invalid key type"); goto err; } @@ -2052,11 +2042,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }); smartlist_free(key_args); - if (err_msg_out) { - *err_msg_out = err_msg; - } else { - tor_free(err_msg); - } *key_new_alg_out = key_new_alg; *key_new_blob_out = key_new_blob; @@ -2066,27 +2051,30 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, /** Helper function to handle parsing a ClientAuth argument to the * ADD_ONION command. Return a new rend_authorized_client_t, or NULL * and an optional control protocol error message on failure. The - * caller is responsible for freeing the returned auth_client and err_msg. + * caller is responsible for freeing the returned auth_client. * * If 'created' is specified, it will be set to 1 when a new cookie has * been generated. + * + * Note: conn is only used for writing control replies. For testing + * purposes, it can be NULL if control_write_reply() is appropriately + * mocked. */ STATIC rend_authorized_client_t * -add_onion_helper_clientauth(const char *arg, int *created, char **err_msg) +add_onion_helper_clientauth(const char *arg, int *created, + control_connection_t *conn) { int ok = 0; tor_assert(arg); tor_assert(created); - tor_assert(err_msg); - *err_msg = NULL; smartlist_t *auth_args = smartlist_new(); rend_authorized_client_t *client = tor_malloc_zero(sizeof(rend_authorized_client_t)); smartlist_split_string(auth_args, arg, ":", 0, 0); if (smartlist_len(auth_args) < 1 || smartlist_len(auth_args) > 2) { - *err_msg = tor_strdup("512 Invalid ClientAuth syntax\r\n"); + control_write_endreply(conn, 512, "Invalid ClientAuth syntax"); goto err; } client->client_name = tor_strdup(smartlist_get(auth_args, 0)); @@ -2096,7 +2084,7 @@ add_onion_helper_clientauth(const char *arg, int *created, char **err_msg) client->descriptor_cookie, NULL, &decode_err_msg) < 0) { tor_assert(decode_err_msg); - tor_asprintf(err_msg, "512 %s\r\n", decode_err_msg); + control_write_endreply(conn, 512, decode_err_msg); tor_free(decode_err_msg); goto err; } @@ -2107,7 +2095,7 @@ add_onion_helper_clientauth(const char *arg, int *created, char **err_msg) } if (!rend_valid_client_name(client->client_name)) { - *err_msg = tor_strdup("512 Invalid name in ClientAuth\r\n"); + control_write_endreply(conn, 512, "Invalid name in ClientAuth"); goto err; } diff --git a/src/feature/control/control_cmd.h b/src/feature/control/control_cmd.h index 5c3d1a1cec..4b6d54abe7 100644 --- a/src/feature/control/control_cmd.h +++ b/src/feature/control/control_cmd.h @@ -91,10 +91,11 @@ STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, add_onion_secret_key_t *decoded_key, - int *hs_version, char **err_msg_out); + int *hs_version, + control_connection_t *conn); STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg, - int *created, char **err_msg_out); + int *created, control_connection_t *conn); STATIC control_cmd_args_t *control_cmd_parse_args( const char *command, diff --git a/src/feature/control/control_proto.c b/src/feature/control/control_proto.c index 1dd62da2be..d2541e7308 100644 --- a/src/feature/control/control_proto.c +++ b/src/feature/control/control_proto.c @@ -176,8 +176,9 @@ send_control_done(control_connection_t *conn) * @param c separator character, usually ' ', '-', or '+' * @param s string */ -void -control_write_reply(control_connection_t *conn, int code, int c, const char *s) +MOCK_IMPL(void, +control_write_reply, (control_connection_t *conn, int code, int c, + const char *s)) { connection_printf_to_buf(conn, "%03d%c%s\r\n", code, c, s); } diff --git a/src/feature/control/control_proto.h b/src/feature/control/control_proto.h index 101b808d88..3182f3d415 100644 --- a/src/feature/control/control_proto.h +++ b/src/feature/control/control_proto.h @@ -21,8 +21,8 @@ size_t write_escaped_data(const char *data, size_t len, char **out); size_t read_escaped_data(const char *data, size_t len, char **out); void send_control_done(control_connection_t *conn); -void control_write_reply(control_connection_t *conn, int code, int c, - const char *s); +MOCK_DECL(void, control_write_reply, (control_connection_t *conn, int code, + int c, const char *s)); void control_vprintf_reply(control_connection_t *conn, int code, int c, const char *fmt, va_list ap) CHECK_PRINTF(4, 0); diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index cdbdf5a216..043bbfc227 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -220,7 +220,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, networkstatus_t *v3_ns) { smartlist_t *chunks = smartlist_new(); - char *packages = NULL; char fingerprint[FINGERPRINT_LEN+1]; char digest[DIGEST_LEN]; uint32_t addr; @@ -246,19 +245,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, v3_ns->server_versions); protocols_lines = format_protocols_lines_for_vote(v3_ns); - if (v3_ns->package_lines) { - smartlist_t *tmp = smartlist_new(); - SMARTLIST_FOREACH(v3_ns->package_lines, const char *, p, - if (validate_recommended_package_line(p)) - smartlist_add_asprintf(tmp, "package %s\n", p)); - smartlist_sort_strings(tmp); - packages = smartlist_join_strings(tmp, "", 0, NULL); - SMARTLIST_FOREACH(tmp, char *, cp, tor_free(cp)); - smartlist_free(tmp); - } else { - packages = tor_strdup(""); - } - /* Get shared random commitments/reveals line(s). */ shared_random_vote_str = sr_get_string_for_vote(); @@ -344,7 +330,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, "voting-delay %d %d\n" "%s%s" /* versions */ "%s" /* protocols */ - "%s" /* packages */ "known-flags %s\n" "flag-thresholds %s\n" "params %s\n" @@ -361,7 +346,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, client_versions_line, server_versions_line, protocols_lines, - packages, flags, flag_thresholds, params, @@ -460,7 +444,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, tor_free(client_versions_line); tor_free(server_versions_line); tor_free(protocols_lines); - tor_free(packages); SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); @@ -4668,15 +4651,6 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, tor_assert_nonfatal(protover_all_supported( v3_out->recommended_client_protocols, NULL)); - v3_out->package_lines = smartlist_new(); - { - config_line_t *cl; - for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { - if (validate_recommended_package_line(cl->value)) - smartlist_add_strdup(v3_out->package_lines, cl->value); - } - } - v3_out->known_flags = smartlist_new(); smartlist_split_string(v3_out->known_flags, DIRVOTE_UNIVERSAL_FLAGS, diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index 656922233e..a68d155651 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -216,9 +216,14 @@ dirserv_load_fingerprint_file(void) #define DISABLE_DISABLING_ED25519 -/** Check whether <b>router</b> has a nickname/identity key combination that - * we recognize from the fingerprint list, or an IP we automatically act on - * according to our configuration. Return the appropriate router status. +/** Check whether <b>router</b> has: + * - a nickname/identity key combination that we recognize from the fingerprint + * list, + * - an IP we automatically act on according to our configuration, + * - an appropriate version, and + * - matching pinned keys. + * + * Return the appropriate router status. * * If the status is 'FP_REJECT' and <b>msg</b> is provided, set * *<b>msg</b> to an explanation of why. */ @@ -236,7 +241,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, return FP_REJECT; } - /* Check for the more usual versions to reject a router first. */ + /* Check for the more common reasons to reject a router first. */ const uint32_t r = dirserv_get_status_impl(d, router->nickname, router->addr, router->or_port, router->platform, msg, severity); @@ -535,7 +540,7 @@ dirserv_add_multiple_descriptors(const char *desc, size_t desclen, int general = purpose == ROUTER_PURPOSE_GENERAL; tor_assert(msg); - r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */ + r=ROUTER_ADDED_SUCCESSFULLY; /* Least severe return value. */ if (!string_is_utf8_no_bom(desc, desclen)) { *msg = "descriptor(s) or extrainfo(s) not valid UTF-8 or had BOM."; @@ -551,9 +556,7 @@ dirserv_add_multiple_descriptors(const char *desc, size_t desclen, !general ? router_purpose_to_string(purpose) : "", !general ? "\n" : "")<0) { *msg = "Couldn't format annotations"; - /* XXX Not cool: we return -1 below, but (was_router_added_t)-1 is - * ROUTER_BAD_EI, which isn't what's gone wrong here. :( */ - return -1; + return ROUTER_AUTHDIR_BUG_ANNOTATIONS; } s = desc; diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c index b669e3836e..b2c7acba1a 100644 --- a/src/feature/dirauth/shared_random_state.c +++ b/src/feature/dirauth/shared_random_state.c @@ -68,7 +68,7 @@ static void disk_state_free_cb(void *); /* Array of variables that are saved to disk as a persistent state. */ static config_var_t state_vars[] = { - V(Version, UINT, "0"), + V(Version, POSINT, "0"), V(TorVersion, STRING, NULL), V(ValidAfter, ISOTIME, NULL), V(ValidUntil, ISOTIME, NULL), diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 05f9940ae6..9817113b23 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -710,6 +710,11 @@ cache_clean_v3_as_client(time_t now) MAP_DEL_CURRENT(key); entry_size = cache_get_client_entry_size(entry); bytes_removed += entry_size; + /* We just removed an old descriptor. We need to close all intro circuits + * so we don't have leftovers that can be selected while lacking a + * descriptor. We leave the rendezvous circuits opened because they could + * be in use. */ + hs_client_close_intro_circuits_from_desc(entry->desc); /* Entry is not in the cache anymore, destroy it. */ cache_client_desc_free(entry); /* Update our OOM. We didn't use the remove() function because we are in diff --git a/src/feature/hs/hs_intropoint.c b/src/feature/hs/hs_intropoint.c index 9333060e7e..6383d3ed22 100644 --- a/src/feature/hs/hs_intropoint.c +++ b/src/feature/hs/hs_intropoint.c @@ -10,6 +10,7 @@ #include "core/or/or.h" #include "app/config/config.h" +#include "core/or/channel.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/relay.h" @@ -546,6 +547,14 @@ circuit_is_suitable_for_introduce1(const or_circuit_t *circ) return 0; } + /* Disallow single hop client circuit. */ + if (circ->p_chan && channel_is_client(circ->p_chan)) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Single hop client was rejected while trying to introduce. " + "Closing circuit."); + return 0; + } + return 1; } diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 9c2debea2f..709ceff53a 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -1459,12 +1459,13 @@ router_descriptor_is_older_than,(const routerinfo_t *router, int seconds)) } /** Add <b>router</b> to the routerlist, if we don't already have it. Replace - * older entries (if any) with the same key. Note: Callers should not hold - * their pointers to <b>router</b> if this function fails; <b>router</b> - * will either be inserted into the routerlist or freed. Similarly, even - * if this call succeeds, they should not hold their pointers to - * <b>router</b> after subsequent calls with other routerinfo's -- they - * might cause the original routerinfo to get freed. + * older entries (if any) with the same key. + * + * Note: Callers should not hold their pointers to <b>router</b> if this + * function fails; <b>router</b> will either be inserted into the routerlist or + * freed. Similarly, even if this call succeeds, they should not hold their + * pointers to <b>router</b> after subsequent calls with other routerinfo's -- + * they might cause the original routerinfo to get freed. * * Returns the status for the operation. Might set *<b>msg</b> if it wants * the poster of the router to know something. diff --git a/src/feature/nodelist/routerlist.h b/src/feature/nodelist/routerlist.h index 5771ebb1ab..dc9203e015 100644 --- a/src/feature/nodelist/routerlist.h +++ b/src/feature/nodelist/routerlist.h @@ -37,9 +37,12 @@ typedef enum was_router_added_t { ROUTER_WAS_NOT_WANTED = -6, /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ - ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ - /* DOCDOC */ - ROUTER_CERTS_EXPIRED = -8 + ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'ROUTER_IS_ALREADY_KNOWN' */ + /* Some certs on this router are expired. */ + ROUTER_CERTS_EXPIRED = -8, + /* We couldn't format the annotations for this router. This is a directory + * authority bug. */ + ROUTER_AUTHDIR_BUG_ANNOTATIONS = -10 } was_router_added_t; /** How long do we avoid using a directory server after it's given us a 503? */ diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 25bb1835c2..51ced6289d 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -3113,33 +3113,22 @@ load_stats_file(const char *filename, const char *end_line, time_t now, return r; } -/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them - * with <b>ident_key</b>. - * - * If ExtraInfoStatistics is 1, also write aggregated statistics and related - * configuration data before signing. Most statistics also have an option that - * enables or disables that particular statistic. - * - * Return 0 on success, negative on failure. */ -int -extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, - crypto_pk_t *ident_key, - const ed25519_keypair_t *signing_keypair) +/** Add header strings to chunks, based on the extrainfo object extrainfo, + * and ed25519 keypair signing_keypair, if emit_ed_sigs is true. + * Helper for extrainfo_dump_to_string(). + * Returns 0 on success, negative on failure. */ +static int +extrainfo_dump_to_string_header_helper( + smartlist_t *chunks, + const extrainfo_t *extrainfo, + const ed25519_keypair_t *signing_keypair, + int emit_ed_sigs) { - const or_options_t *options = get_options(); char identity[HEX_DIGEST_LEN+1]; char published[ISO_TIME_LEN+1]; - char digest[DIGEST_LEN]; - int result; - static int write_stats_to_extrainfo = 1; - char sig[DIROBJ_MAX_SIG_LEN+1]; - char *s = NULL, *pre, *contents, *cp, *s_dup = NULL; - time_t now = time(NULL); - smartlist_t *chunks = smartlist_new(); - extrainfo_t *ei_tmp = NULL; - const int emit_ed_sigs = signing_keypair && - extrainfo->cache_info.signing_key_cert; char *ed_cert_line = NULL; + char *pre = NULL; + int rv = -1; base16_encode(identity, sizeof(identity), extrainfo->cache_info.identity_digest, DIGEST_LEN); @@ -3169,12 +3158,41 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, ed_cert_line = tor_strdup(""); } + /* This is the first chunk in the file. If the file is too big, other chunks + * are removed. So we must only add one chunk here. */ tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n", extrainfo->nickname, identity, ed_cert_line, published); smartlist_add(chunks, pre); + rv = 0; + goto done; + + err: + rv = -1; + + done: + tor_free(ed_cert_line); + return rv; +} + +/** Add pluggable transport and statistics strings to chunks, skipping + * statistics if write_stats_to_extrainfo is false. + * Helper for extrainfo_dump_to_string(). + * Can not fail. */ +static void +extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, + int write_stats_to_extrainfo) +{ + const or_options_t *options = get_options(); + char *contents = NULL; + time_t now = time(NULL); + + /* If the file is too big, these chunks are removed, starting with the last + * chunk. So each chunk must be a complete line, and the file must be valid + * after each chunk. */ + /* Add information about the pluggable transports we support, even if we * are not publishing statistics. This information is needed by BridgeDB * to distribute bridges. */ @@ -3241,34 +3259,132 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } } +} + +/** Add an ed25519 signature of chunks to chunks, using the ed25519 keypair + * signing_keypair. + * Helper for extrainfo_dump_to_string(). + * Returns 0 on success, negative on failure. */ +static int +extrainfo_dump_to_string_ed_sig_helper( + smartlist_t *chunks, + const ed25519_keypair_t *signing_keypair) +{ + char sha256_digest[DIGEST256_LEN]; + ed25519_signature_t ed_sig; + char buf[ED25519_SIG_BASE64_LEN+1]; + int rv = -1; + + /* These are two of the three final chunks in the file. If the file is too + * big, other chunks are removed. So we must only add two chunks here. */ + smartlist_add_strdup(chunks, "router-sig-ed25519 "); + crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN, + ED_DESC_SIGNATURE_PREFIX, + chunks, "", DIGEST_SHA256); + if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN, + signing_keypair) < 0) + goto err; + ed25519_signature_to_base64(buf, &ed_sig); + + smartlist_add_asprintf(chunks, "%s\n", buf); + + rv = 0; + goto done; + + err: + rv = -1; + + done: + return rv; +} + +/** Add an RSA signature of extrainfo_string to chunks, using the RSA key + * ident_key. + * Helper for extrainfo_dump_to_string(). + * Returns 0 on success, negative on failure. */ +static int +extrainfo_dump_to_string_rsa_sig_helper(smartlist_t *chunks, + crypto_pk_t *ident_key, + const char *extrainfo_string) +{ + char sig[DIROBJ_MAX_SIG_LEN+1]; + char digest[DIGEST_LEN]; + int rv = -1; + + memset(sig, 0, sizeof(sig)); + if (router_get_extrainfo_hash(extrainfo_string, strlen(extrainfo_string), + digest) < 0 || + router_append_dirobj_signature(sig, sizeof(sig), digest, DIGEST_LEN, + ident_key) < 0) { + log_warn(LD_BUG, "Could not append signature to extra-info " + "descriptor."); + goto err; + } + smartlist_add_strdup(chunks, sig); + + rv = 0; + goto done; + + err: + rv = -1; + + done: + return rv; +} + +/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them + * with <b>ident_key</b>. + * + * If ExtraInfoStatistics is 1, also write aggregated statistics and related + * configuration data before signing. Most statistics also have an option that + * enables or disables that particular statistic. + * + * Always write pluggable transport lines. + * + * Return 0 on success, negative on failure. */ +int +extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair) +{ + int result; + static int write_stats_to_extrainfo = 1; + char *s = NULL, *cp, *s_dup = NULL; + smartlist_t *chunks = smartlist_new(); + extrainfo_t *ei_tmp = NULL; + const int emit_ed_sigs = signing_keypair && + extrainfo->cache_info.signing_key_cert; + int rv = 0; + + rv = extrainfo_dump_to_string_header_helper(chunks, extrainfo, + signing_keypair, + emit_ed_sigs); + if (rv < 0) + goto err; + + extrainfo_dump_to_string_stats_helper(chunks, write_stats_to_extrainfo); if (emit_ed_sigs) { - char sha256_digest[DIGEST256_LEN]; - smartlist_add_strdup(chunks, "router-sig-ed25519 "); - crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN, - ED_DESC_SIGNATURE_PREFIX, - chunks, "", DIGEST_SHA256); - ed25519_signature_t ed_sig; - char buf[ED25519_SIG_BASE64_LEN+1]; - if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN, - signing_keypair) < 0) + rv = extrainfo_dump_to_string_ed_sig_helper(chunks, signing_keypair); + if (rv < 0) goto err; - ed25519_signature_to_base64(buf, &ed_sig); - - smartlist_add_asprintf(chunks, "%s\n", buf); } + /* This is one of the three final chunks in the file. If the file is too big, + * other chunks are removed. So we must only add one chunk here. */ smartlist_add_strdup(chunks, "router-signature\n"); s = smartlist_join_strings(chunks, "", 0, NULL); while (strlen(s) > MAX_EXTRAINFO_UPLOAD_SIZE - DIROBJ_MAX_SIG_LEN) { /* So long as there are at least two chunks (one for the initial * extra-info line and one for the router-signature), we can keep removing - * things. */ - if (smartlist_len(chunks) > 2) { - /* We remove the next-to-last element (remember, len-1 is the last - element), since we need to keep the router-signature element. */ - int idx = smartlist_len(chunks) - 2; + * things. If emit_ed_sigs is true, we also keep 2 additional chunks at the + * end for the ed25519 signature. */ + const int required_chunks = emit_ed_sigs ? 4 : 2; + if (smartlist_len(chunks) > required_chunks) { + /* We remove the next-to-last or 4th-last element (remember, len-1 is the + * last element), since we need to keep the router-signature elements. */ + int idx = smartlist_len(chunks) - required_chunks; char *e = smartlist_get(chunks, idx); smartlist_del_keeporder(chunks, idx); log_warn(LD_GENERAL, "We just generated an extra-info descriptor " @@ -3285,15 +3401,10 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } - memset(sig, 0, sizeof(sig)); - if (router_get_extrainfo_hash(s, strlen(s), digest) < 0 || - router_append_dirobj_signature(sig, sizeof(sig), digest, DIGEST_LEN, - ident_key) < 0) { - log_warn(LD_BUG, "Could not append signature to extra-info " - "descriptor."); + rv = extrainfo_dump_to_string_rsa_sig_helper(chunks, ident_key, s); + if (rv < 0) goto err; - } - smartlist_add_strdup(chunks, sig); + tor_free(s); s = smartlist_join_strings(chunks, "", 0, NULL); @@ -3329,7 +3440,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, SMARTLIST_FOREACH(chunks, char *, chunk, tor_free(chunk)); smartlist_free(chunks); tor_free(s_dup); - tor_free(ed_cert_line); extrainfo_free(ei_tmp); return result; diff --git a/src/include.am b/src/include.am index 77c126ba45..065bdc31cb 100644 --- a/src/include.am +++ b/src/include.am @@ -5,6 +5,8 @@ include src/lib/err/include.am include src/lib/cc/include.am include src/lib/ctime/include.am include src/lib/compress/include.am +include src/lib/conf/include.am +include src/lib/confmgt/include.am include src/lib/container/include.am include src/lib/crypt_ops/include.am include src/lib/defs/include.am diff --git a/src/lib/conf/.may_include b/src/lib/conf/.may_include new file mode 100644 index 0000000000..4285c3dcb8 --- /dev/null +++ b/src/lib/conf/.may_include @@ -0,0 +1,2 @@ +orconfig.h +lib/cc/*.h diff --git a/src/lib/conf/conftypes.h b/src/lib/conf/conftypes.h new file mode 100644 index 0000000000..e66ab3d5aa --- /dev/null +++ b/src/lib/conf/conftypes.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file conftypes.h + * @brief Types used to specify configurable options. + * + * This header defines the types that different modules will use in order to + * declare their configuration and state variables, and tell the configuration + * management code about those variables. From the individual module's point + * of view, its configuration and state are simply data structures. + * + * For defining new variable types, see var_type_def_st.h. + * + * For the code that manipulates variables defined via this module, see + * lib/confmgt/, especially typedvar.h and (later) structvar.h. The + * configuration manager is responsible for encoding, decoding, and + * maintaining the configuration structures used by the various modules. + * + * STATUS NOTE: This is a work in process refactoring. It is not yet possible + * for modules to define their own variables, and much of the configuration + * management code is still in src/app/config/. + **/ + +#ifndef TOR_SRC_LIB_CONF_CONFTYPES_H +#define TOR_SRC_LIB_CONF_CONFTYPES_H + +#include "lib/cc/torint.h" + +/** Enumeration of types which option values can take */ +typedef enum config_type_t { + CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */ + CONFIG_TYPE_FILENAME, /**< A filename: some prefixes get expanded. */ + CONFIG_TYPE_POSINT, /**< A non-negative integer less than MAX_INT */ + CONFIG_TYPE_INT, /**< Any integer. */ + CONFIG_TYPE_UINT64, /**< A value in range 0..UINT64_MAX */ + CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/ + CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional + * units */ + CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/ + CONFIG_TYPE_DOUBLE, /**< A floating-point value */ + CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */ + CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false, + * 1 for true, and -1 for auto */ + CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to UTC. */ + CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and + * optional whitespace. */ + CONFIG_TYPE_CSV_INTERVAL, /**< A list of strings, separated by commas and + * optional whitespace, representing intervals in + * seconds, with optional units. We allow + * multiple values here for legacy reasons, but + * ignore every value after the first. */ + CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */ + CONFIG_TYPE_LINELIST_S, /**< Uninterpreted, context-sensitive config lines, + * mixed with other keywords. */ + CONFIG_TYPE_LINELIST_V, /**< Catch-all "virtual" option to summarize + * context-sensitive config lines when fetching. + */ + // XXXX this doesn't belong at this level of abstraction. + CONFIG_TYPE_ROUTERSET, /**< A list of router names, addrs, and fps, + * parsed into a routerset_t. */ + CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */ +} config_type_t; + +#ifdef TOR_UNIT_TESTS +/** + * Union used when building in test mode typechecking the members of a type + * used with confparse.c. See CONF_CHECK_VAR_TYPE for a description of how + * it is used. */ +typedef union { + char **STRING; + char **FILENAME; + int *POSINT; /* yes, this is really an int, and not an unsigned int. For + * historical reasons, many configuration values are restricted + * to the range [0,INT_MAX], and stored in signed ints. + */ + uint64_t *UINT64; + int *INT; + int *INTERVAL; + int *MSEC_INTERVAL; + uint64_t *MEMUNIT; + double *DOUBLE; + int *BOOL; + int *AUTOBOOL; + time_t *ISOTIME; + struct smartlist_t **CSV; + int *CSV_INTERVAL; + struct config_line_t **LINELIST; + struct config_line_t **LINELIST_S; + struct config_line_t **LINELIST_V; + // XXXX this doesn't belong at this level of abstraction. + struct routerset_t **ROUTERSET; +} confparse_dummy_values_t; +#endif /* defined(TOR_UNIT_TESTS) */ + +#endif /* !defined(TOR_SRC_LIB_CONF_CONFTYPES_H) */ diff --git a/src/lib/conf/include.am b/src/lib/conf/include.am new file mode 100644 index 0000000000..25355697d2 --- /dev/null +++ b/src/lib/conf/include.am @@ -0,0 +1,4 @@ + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/lib/conf/conftypes.h diff --git a/src/lib/confmgt/.may_include b/src/lib/confmgt/.may_include new file mode 100644 index 0000000000..d85dbf6904 --- /dev/null +++ b/src/lib/confmgt/.may_include @@ -0,0 +1,9 @@ +orconfig.h +lib/cc/*.h +lib/conf/*.h +lib/confmgt/*.h +lib/container/*.h +lib/encoding/*.h +lib/log/*.h +lib/malloc/*.h +lib/string/*.h diff --git a/src/lib/confmgt/include.am b/src/lib/confmgt/include.am new file mode 100644 index 0000000000..a2c7649957 --- /dev/null +++ b/src/lib/confmgt/include.am @@ -0,0 +1,23 @@ +noinst_LIBRARIES += src/lib/libtor-confmgt.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-confmgt-testing.a +endif + +# ADD_C_FILE: INSERT SOURCES HERE. +src_lib_libtor_confmgt_a_SOURCES = \ + src/lib/confmgt/type_defs.c \ + src/lib/confmgt/typedvar.c \ + src/lib/confmgt/unitparse.c + +src_lib_libtor_confmgt_testing_a_SOURCES = \ + $(src_lib_libtor_confmgt_a_SOURCES) +src_lib_libtor_confmgt_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_confmgt_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/lib/confmgt/type_defs.h \ + src/lib/confmgt/typedvar.h \ + src/lib/confmgt/unitparse.h \ + src/lib/confmgt/var_type_def_st.h diff --git a/src/lib/confmgt/type_defs.c b/src/lib/confmgt/type_defs.c new file mode 100644 index 0000000000..62b4c1019d --- /dev/null +++ b/src/lib/confmgt/type_defs.c @@ -0,0 +1,727 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file type_defs.c + * @brief Definitions for various low-level configuration types. + * + * This module creates a number of var_type_def_t objects, to be used by + * typedvar.c in manipulating variables. + * + * The types here are common types that can be implemented with Tor's + * low-level functionality. To define new types, see var_type_def_st.h. + **/ + +#include "orconfig.h" +#include "lib/conf/conftypes.h" +#include "lib/confmgt/typedvar.h" +#include "lib/confmgt/type_defs.h" +#include "lib/confmgt/unitparse.h" + +#include "lib/cc/compat_compiler.h" +#include "lib/conf/conftypes.h" +#include "lib/container/smartlist.h" +#include "lib/encoding/confline.h" +#include "lib/encoding/time_fmt.h" +#include "lib/log/escape.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/string/parse_int.h" +#include "lib/string/printf.h" + +#include "lib/confmgt/var_type_def_st.h" + +#include <stddef.h> +#include <string.h> + +////// +// CONFIG_TYPE_STRING +// CONFIG_TYPE_FILENAME +// +// These two types are the same for now, but they have different names. +////// + +static int +string_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + char **p = (char**)target; + *p = tor_strdup(value); + return 0; +} + +static char * +string_encode(const void *value, const void *params) +{ + (void)params; + const char **p = (const char**)value; + return *p ? tor_strdup(*p) : NULL; +} + +static void +string_clear(void *value, const void *params) +{ + (void)params; + char **p = (char**)value; + tor_free(*p); // sets *p to NULL. +} + +static const var_type_fns_t string_fns = { + .parse = string_parse, + .encode = string_encode, + .clear = string_clear, +}; + +///// +// CONFIG_TYPE_INT +// CONFIG_TYPE_POSINT +// +// These types are implemented as int, possibly with a restricted range. +///// + +typedef struct int_type_params_t { + int minval; + int maxval; +} int_parse_params_t; + +static const int_parse_params_t INT_PARSE_UNRESTRICTED = { + .minval = INT_MIN, + .maxval = INT_MAX, +}; + +static const int_parse_params_t INT_PARSE_POSINT = { + .minval = 0, + .maxval = INT_MAX, +}; + +static int +int_parse(void *target, const char *value, char **errmsg, const void *params) +{ + const int_parse_params_t *pp; + if (params) { + pp = params; + } else { + pp = &INT_PARSE_UNRESTRICTED; + } + int *p = target; + int ok=0; + *p = (int)tor_parse_long(value, 10, pp->minval, pp->maxval, &ok, NULL); + if (!ok) { + tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.", + value); + return -1; + } + return 0; +} + +static char * +int_encode(const void *value, const void *params) +{ + (void)params; + int v = *(int*)value; + char *result; + tor_asprintf(&result, "%d", v); + return result; +} + +static void +int_clear(void *value, const void *params) +{ + (void)params; + *(int*)value = 0; +} + +static bool +int_ok(const void *value, const void *params) +{ + const int_parse_params_t *pp = params; + if (pp) { + int v = *(int*)value; + return pp->minval <= v && v <= pp->maxval; + } else { + return true; + } +} + +static const var_type_fns_t int_fns = { + .parse = int_parse, + .encode = int_encode, + .clear = int_clear, + .ok = int_ok, +}; + +///// +// CONFIG_TYPE_UINT64 +// +// This type is an unrestricted u64. +///// + +static int +uint64_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + uint64_t *p = target; + int ok=0; + *p = tor_parse_uint64(value, 10, 0, UINT64_MAX, &ok, NULL); + if (!ok) { + tor_asprintf(errmsg, "Integer %s is malformed or out of bounds.", + value); + return -1; + } + return 0; +} + +static char * +uint64_encode(const void *value, const void *params) +{ + (void)params; + uint64_t v = *(uint64_t*)value; + char *result; + tor_asprintf(&result, "%"PRIu64, v); + return result; +} + +static void +uint64_clear(void *value, const void *params) +{ + (void)params; + *(uint64_t*)value = 0; +} + +static const var_type_fns_t uint64_fns = { + .parse = uint64_parse, + .encode = uint64_encode, + .clear = uint64_clear, +}; + +///// +// CONFIG_TYPE_INTERVAL +// CONFIG_TYPE_MSEC_INTERVAL +// CONFIG_TYPE_MEMUNIT +// +// These types are implemented using the config_parse_units() function. +// The intervals are stored as ints, whereas memory units are stored as +// uint64_ts. +///// + +static int +units_parse_u64(void *target, const char *value, char **errmsg, + const void *params) +{ + const unit_table_t *table = params; + tor_assert(table); + uint64_t *v = (uint64_t*)target; + int ok=1; + *v = config_parse_units(value, table, &ok); + if (!ok) { + *errmsg = tor_strdup("Provided value is malformed or out of bounds."); + return -1; + } + return 0; +} + +static int +units_parse_int(void *target, const char *value, char **errmsg, + const void *params) +{ + const unit_table_t *table = params; + tor_assert(table); + int *v = (int*)target; + int ok=1; + uint64_t u64 = config_parse_units(value, table, &ok); + if (!ok) { + *errmsg = tor_strdup("Provided value is malformed or out of bounds."); + return -1; + } + if (u64 > INT_MAX) { + tor_asprintf(errmsg, "Provided value %s is too large", value); + return -1; + } + *v = (int) u64; + return 0; +} + +static bool +units_ok_int(const void *value, const void *params) +{ + (void)params; + int v = *(int*)value; + return v >= 0; +} + +static const var_type_fns_t memunit_fns = { + .parse = units_parse_u64, + .encode = uint64_encode, // doesn't use params + .clear = uint64_clear, // doesn't use params +}; + +static const var_type_fns_t interval_fns = { + .parse = units_parse_int, + .encode = int_encode, // doesn't use params + .clear = int_clear, // doesn't use params, + .ok = units_ok_int // can't use int_ok, since that expects int params. +}; + +///// +// CONFIG_TYPE_DOUBLE +// +// This is a nice simple double. +///// + +static int +double_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + double *v = (double*)target; + // XXXX This is the preexisting behavior, but we should detect errors here. + *v = atof(value); + return 0; +} + +static char * +double_encode(const void *value, const void *params) +{ + (void)params; + double v = *(double*)value; + char *result; + tor_asprintf(&result, "%f", v); + return result; +} + +static void +double_clear(void *value, const void *params) +{ + (void)params; + double *v = (double *)value; + *v = 0.0; +} + +static const var_type_fns_t double_fns = { + .parse = double_parse, + .encode = double_encode, + .clear = double_clear, +}; + +///// +// CONFIG_TYPE_BOOL +// CONFIG_TYPE_AUTOBOOL +// +// These types are implemented as a case-insensitive string-to-integer +// mapping. +///// + +typedef struct enumeration_table_t { + const char *name; + int value; +} enumeration_table_t; + +static int +enum_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + const enumeration_table_t *table = params; + int *p = (int *)target; + for (; table->name; ++table) { + if (!strcasecmp(value, table->name)) { + *p = table->value; + return 0; + } + } + tor_asprintf(errmsg, "Unrecognized value %s.", value); + return -1; +} + +static char * +enum_encode(const void *value, const void *params) +{ + int v = *(const int*)value; + const enumeration_table_t *table = params; + for (; table->name; ++table) { + if (v == table->value) + return tor_strdup(table->name); + } + return NULL; // error. +} + +static void +enum_clear(void *value, const void *params) +{ + int *p = (int*)value; + const enumeration_table_t *table = params; + tor_assert(table->name); + *p = table->value; +} + +static bool +enum_ok(const void *value, const void *params) +{ + int v = *(const int*)value; + const enumeration_table_t *table = params; + for (; table->name; ++table) { + if (v == table->value) + return true; + } + return false; +} + +static const enumeration_table_t enum_table_bool[] = { + { "0", 0 }, + { "1", 1 }, + { NULL, 0 }, +}; + +static const enumeration_table_t enum_table_autobool[] = { + { "0", 0 }, + { "1", 1 }, + { "auto", -1 }, + { NULL, 0 }, +}; + +static const var_type_fns_t enum_fns = { + .parse = enum_parse, + .encode = enum_encode, + .clear = enum_clear, + .ok = enum_ok, +}; + +///// +// CONFIG_TYPE_ISOTIME +// +// This is a time_t, encoded in ISO8601 format. +///// + +static int +time_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void) params; + time_t *p = target; + if (parse_iso_time(value, p) < 0) { + tor_asprintf(errmsg, "Invalid time %s", escaped(value)); + return -1; + } + return 0; +} + +static char * +time_encode(const void *value, const void *params) +{ + (void)params; + time_t v = *(const time_t *)value; + char *result = tor_malloc(ISO_TIME_LEN+1); + format_iso_time(result, v); + return result; +} + +static void +time_clear(void *value, const void *params) +{ + (void)params; + time_t *t = value; + *t = 0; +} + +static const var_type_fns_t time_fns = { + .parse = time_parse, + .encode = time_encode, + .clear = time_clear, +}; + +///// +// CONFIG_TYPE_CSV +// +// This type is a comma-separated list of strings, stored in a smartlist_t. +// An empty list may be encoded either as an empty smartlist, or as NULL. +///// + +static int +csv_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + (void)errmsg; + smartlist_t **sl = (smartlist_t**)target; + *sl = smartlist_new(); + smartlist_split_string(*sl, value, ",", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + return 0; +} + +static char * +csv_encode(const void *value, const void *params) +{ + (void)params; + const smartlist_t *sl = *(const smartlist_t **)value; + if (! sl) + return tor_strdup(""); + + return smartlist_join_strings(*(smartlist_t**)value, ",", 0, NULL); +} + +static void +csv_clear(void *value, const void *params) +{ + (void)params; + smartlist_t **sl = (smartlist_t**)value; + if (!*sl) + return; + SMARTLIST_FOREACH(*sl, char *, cp, tor_free(cp)); + smartlist_free(*sl); // clears pointer. +} + +static const var_type_fns_t csv_fns = { + .parse = csv_parse, + .encode = csv_encode, + .clear = csv_clear, +}; + +///// +// CONFIG_TYPE_CSV_INTERVAL +// +// This type used to be a list of time intervals, used to determine a download +// schedule. Now, only the first interval counts: everything after the first +// comma is discarded. +///// + +static int +legacy_csv_interval_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)params; + /* We used to have entire smartlists here. But now that all of our + * download schedules use exponential backoff, only the first part + * matters. */ + const char *comma = strchr(value, ','); + const char *val = value; + char *tmp = NULL; + if (comma) { + tmp = tor_strndup(val, comma - val); + val = tmp; + } + + int rv = units_parse_int(target, val, errmsg, &time_units); + tor_free(tmp); + return rv; +} + +static const var_type_fns_t legacy_csv_interval_fns = { + .parse = legacy_csv_interval_parse, + .encode = int_encode, + .clear = int_clear, +}; + +///// +// CONFIG_TYPE_LINELIST +// CONFIG_TYPE_LINELIST_S +// CONFIG_TYPE_LINELIST_V +// +// A linelist is a raw config_line_t list. Order is preserved. +// +// The LINELIST type is used for homogeneous lists, where all the lines +// have the same key. +// +// The LINELIST_S and LINELIST_V types are used for the case where multiple +// lines of different keys are kept in a single list, to preserve their +// relative order. The unified list is stored as a "virtual" variable whose +// type is LINELIST_V; the individual sublists are treated as variables of +// type LINELIST_S. +// +// A linelist may be fragile or non-fragile. Assigning a line to a fragile +// linelist replaces the list with the line. If the line has the "APPEND" +// command set on it, or if the list is non-fragile, the line is appended. +// Either way, the new list is non-fragile. +///// + +static int +linelist_kv_parse(void *target, const struct config_line_t *line, + char **errmsg, const void *params) +{ + (void)params; + (void)errmsg; + config_line_t **lines = target; + + if (*lines && (*lines)->fragile) { + if (line->command == CONFIG_LINE_APPEND) { + (*lines)->fragile = 0; + } else { + config_free_lines(*lines); // sets it to NULL + } + } + + config_line_append(lines, line->key, line->value); + return 0; +} + +static int +linelist_kv_virt_noparse(void *target, const struct config_line_t *line, + char **errmsg, const void *params) +{ + (void)target; + (void)line; + (void)params; + *errmsg = tor_strdup("Cannot assign directly to virtual option."); + return -1; +} + +static struct config_line_t * +linelist_kv_encode(const char *key, const void *value, + const void *params) +{ + (void)key; + (void)params; + config_line_t *lines = *(config_line_t **)value; + return config_lines_dup(lines); +} + +static struct config_line_t * +linelist_s_kv_encode(const char *key, const void *value, + const void *params) +{ + (void)params; + config_line_t *lines = *(config_line_t **)value; + return config_lines_dup_and_filter(lines, key); +} + +static void +linelist_clear(void *target, const void *params) +{ + (void)params; + config_line_t **lines = target; + config_free_lines(*lines); // sets it to NULL +} + +static bool +linelist_eq(const void *a, const void *b, const void *params) +{ + (void)params; + const config_line_t *lines_a = *(const config_line_t **)a; + const config_line_t *lines_b = *(const config_line_t **)b; + return config_lines_eq(lines_a, lines_b); +} + +static int +linelist_copy(void *target, const void *value, const void *params) +{ + (void)params; + config_line_t **ptr = (config_line_t **)target; + const config_line_t *val = *(const config_line_t **)value; + config_free_lines(*ptr); + *ptr = config_lines_dup(val); + return 0; +} + +static const var_type_fns_t linelist_fns = { + .kv_parse = linelist_kv_parse, + .kv_encode = linelist_kv_encode, + .clear = linelist_clear, + .eq = linelist_eq, + .copy = linelist_copy, +}; + +static const var_type_fns_t linelist_v_fns = { + .kv_parse = linelist_kv_virt_noparse, + .kv_encode = linelist_kv_encode, + .clear = linelist_clear, + .eq = linelist_eq, + .copy = linelist_copy, +}; + +static const var_type_fns_t linelist_s_fns = { + .kv_parse = linelist_kv_parse, + .kv_encode = linelist_s_kv_encode, + .clear = linelist_clear, + .eq = linelist_eq, + .copy = linelist_copy, +}; + +///// +// CONFIG_TYPE_ROUTERSET +// +// XXXX This type is not implemented here, since routerset_t is not available +// XXXX to this module. +///// + +///// +// CONFIG_TYPE_OBSOLETE +// +// Used to indicate an obsolete option. +// +// XXXX This is not a type, and should be handled at a higher level of +// XXXX abstraction. +///// + +static int +ignore_parse(void *target, const char *value, char **errmsg, + const void *params) +{ + (void)target; + (void)value; + (void)errmsg; + (void)params; + // XXXX move this to a higher level, once such a level exists. + log_warn(LD_GENERAL, "Skipping obsolete configuration option."); + return 0; +} + +static char * +ignore_encode(const void *value, const void *params) +{ + (void)value; + (void)params; + return NULL; +} + +static const var_type_fns_t ignore_fns = { + .parse = ignore_parse, + .encode = ignore_encode, +}; + +/** + * Table mapping conf_type_t values to var_type_def_t objects. + **/ +static const var_type_def_t type_definitions_table[] = { + [CONFIG_TYPE_STRING] = { "String", &string_fns, NULL }, + [CONFIG_TYPE_FILENAME] = { "Filename", &string_fns, NULL }, + [CONFIG_TYPE_INT] = { "SignedInteger", &int_fns, &INT_PARSE_UNRESTRICTED }, + [CONFIG_TYPE_POSINT] = { "Integer", &int_fns, &INT_PARSE_POSINT }, + [CONFIG_TYPE_UINT64] = { "Integer", &uint64_fns, NULL, }, + [CONFIG_TYPE_MEMUNIT] = { "DataSize", &memunit_fns, &memory_units }, + [CONFIG_TYPE_INTERVAL] = { "TimeInterval", &interval_fns, &time_units }, + [CONFIG_TYPE_MSEC_INTERVAL] = { "TimeMsecInterval", &interval_fns, + &time_msec_units }, + [CONFIG_TYPE_DOUBLE] = { "Float", &double_fns, NULL }, + [CONFIG_TYPE_BOOL] = { "Boolean", &enum_fns, &enum_table_bool }, + [CONFIG_TYPE_AUTOBOOL] = { "Boolean+Auto", &enum_fns, &enum_table_autobool }, + [CONFIG_TYPE_ISOTIME] = { "Time", &time_fns, NULL }, + [CONFIG_TYPE_CSV] = { "CommaList", &csv_fns, NULL }, + [CONFIG_TYPE_CSV_INTERVAL] = { "TimeInterval", &legacy_csv_interval_fns, + NULL }, + [CONFIG_TYPE_LINELIST] = { "LineList", &linelist_fns, NULL }, + [CONFIG_TYPE_LINELIST_S] = { "Dependent", &linelist_s_fns, NULL }, + [CONFIG_TYPE_LINELIST_V] = { "Virtual", &linelist_v_fns, NULL }, + [CONFIG_TYPE_OBSOLETE] = { "Obsolete", &ignore_fns, NULL } +}; + +/** + * Return a pointer to the var_type_def_t object for the given + * config_type_t value, or NULL if no such type definition exists. + **/ +const var_type_def_t * +lookup_type_def(config_type_t type) +{ + int t = type; + tor_assert(t >= 0); + if (t >= (int)ARRAY_LENGTH(type_definitions_table)) + return NULL; + return &type_definitions_table[t]; +} diff --git a/src/lib/confmgt/type_defs.h b/src/lib/confmgt/type_defs.h new file mode 100644 index 0000000000..ecf040529e --- /dev/null +++ b/src/lib/confmgt/type_defs.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file type_defs.h + * @brief Header for lib/confmgt/type_defs.c + **/ + +#ifndef TOR_LIB_CONFMGT_TYPE_DEFS_H +#define TOR_LIB_CONFMGT_TYPE_DEFS_H + +const struct var_type_def_t *lookup_type_def(config_type_t type); + +#endif /* !defined(TOR_LIB_CONFMGT_TYPE_DEFS_H) */ diff --git a/src/lib/confmgt/typedvar.c b/src/lib/confmgt/typedvar.c new file mode 100644 index 0000000000..fc45c44481 --- /dev/null +++ b/src/lib/confmgt/typedvar.c @@ -0,0 +1,305 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file typedvar.c + * @brief Functions for accessing a pointer as an object of a given type. + * + * These functions represent a low-level API for accessing a typed variable. + * They are used in the configuration system to examine and set fields in + * configuration objects used by individual modules. + * + * Almost no code should call these directly. + **/ + +#include "orconfig.h" +#include "lib/conf/conftypes.h" +#include "lib/confmgt/type_defs.h" +#include "lib/confmgt/typedvar.h" +#include "lib/encoding/confline.h" +#include "lib/log/escape.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/string/util_string.h" + +#include "lib/confmgt/var_type_def_st.h" + +#include <stddef.h> +#include <string.h> + +/** + * Try to parse a string in <b>value</b> that encodes an object of the type + * defined by <b>def</b>. + * + * On success, adjust the lvalue pointed to by <b>target</b> to hold that + * value, and return 0. On failure, set *<b>errmsg</b> to a newly allocated + * string holding an error message, and return -1. + **/ +int +typed_var_assign_ex(void *target, const char *value, char **errmsg, + const var_type_def_t *def) +{ + if (BUG(!def)) + return -1; + // clear old value if needed. + typed_var_free_ex(target, def); + + tor_assert(def->fns->parse); + return def->fns->parse(target, value, errmsg, def->params); +} + +/** + * Try to parse a single line from the head of<b>line</b> that encodes an + * object of the type defined in <b>def</b>. On success and failure, behave as + * typed_var_assign_ex(). + * + * All types for which keys are significant should use this function. + * + * Note that although multiple lines may be provided in <b>line</b>, + * only the first one is handled by this function. + **/ +int +typed_var_kvassign_ex(void *target, const config_line_t *line, + char **errmsg, const var_type_def_t *def) +{ + if (BUG(!def)) + return -1; + + if (def->fns->kv_parse) { + // We do _not_ free the old value here, since linelist options + // sometimes have append semantics. + return def->fns->kv_parse(target, line, errmsg, def->params); + } + + return typed_var_assign_ex(target, line->value, errmsg, def); +} + +/** + * Release storage held by a variable in <b>target</b> of type defined by + * <b>def</b>, and set <b>target</b> to a reasonable default. + **/ +void +typed_var_free_ex(void *target, const var_type_def_t *def) +{ + if (BUG(!def)) + return; + if (def->fns->clear) { + def->fns->clear(target, def->params); + } +} + +/** + * Encode a value of type <b>def</b> pointed to by <b>value</b>, and return + * its result in a newly allocated string. The string may need to be escaped. + * + * Returns NULL if this option has a NULL value, or on internal error. + **/ +char * +typed_var_encode_ex(const void *value, const var_type_def_t *def) +{ + if (BUG(!def)) + return NULL; + tor_assert(def->fns->encode); + return def->fns->encode(value, def->params); +} + +/** + * As typed_var_encode_ex(), but returns a newly allocated config_line_t + * object. The provided <b>key</b> is used as the key of the lines, unless + * the type is one (line a linelist) that encodes its own keys. + * + * This function may return a list of multiple lines. + * + * Returns NULL if there are no lines to encode, or on internal error. + */ +config_line_t * +typed_var_kvencode_ex(const char *key, const void *value, + const var_type_def_t *def) +{ + if (BUG(!def)) + return NULL; + if (def->fns->kv_encode) { + return def->fns->kv_encode(key, value, def->params); + } + char *encoded_value = typed_var_encode_ex(value, def); + if (!encoded_value) + return NULL; + + config_line_t *result = tor_malloc_zero(sizeof(config_line_t)); + result->key = tor_strdup(key); + result->value = encoded_value; + return result; +} + +/** + * Set <b>dest</b> to contain the same value as <b>src</b>. Both types + * must be as defined by <b>def</b>. + * + * Return 0 on success, and -1 on failure. + **/ +int +typed_var_copy_ex(void *dest, const void *src, const var_type_def_t *def) +{ + if (BUG(!def)) + return -1; + if (def->fns->copy) { + // If we have been provided a copy fuction, use it. + return def->fns->copy(dest, src, def); + } + + // Otherwise, encode 'src' and parse the result into 'def'. + char *enc = typed_var_encode_ex(src, def); + if (!enc) { + typed_var_free_ex(dest, def); + return 0; + } + char *err = NULL; + int rv = typed_var_assign_ex(dest, enc, &err, def); + if (BUG(rv < 0)) { + log_warn(LD_BUG, "Encoded value %s was not parseable as a %s: %s", + escaped(enc), def->name, err?err:""); + } + tor_free(err); + tor_free(enc); + return rv; +} + +/** + * Return true if <b>a</b> and <b>b</b> are semantically equivalent. + * Both types must be as defined by <b>def</b>. + **/ +bool +typed_var_eq_ex(const void *a, const void *b, const var_type_def_t *def) +{ + if (BUG(!def)) + return false; + + if (def->fns->eq) { + // Use a provided eq function if we got one. + return def->fns->eq(a, b, def->params); + } + + // Otherwise, encode the values and compare them. + char *enc_a = typed_var_encode_ex(a, def); + char *enc_b = typed_var_encode_ex(b, def); + bool eq = !strcmp_opt(enc_a,enc_b); + tor_free(enc_a); + tor_free(enc_b); + return eq; +} + +/** + * Check whether <b>value</b> encodes a valid value according to the + * type definition in <b>def</b>. + */ +bool +typed_var_ok_ex(const void *value, const var_type_def_t *def) +{ + if (BUG(!def)) + return false; + + if (def->fns->ok) + return def->fns->ok(value, def->params); + + return true; +} + +/* ===== + * The functions below take a config_type_t instead of a var_type_def_t. + * I'd like to deprecate them eventually and use var_type_def_t everywhere, + * but for now they make migration easier. + * ===== */ + +/** + * As typed_var_assign_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +int +typed_var_assign(void *target, const char *value, char **errmsg, + config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_assign_ex(target, value, errmsg, def); +} + +/** + * As typed_var_kvassign_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +int +typed_var_kvassign(void *target, const config_line_t *line, char **errmsg, + config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_kvassign_ex(target, line, errmsg, def); +} + +/** + * As typed_var_free_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +void +typed_var_free(void *target, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_free_ex(target, def); +} + +/** + * As typed_var_encode_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +char * +typed_var_encode(const void *value, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_encode_ex(value, def); +} + +/** + * As typed_var_kvencode_ex(), but look up the definition of the configuration + * type from a provided config_type_t enum. + */ +config_line_t * +typed_var_kvencode(const char *key, const void *value, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_kvencode_ex(key, value, def); +} + +/** + * As typed_var_copy_ex(), but look up the definition of the configuration type + * from a provided config_type_t enum. + */ +int +typed_var_copy(void *dest, const void *src, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_copy_ex(dest, src, def); +} + +/** + * As typed_var_eq_ex(), but look up the definition of the configuration type + * from a provided config_type_t enum. + */ +bool +typed_var_eq(const void *a, const void *b, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_eq_ex(a, b, def); +} + +/** + * As typed_var_ok_ex(), but look up the definition of the configuration type + * from a provided config_type_t enum. + */ +bool +typed_var_ok(const void *value, config_type_t type) +{ + const var_type_def_t *def = lookup_type_def(type); + return typed_var_ok_ex(value, def); +} diff --git a/src/lib/confmgt/typedvar.h b/src/lib/confmgt/typedvar.h new file mode 100644 index 0000000000..720ad54fc6 --- /dev/null +++ b/src/lib/confmgt/typedvar.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file typedvar.h + * @brief Header for lib/confmgt/typedvar.c + **/ + +#ifndef TOR_LIB_CONFMGT_TYPEDVAR_H +#define TOR_LIB_CONFMGT_TYPEDVAR_H + +#include <stdbool.h> + +enum config_type_t; +struct config_line_t; + +typedef struct var_type_fns_t var_type_fns_t; +typedef struct var_type_def_t var_type_def_t; + +int typed_var_assign(void *target, const char *value, char **errmsg, + enum config_type_t type); +void typed_var_free(void *target, enum config_type_t type); +char *typed_var_encode(const void *value, enum config_type_t type); +int typed_var_copy(void *dest, const void *src, enum config_type_t type); +bool typed_var_eq(const void *a, const void *b, enum config_type_t type); +bool typed_var_ok(const void *value, enum config_type_t type); + +int typed_var_kvassign(void *target, const struct config_line_t *line, + char **errmsg, enum config_type_t type); +struct config_line_t *typed_var_kvencode(const char *key, const void *value, + enum config_type_t type); + +int typed_var_assign_ex(void *target, const char *value, char **errmsg, + const var_type_def_t *def); +void typed_var_free_ex(void *target, const var_type_def_t *def); +char *typed_var_encode_ex(const void *value, const var_type_def_t *def); +int typed_var_copy_ex(void *dest, const void *src, const var_type_def_t *def); +bool typed_var_eq_ex(const void *a, const void *b, const var_type_def_t *def); +bool typed_var_ok_ex(const void *value, const var_type_def_t *def); + +int typed_var_kvassign_ex(void *target, const struct config_line_t *line, + char **errmsg, const var_type_def_t *def); +struct config_line_t *typed_var_kvencode_ex(const char *key, const void *value, + const var_type_def_t *def); + +#endif /* !defined(TOR_LIB_CONFMGT_TYPEDVAR_H) */ diff --git a/src/lib/confmgt/unitparse.c b/src/lib/confmgt/unitparse.c new file mode 100644 index 0000000000..c3ed8285a4 --- /dev/null +++ b/src/lib/confmgt/unitparse.c @@ -0,0 +1,206 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file unitparse.c + * @brief Functions for parsing values with units from a configuration file. + **/ + +#include "orconfig.h" +#include "lib/confmgt/unitparse.h" +#include "lib/log/log.h" +#include "lib/log/util_bug.h" +#include "lib/string/parse_int.h" +#include "lib/string/util_string.h" + +#include <string.h> + +/** Table to map the names of memory units to the number of bytes they + * contain. */ +const struct unit_table_t memory_units[] = { + { "", 1 }, + { "b", 1<< 0 }, + { "byte", 1<< 0 }, + { "bytes", 1<< 0 }, + { "kb", 1<<10 }, + { "kbyte", 1<<10 }, + { "kbytes", 1<<10 }, + { "kilobyte", 1<<10 }, + { "kilobytes", 1<<10 }, + { "kilobits", 1<<7 }, + { "kilobit", 1<<7 }, + { "kbits", 1<<7 }, + { "kbit", 1<<7 }, + { "m", 1<<20 }, + { "mb", 1<<20 }, + { "mbyte", 1<<20 }, + { "mbytes", 1<<20 }, + { "megabyte", 1<<20 }, + { "megabytes", 1<<20 }, + { "megabits", 1<<17 }, + { "megabit", 1<<17 }, + { "mbits", 1<<17 }, + { "mbit", 1<<17 }, + { "gb", 1<<30 }, + { "gbyte", 1<<30 }, + { "gbytes", 1<<30 }, + { "gigabyte", 1<<30 }, + { "gigabytes", 1<<30 }, + { "gigabits", 1<<27 }, + { "gigabit", 1<<27 }, + { "gbits", 1<<27 }, + { "gbit", 1<<27 }, + { "tb", UINT64_C(1)<<40 }, + { "tbyte", UINT64_C(1)<<40 }, + { "tbytes", UINT64_C(1)<<40 }, + { "terabyte", UINT64_C(1)<<40 }, + { "terabytes", UINT64_C(1)<<40 }, + { "terabits", UINT64_C(1)<<37 }, + { "terabit", UINT64_C(1)<<37 }, + { "tbits", UINT64_C(1)<<37 }, + { "tbit", UINT64_C(1)<<37 }, + { NULL, 0 }, +}; + +/** Table to map the names of time units to the number of seconds they + * contain. */ +const struct unit_table_t time_units[] = { + { "", 1 }, + { "second", 1 }, + { "seconds", 1 }, + { "minute", 60 }, + { "minutes", 60 }, + { "hour", 60*60 }, + { "hours", 60*60 }, + { "day", 24*60*60 }, + { "days", 24*60*60 }, + { "week", 7*24*60*60 }, + { "weeks", 7*24*60*60 }, + { "month", 2629728, }, /* about 30.437 days */ + { "months", 2629728, }, + { NULL, 0 }, +}; + +/** Table to map the names of time units to the number of milliseconds + * they contain. */ +const struct unit_table_t time_msec_units[] = { + { "", 1 }, + { "msec", 1 }, + { "millisecond", 1 }, + { "milliseconds", 1 }, + { "second", 1000 }, + { "seconds", 1000 }, + { "minute", 60*1000 }, + { "minutes", 60*1000 }, + { "hour", 60*60*1000 }, + { "hours", 60*60*1000 }, + { "day", 24*60*60*1000 }, + { "days", 24*60*60*1000 }, + { "week", 7*24*60*60*1000 }, + { "weeks", 7*24*60*60*1000 }, + { NULL, 0 }, +}; + +/** Parse a string <b>val</b> containing a number, zero or more + * spaces, and an optional unit string. If the unit appears in the + * table <b>u</b>, then multiply the number by the unit multiplier. + * On success, set *<b>ok</b> to 1 and return this product. + * Otherwise, set *<b>ok</b> to 0. + */ +uint64_t +config_parse_units(const char *val, const unit_table_t *u, int *ok) +{ + uint64_t v = 0; + double d = 0; + int use_float = 0; + char *cp; + + tor_assert(ok); + + v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp); + if (!*ok || (cp && *cp == '.')) { + d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp); + if (!*ok) + goto done; + use_float = 1; + } + + if (BUG(!cp)) { + // cp should always be non-NULL if the parse operation succeeds. + + // LCOV_EXCL_START + *ok = 1; + v = use_float ? ((uint64_t)d) : v; + goto done; + // LCOV_EXCL_STOP + } + + cp = (char*) eat_whitespace(cp); + + for ( ;u->unit;++u) { + if (!strcasecmp(u->unit, cp)) { + if (use_float) + v = (uint64_t)(u->multiplier * d); + else + v *= u->multiplier; + *ok = 1; + goto done; + } + } + log_warn(LD_CONFIG, "Unknown unit '%s'.", cp); + *ok = 0; + done: + + if (*ok) + return v; + else + return 0; +} + +/** Parse a string in the format "number unit", where unit is a unit of + * information (byte, KB, M, etc). On success, set *<b>ok</b> to true + * and return the number of bytes specified. Otherwise, set + * *<b>ok</b> to false and return 0. */ +uint64_t +config_parse_memunit(const char *s, int *ok) +{ + uint64_t u = config_parse_units(s, memory_units, ok); + return u; +} + +/** Parse a string in the format "number unit", where unit is a unit of + * time in milliseconds. On success, set *<b>ok</b> to true and return + * the number of milliseconds in the provided interval. Otherwise, set + * *<b>ok</b> to 0 and return -1. */ +int +config_parse_msec_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_msec_units, ok); + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Msec interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} + +/** Parse a string in the format "number unit", where unit is a unit of time. + * On success, set *<b>ok</b> to true and return the number of seconds in + * the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1. + */ +int +config_parse_interval(const char *s, int *ok) +{ + uint64_t r; + r = config_parse_units(s, time_units, ok); + if (r > INT_MAX) { + log_warn(LD_CONFIG, "Interval '%s' is too long", s); + *ok = 0; + return -1; + } + return (int)r; +} diff --git a/src/lib/confmgt/unitparse.h b/src/lib/confmgt/unitparse.h new file mode 100644 index 0000000000..216361a7d4 --- /dev/null +++ b/src/lib/confmgt/unitparse.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file unitparse.h + * @brief Header for lib/confmgt/unitparse.c + **/ + +#ifndef TOR_LIB_CONFMGT_UNITPARSE_H +#define TOR_LIB_CONFMGT_UNITPARSE_H + +#include <lib/cc/torint.h> + +/** Mapping from a unit name to a multiplier for converting that unit into a + * base unit. Used by config_parse_unit. */ +typedef struct unit_table_t { + const char *unit; /**< The name of the unit */ + uint64_t multiplier; /**< How many of the base unit appear in this unit */ +} unit_table_t; + +extern const unit_table_t memory_units[]; +extern const unit_table_t time_units[]; +extern const struct unit_table_t time_msec_units[]; + +uint64_t config_parse_units(const char *val, const unit_table_t *u, int *ok); + +uint64_t config_parse_memunit(const char *s, int *ok); +int config_parse_msec_interval(const char *s, int *ok); +int config_parse_interval(const char *s, int *ok); + +#endif /* !defined(TOR_LIB_CONFMGT_UNITPARSE_H) */ diff --git a/src/lib/confmgt/var_type_def_st.h b/src/lib/confmgt/var_type_def_st.h new file mode 100644 index 0000000000..d142ee1104 --- /dev/null +++ b/src/lib/confmgt/var_type_def_st.h @@ -0,0 +1,147 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file var_type_def_st.h + * @brief Structure declarations for typedvar type definitions. + * + * This structure is used for defining new variable types. If you are not + * defining a new variable type for use by the configuration management + * system, you don't need this structure. + * + * For defining new variables, see the types in conftypes.h. + * + * For data-driven access to configuration variables, see the other members of + * lib/confmgt/. + * + * STATUS NOTE: It is not yet possible to actually define new variables + * outside of config.c, and many of the types that will eventually be used + * to do so are not yet moved. This will change as more of #29211 is + * completed. + **/ + +#ifndef TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H +#define TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H + +#include <stdbool.h> + +struct config_line_t; + +/** + * A structure full of functions pointers to implement a variable type. + * + * Every type MUST implement parse or kv_parse and encode or kv_encode; + * the other functions pointers MAY be NULL. + * + * All functions here take a <b>params</b> argument, whose value + * is determined by the type definition. Two types may have the + * same functions, but differ only in parameters. + **/ +struct var_type_fns_t { + /** + * Try to parse a string in <b>value</b> that encodes an object of this + * type. On success, adjust the lvalue pointed to by <b>target</b> to hold + * that value, and return 0. On failure, set *<b>errmsg</b> to a newly + * allocated string holding an error message, and return -1. + **/ + int (*parse)(void *target, const char *value, char **errmsg, + const void *params); + /** + * Try to parse a single line from the head of<b>line</b> that encodes + * an object of this type. On success and failure, behave as in the parse() + * function. + * + * If this function is absent, it is implemented in terms of parse(). + * + * All types for which keys are significant should use this method. For + * example, a "linelist" type records the actual keys that are given + * for each line, and so should use this method. + * + * Note that although multiple lines may be provided in <b>line</b>, + * only the first one should be handled by this function. + **/ + int (*kv_parse)(void *target, const struct config_line_t *line, + char **errmsg, const void *params); + /** + * Encode a value pointed to by <b>value</b> and return its result + * in a newly allocated string. The string may need to be escaped. + * + * If this function is absent, it is implemented in terms of kv_encode(). + * + * Returns NULL if this option has a NULL value, or on internal error. + * + * Requirement: all strings generated by encode() should produce a + * semantically equivalent value when given to parse(). + **/ + char *(*encode)(const void *value, const void *params); + /** + * As encode(), but returns a newly allocated config_line_t object. The + * provided <b>key</b> is used as the key of the lines, unless the type is + * one that encodes its own keys. + * + * Unlike kv_parse(), this function will return a list of multiple lines, + * if <b>value</b> is such that it must be encoded by multiple lines. + * + * Returns NULL if there are no lines to encode, or on internal error. + * + * If this function is absent, it is implemented in terms of encode(). + **/ + struct config_line_t *(*kv_encode)(const char *key, const void *value, + const void *params); + /** + * Free all storage held in <b>arg</b>, and set <b>arg</b> to a default + * value -- usually zero or NULL. + * + * If this function is absent, the default implementation does nothing. + **/ + void (*clear)(void *arg, const void *params); + /** + * Return true if <b>a</b> and <b>b</b> hold the same value, and false + * otherwise. + * + * If this function is absent, it is implemented by encoding both a and + * b and comparing their encoded strings for equality. + **/ + bool (*eq)(const void *a, const void *b, const void *params); + /** + * Try to copy the value from <b>value</b> into <b>target</b>. + * On success return 0; on failure return -1. + * + * If this function is absent, it is implemented by encoding the value + * into a string, and then parsing it into the target. + **/ + int (*copy)(void *target, const void *value, const void *params); + /** + * Check whether <b>value</b> holds a valid value according to the + * rules of this type; return true if it does and false if it doesn't. + * + * The default implementation for this function assumes that all + * values are valid. + **/ + bool (*ok)(const void *value, const void *params); +}; + +/** + * A structure describing a type that can be manipulated with the typedvar_* + * functions. + **/ +struct var_type_def_t { + /** + * The name of this type. Should not include spaces. Used for + * debugging, log messages, and the controller API. */ + const char *name; + /** + * A function table for this type. + */ + const struct var_type_fns_t *fns; + /** + * A pointer to a value that should be passed as the 'params' argument when + * calling the functions in this type's function table. + */ + const void *params; +}; + +#endif /* !defined(TOR_LIB_CONFMGT_VAR_TYPE_DEF_ST_H) */ diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c index fdb575e03f..0d8384db13 100644 --- a/src/lib/encoding/confline.c +++ b/src/lib/encoding/confline.c @@ -256,7 +256,7 @@ config_lines_dup_and_filter(const config_line_t *inp, /** Return true iff a and b contain identical keys and values in identical * order. */ int -config_lines_eq(config_line_t *a, config_line_t *b) +config_lines_eq(const config_line_t *a, const config_line_t *b) { while (a && b) { if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value)) diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h index 56ea36bf61..12c554c6e7 100644 --- a/src/lib/encoding/confline.h +++ b/src/lib/encoding/confline.h @@ -50,7 +50,7 @@ const config_line_t *config_line_find(const config_line_t *lines, const char *key); const config_line_t *config_line_find_case(const config_line_t *lines, const char *key); -int config_lines_eq(config_line_t *a, config_line_t *b); +int config_lines_eq(const config_line_t *a, const config_line_t *b); int config_count_key(const config_line_t *a, const char *key); void config_free_lines_(config_line_t *front); #define config_free_lines(front) \ diff --git a/src/lib/evloop/.may_include b/src/lib/evloop/.may_include index 273de7bb94..54aa75fbff 100644 --- a/src/lib/evloop/.may_include +++ b/src/lib/evloop/.may_include @@ -8,6 +8,7 @@ lib/log/*.h lib/malloc/*.h lib/net/*.h lib/string/*.h +lib/subsys/*.h lib/testsupport/*.h lib/thread/*.h lib/time/*.h diff --git a/src/lib/evloop/evloop_sys.c b/src/lib/evloop/evloop_sys.c new file mode 100644 index 0000000000..56641a3175 --- /dev/null +++ b/src/lib/evloop/evloop_sys.c @@ -0,0 +1,49 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file evloop_sys.c + * @brief Subsystem definition for the event loop module + **/ + +#include "orconfig.h" +#include "lib/subsys/subsys.h" +#include "lib/evloop/compat_libevent.h" +#include "lib/evloop/evloop_sys.h" +#include "lib/log/log.h" + +static int +subsys_evloop_initialize(void) +{ + if (tor_init_libevent_rng() < 0) { + log_warn(LD_NET, "Problem initializing libevent RNG."); + return -1; + } + return 0; +} + +static void +subsys_evloop_postfork(void) +{ +#ifdef TOR_UNIT_TESTS + tor_libevent_postfork(); +#endif +} + +static void +subsys_evloop_shutdown(void) +{ + tor_libevent_free_all(); +} + +const struct subsys_fns_t sys_evloop = { + .name = "evloop", + .supported = true, + .level = -20, + .initialize = subsys_evloop_initialize, + .shutdown = subsys_evloop_shutdown, + .postfork = subsys_evloop_postfork, +}; diff --git a/src/lib/evloop/evloop_sys.h b/src/lib/evloop/evloop_sys.h new file mode 100644 index 0000000000..e6155c25b0 --- /dev/null +++ b/src/lib/evloop/evloop_sys.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file evloop_sys.h + * @brief Declare subsystem object for the event loop module. + **/ + +#ifndef TOR_LIB_EVLOOP_EVLOOP_SYS_H +#define TOR_LIB_EVLOOP_EVLOOP_SYS_H + +extern const struct subsys_fns_t sys_evloop; + +#endif /* !defined(TOR_LIB_EVLOOP_EVLOOP_SYS_H) */ diff --git a/src/lib/evloop/include.am b/src/lib/evloop/include.am index 6595b3a34b..41cd2f45c5 100644 --- a/src/lib/evloop/include.am +++ b/src/lib/evloop/include.am @@ -8,6 +8,7 @@ endif # ADD_C_FILE: INSERT SOURCES HERE. src_lib_libtor_evloop_a_SOURCES = \ src/lib/evloop/compat_libevent.c \ + src/lib/evloop/evloop_sys.c \ src/lib/evloop/procmon.c \ src/lib/evloop/timers.c \ src/lib/evloop/token_bucket.c \ @@ -21,6 +22,7 @@ src_lib_libtor_evloop_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ src/lib/evloop/compat_libevent.h \ + src/lib/evloop/evloop_sys.h \ src/lib/evloop/procmon.h \ src/lib/evloop/timers.h \ src/lib/evloop/token_bucket.h \ diff --git a/src/lib/evloop/token_bucket.c b/src/lib/evloop/token_bucket.c index ee6d631e3b..ec62d1b018 100644 --- a/src/lib/evloop/token_bucket.c +++ b/src/lib/evloop/token_bucket.c @@ -256,3 +256,55 @@ token_bucket_rw_dec(token_bucket_rw_t *bucket, flags |= TB_WRITE; return flags; } + +/** Initialize a token bucket in <b>bucket</b>, set up to allow <b>rate</b> + * per second, with a maximum burst of <b>burst</b>. The bucket is created + * such that <b>now_ts</b> is the current timestamp. The bucket starts out + * full. */ +void +token_bucket_ctr_init(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst, uint32_t now_ts) +{ + memset(bucket, 0, sizeof(token_bucket_ctr_t)); + token_bucket_ctr_adjust(bucket, rate, burst); + token_bucket_ctr_reset(bucket, now_ts); +} + +/** Change the configured rate and burst of the given token bucket object in + * <b>bucket</b>. */ +void +token_bucket_ctr_adjust(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst) +{ + token_bucket_cfg_init(&bucket->cfg, rate, burst); + token_bucket_raw_adjust(&bucket->counter, &bucket->cfg); +} + +/** Reset <b>bucket</b> to be full, as of timestamp <b>now_ts</b>. */ +void +token_bucket_ctr_reset(token_bucket_ctr_t *bucket, uint32_t now_ts) +{ + token_bucket_raw_reset(&bucket->counter, &bucket->cfg); + bucket->last_refilled_at_timestamp = now_ts; +} + +/** Refill <b>bucket</b> as appropriate, given that the current timestamp is + * <b>now_ts</b>. */ +void +token_bucket_ctr_refill(token_bucket_ctr_t *bucket, uint32_t now_ts) +{ + const uint32_t elapsed_ticks = + (now_ts - bucket->last_refilled_at_timestamp); + if (elapsed_ticks > UINT32_MAX-(300*1000)) { + /* Either about 48 days have passed since the last refill, or the + * monotonic clock has somehow moved backwards. (We're looking at you, + * Windows.). We accept up to a 5 minute jump backwards as + * "unremarkable". + */ + return; + } + + token_bucket_raw_refill_steps(&bucket->counter, &bucket->cfg, + elapsed_ticks); + bucket->last_refilled_at_timestamp = now_ts; +} diff --git a/src/lib/evloop/token_bucket.h b/src/lib/evloop/token_bucket.h index 1ce6f1bf94..dde9bd65a4 100644 --- a/src/lib/evloop/token_bucket.h +++ b/src/lib/evloop/token_bucket.h @@ -103,6 +103,35 @@ token_bucket_rw_get_write(const token_bucket_rw_t *bucket) return token_bucket_raw_get(&bucket->write_bucket); } +/** + * A specialized bucket containing a single counter. + */ + +typedef struct token_bucket_ctr_t { + token_bucket_cfg_t cfg; + token_bucket_raw_t counter; + uint32_t last_refilled_at_timestamp; +} token_bucket_ctr_t; + +void token_bucket_ctr_init(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst, uint32_t now_ts); +void token_bucket_ctr_adjust(token_bucket_ctr_t *bucket, uint32_t rate, + uint32_t burst); +void token_bucket_ctr_reset(token_bucket_ctr_t *bucket, uint32_t now_ts); +void token_bucket_ctr_refill(token_bucket_ctr_t *bucket, uint32_t now_ts); + +static inline bool +token_bucket_ctr_dec(token_bucket_ctr_t *bucket, ssize_t n) +{ + return token_bucket_raw_dec(&bucket->counter, n); +} + +static inline size_t +token_bucket_ctr_get(const token_bucket_ctr_t *bucket) +{ + return token_bucket_raw_get(&bucket->counter); +} + #ifdef TOKEN_BUCKET_PRIVATE /* To avoid making the rates too small, we consider units of "steps", diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 546af800a9..0a2c84caf2 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -373,7 +373,8 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate) * * If <b>accept_regular</b> is set and the address is in neither recognized * reverse lookup hostname format, try parsing the address as a regular - * IPv4 or IPv6 address too. + * IPv4 or IPv6 address too. This mode will accept IPv6 addresses with or + * without square brackets. */ int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address, @@ -1187,17 +1188,22 @@ fmt_addr32(uint32_t addr) } /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string - * may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by - * square brackets. + * may be an IPv4 address, or an IPv6 address surrounded by square brackets. * - * Return an address family on success, or -1 if an invalid address string is - * provided. */ -int -tor_addr_parse(tor_addr_t *addr, const char *src) + * If <b>allow_ipv6_without_brackets</b> is true, also allow IPv6 addresses + * without brackets. + * + * Always rejects IPv4 addresses with brackets. + * + * Returns an address family on success, or -1 if an invalid address string is + * provided. */ +static int +tor_addr_parse_impl(tor_addr_t *addr, const char *src, + bool allow_ipv6_without_brackets) { /* Holds substring of IPv6 address after removing square brackets */ char *tmp = NULL; - int result; + int result = -1; struct in_addr in_tmp; struct in6_addr in6_tmp; int brackets_detected = 0; @@ -1211,21 +1217,46 @@ tor_addr_parse(tor_addr_t *addr, const char *src) src = tmp = tor_strndup(src+1, strlen(src)-2); } - if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) { - result = AF_INET6; - tor_addr_from_in6(addr, &in6_tmp); - } else if (!brackets_detected && - tor_inet_pton(AF_INET, src, &in_tmp) > 0) { - result = AF_INET; - tor_addr_from_in(addr, &in_tmp); - } else { - result = -1; + /* Try to parse an IPv6 address if it has brackets, or if IPv6 addresses + * without brackets are allowed */ + if (brackets_detected || allow_ipv6_without_brackets) { + if (tor_inet_pton(AF_INET6, src, &in6_tmp) > 0) { + result = AF_INET6; + tor_addr_from_in6(addr, &in6_tmp); + } + } + + /* Try to parse an IPv4 address without brackets */ + if (!brackets_detected) { + if (tor_inet_pton(AF_INET, src, &in_tmp) > 0) { + result = AF_INET; + tor_addr_from_in(addr, &in_tmp); + } + } + + /* Clear the address on error, to avoid returning uninitialised or partly + * parsed data. + */ + if (result == -1) { + memset(addr, 0, sizeof(tor_addr_t)); } tor_free(tmp); return result; } +/** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string + * may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by + * square brackets. + * + * Returns an address family on success, or -1 if an invalid address string is + * provided. */ +int +tor_addr_parse(tor_addr_t *addr, const char *src) +{ + return tor_addr_parse_impl(addr, src, 1); +} + #ifdef HAVE_IFADDRS_TO_SMARTLIST /* * Convert a linked list consisting of <b>ifaddrs</b> structures @@ -1718,6 +1749,11 @@ get_interface_address6_list,(int severity, * form "ip" or "ip:0". Otherwise, accept those forms, and set * *<b>port_out</b> to <b>default_port</b>. * + * This function accepts: + * - IPv6 address and port, when the IPv6 address is in square brackets, + * - IPv6 address with square brackets, + * - IPv6 address without square brackets. + * * Return 0 on success, -1 on failure. */ int tor_addr_port_parse(int severity, const char *addrport, @@ -1727,6 +1763,7 @@ tor_addr_port_parse(int severity, const char *addrport, int retval = -1; int r; char *addr_tmp = NULL; + bool has_port; tor_assert(addrport); tor_assert(address_out); @@ -1736,28 +1773,47 @@ tor_addr_port_parse(int severity, const char *addrport, if (r < 0) goto done; - if (!*port_out) { + has_port = !! *port_out; + /* If there's no port, use the default port, or fail if there is no default + */ + if (!has_port) { if (default_port >= 0) *port_out = default_port; else goto done; } - /* make sure that address_out is an IP address */ - if (tor_addr_parse(address_out, addr_tmp) < 0) + /* Make sure that address_out is an IP address. + * If there is no port in addrport, allow IPv6 addresses without brackets. */ + if (tor_addr_parse_impl(address_out, addr_tmp, !has_port) < 0) goto done; retval = 0; done: + /* Clear the address and port on error, to avoid returning uninitialised or + * partly parsed data. + */ + if (retval == -1) { + memset(address_out, 0, sizeof(tor_addr_t)); + *port_out = 0; + } tor_free(addr_tmp); return retval; } /** Given an address of the form "host[:port]", try to divide it into its host - * and port portions, setting *<b>address_out</b> to a newly allocated string - * holding the address portion and *<b>port_out</b> to the port (or 0 if no - * port is given). Return 0 on success, -1 on failure. */ + * and port portions. + * + * Like tor_addr_port_parse(), this function accepts: + * - IPv6 address and port, when the IPv6 address is in square brackets, + * - IPv6 address with square brackets, + * - IPv6 address without square brackets. + * + * Sets *<b>address_out</b> to a newly allocated string holding the address + * portion, and *<b>port_out</b> to the port (or 0 if no port is given). + * + * Return 0 on success, -1 on failure. */ int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out) @@ -1766,8 +1822,11 @@ tor_addr_port_split(int severity, const char *addrport, tor_assert(addrport); tor_assert(address_out); tor_assert(port_out); + /* We need to check for IPv6 manually because the logic below doesn't - * do a good job on IPv6 addresses that lack a port. */ + * do a good job on IPv6 addresses that lack a port. + * If an IPv6 address without square brackets is ambiguous, it gets parsed + * here as an address, rather than address:port. */ if (tor_addr_parse(&a_tmp, addrport) == AF_INET6) { *port_out = 0; *address_out = tor_strdup(addrport); @@ -1807,8 +1866,7 @@ tor_addr_port_split(int severity, const char *addrport, tor_free(address_); } - if (port_out) - *port_out = ok ? ((uint16_t) port_) : 0; + *port_out = ok ? ((uint16_t) port_) : 0; return ok ? 0 : -1; } diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c index 2dda491d14..e8d7d0d94d 100644 --- a/src/lib/net/resolve.c +++ b/src/lib/net/resolve.c @@ -35,6 +35,8 @@ * *<b>addr</b> to the proper IP address, in host byte order. Returns 0 * on success, -1 on failure; 1 on transient failure. * + * This function only accepts IPv4 addresses. + * * (This function exists because standard windows gethostbyname * doesn't treat raw IP addresses properly.) */ @@ -45,6 +47,11 @@ tor_lookup_hostname,(const char *name, uint32_t *addr)) tor_addr_t myaddr; int ret; + if (BUG(!addr)) + return -1; + + *addr = 0; + if ((ret = tor_addr_lookup(name, AF_INET, &myaddr))) return ret; @@ -56,12 +63,125 @@ tor_lookup_hostname,(const char *name, uint32_t *addr)) return -1; } +#ifdef HAVE_GETADDRINFO + +/* Host lookup helper for tor_addr_lookup(), when getaddrinfo() is + * available on this system. + * + * See tor_addr_lookup() for details. + */ +static int +tor_addr_lookup_host_getaddrinfo(const char *name, + uint16_t family, + tor_addr_t *addr) +{ + int err; + struct addrinfo *res=NULL, *res_p; + struct addrinfo *best=NULL; + struct addrinfo hints; + int result = -1; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + err = tor_getaddrinfo(name, NULL, &hints, &res); + /* The check for 'res' here shouldn't be necessary, but it makes static + * analysis tools happy. */ + if (!err && res) { + best = NULL; + for (res_p = res; res_p; res_p = res_p->ai_next) { + if (family == AF_UNSPEC) { + if (res_p->ai_family == AF_INET) { + best = res_p; + break; + } else if (res_p->ai_family == AF_INET6 && !best) { + best = res_p; + } + } else if (family == res_p->ai_family) { + best = res_p; + break; + } + } + if (!best) + best = res; + if (best->ai_family == AF_INET) { + tor_addr_from_in(addr, + &((struct sockaddr_in*)best->ai_addr)->sin_addr); + result = 0; + } else if (best->ai_family == AF_INET6) { + tor_addr_from_in6(addr, + &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); + result = 0; + } + tor_freeaddrinfo(res); + return result; + } + return (err == EAI_AGAIN) ? 1 : -1; +} + +#else /* !(defined(HAVE_GETADDRINFO)) */ + +/* Host lookup helper for tor_addr_lookup(), which calls getaddrinfo(). + * Used when gethostbyname() is not available on this system. + * + * See tor_addr_lookup() for details. + */ +static int +tor_addr_lookup_host_gethostbyname(const char *name, + tor_addr_t *addr) +{ + struct hostent *ent; + int err; +#ifdef HAVE_GETHOSTBYNAME_R_6_ARG + char buf[2048]; + struct hostent hostent; + int r; + r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err); +#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) + char buf[2048]; + struct hostent hostent; + ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err); +#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG) + struct hostent_data data; + struct hostent hent; + memset(&data, 0, sizeof(data)); + err = gethostbyname_r(name, &hent, &data); + ent = err ? NULL : &hent; +#else + ent = gethostbyname(name); +#ifdef _WIN32 + err = WSAGetLastError(); +#else + err = h_errno; +#endif /* defined(_WIN32) */ +#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */ + if (ent) { + if (ent->h_addrtype == AF_INET) { + tor_addr_from_in(addr, (struct in_addr*) ent->h_addr); + } else if (ent->h_addrtype == AF_INET6) { + tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr); + } else { + tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type + } + return 0; + } +#ifdef _WIN32 + return (err == WSATRY_AGAIN) ? 1 : -1; +#else + return (err == TRY_AGAIN) ? 1 : -1; +#endif +} + +#endif /* defined(HAVE_GETADDRINFO) */ + /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set * *<b>addr</b> to the proper IP address and family. The <b>family</b> * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a * <i>preferred</i> family, though another one may be returned if only one * family is implemented for this address. * + * Like tor_addr_parse(), this function accepts IPv6 addresses with or without + * square brackets. + * * Return 0 on success, -1 on failure; 1 on transient failure. */ MOCK_IMPL(int, @@ -70,169 +190,134 @@ tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr)) /* Perhaps eventually this should be replaced by a tor_getaddrinfo or * something. */ - struct in_addr iaddr; - struct in6_addr iaddr6; + int parsed_family = 0; + int result = -1; + tor_assert(name); tor_assert(addr); tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC); + if (!*name) { /* Empty address is an error. */ - return -1; - } else if (tor_inet_pton(AF_INET, name, &iaddr)) { - /* It's an IPv4 IP. */ - if (family == AF_INET6) - return -1; - tor_addr_from_in(addr, &iaddr); - return 0; - } else if (tor_inet_pton(AF_INET6, name, &iaddr6)) { - if (family == AF_INET) - return -1; - tor_addr_from_in6(addr, &iaddr6); - return 0; + goto permfail; + } + + /* Is it an IP address? */ + parsed_family = tor_addr_parse(addr, name); + + if (parsed_family >= 0) { + /* If the IP address family matches, or was unspecified */ + if (parsed_family == family || family == AF_UNSPEC) { + goto success; + } else { + goto permfail; + } } else { + /* Clear the address after a failed tor_addr_parse(). */ + memset(addr, 0, sizeof(tor_addr_t)); #ifdef HAVE_GETADDRINFO - int err; - struct addrinfo *res=NULL, *res_p; - struct addrinfo *best=NULL; - struct addrinfo hints; - int result = -1; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - err = tor_getaddrinfo(name, NULL, &hints, &res); - /* The check for 'res' here shouldn't be necessary, but it makes static - * analysis tools happy. */ - if (!err && res) { - best = NULL; - for (res_p = res; res_p; res_p = res_p->ai_next) { - if (family == AF_UNSPEC) { - if (res_p->ai_family == AF_INET) { - best = res_p; - break; - } else if (res_p->ai_family == AF_INET6 && !best) { - best = res_p; - } - } else if (family == res_p->ai_family) { - best = res_p; - break; - } - } - if (!best) - best = res; - if (best->ai_family == AF_INET) { - tor_addr_from_in(addr, - &((struct sockaddr_in*)best->ai_addr)->sin_addr); - result = 0; - } else if (best->ai_family == AF_INET6) { - tor_addr_from_in6(addr, - &((struct sockaddr_in6*)best->ai_addr)->sin6_addr); - result = 0; - } - tor_freeaddrinfo(res); - return result; - } - return (err == EAI_AGAIN) ? 1 : -1; + result = tor_addr_lookup_host_getaddrinfo(name, family, addr); + goto done; #else /* !(defined(HAVE_GETADDRINFO)) */ - struct hostent *ent; - int err; -#ifdef HAVE_GETHOSTBYNAME_R_6_ARG - char buf[2048]; - struct hostent hostent; - int r; - r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err); -#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) - char buf[2048]; - struct hostent hostent; - ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err); -#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG) - struct hostent_data data; - struct hostent hent; - memset(&data, 0, sizeof(data)); - err = gethostbyname_r(name, &hent, &data); - ent = err ? NULL : &hent; -#else - ent = gethostbyname(name); -#ifdef _WIN32 - err = WSAGetLastError(); -#else - err = h_errno; -#endif -#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */ - if (ent) { - if (ent->h_addrtype == AF_INET) { - tor_addr_from_in(addr, (struct in_addr*) ent->h_addr); - } else if (ent->h_addrtype == AF_INET6) { - tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr); - } else { - tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type - } - return 0; - } -#ifdef _WIN32 - return (err == WSATRY_AGAIN) ? 1 : -1; -#else - return (err == TRY_AGAIN) ? 1 : -1; -#endif + result = tor_addr_lookup_host_gethostbyname(name, addr); + goto done; #endif /* defined(HAVE_GETADDRINFO) */ } + + /* If we weren't successful, and haven't already set the result, + * assume it's a permanent failure */ + permfail: + result = -1; + goto done; + success: + result = 0; + + /* We have set the result, now it's time to clean up */ + done: + if (result) { + /* Clear the address on error */ + memset(addr, 0, sizeof(tor_addr_t)); + } + return result; } /** Parse an address or address-port combination from <b>s</b>, resolve the * address as needed, and put the result in <b>addr_out</b> and (optionally) - * <b>port_out</b>. Return 0 on success, negative on failure. */ + * <b>port_out</b>. + * + * Like tor_addr_port_parse(), this function accepts: + * - IPv6 address and port, when the IPv6 address is in square brackets, + * - IPv6 address with square brackets, + * - IPv6 address without square brackets. + * + * Return 0 on success, negative on failure. */ int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out) { - const char *port; tor_addr_t addr; - uint16_t portval; + uint16_t portval = 0; char *tmp = NULL; + int rv = 0; + int result; tor_assert(s); tor_assert(addr_out); s = eat_whitespace(s); - if (*s == '[') { - port = strstr(s, "]"); - if (!port) - goto err; - tmp = tor_strndup(s+1, port-(s+1)); - port = port+1; - if (*port == ':') - port++; - else - port = NULL; - } else { - port = strchr(s, ':'); - if (port) - tmp = tor_strndup(s, port-s); - else - tmp = tor_strdup(s); - if (port) - ++port; + /* Try parsing s as an address:port first, so we don't have to duplicate + * the logic that rejects IPv6:Port with no square brackets. */ + rv = tor_addr_port_parse(LOG_WARN, s, &addr, &portval, 0); + /* That was easy, no DNS required. */ + if (rv == 0) + goto success; + + /* Now let's check for malformed IPv6 addresses and ports: + * tor_addr_port_parse() requires squared brackes if there is a port, + * and we want tor_addr_port_lookup() to have the same requirement. + * But we strip the port using tor_addr_port_split(), so tor_addr_lookup() + * only sees the address, and will accept it without square brackets. */ + int family = tor_addr_parse(&addr, s); + /* If tor_addr_parse() succeeds where tor_addr_port_parse() failed, we need + * to reject this address as malformed. */ + if (family >= 0) { + /* Double-check it's an IPv6 address. If not, we have a parsing bug. + */ + tor_assertf_nonfatal(family == AF_INET6, + "Wrong family: %d (should be IPv6: %d) which " + "failed IP:port parsing, but passed IP parsing. " + "input string: '%s'; parsed address: '%s'.", + family, AF_INET6, s, fmt_addr(&addr)); + goto err; } - if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) + /* Now we have a hostname. Let's split off the port, if any. */ + rv = tor_addr_port_split(LOG_WARN, s, &tmp, &portval); + if (rv < 0) goto err; - tor_free(tmp); - if (port) { - portval = (int) tor_parse_long(port, 10, 1, 65535, NULL, NULL); - if (!portval) - goto err; - } else { - portval = 0; - } + /* And feed the hostname to the lookup function. */ + if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0) + goto err; + success: if (port_out) *port_out = portval; tor_addr_copy(addr_out, &addr); + result = 0; + goto done; - return 0; err: + /* Clear the address and port on error */ + memset(addr_out, 0, sizeof(tor_addr_t)); + if (port_out) + *port_out = 0; + result = -1; + + /* We have set the result, now it's time to clean up */ + done: tor_free(tmp); - return -1; + return result; } #ifdef USE_SANDBOX_GETADDRINFO diff --git a/src/lib/pubsub/pubsub_check.c b/src/lib/pubsub/pubsub_check.c index a3c22d4f25..bf1196df2c 100644 --- a/src/lib/pubsub/pubsub_check.c +++ b/src/lib/pubsub/pubsub_check.c @@ -172,34 +172,20 @@ pubsub_cfg_dump(const pubsub_cfg_t *cfg, int severity, const char *prefix) /** * Helper: fill a bitarray <b>out</b> with entries corresponding to the - * subsystems listed in <b>items</b>. If any subsystem is listed more than - * once, log a warning. Return 0 on success, -1 on failure. + * subsystems listed in <b>items</b>. **/ -static int +static void get_message_bitarray(const pubsub_adjmap_t *map, - message_id_t msg, const smartlist_t *items, - const char *operation, bitarray_t **out) { - bool ok = true; *out = bitarray_init_zero((unsigned)map->n_subsystems); if (! items) - return 0; + return; SMARTLIST_FOREACH_BEGIN(items, const pubsub_cfg_t *, cfg) { - if (bitarray_is_set(*out, cfg->subsys)) { - log_warn(LD_MESG|LD_BUG, - "Message \"%s\" is configured to be %s by subsystem " - "\"%s\" more than once.", - get_message_id_name(msg), operation, - get_subsys_id_name(cfg->subsys)); - ok = false; - } bitarray_set(*out, cfg->subsys); } SMARTLIST_FOREACH_END(cfg); - - return ok ? 0 : -1; } /** @@ -222,10 +208,8 @@ lint_message_graph(const pubsub_adjmap_t *map, bitarray_t *subscribed_by = NULL; bool ok = true; - if (get_message_bitarray(map, msg, pub, "published", &published_by) < 0) - ok = false; - if (get_message_bitarray(map, msg, sub, "subscribed", &subscribed_by) < 0) - ok = false; + get_message_bitarray(map, pub, &published_by); + get_message_bitarray(map, sub, &subscribed_by); /* Check whether any subsystem is publishing and subscribing the same * message. [??] diff --git a/src/test/include.am b/src/test/include.am index 85f9c9f880..0ec4d96ad4 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -120,6 +120,7 @@ src_test_test_SOURCES += \ src/test/test_circuitstats.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ + src/test/test_confparse.c \ src/test/test_connection.c \ src/test/test_conscache.c \ src/test/test_consdiff.c \ @@ -193,6 +194,7 @@ src_test_test_SOURCES += \ src/test/test_status.c \ src/test/test_storagedir.c \ src/test/test_threads.c \ + src/test/test_token_bucket.c \ src/test/test_tortls.c \ src/test/test_util.c \ src/test/test_util_format.c \ diff --git a/src/test/test.c b/src/test/test.c index cac98dd839..266b7454a3 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -840,6 +840,7 @@ struct testgroup_t testgroups[] = { { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, + { "config/parse/", confparse_tests }, { "connection/", connection_tests }, { "conscache/", conscache_tests }, { "consdiff/", consdiff_tests }, @@ -916,6 +917,7 @@ struct testgroup_t testgroups[] = { { "socks/", socks_tests }, { "status/" , status_tests }, { "storagedir/", storagedir_tests }, + { "token_bucket/", token_bucket_tests }, { "tortls/", tortls_tests }, #ifndef ENABLE_NSS { "tortls/openssl/", tortls_openssl_tests }, diff --git a/src/test/test.h b/src/test/test.h index 167fd090ac..322716a9ab 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -197,6 +197,7 @@ extern struct testcase_t circuitstats_tests[]; extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; +extern struct testcase_t confparse_tests[]; extern struct testcase_t connection_tests[]; extern struct testcase_t conscache_tests[]; extern struct testcase_t consdiff_tests[]; @@ -272,6 +273,7 @@ extern struct testcase_t sr_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t thread_tests[]; +extern struct testcase_t token_bucket_tests[]; extern struct testcase_t tortls_openssl_tests[]; extern struct testcase_t tortls_tests[]; extern struct testcase_t util_format_tests[]; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 05d8bf6c7b..0f50a43615 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -724,155 +724,570 @@ test_addr_ip6_helpers(void *arg) ; } +/* Test that addr_str successfully parses, and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str. + */ +#define TEST_ADDR_PARSE_FMT(addr_str, expect_family, fmt_decorated, \ + expect_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_parse(&addr, addr_str); \ + tt_int_op(r, OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + STMT_END + +/* Test that addr_str fails to parse, and: + * - the returned address is null. + */ +#define TEST_ADDR_PARSE_XFAIL(addr_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + r = tor_addr_parse(&addr, addr_str); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that addr_port_str and default_port successfully parse, and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str, + * - the port is expect_port. + */ +#define TEST_ADDR_PORT_PARSE_FMT(addr_port_str, default_port, expect_family, \ + fmt_decorated, expect_str, expect_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \ + default_port); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + tt_int_op(port, OP_EQ, expect_port); \ + STMT_END + +/* Test that addr_port_str and default_port fail to parse, and: + * - the returned address is null, + * - the returned port is 0. + */ +#define TEST_ADDR_PORT_PARSE_XFAIL(addr_port_str, default_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \ + default_port); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + STMT_END + +/* Test that addr_str successfully parses as an IPv4 address using + * tor_lookup_hostname(), and: + * - the fmt_addr32() of the result is expect_str. + */ +#define TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, expect_str) \ + STMT_BEGIN \ + int r; \ + uint32_t addr32h; \ + r = tor_lookup_hostname(addr_str, &addr32h); \ + tt_int_op(r, OP_EQ, 0); \ + tt_str_op(fmt_addr32(addr32h), OP_EQ, expect_str); \ + STMT_END + +/* Test that bad_str fails to parse using tor_lookup_hostname(), with a + * permanent failure, and: + * - the returned address is 0. + */ +#define TEST_ADDR_V4_LOOKUP_XFAIL(bad_str) \ + STMT_BEGIN \ + int r; \ + uint32_t addr32h; \ + r = tor_lookup_hostname(bad_str, &addr32h); \ + tt_int_op(r, OP_EQ, -1); \ + tt_int_op(addr32h, OP_EQ, 0); \ + STMT_END + +/* Test that looking up host_str as an IPv4 address using tor_lookup_hostname() + * does something sensible: + * - the result is -1, 0, or 1. + * - if the result is a failure, the returned address is 0. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_V4_LOOKUP(host_str) \ + STMT_BEGIN \ + int r; \ + uint32_t addr32h; \ + r = tor_lookup_hostname(host_str, &addr32h); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 1); \ + if (r != 0) \ + tt_int_op(addr32h, OP_EQ, 0); \ + STMT_END + +/* Test that addr_str successfully parses as a require_family IP address using + * tor_addr_lookup(), and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str. + */ +#define TEST_ADDR_LOOKUP_FMT(addr_str, require_family, expect_family, \ + fmt_decorated, expect_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_lookup(addr_str, require_family, &addr); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + STMT_END + +/* Test that bad_str fails to parse as a require_family IP address using + * tor_addr_lookup(), with a permanent failure, and: + * - the returned address is null. + */ +#define TEST_ADDR_LOOKUP_XFAIL(bad_str, require_family) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + r = tor_addr_lookup(bad_str, require_family, &addr); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that looking up host_string as a require_family IP address using + * tor_addr_lookup(), does something sensible: + * - the result is -1, 0, or 1. + * - if the result is a failure, the returned address is null. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_LOOKUP(host_str, require_family) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + r = tor_addr_lookup(host_str, require_family, &addr); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 1); \ + if (r != 0) \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that addr_port_str successfully parses as an IP address and port + * using tor_addr_port_lookup(), and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str, + * - the port is expect_port. + */ +#define TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, expect_family, \ + fmt_decorated, expect_str, expect_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + char buf[TOR_ADDR_BUF_LEN]; \ + const char *sv; \ + r = tor_addr_port_lookup(addr_port_str, &addr, &port); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + tt_int_op(port, OP_EQ, expect_port); \ + STMT_END + +/* Test that bad_str fails to parse as an IP address and port + * using tor_addr_port_lookup(), and: + * - the returned address is null, + * - the returned port is 0. + */ +#define TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + r = tor_addr_port_lookup(bad_str, &addr, &port); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + STMT_END + +/* Test that looking up host_port_str as an IP address using + * tor_addr_port_lookup(), does something sensible: + * - the result is -1 or 0. + * - if the result is a failure, the returned address is null, and the + * returned port is zero, + * - if the result is a success, the returned port is expect_success_port, + * and the returned family is AF_INET or AF_INET6. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port) \ + STMT_BEGIN \ + int r; \ + tor_addr_t addr; \ + uint16_t port; \ + r = tor_addr_port_lookup(host_port_str, &addr, &port); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 0); \ + if (r == -1) { \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + } else { \ + tt_assert(tor_addr_family(&addr) == AF_INET || \ + tor_addr_family(&addr) == AF_INET6); \ + tt_int_op(port, OP_EQ, expect_success_port); \ + } \ + STMT_END + +/* Test that addr_str successfully parses as a canonical IPv4 address. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), + * - tor_addr_lookup() with AF_INET, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_port_lookup(), because there is no port. + */ +#define TEST_ADDR_V4_PARSE_CANONICAL(addr_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET, 0, addr_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 111, AF_INET, 0, \ + addr_str, 111); \ + TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, addr_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET, 0, addr_str, 0); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET, AF_INET, 0, addr_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET, 0, addr_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET6); \ + STMT_END + +/* Test that addr_str successfully parses as a canonical fmt_decorated + * IPv6 address. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port, + * - tor_lookup_hostname(), because it only supports IPv4, + * - tor_addr_lookup() with AF_INET. + */ +#define TEST_ADDR_V6_PARSE_CANONICAL(addr_str, fmt_decorated) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, addr_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 222, AF_INET6, fmt_decorated, \ + addr_str, 222); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \ + addr_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \ + addr_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, addr_str, \ + 0); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \ + STMT_END + +/* Test that addr_str successfully parses, and the fmt_decorated canonical + * IPv6 string is expect_str. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port. + * - tor_lookup_hostname(), because it only supports IPv4, + * - tor_addr_lookup() with AF_INET. + */ +#define TEST_ADDR_V6_PARSE(addr_str, fmt_decorated, expect_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, expect_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 333, AF_INET6, fmt_decorated, \ + expect_str, 333); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \ + expect_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \ + expect_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, expect_str, \ + 0); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \ + STMT_END + +/* Test that addr_port_str successfully parses to the canonical IPv4 address + * string expect_str, and port expect_port. + * Check for successful parsing using: + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_addr_port_lookup(). + * Check for failures using: + * - tor_addr_parse(), because there is a port, + * - tor_lookup_hostname(), because there is a port. + * - tor_addr_lookup(), regardless of the address family, because there is a + * port. + */ +#define TEST_ADDR_V4_PORT_PARSE(addr_port_str, expect_str, expect_port) \ + STMT_BEGIN \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 444, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PARSE_XFAIL(addr_port_str); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \ + STMT_END + +/* Test that addr_port_str successfully parses to the canonical undecorated + * IPv6 address string expect_str, and port expect_port. + * Check for successful parsing using: + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_addr_port_lookup(). + * Check for failures using: + * - tor_addr_parse(), because there is a port, + * - tor_lookup_hostname(), because there is a port, and because it only + * supports IPv4, + * - tor_addr_lookup(), regardless of the address family, because there is a + * port. + */ +#define TEST_ADDR_V6_PORT_PARSE(addr_port_str, expect_str, expect_port) \ + STMT_BEGIN \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 555, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PARSE_XFAIL(addr_port_str); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \ + STMT_END + +/* Test that bad_str fails to parse due to a bad address or port. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), + * - tor_addr_lookup(), regardless of the address family, + * - tor_addr_port_lookup(). + */ +#define TEST_ADDR_PARSE_XFAIL_MALFORMED(bad_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_XFAIL(bad_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(bad_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(bad_str, 666); \ + TEST_ADDR_V4_LOOKUP_XFAIL(bad_str); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET6); \ + TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str); \ + STMT_END + +/* Test that host_str is treated as a hostname, and not an address. + * Check for success or failure using the network-dependent functions: + * - tor_lookup_hostname(), + * - tor_addr_lookup(), regardless of the address family, + * - tor_addr_port_lookup(), expecting a zero port. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port. + */ +#define TEST_HOSTNAME(host_str) \ + STMT_BEGIN \ + TEST_HOST_V4_LOOKUP(host_str); \ + TEST_HOST_LOOKUP(host_str, AF_UNSPEC); \ + TEST_HOST_LOOKUP(host_str, AF_INET); \ + TEST_HOST_LOOKUP(host_str, AF_INET6); \ + TEST_HOST_PORT_LOOKUP(host_str, 0); \ + TEST_ADDR_PARSE_XFAIL(host_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_str, 777); \ + STMT_END + +/* Test that host_port_str is treated as a hostname and port, and not a + * hostname or an address. + * Check for success or failure using the network-dependent function: + * - tor_addr_port_lookup(), expecting expect_success_port if the lookup is + * successful. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), because it doesn't support ports, + * - tor_addr_lookup(), regardless of the address family, because it doesn't + * support ports. + */ +#define TEST_HOSTNAME_PORT(host_port_str, expect_success_port) \ + STMT_BEGIN \ + TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port); \ + TEST_ADDR_PARSE_XFAIL(host_port_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, 888); \ + TEST_ADDR_V4_LOOKUP_XFAIL(host_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET6); \ + STMT_END + /** Test tor_addr_parse() and tor_addr_port_parse(). */ static void test_addr_parse(void *arg) { - int r; - tor_addr_t addr; - char buf[TOR_ADDR_BUF_LEN]; - uint16_t port = 0; - - /* Correct call. */ (void)arg; - r= tor_addr_parse(&addr, "192.0.2.1"); - tt_int_op(r,OP_EQ, AF_INET); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "192.0.2.1"); - - r= tor_addr_parse(&addr, "11:22::33:44"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44"); - - r= tor_addr_parse(&addr, "[11:22::33:44]"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44"); - - r= tor_addr_parse(&addr, "11:22:33:44:55:66:1.2.3.4"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22:33:44:55:66:102:304"); - - r= tor_addr_parse(&addr, "11:22::33:44:1.2.3.4"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44:102:304"); - /* Empty string. */ - r= tor_addr_parse(&addr, ""); - tt_int_op(r,OP_EQ, -1); + /* Correct calls. */ + TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.1"); + TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.2"); - /* Square brackets around IPv4 address. */ - r= tor_addr_parse(&addr, "[192.0.2.1]"); - tt_int_op(r,OP_EQ, -1); + TEST_ADDR_V6_PARSE_CANONICAL("[11:22::33:44]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[::1]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[::]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[2::]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[11:22:33:44:55:66:77:88]", 1); - /* Only left square bracket. */ - r= tor_addr_parse(&addr, "[11:22::33:44"); - tt_int_op(r,OP_EQ, -1); + /* Allow IPv6 without square brackets, when there is no port, but only if + * there is a default port */ + TEST_ADDR_V6_PARSE_CANONICAL("11:22::33:44", 0); + TEST_ADDR_V6_PARSE_CANONICAL("::1", 0); + TEST_ADDR_V6_PARSE_CANONICAL("::", 0); + TEST_ADDR_V6_PARSE_CANONICAL("2::", 0); + TEST_ADDR_V6_PARSE_CANONICAL("11:22:33:44:55:66:77:88", 0); - /* Only right square bracket. */ - r= tor_addr_parse(&addr, "11:22::33:44]"); - tt_int_op(r,OP_EQ, -1); + /* IPv6-mapped IPv4 addresses. Tor doesn't really use these. */ + TEST_ADDR_V6_PARSE("11:22:33:44:55:66:1.2.3.4", 0, + "11:22:33:44:55:66:102:304"); - /* Leading colon. */ - r= tor_addr_parse(&addr, ":11:22::33:44"); - tt_int_op(r,OP_EQ, -1); + TEST_ADDR_V6_PARSE("11:22::33:44:1.2.3.4", 0, + "11:22::33:44:102:304"); - /* Trailing colon. */ - r= tor_addr_parse(&addr, "11:22::33:44:"); - tt_int_op(r,OP_EQ, -1); - - /* Too many hex words in IPv4-mapped IPv6 address. */ - r= tor_addr_parse(&addr, "11:22:33:44:55:66:77:88:1.2.3.4"); - tt_int_op(r,OP_EQ, -1); - - /* Correct call. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.1:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, 0); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "192.0.2.1"); - tt_int_op(port,OP_EQ, 1234); - - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, 0); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "::1"); - tt_int_op(port,OP_EQ, 1234); - - /* Domain name. */ - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Ports. */ + TEST_ADDR_V4_PORT_PARSE("192.0.2.1:1234", "192.0.2.1", 1234); + TEST_ADDR_V6_PORT_PARSE("[::1]:1234", "::1", 1234); - /* Only IP. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Host names. */ + TEST_HOSTNAME("localhost"); + TEST_HOSTNAME_PORT("localhost:1234", 1234); + TEST_HOSTNAME_PORT("localhost:0", 0); - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,200); + TEST_HOSTNAME("torproject.org"); + TEST_HOSTNAME_PORT("torproject.org:56", 56); - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + TEST_HOSTNAME("probably-not-a-valid-dns.name-tld"); + TEST_HOSTNAME_PORT("probably-not-a-valid-dns.name-tld:789", 789); - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]", - &addr, &port, 400); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,400); + /* Malformed addresses. */ + /* Empty string. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED(""); - /* Bad port. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:66666", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:66666", - &addr, &port, 200); - tt_int_op(r, OP_EQ, -1); + /* Square brackets around IPv4 address. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.1]"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.3]:12345"); - /* Only domain name */ - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org", - &addr, &port, 200); - tt_int_op(r, OP_EQ, -1); + /* Only left square bracket. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("[11:22::33:44"); - /* Bad IP address */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Only right square bracket. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44]"); + + /* Leading colon. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED(":11:22::33:44"); + + /* Trailing colon. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44:"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:"); - /* Make sure that the default port has lower priority than the real - one */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:1337", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,1337); - - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]:1369", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,1369); + /* Bad port. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:66666"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:77777"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:88888"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:99999"); + + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:-1"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:-2"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:-3"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:-4"); + + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:1 bad"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:bad-port-1"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:1-bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port-1"); + + /* Bad hostname */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid:22222"); + + /* Ambiguous cases */ + /* Too many hex words in IPv4-mapped IPv6 address. + * But some OS host lookup routines accept it as a hostname, or + * as an IP address?? (I assume they discard unused characters). */ + TEST_HOSTNAME("11:22:33:44:55:66:77:88:1.2.3.4"); + + /* IPv6 address with port and no brackets + * We reject it, but some OS host lookup routines accept it as an + * IPv6 address:port ? */ + TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345); + /* Is it a port, or are there too many hex words? + * We reject it either way, but some OS host lookup routines accept it as an + * IPv6 address:port */ + TEST_HOSTNAME_PORT("11:22:33:44:55:66:77:88:99", 99); + /* But we accept it if it has square brackets. */ + TEST_ADDR_V6_PORT_PARSE("[11:22:33:44:55:66:77:88]:99", + "11:22:33:44:55:66:77:88",99); + + /* Bad IPv4 address + * We reject it, but some OS host lookup routines accept it as an + * IPv4 address[:port], with a zero last octet */ + TEST_HOSTNAME("192.0.1"); + TEST_HOSTNAME_PORT("192.0.2:1234", 1234); + + /* More bad IPv6 addresses and ports: no brackets + * We reject it, but some OS host lookup routines accept it as an + * IPv6 address[:port] */ + TEST_HOSTNAME_PORT("::1:12345", 12345); + TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345); + + /* And this is an ambiguous case, which is interpreted as an IPv6 address. */ + TEST_ADDR_V6_PARSE_CANONICAL("11:22::88:99", 0); + /* Use square brackets to resolve the ambiguity */ + TEST_ADDR_V6_PARSE_CANONICAL("[11:22::88:99]", 1); + TEST_ADDR_V6_PORT_PARSE("[11:22::88]:99", + "11:22::88",99); done: ; diff --git a/src/test/test_btrack.c b/src/test/test_btrack.c index 48486fb5a1..9e5d0d0723 100644 --- a/src/test/test_btrack.c +++ b/src/test/test_btrack.c @@ -4,6 +4,7 @@ #include "core/or/or.h" #include "test/test.h" +#include "test_helpers.h" #include "test/log_test_helpers.h" #define OCIRC_EVENT_PRIVATE @@ -12,48 +13,73 @@ #include "core/or/orconn_event.h" static void +send_state(const orconn_state_msg_t *msg_in) +{ + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + orconn_state_publish(msg); +} + +static void +send_status(const orconn_status_msg_t *msg_in) +{ + orconn_status_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + orconn_status_publish(msg); +} + +static void +send_chan(const ocirc_chan_msg_t *msg_in) +{ + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + ocirc_chan_publish(msg); +} + +static void test_btrack_launch(void *arg) { - orconn_event_msg_t conn; - ocirc_event_msg_t circ; + orconn_state_msg_t conn; + ocirc_chan_msg_t circ; (void)arg; - conn.type = ORCONN_MSGTYPE_STATE; - conn.u.state.gid = 1; - conn.u.state.chan = 1; - conn.u.state.proxy_type = PROXY_NONE; - conn.u.state.state = OR_CONN_STATE_CONNECTING; + conn.gid = 1; + conn.chan = 1; + conn.proxy_type = PROXY_NONE; + conn.state = OR_CONN_STATE_CONNECTING; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&conn); expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0 state=1"); expect_no_log_msg_containing("ORCONN BEST_"); teardown_capture_of_logs(); - circ.type = OCIRC_MSGTYPE_CHAN; - circ.u.chan.chan = 1; - circ.u.chan.onehop = true; + circ.chan = 1; + circ.onehop = true; setup_full_capture_of_logs(LOG_DEBUG); - ocirc_event_publish(&circ); + send_chan(&circ); expect_log_msg_containing("ORCONN LAUNCH chan=1 onehop=1"); expect_log_msg_containing("ORCONN BEST_ANY state -1->1 gid=1"); teardown_capture_of_logs(); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&conn); expect_log_msg_containing("ORCONN gid=2 chan=2 proxy_type=0 state=1"); expect_no_log_msg_containing("ORCONN BEST_"); teardown_capture_of_logs(); - circ.u.chan.chan = 2; - circ.u.chan.onehop = false; + circ.chan = 2; + circ.onehop = false; setup_full_capture_of_logs(LOG_DEBUG); - ocirc_event_publish(&circ); + send_chan(&circ); expect_log_msg_containing("ORCONN LAUNCH chan=2 onehop=0"); expect_log_msg_containing("ORCONN BEST_AP state -1->1 gid=2"); teardown_capture_of_logs(); @@ -65,27 +91,26 @@ test_btrack_launch(void *arg) static void test_btrack_delete(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t state; + orconn_status_msg_t status; (void)arg; - conn.type = ORCONN_MSGTYPE_STATE; - conn.u.state.gid = 1; - conn.u.state.chan = 1; - conn.u.state.proxy_type = PROXY_NONE; - conn.u.state.state = OR_CONN_STATE_CONNECTING; + state.gid = 1; + state.chan = 1; + state.proxy_type = PROXY_NONE; + state.state = OR_CONN_STATE_CONNECTING; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&state); expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0"); teardown_capture_of_logs(); - conn.type = ORCONN_MSGTYPE_STATUS; - conn.u.status.gid = 1; - conn.u.status.status = OR_CONN_EVENT_CLOSED; - conn.u.status.reason = 0; + status.gid = 1; + status.status = OR_CONN_EVENT_CLOSED; + status.reason = 0; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_status(&status); expect_log_msg_containing("ORCONN DELETE gid=1 status=3 reason=0"); teardown_capture_of_logs(); @@ -94,7 +119,7 @@ test_btrack_delete(void *arg) } struct testcase_t btrack_tests[] = { - { "launch", test_btrack_launch, TT_FORK, 0, NULL }, - { "delete", test_btrack_delete, TT_FORK, 0, NULL }, + { "launch", test_btrack_launch, TT_FORK, &helper_pubsub_setup, NULL }, + { "delete", test_btrack_delete, TT_FORK, &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c index 5d012e462b..885246628e 100644 --- a/src/test/test_channelpadding.c +++ b/src/test/test_channelpadding.c @@ -289,8 +289,6 @@ test_channelpadding_timers(void *arg) channel_t *chans[CHANNELS_TO_TEST]; (void)arg; - tor_libevent_postfork(); - if (!connection_array) connection_array = smartlist_new(); @@ -393,7 +391,6 @@ test_channelpadding_killonehop(void *arg) channelpadding_decision_t decision; int64_t new_time; (void)arg; - tor_libevent_postfork(); routerstatus_t *relay = tor_malloc_zero(sizeof(routerstatus_t)); monotime_init(); @@ -502,8 +499,6 @@ test_channelpadding_consensus(void *arg) int64_t new_time; (void)arg; - tor_libevent_postfork(); - /* * Params tested: * nf_pad_before_usage @@ -898,8 +893,6 @@ test_channelpadding_decide_to_pad_channel(void *arg) connection_array = smartlist_new(); (void)arg; - tor_libevent_postfork(); - monotime_init(); monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 0c23091594..196d8cd355 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -177,6 +177,6 @@ struct testcase_t circuitbuild_tests[] = { { "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL }, { "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL }, { "upgrade_from_guard_wait", test_upgrade_from_guard_wait, TT_FORK, - NULL, NULL }, + &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c index 2a09622f09..9bfaabeb2f 100644 --- a/src/test/test_circuitstats.c +++ b/src/test/test_circuitstats.c @@ -197,7 +197,7 @@ test_circuitstats_hoplen(void *arg) } #define TEST_CIRCUITSTATS(name, flags) \ - { #name, test_##name, (flags), NULL, NULL } + { #name, test_##name, (flags), &helper_pubsub_setup, NULL } struct testcase_t circuitstats_tests[] = { TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK), diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index 5d625483da..ecd97e3474 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -151,8 +151,6 @@ test_compat_libevent_postloop_events(void *arg) mainloop_event_t *a = NULL, *b = NULL; periodic_timer_t *timed = NULL; - tor_libevent_postfork(); - /* If postloop events don't work, then these events will activate one * another ad infinitum and, and the periodic event will never occur. */ b = mainloop_event_postloop_new(activate_event_cb, &a); diff --git a/src/test/test_confparse.c b/src/test/test_confparse.c new file mode 100644 index 0000000000..dde61b1c81 --- /dev/null +++ b/src/test/test_confparse.c @@ -0,0 +1,904 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * Tests for confparse.c module that we use to parse various + * configuration/state file types. + */ + +#define CONFPARSE_PRIVATE +#include "orconfig.h" + +#include "core/or/or.h" +#include "lib/encoding/confline.h" +#include "feature/nodelist/routerset.h" +#include "app/config/confparse.h" +#include "test/test.h" +#include "test/log_test_helpers.h" + +#include "lib/confmgt/unitparse.h" + +typedef struct test_struct_t { + uint32_t magic; + char *s; + char *fn; + int pos; + int i; + int deprecated_int; + uint64_t u64; + int interval; + int msec_interval; + uint64_t mem; + double dbl; + int boolean; + int autobool; + time_t time; + smartlist_t *csv; + int csv_interval; + config_line_t *lines; + config_line_t *mixed_lines; + routerset_t *routerset; + int hidden_int; + config_line_t *mixed_hidden_lines; + + config_line_t *extra_lines; +} test_struct_t; + +static test_struct_t test_struct_t_dummy; + +#define VAR(name,conftype,member,initvalue) \ + { name, CONFIG_TYPE_##conftype, offsetof(test_struct_t, member), \ + initvalue CONF_TEST_MEMBERS(test_struct_t, conftype, member) } + +#define V(name,conftype,initvalue) \ + VAR( #name, conftype, name, initvalue ) + +#define OBSOLETE(name) \ + { name, CONFIG_TYPE_OBSOLETE, 0, NULL, {.INT=NULL} } + +static config_var_t test_vars[] = { + V(s, STRING, "hello"), + V(fn, FILENAME, NULL), + V(pos, POSINT, NULL), + V(i, INT, "-10"), + V(deprecated_int, INT, "3"), + V(u64, UINT64, NULL), + V(interval, INTERVAL, "10 seconds"), + V(msec_interval, MSEC_INTERVAL, "150 msec"), + V(mem, MEMUNIT, "10 MB"), + V(dbl, DOUBLE, NULL), + V(boolean, BOOL, "0"), + V(autobool, AUTOBOOL, "auto"), + V(time, ISOTIME, NULL), + V(csv, CSV, NULL), + V(csv_interval, CSV_INTERVAL, "5 seconds"), + V(lines, LINELIST, NULL), + VAR("MixedLines", LINELIST_V, mixed_lines, NULL), + VAR("LineTypeA", LINELIST_S, mixed_lines, NULL), + VAR("LineTypeB", LINELIST_S, mixed_lines, NULL), + OBSOLETE("obsolete"), + V(routerset, ROUTERSET, NULL), + VAR("__HiddenInt", POSINT, hidden_int, "0"), + VAR("MixedHiddenLines", LINELIST_V, mixed_hidden_lines, NULL), + VAR("__HiddenLineA", LINELIST_S, mixed_hidden_lines, NULL), + VAR("VisibleLineB", LINELIST_S, mixed_hidden_lines, NULL), + + END_OF_CONFIG_VARS, +}; + +static config_abbrev_t test_abbrevs[] = { + { "uint", "pos", 0, 0 }, + { "float", "dbl", 0, 1 }, + { NULL, NULL, 0, 0 } +}; + +static config_deprecation_t test_deprecation_notes[] = { + { "deprecated_int", "This integer is deprecated." }, + { NULL, NULL } +}; + +static int +test_validate_cb(void *old_options, void *options, void *default_options, + int from_setconf, char **msg) +{ + (void)old_options; + (void)default_options; + (void)from_setconf; + (void)msg; + test_struct_t *ts = options; + + if (ts->i == 0xbad) { + *msg = tor_strdup("bad value for i"); + return -1; + } + return 0; +} + +static void test_free_cb(void *options); + +#define TEST_MAGIC 0x1337 + +static config_format_t test_fmt = { + sizeof(test_struct_t), + TEST_MAGIC, + offsetof(test_struct_t, magic), + test_abbrevs, + test_deprecation_notes, + test_vars, + test_validate_cb, + test_free_cb, + NULL, +}; + +static void +test_free_cb(void *options) +{ + if (!options) + return; + + config_free(&test_fmt, options); +} + +/* Make sure that config_init sets everything to the right defaults. */ +static void +test_confparse_init(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_init(&test_fmt, tst); + + // Make sure that options are initialized right. */ + tt_uint_op(tst->magic, OP_EQ, TEST_MAGIC); + tt_str_op(tst->s, OP_EQ, "hello"); + tt_ptr_op(tst->fn, OP_EQ, NULL); + tt_int_op(tst->pos, OP_EQ, 0); + tt_int_op(tst->i, OP_EQ, -10); + tt_int_op(tst->deprecated_int, OP_EQ, 3); + tt_u64_op(tst->u64, OP_EQ, 0); + tt_int_op(tst->interval, OP_EQ, 10); + tt_int_op(tst->msec_interval, OP_EQ, 150); + tt_u64_op(tst->mem, OP_EQ, 10 * 1024 * 1024); + tt_double_op(tst->dbl, OP_LT, .0000000001); + tt_double_op(tst->dbl, OP_GT, -0.0000000001); + tt_int_op(tst->boolean, OP_EQ, 0); + tt_int_op(tst->autobool, OP_EQ, -1); + tt_i64_op(tst->time, OP_EQ, 0); + tt_ptr_op(tst->csv, OP_EQ, NULL); + tt_int_op(tst->csv_interval, OP_EQ, 5); + tt_ptr_op(tst->lines, OP_EQ, NULL); + tt_ptr_op(tst->mixed_lines, OP_EQ, NULL); + tt_int_op(tst->hidden_int, OP_EQ, 0); + + done: + config_free(&test_fmt, tst); +} + +static const char simple_settings[] = + "s this is a \n" + "fn /simple/test of the\n" + "uint 77\n" // this is an abbrev + "i 3\n" + "u64 1000000000000 \n" + "interval 5 minutes \n" + "msec_interval 5 minutes \n" + "mem 10\n" + "dbl 6.060842\n" + "BOOLEAN 1\n" + "aUtObOOl 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration, parsing , system \n" + "csv_interval 10 seconds, 5 seconds, 10 hours\n" + "lines hello\n" + "LINES world\n" + "linetypea i d\n" + "linetypeb i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "__hiddenint 11\n" + "__hiddenlineA XYZ\n" + "visiblelineB ABC\n"; + +/* Return a configuration object set up from simple_settings above. */ +static test_struct_t * +get_simple_config(void) +{ + test_struct_t *result = NULL; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines(simple_settings, &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + result = tst; + tst = NULL; // prevent free + done: + tor_free(msg); + config_free_lines(lines); + config_free(&test_fmt, tst); + return result; +} + +/* Make sure that config_assign can parse things. */ +static void +test_confparse_assign_simple(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + + tt_str_op(tst->s, OP_EQ, "this is a"); + tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); + tt_int_op(tst->pos, OP_EQ, 77); + tt_int_op(tst->i, OP_EQ, 3); + tt_int_op(tst->deprecated_int, OP_EQ, 3); + tt_u64_op(tst->u64, OP_EQ, UINT64_C(1000000000000)); + tt_int_op(tst->interval, OP_EQ, 5 * 60); + tt_int_op(tst->msec_interval, OP_EQ, 5 * 60 * 1000); + tt_u64_op(tst->mem, OP_EQ, 10); + tt_double_op(tst->dbl, OP_LT, 6.060843); + tt_double_op(tst->dbl, OP_GT, 6.060841); + tt_int_op(tst->boolean, OP_EQ, 1); + tt_int_op(tst->autobool, OP_EQ, 0); + tt_i64_op(tst->time, OP_EQ, 1560520731); + tt_ptr_op(tst->csv, OP_NE, NULL); + tt_int_op(smartlist_len(tst->csv), OP_EQ, 3); + tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "configuration"); + tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "parsing"); + tt_str_op(smartlist_get(tst->csv, 2), OP_EQ, "system"); + tt_int_op(tst->csv_interval, OP_EQ, 10); + tt_int_op(tst->hidden_int, OP_EQ, 11); + + tt_assert(tst->lines); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "hello"); + tt_assert(tst->lines->next); + tt_str_op(tst->lines->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->value, OP_EQ, "world"); + tt_assert(!tst->lines->next->next); + + tt_assert(tst->mixed_lines); + tt_str_op(tst->mixed_lines->key, OP_EQ, "LineTypeA"); + tt_str_op(tst->mixed_lines->value, OP_EQ, "i d"); + tt_assert(tst->mixed_lines->next); + tt_str_op(tst->mixed_lines->next->key, OP_EQ, "LineTypeB"); + tt_str_op(tst->mixed_lines->next->value, OP_EQ, "i c"); + tt_assert(!tst->mixed_lines->next->next); + + tt_assert(tst->mixed_hidden_lines); + tt_str_op(tst->mixed_hidden_lines->key, OP_EQ, "__HiddenLineA"); + tt_str_op(tst->mixed_hidden_lines->value, OP_EQ, "XYZ"); + tt_assert(tst->mixed_hidden_lines->next); + tt_str_op(tst->mixed_hidden_lines->next->key, OP_EQ, "VisibleLineB"); + tt_str_op(tst->mixed_hidden_lines->next->value, OP_EQ, "ABC"); + tt_assert(!tst->mixed_hidden_lines->next->next); + + done: + config_free(&test_fmt, tst); +} + +/* Try to assign to an obsolete option, and make sure we get a warning. */ +static void +test_confparse_assign_obsolete(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("obsolete option here", + &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("Skipping obsolete configuration option"); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to assign to an deprecated option, and make sure we get a warning + * but the assignment works anyway. */ +static void +test_confparse_assign_deprecated(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("deprecated_int 7", + &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, CAL_WARN_DEPRECATIONS, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("This integer is deprecated."); + + tt_int_op(tst->deprecated_int, OP_EQ, 7); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to re-assign an option name that has been depreacted in favor of + * another. */ +static void +test_confparse_assign_replaced(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("float 1000\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, CAL_WARN_DEPRECATIONS, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("use 'dbl' instead."); + + tt_double_op(tst->dbl, OP_GT, 999.999); + tt_double_op(tst->dbl, OP_LT, 1000.001); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to set a linelist value with no option. */ +static void +test_confparse_assign_emptystring(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("lines\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("has no value"); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Try to set a the same option twice; make sure we get a warning. */ +static void +test_confparse_assign_twice(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines("pos 10\n" + "pos 99\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("used more than once"); + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +typedef struct badval_test_t { + const char *cfg; + const char *expect_msg; +} badval_test_t; + +/* Try to set an option and make sure that we get a failure and an expected + * warning. */ +static void +test_confparse_assign_badval(void *arg) +{ + const badval_test_t *bt = arg; + test_struct_t *tst = config_new(&test_fmt); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(&test_fmt, tst); + + int r = config_get_lines(bt->cfg, &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + if (! strstr(msg, bt->expect_msg)) { + TT_DIE(("'%s' did not contain '%s'" , msg, bt->expect_msg)); + } + + done: + teardown_capture_of_logs(); + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Various arguments for badval test. + * + * Note that the expected warnings here are _very_ truncated, since we + * are writing these tests before a refactoring that we expect will + * change them. + */ +static const badval_test_t bv_notint = { "pos X\n", "malformed" }; +static const badval_test_t bv_negint = { "pos -10\n", "out of bounds" }; +static const badval_test_t bv_badu64 = { "u64 u64\n", "malformed" }; +static const badval_test_t bv_badcsvi1 = + { "csv_interval 10 wl\n", "malformed" }; +static const badval_test_t bv_badcsvi2 = + { "csv_interval cl,10\n", "malformed" }; +static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" }; +static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" }; +static const badval_test_t bv_badbool = { "boolean 7\n", "Unrecognized value"}; +static const badval_test_t bv_badabool = + { "autobool 7\n", "Unrecognized value" }; +static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" }; +static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" }; +static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" }; + +/* Try config_dump(), and make sure it behaves correctly */ +static void +test_confparse_dump(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + char *dumped = NULL; + + /* Minimal version. */ + dumped = config_dump(&test_fmt, NULL, tst, 1, 0); + tt_str_op(dumped, OP_EQ, + "s this is a\n" + "fn /simple/test of the\n" + "pos 77\n" + "i 3\n" + "u64 1000000000000\n" + "interval 300\n" + "msec_interval 300000\n" + "mem 10\n" + "dbl 6.060842\n" + "boolean 1\n" + "autobool 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "lines hello\n" + "lines world\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "VisibleLineB ABC\n"); + + /* Maximal */ + tor_free(dumped); + dumped = config_dump(&test_fmt, NULL, tst, 0, 0); + tt_str_op(dumped, OP_EQ, + "s this is a\n" + "fn /simple/test of the\n" + "pos 77\n" + "i 3\n" + "deprecated_int 3\n" + "u64 1000000000000\n" + "interval 300\n" + "msec_interval 300000\n" + "mem 10\n" + "dbl 6.060842\n" + "boolean 1\n" + "autobool 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "lines hello\n" + "lines world\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "VisibleLineB ABC\n"); + + /* commented */ + tor_free(dumped); + dumped = config_dump(&test_fmt, NULL, tst, 0, 1); + tt_str_op(dumped, OP_EQ, + "s this is a\n" + "fn /simple/test of the\n" + "pos 77\n" + "i 3\n" + "# deprecated_int 3\n" + "u64 1000000000000\n" + "interval 300\n" + "msec_interval 300000\n" + "mem 10\n" + "dbl 6.060842\n" + "boolean 1\n" + "autobool 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "lines hello\n" + "lines world\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "VisibleLineB ABC\n"); + + done: + config_free(&test_fmt, tst); + tor_free(dumped); +} + +/* Try confparse_reset_line(), and make sure it behaves correctly */ +static void +test_confparse_reset(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + + config_reset_line(&test_fmt, tst, "interval", 0); + tt_int_op(tst->interval, OP_EQ, 0); + + config_reset_line(&test_fmt, tst, "interval", 1); + tt_int_op(tst->interval, OP_EQ, 10); + + done: + config_free(&test_fmt, tst); +} + +/* Try setting options a second time on a config object, and make sure + * it behaves correctly. */ +static void +test_confparse_reassign(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + config_line_t *lines = NULL; + char *msg = NULL, *rs = NULL; + + int r = config_get_lines( + "s eleven\n" + "i 12\n" + "lines 13\n" + "csv 14,15\n" + "routerset 127.0.0.1\n", + &lines, 0); + r = config_assign(&test_fmt, tst,lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_str_op(tst->s, OP_EQ, "eleven"); + tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); // unchanged + tt_int_op(tst->pos, OP_EQ, 77); // unchanged + tt_int_op(tst->i, OP_EQ, 12); + tt_ptr_op(tst->lines, OP_NE, NULL); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "13"); + tt_ptr_op(tst->lines->next, OP_EQ, NULL); + tt_int_op(smartlist_len(tst->csv), OP_EQ, 2); + tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "14"); + tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "15"); + + rs = routerset_to_string(tst->routerset); + tt_str_op(rs, OP_EQ, "127.0.0.1"); + + // Try again with the CLEAR_FIRST and USE_DEFAULTS flags + r = config_assign(&test_fmt, tst, lines, + CAL_CLEAR_FIRST|CAL_USE_DEFAULTS, &msg); + tt_int_op(r, OP_EQ, 0); + + tt_ptr_op(msg, OP_EQ, NULL); + tt_str_op(tst->s, OP_EQ, "eleven"); + // tt_ptr_op(tst->fn, OP_EQ, NULL); //XXXX why is this not cleared? + // tt_int_op(tst->pos, OP_EQ, 0); //XXXX why is this not cleared? + tt_int_op(tst->i, OP_EQ, 12); + + done: + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); + tor_free(rs); +} + +/* Try setting options a second time on a config object, using the +foo + * linelist-extending syntax. */ +static void +test_confparse_reassign_extend(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + config_line_t *lines = NULL; + char *msg = NULL; + + int r = config_get_lines( + "+lines 13\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst,lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_assert(tst->lines); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "hello"); + tt_assert(tst->lines->next); + tt_str_op(tst->lines->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->value, OP_EQ, "world"); + tt_assert(tst->lines->next->next); + tt_str_op(tst->lines->next->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->next->value, OP_EQ, "13"); + tt_assert(tst->lines->next->next->next == NULL); + config_free_lines(lines); + + r = config_get_lines( + "/lines\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + tt_assert(tst->lines == NULL); + config_free_lines(lines); + + config_free(&test_fmt, tst); + tst = get_simple_config(); + r = config_get_lines( + "/lines away!\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(&test_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + tt_assert(tst->lines == NULL); + + done: + config_free(&test_fmt, tst); + config_free_lines(lines); + tor_free(msg); +} + +/* Test out confparse_get_assigned(). */ +static void +test_confparse_get_assigned(void *arg) +{ + (void)arg; + test_struct_t *tst = get_simple_config(); + config_line_t *lines = NULL; + + lines = config_get_assigned_option(&test_fmt, tst, "I", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "i"); + tt_str_op(lines->value, OP_EQ, "3"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(&test_fmt, tst, "s", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "s"); + tt_str_op(lines->value, OP_EQ, "this is a"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(&test_fmt, tst, "obsolete", 1); + tt_assert(!lines); + + lines = config_get_assigned_option(&test_fmt, tst, "nonesuch", 1); + tt_assert(!lines); + + lines = config_get_assigned_option(&test_fmt, tst, "mixedlines", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "LineTypeA"); + tt_str_op(lines->value, OP_EQ, "i d"); + tt_assert(lines->next); + tt_str_op(lines->next->key, OP_EQ, "LineTypeB"); + tt_str_op(lines->next->value, OP_EQ, "i c"); + tt_assert(lines->next->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(&test_fmt, tst, "linetypeb", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "LineTypeB"); + tt_str_op(lines->value, OP_EQ, "i c"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + tor_free(tst->s); + tst->s = tor_strdup("Hello\nWorld"); + lines = config_get_assigned_option(&test_fmt, tst, "s", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "s"); + tt_str_op(lines->value, OP_EQ, "\"Hello\\nWorld\""); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + done: + config_free(&test_fmt, tst); + config_free_lines(lines); +} + +/* Another variant, which accepts and stores unrecognized lines.*/ +#define ETEST_MAGIC 13371337 + +static config_var_t extra = VAR("__extra", LINELIST, extra_lines, NULL); + +static config_format_t etest_fmt = { + sizeof(test_struct_t), + ETEST_MAGIC, + offsetof(test_struct_t, magic), + test_abbrevs, + test_deprecation_notes, + test_vars, + test_validate_cb, + test_free_cb, + &extra, +}; + +/* Try out the feature where we can store unrecognized lines and dump them + * again. (State files use this.) */ +static void +test_confparse_extra_lines(void *arg) +{ + (void)arg; + test_struct_t *tst = config_new(&etest_fmt); + config_line_t *lines = NULL; + char *msg = NULL, *dump = NULL; + + config_init(&etest_fmt, tst); + + int r = config_get_lines( + "unknotty addita\n" + "pos 99\n" + "wombat knish\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(&etest_fmt, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_assert(tst->extra_lines); + + dump = config_dump(&etest_fmt, NULL, tst, 1, 0); + tt_str_op(dump, OP_EQ, + "pos 99\n" + "unknotty addita\n" + "wombat knish\n"); + + done: + tor_free(msg); + tor_free(dump); + config_free_lines(lines); + config_free(&etest_fmt, tst); +} + +static void +test_confparse_unitparse(void *args) +{ + (void)args; + /* spot-check a few memunit values. */ + int ok = 3; + tt_u64_op(config_parse_memunit("100 MB", &ok), OP_EQ, 100<<20); + tt_assert(ok); + tt_u64_op(config_parse_memunit("100 TB", &ok), OP_EQ, UINT64_C(100)<<40); + tt_assert(ok); + // This is a floating-point value, but note that 1.5 can be represented + // precisely. + tt_u64_op(config_parse_memunit("1.5 MB", &ok), OP_EQ, 3<<19); + tt_assert(ok); + + /* Try some good intervals and msec intervals */ + tt_int_op(config_parse_interval("2 days", &ok), OP_EQ, 48*3600); + tt_assert(ok); + tt_int_op(config_parse_interval("1.5 hour", &ok), OP_EQ, 5400); + tt_assert(ok); + tt_u64_op(config_parse_interval("1 minute", &ok), OP_EQ, 60); + tt_assert(ok); + tt_int_op(config_parse_msec_interval("2 days", &ok), OP_EQ, 48*3600*1000); + tt_assert(ok); + tt_int_op(config_parse_msec_interval("10 msec", &ok), OP_EQ, 10); + tt_assert(ok); + + /* Try a couple of unitless values. */ + tt_int_op(config_parse_interval("10", &ok), OP_EQ, 10); + tt_assert(ok); + tt_u64_op(config_parse_interval("15.0", &ok), OP_EQ, 15); + tt_assert(ok); + + /* u64 overflow */ + /* XXXX our implementation does not currently detect this. See bug 30920. */ + /* + tt_u64_op(config_parse_memunit("20000000 TB", &ok), OP_EQ, 0); + tt_assert(!ok); + */ + + /* i32 overflow */ + tt_int_op(config_parse_interval("1000 months", &ok), OP_EQ, -1); + tt_assert(!ok); + tt_int_op(config_parse_msec_interval("4 weeks", &ok), OP_EQ, -1); + tt_assert(!ok); + + /* bad units */ + tt_u64_op(config_parse_memunit("7 nybbles", &ok), OP_EQ, 0); + tt_assert(!ok); + // XXXX these next two should return -1 according to the documentation. + tt_int_op(config_parse_interval("7 cowznofski", &ok), OP_EQ, 0); + tt_assert(!ok); + tt_int_op(config_parse_msec_interval("1 kalpa", &ok), OP_EQ, 0); + tt_assert(!ok); + + done: + ; +} + +#define CONFPARSE_TEST(name, flags) \ + { #name, test_confparse_ ## name, flags, NULL, NULL } + +#define BADVAL_TEST(name) \ + { "badval_" #name, test_confparse_assign_badval, 0, \ + &passthrough_setup, (void*)&bv_ ## name } + +struct testcase_t confparse_tests[] = { + CONFPARSE_TEST(init, 0), + CONFPARSE_TEST(assign_simple, 0), + CONFPARSE_TEST(assign_obsolete, 0), + CONFPARSE_TEST(assign_deprecated, 0), + CONFPARSE_TEST(assign_replaced, 0), + CONFPARSE_TEST(assign_emptystring, 0), + CONFPARSE_TEST(assign_twice, 0), + BADVAL_TEST(notint), + BADVAL_TEST(negint), + BADVAL_TEST(badu64), + BADVAL_TEST(badcsvi1), + BADVAL_TEST(badcsvi2), + BADVAL_TEST(nonoption), + BADVAL_TEST(badmem), + BADVAL_TEST(badbool), + BADVAL_TEST(badabool), + BADVAL_TEST(badtime), + BADVAL_TEST(virt), + BADVAL_TEST(rs), + CONFPARSE_TEST(dump, 0), + CONFPARSE_TEST(reset, 0), + CONFPARSE_TEST(reassign, 0), + CONFPARSE_TEST(reassign_extend, 0), + CONFPARSE_TEST(get_assigned, 0), + CONFPARSE_TEST(extra_lines, 0), + CONFPARSE_TEST(unitparse, 0), + END_OF_TESTCASES +}; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index ee48d656bd..b9cbe0a14d 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -9,6 +9,7 @@ #include "feature/control/control.h" #include "feature/control/control_cmd.h" #include "feature/control/control_getinfo.h" +#include "feature/control/control_proto.h" #include "feature/client/entrynodes.h" #include "feature/hs/hs_common.h" #include "feature/nodelist/networkstatus.h" @@ -201,42 +202,58 @@ static const control_cmd_syntax_t one_arg_kwargs_syntax = { static const parse_test_params_t parse_one_arg_kwargs_params = TESTPARAMS( one_arg_kwargs_syntax, one_arg_kwargs_tests ); +static char *reply_str = NULL; +/* Mock for control_write_reply that copies the string for inspection + * by tests */ +static void +mock_control_write_reply(control_connection_t *conn, int code, int c, + const char *s) +{ + (void)conn; + (void)code; + (void)c; + tor_free(reply_str); + reply_str = tor_strdup(s); +} + static void test_add_onion_helper_keyarg_v3(void *arg) { int ret, hs_version; add_onion_secret_key_t pk; char *key_new_blob = NULL; - char *err_msg = NULL; const char *key_new_alg = NULL; (void) arg; + MOCK(control_write_reply, mock_control_write_reply); memset(&pk, 0, sizeof(pk)); /* Test explicit ED25519-V3 key generation. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); tt_assert(pk.v3); tt_str_op(key_new_alg, OP_EQ, "ED25519-V3"); tt_assert(key_new_blob); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); /* Test discarding the private key. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); tt_assert(pk.v3); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); @@ -256,9 +273,10 @@ test_add_onion_helper_keyarg_v3(void *arg) tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); tt_assert(key_blob); + tor_free(reply_str); ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tor_free(key_blob); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); @@ -266,7 +284,7 @@ test_add_onion_helper_keyarg_v3(void *arg) tt_mem_op(pk.v3, OP_EQ, hex_sk, 64); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); } @@ -274,7 +292,8 @@ test_add_onion_helper_keyarg_v3(void *arg) done: tor_free(pk.v3); tor_free(key_new_blob); - tor_free(err_msg); + tor_free(reply_str); + UNMOCK(control_write_reply); } static void @@ -285,72 +304,73 @@ test_add_onion_helper_keyarg_v2(void *arg) crypto_pk_t *pk1 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; char *encoded = NULL; char *arg_str = NULL; (void) arg; + MOCK(control_write_reply, mock_control_write_reply); memset(&pk, 0, sizeof(pk)); /* Test explicit RSA1024 key generation. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test "BEST" key generation (Assumes BEST = RSA1024). */ crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test discarding the private key. */ crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test generating a invalid key type. */ crypto_pk_free(pk.v2); pk.v2 = NULL; ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_assert(err_msg); + tt_assert(reply_str); /* Test loading a RSA1024 key. */ - tor_free(err_msg); + tor_free(reply_str); pk1 = pk_generate(0); tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0); /* Test loading a invalid key type. */ @@ -359,36 +379,37 @@ test_add_onion_helper_keyarg_v2(void *arg) crypto_pk_free(pk.v2); pk.v2 = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_assert(err_msg); + tt_assert(reply_str); /* Test loading a invalid key. */ tor_free(arg_str); crypto_pk_free(pk.v2); pk.v2 = NULL; - tor_free(err_msg); + tor_free(reply_str); encoded[strlen(encoded)/2] = '\0'; tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_assert(err_msg); + tt_assert(reply_str); done: crypto_pk_free(pk1); crypto_pk_free(pk.v2); tor_free(key_new_blob); - tor_free(err_msg); + tor_free(reply_str); tor_free(encoded); tor_free(arg_str); + UNMOCK(control_write_reply); } static void @@ -542,49 +563,52 @@ static void test_add_onion_helper_clientauth(void *arg) { rend_authorized_client_t *client = NULL; - char *err_msg = NULL; int created = 0; (void)arg; + MOCK(control_write_reply, mock_control_write_reply); /* Test "ClientName" only. */ - client = add_onion_helper_clientauth("alice", &created, &err_msg); + tor_free(reply_str); + client = add_onion_helper_clientauth("alice", &created, NULL); tt_assert(client); tt_assert(created); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); rend_authorized_client_free(client); /* Test "ClientName:Blob" */ + tor_free(reply_str); client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B", - &created, &err_msg); + &created, NULL); tt_assert(client); tt_assert(!created); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); rend_authorized_client_free(client); /* Test invalid client names */ + tor_free(reply_str); client = add_onion_helper_clientauth("no*asterisks*allowed", &created, - &err_msg); + NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); /* Test invalid auth cookie */ - client = add_onion_helper_clientauth("alice:12345", &created, &err_msg); + tor_free(reply_str); + client = add_onion_helper_clientauth("alice:12345", &created, NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); /* Test invalid syntax */ + tor_free(reply_str); client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created, - &err_msg); + NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); done: rend_authorized_client_free(client); - tor_free(err_msg); + tor_free(reply_str); + UNMOCK(control_write_reply); } /* Mocks and data/variables used for GETINFO download status tests */ diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 910aacace3..a8967bba50 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -7,6 +7,7 @@ #define CONTROL_EVENTS_PRIVATE #define OCIRC_EVENT_PRIVATE #define ORCONN_EVENT_PRIVATE +#include "app/main/subsysmgr.h" #include "core/or/or.h" #include "core/or/channel.h" #include "core/or/channeltls.h" @@ -16,6 +17,7 @@ #include "core/mainloop/connection.h" #include "feature/control/control_events.h" #include "test/test.h" +#include "test/test_helpers.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" @@ -394,38 +396,39 @@ test_cntev_dirboot_defer_orconn(void *arg) } static void -setup_orconn_state(orconn_event_msg_t *msg, uint64_t gid, uint64_t chan, +setup_orconn_state(orconn_state_msg_t *msg, uint64_t gid, uint64_t chan, int proxy_type) { - msg->type = ORCONN_MSGTYPE_STATE; - msg->u.state.gid = gid; - msg->u.state.chan = chan; - msg->u.state.proxy_type = proxy_type; + msg->gid = gid; + msg->chan = chan; + msg->proxy_type = proxy_type; } static void -send_orconn_state(orconn_event_msg_t *msg, uint8_t state) +send_orconn_state(const orconn_state_msg_t *msg_in, uint8_t state) { - msg->u.state.state = state; - orconn_event_publish(msg); + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + msg->state = state; + orconn_state_publish(msg); } static void send_ocirc_chan(uint32_t gid, uint64_t chan, bool onehop) { - ocirc_event_msg_t msg; + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = OCIRC_MSGTYPE_CHAN; - msg.u.chan.gid = gid; - msg.u.chan.chan = chan; - msg.u.chan.onehop = onehop; - ocirc_event_publish(&msg); + msg->gid = gid; + msg->chan = chan; + msg->onehop = onehop; + ocirc_chan_publish(msg); } static void test_cntev_orconn_state(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -442,8 +445,8 @@ test_cntev_orconn_state(void *arg) send_orconn_state(&conn, OR_CONN_STATE_OPEN); assert_bootmsg("15 TAG=handshake_done"); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); /* It doesn't know it's an origin circuit yet */ assert_bootmsg("15 TAG=handshake_done"); @@ -464,7 +467,7 @@ test_cntev_orconn_state(void *arg) static void test_cntev_orconn_state_pt(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -484,8 +487,8 @@ test_cntev_orconn_state_pt(void *arg) assert_bootmsg("15 TAG=handshake_done"); send_ocirc_chan(2, 2, false); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); assert_bootmsg("76 TAG=ap_conn_pt"); send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING); @@ -499,7 +502,7 @@ test_cntev_orconn_state_pt(void *arg) static void test_cntev_orconn_state_proxy(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -519,8 +522,8 @@ test_cntev_orconn_state_proxy(void *arg) assert_bootmsg("15 TAG=handshake_done"); send_ocirc_chan(2, 2, false); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); assert_bootmsg("78 TAG=ap_conn_proxy"); send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING); @@ -534,15 +537,18 @@ test_cntev_orconn_state_proxy(void *arg) #define TEST(name, flags) \ { #name, test_cntev_ ## name, flags, 0, NULL } +#define T_PUBSUB(name, setup) \ + { #name, test_cntev_ ## name, TT_FORK, &helper_pubsub_setup, NULL } + struct testcase_t controller_event_tests[] = { TEST(sum_up_cell_stats, TT_FORK), TEST(append_cell_stats, TT_FORK), TEST(format_cell_stats, TT_FORK), TEST(event_mask, TT_FORK), - TEST(dirboot_defer_desc, TT_FORK), - TEST(dirboot_defer_orconn, TT_FORK), - TEST(orconn_state, TT_FORK), - TEST(orconn_state_pt, TT_FORK), - TEST(orconn_state_proxy, TT_FORK), + T_PUBSUB(dirboot_defer_desc, TT_FORK), + T_PUBSUB(dirboot_defer_orconn, TT_FORK), + T_PUBSUB(orconn_state, TT_FORK), + T_PUBSUB(orconn_state_pt, TT_FORK), + T_PUBSUB(orconn_state_proxy, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index 38aca90266..cb53a4e662 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -587,6 +587,6 @@ struct testcase_t extorport_tests[] = { { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL }, { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK, NULL, NULL }, - { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL }, + { "handshake", test_ext_or_handshake, TT_FORK, &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 489c257761..b4389f2d17 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -17,12 +17,17 @@ #include "lib/buf/buffers.h" #include "app/config/config.h" #include "app/config/confparse.h" +#include "app/main/subsysmgr.h" #include "core/mainloop/connection.h" #include "lib/crypt_ops/crypto_rand.h" #include "core/mainloop/mainloop.h" #include "feature/nodelist/nodelist.h" #include "core/or/relay.h" #include "feature/nodelist/routerlist.h" +#include "lib/dispatch/dispatch.h" +#include "lib/dispatch/dispatch_naming.h" +#include "lib/pubsub/pubsub_build.h" +#include "lib/pubsub/pubsub_connect.h" #include "lib/encoding/confline.h" #include "lib/net/resolve.h" @@ -303,3 +308,54 @@ helper_parse_options(const char *conf) } return opt; } + +/** + * Dispatch alertfn callback: flush all messages right now. Implements + * DELIV_IMMEDIATE. + **/ +static void +alertfn_immediate(dispatch_t *d, channel_id_t chan, void *arg) +{ + (void) arg; + dispatch_flush(d, chan, INT_MAX); +} + +/** + * Setup helper for tests that need pubsub active + * + * Does not hook up mainloop events. Does set immediate delivery for + * all channels. + */ +void * +helper_setup_pubsub(const struct testcase_t *testcase) +{ + dispatch_t *dispatcher = NULL; + pubsub_builder_t *builder = pubsub_builder_new(); + channel_id_t chan = get_channel_id("orconn"); + + (void)testcase; + (void)subsystems_add_pubsub(builder); + dispatcher = pubsub_builder_finalize(builder, NULL); + tor_assert(dispatcher); + dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL); + chan = get_channel_id("ocirc"); + dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL); + return dispatcher; +} + +/** + * Cleanup helper for tests that need pubsub active + */ +int +helper_cleanup_pubsub(const struct testcase_t *testcase, void *dispatcher_) +{ + dispatch_t *dispatcher = dispatcher_; + + (void)testcase; + dispatch_free(dispatcher); + return 1; +} + +const struct testcase_setup_t helper_pubsub_setup = { + helper_setup_pubsub, helper_cleanup_pubsub +}; diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 9e376a563d..d82072bb34 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -7,6 +7,7 @@ #define BUFFERS_PRIVATE #include "core/or/or.h" +#include "tinytest.h" const char *get_yesterday_date_str(void); @@ -31,5 +32,10 @@ or_options_t *helper_parse_options(const char *conf); extern const char TEST_DESCRIPTORS[]; +void *helper_setup_pubsub(const struct testcase_t *); +int helper_cleanup_pubsub(const struct testcase_t *, void *); + +extern const struct testcase_setup_t helper_pubsub_setup; + #endif /* !defined(TOR_TEST_HELPERS_H) */ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 0d25a98bb3..fb497d52a1 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -37,6 +37,7 @@ #include "feature/hs/hs_config.h" #include "feature/hs/hs_ident.h" #include "feature/hs/hs_cache.h" +#include "feature/rend/rendcache.h" #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" #include "core/mainloop/connection.h" @@ -1007,6 +1008,92 @@ test_close_intro_circuits_new_desc(void *arg) UNMOCK(networkstatus_get_live_consensus); } +static void +test_close_intro_circuits_cache_clean(void *arg) +{ + int ret; + ed25519_keypair_t service_kp; + circuit_t *circ = NULL; + origin_circuit_t *ocirc = NULL; + hs_descriptor_t *desc1 = NULL; + + (void) arg; + + hs_init(); + rend_cache_init(); + + /* This is needed because of the client cache expiration timestamp is based + * on having a consensus. See cached_client_descriptor_has_expired(). */ + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + /* Set consensus time */ + parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC", + &mock_ns.valid_until); + + /* Generate service keypair */ + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0)); + + /* Create and add to the global list a dummy client introduction circuits. + * We'll then make sure the hs_ident is attached to a dummy descriptor. */ + circ = dummy_origin_circuit_new(0); + tt_assert(circ); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; + ocirc = TO_ORIGIN_CIRCUIT(circ); + + /* Build the first descriptor and cache it. */ + { + char *encoded; + desc1 = hs_helper_build_hs_desc_with_ip(&service_kp); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded); + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + /* Store it */ + ret = hs_cache_store_as_client(encoded, &service_kp.pubkey); + tt_int_op(ret, OP_EQ, 0); + tor_free(encoded); + tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey)); + } + + /* We'll pick one introduction point and associate it with the circuit. */ + { + const hs_desc_intro_point_t *ip = + smartlist_get(desc1->encrypted_data.intro_points, 0); + tt_assert(ip); + ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey, + HS_IDENT_CIRCUIT_INTRO); + ed25519_pubkey_copy(ô->hs_ident->intro_auth_pk, + &ip->auth_key_cert->signed_key); + } + + /* Before we are about to clean up the intro circuits, make sure it is + * actually there. */ + tt_assert(circuit_get_next_intro_circ(NULL, true)); + + /* Cleanup the client cache. The ns valid after time is what decides if the + * descriptor has expired so put it in the future enough (72h) so we are + * sure to always expire. */ + mock_ns.valid_after = approx_time() + (72 * 24 * 60 * 60); + hs_cache_clean_as_client(0); + + /* Once stored, our intro circuit should be closed because it is related to + * an old introduction point that doesn't exists anymore. */ + tt_assert(!circuit_get_next_intro_circ(NULL, true)); + + done: + circuit_free(circ); + hs_descriptor_free(desc1); + hs_free_all(); + rend_cache_free_all(); + UNMOCK(networkstatus_get_live_consensus); +} + struct testcase_t hs_client_tests[] = { { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy, TT_FORK, NULL, NULL }, @@ -1026,6 +1113,8 @@ struct testcase_t hs_client_tests[] = { TT_FORK, NULL, NULL }, { "close_intro_circuits_new_desc", test_close_intro_circuits_new_desc, TT_FORK, NULL, NULL }, + { "close_intro_circuits_cache_clean", test_close_intro_circuits_cache_clean, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 732836fb5b..0cdb1fef27 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -16,6 +16,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "core/or/or.h" +#include "core/or/channel.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "ht.h" @@ -693,6 +694,17 @@ test_introduce1_suitable_circuit(void *arg) tt_int_op(ret, OP_EQ, 0); } + /* Single hop circuit should not be allowed. */ + { + circ = or_circuit_new(0, NULL); + circ->p_chan = tor_malloc_zero(sizeof(channel_t)); + circ->p_chan->is_client = 1; + ret = circuit_is_suitable_for_introduce1(circ); + tor_free(circ->p_chan); + circuit_free_(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + done: ; } @@ -927,4 +939,3 @@ struct testcase_t hs_intropoint_tests[] = { END_OF_TESTCASES }; - diff --git a/src/test/test_options.c b/src/test/test_options.c index 7009910b0f..64fcd011e7 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -258,13 +258,17 @@ test_options_validate(void *arg) WANT_ERR("BridgeRelay 1\nDirCache 0", "We're a bridge but DirCache is disabled.", PH_VALIDATE); + // XXXX We should replace this with a more full error message once #29211 + // XXXX is done. It is truncated for now because at the current stage + // XXXX of refactoring, we can't give a full error message like before. WANT_ERR_LOG("HeartbeatPeriod 21 snarks", - "Interval 'HeartbeatPeriod 21 snarks' is malformed or" - " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + "malformed or out of bounds", LOG_WARN, + "Unknown unit 'snarks'.", PH_ASSIGN); + // XXXX As above. WANT_ERR_LOG("LogTimeGranularity 21 snarks", - "Msec interval 'LogTimeGranularity 21 snarks' is malformed or" - " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + "malformed or out of bounds", LOG_WARN, + "Unknown unit 'snarks'.", PH_ASSIGN); OK("HeartbeatPeriod 1 hour", PH_VALIDATE); OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE); @@ -1343,29 +1347,6 @@ test_options_validate__token_bucket(void *ignored) } static void -test_options_validate__recommended_packages(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - setup_capture_of_logs(LOG_WARN); - options_test_data_t *tdata = get_options_test_data( - "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n" - "RecommendedPackages invalid-package-line\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - expect_no_log_msg("Invalid RecommendedPackage line " - "invalid-package-line will be ignored\n"); - - done: - escaped(NULL); // This will free the leaking memory from the previous escaped - teardown_capture_of_logs(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__fetch_dir(void *ignored) { (void)ignored; @@ -4200,7 +4181,6 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(exclude_nodes), LOCAL_VALIDATE_TEST(node_families), LOCAL_VALIDATE_TEST(token_bucket), - LOCAL_VALIDATE_TEST(recommended_packages), LOCAL_VALIDATE_TEST(fetch_dir), LOCAL_VALIDATE_TEST(conn_limit), LOCAL_VALIDATE_TEST(paths_needed), diff --git a/src/test/test_pubsub_build.c b/src/test/test_pubsub_build.c index ce5bf60080..021323fbf1 100644 --- a/src/test/test_pubsub_build.c +++ b/src/test/test_pubsub_build.c @@ -493,48 +493,6 @@ test_pubsub_build_sub_many(void *arg) tor_free(sysname); } -/* The same subsystem can only declare one publish or subscribe. */ -static void -test_pubsub_build_pubsub_redundant(void *arg) -{ - (void)arg; - pubsub_builder_t *b = NULL; - dispatch_t *dispatcher = NULL; - pubsub_connector_t *c = NULL; - - b = pubsub_builder_new(); - seed_pubsub_builder_basic(b); - pub_binding_t btmp; - - { - c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2")); - DISPATCH_ADD_SUB(c, main, bunch_of_coconuts); - pubsub_add_pub_(c, &btmp, get_channel_id("main"), - get_message_id("yes_we_have_no"), - get_msg_type_id("string"), - 0 /* flags */, - "somewhere.c", 22); - pubsub_connector_free(c); - }; - - setup_full_capture_of_logs(LOG_WARN); - dispatcher = pubsub_builder_finalize(b, NULL); - b = NULL; - tt_assert(dispatcher == NULL); - - expect_log_msg_containing( - "Message \"yes_we_have_no\" is configured to be published by " - "subsystem \"sys2\" more than once."); - expect_log_msg_containing( - "Message \"bunch_of_coconuts\" is configured to be subscribed by " - "subsystem \"sys2\" more than once."); - - done: - pubsub_builder_free(b); - dispatch_free(dispatcher); - teardown_capture_of_logs(); -} - /* It's fine to declare the excl flag. */ static void test_pubsub_build_excl_ok(void *arg) @@ -614,7 +572,6 @@ struct testcase_t pubsub_build_tests[] = { T(pubsub_same, TT_FORK), T(pubsub_multi, TT_FORK), T(sub_many, TT_FORK), - T(pubsub_redundant, TT_FORK), T(excl_ok, TT_FORK), T(excl_bad, TT_FORK), END_OF_TESTCASES diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh index e0d8394d38..d6d9d86668 100755 --- a/src/test/test_rebind.sh +++ b/src/test/test_rebind.sh @@ -12,8 +12,6 @@ if test "$UNAME_OS" = 'CYGWIN' || \ fi fi -exitcode=0 - tmpdir= clean () { if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then diff --git a/src/test/test_token_bucket.c b/src/test/test_token_bucket.c new file mode 100644 index 0000000000..d3ce591388 --- /dev/null +++ b/src/test/test_token_bucket.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_bwmgt.c + * \brief tests for bandwidth management / token bucket functions + */ + +#define TOKEN_BUCKET_PRIVATE + +#include "core/or/or.h" +#include "test/test.h" + +#include "lib/evloop/token_bucket.h" + +// an imaginary time, in timestamp units. Chosen so it will roll over. +static const uint32_t START_TS = UINT32_MAX - 1000; +static const uint32_t RATE = 10; +static const uint32_t BURST = 50; + +static void +test_token_bucket_ctr_init(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST); + tt_uint_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS); + tt_int_op(tb.counter.bucket, OP_EQ, BURST); + + done: + ; +} + +static void +test_token_bucket_ctr_adjust(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Increase burst. */ + token_bucket_ctr_adjust(&tb, RATE, BURST * 2); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST * 2); + + /* Decrease burst but still above bucket value. */ + token_bucket_ctr_adjust(&tb, RATE, BURST + 10); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST + 10); + + /* Decrease burst below bucket value. */ + token_bucket_ctr_adjust(&tb, RATE, BURST - 1); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST - 1); + + /* Change rate. */ + token_bucket_ctr_adjust(&tb, RATE * 2, BURST); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE * 2); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST); + + done: + ; +} + +static void +test_token_bucket_ctr_dec(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Simple decrement by one. */ + tt_uint_op(0, OP_EQ, token_bucket_ctr_dec(&tb, 1)); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + + /* Down to 0. Becomes empty. */ + tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST - 1)); + tt_uint_op(tb.counter.bucket, OP_EQ, 0); + + /* Reset and try to underflow. */ + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST + 1)); + tt_int_op(tb.counter.bucket, OP_EQ, -1); + + /* Keep underflowing shouldn't flag the bucket as empty. */ + tt_uint_op(false, OP_EQ, token_bucket_ctr_dec(&tb, BURST)); + tt_int_op(tb.counter.bucket, OP_EQ, (int32_t) ((BURST + 1) * -1)); + + done: + ; +} + +static void +test_token_bucket_ctr_refill(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Reduce of half the bucket and let a single second go before refill. */ + token_bucket_ctr_dec(&tb, BURST / 2); + tt_int_op(tb.counter.bucket, OP_EQ, BURST / 2); + token_bucket_ctr_refill(&tb, START_TS + 1); + tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1); + + /* No time change, nothing should move. */ + token_bucket_ctr_refill(&tb, START_TS + 1); + tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1); + + /* Add 99 seconds, bucket should be back to a full BURST. */ + token_bucket_ctr_refill(&tb, START_TS + 99); + tt_int_op(tb.counter.bucket, OP_EQ, BURST); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 99); + + /* Empty bucket at once. */ + token_bucket_ctr_dec(&tb, BURST); + tt_int_op(tb.counter.bucket, OP_EQ, 0); + /* On second passes. */ + token_bucket_ctr_refill(&tb, START_TS + 100); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 100); + tt_int_op(tb.counter.bucket, OP_EQ, RATE); + /* A second second passes. */ + token_bucket_ctr_refill(&tb, START_TS + 101); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 101); + tt_int_op(tb.counter.bucket, OP_EQ, RATE * 2); + + done: + ; +} + +#define TOKEN_BUCKET(name) \ + { #name, test_token_bucket_ ## name , 0, NULL, NULL } + +struct testcase_t token_bucket_tests[] = { + TOKEN_BUCKET(ctr_init), + TOKEN_BUCKET(ctr_adjust), + TOKEN_BUCKET(ctr_dec), + TOKEN_BUCKET(ctr_refill), + END_OF_TESTCASES +}; diff --git a/src/test/test_util.c b/src/test/test_util.c index 2faadd4e19..41ecbfd388 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5399,6 +5399,11 @@ test_util_socketpair(void *arg) tt_skip(); } #endif /* defined(__FreeBSD__) */ + if (ersatz && socketpair_result == -ENETUNREACH) { + /* We can also fail with -ENETUNREACH if we have no network stack at + * all. */ + tt_skip(); + } tt_int_op(0, OP_EQ, socketpair_result); tt_assert(SOCKET_OK(fds[0])); diff --git a/src/trunnel/channelpadding_negotiation.c b/src/trunnel/channelpadding_negotiation.c index 59e6b38384..d96496e90c 100644 --- a/src/trunnel/channelpadding_negotiation.c +++ b/src/trunnel/channelpadding_negotiation.c @@ -1,4 +1,4 @@ -/* channelpadding_negotiation.c -- generated by Trunnel v1.5.2. +/* channelpadding_negotiation.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/channelpadding_negotiation.h b/src/trunnel/channelpadding_negotiation.h index fcfc232fea..3f96174f68 100644 --- a/src/trunnel/channelpadding_negotiation.h +++ b/src/trunnel/channelpadding_negotiation.h @@ -1,4 +1,4 @@ -/* channelpadding_negotiation.h -- generated by Trunnel v1.5.2. +/* channelpadding_negotiation.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/circpad_negotiation.c b/src/trunnel/circpad_negotiation.c index 236be06ada..547818f2ec 100644 --- a/src/trunnel/circpad_negotiation.c +++ b/src/trunnel/circpad_negotiation.c @@ -1,4 +1,4 @@ -/* circpad_negotiation.c -- generated by Trunnel v1.5.2. +/* circpad_negotiation.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/circpad_negotiation.h b/src/trunnel/circpad_negotiation.h index d09080dc16..ba9155019e 100644 --- a/src/trunnel/circpad_negotiation.h +++ b/src/trunnel/circpad_negotiation.h @@ -1,4 +1,4 @@ -/* circpad_negotiation.h -- generated by Trunnel v1.5.2. +/* circpad_negotiation.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c index 1276c7a505..86b79ef9b6 100644 --- a/src/trunnel/ed25519_cert.c +++ b/src/trunnel/ed25519_cert.c @@ -1,4 +1,4 @@ -/* ed25519_cert.c -- generated by Trunnel v1.5.2. +/* ed25519_cert.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h index e086c6fced..bd91ce1055 100644 --- a/src/trunnel/ed25519_cert.h +++ b/src/trunnel/ed25519_cert.h @@ -1,4 +1,4 @@ -/* ed25519_cert.h -- generated by Trunnel v1.5.2. +/* ed25519_cert.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_common.c b/src/trunnel/hs/cell_common.c index af223560c1..830af5c78b 100644 --- a/src/trunnel/hs/cell_common.c +++ b/src/trunnel/hs/cell_common.c @@ -1,4 +1,4 @@ -/* cell_common.c -- generated by Trunnel v1.5.2. +/* cell_common.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_common.h b/src/trunnel/hs/cell_common.h index e08eedfdb3..c84d17d8e5 100644 --- a/src/trunnel/hs/cell_common.h +++ b/src/trunnel/hs/cell_common.h @@ -1,4 +1,4 @@ -/* cell_common.h -- generated by Trunnel v1.5.2. +/* cell_common.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_establish_intro.c b/src/trunnel/hs/cell_establish_intro.c index ae3b7b1bc8..99ceadbda4 100644 --- a/src/trunnel/hs/cell_establish_intro.c +++ b/src/trunnel/hs/cell_establish_intro.c @@ -1,4 +1,4 @@ -/* cell_establish_intro.c -- generated by Trunnel v1.5.2. +/* cell_establish_intro.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_establish_intro.h b/src/trunnel/hs/cell_establish_intro.h index ccaef5488c..1908645aa6 100644 --- a/src/trunnel/hs/cell_establish_intro.h +++ b/src/trunnel/hs/cell_establish_intro.h @@ -1,4 +1,4 @@ -/* cell_establish_intro.h -- generated by Trunnel v1.5.2. +/* cell_establish_intro.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_introduce1.c b/src/trunnel/hs/cell_introduce1.c index 53b3d299f2..016c9fa8d6 100644 --- a/src/trunnel/hs/cell_introduce1.c +++ b/src/trunnel/hs/cell_introduce1.c @@ -1,4 +1,4 @@ -/* cell_introduce1.c -- generated by Trunnel v1.5.2. +/* cell_introduce1.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_introduce1.h b/src/trunnel/hs/cell_introduce1.h index 986a531ca7..8dabff3cb5 100644 --- a/src/trunnel/hs/cell_introduce1.h +++ b/src/trunnel/hs/cell_introduce1.h @@ -1,4 +1,4 @@ -/* cell_introduce1.h -- generated by Trunnel v1.5.2. +/* cell_introduce1.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_rendezvous.c b/src/trunnel/hs/cell_rendezvous.c index 53cb609138..1204e93cfc 100644 --- a/src/trunnel/hs/cell_rendezvous.c +++ b/src/trunnel/hs/cell_rendezvous.c @@ -1,4 +1,4 @@ -/* cell_rendezvous.c -- generated by Trunnel v1.5.2. +/* cell_rendezvous.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/hs/cell_rendezvous.h b/src/trunnel/hs/cell_rendezvous.h index 39e14da25b..5a8c2ff52a 100644 --- a/src/trunnel/hs/cell_rendezvous.h +++ b/src/trunnel/hs/cell_rendezvous.h @@ -1,4 +1,4 @@ -/* cell_rendezvous.h -- generated by Trunnel v1.5.2. +/* cell_rendezvous.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/include.am b/src/trunnel/include.am index ce15570b15..6c3a5ff06b 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -11,7 +11,7 @@ TRUNNELINPUTS = \ src/trunnel/link_handshake.trunnel \ src/trunnel/pwbox.trunnel \ src/trunnel/channelpadding_negotiation.trunnel \ - src/trunnel/sendme.trunnel \ + src/trunnel/sendme_cell.trunnel \ src/trunnel/socks5.trunnel \ src/trunnel/circpad_negotiation.trunnel @@ -25,7 +25,7 @@ TRUNNELSOURCES = \ src/trunnel/hs/cell_introduce1.c \ src/trunnel/hs/cell_rendezvous.c \ src/trunnel/channelpadding_negotiation.c \ - src/trunnel/sendme.c \ + src/trunnel/sendme_cell.c \ src/trunnel/socks5.c \ src/trunnel/netinfo.c \ src/trunnel/circpad_negotiation.c @@ -42,7 +42,7 @@ TRUNNELHEADERS = \ src/trunnel/hs/cell_introduce1.h \ src/trunnel/hs/cell_rendezvous.h \ src/trunnel/channelpadding_negotiation.h \ - src/trunnel/sendme.h \ + src/trunnel/sendme_cell.h \ src/trunnel/socks5.h \ src/trunnel/netinfo.h \ src/trunnel/circpad_negotiation.h diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c index 03ead31c62..76db4b0e29 100644 --- a/src/trunnel/link_handshake.c +++ b/src/trunnel/link_handshake.c @@ -1,4 +1,4 @@ -/* link_handshake.c -- generated by Trunnel v1.5.2. +/* link_handshake.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h index 6a23483adc..0c7ac36b1b 100644 --- a/src/trunnel/link_handshake.h +++ b/src/trunnel/link_handshake.h @@ -1,4 +1,4 @@ -/* link_handshake.h -- generated by Trunnel v1.5.2. +/* link_handshake.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/netinfo.c b/src/trunnel/netinfo.c index 5d815b9b12..d7d0cddc89 100644 --- a/src/trunnel/netinfo.c +++ b/src/trunnel/netinfo.c @@ -1,4 +1,4 @@ -/* netinfo.c -- generated by Trunnel v1.5.2. +/* netinfo.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/netinfo.h b/src/trunnel/netinfo.h index ac46e603ba..37c2ae3c2d 100644 --- a/src/trunnel/netinfo.h +++ b/src/trunnel/netinfo.h @@ -1,4 +1,4 @@ -/* netinfo.h -- generated by Trunnel v1.5.2. +/* netinfo.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c index c356515d36..c159a5e687 100644 --- a/src/trunnel/pwbox.c +++ b/src/trunnel/pwbox.c @@ -1,4 +1,4 @@ -/* pwbox.c -- generated by Trunnel v1.5.2. +/* pwbox.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h index a9a421408a..36d595f4ef 100644 --- a/src/trunnel/pwbox.h +++ b/src/trunnel/pwbox.h @@ -1,4 +1,4 @@ -/* pwbox.h -- generated by Trunnel v1.5.2. +/* pwbox.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/sendme.c b/src/trunnel/sendme_cell.c index 262b915234..b9f8fe967f 100644 --- a/src/trunnel/sendme.c +++ b/src/trunnel/sendme_cell.c @@ -1,11 +1,11 @@ -/* sendme.c -- generated by Trunnel v1.5.2. +/* sendme_cell.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ #include <stdlib.h> #include "trunnel-impl.h" -#include "sendme.h" +#include "sendme_cell.h" #define TRUNNEL_SET_ERROR_CODE(obj) \ do { \ @@ -15,8 +15,8 @@ #if defined(__COVERITY__) || defined(__clang_analyzer__) /* If we're running a static analysis tool, we don't want it to complain * that some of our remaining-bytes checks are dead-code. */ -int sendme_deadcode_dummy__ = 0; -#define OR_DEADCODE_DUMMY || sendme_deadcode_dummy__ +int sendmecell_deadcode_dummy__ = 0; +#define OR_DEADCODE_DUMMY || sendmecell_deadcode_dummy__ #else #define OR_DEADCODE_DUMMY #endif diff --git a/src/trunnel/sendme.h b/src/trunnel/sendme_cell.h index f3c3dd78c4..45efb9f10d 100644 --- a/src/trunnel/sendme.h +++ b/src/trunnel/sendme_cell.h @@ -1,9 +1,9 @@ -/* sendme.h -- generated by Trunnel v1.5.2. +/* sendme_cell.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ -#ifndef TRUNNEL_SENDME_H -#define TRUNNEL_SENDME_H +#ifndef TRUNNEL_SENDME_CELL_H +#define TRUNNEL_SENDME_CELL_H #include <stdint.h> #include "trunnel.h" diff --git a/src/trunnel/sendme.trunnel b/src/trunnel/sendme_cell.trunnel index 300963e679..300963e679 100644 --- a/src/trunnel/sendme.trunnel +++ b/src/trunnel/sendme_cell.trunnel diff --git a/src/trunnel/socks5.c b/src/trunnel/socks5.c index 057a52b042..f32862e353 100644 --- a/src/trunnel/socks5.c +++ b/src/trunnel/socks5.c @@ -1,4 +1,4 @@ -/* socks5.c -- generated by Trunnel v1.5.2. +/* socks5.c -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/socks5.h b/src/trunnel/socks5.h index d3bea152e7..23ac64faba 100644 --- a/src/trunnel/socks5.h +++ b/src/trunnel/socks5.h @@ -1,4 +1,4 @@ -/* socks5.h -- generated by Trunnel v1.5.2. +/* socks5.h -- generated by Trunnel v1.5.3. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 64dccf2e1d..25bfb9ada5 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -218,7 +218,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.4.1.4-rc" +#define VERSION "0.4.2.0-alpha-dev" |