Merge branch 'bug8929_rebase_2'

This commit is contained in:
Nick Mathewson 2013-07-18 08:45:13 -04:00
commit b551988ef4
12 changed files with 309 additions and 20 deletions

4
changes/bug8929 Normal file
View File

@ -0,0 +1,4 @@
o Minor features:
- Add a new torrc option "ServerTransportOptions" that allows
bridge operators to pass configuration parameters to their
pluggable transports. Resolves ticket 8929.

View File

@ -186,6 +186,11 @@ GENERAL OPTIONS
listening address of any pluggable transport proxy that tries to
launch __transport__.
**ServerTransportOptions** __transport__ __k=v__ __k=v__ ...::
When this option is set, Tor will pass the __k=v__ parameters to
any pluggable transport proxy that tries to launch __transport__. +
(Example: ServerTransportOptions obfs45 shared-secret=bridgepasswd cache=/var/lib/tor/cache)
**ConnLimit** __NUM__::
The minimum number of file descriptors that must be available to the Tor
process before it will start. Tor will ask the OS for as many file

View File

@ -1223,17 +1223,14 @@ escaped(const char *s)
return escaped_val_;
}
/** Escape every ";" or "\" character of <b>string</b>. Use
* <b>escape_char</b> as the character to use for escaping.
* The returned string is allocated on the heap and it's the
* responsibility of the caller to free it. */
/** Return a newly allocated string equal to <b>string</b>, except that every
* character in <b>chars_to_escape</b> is preceded by a backslash. */
char *
tor_escape_str_for_socks_arg(const char *string)
tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape)
{
char *new_string = NULL;
char *new_cp = NULL;
size_t length, new_length;
static const char *chars_to_escape = ";\\";
tor_assert(string);

View File

@ -231,7 +231,8 @@ int tor_digest256_is_zero(const char *digest);
char *esc_for_log(const char *string) ATTR_MALLOC;
const char *escaped(const char *string);
char *tor_escape_str_for_socks_arg(const char *string);
char *tor_escape_str_for_pt_args(const char *string,
const char *chars_to_escape);
struct smartlist_t;
int tor_vsscanf(const char *buf, const char *pattern, va_list ap) \

View File

@ -280,6 +280,7 @@ static config_var_t option_vars_[] = {
V(IPv6Exit, BOOL, "0"),
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(ServerTransportListenAddr, LINELIST, NULL),
V(ServerTransportOptions, LINELIST, NULL),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
@ -3147,6 +3148,19 @@ options_validate(or_options_t *old_options, or_options_t *options,
"ServerTransportListenAddr line will be ignored.");
}
for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
/** If get_options_from_transport_options_line() fails with
'transport' being NULL, it means that something went wrong
while parsing the ServerTransportOptions line. */
smartlist_t *options_sl =
get_options_from_transport_options_line(cl->value, NULL);
if (!options_sl)
REJECT("ServerTransportOptions did not parse. See logs for details.");
SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp));
smartlist_free(options_sl);
}
if (options->ConstrainedSockets) {
/* If the user wants to constrain socket buffer use, make sure the desired
* limit is between MIN|MAX_TCPSOCK_BUFFER in k increments. */
@ -4580,6 +4594,63 @@ get_bindaddr_from_transport_listen_line(const char *line,const char *transport)
return addrport;
}
/** Given a ServerTransportOptions <b>line</b>, return a smartlist
* with the options. Return NULL if the line was not well-formed.
*
* If <b>transport</b> is set, return NULL if the line is not
* referring to <b>transport</b>.
*
* The returned smartlist and its strings are allocated on the heap
* and it's the responsibility of the caller to free it. */
smartlist_t *
get_options_from_transport_options_line(const char *line,const char *transport)
{
smartlist_t *items = smartlist_new();
smartlist_t *options = smartlist_new();
const char *parsed_transport = NULL;
smartlist_split_string(items, line, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
if (smartlist_len(items) < 2) {
log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line.");
goto err;
}
parsed_transport = smartlist_get(items, 0);
/* If 'transport' is given, check if it matches the one on the line */
if (transport && strcmp(transport, parsed_transport))
goto err;
SMARTLIST_FOREACH_BEGIN(items, const char *, option) {
if (option_sl_idx == 0) /* skip the transport field (first field)*/
continue;
/* validate that it's a k=v value */
if (!string_is_key_value(LOG_WARN, option)) {
log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option));
goto err;
}
/* add it to the options smartlist */
smartlist_add(options, tor_strdup(option));
log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option));
} SMARTLIST_FOREACH_END(option);
goto done;
err:
SMARTLIST_FOREACH(options, char*, s, tor_free(s));
smartlist_free(options);
options = NULL;
done:
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
return options;
}
/** Given the name of a pluggable transport in <b>transport</b>, check
* the configuration file to see if the user has explicitly asked for
* it to listen on a specific port. Return a <address:port> string if
@ -4600,6 +4671,26 @@ get_transport_bindaddr_from_config(const char *transport)
return NULL;
}
/** Given the name of a pluggable transport in <b>transport</b>, check
* the configuration file to see if the user has asked us to pass any
* parameters to the pluggable transport. Return a smartlist
* containing the parameters, otherwise NULL. */
smartlist_t *
get_options_for_server_transport(const char *transport)
{
config_line_t *cl;
const or_options_t *options = get_options();
for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
smartlist_t *options_sl =
get_options_from_transport_options_line(cl->value, transport);
if (options_sl)
return options_sl;
}
return NULL;
}
/** Read the contents of a ServerTransportPlugin line from
* <b>line</b>. Return 0 if the line is well-formed, and -1 if it
* isn't.

View File

@ -112,6 +112,9 @@ typedef struct bridge_line_t {
void bridge_line_free(bridge_line_t *bridge_line);
bridge_line_t *parse_bridge_line(const char *line);
smartlist_t *get_options_from_transport_options_line(const char *line,
const char *transport);
smartlist_t *get_options_for_server_transport(const char *transport);
#endif

View File

@ -3500,6 +3500,9 @@ typedef struct {
/** List of TCP/IP addresses that transports should listen at. */
config_line_t *ServerTransportListenAddr;
/** List of options that must be passed to pluggable transports. */
config_line_t *ServerTransportOptions;
int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
* this explicit so we can change how we behave in the
* future. */

