Add support for patterns on %include #25140

Also adds generic tor_glob function to expand globs.
This commit is contained in:
Daniel Pinto 2020-06-03 22:09:42 +01:00
parent a7226ca06e
commit 34fa2c4d0d
20 changed files with 1339 additions and 87 deletions

View File

@ -866,6 +866,7 @@ dnl Where do you live, libevent? And how do we call you?
if test "$bwin32" = "true"; then
TOR_LIB_WS32=-lws2_32
TOR_LIB_IPHLPAPI=-liphlpapi
TOR_LIB_SHLWAPI=-lshlwapi
# Some of the cargo-cults recommend -lwsock32 as well, but I don't
# think it's actually necessary.
TOR_LIB_GDI=-lgdi32
@ -878,6 +879,7 @@ fi
AC_SUBST(TOR_LIB_WS32)
AC_SUBST(TOR_LIB_GDI)
AC_SUBST(TOR_LIB_IPHLPAPI)
AC_SUBST(TOR_LIB_SHLWAPI)
AC_SUBST(TOR_LIB_USERENV)
tor_libevent_pkg_redhat="libevent"
@ -1646,7 +1648,8 @@ AC_CHECK_HEADERS([errno.h \
sys/utime.h \
sys/wait.h \
syslog.h \
utime.h])
utime.h \
glob.h])
AC_CHECK_HEADERS(sys/param.h)

View File

@ -205,14 +205,22 @@ 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.
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
option with the value being a path. This path can have wildcards. Wildcards are
expanded first, using lexical order. Then, for each matching file or folder, the
following rules are followed: 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.
order. Files starting with a dot are ignored. Files in subfolders are ignored.
The %include option can be used recursively.
New configuration files or directories cannot be added to already running Tor
instance if **Sandbox** is enabled.
The supported wildcards are * meaning any number of characters including none
and ? meaning exactly one character. These characters can be escaped by preceding
them with a backslash, except on Windows. Files starting with a dot are not matched
when expanding wildcards unless the starting dot is explicitly in the pattern, except
on Windows.
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
the defaults file.

View File

@ -35,7 +35,7 @@ FUZZING_LIBS = \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
@TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@ \
@TOR_LZMA_LIBS@ \
@TOR_ZSTD_LIBS@

View File

