mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-24 22:58:50 +01:00
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
This commit is contained in:
parent
ec6b2bbf9b
commit
ba3a5f82f1
13 changed files with 908 additions and 24 deletions
11
changes/feature1922
Normal file
11
changes/feature1922
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
o Minor feature (include on config files):
|
||||||
|
- Allow the use of %include on configuration files to include settings
|
||||||
|
from other files or directories. Using %include with a directory will
|
||||||
|
include all (non-dot) files in that directory in lexically sorted order
|
||||||
|
(non-recursive), closes ticket 1922.
|
||||||
|
- Makes SAVECONF command return error when overwriting a torrc
|
||||||
|
that has includes. Using SAVECONF with the FORCE option will
|
||||||
|
allow it to overwrite torrc even if includes are used, closes ticket
|
||||||
|
1922.
|
||||||
|
- Adds config-can-saveconf to GETINFO command to tell if SAVECONF
|
||||||
|
will work without the FORCE option, closes ticket 1922.
|
|
@ -153,6 +153,13 @@ values. To split one configuration entry into multiple lines, use a single
|
||||||
backslash character (\) before the end of the line. Comments can be used in
|
backslash character (\) before the end of the line. Comments can be used in
|
||||||
such multiline entries, but they must start at the beginning of a line.
|
such multiline entries, but they must start at the beginning of a line.
|
||||||
|
|
||||||
|
Configuration options can be imported from files or folders using the %include
|
||||||
|
option with the value being a path. If the path is a file, the options from the
|
||||||
|
file will be parsed as if they were written where the %include option is. If
|
||||||
|
the path is a folder, all files on that folder will be parsed following lexical
|
||||||
|
order. Files starting with a dot are ignored. Files on subfolders are ignored.
|
||||||
|
The %include option can be used recursively.
|
||||||
|
|
||||||
By default, an option on the command line overrides an option found in the
|
By default, an option on the command line overrides an option found in the
|
||||||
configuration file, and an option in a configuration file overrides one in
|
configuration file, and an option in a configuration file overrides one in
|
||||||
the defaults file.
|
the defaults file.
|
||||||
|
|
|
@ -8,6 +8,19 @@
|
||||||
#include "confline.h"
|
#include "confline.h"
|
||||||
#include "torlog.h"
|
#include "torlog.h"
|
||||||
#include "util.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',
|
/** Helper: allocate a new configuration option mapping 'key' to 'val',
|
||||||
* append it to *<b>lst</b>. */
|
* append it to *<b>lst</b>. */
|
||||||
|
@ -65,19 +78,25 @@ config_line_find(const config_line_t *lines,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper: parse the config string and strdup into key/value
|
/** Auxiliary function that does all the work of config_get_lines.
|
||||||
* strings. Set *result to the list, or NULL if parsing the string
|
* <b>recursion_level</b> is the count of how many nested %includes we have.
|
||||||
* failed. Return 0 on success, -1 on failure. Warn and ignore any
|
* Returns the a pointer to the last element of the <b>result</b> in
|
||||||
* misformatted lines.
|
* <b>last</b>. */
|
||||||
*
|
static int
|
||||||
* If <b>extended</b> is set, then treat keys beginning with / and with + as
|
config_get_lines_aux(const char *string, config_line_t **result, int extended,
|
||||||
* indicating "clear" and "append" respectively. */
|
int allow_include, int *has_include, int recursion_level,
|
||||||
int
|
config_line_t **last)
|
||||||
config_get_lines(const char *string, config_line_t **result, int extended)
|
|
||||||
{
|
{
|
||||||
config_line_t *list = NULL, **next;
|
config_line_t *list = NULL, **next, *list_last = NULL;
|
||||||
char *k, *v;
|
char *k, *v;
|
||||||
const char *parse_err;
|
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;
|
next = &list;
|
||||||
do {
|
do {
|
||||||
|
@ -108,25 +127,179 @@ config_get_lines(const char *string, config_line_t **result, int extended)
|
||||||
command = CONFIG_LINE_CLEAR;
|
command = CONFIG_LINE_CLEAR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
/* 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
|
* rather than using config_line_append over and over and getting
|
||||||
* n^2 performance. */
|
* n^2 performance. */
|
||||||
*next = tor_malloc_zero(sizeof(config_line_t));
|
*next = tor_malloc_zero(sizeof(**next));
|
||||||
(*next)->key = k;
|
(*next)->key = k;
|
||||||
(*next)->value = v;
|
(*next)->value = v;
|
||||||
(*next)->next = NULL;
|
(*next)->next = NULL;
|
||||||
(*next)->command = command;
|
(*next)->command = command;
|
||||||
|
list_last = *next;
|
||||||
next = &((*next)->next);
|
next = &((*next)->next);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tor_free(k);
|
tor_free(k);
|
||||||
tor_free(v);
|
tor_free(v);
|
||||||
}
|
}
|
||||||
} while (*string);
|
} while (*string);
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
*last = list_last;
|
||||||
|
}
|
||||||
|
if (has_include) {
|
||||||
|
*has_include = include_used;
|
||||||
|
}
|
||||||
*result = list;
|
*result = list;
|
||||||
return 0;
|
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>.
|
* Free all the configuration lines on the linked list <b>front</b>.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
/* Removes all previous configuration for an option. */
|
/* Removes all previous configuration for an option. */
|
||||||
#define CONFIG_LINE_CLEAR 2
|
#define CONFIG_LINE_CLEAR 2
|
||||||
|
|
||||||
|
#define MAX_INCLUDE_RECURSION_LEVEL 31
|
||||||
|
|
||||||
/** A linked list of lines in a config file, or elsewhere */
|
/** A linked list of lines in a config file, or elsewhere */
|
||||||
typedef struct config_line_t {
|
typedef struct config_line_t {
|
||||||
char *key;
|
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_lines_eq(config_line_t *a, config_line_t *b);
|
||||||
int config_count_key(const config_line_t *a, const char *key);
|
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(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);
|
void config_free_lines(config_line_t *front);
|
||||||
const char *parse_config_line_from_str_verbose(const char *line,
|
const char *parse_config_line_from_str_verbose(const char *line,
|
||||||
char **key_out, char **value_out,
|
char **key_out, char **value_out,
|
||||||
|
|
|
@ -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
|
/** Expand any homedir prefix on <b>filename</b>; return a newly allocated
|
||||||
* string. */
|
* string. */
|
||||||
char *
|
char *
|
||||||
|
|
|
@ -389,6 +389,7 @@ char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
|
||||||
size_t *sz_out)
|
size_t *sz_out)
|
||||||
ATTR_MALLOC;
|
ATTR_MALLOC;
|
||||||
const char *unescape_string(const char *s, char **result, size_t *size_out);
|
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);
|
char *expand_filename(const char *filename);
|
||||||
MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
|
MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
|
||||||
int path_is_relative(const char *filename);
|
int path_is_relative(const char *filename);
|
||||||
|
|
|
@ -209,3 +209,12 @@
|
||||||
## address manually to your friends, uncomment this line:
|
## address manually to your friends, uncomment this line:
|
||||||
#PublishServerDescriptor 0
|
#PublishServerDescriptor 0
|
||||||
|
|
||||||
|
## Configuration options can be imported from files or folders using the %include
|
||||||
|
## option with the value being a path. If the path is a file, the options from the
|
||||||
|
## file will be parsed as if they were written where the %include option is. If
|
||||||
|
## the path is a folder, all files on that folder will be parsed following lexical
|
||||||
|
## order. Files starting with a dot are ignored. Files on subfolders are ignored.
|
||||||
|
## The %include option can be used recursively.
|
||||||
|
#%include /etc/torrc.d/
|
||||||
|
#%include /etc/torrc.custom
|
||||||
|
|
||||||
|
|
|
@ -5056,6 +5056,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
|
||||||
config_line_t *cl;
|
config_line_t *cl;
|
||||||
int retval;
|
int retval;
|
||||||
setopt_err_t err = SETOPT_ERR_MISC;
|
setopt_err_t err = SETOPT_ERR_MISC;
|
||||||
|
int cf_has_include;
|
||||||
tor_assert(msg);
|
tor_assert(msg);
|
||||||
|
|
||||||
oldoptions = global_options; /* get_options unfortunately asserts if
|
oldoptions = global_options; /* get_options unfortunately asserts if
|
||||||
|
@ -5072,7 +5073,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
|
||||||
if (!body)
|
if (!body)
|
||||||
continue;
|
continue;
|
||||||
/* get config lines, assign them */
|
/* get config lines, assign them */
|
||||||
retval = config_get_lines(body, &cl, 1);
|
retval = config_get_lines_include(body, &cl, 1,
|
||||||
|
body == cf ? &cf_has_include : NULL);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
err = SETOPT_ERR_PARSE;
|
err = SETOPT_ERR_PARSE;
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -5100,6 +5102,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newoptions->IncludeUsed = cf_has_include;
|
||||||
|
|
||||||
/* If this is a testing network configuration, change defaults
|
/* If this is a testing network configuration, change defaults
|
||||||
* for a list of dependent config options, re-initialize newoptions
|
* for a list of dependent config options, re-initialize newoptions
|
||||||
* with the new defaults, and assign all options to it second time. */
|
* with the new defaults, and assign all options to it second time. */
|
||||||
|
@ -5143,7 +5147,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
|
||||||
if (!body)
|
if (!body)
|
||||||
continue;
|
continue;
|
||||||
/* get config lines, assign them */
|
/* get config lines, assign them */
|
||||||
retval = config_get_lines(body, &cl, 1);
|
retval = config_get_lines_include(body, &cl, 1,
|
||||||
|
body == cf ? &cf_has_include : NULL);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
err = SETOPT_ERR_PARSE;
|
err = SETOPT_ERR_PARSE;
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -5166,6 +5171,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newoptions->IncludeUsed = cf_has_include;
|
||||||
|
|
||||||
/* Validate newoptions */
|
/* Validate newoptions */
|
||||||
if (options_validate(oldoptions, newoptions, newdefaultoptions,
|
if (options_validate(oldoptions, newoptions, newdefaultoptions,
|
||||||
0, msg) < 0) {
|
0, msg) < 0) {
|
||||||
|
|
|
@ -124,7 +124,6 @@ const char *config_find_deprecation(const config_format_t *fmt,
|
||||||
const char *key);
|
const char *key);
|
||||||
const config_var_t *config_find_option(const config_format_t *fmt,
|
const config_var_t *config_find_option(const config_format_t *fmt,
|
||||||
const char *key);
|
const char *key);
|
||||||
|
|
||||||
const char *config_expand_abbrev(const config_format_t *fmt,
|
const char *config_expand_abbrev(const config_format_t *fmt,
|
||||||
const char *option,
|
const char *option,
|
||||||
int command_line, int warn_obsolete);
|
int command_line, int warn_obsolete);
|
||||||
|
|
|
@ -1462,8 +1462,10 @@ handle_control_saveconf(control_connection_t *conn, uint32_t len,
|
||||||
const char *body)
|
const char *body)
|
||||||
{
|
{
|
||||||
(void) len;
|
(void) len;
|
||||||
(void) body;
|
|
||||||
if (options_save_current()<0) {
|
int force = !strcmpstart(body, "FORCE");
|
||||||
|
const or_options_t *options = get_options();
|
||||||
|
if ((!force && options->IncludeUsed) || options_save_current() < 0) {
|
||||||
connection_write_str_to_buf(
|
connection_write_str_to_buf(
|
||||||
"551 Unable to write configuration to disk.\r\n", conn);
|
"551 Unable to write configuration to disk.\r\n", conn);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1677,6 +1679,8 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
|
||||||
*answer = tor_strdup(a);
|
*answer = tor_strdup(a);
|
||||||
} else if (!strcmp(question, "config-text")) {
|
} else if (!strcmp(question, "config-text")) {
|
||||||
*answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL);
|
*answer = options_dump(get_options(), OPTIONS_DUMP_MINIMAL);
|
||||||
|
} else if (!strcmp(question, "config-can-saveconf")) {
|
||||||
|
*answer = tor_strdup(get_options()->IncludeUsed ? "0" : "1");
|
||||||
} else if (!strcmp(question, "info/names")) {
|
} else if (!strcmp(question, "info/names")) {
|
||||||
*answer = list_getinfo_options();
|
*answer = list_getinfo_options();
|
||||||
} else if (!strcmp(question, "dormant")) {
|
} else if (!strcmp(question, "dormant")) {
|
||||||
|
@ -2931,6 +2935,8 @@ static const getinfo_item_t getinfo_items[] = {
|
||||||
ITEM("config-defaults-file", misc, "Current location of the defaults file."),
|
ITEM("config-defaults-file", misc, "Current location of the defaults file."),
|
||||||
ITEM("config-text", misc,
|
ITEM("config-text", misc,
|
||||||
"Return the string that would be written by a saveconf command."),
|
"Return the string that would be written by a saveconf command."),
|
||||||
|
ITEM("config-can-saveconf", misc,
|
||||||
|
"Is it possible to save the configuration to the \"torrc\" file?"),
|
||||||
ITEM("accounting/bytes", accounting,
|
ITEM("accounting/bytes", accounting,
|
||||||
"Number of bytes read/written so far in the accounting interval."),
|
"Number of bytes read/written so far in the accounting interval."),
|
||||||
ITEM("accounting/bytes-left", accounting,
|
ITEM("accounting/bytes-left", accounting,
|
||||||
|
|
|
@ -4549,6 +4549,9 @@ typedef struct {
|
||||||
* do we enforce Ed25519 identity match? */
|
* do we enforce Ed25519 identity match? */
|
||||||
/* NOTE: remove this option someday. */
|
/* NOTE: remove this option someday. */
|
||||||
int AuthDirTestEd25519LinkKeys;
|
int AuthDirTestEd25519LinkKeys;
|
||||||
|
|
||||||
|
/** Bool (default: 0): Tells if a %include was used on torrc */
|
||||||
|
int IncludeUsed;
|
||||||
} or_options_t;
|
} or_options_t;
|
||||||
|
|
||||||
/** Persistent state for an onion router, as saved to disk. */
|
/** Persistent state for an onion router, as saved to disk. */
|
||||||
|
|
|
@ -4810,6 +4810,542 @@ test_config_parse_log_severity(void *data)
|
||||||
tor_free(severity);
|
tor_free(severity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_limit(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_limit"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char torrc_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir);
|
||||||
|
char torrc_contents[1000];
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
|
||||||
|
torrc_path);
|
||||||
|
tt_int_op(write_str_to_file(torrc_path, torrc_contents, 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
|
||||||
|
OP_EQ, -1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_does_not_exist(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_does_not_exist"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char missing_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(missing_path, sizeof(missing_path), "%s"PATH_SEPARATOR"missing",
|
||||||
|
dir);
|
||||||
|
char torrc_contents[1000];
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
|
||||||
|
missing_path);
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
|
||||||
|
OP_EQ, -1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_error_in_included_file(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_error_in_included_file"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char invalid_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(invalid_path, sizeof(invalid_path), "%s"PATH_SEPARATOR"invalid",
|
||||||
|
dir);
|
||||||
|
tt_int_op(write_str_to_file(invalid_path, "unclosed \"", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
char torrc_contents[1000];
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
|
||||||
|
invalid_path);
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, NULL),
|
||||||
|
OP_EQ, -1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_empty_file_folder(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_empty_file_folder"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char folder_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(folder_path, sizeof(folder_path), "%s"PATH_SEPARATOR"empty_dir",
|
||||||
|
dir);
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(folder_path), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(folder_path, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
char file_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(file_path, sizeof(file_path), "%s"PATH_SEPARATOR"empty_file",
|
||||||
|
dir);
|
||||||
|
tt_int_op(write_str_to_file(file_path, "", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
char torrc_contents[1000];
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents),
|
||||||
|
"%%include %s\n"
|
||||||
|
"%%include %s\n",
|
||||||
|
folder_path, file_path);
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
int include_used;
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_ptr_op(result, OP_EQ, NULL);
|
||||||
|
tt_int_op(include_used, OP_EQ, 1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_recursion_before_after(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_recursion_before_after"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char torrc_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir);
|
||||||
|
|
||||||
|
char file_contents[1000];
|
||||||
|
const int limit = MAX_INCLUDE_RECURSION_LEVEL;
|
||||||
|
int i;
|
||||||
|
// Loop backwards so file_contents has the contents of the first file by the
|
||||||
|
// end of the loop
|
||||||
|
for (i = limit; i > 0; i--) {
|
||||||
|
if (i < limit) {
|
||||||
|
tor_snprintf(file_contents, sizeof(file_contents),
|
||||||
|
"Test %d\n"
|
||||||
|
"%%include %s%d\n"
|
||||||
|
"Test %d\n",
|
||||||
|
i, torrc_path, i + 1, 2 * limit - i);
|
||||||
|
} else {
|
||||||
|
tor_snprintf(file_contents, sizeof(file_contents), "Test %d\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 1) {
|
||||||
|
char file_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(file_path, sizeof(file_path), "%s%d", torrc_path, i);
|
||||||
|
tt_int_op(write_str_to_file(file_path, file_contents, 0), OP_EQ, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
int include_used;
|
||||||
|
tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_ptr_op(result, OP_NE, NULL);
|
||||||
|
tt_int_op(include_used, OP_EQ, 1);
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
config_line_t *next;
|
||||||
|
for (next = result; next != NULL; next = next->next) {
|
||||||
|
char expected[10];
|
||||||
|
tor_snprintf(expected, sizeof(expected), "%d", len + 1);
|
||||||
|
tt_str_op(next->key, OP_EQ, "Test");
|
||||||
|
tt_str_op(next->value, OP_EQ, expected);
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
tt_int_op(len, OP_EQ, 2 * limit - 1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_recursion_after_only(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_recursion_after_only"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char torrc_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir);
|
||||||
|
|
||||||
|
char file_contents[1000];
|
||||||
|
const int limit = MAX_INCLUDE_RECURSION_LEVEL;
|
||||||
|
int i;
|
||||||
|
// Loop backwards so file_contents has the contents of the first file by the
|
||||||
|
// end of the loop
|
||||||
|
for (i = limit; i > 0; i--) {
|
||||||
|
int n = (i - limit - 1) * -1;
|
||||||
|
if (i < limit) {
|
||||||
|
tor_snprintf(file_contents, sizeof(file_contents),
|
||||||
|
"%%include %s%d\n"
|
||||||
|
"Test %d\n",
|
||||||
|
torrc_path, i + 1, n);
|
||||||
|
} else {
|
||||||
|
tor_snprintf(file_contents, sizeof(file_contents), "Test %d\n", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 1) {
|
||||||
|
char file_path[PATH_MAX+1];
|
||||||
|
tor_snprintf(file_path, sizeof(file_path), "%s%d", torrc_path, i);
|
||||||
|
tt_int_op(write_str_to_file(file_path, file_contents, 0), OP_EQ, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
int include_used;
|
||||||
|
tt_int_op(config_get_lines_include(file_contents, &result, 0, &include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_ptr_op(result, OP_NE, NULL);
|
||||||
|
tt_int_op(include_used, OP_EQ, 1);
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
config_line_t *next;
|
||||||
|
for (next = result; next != NULL; next = next->next) {
|
||||||
|
char expected[10];
|
||||||
|
tor_snprintf(expected, sizeof(expected), "%d", len + 1);
|
||||||
|
tt_str_op(next->key, OP_EQ, "Test");
|
||||||
|
tt_str_op(next->value, OP_EQ, expected);
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
tt_int_op(len, OP_EQ, limit);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_folder_order(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_folder_order"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char torrcd[PATH_MAX+1];
|
||||||
|
tor_snprintf(torrcd, sizeof(torrcd), "%s"PATH_SEPARATOR"%s", dir, "torrc.d");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(torrcd), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(torrcd, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// test that files in subfolders are ignored
|
||||||
|
char path[PATH_MAX+1];
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd,
|
||||||
|
"subfolder");
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(path), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(path, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char path2[PATH_MAX+1];
|
||||||
|
tor_snprintf(path2, sizeof(path2), "%s"PATH_SEPARATOR"%s", path,
|
||||||
|
"01_ignore");
|
||||||
|
tt_int_op(write_str_to_file(path2, "ShouldNotSee 1\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
// test that files starting with . are ignored
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, ".dot");
|
||||||
|
tt_int_op(write_str_to_file(path, "ShouldNotSee 2\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
// test file order
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "01_1st");
|
||||||
|
tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "02_2nd");
|
||||||
|
tt_int_op(write_str_to_file(path, "Test 2\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "aa_3rd");
|
||||||
|
tt_int_op(write_str_to_file(path, "Test 3\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "ab_4th");
|
||||||
|
tt_int_op(write_str_to_file(path, "Test 4\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
char torrc_contents[1000];
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents),
|
||||||
|
"%%include %s\n",
|
||||||
|
torrcd);
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
int include_used;
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_ptr_op(result, OP_NE, NULL);
|
||||||
|
tt_int_op(include_used, OP_EQ, 1);
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
config_line_t *next;
|
||||||
|
for (next = result; next != NULL; next = next->next) {
|
||||||
|
char expected[10];
|
||||||
|
tor_snprintf(expected, sizeof(expected), "%d", len + 1);
|
||||||
|
tt_str_op(next->key, OP_EQ, "Test");
|
||||||
|
tt_str_op(next->value, OP_EQ, expected);
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
tt_int_op(len, OP_EQ, 4);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_path_syntax(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_path_syntax"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char torrc_contents[1000];
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents),
|
||||||
|
"%%include \"%s\"\n"
|
||||||
|
"%%include %s"PATH_SEPARATOR"\n"
|
||||||
|
"%%include \"%s"PATH_SEPARATOR"\"\n",
|
||||||
|
dir, dir, dir);
|
||||||
|
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
int include_used;
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_ptr_op(result, OP_EQ, NULL);
|
||||||
|
tt_int_op(include_used, OP_EQ, 1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_not_processed(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char torrc_contents[1000] = "%include does_not_exist\n";
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
tt_int_op(config_get_lines(torrc_contents, &result, 0),OP_EQ, 0);
|
||||||
|
tt_ptr_op(result, OP_NE, NULL);
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
config_line_t *next;
|
||||||
|
for (next = result; next != NULL; next = next->next) {
|
||||||
|
tt_str_op(next->key, OP_EQ, "%include");
|
||||||
|
tt_str_op(next->value, OP_EQ, "does_not_exist");
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
tt_int_op(len, OP_EQ, 1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_has_include(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_has_include"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char torrc_contents[1000] = "Test 1\n";
|
||||||
|
config_line_t *result = NULL;
|
||||||
|
int include_used;
|
||||||
|
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_int_op(include_used, OP_EQ, 0);
|
||||||
|
config_free_lines(result);
|
||||||
|
|
||||||
|
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s\n", dir);
|
||||||
|
tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
|
||||||
|
OP_EQ, 0);
|
||||||
|
tt_int_op(include_used, OP_EQ, 1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
config_free_lines(result);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_flag_both_without(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *errmsg = NULL;
|
||||||
|
char conf_empty[1000];
|
||||||
|
tor_snprintf(conf_empty, sizeof(conf_empty),
|
||||||
|
"DataDirectory %s\n",
|
||||||
|
get_fname(NULL));
|
||||||
|
// test with defaults-torrc and torrc without include
|
||||||
|
int ret = options_init_from_string(conf_empty, conf_empty, CMD_RUN_UNITTESTS,
|
||||||
|
NULL, &errmsg);
|
||||||
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
|
||||||
|
const or_options_t *options = get_options();
|
||||||
|
tt_int_op(options->IncludeUsed, OP_EQ, 0);
|
||||||
|
|
||||||
|
done:
|
||||||
|
tor_free(errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_flag_torrc_only(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *errmsg = NULL;
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_flag_torrc_only"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char path[PATH_MAX+1];
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", dir, "dummy");
|
||||||
|
tt_int_op(write_str_to_file(path, "\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
char conf_empty[1000];
|
||||||
|
tor_snprintf(conf_empty, sizeof(conf_empty),
|
||||||
|
"DataDirectory %s\n",
|
||||||
|
get_fname(NULL));
|
||||||
|
char conf_include[1000];
|
||||||
|
tor_snprintf(conf_include, sizeof(conf_include), "%%include %s", path);
|
||||||
|
|
||||||
|
// test with defaults-torrc without include and torrc with include
|
||||||
|
int ret = options_init_from_string(conf_empty, conf_include,
|
||||||
|
CMD_RUN_UNITTESTS, NULL, &errmsg);
|
||||||
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
|
||||||
|
const or_options_t *options = get_options();
|
||||||
|
tt_int_op(options->IncludeUsed, OP_EQ, 1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
tor_free(errmsg);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_config_include_flag_defaults_only(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
char *errmsg = NULL;
|
||||||
|
char *dir = tor_strdup(get_fname("test_include_flag_defaults_only"));
|
||||||
|
tt_ptr_op(dir, OP_NE, NULL);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
tt_int_op(mkdir(dir), OP_EQ, 0);
|
||||||
|
#else
|
||||||
|
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char path[PATH_MAX+1];
|
||||||
|
tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", dir, "dummy");
|
||||||
|
tt_int_op(write_str_to_file(path, "\n", 0), OP_EQ, 0);
|
||||||
|
|
||||||
|
char conf_empty[1000];
|
||||||
|
tor_snprintf(conf_empty, sizeof(conf_empty),
|
||||||
|
"DataDirectory %s\n",
|
||||||
|
get_fname(NULL));
|
||||||
|
char conf_include[1000];
|
||||||
|
tor_snprintf(conf_include, sizeof(conf_include), "%%include %s", path);
|
||||||
|
|
||||||
|
// test with defaults-torrc with include and torrc without include
|
||||||
|
int ret = options_init_from_string(conf_include, conf_empty,
|
||||||
|
CMD_RUN_UNITTESTS, NULL, &errmsg);
|
||||||
|
tt_int_op(ret, OP_EQ, 0);
|
||||||
|
|
||||||
|
const or_options_t *options = get_options();
|
||||||
|
tt_int_op(options->IncludeUsed, OP_EQ, 0);
|
||||||
|
|
||||||
|
done:
|
||||||
|
tor_free(errmsg);
|
||||||
|
tor_free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
#define CONFIG_TEST(name, flags) \
|
#define CONFIG_TEST(name, flags) \
|
||||||
{ #name, test_config_ ## name, flags, NULL, NULL }
|
{ #name, test_config_ ## name, flags, NULL, NULL }
|
||||||
|
|
||||||
|
@ -4836,6 +5372,19 @@ struct testcase_t config_tests[] = {
|
||||||
CONFIG_TEST(parse_port_config__ports__server_options, 0),
|
CONFIG_TEST(parse_port_config__ports__server_options, 0),
|
||||||
CONFIG_TEST(parse_port_config__ports__ports_given, 0),
|
CONFIG_TEST(parse_port_config__ports__ports_given, 0),
|
||||||
CONFIG_TEST(parse_log_severity, 0),
|
CONFIG_TEST(parse_log_severity, 0),
|
||||||
|
CONFIG_TEST(include_limit, 0),
|
||||||
|
CONFIG_TEST(include_does_not_exist, 0),
|
||||||
|
CONFIG_TEST(include_error_in_included_file, 0),
|
||||||
|
CONFIG_TEST(include_empty_file_folder, 0),
|
||||||
|
CONFIG_TEST(include_recursion_before_after, 0),
|
||||||
|
CONFIG_TEST(include_recursion_after_only, 0),
|
||||||
|
CONFIG_TEST(include_folder_order, 0),
|
||||||
|
CONFIG_TEST(include_path_syntax, 0),
|
||||||
|
CONFIG_TEST(include_not_processed, 0),
|
||||||
|
CONFIG_TEST(include_has_include, 0),
|
||||||
|
CONFIG_TEST(include_flag_both_without, TT_FORK),
|
||||||
|
CONFIG_TEST(include_flag_torrc_only, TT_FORK),
|
||||||
|
CONFIG_TEST(include_flag_defaults_only, TT_FORK),
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5731,6 +5731,85 @@ test_util_htonll(void *arg)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_util_get_unquoted_path(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
char *r;
|
||||||
|
|
||||||
|
r = get_unquoted_path("\""); // "
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"\"\""); // """
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\\\""); // \"
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\\\"\\\""); // \"\"
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("A\\B\\C\""); // A\B\C"
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"A\\B\\C"); // "A\B\C
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"A\\B\"C\""); // "A\B"C"
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("A\\B\"C"); // A\B"C
|
||||||
|
tt_ptr_op(r, OP_EQ, NULL);
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("");
|
||||||
|
tt_str_op(r, OP_EQ, "");
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"\""); // ""
|
||||||
|
tt_str_op(r, OP_EQ, "");
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("A\\B\\C"); // A\B\C
|
||||||
|
tt_str_op(r, OP_EQ, "A\\B\\C"); // A\B\C
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"A\\B\\C\""); // "A\B\C"
|
||||||
|
tt_str_op(r, OP_EQ, "A\\B\\C"); // A\B\C
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"\\\""); // "\"
|
||||||
|
tt_str_op(r, OP_EQ, "\\"); // \ /* comment to prevent line continuation */
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"\\\"\""); // "\""
|
||||||
|
tt_str_op(r, OP_EQ, "\""); // "
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"A\\B\\C\\\"\""); // "A\B\C\""
|
||||||
|
tt_str_op(r, OP_EQ, "A\\B\\C\""); // A\B\C"
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("A\\B\\\"C"); // A\B\"C
|
||||||
|
tt_str_op(r, OP_EQ, "A\\B\"C"); // A\B"C
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
r = get_unquoted_path("\"A\\B\\\"C\""); // "A\B\"C"
|
||||||
|
tt_str_op(r, OP_EQ, "A\\B\"C"); // A\B"C
|
||||||
|
tor_free(r);
|
||||||
|
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
#define UTIL_LEGACY(name) \
|
#define UTIL_LEGACY(name) \
|
||||||
{ #name, test_util_ ## name , 0, NULL, NULL }
|
{ #name, test_util_ ## name , 0, NULL, NULL }
|
||||||
|
|
||||||
|
@ -5833,6 +5912,7 @@ struct testcase_t util_tests[] = {
|
||||||
UTIL_TEST(monotonic_time, 0),
|
UTIL_TEST(monotonic_time, 0),
|
||||||
UTIL_TEST(monotonic_time_ratchet, TT_FORK),
|
UTIL_TEST(monotonic_time_ratchet, TT_FORK),
|
||||||
UTIL_TEST(htonll, 0),
|
UTIL_TEST(htonll, 0),
|
||||||
|
UTIL_TEST(get_unquoted_path, 0),
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue