123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 |
- /* 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>
- #include <errno.h>
- //////
- // CONFIG_TYPE_STRING
- // CONFIG_TYPE_FILENAME
- //
- // These two types are the same for now, but they have different names.
- //
- // Warning: For this type, the default value (NULL) and "" are considered
- // different values. That is generally risky, and best avoided for other
- // types in the future.
- //////
- static int
- string_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
- {
- (void)params;
- (void)errmsg;
- (void)key;
- 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 char *key)
- {
- (void)key;
- 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, const char *key)
- {
- (void)params;
- (void)errmsg;
- (void)key;
- 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 char *key)
- {
- (void)key;
- 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 char *key)
- {
- (void)key;
- 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, const char *key)
- {
- (void)params;
- (void)errmsg;
- (void)key;
- double *v = (double*)target;
- char *endptr=NULL;
- errno = 0;
- *v = strtod(value, &endptr);
- if (endptr == value || *endptr != '\0') {
- // Either there are no converted characters, or there were some characters
- // that didn't get converted.
- tor_asprintf(errmsg, "Could not convert %s to a number.", escaped(value));
- return -1;
- }
- if (errno == ERANGE) {
- // strtod will set errno to ERANGE on underflow or overflow.
- bool underflow = -.00001 < *v && *v < .00001;
- tor_asprintf(errmsg,
- "%s is too %s to express as a floating-point number.",
- escaped(value), underflow ? "small" : "large");
- return -1;
- }
- 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 char *key)
- {
- (void)key;
- 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, const char *key)
- {
- (void) params;
- (void) key;
- 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, const char *key)
- {
- (void)params;
- (void)errmsg;
- (void)key;
- 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, const char *key)
- {
- (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, key);
- 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 void
- linelist_mark_fragile(void *target, const void *params)
- {
- (void)params;
- config_line_t **ptr = (config_line_t **)target;
- if (*ptr)
- (*ptr)->fragile = 1;
- }
- 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,
- .mark_fragile = linelist_mark_fragile,
- };
- 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,
- .mark_fragile = linelist_mark_fragile,
- };
- 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, const char *key)
- {
- (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%s%s%s",
- key && *key ? " \"" : "",
- key && *key ? key : "",
- key && *key ? "\"." : ".");
- 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] = { .name="String", .fns=&string_fns },
- [CONFIG_TYPE_FILENAME] = { .name="Filename", .fns=&string_fns },
- [CONFIG_TYPE_INT] = { .name="SignedInteger", .fns=&int_fns,
- .params=&INT_PARSE_UNRESTRICTED },
- [CONFIG_TYPE_POSINT] = { .name="Integer", .fns=&int_fns,
- .params=&INT_PARSE_POSINT },
- [CONFIG_TYPE_UINT64] = { .name="Integer", .fns=&uint64_fns, },
- [CONFIG_TYPE_MEMUNIT] = { .name="DataSize", .fns=&memunit_fns,
- .params=&memory_units },
- [CONFIG_TYPE_INTERVAL] = { .name="TimeInterval", .fns=&interval_fns,
- .params=&time_units },
- [CONFIG_TYPE_MSEC_INTERVAL] = { .name="TimeMsecInterval",
- .fns=&interval_fns,
- .params=&time_msec_units },
- [CONFIG_TYPE_DOUBLE] = { .name="Float", .fns=&double_fns, },
- [CONFIG_TYPE_BOOL] = { .name="Boolean", .fns=&enum_fns,
- .params=&enum_table_bool },
- [CONFIG_TYPE_AUTOBOOL] = { .name="Boolean+Auto", .fns=&enum_fns,
- .params=&enum_table_autobool },
- [CONFIG_TYPE_ISOTIME] = { .name="Time", .fns=&time_fns, },
- [CONFIG_TYPE_CSV] = { .name="CommaList", .fns=&csv_fns, },
- [CONFIG_TYPE_CSV_INTERVAL] = { .name="TimeInterval",
- .fns=&legacy_csv_interval_fns, },
- [CONFIG_TYPE_LINELIST] = { .name="LineList", .fns=&linelist_fns,
- .flags=CFLG_NOREPLACE },
- /*
- * A "linelist_s" is a derived view of a linelist_v: inspecting
- * it gets part of a linelist_v, and setting it adds to the linelist_v.
- */
- [CONFIG_TYPE_LINELIST_S] = { .name="Dependent", .fns=&linelist_s_fns,
- .flags=CFLG_NOREPLACE|
- /* The operations we disable here are
- * handled by the linelist_v. */
- CFLG_NOCOPY|CFLG_NOCMP|CFLG_NODUMP },
- [CONFIG_TYPE_LINELIST_V] = { .name="Virtual", .fns=&linelist_v_fns,
- .flags=CFLG_NOREPLACE|CFLG_NOSET },
- [CONFIG_TYPE_OBSOLETE] = {
- .name="Obsolete", .fns=&ignore_fns,
- .flags=CFLG_GROUP_OBSOLETE,
- }
- };
- /**
- * 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];
- }
|