@ -18,7 +18,7 @@ src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_li
src_app_tor_LDADD = $(TOR_INTERNAL_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \
@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@
@ -29,7 +29,7 @@ src_app_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
src_app_tor_cov_LDADD = $(TOR_INTERNAL_TESTING_LIBS) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ \
@CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \
@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@
endif

View File

@ -242,11 +242,12 @@
#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.
## option with the value being a path. This path can have wildcards. Wildcards are
## expanded first, using lexical order. Then, for each matching file or folder, the following
## rules are followed: 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
#%include /etc/torrc.d/*.conf

View File

@ -21,6 +21,8 @@
#include "lib/malloc/malloc.h"
#include "lib/string/printf.h"
#include <stdbool.h>
static smartlist_t *config_get_file_list(const char *path,
smartlist_t *opened_files);
static int config_get_included_config(const char *path, int recursion_level,
@ -50,19 +52,58 @@ config_get_lines_include(const char *string, config_line_t **result,
opened_lst, 1, NULL, config_process_include);
}
/** 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,
/** Returns a list of paths obtained when expading globs in <b>pattern</b>. If
* <b>pattern</b> has no globs, returns a list with <b>pattern</b> if it is an
* existing path or NULL otherwise. If <b>opened_files</b> is provided, adds
* paths opened by glob to it. Returns NULL on failure. */
static smartlist_t *
expand_glob(const char *pattern, smartlist_t *opened_files)
{
smartlist_t *matches = tor_glob(pattern);
if (!matches) {
return NULL;
}
// if it is not a glob, return error when the path is missing
if (!has_glob(pattern) && smartlist_len(matches) == 0) {
smartlist_free(matches);
return NULL;
}
if (opened_files) {
smartlist_t *glob_opened = get_glob_opened_files(pattern);
if (!glob_opened) {
SMARTLIST_FOREACH(matches, char *, f, tor_free(f));
smartlist_free(matches);
return NULL;
}
smartlist_add_all(opened_files, glob_opened);
smartlist_free(glob_opened);
}
smartlist_sort_strings(matches);
return matches;
}
/** Returns a list of configuration files present on paths that match
* <b>pattern</b>. The pattern is expanded and then all the paths are
* processed. A path can be a file or a directory. If it is a file, that file
* will be added to the list to be returned. 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>.
* whose name starts with a dot will be added to the list to be returned.
* <b>opened_files</b> will have a list of files opened by this function
* if provided. Return 0 on success, -1 on failure. Ignores empty files.
* if provided. Return NULL on failure. Ignores empty files.
*/
static smartlist_t *
config_get_file_list(const char *path, smartlist_t *opened_files)
config_get_file_list(const char *pattern, smartlist_t *opened_files)
{
smartlist_t *file_list = smartlist_new();
smartlist_t *glob_matches = expand_glob(pattern, opened_files);
if (!glob_matches) {
return NULL;
}
bool error_found = false;
smartlist_t *file_list = smartlist_new();
SMARTLIST_FOREACH_BEGIN(glob_matches, char *, path) {
if (opened_files) {
smartlist_add_strdup(opened_files, path);
}
@ -70,23 +111,20 @@ config_get_file_list(const char *path, smartlist_t *opened_files)
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;
error_found = true;
break;
}
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 (opened_files) {
smartlist_add_strdup(opened_files, fullname);
@ -98,14 +136,25 @@ config_get_file_list(const char *path, smartlist_t *opened_files)
}
smartlist_add(file_list, fullname);
} SMARTLIST_FOREACH_END(f);
SMARTLIST_FOREACH(all_files, char *, f, tor_free(f));
smartlist_free(all_files);
return file_list;
} else if (file_type == FN_EMPTY) {
return file_list;
continue;
} else {
smartlist_free(file_list);
return NULL;
error_found = true;
break;
}
} SMARTLIST_FOREACH_END(path);
SMARTLIST_FOREACH(glob_matches, char *, f, tor_free(f));
smartlist_free(glob_matches);
if (error_found) {
SMARTLIST_FOREACH(file_list, char *, f, tor_free(f));
smartlist_free(file_list);
file_list = NULL;
}
return file_list;
}
/** Creates a list of config lines present on included <b>path</b>.
@ -133,19 +182,19 @@ config_get_included_config(const char *path, int recursion_level, int extended,
return 0;
}
/** Process an %include <b>path</b> in a config file. Set <b>list</b> to the
/** Process an %include <b>pattern</b> in a config file. Set <b>list</b> to the
* list of configuration settings obtained and <b>list_last</b> to the last
* element of the same list. <b>opened_lst</b> will have a list of opened
* files if provided. Return 0 on success, -1 on failure. */
static int
config_process_include(const char *path, int recursion_level, int extended,
config_process_include(const char *pattern, int recursion_level, int extended,
config_line_t **list, config_line_t **list_last,
smartlist_t *opened_lst)
{
config_line_t *ret_list = NULL;
config_line_t **next = &ret_list;
smartlist_t *config_files = config_get_file_list(path, opened_lst);
smartlist_t *config_files = config_get_file_list(pattern, opened_lst);
if (!config_files) {
return -1;
}

View File

@ -247,6 +247,22 @@ file_status(const char *fname)
}
}
/** Returns true if <b>file_type</b> represents an existing file (even if
* empty). Returns false otherwise. */
bool
is_file(file_status_t file_type)
{
return file_type != FN_ERROR && file_type != FN_NOENT && file_type != FN_DIR;
}
/** Returns true if <b>file_type</b> represents an existing directory. Returns
* false otherwise. */
bool
is_dir(file_status_t file_type)
{
return file_type == FN_DIR;
}
/** Create a file named <b>fname</b> with the contents <b>str</b>. Overwrite
* the previous <b>fname</b> if possible. Return 0 on success, -1 on failure.
*

View File

@ -55,6 +55,8 @@ MOCK_DECL(int,tor_unlink,(const char *pathname));
typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR, FN_EMPTY } file_status_t;
file_status_t file_status(const char *filename);
bool is_file(file_status_t file_type);
bool is_dir(file_status_t file_type);
int64_t tor_get_avail_disk_space(const char *path);

View File

@ -13,18 +13,44 @@
#include "lib/malloc/malloc.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/container/smartlist.h"
#include "lib/sandbox/sandbox.h"
#include "lib/string/printf.h"
#include "lib/string/util_string.h"
#include "lib/string/compat_ctype.h"
#include "lib/string/compat_string.h"
#include "lib/fs/files.h"
#include "lib/fs/dir.h"
#include "lib/fs/userdb.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#include <shlwapi.h>
#else /* !(defined(_WIN32)) */
#include <dirent.h>
#include <glob.h>
#endif /* defined(_WIN32) */
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#define IS_GLOB_CHAR(s,i) (((s)[(i)]) == '*' || ((s)[(i)]) == '?')
#else
#define IS_GLOB_CHAR(s,i) ((((s)[(i)]) == '*' || ((s)[(i)]) == '?') &&\
((i) == 0 || (s)[(i)-1] != '\\')) /* check escape */
#endif
/** 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 success or 0 if <b>path</b> is not quoted correctly. */
@ -294,3 +320,273 @@ make_path_absolute(const char *fname)
return absfname;
#endif /* defined(_WIN32) */
}
/** Expands globs in <b>pattern</b> for the path fragment between
* <b>prev_sep</b> and <b>next_sep</b>. Returns NULL on failure. */
typedef struct smartlist_t * unglob_fn(const char *pattern, int prev_sep,
int next_sep);
/** Adds <b>path</b> to <b>result</b> if it exists and is a file type we can
* handle. Returns false if <b>path</b> is a file type we cannot handle,
* returns true otherwise. Used on tor_glob for WIN32. */
static bool
add_non_glob_path(const char *path, struct smartlist_t *result)
{
file_status_t file_type = file_status(path);
if (file_type == FN_ERROR) {
return false;
} else if (file_type != FN_NOENT) {
char *to_add = tor_strdup(path);
clean_fname_for_stat(to_add);
smartlist_add(result, to_add);
}
/* If WIN32 tor_glob is called with a non-existing path, we want it to
* return an empty list instead of error to match the regular version */
return true;
}
/** Auxiliary function used by get_glob_opened_files and WIN32 tor_glob.
* Returns a list of paths obtained from <b>pattern</b> using <b>unglob</b> to
* expand each path fragment. If <b>final</b> is true, the paths are the result
* of the glob expansion of <b>pattern</b> (implements tor_glob). Otherwise,
* the paths are the paths opened by glob while expanding <b>pattern</b>
* (implements get_glb_opened_files). Returns NULL on failure. */
static struct smartlist_t *
get_glob_paths(const char *pattern, unglob_fn unglob, bool final)
{
smartlist_t *result = smartlist_new();
int i, prev_sep = -1, next_sep = -1;
bool is_glob = false, error_found = false, is_sep = false, is_last = false;
// find first path fragment with globs
for (i = 0; pattern[i]; i++) {
is_glob = is_glob || IS_GLOB_CHAR(pattern, i);
is_last = !pattern[i+1];
is_sep = pattern[i] == *PATH_SEPARATOR || pattern[i] == '/';
if (is_sep || is_last) {
prev_sep = next_sep;
next_sep = i; // next_sep+1 is start of next fragment or end of string
if (is_glob) {
break;
}
}
}
if (!is_glob) { // pattern fully expanded or no glob in pattern
if (final && !add_non_glob_path(pattern, result)) {
error_found = true;
goto end;
}
return result;
}
if (!final) {
// add path before the glob to result
int len = prev_sep < 1 ? prev_sep + 1 : prev_sep; // handle /*
char *path_until_glob = tor_strndup(pattern, len);
smartlist_add(result, path_until_glob);
}
smartlist_t *unglobbed_paths = unglob(pattern, prev_sep, next_sep);
if (!unglobbed_paths) {
error_found = true;
} else {
// for each path for current fragment, add the rest of the pattern
// and call recursively to get all expanded paths
SMARTLIST_FOREACH_BEGIN(unglobbed_paths, char *, current_path) {
char *next_path;
tor_asprintf(&next_path, "%s"PATH_SEPARATOR"%s", current_path,
&pattern[next_sep+1]);
smartlist_t *opened_next = get_glob_paths(next_path, unglob, final);
tor_free(next_path);
if (!opened_next) {
error_found = true;
break;
}
smartlist_add_all(result, opened_next);
smartlist_free(opened_next);
} SMARTLIST_FOREACH_END(current_path);
SMARTLIST_FOREACH(unglobbed_paths, char *, p, tor_free(p));
smartlist_free(unglobbed_paths);
}
end:
if (error_found) {
SMARTLIST_FOREACH(result, char *, p, tor_free(p));
smartlist_free(result);
result = NULL;
}
return result;
}
#ifdef _WIN32
/** Expands globs in <b>pattern</b> for the path fragment between
* <b>prev_sep</b> and <b>next_sep</b> using the WIN32 API. Returns NULL on
* failure. Used by the WIN32 implementation of tor_glob. */
static struct smartlist_t *
unglob_win32(const char *pattern, int prev_sep, int next_sep)
{
smartlist_t *result = smartlist_new();
int len = prev_sep < 1 ? prev_sep + 1 : prev_sep; // handle /*
char *path_until_glob = tor_strndup(pattern, len);
if (!is_file(file_status(path_until_glob))) {
smartlist_t *filenames = tor_listdir(path_until_glob);
if (!filenames) {
smartlist_free(result);
result = NULL;
} else {
SMARTLIST_FOREACH_BEGIN(filenames, char *, filename) {
TCHAR tpattern[MAX_PATH] = {0};
TCHAR tfile[MAX_PATH] = {0};
char *full_path;
tor_asprintf(&full_path, "%s"PATH_SEPARATOR"%s",
path_until_glob, filename);
char *path_curr_glob = tor_strndup(pattern, next_sep + 1);
// *\ must return only dirs, remove \ from the pattern so it matches
if (is_dir(file_status(full_path))) {
clean_fname_for_stat(path_curr_glob);
}
#ifdef UNICODE
mbstowcs(tpattern, path_curr_glob, MAX_PATH);
mbstowcs(tfile, full_path, MAX_PATH);
#else
strlcpy(tpattern, path_curr_glob, MAX_PATH);
strlcpy(tfile, full_path, MAX_PATH);
#endif
if (PathMatchSpec(tfile, tpattern)) {
smartlist_add(result, full_path);
} else {
tor_free(full_path);
}
tor_free(path_curr_glob);
} SMARTLIST_FOREACH_END(filename);
SMARTLIST_FOREACH(filenames, char *, p, tor_free(p));
smartlist_free(filenames);
}
}
tor_free(path_until_glob);
return result;
}
#else /* !defined(_WIN32) */
/** Same as opendir but calls sandbox_intern_string before */
static DIR *
prot_opendir(const char *name)
{
return opendir(sandbox_intern_string(name));
}
/** Same as stat but calls sandbox_intern_string before */
static int
prot_stat(const char *pathname, struct stat *buf)
{
return stat(sandbox_intern_string(pathname), buf);
}
/** Same as lstat but calls sandbox_intern_string before */
static int
prot_lstat(const char *pathname, struct stat *buf)
{
return lstat(sandbox_intern_string(pathname), buf);
}
#endif /* defined(_WIN32) */
/** Return a new list containing the paths that match the pattern
* <b>pattern</b>. Return NULL on error.
*/
struct smartlist_t *
tor_glob(const char *pattern)
{
smartlist_t *result;
#ifdef _WIN32
// PathMatchSpec does not support forward slashes, change them to backslashes
char *pattern_normalized = tor_strdup(pattern);
tor_strreplacechar(pattern_normalized, '/', *PATH_SEPARATOR);
result = get_glob_paths(pattern_normalized, unglob_win32, true);
tor_free(pattern_normalized);
#else /* !(defined(_WIN32)) */
glob_t matches;
int flags = GLOB_ERR | GLOB_NOSORT;
#ifdef GLOB_ALTDIRFUNC
/* use functions that call sandbox_intern_string */
flags |= GLOB_ALTDIRFUNC;
typedef void *(*gl_opendir)(const char * name);
typedef struct dirent *(*gl_readdir)(void *);
typedef void (*gl_closedir)(void *);
matches.gl_opendir = (gl_opendir) &prot_opendir;
matches.gl_readdir = (gl_readdir) &readdir;
matches.gl_closedir = (gl_closedir) &closedir;
matches.gl_stat = &prot_stat;
matches.gl_lstat = &prot_lstat;
#endif /* defined(GLOB_ALTDIRFUNC) */
int ret = glob(pattern, flags, NULL, &matches);
if (ret == GLOB_NOMATCH) {
return smartlist_new();
} else if (ret != 0) {
return NULL;
}
result = smartlist_new();
size_t i;
for (i = 0; i < matches.gl_pathc; i++) {
char *match = tor_strdup(matches.gl_pathv[i]);
size_t len = strlen(match);
if (len > 0 && match[len-1] == *PATH_SEPARATOR) {
match[len-1] = '\0';
}
smartlist_add(result, match);
}
globfree(&matches);
#endif /* defined(_WIN32) */
return result;
}
/** Returns true if <b>s</b> contains characters that can be globbed.
* Returns false otherwise. */
bool
has_glob(const char *s)
{
int i;
for (i = 0; s[i]; i++) {
if (IS_GLOB_CHAR(s, i)) {
return true;
}
}
return false;
}
/** Expands globs in <b>pattern</b> for the path fragment between
* <b>prev_sep</b> and <b>next_sep</b> using tor_glob. Returns NULL on
* failure. Used by get_glob_opened_files. */
static struct smartlist_t *
unglob_opened_files(const char *pattern, int prev_sep, int next_sep)
{
(void)prev_sep;
smartlist_t *result = smartlist_new();
// if the following fragments have no globs, we're done
if (has_glob(&pattern[next_sep+1])) {
// if there is a glob after next_sep, we know it is a separator and not the
// last char and glob_path will have the path without the separator
char *glob_path = tor_strndup(pattern, next_sep);
smartlist_t *child_paths = tor_glob(glob_path);
tor_free(glob_path);
if (!child_paths) {
smartlist_free(result);
result = NULL;
} else {
smartlist_add_all(result, child_paths);
smartlist_free(child_paths);
}
}
return result;
}
/** Returns a list of files that are opened by the tor_glob function when
* called with <b>pattern</b>. Returns NULL on error. The purpose of this
* function is to create a list of files to be added to the sandbox white list
* before the sandbox is enabled. */
struct smartlist_t *
get_glob_opened_files(const char *pattern)
{
return get_glob_paths(pattern, unglob_opened_files, false);
}