View File

@ -102,9 +102,6 @@ create_managed_proxy_environment(const managed_proxy_t *mp);
static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
static void managed_proxy_destroy(managed_proxy_t *mp,
int also_terminate_process);
static void handle_finished_proxy(managed_proxy_t *mp);
static void configure_proxy(managed_proxy_t *mp);
@ -694,7 +691,7 @@ register_proxy(const managed_proxy_t *mp)
}
/** Free memory allocated by managed proxy <b>mp</b>. */
static void
STATIC void
managed_proxy_destroy(managed_proxy_t *mp,
int also_terminate_process)
{
@ -1100,6 +1097,50 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
return r;
}
/** Return a newly allocated string that tor should place in
* TOR_PT_SERVER_TRANSPORT_OPTIONS while configuring the server
* manged proxy in <b>mp</b>. Return NULL if no such options are found. */
STATIC char *
get_transport_options_for_server_proxy(const managed_proxy_t *mp)
{
char *options_string = NULL;
smartlist_t *string_sl = smartlist_new();
tor_assert(mp->is_server);
/** Loop over the transports of the proxy. If we have options for
any of them, format them appropriately and place them in our
smartlist. Finally, join our smartlist to get the final
string. */
SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) {
smartlist_t *options_tmp_sl = NULL;
options_tmp_sl = get_options_for_server_transport(transport);
if (!options_tmp_sl)
continue;
/** Loop over the options of this transport, escape them, and
place them in the smartlist. */
SMARTLIST_FOREACH_BEGIN(options_tmp_sl, const char *, options) {
char *escaped_opts = tor_escape_str_for_pt_args(options, ":;\\");
smartlist_add_asprintf(string_sl, "%s:%s",
transport, escaped_opts);
tor_free(escaped_opts);
} SMARTLIST_FOREACH_END(options);
SMARTLIST_FOREACH(options_tmp_sl, char *, c, tor_free(c));
smartlist_free(options_tmp_sl);
} SMARTLIST_FOREACH_END(transport);
if (smartlist_len(string_sl)) {
options_string = smartlist_join_strings(string_sl, ";", 0, NULL);
}
SMARTLIST_FOREACH(string_sl, char *, t, tor_free(t));
smartlist_free(string_sl);
return options_string;
}
/** Return the string that tor should place in TOR_PT_SERVER_BINDADDR
* while configuring the server managed proxy in <b>mp</b>. The
* string is stored in the heap, and it's the the responsibility of
@ -1181,6 +1222,16 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
tor_free(bindaddr_tmp);
}
{
char *server_transport_options =
get_transport_options_for_server_proxy(mp);
if (server_transport_options) {
smartlist_add_asprintf(envs, "TOR_PT_SERVER_TRANSPORT_OPTIONS=%s",
server_transport_options);
tor_free(server_transport_options);
}
}
/* XXX024 Remove the '=' here once versions of obfsproxy which
* assert that this env var exists are sufficiently dead.
*
@ -1211,7 +1262,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
* <b>proxy_argv</b>.
*
* Requires that proxy_argv have at least one element. */
static managed_proxy_t *
STATIC managed_proxy_t *
managed_proxy_create(const smartlist_t *transport_list,
char **proxy_argv, int is_server)
{
@ -1440,7 +1491,7 @@ pt_stringify_socks_args(const smartlist_t *socks_args)
SMARTLIST_FOREACH_BEGIN(socks_args, const char *, s) {
/* Escape ';' and '\'. */
escaped_string = tor_escape_str_for_socks_arg(s);
escaped_string = tor_escape_str_for_pt_args(s, ";\\");
if (!escaped_string)
goto done;

View File

@ -110,6 +110,12 @@ STATIC int parse_smethod_line(const char *line, managed_proxy_t *mp);
STATIC int parse_version(const char *line, managed_proxy_t *mp);
STATIC void parse_env_error(const char *line);
STATIC void handle_proxy_line(const char *line, managed_proxy_t *mp);
STATIC char *get_transport_options_for_server_proxy(const managed_proxy_t *mp);
STATIC void managed_proxy_destroy(managed_proxy_t *mp,
int also_terminate_process);
STATIC managed_proxy_t *managed_proxy_create(const smartlist_t *transport_list,
char **proxy_argv, int is_server);
#endif

View File

@ -438,12 +438,84 @@ test_config_parse_bridge_line(void *arg)
"aa=b");
}
static void
test_config_parse_transport_options_line(void *arg)
{
smartlist_t *options_sl = NULL, *sl_tmp = NULL;
(void) arg;
{ /* too small line */
options_sl = get_options_from_transport_options_line("valley", NULL);
test_assert(!options_sl);
}
{ /* no k=v values */
options_sl = get_options_from_transport_options_line("hit it!", NULL);
test_assert(!options_sl);
}
{ /* correct line, but wrong transport specified */
options_sl =
get_options_from_transport_options_line("trebuchet k=v", "rook");
test_assert(!options_sl);
}
{ /* correct -- no transport specified */
sl_tmp = smartlist_new();
smartlist_add_asprintf(sl_tmp, "ladi=dadi");
smartlist_add_asprintf(sl_tmp, "weliketo=party");
options_sl =
get_options_from_transport_options_line("rook ladi=dadi weliketo=party",
NULL);
test_assert(options_sl);
test_assert(smartlist_strings_eq(options_sl, sl_tmp));
SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
smartlist_free(sl_tmp);
sl_tmp = NULL;
SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s));
smartlist_free(options_sl);
options_sl = NULL;
}
{ /* correct -- correct transport specified */
sl_tmp = smartlist_new();
smartlist_add_asprintf(sl_tmp, "ladi=dadi");
smartlist_add_asprintf(sl_tmp, "weliketo=party");
options_sl =
get_options_from_transport_options_line("rook ladi=dadi weliketo=party",
"rook");
test_assert(options_sl);
test_assert(smartlist_strings_eq(options_sl, sl_tmp));
SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
smartlist_free(sl_tmp);
sl_tmp = NULL;
SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s));
smartlist_free(options_sl);
options_sl = NULL;
}
done:
if (options_sl) {
SMARTLIST_FOREACH(options_sl, char *, s, tor_free(s));
smartlist_free(options_sl);
}
if (sl_tmp) {
SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
smartlist_free(sl_tmp);
}
}
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
CONFIG_TEST(addressmap, 0),
CONFIG_TEST(parse_bridge_line, 0),
CONFIG_TEST(parse_transport_options_line, 0),
CONFIG_TEST(check_or_create_data_subdir, TT_FORK),
CONFIG_TEST(write_to_data_subdir, TT_FORK),
END_OF_TESTCASES

