mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 23:53:32 +01:00
Merge branch 'bug5105-v2-squashed'
Conflicts: src/or/transports.c
This commit is contained in:
commit
eaedcba493
11
changes/bug5105
Normal file
11
changes/bug5105
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
o Minor bugfixes:
|
||||||
|
|
||||||
|
- Ensure that variables set in Tor's environment cannot override
|
||||||
|
environment variables which Tor tries to pass to a managed
|
||||||
|
pluggable-transport proxy. Previously, Tor would pass every
|
||||||
|
variable in its environment to managed proxies along with the
|
||||||
|
new ones, in such a way that on many operating systems, the
|
||||||
|
inherited environment variables would override those which Tor
|
||||||
|
tried to explicitly set. Bugfix on 0.2.3.12-alpha for most
|
||||||
|
Unixoid systems; bugfix on 0.2.3.9-alpha for Windows.
|
||||||
|
|
@ -172,6 +172,35 @@ _tor_malloc_zero(size_t size DMALLOC_PARAMS)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill
|
||||||
|
* the memory with zero bytes, and return a pointer to the result.
|
||||||
|
* Log and terminate the process on error. (Same as
|
||||||
|
* calloc(<b>nmemb</b>,<b>size</b>), but never returns NULL.)
|
||||||
|
*
|
||||||
|
* XXXX This implementation probably asserts in cases where it could
|
||||||
|
* work, because it only tries dividing SIZE_MAX by size (according to
|
||||||
|
* the calloc(3) man page, the size of an element of the nmemb-element
|
||||||
|
* array to be allocated), not by nmemb (which could in theory be
|
||||||
|
* smaller than size). Don't do that then.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS)
|
||||||
|
{
|
||||||
|
/* You may ask yourself, "wouldn't it be smart to use calloc instead of
|
||||||
|
* malloc+memset? Perhaps libc's calloc knows some nifty optimization trick
|
||||||
|
* we don't!" Indeed it does, but its optimizations are only a big win when
|
||||||
|
* we're allocating something very big (it knows if it just got the memory
|
||||||
|
* from the OS in a pre-zeroed state). We don't want to use tor_malloc_zero
|
||||||
|
* for big stuff, so we don't bother with calloc. */
|
||||||
|
void *result;
|
||||||
|
size_t max_nmemb = (size == 0) ? SIZE_MAX : SIZE_MAX/size;
|
||||||
|
|
||||||
|
tor_assert(nmemb < max_nmemb);
|
||||||
|
|
||||||
|
result = _tor_malloc_zero((nmemb * size) DMALLOC_FN_ARGS);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b>
|
/** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b>
|
||||||
* bytes long; return the new memory block. On error, log and
|
* bytes long; return the new memory block. On error, log and
|
||||||
* terminate. (Like realloc(ptr,size), but never returns NULL.)
|
* terminate. (Like realloc(ptr,size), but never returns NULL.)
|
||||||
@ -3284,11 +3313,7 @@ process_handle_new(void)
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
tor_spawn_background(const char *const filename, const char **argv,
|
tor_spawn_background(const char *const filename, const char **argv,
|
||||||
#ifdef _WIN32
|
process_environment_t *env,
|
||||||
LPVOID envp,
|
|
||||||
#else
|
|
||||||
const char **envp,
|
|
||||||
#endif
|
|
||||||
process_handle_t **process_handle_out)
|
process_handle_t **process_handle_out)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -3305,8 +3330,6 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|||||||
SECURITY_ATTRIBUTES saAttr;
|
SECURITY_ATTRIBUTES saAttr;
|
||||||
char *joined_argv;
|
char *joined_argv;
|
||||||
|
|
||||||
(void)envp; // Unused on Windows
|
|
||||||
|
|
||||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
saAttr.bInheritHandle = TRUE;
|
saAttr.bInheritHandle = TRUE;
|
||||||
/* TODO: should we set explicit security attributes? (#2046, comment 5) */
|
/* TODO: should we set explicit security attributes? (#2046, comment 5) */
|
||||||
@ -3371,7 +3394,7 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|||||||
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
|
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
|
||||||
* work?) */
|
* work?) */
|
||||||
0, // creation flags
|
0, // creation flags
|
||||||
envp, // use parent's environment
|
(env==NULL) ? NULL : env->windows_environment_block,
|
||||||
NULL, // use parent's current directory
|
NULL, // use parent's current directory
|
||||||
&siStartInfo, // STARTUPINFO pointer
|
&siStartInfo, // STARTUPINFO pointer
|
||||||
&(process_handle->pid)); // receives PROCESS_INFORMATION
|
&(process_handle->pid)); // receives PROCESS_INFORMATION
|
||||||
@ -3501,8 +3524,8 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|||||||
/* Call the requested program. We need the cast because
|
/* Call the requested program. We need the cast because
|
||||||
execvp doesn't define argv as const, even though it
|
execvp doesn't define argv as const, even though it
|
||||||
does not modify the arguments */
|
does not modify the arguments */
|
||||||
if (envp)
|
if (env)
|
||||||
execve(filename, (char *const *) argv, (char*const*)envp);
|
execve(filename, (char *const *) argv, env->unixoid_environment_block);
|
||||||
else
|
else
|
||||||
execvp(filename, (char *const *) argv);
|
execvp(filename, (char *const *) argv);
|
||||||
|
|
||||||
@ -3692,6 +3715,162 @@ tor_get_exit_code(const process_handle_t *process_handle,
|
|||||||
return PROCESS_EXIT_EXITED;
|
return PROCESS_EXIT_EXITED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return non-zero iff getenv would consider <b>s1</b> and <b>s2</b>
|
||||||
|
* to have the same name as strings in a process's environment. */
|
||||||
|
int
|
||||||
|
environment_variable_names_equal(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
size_t s1_name_len = strcspn(s1, "=");
|
||||||
|
size_t s2_name_len = strcspn(s2, "=");
|
||||||
|
|
||||||
|
return (s1_name_len == s2_name_len &&
|
||||||
|
tor_memeq(s1, s2, s1_name_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Free <b>env</b> (assuming it was produced by
|
||||||
|
* process_environment_make). */
|
||||||
|
void
|
||||||
|
process_environment_free(process_environment_t *env)
|
||||||
|
{
|
||||||
|
if (env == NULL) return;
|
||||||
|
|
||||||
|
/* As both an optimization hack to reduce consing on Unixoid systems
|
||||||
|
* and a nice way to ensure that some otherwise-Windows-specific
|
||||||
|
* code will always get tested before changes to it get merged, the
|
||||||
|
* strings which env->unixoid_environment_block points to are packed
|
||||||
|
* into env->windows_environment_block. */
|
||||||
|
tor_free(env->unixoid_environment_block);
|
||||||
|
tor_free(env->windows_environment_block);
|
||||||
|
|
||||||
|
tor_free(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Make a process_environment_t containing the environment variables
|
||||||
|
* specified in <b>env_vars</b> (as C strings of the form
|
||||||
|
* "NAME=VALUE"). */
|
||||||
|
process_environment_t *
|
||||||
|
process_environment_make(struct smartlist_t *env_vars)
|
||||||
|
{
|
||||||
|
process_environment_t *env = tor_malloc_zero(sizeof(process_environment_t));
|
||||||
|
size_t n_env_vars = smartlist_len(env_vars);
|
||||||
|
size_t i;
|
||||||
|
size_t total_env_length;
|
||||||
|
smartlist_t *env_vars_sorted;
|
||||||
|
|
||||||
|
tor_assert(n_env_vars + 1 != 0);
|
||||||
|
env->unixoid_environment_block = tor_calloc(n_env_vars + 1, sizeof(char *));
|
||||||
|
/* env->unixoid_environment_block is already NULL-terminated,
|
||||||
|
* because we assume that NULL == 0 (and check that during compilation). */
|
||||||
|
|
||||||
|
total_env_length = 1; /* terminating NUL of terminating empty string */
|
||||||
|
for (i = 0; i < n_env_vars; ++i) {
|
||||||
|
const char *s = smartlist_get(env_vars, i);
|
||||||
|
size_t slen = strlen(s);
|
||||||
|
|
||||||
|
tor_assert(slen + 1 != 0);
|
||||||
|
tor_assert(slen + 1 < SIZE_MAX - total_env_length);
|
||||||
|
total_env_length += slen + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->windows_environment_block = tor_malloc_zero(total_env_length);
|
||||||
|
/* env->windows_environment_block is already
|
||||||
|
* (NUL-terminated-empty-string)-terminated. */
|
||||||
|
|
||||||
|
/* Some versions of Windows supposedly require that environment
|
||||||
|
* blocks be sorted. Or maybe some Windows programs (or their
|
||||||
|
* runtime libraries) fail to look up strings in non-sorted
|
||||||
|
* environment blocks.
|
||||||
|
*
|
||||||
|
* Also, sorting strings makes it easy to find duplicate environment
|
||||||
|
* variables and environment-variable strings without an '=' on all
|
||||||
|
* OSes, and they can cause badness. Let's complain about those. */
|
||||||
|
env_vars_sorted = smartlist_new();
|
||||||
|
smartlist_add_all(env_vars_sorted, env_vars);
|
||||||
|
smartlist_sort_strings(env_vars_sorted);
|
||||||
|
|
||||||
|
/* Now copy the strings into the environment blocks. */
|
||||||
|
{
|
||||||
|
char *cp = env->windows_environment_block;
|
||||||
|
const char *prev_env_var = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < n_env_vars; ++i) {
|
||||||
|
const char *s = smartlist_get(env_vars_sorted, i);
|
||||||
|
size_t slen = strlen(s);
|
||||||
|
size_t s_name_len = strcspn(s, "=");
|
||||||
|
|
||||||
|
if (s_name_len == slen) {
|
||||||
|
log_warn(LD_GENERAL,
|
||||||
|
"Preparing an environment containing a variable "
|
||||||
|
"without a value: %s",
|
||||||
|
s);
|
||||||
|
}
|
||||||
|
if (prev_env_var != NULL &&
|
||||||
|
environment_variable_names_equal(s, prev_env_var)) {
|
||||||
|
log_warn(LD_GENERAL,
|
||||||
|
"Preparing an environment containing two variables "
|
||||||
|
"with the same name: %s and %s",
|
||||||
|
prev_env_var, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_env_var = s;
|
||||||
|
|
||||||
|
/* Actually copy the string into the environment. */
|
||||||
|
memcpy(cp, s, slen+1);
|
||||||
|
env->unixoid_environment_block[i] = cp;
|
||||||
|
cp += slen+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tor_assert(cp == env->windows_environment_block + total_env_length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a newly allocated smartlist containing every variable in
|
||||||
|
* this process's environment, as a NUL-terminated string of the form
|
||||||
|
* "NAME=VALUE". Note that on some/many/most/all OSes, the parent
|
||||||
|
* process can put strings not of that form in our environment;
|
||||||
|
* callers should try to not get crashed by that.
|
||||||
|
*
|
||||||
|
* The returned strings are heap-allocated, and must be freed by the
|
||||||
|
* caller. */
|
||||||
|
struct smartlist_t *
|
||||||
|
get_current_process_environment_variables(void)
|
||||||
|
{
|
||||||
|
smartlist_t *sl = smartlist_new();
|
||||||
|
|
||||||
|
char **environ_tmp; /* Not const char ** ? Really? */
|
||||||
|
for (environ_tmp = environ; *environ_tmp; ++environ_tmp) {
|
||||||
|
smartlist_add(sl, tor_strdup(*environ_tmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For each string s in <b>env_vars</b> such that
|
||||||
|
* environment_variable_names_equal(s, <b>new_var</b>), remove it; if
|
||||||
|
* <b>free_p</b> is non-zero, call <b>free_old</b>(s). If
|
||||||
|
* <b>new_var</b> contains '=', insert it into <b>env_vars</b>. */
|
||||||
|
void
|
||||||
|
set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
|
||||||
|
const char *new_var,
|
||||||
|
void (*free_old)(void*),
|
||||||
|
int free_p)
|
||||||
|
{
|
||||||
|
SMARTLIST_FOREACH_BEGIN(env_vars, const char *, s) {
|
||||||
|
if (environment_variable_names_equal(s, new_var)) {
|
||||||
|
SMARTLIST_DEL_CURRENT(env_vars, s);
|
||||||
|
if (free_p) {
|
||||||
|
free_old((void *)s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} SMARTLIST_FOREACH_END(s);
|
||||||
|
|
||||||
|
if (strchr(new_var, '=') != NULL) {
|
||||||
|
smartlist_add(env_vars, (void *)new_var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If
|
/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If
|
||||||
* <b>hProcess</b> is NULL, the function will return immediately if there is
|
* <b>hProcess</b> is NULL, the function will return immediately if there is
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
void *_tor_malloc(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
|
void *_tor_malloc(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
|
||||||
void *_tor_malloc_zero(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
|
void *_tor_malloc_zero(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
|
||||||
void *_tor_malloc_roundup(size_t *size DMALLOC_PARAMS) ATTR_MALLOC;
|
void *_tor_malloc_roundup(size_t *size DMALLOC_PARAMS) ATTR_MALLOC;
|
||||||
|
void *_tor_calloc(size_t nmemb, size_t size DMALLOC_PARAMS) ATTR_MALLOC;
|
||||||
void *_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS);
|
void *_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS);
|
||||||
char *_tor_strdup(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1));
|
char *_tor_strdup(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1));
|
||||||
char *_tor_strndup(const char *s, size_t n DMALLOC_PARAMS)
|
char *_tor_strndup(const char *s, size_t n DMALLOC_PARAMS)
|
||||||
@ -107,6 +108,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
|
|||||||
|
|
||||||
#define tor_malloc(size) _tor_malloc(size DMALLOC_ARGS)
|
#define tor_malloc(size) _tor_malloc(size DMALLOC_ARGS)
|
||||||
#define tor_malloc_zero(size) _tor_malloc_zero(size DMALLOC_ARGS)
|
#define tor_malloc_zero(size) _tor_malloc_zero(size DMALLOC_ARGS)
|
||||||
|
#define tor_calloc(nmemb,size) _tor_calloc(nmemb, size DMALLOC_ARGS)
|
||||||
#define tor_malloc_roundup(szp) _tor_malloc_roundup(szp DMALLOC_ARGS)
|
#define tor_malloc_roundup(szp) _tor_malloc_roundup(szp DMALLOC_ARGS)
|
||||||
#define tor_realloc(ptr, size) _tor_realloc(ptr, size DMALLOC_ARGS)
|
#define tor_realloc(ptr, size) _tor_realloc(ptr, size DMALLOC_ARGS)
|
||||||
#define tor_strdup(s) _tor_strdup(s DMALLOC_ARGS)
|
#define tor_strdup(s) _tor_strdup(s DMALLOC_ARGS)
|
||||||
@ -363,12 +365,9 @@ void tor_check_port_forwarding(const char *filename,
|
|||||||
int dir_port, int or_port, time_t now);
|
int dir_port, int or_port, time_t now);
|
||||||
|
|
||||||
typedef struct process_handle_t process_handle_t;
|
typedef struct process_handle_t process_handle_t;
|
||||||
|
typedef struct process_environment_t process_environment_t;
|
||||||
int tor_spawn_background(const char *const filename, const char **argv,
|
int tor_spawn_background(const char *const filename, const char **argv,
|
||||||
#ifdef _WIN32
|
process_environment_t *env,
|
||||||
LPVOID envp,
|
|
||||||
#else
|
|
||||||
const char **envp,
|
|
||||||
#endif
|
|
||||||
process_handle_t **process_handle_out);
|
process_handle_t **process_handle_out);
|
||||||
|
|
||||||
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
|
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
|
||||||
@ -377,6 +376,27 @@ int tor_spawn_background(const char *const filename, const char **argv,
|
|||||||
HANDLE load_windows_system_library(const TCHAR *library_name);
|
HANDLE load_windows_system_library(const TCHAR *library_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int environment_variable_names_equal(const char *s1, const char *s2);
|
||||||
|
|
||||||
|
struct process_environment_t {
|
||||||
|
/** A pointer to a sorted empty-string-terminated sequence of
|
||||||
|
* NUL-terminated strings of the form "NAME=VALUE". */
|
||||||
|
char *windows_environment_block;
|
||||||
|
/** A pointer to a NULL-terminated array of pointers to
|
||||||
|
* NUL-terminated strings of the form "NAME=VALUE". */
|
||||||
|
char **unixoid_environment_block;
|
||||||
|
};
|
||||||
|
|
||||||
|
process_environment_t *process_environment_make(struct smartlist_t *env_vars);
|
||||||
|
void process_environment_free(process_environment_t *env);
|
||||||
|
|
||||||
|
struct smartlist_t *get_current_process_environment_variables(void);
|
||||||
|
|
||||||
|
void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
|
||||||
|
const char *new_var,
|
||||||
|
void (*free_old)(void*),
|
||||||
|
int free_p);
|
||||||
|
|
||||||
/* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be
|
/* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be
|
||||||
* 0 because tor_check_port_forwarding depends on this being the initial
|
* 0 because tor_check_port_forwarding depends on this being the initial
|
||||||
* statue of the static instance of process_handle_t */
|
* statue of the static instance of process_handle_t */
|
||||||
|
@ -91,13 +91,8 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
static process_environment_t *
|
||||||
static void set_managed_proxy_environment(LPVOID *envp,
|
create_managed_proxy_environment(const managed_proxy_t *mp);
|
||||||
const managed_proxy_t *mp);
|
|
||||||
#else
|
|
||||||
static void set_managed_proxy_environment(char ***envp,
|
|
||||||
const managed_proxy_t *mp);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
|
static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
|
||||||
|
|
||||||
@ -287,35 +282,23 @@ launch_managed_proxy(managed_proxy_t *mp)
|
|||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
process_environment_t *env = create_managed_proxy_environment(mp);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
LPVOID envp=NULL;
|
|
||||||
|
|
||||||
set_managed_proxy_environment(&envp, mp);
|
|
||||||
tor_assert(envp);
|
|
||||||
|
|
||||||
/* Passing NULL as lpApplicationName makes Windows search for the .exe */
|
/* Passing NULL as lpApplicationName makes Windows search for the .exe */
|
||||||
retval = tor_spawn_background(NULL, (const char **)mp->argv, envp,
|
retval = tor_spawn_background(NULL,
|
||||||
|
(const char **)mp->argv,
|
||||||
|
env,
|
||||||
&mp->process_handle);
|
&mp->process_handle);
|
||||||
|
|
||||||
tor_free(envp);
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
retval = tor_spawn_background(mp->argv[0],
|
||||||
char **envp=NULL;
|
(const char **)mp->argv,
|
||||||
|
env,
|
||||||
/* prepare the environment variables for the managed proxy */
|
&mp->process_handle);
|
||||||
set_managed_proxy_environment(&envp, mp);
|
|
||||||
tor_assert(envp);
|
|
||||||
|
|
||||||
retval = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
|
|
||||||
(const char **)envp, &mp->process_handle);
|
|
||||||
|
|
||||||
/* free the memory allocated by set_managed_proxy_environment(). */
|
|
||||||
free_execve_args(envp);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
process_environment_free(env);
|
||||||
|
|
||||||
if (retval == PROCESS_STATUS_ERROR) {
|
if (retval == PROCESS_STATUS_ERROR) {
|
||||||
log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
|
log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
|
||||||
mp->argv[0]);
|
mp->argv[0]);
|
||||||
@ -969,170 +952,80 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
|
|||||||
return bindaddr_result;
|
return bindaddr_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
/** Return a newly allocated process_environment_t * for <b>mp</b>'s
|
||||||
|
* process. */
|
||||||
/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>.
|
static process_environment_t *
|
||||||
* <b>envp</b> is allocated on the heap and should be freed by the
|
create_managed_proxy_environment(const managed_proxy_t *mp)
|
||||||
* caller after its use. */
|
|
||||||
static void
|
|
||||||
set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp)
|
|
||||||
{
|
{
|
||||||
const or_options_t *options = get_options();
|
const or_options_t *options = get_options();
|
||||||
|
|
||||||
char *tmp=NULL;
|
/* Environment variables to be added to or set in mp's environment. */
|
||||||
char *state_tmp=NULL;
|
|
||||||
char *state_env=NULL;
|
|
||||||
char *transports_to_launch=NULL;
|
|
||||||
char *transports_env=NULL;
|
|
||||||
char *bindaddr_tmp=NULL;
|
|
||||||
char *bindaddr_env=NULL;
|
|
||||||
char *orport_env=NULL;
|
|
||||||
|
|
||||||
char version_env[31]; /* XXX temp */
|
|
||||||
char extended_env[43]; /* XXX temp */
|
|
||||||
|
|
||||||
int env_size = 0;
|
|
||||||
|
|
||||||
/* A smartlist carrying all the env. variables that the managed
|
|
||||||
proxy should inherit. */
|
|
||||||
smartlist_t *envs = smartlist_new();
|
smartlist_t *envs = smartlist_new();
|
||||||
|
/* XXXX The next time someone touches this code, shorten the name of
|
||||||
|
* set_environment_variable_in_smartlist, add a
|
||||||
|
* set_env_var_in_smartlist_asprintf function, and get rid of the
|
||||||
|
* silly extra envs smartlist. */
|
||||||
|
|
||||||
/* Copy the whole environment of the Tor process.
|
/* The final environment to be passed to mp. */
|
||||||
It should also copy PATH and HOME of the Tor process.*/
|
smartlist_t *merged_env_vars = get_current_process_environment_variables();
|
||||||
char **environ_tmp = environ;
|
|
||||||
while (*environ_tmp)
|
|
||||||
smartlist_add(envs, *environ_tmp++);
|
|
||||||
|
|
||||||
/* Create the TOR_PT_* environment variables. */
|
process_environment_t *env;
|
||||||
state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */
|
|
||||||
tor_asprintf(&state_env, "TOR_PT_STATE_LOCATION=%s", state_tmp);
|
|
||||||
|
|
||||||
strcpy(version_env, "TOR_PT_MANAGED_TRANSPORT_VER=1");
|
{
|
||||||
|
char *state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */
|
||||||
|
smartlist_add_asprintf(envs, "TOR_PT_STATE_LOCATION=%s", state_tmp);
|
||||||
|
tor_free(state_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
transports_to_launch =
|
smartlist_add(envs, tor_strdup("TOR_PT_MANAGED_TRANSPORT_VER=1"));
|
||||||
|
|
||||||
|
{
|
||||||
|
char *transports_to_launch =
|
||||||
smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
|
smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
|
||||||
|
|
||||||
tor_asprintf(&transports_env,
|
smartlist_add_asprintf(envs,
|
||||||
mp->is_server ?
|
mp->is_server ?
|
||||||
"TOR_PT_SERVER_TRANSPORTS=%s" : "TOR_PT_CLIENT_TRANSPORTS=%s",
|
"TOR_PT_SERVER_TRANSPORTS=%s" :
|
||||||
|
"TOR_PT_CLIENT_TRANSPORTS=%s",
|
||||||
transports_to_launch);
|
transports_to_launch);
|
||||||
|
|
||||||
smartlist_add(envs, state_env);
|
tor_free(transports_to_launch);
|
||||||
smartlist_add(envs, version_env);
|
}
|
||||||
smartlist_add(envs, transports_env);
|
|
||||||
|
|
||||||
if (mp->is_server) {
|
if (mp->is_server) {
|
||||||
tor_asprintf(&orport_env, "TOR_PT_ORPORT=127.0.0.1:%s",
|
smartlist_add_asprintf(envs, "TOR_PT_ORPORT=127.0.0.1:%s",
|
||||||
options->ORPort->value);
|
options->ORPort->value);
|
||||||
|
|
||||||
bindaddr_tmp = get_bindaddr_for_server_proxy(mp);
|
{
|
||||||
tor_asprintf(&bindaddr_env, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp);
|
char *bindaddr_tmp = get_bindaddr_for_server_proxy(mp);
|
||||||
|
smartlist_add_asprintf(envs, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp);
|
||||||
strlcpy(extended_env, "TOR_PT_EXTENDED_SERVER_PORT=",
|
tor_free(bindaddr_tmp);
|
||||||
sizeof(extended_env));
|
|
||||||
|
|
||||||
smartlist_add(envs, orport_env);
|
|
||||||
smartlist_add(envs, extended_env);
|
|
||||||
smartlist_add(envs, bindaddr_env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* It seems like some versions of Windows need a sorted lpEnvironment
|
/* XXX023 Remove the '=' here once versions of obfsproxy which
|
||||||
block. */
|
* assert that this env var exists are sufficiently dead.
|
||||||
smartlist_sort_strings(envs);
|
*
|
||||||
|
* (If we remove this line entirely, some joker will stick this
|
||||||
|
* variable in Tor's environment and crash PTs that try to parse
|
||||||
|
* it even when not run in server mode.) */
|
||||||
|
smartlist_add(envs, tor_strdup("TOR_PT_EXTENDED_SERVER_PORT="));
|
||||||
|
}
|
||||||
|
|
||||||
/* An environment block consists of a null-terminated block of
|
SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {
|
||||||
null-terminated strings: */
|
set_environment_variable_in_smartlist(merged_env_vars, env_var,
|
||||||
|
_tor_free, 1);
|
||||||
|
} SMARTLIST_FOREACH_END(env_var);
|
||||||
|
|
||||||
/* Calculate the block's size. */
|
env = process_environment_make(merged_env_vars);
|
||||||
SMARTLIST_FOREACH(envs, const char *, s,
|
|
||||||
env_size += strlen(s) + 1);
|
|
||||||
env_size += 1; /* space for last NUL */
|
|
||||||
|
|
||||||
*envp = tor_malloc(env_size);
|
|
||||||
tmp = *envp;
|
|
||||||
|
|
||||||
/* Create the block. */
|
|
||||||
SMARTLIST_FOREACH_BEGIN(envs, const char *, s) {
|
|
||||||
memcpy(tmp, s, strlen(s)); /* copy the env. variable string */
|
|
||||||
tmp += strlen(s);
|
|
||||||
memset(tmp, '\0', 1); /* append NUL at the end of the string */
|
|
||||||
tmp += 1;
|
|
||||||
} SMARTLIST_FOREACH_END(s);
|
|
||||||
memset(tmp, '\0', 1); /* last NUL */
|
|
||||||
|
|
||||||
/* Finally, free the whole mess. */
|
|
||||||
tor_free(state_tmp);
|
|
||||||
tor_free(state_env);
|
|
||||||
tor_free(transports_to_launch);
|
|
||||||
tor_free(transports_env);
|
|
||||||
tor_free(bindaddr_tmp);
|
|
||||||
tor_free(bindaddr_env);
|
|
||||||
tor_free(orport_env);
|
|
||||||
|
|
||||||
smartlist_free(envs);
|
smartlist_free(envs);
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(merged_env_vars, void *, x, tor_free(x));
|
||||||
|
smartlist_free(merged_env_vars);
|
||||||
|
|
||||||
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* _WIN32 */
|
|
||||||
|
|
||||||
/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>.
|
|
||||||
* <b>envp</b> is allocated on the heap and should be freed by the
|
|
||||||
* caller after its use. */
|
|
||||||
static void
|
|
||||||
set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
|
|
||||||
{
|
|
||||||
const or_options_t *options = get_options();
|
|
||||||
char **tmp=NULL;
|
|
||||||
char *state_loc=NULL;
|
|
||||||
char *transports_to_launch=NULL;
|
|
||||||
char *bindaddr=NULL;
|
|
||||||
int environ_size=0;
|
|
||||||
char **environ_tmp = get_environment();
|
|
||||||
char **environ_save = environ_tmp;
|
|
||||||
|
|
||||||
int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT;
|
|
||||||
|
|
||||||
while (*environ_tmp) {
|
|
||||||
environ_size++;
|
|
||||||
environ_tmp++;
|
|
||||||
}
|
|
||||||
environ_tmp = environ_save;
|
|
||||||
|
|
||||||
/* allocate enough space for our env. vars and a NULL pointer */
|
|
||||||
*envp = tor_malloc(sizeof(char*)*(environ_size+n_envs+1));
|
|
||||||
tmp = *envp;
|
|
||||||
|
|
||||||
state_loc = get_datadir_fname("pt_state/"); /* XXX temp */
|
|
||||||
transports_to_launch =
|
|
||||||
smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
|
|
||||||
|
|
||||||
while (*environ_tmp) {
|
|
||||||
*tmp = tor_strdup(*environ_tmp);
|
|
||||||
tmp++, environ_tmp++;
|
|
||||||
}
|
|
||||||
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc);
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */
|
|
||||||
if (mp->is_server) {
|
|
||||||
bindaddr = get_bindaddr_for_server_proxy(mp);
|
|
||||||
|
|
||||||
/* XXX temp */
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d",
|
|
||||||
router_get_advertised_or_port(options));
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_EXTENDED_SERVER_PORT=");
|
|
||||||
} else {
|
|
||||||
tor_asprintf(tmp++, "TOR_PT_CLIENT_TRANSPORTS=%s", transports_to_launch);
|
|
||||||
}
|
|
||||||
*tmp = NULL;
|
|
||||||
|
|
||||||
tor_free(state_loc);
|
|
||||||
tor_free(transports_to_launch);
|
|
||||||
tor_free(bindaddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _WIN32 */
|
|
||||||
|
|
||||||
/** Create and return a new managed proxy for <b>transport</b> using
|
/** Create and return a new managed proxy for <b>transport</b> using
|
||||||
* <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server
|
* <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server
|
||||||
* managed proxy. */
|
* managed proxy. */
|
||||||
|
@ -1856,6 +1856,245 @@ test_util_eat_whitespace(void *ptr)
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return a newly allocated smartlist containing the lines of text in
|
||||||
|
* <b>lines</b>. The returned strings are heap-allocated, and must be
|
||||||
|
* freed by the caller.
|
||||||
|
*
|
||||||
|
* XXXX? Move to container.[hc] ? */
|
||||||
|
static smartlist_t *
|
||||||
|
smartlist_new_from_text_lines(const char *lines)
|
||||||
|
{
|
||||||
|
smartlist_t *sl = smartlist_new();
|
||||||
|
char *last_line;
|
||||||
|
|
||||||
|
smartlist_split_string(sl, lines, "\n", 0, 0);
|
||||||
|
|
||||||
|
last_line = smartlist_pop_last(sl);
|
||||||
|
if (last_line != NULL && *last_line != '\0') {
|
||||||
|
smartlist_add(sl, last_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test smartlist_new_from_text_lines */
|
||||||
|
static void
|
||||||
|
test_util_sl_new_from_text_lines(void *ptr)
|
||||||
|
{
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
{ /* Normal usage */
|
||||||
|
smartlist_t *sl = smartlist_new_from_text_lines("foo\nbar\nbaz\n");
|
||||||
|
int sl_len = smartlist_len(sl);
|
||||||
|
|
||||||
|
tt_want_int_op(sl_len, ==, 3);
|
||||||
|
|
||||||
|
if (sl_len > 0) tt_want_str_op(smartlist_get(sl, 0), ==, "foo");
|
||||||
|
if (sl_len > 1) tt_want_str_op(smartlist_get(sl, 1), ==, "bar");
|
||||||
|
if (sl_len > 2) tt_want_str_op(smartlist_get(sl, 2), ==, "baz");
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(sl, void *, x, tor_free(x));
|
||||||
|
smartlist_free(sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* No final newline */
|
||||||
|
smartlist_t *sl = smartlist_new_from_text_lines("foo\nbar\nbaz");
|
||||||
|
int sl_len = smartlist_len(sl);
|
||||||
|
|
||||||
|
tt_want_int_op(sl_len, ==, 3);
|
||||||
|
|
||||||
|
if (sl_len > 0) tt_want_str_op(smartlist_get(sl, 0), ==, "foo");
|
||||||
|
if (sl_len > 1) tt_want_str_op(smartlist_get(sl, 1), ==, "bar");
|
||||||
|
if (sl_len > 2) tt_want_str_op(smartlist_get(sl, 2), ==, "baz");
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(sl, void *, x, tor_free(x));
|
||||||
|
smartlist_free(sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* No newlines */
|
||||||
|
smartlist_t *sl = smartlist_new_from_text_lines("foo");
|
||||||
|
int sl_len = smartlist_len(sl);
|
||||||
|
|
||||||
|
tt_want_int_op(sl_len, ==, 1);
|
||||||
|
|
||||||
|
if (sl_len > 0) tt_want_str_op(smartlist_get(sl, 0), ==, "foo");
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(sl, void *, x, tor_free(x));
|
||||||
|
smartlist_free(sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* No text at all */
|
||||||
|
smartlist_t *sl = smartlist_new_from_text_lines("");
|
||||||
|
int sl_len = smartlist_len(sl);
|
||||||
|
|
||||||
|
tt_want_int_op(sl_len, ==, 0);
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(sl, void *, x, tor_free(x));
|
||||||
|
smartlist_free(sl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test process_environment_make */
|
||||||
|
static void
|
||||||
|
test_util_make_environment(void *ptr)
|
||||||
|
{
|
||||||
|
const char *env_vars_string =
|
||||||
|
"PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/bin\n"
|
||||||
|
"HOME=/home/foozer\n";
|
||||||
|
const char expected_windows_env_block[] =
|
||||||
|
"HOME=/home/foozer\000"
|
||||||
|
"PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/bin\000"
|
||||||
|
"\000";
|
||||||
|
size_t expected_windows_env_block_len = sizeof(expected_windows_env_block);
|
||||||
|
|
||||||
|
smartlist_t *env_vars = smartlist_new_from_text_lines(env_vars_string);
|
||||||
|
smartlist_t *env_vars_sorted = smartlist_new();
|
||||||
|
smartlist_t *env_vars_in_unixoid_env_block_sorted = smartlist_new();
|
||||||
|
|
||||||
|
process_environment_t *env;
|
||||||
|
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
env = process_environment_make(env_vars);
|
||||||
|
|
||||||
|
/* Check that the Windows environment block is correct. */
|
||||||
|
tt_want(tor_memeq(expected_windows_env_block, env->windows_environment_block,
|
||||||
|
expected_windows_env_block_len));
|
||||||
|
|
||||||
|
/* Now for the Unixoid environment block. We don't care which order
|
||||||
|
* these environment variables are in, so we sort both lists first. */
|
||||||
|
|
||||||
|
smartlist_add_all(env_vars_sorted, env_vars);
|
||||||
|
|
||||||
|
{
|
||||||
|
char **v;
|
||||||
|
for (v = env->unixoid_environment_block; *v; ++v) {
|
||||||
|
smartlist_add(env_vars_in_unixoid_env_block_sorted, *v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
smartlist_sort_strings(env_vars_sorted);
|
||||||
|
smartlist_sort_strings(env_vars_in_unixoid_env_block_sorted);
|
||||||
|
|
||||||
|
tt_want_int_op(smartlist_len(env_vars_sorted), ==,
|
||||||
|
smartlist_len(env_vars_in_unixoid_env_block_sorted));
|
||||||
|
{
|
||||||
|
int len = smartlist_len(env_vars_sorted);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (smartlist_len(env_vars_in_unixoid_env_block_sorted) < len) {
|
||||||
|
len = smartlist_len(env_vars_in_unixoid_env_block_sorted);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
tt_want_str_op(smartlist_get(env_vars_sorted, i), ==,
|
||||||
|
smartlist_get(env_vars_in_unixoid_env_block_sorted, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up. */
|
||||||
|
smartlist_free(env_vars_in_unixoid_env_block_sorted);
|
||||||
|
smartlist_free(env_vars_sorted);
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(env_vars, char *, x, tor_free(x));
|
||||||
|
smartlist_free(env_vars);
|
||||||
|
|
||||||
|
process_environment_free(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test set_environment_variable_in_smartlist */
|
||||||
|
static void
|
||||||
|
test_util_set_env_var_in_sl(void *ptr)
|
||||||
|
{
|
||||||
|
/* The environment variables in these strings are in arbitrary
|
||||||
|
* order; we sort the resulting lists before comparing them.
|
||||||
|
*
|
||||||
|
* (They *will not* end up in the order shown in
|
||||||
|
* expected_resulting_env_vars_string.) */
|
||||||
|
|
||||||
|
const char *base_env_vars_string =
|
||||||
|
"PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/bin\n"
|
||||||
|
"HOME=/home/foozer\n"
|
||||||
|
"TERM=xterm\n"
|
||||||
|
"SHELL=/bin/ksh\n"
|
||||||
|
"USER=foozer\n"
|
||||||
|
"LOGNAME=foozer\n"
|
||||||
|
"USERNAME=foozer\n"
|
||||||
|
"LANG=en_US.utf8\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
const char *new_env_vars_string =
|
||||||
|
"TERM=putty\n"
|
||||||
|
"DISPLAY=:18.0\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
const char *expected_resulting_env_vars_string =
|
||||||
|
"PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/bin\n"
|
||||||
|
"HOME=/home/foozer\n"
|
||||||
|
"TERM=putty\n"
|
||||||
|
"SHELL=/bin/ksh\n"
|
||||||
|
"USER=foozer\n"
|
||||||
|
"LOGNAME=foozer\n"
|
||||||
|
"USERNAME=foozer\n"
|
||||||
|
"LANG=en_US.utf8\n"
|
||||||
|
"DISPLAY=:18.0\n"
|
||||||
|
;
|
||||||
|
|
||||||
|
smartlist_t *merged_env_vars =
|
||||||
|
smartlist_new_from_text_lines(base_env_vars_string);
|
||||||
|
smartlist_t *new_env_vars =
|
||||||
|
smartlist_new_from_text_lines(new_env_vars_string);
|
||||||
|
smartlist_t *expected_resulting_env_vars =
|
||||||
|
smartlist_new_from_text_lines(expected_resulting_env_vars_string);
|
||||||
|
|
||||||
|
/* Elements of merged_env_vars are heap-allocated, and must be
|
||||||
|
* freed. Some of them are (or should) be freed by
|
||||||
|
* set_environment_variable_in_smartlist.
|
||||||
|
*
|
||||||
|
* Elements of new_env_vars are heap-allocated, but are copied into
|
||||||
|
* merged_env_vars, so they are not freed separately at the end of
|
||||||
|
* the function.
|
||||||
|
*
|
||||||
|
* Elements of expected_resulting_env_vars are heap-allocated, and
|
||||||
|
* must be freed. */
|
||||||
|
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(new_env_vars, char *, env_var,
|
||||||
|
set_environment_variable_in_smartlist(merged_env_vars,
|
||||||
|
env_var,
|
||||||
|
_tor_free,
|
||||||
|
1));
|
||||||
|
|
||||||
|
smartlist_sort_strings(merged_env_vars);
|
||||||
|
smartlist_sort_strings(expected_resulting_env_vars);
|
||||||
|
|
||||||
|
tt_want_int_op(smartlist_len(merged_env_vars), ==,
|
||||||
|
smartlist_len(expected_resulting_env_vars));
|
||||||
|
{
|
||||||
|
int len = smartlist_len(merged_env_vars);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (smartlist_len(expected_resulting_env_vars) < len) {
|
||||||
|
len = smartlist_len(expected_resulting_env_vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
tt_want_str_op(smartlist_get(merged_env_vars, i), ==,
|
||||||
|
smartlist_get(expected_resulting_env_vars, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up. */
|
||||||
|
SMARTLIST_FOREACH(merged_env_vars, char *, x, tor_free(x));
|
||||||
|
smartlist_free(merged_env_vars);
|
||||||
|
|
||||||
|
smartlist_free(new_env_vars);
|
||||||
|
|
||||||
|
SMARTLIST_FOREACH(expected_resulting_env_vars, char *, x, tor_free(x));
|
||||||
|
smartlist_free(expected_resulting_env_vars);
|
||||||
|
}
|
||||||
|
|
||||||
#define UTIL_LEGACY(name) \
|
#define UTIL_LEGACY(name) \
|
||||||
{ #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
|
{ #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
|
||||||
|
|
||||||
@ -1895,6 +2134,9 @@ struct testcase_t util_tests[] = {
|
|||||||
UTIL_TEST(split_lines, 0),
|
UTIL_TEST(split_lines, 0),
|
||||||
UTIL_TEST(n_bits_set, 0),
|
UTIL_TEST(n_bits_set, 0),
|
||||||
UTIL_TEST(eat_whitespace, 0),
|
UTIL_TEST(eat_whitespace, 0),
|
||||||
|
UTIL_TEST(sl_new_from_text_lines, 0),
|
||||||
|
UTIL_TEST(make_environment, 0),
|
||||||
|
UTIL_TEST(set_env_var_in_sl, 0),
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user