View File

@ -12,6 +12,10 @@
#ifndef TOR_PATH_H
#define TOR_PATH_H
#include <stdbool.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include "lib/cc/compat_compiler.h"
#ifdef _WIN32
@ -26,5 +30,8 @@ int path_is_relative(const char *filename);
void clean_fname_for_stat(char *name);
int get_parent_directory(char *fname);
char *make_path_absolute(const char *fname);
struct smartlist_t *tor_glob(const char *pattern);
bool has_glob(const char *s);
struct smartlist_t *get_glob_opened_files(const char *pattern);
#endif /* !defined(TOR_PATH_H) */

View File

@ -200,6 +200,8 @@ static int filter_nopar_gen[] = {
#ifdef __NR__llseek
SCMP_SYS(_llseek),
#endif
// glob uses this..
SCMP_SYS(lstat),
SCMP_SYS(mkdir),
SCMP_SYS(mlockall),
#ifdef __NR_mmap

View File

@ -143,6 +143,15 @@ tor_strupper(char *s)
}
}
/** Replaces <b>old</b> with <b>replacement</b> in <b>s</b> */
void
tor_strreplacechar(char *s, char find, char replacement)
{
for (s = strchr(s, find); s; s = strchr(s + 1, find)) {
*s = replacement;
}
}
/** Return 1 if every character in <b>s</b> is printable, else return 0.
*/
int