View File

@ -6,6 +6,8 @@
#include "orconfig.h"
#define PT_PRIVATE
#include "or.h"
#include "config.h"
#include "confparse.h"
#include "transports.h"
#include "circuitbuild.h"
#include "test.h"
@ -107,6 +109,58 @@ test_pt_parsing(void)
tor_free(mp);
}
static void
test_pt_get_transport_options(void *arg)
{
char **execve_args;
smartlist_t *transport_list = smartlist_new();
managed_proxy_t *mp;
or_options_t *options = get_options_mutable();
char *opt_str = NULL;
config_line_t *cl = NULL;
(void)arg;
execve_args = tor_malloc(sizeof(char*)*2);
execve_args[0] = tor_strdup("cheeseshop");
execve_args[1] = NULL;
mp = managed_proxy_create(transport_list, execve_args, 1);
tt_ptr_op(mp, !=, NULL);
opt_str = get_transport_options_for_server_proxy(mp);
tt_ptr_op(opt_str, ==, NULL);
smartlist_add(mp->transports_to_launch, tor_strdup("gruyere"));
smartlist_add(mp->transports_to_launch, tor_strdup("roquefort"));
smartlist_add(mp->transports_to_launch, tor_strdup("stnectaire"));
tt_assert(options);
cl = tor_malloc_zero(sizeof(config_line_t));
cl->value = tor_strdup("gruyere melty=10 hardness=se;ven");
options->ServerTransportOptions = cl;
cl = tor_malloc_zero(sizeof(config_line_t));
cl->value = tor_strdup("stnectaire melty=4 hardness=three");
cl->next = options->ServerTransportOptions;
options->ServerTransportOptions = cl;
cl = tor_malloc_zero(sizeof(config_line_t));
cl->value = tor_strdup("pepperjack melty=12 hardness=five");
cl->next = options->ServerTransportOptions;
options->ServerTransportOptions = cl;
opt_str = get_transport_options_for_server_proxy(mp);
tt_str_op(opt_str, ==,
"gruyere:melty=10;gruyere:hardness=se\\;ven;"
"stnectaire:melty=4;stnectaire:hardness=three");
done:
tor_free(opt_str);
config_free_lines(cl);
managed_proxy_destroy(mp, 0);
smartlist_free(transport_list);
}
static void
test_pt_protocol(void)
{
@ -159,6 +213,8 @@ test_pt_protocol(void)
struct testcase_t pt_tests[] = {
PT_LEGACY(parsing),
PT_LEGACY(protocol),
{ "get_transport_options", test_pt_get_transport_options, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
};

View File

@ -796,37 +796,37 @@ test_util_expand_filename(void)
}
#endif
/** Test tor_escape_str_for_socks_arg(). */
/** Test tor_escape_str_for_pt_args(). */
static void
test_util_escape_string_socks(void)
{
char *escaped_string = NULL;
/** Simple backslash escape. */
escaped_string = tor_escape_str_for_socks_arg("This is a backslash: \\");
escaped_string = tor_escape_str_for_pt_args("This is a backslash: \\",";\\");
test_assert(escaped_string);
test_streq(escaped_string, "This is a backslash: \\\\");
tor_free(escaped_string);
/** Simple semicolon escape. */
escaped_string = tor_escape_str_for_socks_arg("First rule: Do not use ;");
escaped_string = tor_escape_str_for_pt_args("First rule:Do not use ;",";\\");
test_assert(escaped_string);
test_streq(escaped_string, "First rule: Do not use \\;");
test_streq(escaped_string, "First rule:Do not use \\;");
tor_free(escaped_string);
/** Empty string. */
escaped_string = tor_escape_str_for_socks_arg("");
escaped_string = tor_escape_str_for_pt_args("", ";\\");
test_assert(escaped_string);
test_streq(escaped_string, "");
tor_free(escaped_string);
/** Escape all characters. */
escaped_string = tor_escape_str_for_socks_arg(";\\;\\");
escaped_string = tor_escape_str_for_pt_args(";\\;\\", ";\\");
test_assert(escaped_string);
test_streq(escaped_string, "\\;\\\\\\;\\\\");
tor_free(escaped_string);
escaped_string = tor_escape_str_for_socks_arg(";");
escaped_string = tor_escape_str_for_pt_args(";", ";\\");
test_assert(escaped_string);
test_streq(escaped_string, "\\;");
tor_free(escaped_string);