diff options
author | Daniel Pinto <danielpinto52@gmail.com> | 2017-05-18 23:44:16 +0100 |
---|---|---|
committer | Daniel Pinto <danielpinto52@gmail.com> | 2017-05-18 23:44:16 +0100 |
commit | ba3a5f82f11388237a3ba4995ddf0b6ffaaf492a (patch) | |
tree | c0cf4342524cd040e24fe9cc420bb70e21c0a9d5 /src/common | |
parent | ec6b2bbf9bb561e34f9fbe6663d1eb691604348d (diff) | |
download | tor-ba3a5f82f11388237a3ba4995ddf0b6ffaaf492a.tar.gz tor-ba3a5f82f11388237a3ba4995ddf0b6ffaaf492a.zip |
Add support for %include funcionality on torrc #1922
config_get_lines is now split into two functions:
- config_get_lines which is the same as before we had %include
- config_get_lines_include which actually processes %include
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/confline.c | 211 | ||||
-rw-r--r-- | src/common/confline.h | 4 | ||||
-rw-r--r-- | src/common/util.c | 35 | ||||
-rw-r--r-- | src/common/util.h | 1 |
4 files changed, 232 insertions, 19 deletions
diff --git a/src/common/confline.c b/src/common/confline.c index d4468f80ea..e078b1da04 100644 --- a/src/common/confline.c +++ b/src/common/confline.c @@ -8,6 +8,19 @@ #include "confline.h" #include "torlog.h" #include "util.h" +#include "container.h" + +static int config_get_lines_aux(const char *string, config_line_t **result, + int extended, int allow_include, + int *has_include, int recursion_level, + config_line_t **last); +static smartlist_t *config_get_file_list(const char *path); +static int config_get_included_list(const char *path, int recursion_level, + int extended, config_line_t **list, + config_line_t **list_last); +static int config_process_include(const char *path, int recursion_level, + int extended, config_line_t ***next, + config_line_t **list_last); /** Helper: allocate a new configuration option mapping 'key' to 'val', * append it to *<b>lst</b>. */ @@ -65,19 +78,25 @@ config_line_find(const config_line_t *lines, return NULL; } -/** Helper: parse the config string and strdup into key/value - * strings. Set *result to the list, or NULL if parsing the string - * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. - * - * If <b>extended</b> is set, then treat keys beginning with / and with + as - * indicating "clear" and "append" respectively. */ -int -config_get_lines(const char *string, config_line_t **result, int extended) +/** 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. + * Returns the a pointer to the last element of the <b>result</b> in + * <b>last</b>. */ +static int +config_get_lines_aux(const char *string, config_line_t **result, int extended, + int allow_include, int *has_include, int recursion_level, + config_line_t **last) { - config_line_t *list = NULL, **next; + 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 { @@ -108,25 +127,179 @@ config_get_lines(const char *string, config_line_t **result, int extended) command = CONFIG_LINE_CLEAR; } } - /* 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(config_line_t)); - (*next)->key = k; - (*next)->value = v; - (*next)->next = NULL; - (*next)->command = command; - next = &((*next)->next); + + if (allow_include && !strcmp(k, "%include")) { + tor_free(k); + include_used = 1; + + if (config_process_include(v, recursion_level, extended, &next, + &list_last) < 0) { + log_warn(LD_CONFIG, "Error reading included configuration " + "file or directory: \"%s\".", v); + config_free_lines(list); + tor_free(v); + return -1; + } + 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; } +/** Helper: parse the config string and strdup into key/value + * strings. Set *result to the list, or NULL if parsing the string + * failed. Set *has_include to 1 if <b>result</b> has values from + * %included files. Return 0 on success, -1 on failure. Warn and ignore any + * misformatted lines. + * + * If <b>extended</b> is set, then treat keys beginning with / and with + as + * indicating "clear" and "append" respectively. */ +int +config_get_lines_include(const char *string, config_line_t **result, + int extended, int *has_include) +{ + return config_get_lines_aux(string, result, extended, 1, has_include, 1, + NULL); +} + +/** 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, 1, NULL); +} + +/** Adds a list of configuration files present on <b>path</b> to + * <b>file_list</b>. <b>path</b> can be a file or a directory. If it is a file, + * only that file will be added to <b>file_list</b>. If it is a directory, + * all paths for files on that directory root (no recursion) except for files + * whose name starts with a dot will be added to <b>file_list</b>. + * Return 0 on success, -1 on failure. Ignores empty files. + */ +static smartlist_t * +config_get_file_list(const char *path) +{ + smartlist_t *file_list = smartlist_new(); + file_status_t file_type = file_status(path); + if (file_type == FN_FILE) { + smartlist_add_strdup(file_list, path); + return file_list; + } else if (file_type == FN_DIR) { + smartlist_t *all_files = tor_listdir(path); + if (!all_files) { + smartlist_free(file_list); + return NULL; + } + smartlist_sort_strings(all_files); + SMARTLIST_FOREACH_BEGIN(all_files, char *, f) { + if (f[0] == '.') { + tor_free(f); + continue; + } + + char *fullname; + tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f); + tor_free(f); + + if (file_status(fullname) != FN_FILE) { + tor_free(fullname); + continue; + } + smartlist_add(file_list, fullname); + } SMARTLIST_FOREACH_END(f); + smartlist_free(all_files); + return file_list; + } else if (file_type == FN_EMPTY) { + return file_list; + } else { + smartlist_free(file_list); + return NULL; + } +} + +/** Creates a list of config lines present on included <b>path</b>. + * Set <b>list</b> to the list and <b>list_last</b> to the last element of + * <b>list</b>. Return 0 on success, -1 on failure. */ +static int +config_get_included_list(const char *path, int recursion_level, int extended, + config_line_t **list, config_line_t **list_last) +{ + char *included_conf = read_file_to_str(path, 0, NULL); + if (!included_conf) { + return -1; + } + + if (config_get_lines_aux(included_conf, list, extended, 1, NULL, + recursion_level+1, list_last) < 0) { + tor_free(included_conf); + return -1; + } + + tor_free(included_conf); + return 0; +} + +/** Process an %include <b>path</b> in a config file. Set <b>next</b> to a + * pointer to the next pointer of the last element of the config_line_t list + * obtained from the config file and <b>list_last</b> to the last element of + * the same list. Return 0 on success, -1 on failure. */ +static int +config_process_include(const char *path, int recursion_level, int extended, + config_line_t ***next, config_line_t **list_last) +{ + char *unquoted_path = get_unquoted_path(path); + if (!unquoted_path) { + return -1; + } + + smartlist_t *config_files = config_get_file_list(unquoted_path); + if (!config_files) { + tor_free(unquoted_path); + return -1; + } + tor_free(unquoted_path); + + SMARTLIST_FOREACH_BEGIN(config_files, char *, config_file) { + config_line_t *included_list = NULL; + if (config_get_included_list(config_file, recursion_level, extended, + &included_list, list_last) < 0) { + SMARTLIST_FOREACH(config_files, char *, f, tor_free(f)); + smartlist_free(config_files); + return -1; + } + tor_free(config_file); + + **next = included_list; + *next = &(*list_last)->next; + + } SMARTLIST_FOREACH_END(config_file); + smartlist_free(config_files); + return 0; +} + /** * Free all the configuration lines on the linked list <b>front</b>. */ diff --git a/src/common/confline.h b/src/common/confline.h index 477c6929a2..eb863e8fd8 100644 --- a/src/common/confline.h +++ b/src/common/confline.h @@ -15,6 +15,8 @@ /* Removes all previous configuration for an option. */ #define CONFIG_LINE_CLEAR 2 +#define MAX_INCLUDE_RECURSION_LEVEL 31 + /** A linked list of lines in a config file, or elsewhere */ typedef struct config_line_t { char *key; @@ -41,6 +43,8 @@ const config_line_t *config_line_find(const config_line_t *lines, int config_lines_eq(config_line_t *a, config_line_t *b); int config_count_key(const config_line_t *a, const char *key); int config_get_lines(const char *string, config_line_t **result, int extended); +int config_get_lines_include(const char *string, config_line_t **result, + int extended, int *has_include); void config_free_lines(config_line_t *front); const char *parse_config_line_from_str_verbose(const char *line, char **key_out, char **value_out, diff --git a/src/common/util.c b/src/common/util.c index ca2a0c4a9c..5c455585e7 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3045,6 +3045,41 @@ unescape_string(const char *s, char **result, size_t *size_out) } } +/** Removes enclosing quotes from <b>path</b> and unescapes quotes between the + * enclosing quotes. Backslashes are not unescaped. Return the unquoted + * <b>path</b> on sucess or 0 if <b>path</b> is not quoted correctly. */ +char * +get_unquoted_path(const char *path) +{ + int len = strlen(path); + + if (len == 0) { + return tor_strdup(""); + } + + int has_start_quote = (path[0] == '\"'); + int has_end_quote = (len > 0 && path[len-1] == '\"'); + if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) { + return NULL; + } + + char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1); + char *s = unquoted_path; + int i; + for (i = has_start_quote; i < len - has_end_quote; i++) { + if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) { + *(s-1) = path[i]; + } else if (path[i] != '\"') { + *s++ = path[i]; + } else { /* unescaped quote */ + tor_free(unquoted_path); + return NULL; + } + } + *s = '\0'; + return unquoted_path; +} + /** Expand any homedir prefix on <b>filename</b>; return a newly allocated * string. */ char * diff --git a/src/common/util.h b/src/common/util.h index 18eb57f1bc..d56abcee2e 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -389,6 +389,7 @@ char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out) ATTR_MALLOC; const char *unescape_string(const char *s, char **result, size_t *size_out); +char *get_unquoted_path(const char *path); char *expand_filename(const char *filename); MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname)); int path_is_relative(const char *filename); |