View File

@ -31,6 +31,7 @@ int tor_digest256_is_zero(const char *digest);
#define HEX_CHARACTERS "0123456789ABCDEFabcdef"
void tor_strlower(char *s);
void tor_strupper(char *s);
void tor_strreplacechar(char *s, char find, char replacement);
int tor_strisprint(const char *s);
int tor_strisnonupper(const char *s);
int tor_strisspace(const char *s);

View File

@ -11,7 +11,7 @@ FUZZING_LIBS = \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
@TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@ \
@TOR_LZMA_LIBS@ \
@TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@

View File

@ -296,16 +296,15 @@ src_test_test_switch_id_LDADD = \
$(TOR_UTIL_TESTING_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_USERENV@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_USERENV@ \
@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
@TOR_LDFLAGS_libevent@
src_test_test_LDADD = \
$(TOR_INTERNAL_TESTING_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@
@ -334,7 +333,7 @@ src_test_bench_LDADD = \
$(TOR_INTERNAL_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@
@ -344,7 +343,7 @@ src_test_test_workqueue_LDADD = \
$(TOR_INTERNAL_TESTING_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@CURVE25519_LIBS@ \
@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@
@ -356,7 +355,7 @@ src_test_test_timers_LDADD = \
$(TOR_UTIL_TESTING_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@CURVE25519_LIBS@ \
@TOR_LZMA_LIBS@ @TOR_TRACE_LIBS@
src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
@ -393,7 +392,7 @@ src_test_test_ntor_cl_LDADD = \
$(TOR_INTERNAL_LIBS) \
$(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@CURVE25519_LIBS@ @TOR_LZMA_LIBS@ @TOR_TRACE_LIBS@
src_test_test_ntor_cl_AM_CPPFLAGS = \
$(AM_CPPFLAGS)
@ -403,7 +402,7 @@ src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
src_test_test_hs_ntor_cl_LDADD = \
$(TOR_INTERNAL_LIBS) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ \
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ \
@CURVE25519_LIBS@ @TOR_TRACE_LIBS@
src_test_test_hs_ntor_cl_AM_CPPFLAGS = \
$(AM_CPPFLAGS)
@ -416,7 +415,7 @@ src_test_test_bt_cl_LDADD = \
$(TOR_UTIL_TESTING_LIBS) \
$(rust_ldadd) \
@TOR_LIB_MATH@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
@TOR_TRACE_LIBS@
src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)

View File

@ -5957,6 +5957,286 @@ test_config_include_flag_defaults_only(void *data)
tor_free(dir);
}
static void
test_config_include_wildcards(void *data)
{
(void)data;
char *temp = NULL, *folder = NULL;
config_line_t *result = NULL;
char *dir = tor_strdup(get_fname("test_include_wildcards"));
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
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", dir, "01_one.conf");
tt_int_op(write_str_to_file(temp, "Test 1\n", 0), OP_EQ, 0);
tor_free(temp);
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", dir, "02_two.conf");
tt_int_op(write_str_to_file(temp, "Test 2\n", 0), OP_EQ, 0);
tor_free(temp);
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", dir, "aa_three.conf");
tt_int_op(write_str_to_file(temp, "Test 3\n", 0), OP_EQ, 0);
tor_free(temp);
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", dir, "foo");
tt_int_op(write_str_to_file(temp, "Test 6\n", 0), OP_EQ, 0);
tor_free(temp);
tor_asprintf(&folder, "%s"PATH_SEPARATOR"%s", dir, "folder");
#ifdef _WIN32
tt_int_op(mkdir(folder), OP_EQ, 0);
#else
tt_int_op(mkdir(folder, 0700), OP_EQ, 0);
#endif
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", folder, "04_four.conf");
tt_int_op(write_str_to_file(temp, "Test 4\n", 0), OP_EQ, 0);
tor_free(temp);
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", folder, "05_five.conf");
tt_int_op(write_str_to_file(temp, "Test 5\n", 0), OP_EQ, 0);
tor_free(temp);
char torrc_contents[1000];
int include_used;
// test pattern that matches no file
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"not-exist*\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_EQ, NULL);
tt_int_op(include_used, OP_EQ, 1);
config_free_lines(result);
#ifndef _WIN32
// test wildcard escaping
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"\\*\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, -1);
tt_ptr_op(result, OP_EQ, NULL);
tt_int_op(include_used, OP_EQ, 1);
config_free_lines(result);
#endif
// test pattern *.conf
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"*.conf\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), 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;
char expected[10];
for (next = result; next != NULL; next = next->next) {
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, 3);
config_free_lines(result);
// test pattern that matches folder and files
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"*\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
len = 0;
for (next = result; next != NULL; next = next->next) {
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, 6);
config_free_lines(result);
// test pattern ending in PATH_SEPARATOR, test linux path separator
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s/f*/\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
len = 0;
for (next = result; next != NULL; next = next->next) {
tor_snprintf(expected, sizeof(expected), "%d", len + 1 + 3);
tt_str_op(next->key, OP_EQ, "Test");
tt_str_op(next->value, OP_EQ, expected);
len++;
}
tt_int_op(len, OP_EQ, 2);
config_free_lines(result);
// test pattern with wildcards in folder and file
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"*"PATH_SEPARATOR"*.conf\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
len = 0;
for (next = result; next != NULL; next = next->next) {
tor_snprintf(expected, sizeof(expected), "%d", len + 1 + 3);
tt_str_op(next->key, OP_EQ, "Test");
tt_str_op(next->value, OP_EQ, expected);
len++;
}
tt_int_op(len, OP_EQ, 2);
config_free_lines(result);
done:
config_free_lines(result);
tor_free(folder);
tor_free(temp);
tor_free(dir);
}
static void
test_config_include_hidden(void *data)
{
(void)data;
char *temp = NULL, *folder = NULL;
config_line_t *result = NULL;
char *dir = tor_strdup(get_fname("test_include_hidden"));
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
tor_asprintf(&folder, "%s"PATH_SEPARATOR"%s", dir, ".dotdir");
#ifdef _WIN32
tt_int_op(mkdir(folder), OP_EQ, 0);
#else
tt_int_op(mkdir(folder, 0700), OP_EQ, 0);
#endif
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", folder, ".dotfile");
tt_int_op(write_str_to_file(temp, "Test 1\n", 0), OP_EQ, 0);
tor_free(temp);
tor_asprintf(&temp, "%s"PATH_SEPARATOR"%s", folder, "file");
tt_int_op(write_str_to_file(temp, "Test 2\n", 0), OP_EQ, 0);
tor_free(temp);
char torrc_contents[1000];
int include_used;
int len = 0;
config_line_t *next;
char expected[10];
// test wildcards do not expand to dot folders (except for windows)
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"*\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_int_op(include_used, OP_EQ, 1);
#ifdef _WIN32 // wildcard expansion includes dot files on Windows
for (next = result; next != NULL; next = next->next) {
tor_snprintf(expected, sizeof(expected), "%d", len + 2);
tt_str_op(next->key, OP_EQ, "Test");
tt_str_op(next->value, OP_EQ, expected);
len++;
}
tt_int_op(len, OP_EQ, 1);
#else
tt_ptr_op(result, OP_EQ, NULL);
#endif
config_free_lines(result);
// test wildcards match hidden folders when explicitly in the pattern
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR".*\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
len = 0;
for (next = result; next != NULL; next = next->next) {
tor_snprintf(expected, sizeof(expected), "%d", len + 2);
tt_str_op(next->key, OP_EQ, "Test");
tt_str_op(next->value, OP_EQ, expected);
len++;
}
tt_int_op(len, OP_EQ, 1);
config_free_lines(result);
// test hidden dir when explicitly included
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR".dotdir\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
len = 0;
for (next = result; next != NULL; next = next->next) {
tor_snprintf(expected, sizeof(expected), "%d", len + 2);
tt_str_op(next->key, OP_EQ, "Test");
tt_str_op(next->value, OP_EQ, expected);
len++;
}
tt_int_op(len, OP_EQ, 1);
config_free_lines(result);
// test hidden file when explicitly included
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR".dotdir"PATH_SEPARATOR".dotfile\n",
dir);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
NULL), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
len = 0;
for (next = result; next != NULL; next = next->next) {
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, 1);
config_free_lines(result);
done:
config_free_lines(result);
tor_free(folder);
tor_free(temp);
tor_free(dir);
}
static void
test_config_dup_and_filter(void *arg)
{
@ -6086,7 +6366,7 @@ test_config_include_opened_file_list(void *data)
smartlist_t *opened_files = smartlist_new();
char *torrcd = NULL;
char *subfolder = NULL;
char *path = NULL;
char *in_subfolder = NULL;
char *empty = NULL;
char *file = NULL;
char *dot = NULL;
@ -6115,9 +6395,9 @@ test_config_include_opened_file_list(void *data)
tt_int_op(mkdir(subfolder, 0700), OP_EQ, 0);
#endif
tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", subfolder,
tor_asprintf(&in_subfolder, "%s"PATH_SEPARATOR"%s", subfolder,
"01_file_in_subfolder");
tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0);
tt_int_op(write_str_to_file(in_subfolder, "Test 1\n", 0), OP_EQ, 0);
tor_asprintf(&empty, "%s"PATH_SEPARATOR"%s", torrcd, "empty");
tt_int_op(write_str_to_file(empty, "", 0), OP_EQ, 0);
@ -6148,13 +6428,69 @@ test_config_include_opened_file_list(void *data)
// dot files are not opened as we ignore them when we get their name from
// their parent folder
// test with wildcards
SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
smartlist_clear(opened_files);
config_free_lines(result);
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"*\n",
torrcd);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
opened_files), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
#ifdef _WIN32
tt_int_op(smartlist_len(opened_files), OP_EQ, 6);
#else
tt_int_op(smartlist_len(opened_files), OP_EQ, 5);
#endif
tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1);
// * will match the subfolder inside torrc.d, so it will be included
tt_int_op(smartlist_contains_string(opened_files, in_subfolder), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1);
#ifdef _WIN32
// * matches the dot file on Windows
tt_int_op(smartlist_contains_string(opened_files, dot), OP_EQ, 1);
#endif
// test with wildcards in folder and file
SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
smartlist_clear(opened_files);
config_free_lines(result);
tor_snprintf(torrc_contents, sizeof(torrc_contents),
"%%include %s"PATH_SEPARATOR"*"PATH_SEPARATOR"*\n",
torrcd);
tt_int_op(config_get_lines_include(torrc_contents, &result, 0, &include_used,
opened_files), OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
tt_int_op(include_used, OP_EQ, 1);
#ifdef _WIN32
tt_int_op(smartlist_len(opened_files), OP_EQ, 6);
#else
tt_int_op(smartlist_len(opened_files), OP_EQ, 5);
#endif
tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, in_subfolder), OP_EQ, 1);
// stat is called on the following files, so they count as opened
tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1);
tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1);
#ifdef _WIN32
// * matches the dot file on Windows
tt_int_op(smartlist_contains_string(opened_files, dot), OP_EQ, 1);
#endif
done:
SMARTLIST_FOREACH(opened_files, char *, f, tor_free(f));
smartlist_free(opened_files);
config_free_lines(result);
tor_free(torrcd);
tor_free(subfolder);
tor_free(path);
tor_free(in_subfolder);
tor_free(empty);
tor_free(file);
tor_free(dot);
@ -6538,6 +6874,8 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(include_flag_both_without, TT_FORK),
CONFIG_TEST(include_flag_torrc_only, TT_FORK),
CONFIG_TEST(include_flag_defaults_only, TT_FORK),
CONFIG_TEST(include_wildcards, 0),
CONFIG_TEST(include_hidden, 0),
CONFIG_TEST(dup_and_filter, 0),
CONFIG_TEST(check_bridge_distribution_setting_not_a_bridge, TT_FORK),
CONFIG_TEST(check_bridge_distribution_setting_valid, 0),

