summaryrefslogtreecommitdiff
path: root/src/lib/confmgt
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/confmgt')
-rw-r--r--src/lib/confmgt/.may_include1
-rw-r--r--src/lib/confmgt/confmgt.c (renamed from src/lib/confmgt/confparse.c)191
-rw-r--r--src/lib/confmgt/confmgt.h (renamed from src/lib/confmgt/confparse.h)134
-rw-r--r--src/lib/confmgt/include.am4
-rw-r--r--src/lib/confmgt/lib_confmgt.md7
-rw-r--r--src/lib/confmgt/structvar.c24
-rw-r--r--src/lib/confmgt/structvar.h2
-rw-r--r--src/lib/confmgt/type_defs.c244
-rw-r--r--src/lib/confmgt/type_defs.h2
-rw-r--r--src/lib/confmgt/typedvar.c22
-rw-r--r--src/lib/confmgt/typedvar.h4
-rw-r--r--src/lib/confmgt/unitparse.c70
-rw-r--r--src/lib/confmgt/unitparse.h5
-rw-r--r--src/lib/confmgt/var_type_def_st.h7
14 files changed, 473 insertions, 244 deletions
diff --git a/src/lib/confmgt/.may_include b/src/lib/confmgt/.may_include
index 2564133917..5ff949f103 100644
--- a/src/lib/confmgt/.may_include
+++ b/src/lib/confmgt/.may_include
@@ -4,6 +4,7 @@ lib/conf/*.h
lib/confmgt/*.h
lib/container/*.h
lib/encoding/*.h
+lib/intmath/*.h
lib/log/*.h
lib/malloc/*.h
lib/string/*.h
diff --git a/src/lib/confmgt/confparse.c b/src/lib/confmgt/confmgt.c
index 08e562f654..bf2764160e 100644
--- a/src/lib/confmgt/confparse.c
+++ b/src/lib/confmgt/confmgt.c
@@ -1,11 +1,11 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
- * \file confparse.c
+ * \file confmgt.c
*
* \brief Back-end for parsing and generating key-value files, used to
* implement the torrc file format and the state file.
@@ -21,9 +21,9 @@
* specified, and a linked list of key-value pairs.
*/
-#define CONFPARSE_PRIVATE
+#define CONFMGT_PRIVATE
#include "orconfig.h"
-#include "lib/confmgt/confparse.h"
+#include "lib/confmgt/confmgt.h"
#include "lib/confmgt/structvar.h"
#include "lib/confmgt/unitparse.h"
@@ -169,9 +169,14 @@ config_mgr_register_fmt(config_mgr_t *mgr,
"it had been frozen.");
if (object_idx != IDX_TOPLEVEL) {
- tor_assertf(fmt->config_suite_offset < 0,
+ tor_assertf(! fmt->has_config_suite,
"Tried to register a toplevel format in a non-toplevel position");
}
+ if (fmt->config_suite_offset) {
+ tor_assertf(fmt->has_config_suite,
+ "config_suite_offset was set, but has_config_suite was not.");
+ }
+
tor_assertf(fmt != mgr->toplevel &&
! smartlist_contains(mgr->subconfigs, fmt),
"Tried to register an already-registered format.");
@@ -223,7 +228,7 @@ config_mgr_add_format(config_mgr_t *mgr,
static inline config_suite_t **
config_mgr_get_suite_ptr(const config_mgr_t *mgr, void *toplevel)
{
- if (mgr->toplevel->config_suite_offset < 0)
+ if (! mgr->toplevel->has_config_suite)
return NULL;
return STRUCT_VAR_P(toplevel, mgr->toplevel->config_suite_offset);
}
@@ -237,7 +242,7 @@ config_mgr_get_suite_ptr(const config_mgr_t *mgr, void *toplevel)
* to configuration objects for other modules. This function gets
* the sub-object for a particular module.
*/
-STATIC void *
+void *
config_mgr_get_obj_mutable(const config_mgr_t *mgr, void *toplevel, int idx)
{
tor_assert(mgr);
@@ -256,7 +261,7 @@ config_mgr_get_obj_mutable(const config_mgr_t *mgr, void *toplevel, int idx)
}
/** As config_mgr_get_obj_mutable(), but return a const pointer. */
-STATIC const void *
+const void *
config_mgr_get_obj(const config_mgr_t *mgr, const void *toplevel, int idx)
{
return config_mgr_get_obj_mutable(mgr, (void*)toplevel, idx);
@@ -334,6 +339,17 @@ config_mgr_list_deprecated_vars(const config_mgr_t *mgr)
return result;
}
+/**
+ * Check the magic number on <b>object</b> to make sure it's a valid toplevel
+ * object, created with <b>mgr</b>. Exit with an assertion if it isn't.
+ **/
+void
+config_check_toplevel_magic(const config_mgr_t *mgr,
+ const void *object)
+{
+ struct_check_magic(object, &mgr->toplevel_magic);
+}
+
/** Assert that the magic fields in <b>options</b> and its subsidiary
* objects are all okay. */
static void
@@ -641,6 +657,14 @@ config_assign_value(const config_mgr_t *mgr, void *options,
tor_assert(!strcmp(c->key, var->cvar->member.name));
void *object = config_mgr_get_obj_mutable(mgr, options, var->object_idx);
+ if (config_var_has_flag(var->cvar, CFLG_WARN_OBSOLETE)) {
+ log_warn(LD_GENERAL, "Skipping obsolete configuration option \"%s\".",
+ var->cvar->member.name);
+ } else if (config_var_has_flag(var->cvar, CFLG_WARN_DISABLED)) {
+ log_warn(LD_GENERAL, "This copy of Tor was built without support for "
+ "the option \"%s\". Skipping.", var->cvar->member.name);
+ }
+
return struct_var_kvassign(object, c, msg, &var->cvar->member);
}
@@ -1142,6 +1166,146 @@ config_init(const config_mgr_t *mgr, void *options)
} SMARTLIST_FOREACH_END(mv);
}
+/**
+ * Helper for config_validate_single: see whether any immutable option
+ * has changed between old_options and new_options.
+ *
+ * On success return 0; on failure set *msg_out to a newly allocated
+ * string explaining what is wrong, and return -1.
+ */
+static int
+config_check_immutable_flags(const config_format_t *fmt,
+ const void *old_options,
+ const void *new_options,
+ char **msg_out)
+{
+ tor_assert(fmt);
+ tor_assert(new_options);
+ if (BUG(! old_options))
+ return 0;
+
+ unsigned i;
+ for (i = 0; fmt->vars[i].member.name; ++i) {
+ const config_var_t *v = &fmt->vars[i];
+ if (! config_var_has_flag(v, CFLG_IMMUTABLE))
+ continue;
+
+ if (! struct_var_eq(old_options, new_options, &v->member)) {
+ tor_asprintf(msg_out,
+ "While Tor is running, changing %s is not allowed",
+ v->member.name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Normalize and validate a single object `options` within a configuration
+ * suite, according to its format. `options` may be modified as appropriate
+ * in order to set ancillary data. If `old_options` is provided, make sure
+ * that the transition from `old_options` to `options` is permitted.
+ *
+ * On success return VSTAT_OK; on failure set *msg_out to a newly allocated
+ * string explaining what is wrong, and return a different validation_status_t
+ * to describe which step failed.
+ **/
+static validation_status_t
+config_validate_single(const config_format_t *fmt,
+ const void *old_options, void *options,
+ char **msg_out)
+{
+ tor_assert(fmt);
+ tor_assert(options);
+
+ if (fmt->pre_normalize_fn) {
+ if (fmt->pre_normalize_fn(options, msg_out) < 0) {
+ return VSTAT_PRE_NORMALIZE_ERR;
+ }
+ }
+
+ if (fmt->legacy_validate_fn) {
+ if (fmt->legacy_validate_fn(old_options, options, msg_out) < 0) {
+ return VSTAT_LEGACY_ERR;
+ }
+ }
+
+ if (fmt->validate_fn) {
+ if (fmt->validate_fn(options, msg_out) < 0) {
+ return VSTAT_VALIDATE_ERR;
+ }
+ }
+
+ if (old_options) {
+ if (config_check_immutable_flags(fmt, old_options, options, msg_out) < 0) {
+ return VSTAT_TRANSITION_ERR;
+ }
+
+ if (fmt->check_transition_fn) {
+ if (fmt->check_transition_fn(old_options, options, msg_out) < 0) {
+ return VSTAT_TRANSITION_ERR;
+ }
+ }
+ }
+
+ if (fmt->post_normalize_fn) {
+ if (fmt->post_normalize_fn(options, msg_out) < 0) {
+ return VSTAT_POST_NORMALIZE_ERR;
+ }
+ }
+
+ return VSTAT_OK;
+}
+
+/**
+ * Normalize and validate all the options in configuration object `options`
+ * and its sub-objects. `options` may be modified as appropriate in order to
+ * set ancillary data. If `old_options` is provided, make sure that the
+ * transition from `old_options` to `options` is permitted.
+ *
+ * On success return VSTAT_OK; on failure set *msg_out to a newly allocated
+ * string explaining what is wrong, and return a different validation_status_t
+ * to describe which step failed.
+ **/
+validation_status_t
+config_validate(const config_mgr_t *mgr,
+ const void *old_options, void *options,
+ char **msg_out)
+{
+ validation_status_t rv;
+ CONFIG_CHECK(mgr, options);
+ if (old_options) {
+ CONFIG_CHECK(mgr, old_options);
+ }
+
+ config_suite_t **suitep_new = config_mgr_get_suite_ptr(mgr, options);
+ config_suite_t **suitep_old = NULL;
+ if (old_options)
+ suitep_old = config_mgr_get_suite_ptr(mgr, (void*) old_options);
+
+ /* Validate the sub-objects */
+ if (suitep_new) {
+ SMARTLIST_FOREACH_BEGIN(mgr->subconfigs, const config_format_t *, fmt) {
+ void *obj = smartlist_get((*suitep_new)->configs, fmt_sl_idx);
+ const void *obj_old=NULL;
+ if (suitep_old)
+ obj_old = smartlist_get((*suitep_old)->configs, fmt_sl_idx);
+
+ rv = config_validate_single(fmt, obj_old, obj, msg_out);
+ if (rv < 0)
+ return rv;
+ } SMARTLIST_FOREACH_END(fmt);
+ }
+
+ /* Validate the top-level object. */
+ rv = config_validate_single(mgr->toplevel, old_options, options, msg_out);
+ if (rv < 0)
+ return rv;
+
+ return VSTAT_OK;
+}
+
/** Allocate and return a new string holding the written-out values of the vars
* in 'options'. If 'minimal', do not write out any default-valued vars.
* Else, if comment_defaults, write default values as comments.
@@ -1166,7 +1330,7 @@ config_dump(const config_mgr_t *mgr, 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) {
+ if (config_validate(mgr, NULL, defaults_tmp, &msg) < 0) {
// LCOV_EXCL_START
log_err(LD_BUG, "Failed to validate default config: %s", msg);
tor_free(msg);
@@ -1197,9 +1361,10 @@ config_dump(const config_mgr_t *mgr, const void *default_options,
*/
continue;
}
- smartlist_add_asprintf(elements, "%s%s %s\n",
+ int value_exists = line->value && *(line->value);
+ smartlist_add_asprintf(elements, "%s%s%s%s\n",
comment_option ? "# " : "",
- line->key, line->value);
+ line->key, value_exists ? " " : "", line->value);
}
config_free_lines(assigned);
} SMARTLIST_FOREACH_END(mv);
@@ -1207,7 +1372,9 @@ config_dump(const config_mgr_t *mgr, const void *default_options,
if (fmt->extra) {
line = *(config_line_t**)STRUCT_VAR_P(options, fmt->extra->offset);
for (; line; line = line->next) {
- smartlist_add_asprintf(elements, "%s %s\n", line->key, line->value);
+ int value_exists = line->value && *(line->value);
+ smartlist_add_asprintf(elements, "%s%s%s\n",
+ line->key, value_exists ? " " : "", line->value);
}
}
diff --git a/src/lib/confmgt/confparse.h b/src/lib/confmgt/confmgt.h
index 2332f69790..5065c13b60 100644
--- a/src/lib/confmgt/confparse.h
+++ b/src/lib/confmgt/confmgt.h
@@ -1,116 +1,23 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
- * \file confparse.h
+ * \file confmgt.h
*
- * \brief Header for confparse.c.
+ * \brief Header for confmgt.c.
*/
-#ifndef TOR_CONFPARSE_H
-#define TOR_CONFPARSE_H
+#ifndef TOR_CONFMGT_H
+#define TOR_CONFMGT_H
#include "lib/conf/conftypes.h"
#include "lib/conf/confmacros.h"
#include "lib/testsupport/testsupport.h"
/**
- * An abbreviation or alias for a configuration option.
- **/
-typedef struct config_abbrev_t {
- /** The option name as abbreviated. Not case-sensitive. */
- const char *abbreviated;
- /** The full name of the option. Not case-sensitive. */
- const char *full;
- /** True if this abbreviation should only be allowed on the command line. */
- int commandline_only;
- /** True if we should warn whenever this abbreviation is used. */
- int warn;
-} config_abbrev_t;
-
-/**
- * A note that a configuration option is deprecated, with an explanation why.
- */
-typedef struct config_deprecation_t {
- /** The option that is deprecated. */
- const char *name;
- /** A user-facing string explaining why the option is deprecated. */
- const char *why_deprecated;
-} config_deprecation_t;
-
-/**
- * Handy macro for declaring "In the config file or on the command line, you
- * can abbreviate <b>tok</b>s as <b>tok</b>". Used inside an array of
- * config_abbrev_t.
- *
- * For example, to declare "NumCpu" as an abbreviation for "NumCPUs",
- * you can say PLURAL(NumCpu).
- **/
-#define PLURAL(tok) { #tok, #tok "s", 0, 0 }
-
-/**
- * Type of a callback to validate whether a given configuration is
- * well-formed and consistent.
- *
- * The configuration to validate is passed as <b>newval</b>. The previous
- * configuration, if any, is provided in <b>oldval</b>. The
- * <b>default_val</b> argument receives a configuration object initialized
- * with default values for all its fields. The <b>from_setconf</b> argument
- * is true iff the input comes from a SETCONF controller command.
- *
- * On success, return 0. On failure, set *<b>msg_out</b> to a newly allocated
- * error message, and return -1.
- *
- * REFACTORING NOTE: Currently, this callback type is only used from inside
- * config_dump(); later in our refactoring, it will be cleaned up and used
- * more generally.
- */
-typedef int (*validate_fn_t)(void *oldval,
- void *newval,
- void *default_val,
- int from_setconf,
- char **msg_out);
-
-struct config_mgr_t;
-
-/**
- * Callback to clear all non-managed fields of a configuration object.
- *
- * <b>obj</b> is the configuration object whose non-managed fields should be
- * cleared.
- *
- * (Regular fields get cleared by config_reset(), but you might have fields
- * in the object that do not correspond to configuration variables. If those
- * fields need to be cleared or freed, this is where to do it.)
- */
-typedef void (*clear_cfg_fn_t)(const struct config_mgr_t *mgr, void *obj);
-
-/** Information on the keys, value types, key-to-struct-member mappings,
- * variable descriptions, validation functions, and abbreviations for a
- * configuration or storage format. */
-typedef struct config_format_t {
- size_t size; /**< Size of the struct that everything gets parsed into. */
- struct_magic_decl_t magic; /**< Magic number info for this struct. */
- const config_abbrev_t *abbrevs; /**< List of abbreviations that we expand
- * when parsing this format. */
- const config_deprecation_t *deprecations; /** List of deprecated options */
- const config_var_t *vars; /**< List of variables we recognize, their default
- * values, and where we stick them in the
- * structure. */
- validate_fn_t validate_fn; /**< Function to validate config. */
- clear_cfg_fn_t clear_fn; /**< Function to clear the configuration. */
- /** If present, extra denotes a LINELIST variable for unrecognized
- * lines. Otherwise, unrecognized lines are an error. */
- const struct_member_t *extra;
- /** The position of a config_suite_t pointer within the toplevel object,
- * or -1 if there is no such pointer. */
- ptrdiff_t config_suite_offset;
-} config_format_t;
-
-/**
* A collection of config_format_t objects to describe several objects
* that are all configured with the same configuration file.
*
@@ -171,10 +78,26 @@ int config_is_same(const config_mgr_t *fmt,
struct config_line_t *config_get_changes(const config_mgr_t *mgr,
const void *options1, const void *options2);
void config_init(const config_mgr_t *mgr, void *options);
+
+/** An enumeration to report which validation step failed. */
+typedef enum {
+ VSTAT_PRE_NORMALIZE_ERR = -5,
+ VSTAT_VALIDATE_ERR = -4,
+ VSTAT_LEGACY_ERR = -3,
+ VSTAT_TRANSITION_ERR = -2,
+ VSTAT_POST_NORMALIZE_ERR = -1,
+ VSTAT_OK = 0,
+} validation_status_t;
+
+validation_status_t config_validate(const config_mgr_t *mgr,
+ const void *old_options, void *options,
+ char **msg_out);
void *config_dup(const config_mgr_t *mgr, const void *old);
char *config_dump(const config_mgr_t *mgr, const void *default_options,
const void *options, int minimal,
int comment_defaults);
+void config_check_toplevel_magic(const config_mgr_t *mgr,
+ const void *object);
bool config_check_ok(const config_mgr_t *mgr, const void *options,
int severity);
int config_assign(const config_mgr_t *mgr, void *options,
@@ -200,13 +123,14 @@ bool config_var_is_listable(const config_var_t *var);
#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
+void *config_mgr_get_obj_mutable(const config_mgr_t *mgr,
+ void *toplevel, int idx);
+const void *config_mgr_get_obj(const config_mgr_t *mgr,
+ const void *toplevel, int idx);
+
+#ifdef CONFMGT_PRIVATE
STATIC void config_reset_line(const config_mgr_t *mgr, void *options,
const char *key, int use_defaults);
-STATIC void *config_mgr_get_obj_mutable(const config_mgr_t *mgr,
- void *toplevel, int idx);
-STATIC const void *config_mgr_get_obj(const config_mgr_t *mgr,
- const void *toplevel, int idx);
-#endif /* defined(CONFPARSE_PRIVATE) */
+#endif /* defined(CONFMGT_PRIVATE) */
-#endif /* !defined(TOR_CONFPARSE_H) */
+#endif /* !defined(TOR_CONFMGT_H) */
diff --git a/src/lib/confmgt/include.am b/src/lib/confmgt/include.am
index 81cd868e5e..d3a7a7cd69 100644
--- a/src/lib/confmgt/include.am
+++ b/src/lib/confmgt/include.am
@@ -6,7 +6,7 @@ endif
# ADD_C_FILE: INSERT SOURCES HERE.
src_lib_libtor_confmgt_a_SOURCES = \
- src/lib/confmgt/confparse.c \
+ src/lib/confmgt/confmgt.c \
src/lib/confmgt/structvar.c \
src/lib/confmgt/type_defs.c \
src/lib/confmgt/typedvar.c \
@@ -19,7 +19,7 @@ src_lib_libtor_confmgt_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
- src/lib/confmgt/confparse.h \
+ src/lib/confmgt/confmgt.h \
src/lib/confmgt/structvar.h \
src/lib/confmgt/type_defs.h \
src/lib/confmgt/typedvar.h \
diff --git a/src/lib/confmgt/lib_confmgt.md b/src/lib/confmgt/lib_confmgt.md
new file mode 100644
index 0000000000..861e720f64
--- /dev/null
+++ b/src/lib/confmgt/lib_confmgt.md
@@ -0,0 +1,7 @@
+@dir /lib/confmgt
+@brief lib/confmgt: Parse, encode, manipulate configuration files.
+
+This logic is used in common by our state files (statefile.c) and
+configuration files (config.c) to manage a set of named, typed fields,
+reading and writing them to disk and to the controller.
+
diff --git a/src/lib/confmgt/structvar.c b/src/lib/confmgt/structvar.c
index 7a3b8c7df2..55deb4759c 100644
--- a/src/lib/confmgt/structvar.c
+++ b/src/lib/confmgt/structvar.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -30,13 +30,28 @@
#include <stddef.h>
/**
+ * Return true iff all fields on <b>decl</b> are NULL or 0, indicating that
+ * there is no object or no magic number to check.
+ **/
+static inline bool
+magic_is_null(const struct_magic_decl_t *decl)
+{
+ return decl->typename == NULL &&
+ decl->magic_offset == 0 &&
+ decl->magic_val == 0;
+}
+
+/**
* Set the 'magic number' on <b>object</b> to correspond to decl.
**/
void
struct_set_magic(void *object, const struct_magic_decl_t *decl)
{
- tor_assert(object);
tor_assert(decl);
+ if (magic_is_null(decl))
+ return;
+
+ tor_assert(object);
uint32_t *ptr = STRUCT_VAR_P(object, decl->magic_offset);
*ptr = decl->magic_val;
}
@@ -47,8 +62,11 @@ struct_set_magic(void *object, const struct_magic_decl_t *decl)
void
struct_check_magic(const void *object, const struct_magic_decl_t *decl)
{
- tor_assert(object);
tor_assert(decl);
+ if (magic_is_null(decl))
+ return;
+
+ tor_assert(object);
const uint32_t *ptr = STRUCT_VAR_P(object, decl->magic_offset);
tor_assertf(*ptr == decl->magic_val,
diff --git a/src/lib/confmgt/structvar.h b/src/lib/confmgt/structvar.h
index bcb4b58c3f..91334fa8c5 100644
--- a/src/lib/confmgt/structvar.h
+++ b/src/lib/confmgt/structvar.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/lib/confmgt/type_defs.c b/src/lib/confmgt/type_defs.c
index 5066e12265..d9e5e1e4c2 100644
--- a/src/lib/confmgt/type_defs.c
+++ b/src/lib/confmgt/type_defs.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -17,12 +17,12 @@
#include "orconfig.h"
#include "lib/conf/conftypes.h"
+#include "lib/conf/confdecl.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"
@@ -52,11 +52,10 @@
static int
string_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
(void)params;
(void)errmsg;
- (void)key;
char **p = (char**)target;
*p = tor_strdup(value);
return 0;
@@ -91,9 +90,12 @@ static const var_type_fns_t string_fns = {
// These types are implemented as int, possibly with a restricted range.
/////
+/**
+ * Parameters for parsing an integer type.
+ **/
typedef struct int_type_params_t {
- int minval;
- int maxval;
+ int minval; /**< Lowest allowed value */
+ int maxval; /**< Highest allowed value */
} int_parse_params_t;
static const int_parse_params_t INT_PARSE_UNRESTRICTED = {
@@ -107,10 +109,8 @@ static const int_parse_params_t INT_PARSE_POSINT = {
};
static int
-int_parse(void *target, const char *value, char **errmsg, const void *params,
- const char *key)
+int_parse(void *target, const char *value, char **errmsg, const void *params)
{
- (void)key;
const int_parse_params_t *pp;
if (params) {
pp = params;
@@ -121,8 +121,9 @@ int_parse(void *target, const char *value, char **errmsg, const void *params,
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);
+ tor_asprintf(errmsg, "Integer %s is malformed or out of bounds. "
+ "Allowed values are between %d and %d.",
+ value, pp->minval, pp->maxval);
return -1;
}
return 0;
@@ -172,11 +173,10 @@ static const var_type_fns_t int_fns = {
static int
uint64_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
(void)params;
(void)errmsg;
- (void)key;
uint64_t *p = target;
int ok=0;
*p = tor_parse_uint64(value, 10, 0, UINT64_MAX, &ok, NULL);
@@ -223,35 +223,45 @@ static const var_type_fns_t uint64_fns = {
static int
units_parse_u64(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
- (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);
+ char *msg = NULL;
+ *v = config_parse_units(value, table, &ok, &msg);
if (!ok) {
- *errmsg = tor_strdup("Provided value is malformed or out of bounds.");
+ tor_asprintf(errmsg, "Provided value is malformed or out of bounds: %s",
+ msg);
+ tor_free(msg);
return -1;
}
+ if (BUG(msg)) {
+ tor_free(msg);
+ }
return 0;
}
static int
units_parse_int(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
- (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);
+ char *msg = NULL;
+ uint64_t u64 = config_parse_units(value, table, &ok, &msg);
if (!ok) {
- *errmsg = tor_strdup("Provided value is malformed or out of bounds.");
+ tor_asprintf(errmsg, "Provided value is malformed or out of bounds: %s",
+ msg);
+ tor_free(msg);
return -1;
}
+ if (BUG(msg)) {
+ tor_free(msg);
+ }
if (u64 > INT_MAX) {
tor_asprintf(errmsg, "Provided value %s is too large", value);
return -1;
@@ -289,11 +299,10 @@ static const var_type_fns_t interval_fns = {
static int
double_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
(void)params;
(void)errmsg;
- (void)key;
double *v = (double*)target;
char *endptr=NULL;
errno = 0;
@@ -352,12 +361,17 @@ typedef struct enumeration_table_t {
int value;
} enumeration_table_t;
+typedef struct enumeration_params_t {
+ const char *allowed_val_string;
+ const enumeration_table_t *table;
+} enumeration_params_t;
+
static int
enum_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params_)
{
- (void)key;
- const enumeration_table_t *table = params;
+ const enumeration_params_t *params = params_;
+ const enumeration_table_t *table = params->table;
int *p = (int *)target;
for (; table->name; ++table) {
if (!strcasecmp(value, table->name)) {
@@ -365,15 +379,17 @@ enum_parse(void *target, const char *value, char **errmsg,
return 0;
}
}
- tor_asprintf(errmsg, "Unrecognized value %s.", value);
+ tor_asprintf(errmsg, "Unrecognized value %s. %s",
+ value, params->allowed_val_string);
return -1;
}
static char *
-enum_encode(const void *value, const void *params)
+enum_encode(const void *value, const void *params_)
{
int v = *(const int*)value;
- const enumeration_table_t *table = params;
+ const enumeration_params_t *params = params_;
+ const enumeration_table_t *table = params->table;
for (; table->name; ++table) {
if (v == table->value)
return tor_strdup(table->name);
@@ -382,19 +398,21 @@ enum_encode(const void *value, const void *params)
}
static void
-enum_clear(void *value, const void *params)
+enum_clear(void *value, const void *params_)
{
int *p = (int*)value;
- const enumeration_table_t *table = params;
+ const enumeration_params_t *params = params_;
+ const enumeration_table_t *table = params->table;
tor_assert(table->name);
*p = table->value;
}
static bool
-enum_ok(const void *value, const void *params)
+enum_ok(const void *value, const void *params_)
{
int v = *(const int*)value;
- const enumeration_table_t *table = params;
+ const enumeration_params_t *params = params_;
+ const enumeration_table_t *table = params->table;
for (; table->name; ++table) {
if (v == table->value)
return true;
@@ -408,6 +426,11 @@ static const enumeration_table_t enum_table_bool[] = {
{ NULL, 0 },
};
+static const enumeration_params_t enum_params_bool = {
+ "Allowed values are 0 and 1.",
+ enum_table_bool
+};
+
static const enumeration_table_t enum_table_autobool[] = {
{ "0", 0 },
{ "1", 1 },
@@ -415,6 +438,11 @@ static const enumeration_table_t enum_table_autobool[] = {
{ NULL, 0 },
};
+static const enumeration_params_t enum_params_autobool = {
+ "Allowed values are 0, 1, and auto.",
+ enum_table_autobool
+};
+
static const var_type_fns_t enum_fns = {
.parse = enum_parse,
.encode = enum_encode,
@@ -430,10 +458,9 @@ static const var_type_fns_t enum_fns = {
static int
time_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
(void) params;
- (void) key;
time_t *p = target;
if (parse_iso_time(value, p) < 0) {
tor_asprintf(errmsg, "Invalid time %s", escaped(value));
@@ -475,11 +502,10 @@ static const var_type_fns_t time_fns = {
static int
csv_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
(void)params;
(void)errmsg;
- (void)key;
smartlist_t **sl = (smartlist_t**)target;
*sl = smartlist_new();
smartlist_split_string(*sl, value, ",",
@@ -525,7 +551,7 @@ static const var_type_fns_t csv_fns = {
static int
legacy_csv_interval_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ const void *params)
{
(void)params;
/* We used to have entire smartlists here. But now that all of our
@@ -539,7 +565,7 @@ legacy_csv_interval_parse(void *target, const char *value, char **errmsg,
val = tmp;
}
- int rv = units_parse_int(target, val, errmsg, &time_units, key);
+ int rv = units_parse_int(target, val, errmsg, &time_units);
tor_free(tmp);
return rv;
}
@@ -688,32 +714,23 @@ static const var_type_fns_t linelist_s_fns = {
/////
// 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.
+// CONFIG_TYPE_IGNORE
//
-// XXXX This is not a type, and should be handled at a higher level of
-// XXXX abstraction.
+// Used to indicate an option that cannot be stored or encoded.
/////
static int
ignore_parse(void *target, const char *value, char **errmsg,
- const void *params, const char *key)
+ 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%s%s%s",
- key && *key ? " \"" : "",
- key && *key ? key : "",
- key && *key ? "\"." : ".");
return 0;
}
@@ -730,50 +747,91 @@ static const var_type_fns_t ignore_fns = {
.encode = ignore_encode,
};
+const var_type_def_t STRING_type_defn = {
+ .name="String", .fns=&string_fns };
+const var_type_def_t FILENAME_type_defn = {
+ .name="Filename", .fns=&string_fns };
+const var_type_def_t INT_type_defn = {
+ .name="SignedInteger", .fns=&int_fns,
+ .params=&INT_PARSE_UNRESTRICTED };
+const var_type_def_t POSINT_type_defn = {
+ .name="Integer", .fns=&int_fns,
+ .params=&INT_PARSE_POSINT };
+const var_type_def_t UINT64_type_defn = {
+ .name="Integer", .fns=&uint64_fns, };
+const var_type_def_t MEMUNIT_type_defn = {
+ .name="DataSize", .fns=&memunit_fns,
+ .params=&memory_units };
+const var_type_def_t INTERVAL_type_defn = {
+ .name="TimeInterval", .fns=&interval_fns,
+ .params=&time_units };
+const var_type_def_t MSEC_INTERVAL_type_defn = {
+ .name="TimeMsecInterval",
+ .fns=&interval_fns,
+ .params=&time_msec_units };
+const var_type_def_t DOUBLE_type_defn = {
+ .name="Float", .fns=&double_fns, };
+const var_type_def_t BOOL_type_defn = {
+ .name="Boolean", .fns=&enum_fns,
+ .params=&enum_params_bool };
+const var_type_def_t AUTOBOOL_type_defn = {
+ .name="Boolean+Auto", .fns=&enum_fns,
+ .params=&enum_params_autobool };
+const var_type_def_t ISOTIME_type_defn = {
+ .name="Time", .fns=&time_fns, };
+const var_type_def_t CSV_type_defn = {
+ .name="CommaList", .fns=&csv_fns, };
+const var_type_def_t CSV_INTERVAL_type_defn = {
+ .name="TimeInterval",
+ .fns=&legacy_csv_interval_fns, };
+const var_type_def_t LINELIST_type_defn = {
+ .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.
+ */
+const var_type_def_t LINELIST_S_type_defn = {
+ .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 };
+const var_type_def_t LINELIST_V_type_defn = {
+ .name="Virtual", .fns=&linelist_v_fns,
+ .flags=CFLG_NOREPLACE|CFLG_NOSET };
+const var_type_def_t IGNORE_type_defn = {
+ .name="Ignored", .fns=&ignore_fns,
+ .flags=CFLG_NOCOPY|CFLG_NOCMP|CFLG_NODUMP|CFLG_NOSET,
+};
+const var_type_def_t OBSOLETE_type_defn = {
+ .name="Obsolete", .fns=&ignore_fns,
+ .flags=CFLG_GROUP_OBSOLETE,
+};
+
/**
* 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,
- }
+static const var_type_def_t *type_definitions_table[] = {
+ [CONFIG_TYPE_STRING] = &STRING_type_defn,
+ [CONFIG_TYPE_FILENAME] = &FILENAME_type_defn,
+ [CONFIG_TYPE_INT] = &INT_type_defn,
+ [CONFIG_TYPE_POSINT] = &POSINT_type_defn,
+ [CONFIG_TYPE_UINT64] = &UINT64_type_defn,
+ [CONFIG_TYPE_MEMUNIT] = &MEMUNIT_type_defn,
+ [CONFIG_TYPE_INTERVAL] = &INTERVAL_type_defn,
+ [CONFIG_TYPE_MSEC_INTERVAL] = &MSEC_INTERVAL_type_defn,
+ [CONFIG_TYPE_DOUBLE] = &DOUBLE_type_defn,
+ [CONFIG_TYPE_BOOL] = &BOOL_type_defn,
+ [CONFIG_TYPE_AUTOBOOL] = &AUTOBOOL_type_defn,
+ [CONFIG_TYPE_ISOTIME] = &ISOTIME_type_defn,
+ [CONFIG_TYPE_CSV] = &CSV_type_defn,
+ [CONFIG_TYPE_CSV_INTERVAL] = &CSV_INTERVAL_type_defn,
+ [CONFIG_TYPE_LINELIST] = &LINELIST_type_defn,
+ [CONFIG_TYPE_LINELIST_S] = &LINELIST_S_type_defn,
+ [CONFIG_TYPE_LINELIST_V] = &LINELIST_V_type_defn,
+ [CONFIG_TYPE_IGNORE] = &IGNORE_type_defn,
+ [CONFIG_TYPE_OBSOLETE] = &OBSOLETE_type_defn,
};
/**
@@ -787,5 +845,5 @@ lookup_type_def(config_type_t type)
tor_assert(t >= 0);
if (t >= (int)ARRAY_LENGTH(type_definitions_table))
return NULL;
- return &type_definitions_table[t];
+ return type_definitions_table[t];
}
diff --git a/src/lib/confmgt/type_defs.h b/src/lib/confmgt/type_defs.h
index ecf040529e..fec002b1d3 100644
--- a/src/lib/confmgt/type_defs.h
+++ b/src/lib/confmgt/type_defs.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/lib/confmgt/typedvar.c b/src/lib/confmgt/typedvar.c
index ce11a69379..1955302cdc 100644
--- a/src/lib/confmgt/typedvar.c
+++ b/src/lib/confmgt/typedvar.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -24,6 +24,7 @@
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
#include "lib/string/util_string.h"
#include "lib/confmgt/var_type_def_st.h"
@@ -33,8 +34,7 @@
/**
* Try to parse a string in <b>value</b> that encodes an object of the type
- * defined by <b>def</b>. If not NULL, <b>key</b> is the name of the option,
- * which may be used for logging.
+ * 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
@@ -42,7 +42,7 @@
**/
int
typed_var_assign(void *target, const char *value, char **errmsg,
- const var_type_def_t *def, const char *key)
+ const var_type_def_t *def)
{
if (BUG(!def))
return -1; // LCOV_EXCL_LINE
@@ -50,7 +50,7 @@ typed_var_assign(void *target, const char *value, char **errmsg,
typed_var_free(target, def);
tor_assert(def->fns->parse);
- return def->fns->parse(target, value, errmsg, def->params, key);
+ return def->fns->parse(target, value, errmsg, def->params);
}
/**
@@ -76,7 +76,15 @@ typed_var_kvassign(void *target, const config_line_t *line,
return def->fns->kv_parse(target, line, errmsg, def->params);
}
- return typed_var_assign(target, line->value, errmsg, def, line->key);
+ int rv = typed_var_assign(target, line->value, errmsg, def);
+ if (rv < 0 && *errmsg != NULL) {
+ /* typed_var_assign() didn't know the line's keyword, but we do.
+ * Let's add it to the error message. */
+ char *oldmsg = *errmsg;
+ tor_asprintf(errmsg, "Could not parse %s: %s", line->key, oldmsg);
+ tor_free(oldmsg);
+ }
+ return rv;
}
/**
@@ -159,7 +167,7 @@ typed_var_copy(void *dest, const void *src, const var_type_def_t *def)
return 0;
}
char *err = NULL;
- int rv = typed_var_assign(dest, enc, &err, def, NULL);
+ int rv = typed_var_assign(dest, enc, &err, def);
if (BUG(rv < 0)) {
// LCOV_EXCL_START
log_warn(LD_BUG, "Encoded value %s was not parseable as a %s: %s",
diff --git a/src/lib/confmgt/typedvar.h b/src/lib/confmgt/typedvar.h
index 4382613833..cc90ed10a3 100644
--- a/src/lib/confmgt/typedvar.h
+++ b/src/lib/confmgt/typedvar.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -21,7 +21,7 @@ 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,
- const var_type_def_t *def, const char *key);
+ const var_type_def_t *def);
void typed_var_free(void *target, const var_type_def_t *def);
char *typed_var_encode(const void *value, const var_type_def_t *def);
int typed_var_copy(void *dest, const void *src, const var_type_def_t *def);
diff --git a/src/lib/confmgt/unitparse.c b/src/lib/confmgt/unitparse.c
index c3ed8285a4..61edc60694 100644
--- a/src/lib/confmgt/unitparse.c
+++ b/src/lib/confmgt/unitparse.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,8 +13,11 @@
#include "lib/confmgt/unitparse.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/string/util_string.h"
+#include "lib/intmath/muldiv.h"
#include <string.h>
@@ -109,22 +112,30 @@ const struct unit_table_t time_msec_units[] = {
* 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.
+ *
+ * If an error (like overflow or a negative value is detected), put an error
+ * message in *<b>errmsg_out</b> if that pointer is non-NULL, and otherwise
+ * log a warning.
*/
uint64_t
-config_parse_units(const char *val, const unit_table_t *u, int *ok)
+config_parse_units(const char *val, const unit_table_t *u, int *ok,
+ char **errmsg_out)
{
uint64_t v = 0;
double d = 0;
int use_float = 0;
char *cp;
+ char *errmsg = NULL;
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)
+ if (!*ok) {
+ tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
goto done;
+ }
use_float = 1;
}
@@ -142,18 +153,55 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok)
for ( ;u->unit;++u) {
if (!strcasecmp(u->unit, cp)) {
- if (use_float)
- v = (uint64_t)(u->multiplier * d);
- else
- v *= u->multiplier;
+ if (use_float) {
+ d = u->multiplier * d;
+
+ if (d < 0) {
+ tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
+ val, u->unit);
+ *ok = 0;
+ goto done;
+ }
+
+ // Some compilers may warn about casting a double to an unsigned type
+ // because they don't know if d is >= 0
+ if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
+ tor_asprintf(&errmsg, "Overflow while parsing %s %s",
+ val, u->unit);
+ *ok = 0;
+ goto done;
+ }
+
+ v = (uint64_t) d;
+ } else {
+ v = tor_mul_u64_nowrap(v, u->multiplier);
+
+ if (v > INT64_MAX) {
+ tor_asprintf(&errmsg, "Overflow while parsing %s %s",
+ val, u->unit);
+ *ok = 0;
+ goto done;
+ }
+ }
+
*ok = 1;
goto done;
}
}
- log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
+ tor_asprintf(&errmsg, "Unknown unit in %s", val);
*ok = 0;
done:
+ if (errmsg) {
+ tor_assert_nonfatal(!*ok);
+ if (errmsg_out) {
+ *errmsg_out = errmsg;
+ } else {
+ log_warn(LD_CONFIG, "%s", errmsg);
+ tor_free(errmsg);
+ }
+ }
+
if (*ok)
return v;
else
@@ -167,7 +215,7 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok)
uint64_t
config_parse_memunit(const char *s, int *ok)
{
- uint64_t u = config_parse_units(s, memory_units, ok);
+ uint64_t u = config_parse_units(s, memory_units, ok, NULL);
return u;
}
@@ -179,7 +227,7 @@ int
config_parse_msec_interval(const char *s, int *ok)
{
uint64_t r;
- r = config_parse_units(s, time_msec_units, ok);
+ r = config_parse_units(s, time_msec_units, ok, NULL);
if (r > INT_MAX) {
log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
*ok = 0;
@@ -196,7 +244,7 @@ int
config_parse_interval(const char *s, int *ok)
{
uint64_t r;
- r = config_parse_units(s, time_units, ok);
+ r = config_parse_units(s, time_units, ok, NULL);
if (r > INT_MAX) {
log_warn(LD_CONFIG, "Interval '%s' is too long", s);
*ok = 0;
diff --git a/src/lib/confmgt/unitparse.h b/src/lib/confmgt/unitparse.h
index 216361a7d4..047e11b424 100644
--- a/src/lib/confmgt/unitparse.h
+++ b/src/lib/confmgt/unitparse.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -25,7 +25,8 @@ 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_units(const char *val, const unit_table_t *u, int *ok,
+ char **errmsg_out);
uint64_t config_parse_memunit(const char *s, int *ok);
int config_parse_msec_interval(const char *s, int *ok);
diff --git a/src/lib/confmgt/var_type_def_st.h b/src/lib/confmgt/var_type_def_st.h
index aa9ded39e9..2519b86aa0 100644
--- a/src/lib/confmgt/var_type_def_st.h
+++ b/src/lib/confmgt/var_type_def_st.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -52,12 +52,9 @@ struct var_type_fns_t {
* 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.
- *
- * If not NULL, <b>key</b> is the name of the option, which may be used for
- * logging.
**/
int (*parse)(void *target, const char *value, char **errmsg,
- const void *params, const char *key);
+ 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()