Browse Source

Merge branch 'bug8929_rebase_2'

Nick Mathewson 10 years ago
parent
commit
b551988ef4
12 changed files with 309 additions and 20 deletions
  1. 4 0
      changes/bug8929
  2. 5 0
      doc/tor.1.txt
  3. 3 6
      src/common/util.c
  4. 2 1
      src/common/util.h
  5. 91 0
      src/or/config.c
  6. 3 0
      src/or/config.h
  7. 3 0
      src/or/or.h
  8. 57 6
      src/or/transports.c
  9. 6 0
      src/or/transports.h
  10. 72 0
      src/test/test_config.c
  11. 56 0
      src/test/test_pt.c
  12. 7 7
      src/test/test_util.c

+ 4 - 0
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.

+ 5 - 0
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

+ 3 - 6
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);
 

+ 2 - 1
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) \

+ 91 - 0
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.

+ 3 - 0
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
 

+ 3 - 0
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. */

+ 57 - 6
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;
 

+ 6 - 0
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
 

+ 72 - 0
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

+ 56 - 0
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"
@@ -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
 };
 

+ 7 - 7
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);