diff options
author | Nick Mathewson <nickm@torproject.org> | 2013-07-18 08:45:13 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2013-07-18 08:45:13 -0400 |
commit | b551988ef46ac29427a3bd8d8a76965b190623ab (patch) | |
tree | ebb700b59bc4e5f732514855b48b2b626db4312d | |
parent | dd44ff663e074d07a8bf8b94dc5d8b9d8dc3fb32 (diff) | |
parent | 8a01a7c35b422a2c22fad2ee71f0268aa1b28b85 (diff) | |
download | tor-b551988ef46ac29427a3bd8d8a76965b190623ab.tar.gz tor-b551988ef46ac29427a3bd8d8a76965b190623ab.zip |
Merge branch 'bug8929_rebase_2'
-rw-r--r-- | changes/bug8929 | 4 | ||||
-rw-r--r-- | doc/tor.1.txt | 5 | ||||
-rw-r--r-- | src/common/util.c | 9 | ||||
-rw-r--r-- | src/common/util.h | 3 | ||||
-rw-r--r-- | src/or/config.c | 91 | ||||
-rw-r--r-- | src/or/config.h | 3 | ||||
-rw-r--r-- | src/or/or.h | 3 | ||||
-rw-r--r-- | src/or/transports.c | 63 | ||||
-rw-r--r-- | src/or/transports.h | 6 | ||||
-rw-r--r-- | src/test/test_config.c | 72 | ||||
-rw-r--r-- | src/test/test_pt.c | 56 | ||||
-rw-r--r-- | src/test/test_util.c | 14 |
12 files changed, 309 insertions, 20 deletions
diff --git a/changes/bug8929 b/changes/bug8929 new file mode 100644 index 0000000000..2a1898b8a9 --- /dev/null +++ b/changes/bug8929 @@ -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. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index a05ab33fce..bade531a07 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -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 diff --git a/src/common/util.c b/src/common/util.c index d9913dda45..0e8d34eafd 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -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); diff --git a/src/common/util.h b/src/common/util.h index ad75266587..0a8e4a23fc 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -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) \ diff --git a/src/or/config.c b/src/or/config.c index 6d3b133389..4ddced8a68 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -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. diff --git a/src/or/config.h b/src/or/config.h index c35214cd02..9910f18736 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -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 diff --git a/src/or/or.h b/src/or/or.h index 568958d61f..b8c4279896 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -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. */ diff --git a/src/or/transports.c b/src/or/transports.c index cfec70340c..5941560cc7 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -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; diff --git a/src/or/transports.h b/src/or/transports.h index cc3e018d6d..dc68e946f2 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -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 diff --git a/src/test/test_config.c b/src/test/test_config.c index 4e9e13e474..3848d352d5 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -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 diff --git a/src/test/test_pt.c b/src/test/test_pt.c index 16daa2836a..4970cfff44 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -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" @@ -108,6 +110,58 @@ test_pt_parsing(void) } 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) { char line[200]; @@ -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 }; diff --git a/src/test/test_util.c b/src/test/test_util.c index 2cc25e9861..f7513c0f31 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -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); |