View File

@ -47,6 +47,17 @@
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerlist_st.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef _WIN32
/* For mkdir() */
#include <direct.h>
#else
#include <dirent.h>
#endif /* defined(_WIN32) */
#include "test/test.h"
#include "test/test_helpers.h"
#include "test/test_connection.h"
@ -194,6 +205,78 @@ mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
return tor_addr_lookup__real(name, family, out);
}
static char *
create_directory(const char *parent_dir, const char *name)
{
char *dir = NULL;
tor_asprintf(&dir, "%s"PATH_SEPARATOR"%s", parent_dir, name);
#ifdef _WIN32
tt_int_op(mkdir(dir), OP_EQ, 0);
#else
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
return dir;
done:
tor_free(dir);
return NULL;
}
static char *
create_file(const char *parent_dir, const char *name, const char *contents)
{
char *path = NULL;
tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", parent_dir, name);
contents = contents == NULL ? "" : contents;
tt_int_op(write_str_to_file(path, contents, 0), OP_EQ, 0);
return path;
done:
tor_free(path);
return NULL;
}
int
create_test_directory_structure(const char *parent_dir)
{
int ret = -1;
char *dir1 = NULL;
char *dir2 = NULL;
char *file1 = NULL;
char *file2 = NULL;
char *dot = NULL;
char *empty = NULL;
char *forbidden = NULL;
dir1 = create_directory(parent_dir, "dir1");
tt_assert(dir1);
dir2 = create_directory(parent_dir, "dir2");
tt_assert(dir2);
file1 = create_file(parent_dir, "file1", "Test 1");
tt_assert(file1);
file2 = create_file(parent_dir, "file2", "Test 2");
tt_assert(file2);
dot = create_file(parent_dir, ".test-hidden", "Test .");
tt_assert(dot);
empty = create_file(parent_dir, "empty", NULL);
tt_assert(empty);
forbidden = create_directory(parent_dir, "forbidden");
tt_assert(forbidden);
#ifndef _WIN32
tt_int_op(chmod(forbidden, 0), OP_EQ, 0);
#endif
ret = 0;
done:
tor_free(dir1);
tor_free(dir2);
tor_free(file1);
tor_free(file2);
tor_free(dot);
tor_free(empty);
tor_free(forbidden);
return ret;
}
/*********** Helper funcs for making new connections/streams *****************/
/* Helper for test_conn_get_connection() */

