123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- /* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2018, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * \file confline.c
- *
- * \brief Functions to manipulate a linked list of key-value pairs, of the
- * type used in Tor's configuration files.
- *
- * Tor uses the config_line_t type and its associated serialized format for
- * human-readable key-value pairs in many places, including its configuration,
- * its state files, its consensus cache, and so on.
- **/
- #include "lib/encoding/confline.h"
- #include "lib/encoding/cstring.h"
- #include "lib/log/log.h"
- #include "lib/log/util_bug.h"
- #include "lib/malloc/malloc.h"
- #include "lib/string/compat_ctype.h"
- #include "lib/string/compat_string.h"
- #include "lib/string/util_string.h"
- #include <string.h>
- /** Helper: allocate a new configuration option mapping 'key' to 'val',
- * append it to *<b>lst</b>. */
- void
- config_line_append(config_line_t **lst,
- const char *key,
- const char *val)
- {
- tor_assert(lst);
- config_line_t *newline;
- newline = tor_malloc_zero(sizeof(config_line_t));
- newline->key = tor_strdup(key);
- newline->value = tor_strdup(val);
- newline->next = NULL;
- while (*lst)
- lst = &((*lst)->next);
- (*lst) = newline;
- }
- /** Helper: allocate a new configuration option mapping 'key' to 'val',
- * and prepend it to *<b>lst</b> */
- void
- config_line_prepend(config_line_t **lst,
- const char *key,
- const char *val)
- {
- tor_assert(lst);
- config_line_t *newline;
- newline = tor_malloc_zero(sizeof(config_line_t));
- newline->key = tor_strdup(key);
- newline->value = tor_strdup(val);
- newline->next = *lst;
- *lst = newline;
- }
- /** Return the first line in <b>lines</b> whose key is exactly <b>key</b>, or
- * NULL if no such key exists.
- *
- * (In options parsing, this is for handling commandline-only options only;
- * other options should be looked up in the appropriate data structure.) */
- const config_line_t *
- config_line_find(const config_line_t *lines,
- const char *key)
- {
- const config_line_t *cl;
- for (cl = lines; cl; cl = cl->next) {
- if (!strcmp(cl->key, key))
- return cl;
- }
- return NULL;
- }
- /** Auxiliary function that does all the work of config_get_lines.
- * <b>recursion_level</b> is the count of how many nested %includes we have.
- * <b>opened_lst</b> will have a list of opened files if provided.
- * Returns the a pointer to the last element of the <b>result</b> in
- * <b>last</b>. */
- int
- config_get_lines_aux(const char *string, config_line_t **result, int extended,
- int allow_include, int *has_include,
- struct smartlist_t *opened_lst, int recursion_level,
- config_line_t **last,
- include_handler_fn handle_include)
- {
- config_line_t *list = NULL, **next, *list_last = NULL;
- char *k, *v;
- const char *parse_err;
- int include_used = 0;
- if (recursion_level > MAX_INCLUDE_RECURSION_LEVEL) {
- log_warn(LD_CONFIG, "Error while parsing configuration: more than %d "
- "nested %%includes.", MAX_INCLUDE_RECURSION_LEVEL);
- return -1;
- }
- next = &list;
- do {
- k = v = NULL;
- string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err);
- if (!string) {
- log_warn(LD_CONFIG, "Error while parsing configuration: %s",
- parse_err?parse_err:"<unknown>");
- config_free_lines(list);
- tor_free(k);
- tor_free(v);
- return -1;
- }
- if (k && v) {
- unsigned command = CONFIG_LINE_NORMAL;
- if (extended) {
- if (k[0] == '+') {
- char *k_new = tor_strdup(k+1);
- tor_free(k);
- k = k_new;
- command = CONFIG_LINE_APPEND;
- } else if (k[0] == '/') {
- char *k_new = tor_strdup(k+1);
- tor_free(k);
- k = k_new;
- tor_free(v);
- v = tor_strdup("");
- command = CONFIG_LINE_CLEAR;
- }
- }
- if (allow_include && !strcmp(k, "%include") && handle_include) {
- tor_free(k);
- include_used = 1;
- config_line_t *include_list;
- if (handle_include(v, recursion_level, extended, &include_list,
- &list_last, opened_lst) < 0) {
- log_warn(LD_CONFIG, "Error reading included configuration "
- "file or directory: \"%s\".", v);
- config_free_lines(list);
- tor_free(v);
- return -1;
- }
- log_notice(LD_CONFIG, "Included configuration file or "
- "directory at recursion level %d: \"%s\".",
- recursion_level, v);
- *next = include_list;
- if (list_last)
- next = &list_last->next;
- tor_free(v);
- } else {
- /* This list can get long, so we keep a pointer to the end of it
- * rather than using config_line_append over and over and getting
- * n^2 performance. */
- *next = tor_malloc_zero(sizeof(**next));
- (*next)->key = k;
- (*next)->value = v;
- (*next)->next = NULL;
- (*next)->command = command;
- list_last = *next;
- next = &((*next)->next);
- }
- } else {
- tor_free(k);
- tor_free(v);
- }
- } while (*string);
- if (last) {
- *last = list_last;
- }
- if (has_include) {
- *has_include = include_used;
- }
- *result = list;
- return 0;
- }
- /** Same as config_get_lines_include but does not allow %include */
- int
- config_get_lines(const char *string, config_line_t **result, int extended)
- {
- return config_get_lines_aux(string, result, extended, 0, NULL, NULL, 1,
- NULL, NULL);
- }
- /**
- * Free all the configuration lines on the linked list <b>front</b>.
- */
- void
- config_free_lines_(config_line_t *front)
- {
- config_line_t *tmp;
- while (front) {
- tmp = front;
- front = tmp->next;
- tor_free(tmp->key);
- tor_free(tmp->value);
- tor_free(tmp);
- }
- }
- /** Return a newly allocated deep copy of the lines in <b>inp</b>. */
- config_line_t *
- config_lines_dup(const config_line_t *inp)
- {
- return config_lines_dup_and_filter(inp, NULL);
- }
- /** Return a newly allocated deep copy of the lines in <b>inp</b>,
- * but only the ones whose keys begin with <b>key</b> (case-insensitive).
- * If <b>key</b> is NULL, do not filter. */
- config_line_t *
- config_lines_dup_and_filter(const config_line_t *inp,
- const char *key)
- {
- config_line_t *result = NULL;
- config_line_t **next_out = &result;
- while (inp) {
- if (key && strcasecmpstart(inp->key, key)) {
- inp = inp->next;
- continue;
- }
- *next_out = tor_malloc_zero(sizeof(config_line_t));
- (*next_out)->key = tor_strdup(inp->key);
- (*next_out)->value = tor_strdup(inp->value);
- inp = inp->next;
- next_out = &((*next_out)->next);
- }
- (*next_out) = NULL;
- return result;
- }
- /** 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)
- {
- while (a && b) {
- if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value))
- return 0;
- a = a->next;
- b = b->next;
- }
- if (a || b)
- return 0;
- return 1;
- }
- /** Return the number of lines in <b>a</b> whose key is <b>key</b>. */
- int
- config_count_key(const config_line_t *a, const char *key)
- {
- int n = 0;
- while (a) {
- if (!strcasecmp(a->key, key)) {
- ++n;
- }
- a = a->next;
- }
- return n;
- }
- /** Given a string containing part of a configuration file or similar format,
- * advance past comments and whitespace and try to parse a single line. If we
- * parse a line successfully, set *<b>key_out</b> to a new string holding the
- * key portion and *<b>value_out</b> to a new string holding the value portion
- * of the line, and return a pointer to the start of the next line. If we run
- * out of data, return a pointer to the end of the string. If we encounter an
- * error, return NULL and set *<b>err_out</b> (if provided) to an error
- * message.
- */
- const char *
- parse_config_line_from_str_verbose(const char *line, char **key_out,
- char **value_out,
- const char **err_out)
- {
- /*
- See torrc_format.txt for a description of the (silly) format this parses.
- */
- const char *key, *val, *cp;
- int continuation = 0;
- tor_assert(key_out);
- tor_assert(value_out);
- *key_out = *value_out = NULL;
- key = val = NULL;
- /* Skip until the first keyword. */
- while (1) {
- while (TOR_ISSPACE(*line))
- ++line;
- if (*line == '#') {
- while (*line && *line != '\n')
- ++line;
- } else {
- break;
- }
- }
- if (!*line) { /* End of string? */
- *key_out = *value_out = NULL;
- return line;
- }
- /* Skip until the next space or \ followed by newline. */
- key = line;
- while (*line && !TOR_ISSPACE(*line) && *line != '#' &&
- ! (line[0] == '\\' && line[1] == '\n'))
- ++line;
- *key_out = tor_strndup(key, line-key);
- /* Skip until the value. */
- while (*line == ' ' || *line == '\t')
- ++line;
- val = line;
- /* Find the end of the line. */
- if (*line == '\"') { // XXX No continuation handling is done here
- if (!(line = unescape_string(line, value_out, NULL))) {
- if (err_out)
- *err_out = "Invalid escape sequence in quoted string";
- return NULL;
- }
- while (*line == ' ' || *line == '\t')
- ++line;
- if (*line == '\r' && *(++line) == '\n')
- ++line;
- if (*line && *line != '#' && *line != '\n') {
- if (err_out)
- *err_out = "Excess data after quoted string";
- return NULL;
- }
- } else {
- /* Look for the end of the line. */
- while (*line && *line != '\n' && (*line != '#' || continuation)) {
- if (*line == '\\' && line[1] == '\n') {
- continuation = 1;
- line += 2;
- } else if (*line == '#') {
- do {
- ++line;
- } while (*line && *line != '\n');
- if (*line == '\n')
- ++line;
- } else {
- ++line;
- }
- }
- if (*line == '\n') {
- cp = line++;
- } else {
- cp = line;
- }
- /* Now back cp up to be the last nonspace character */
- while (cp>val && TOR_ISSPACE(*(cp-1)))
- --cp;
- tor_assert(cp >= val);
- /* Now copy out and decode the value. */
- *value_out = tor_strndup(val, cp-val);
- if (continuation) {
- char *v_out, *v_in;
- v_out = v_in = *value_out;
- while (*v_in) {
- if (*v_in == '#') {
- do {
- ++v_in;
- } while (*v_in && *v_in != '\n');
- if (*v_in == '\n')
- ++v_in;
- } else if (v_in[0] == '\\' && v_in[1] == '\n') {
- v_in += 2;
- } else {
- *v_out++ = *v_in++;
- }
- }
- *v_out = '\0';
- }
- }
- if (*line == '#') {
- do {
- ++line;
- } while (*line && *line != '\n');
- }
- while (TOR_ISSPACE(*line)) ++line;
- return line;
- }
|