View File

@ -33,6 +33,8 @@ connection_t *test_conn_get_connection(uint8_t state,
uint8_t type, uint8_t purpose);
or_options_t *helper_parse_options(const char *conf);
int create_test_directory_structure(const char *parent_dir);
extern const char TEST_DESCRIPTORS[];
void *helper_setup_pubsub(const struct testcase_t *);

View File

@ -18,6 +18,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/defs/time.h"
#include "test/test.h"
#include "test/test_helpers.h"
#include "lib/memarea/memarea.h"
#include "lib/process/waitpid.h"
#include "lib/process/process_win32.h"
@ -4132,6 +4133,31 @@ test_util_find_str_at_start_of_line(void *ptr)
;
}
static void
test_util_tor_strreplacechar(void *ptr)
{
(void)ptr;
char empty[] = "";
char not_contain[] = "bbb";
char contains[] = "bab";
char contains_all[] = "aaa";
tor_strreplacechar(empty, 'a', 'b');
tt_str_op(empty, OP_EQ, "");
tor_strreplacechar(not_contain, 'a', 'b');
tt_str_op(not_contain, OP_EQ, "bbb");
tor_strreplacechar(contains, 'a', 'b');
tt_str_op(contains, OP_EQ, "bbb");
tor_strreplacechar(contains_all, 'a', 'b');
tt_str_op(contains_all, OP_EQ, "bbb");
done:
;
}
static void
test_util_string_is_C_identifier(void *ptr)
{
@ -4358,6 +4384,413 @@ test_util_listdir(void *ptr)
}
}
static void
test_util_glob(void *ptr)
{
(void)ptr;
smartlist_t *results = NULL;
int r, i;
char *dir1 = NULL, *dir2 = NULL, *forbidden = NULL, *dirname = NULL;
char *expected = NULL, *pattern = NULL;
// used for cleanup
char *dir1_forbidden = NULL, *dir2_forbidden = NULL;
char *forbidden_forbidden = NULL;
dirname = tor_strdup(get_fname("test_glob"));
tt_ptr_op(dirname, OP_NE, NULL);
#ifdef _WIN32
r = mkdir(dirname);
#else
r = mkdir(dirname, 0700);
#endif
if (r) {
fprintf(stderr, "Can't create directory %s:", dirname);
perror("");
exit(1);
}
tt_int_op(0, OP_EQ, create_test_directory_structure(dirname));
tor_asprintf(&dir1, "%s"PATH_SEPARATOR"dir1", dirname);
tor_asprintf(&dir1_forbidden,
"%s"PATH_SEPARATOR"dir1"PATH_SEPARATOR"forbidden", dirname);
tt_int_op(0, OP_EQ, create_test_directory_structure(dir1));
tor_asprintf(&dir2, "%s"PATH_SEPARATOR"dir2", dirname);
tor_asprintf(&dir2_forbidden,
"%s"PATH_SEPARATOR"dir2"PATH_SEPARATOR"forbidden", dirname);
tt_int_op(0, OP_EQ, create_test_directory_structure(dir2));
tor_asprintf(&forbidden, "%s"PATH_SEPARATOR"forbidden", dirname);
tor_asprintf(&forbidden_forbidden,
"%s"PATH_SEPARATOR"forbidden"PATH_SEPARATOR"forbidden",dirname);
#ifndef _WIN32
chmod(forbidden, 0700);
#endif
tt_int_op(0, OP_EQ, create_test_directory_structure(forbidden));
#ifndef _WIN32
chmod(forbidden, 0);
#endif
#define TEST(input) \
do { \
tor_asprintf(&pattern, "%s"PATH_SEPARATOR"%s", dirname, input); \
results = tor_glob(pattern); \
tor_free(pattern); \
tt_assert(results); \
smartlist_sort_strings(results); \
} while (0);
#define EXPECT(result) \
do { \
tt_int_op(smartlist_len(results), OP_EQ, \
sizeof(result)/sizeof(*result)); \
i = 0; \
SMARTLIST_FOREACH_BEGIN(results, const char *, f) { \
tor_asprintf(&expected, "%s"PATH_SEPARATOR"%s", dirname, result[i]); \
tt_str_op(f, OP_EQ, expected); \
i++; \
tor_free(expected); \
} SMARTLIST_FOREACH_END(f); \
SMARTLIST_FOREACH(results, char *, f, tor_free(f)); \
smartlist_free(results); \
} while (0);
#define EXPECT_EMPTY() \
do { \
tt_int_op(smartlist_len(results), OP_EQ, 0); \
SMARTLIST_FOREACH(results, char *, f, tor_free(f)); \
smartlist_free(results); \
} while (0);
// wilcards at beginning
const char *results_test1[] = {"dir2", "file2"};
TEST("*2");
EXPECT(results_test1);
// wildcards at end
const char *results_test2[] = {"dir1", "dir2"};
TEST("d*");
EXPECT(results_test2);
// wildcards at beginning and end
#ifdef _WIN32
// dot files are not ignored on Windows
const char *results_test3[] = {".test-hidden", "dir1", "dir2", "file1",
"file2", "forbidden"};
#else
const char *results_test3[] = {"dir1", "dir2", "file1", "file2",
"forbidden"};
#endif
TEST("*i*");
EXPECT(results_test3);
// wildcards in middle
const char *results_test4[] = {"dir1", "dir2"};
TEST("d?r*");
EXPECT(results_test4);
// test file that does not exist
TEST("not-exist");
EXPECT_EMPTY();
// test wildcard that matches nothing
TEST("*not-exist*");
EXPECT_EMPTY();
// test path separator at end - no wildcards
const char *results_test7[] = {"dir1"};
TEST("dir1");
EXPECT(results_test7);
const char *results_test8[] = {"dir1"};
TEST("dir1"PATH_SEPARATOR);
EXPECT(results_test8);
const char *results_test9[] = {"file1"};
TEST("file1");
EXPECT(results_test9);
#if defined(__APPLE__) || defined(__darwin__) || \
defined(__FreeBSD__) || defined(__NetBSD__) || defined(OpenBSD)
TEST("file1"PATH_SEPARATOR);
EXPECT_EMPTY();
#else
const char *results_test10[] = {"file1"};
TEST("file1"PATH_SEPARATOR);
EXPECT(results_test10);
#endif
// test path separator at end - with wildcards and linux path separator
const char *results_test11[] = {"dir1", "dir2", "forbidden"};
TEST("*/");
EXPECT(results_test11);
#ifdef _WIN32
// dot files are not ignored on Windows
const char *results_test12[] = {".test-hidden", "dir1", "dir2", "empty",
"file1", "file2", "forbidden"};
#else
const char *results_test12[] = {"dir1", "dir2", "empty", "file1", "file2",
"forbidden"};
#endif
TEST("*");
EXPECT(results_test12);
// wildcards on folder and file and linux path separator
const char *results_test13[] = {"dir1"PATH_SEPARATOR"dir1",
"dir1"PATH_SEPARATOR"dir2",
"dir1"PATH_SEPARATOR"file1",
"dir1"PATH_SEPARATOR"file2",
"dir2"PATH_SEPARATOR"dir1",
"dir2"PATH_SEPARATOR"dir2",
"dir2"PATH_SEPARATOR"file1",
"dir2"PATH_SEPARATOR"file2"};
TEST("?i*/?i*");
EXPECT(results_test13);
// wildcards on file only
const char *results_test14[] = {"dir1"PATH_SEPARATOR"dir1",
"dir1"PATH_SEPARATOR"dir2",
"dir1"PATH_SEPARATOR"file1",
"dir1"PATH_SEPARATOR"file2"};
TEST("dir1"PATH_SEPARATOR"?i*");
EXPECT(results_test14);
// wildcards on folder only
const char *results_test15[] = {"dir1"PATH_SEPARATOR"file1",
"dir2"PATH_SEPARATOR"file1"};
TEST("?i*"PATH_SEPARATOR"file1");
EXPECT(results_test15);
// wildcards after file name
TEST("file1"PATH_SEPARATOR"*");
EXPECT_EMPTY();
#ifndef _WIN32
// test wildcard escaping
TEST("\\*");
EXPECT_EMPTY();
// test forbidden directory
tor_asprintf(&pattern, "%s"PATH_SEPARATOR"*"PATH_SEPARATOR"*", dirname);
results = tor_glob(pattern);
tor_free(pattern);
tt_assert(!results);
#endif
#undef TEST
#undef EXPECT
#undef EXPECT_EMPTY
done:
#ifndef _WIN32
chmod(forbidden, 0700);
chmod(dir1_forbidden, 0700);
chmod(dir2_forbidden, 0700);
chmod(forbidden_forbidden, 0700);
#endif
tor_free(dir1);
tor_free(dir2);
tor_free(forbidden);
tor_free(dirname);
tor_free(dir1_forbidden);
tor_free(dir2_forbidden);
tor_free(forbidden_forbidden);
tor_free(expected);
tor_free(pattern);
if (results) {
SMARTLIST_FOREACH(results, char *, f, tor_free(f));
smartlist_free(results);
}
}
static void
test_util_get_glob_opened_files(void *ptr)
{
(void)ptr;
smartlist_t *results = NULL;
int r, i;
char *dir1 = NULL, *dir2 = NULL, *forbidden = NULL, *dirname = NULL;
char *expected = NULL, *pattern = NULL;
// used for cleanup
char *dir1_forbidden = NULL, *dir2_forbidden = NULL;
char *forbidden_forbidden = NULL;
dirname = tor_strdup(get_fname("test_get_glob_opened_files"));
tt_ptr_op(dirname, OP_NE, NULL);
#ifdef _WIN32
r = mkdir(dirname);
#else
r = mkdir(dirname, 0700);
#endif
if (r) {
fprintf(stderr, "Can't create directory %s:", dirname);
perror("");
exit(1);
}
tt_int_op(0, OP_EQ, create_test_directory_structure(dirname));
tor_asprintf(&dir1, "%s"PATH_SEPARATOR"dir1", dirname);
tor_asprintf(&dir1_forbidden,
"%s"PATH_SEPARATOR"dir1"PATH_SEPARATOR"forbidden", dirname);
tt_int_op(0, OP_EQ, create_test_directory_structure(dir1));
tor_asprintf(&dir2, "%s"PATH_SEPARATOR"dir2", dirname);
tor_asprintf(&dir2_forbidden,
"%s"PATH_SEPARATOR"dir2"PATH_SEPARATOR"forbidden", dirname);
tt_int_op(0, OP_EQ, create_test_directory_structure(dir2));
tor_asprintf(&forbidden, "%s"PATH_SEPARATOR"forbidden", dirname);
tor_asprintf(&forbidden_forbidden,
"%s"PATH_SEPARATOR"forbidden"PATH_SEPARATOR"forbidden",dirname);
#ifndef _WIN32
chmod(forbidden, 0700);
#endif
tt_int_op(0, OP_EQ, create_test_directory_structure(forbidden));
#ifndef _WIN32
chmod(forbidden, 0);
#endif
#define TEST(input) \
do { \
if (*input) { \
tor_asprintf(&pattern, "%s"PATH_SEPARATOR"%s", dirname, input); \
} else { /* do not add path separator if empty string */ \
tor_asprintf(&pattern, "%s", dirname); \
} \
results = get_glob_opened_files(pattern); \
tor_free(pattern); \
tt_assert(results); \
smartlist_sort_strings(results); \
} while (0);
#define EXPECT(result) \
do { \
tt_int_op(smartlist_len(results), OP_EQ, \
sizeof(result)/sizeof(*result)); \
i = 0; \
SMARTLIST_FOREACH_BEGIN(results, const char *, f) { \
if (*result[i]) { \
tor_asprintf(&expected, "%s"PATH_SEPARATOR"%s", dirname, result[i]); \
} else { /* do not add path separator if empty string */ \
tor_asprintf(&expected, "%s", dirname); \
} \
tt_str_op(f, OP_EQ, expected); \
i++; \
tor_free(expected); \
} SMARTLIST_FOREACH_END(f); \
SMARTLIST_FOREACH(results, char *, f, tor_free(f)); \
smartlist_free(results); \
} while (0);
#define EXPECT_EMPTY() \
do { \
tt_int_op(smartlist_len(results), OP_EQ, 0); \
SMARTLIST_FOREACH(results, char *, f, tor_free(f)); \
smartlist_free(results); \
} while (0);
// all files on folder
const char *results_test1[] = {""}; // only the folder is read
TEST("*");
EXPECT(results_test1);
// same as before but ending in path separator
const char *results_test2[] = {""}; // only the folder is read
TEST("*"PATH_SEPARATOR);
EXPECT(results_test2);
// wilcards in multiple path components
#ifndef _WIN32
const char *results_test3[] = {"", "dir1", "dir2", "empty", "file1", "file2",
"forbidden"};
#else
// dot files are not special on windows
const char *results_test3[] = {"", ".test-hidden", "dir1", "dir2", "empty",
"file1", "file2", "forbidden"};
#endif
TEST("*"PATH_SEPARATOR"*");
EXPECT(results_test3);
// same as before but ending in path separator
#ifndef _WIN32
const char *results_test4[] = {"", "dir1", "dir2", "empty", "file1", "file2",
"forbidden"};
#else
// dot files are not special on windows
const char *results_test4[] = {"", ".test-hidden", "dir1", "dir2", "empty",
"file1", "file2", "forbidden"};
#endif
TEST("*"PATH_SEPARATOR"*"PATH_SEPARATOR);
EXPECT(results_test4);
// no glob - folder
TEST("");
EXPECT_EMPTY();
// same as before but ending in path separator
TEST(PATH_SEPARATOR);
EXPECT_EMPTY();
// no glob - file
TEST("file1");
EXPECT_EMPTY();
// same as before but ending in path separator and linux path separator
TEST("file1/");
EXPECT_EMPTY();
// file but with wildcard after
const char *results_test9[] = {"file1"};
TEST("file1"PATH_SEPARATOR"*");
EXPECT(results_test9);
// dir inside dir and linux path separator
TEST("dir1/dir1");
EXPECT_EMPTY();
// same as before but ending in path separator
TEST("dir1"PATH_SEPARATOR"dir1"PATH_SEPARATOR);
EXPECT_EMPTY();
// no glob - empty
TEST("empty");
EXPECT_EMPTY();
// same as before but ending in path separator
TEST("empty"PATH_SEPARATOR);
EXPECT_EMPTY();
// no glob - does not exist
TEST("not_exist");
EXPECT_EMPTY();
#undef TEST
#undef EXPECT
#undef EXPECT_EMPTY
done:
#ifndef _WIN32
chmod(forbidden, 0700);
chmod(dir1_forbidden, 0700);
chmod(dir2_forbidden, 0700);
chmod(forbidden_forbidden, 0700);
#endif
tor_free(dir1);
tor_free(dir2);
tor_free(forbidden);
tor_free(dirname);
tor_free(dir1_forbidden);
tor_free(dir2_forbidden);
tor_free(forbidden_forbidden);
tor_free(expected);
tor_free(pattern);
if (results) {
SMARTLIST_FOREACH(results, char *, f, tor_free(f));
smartlist_free(results);
}
}
static void
test_util_parent_dir(void *ptr)
{
@ -6520,10 +6953,13 @@ struct testcase_t util_tests[] = {
UTIL_TEST(laplace, 0),
UTIL_TEST(clamp_double_to_int64, 0),
UTIL_TEST(find_str_at_start_of_line, 0),
UTIL_TEST(tor_strreplacechar, 0),
UTIL_TEST(string_is_C_identifier, 0),
UTIL_TEST(string_is_utf8, 0),
UTIL_TEST(asprintf, 0),
UTIL_TEST(listdir, 0),
UTIL_TEST(glob, 0),
UTIL_TEST(get_glob_opened_files, 0),
UTIL_TEST(parent_dir, 0),
UTIL_TEST(ftruncate, 0),
UTIL_TEST(nowrap_math, 0),

View File

@ -11,7 +11,7 @@ src_tools_tor_resolve_LDADD = \
$(TOR_UTIL_LIBS) \
$(TOR_CRYPTO_LIBS) $(TOR_LIBS_CRYPTLIB)\
$(rust_ldadd) \
@TOR_LIB_MATH@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_USERENV@
@TOR_LIB_MATH@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_USERENV@
if COVERAGE_ENABLED
src_tools_tor_cov_resolve_SOURCES = src/tools/tor-resolve.c
@ -36,7 +36,7 @@ src_tools_tor_gencert_LDADD = \
$(TOR_UTIL_LIBS) \
$(rust_ldadd) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@
endif
src_tools_tor_print_ed_signing_cert_SOURCES = src/tools/tor-print-ed-signing-cert.c
@ -46,7 +46,7 @@ src_tools_tor_print_ed_signing_cert_LDADD = \
$(TOR_CRYPTO_LIBS) \
$(TOR_UTIL_LIBS) \
@TOR_LIB_MATH@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_USERENV@ @TOR_LIB_GDI@
@TOR_LIB_WS32@ @TOR_LIB_USERENV@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@
if USE_NSS
# ...
@ -61,7 +61,7 @@ src_tools_tor_cov_gencert_LDADD = \
$(TOR_CRYPTO_TESTING_LIBS) \
$(TOR_UTIL_TESTING_LIBS) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
endif
endif