Browse Source

Merge remote-tracking branch 'andrea/ticket6456'

Somewhat tricky conflicts:
	src/or/config.c

Also, s/test_assert/tt_assert in test_config.c
Nick Mathewson 9 years ago
parent
commit
9619c395ac
7 changed files with 361 additions and 205 deletions
  1. 95 192
      src/or/config.c
  2. 3 0
      src/or/config.h
  3. 2 2
      src/or/entrynodes.c
  4. 1 1
      src/or/entrynodes.h
  5. 6 6
      src/or/transports.c
  6. 6 4
      src/or/transports.h
  7. 248 0
      src/test/test_config.c

+ 95 - 192
src/or/config.c

@@ -510,12 +510,6 @@ static int options_transition_affects_workers(
 static int options_transition_affects_descriptor(
       const or_options_t *old_options, const or_options_t *new_options);
 static int check_nickname_list(char **lst, const char *name, char **msg);
-
-static int parse_client_transport_line(const or_options_t *options,
-                                       const char *line, int validate_only);
-
-static int parse_server_transport_line(const or_options_t *options,
-                                       const char *line, int validate_only);
 static char *get_bindaddr_from_transport_listen_line(const char *line,
                                                      const char *transport);
 static int parse_dir_authority_line(const char *line,
@@ -1413,7 +1407,7 @@ options_act(const or_options_t *old_options)
   if (!options->DisableNetwork) {
     if (options->ClientTransportPlugin) {
       for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
-        if (parse_client_transport_line(options, cl->value, 0)<0) {
+        if (parse_transport_line(options, cl->value, 0, 0) < 0) {
           log_warn(LD_BUG,
                    "Previously validated ClientTransportPlugin line "
                    "could not be added!");
@@ -1424,7 +1418,7 @@ options_act(const or_options_t *old_options)
 
     if (options->ServerTransportPlugin && server_mode(options)) {
       for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
-        if (parse_server_transport_line(options, cl->value, 0)<0) {
+        if (parse_transport_line(options, cl->value, 0, 1) < 0) {
           log_warn(LD_BUG,
                    "Previously validated ServerTransportPlugin line "
                    "could not be added!");
@@ -3299,12 +3293,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
   }
 
   for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
-    if (parse_client_transport_line(options, cl->value, 1)<0)
+    if (parse_transport_line(options, cl->value, 1, 0) < 0)
       REJECT("Invalid client transport line. See logs for details.");
   }
 
   for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
-    if (parse_server_transport_line(options, cl->value, 1)<0)
+    if (parse_transport_line(options, cl->value, 1, 1) < 0)
       REJECT("Invalid server transport line. See logs for details.");
   }
 
@@ -4760,46 +4754,52 @@ parse_bridge_line(const char *line)
   return bridge_line;
 }
 
-/** Read the contents of a ClientTransportPlugin line from
- * <b>line</b>. Return 0 if the line is well-formed, and -1 if it
- * isn't.
+/** Read the contents of a ClientTransportPlugin or ServerTransportPlugin
+ * line from <b>line</b>, depending on the value of <b>server</b>. Return 0
+ * if the line is well-formed, and -1 if it isn't.
  *
- * If <b>validate_only</b> is 0, the line is well-formed, and the
- * transport is needed by some bridge:
+ * If <b>validate_only</b> is 0, the line is well-formed, and the transport is
+ * needed by some bridge:
  * - If it's an external proxy line, add the transport described in the line to
  * our internal transport list.
- * - If it's a managed proxy line, launch the managed proxy. */
-static int
-parse_client_transport_line(const or_options_t *options,
-                            const char *line, int validate_only)
+ * - If it's a managed proxy line, launch the managed proxy.
+ */
+
+STATIC int
+parse_transport_line(const or_options_t *options,
+                     const char *line, int validate_only,
+                     int server)
 {
+
   smartlist_t *items = NULL;
   int r;
-  char *field2=NULL;
-
-  const char *transports=NULL;
-  smartlist_t *transport_list=NULL;
-  char *addrport=NULL;
+  const char *transports = NULL;
+  smartlist_t *transport_list = NULL;
+  char *type = NULL;
+  char *addrport = NULL;
   tor_addr_t addr;
   uint16_t port = 0;
-  int socks_ver=PROXY_NONE;
+  int socks_ver = PROXY_NONE;
 
   /* managed proxy options */
-  int is_managed=0;
-  char **proxy_argv=NULL;
-  char **tmp=NULL;
+  int is_managed = 0;
+  char **proxy_argv = NULL;
+  char **tmp = NULL;
   int proxy_argc, i;
-  int is_useless_proxy=1;
+  int is_useless_proxy = 1;
 
   int line_length;
 
+  /* Split the line into space-separated tokens */
   items = smartlist_new();
   smartlist_split_string(items, line, NULL,
                          SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+  line_length = smartlist_len(items);
 
-  line_length =  smartlist_len(items);
   if (line_length < 3) {
-    log_warn(LD_CONFIG, "Too few arguments on ClientTransportPlugin line.");
+    log_warn(LD_CONFIG,
+             "Too few arguments on %sTransportPlugin line.",
+             server ? "Server" : "Client");
     goto err;
   }
 
@@ -4823,71 +4823,97 @@ parse_client_transport_line(const or_options_t *options,
       is_useless_proxy = 0;
   } SMARTLIST_FOREACH_END(transport_name);
 
-  /* field2 is either a SOCKS version or "exec" */
-  field2 = smartlist_get(items, 1);
-
-  if (!strcmp(field2,"socks4")) {
+  type = smartlist_get(items, 1);
+  if (!strcmp(type, "exec")) {
+    is_managed = 1;
+  } else if (server && !strcmp(type, "proxy")) {
+    /* 'proxy' syntax only with ServerTransportPlugin */
+    is_managed = 0;
+  } else if (!server && !strcmp(type, "socks4")) {
+    /* 'socks4' syntax only with ClientTransportPlugin */
+    is_managed = 0;
     socks_ver = PROXY_SOCKS4;
-  } else if (!strcmp(field2,"socks5")) {
+  } else if (!server && !strcmp(type, "socks5")) {
+    /* 'socks5' syntax only with ClientTransportPlugin */
+    is_managed = 0;
     socks_ver = PROXY_SOCKS5;
-  } else if (!strcmp(field2,"exec")) {
-    is_managed=1;
   } else {
-    log_warn(LD_CONFIG, "Strange ClientTransportPlugin field '%s'.",
-             field2);
+    log_warn(LD_CONFIG,
+             "Strange %sTransportPlugin type '%s'",
+             server ? "Server" : "Client", type);
     goto err;
   }
 
   if (is_managed && options->Sandbox) {
-    log_warn(LD_CONFIG, "Managed proxies are not compatible with Sandbox mode."
-             "(ClientTransportPlugin line was %s)", escaped(line));
+    log_warn(LD_CONFIG,
+             "Managed proxies are not compatible with Sandbox mode."
+             "(%sTransportPlugin line was %s)",
+             server ? "Server" : "Client", escaped(line));
     goto err;
   }
 
-  if (is_managed) { /* managed */
-    if (!validate_only && is_useless_proxy) {
-      log_info(LD_GENERAL, "Pluggable transport proxy (%s) does not provide "
-               "any needed transports and will not be launched.", line);
+  if (is_managed) {
+    /* managed */
+
+    if (!server && !validate_only && is_useless_proxy) {
+      log_info(LD_GENERAL,
+               "Pluggable transport proxy (%s) does not provide "
+               "any needed transports and will not be launched.",
+               line);
     }
 
-    /* If we are not just validating, use the rest of the line as the
-       argv of the proxy to be launched. Also, make sure that we are
-       only launching proxies that contribute useful transports.  */
-    if (!validate_only && !is_useless_proxy) {
-      proxy_argc = line_length-2;
+    /*
+     * If we are not just validating, use the rest of the line as the
+     * argv of the proxy to be launched. Also, make sure that we are
+     * only launching proxies that contribute useful transports.
+     */
+
+    if (!validate_only && (server || !is_useless_proxy)) {
+      proxy_argc = line_length - 2;
       tor_assert(proxy_argc > 0);
       proxy_argv = tor_calloc((proxy_argc + 1), sizeof(char *));
       tmp = proxy_argv;
-      for (i=0;i<proxy_argc;i++) { /* store arguments */
+
+      for (i = 0; i < proxy_argc; i++) {
+        /* store arguments */
         *tmp++ = smartlist_get(items, 2);
         smartlist_del_keeporder(items, 2);
       }
-      *tmp = NULL; /*terminated with NULL, just like execve() likes it*/
+      *tmp = NULL; /* terminated with NULL, just like execve() likes it */
 
       /* kickstart the thing */
-      pt_kickstart_client_proxy(transport_list, proxy_argv);
+      if (server) {
+        pt_kickstart_server_proxy(transport_list, proxy_argv);
+      } else {
+        pt_kickstart_client_proxy(transport_list, proxy_argv);
+      }
     }
-  } else { /* external */
+  } else {
+    /* external */
+
     /* ClientTransportPlugins connecting through a proxy is managed only. */
-    if (options->Socks4Proxy || options->Socks5Proxy || options->HTTPSProxy) {
+    if (!server &&
+        (options->Socks4Proxy || options->Socks5Proxy || options->HTTPSProxy)) {
       log_warn(LD_CONFIG, "You have configured an external proxy with another "
                           "proxy type. (Socks4Proxy|Socks5Proxy|HTTPSProxy)");
       goto err;
     }
 
     if (smartlist_len(transport_list) != 1) {
-      log_warn(LD_CONFIG, "You can't have an external proxy with "
-               "more than one transports.");
+      log_warn(LD_CONFIG,
+               "You can't have an external proxy with more than "
+               "one transport.");
       goto err;
     }
 
     addrport = smartlist_get(items, 2);
 
-    if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
-      log_warn(LD_CONFIG, "Error parsing transport "
-               "address '%s'", addrport);
+    if (tor_addr_port_lookup(addrport, &addr, &port) < 0) {
+      log_warn(LD_CONFIG,
+               "Error parsing transport address '%s'", addrport);
       goto err;
     }
+
     if (!port) {
       log_warn(LD_CONFIG,
                "Transport address '%s' has no port.", addrport);
@@ -4895,11 +4921,15 @@ parse_client_transport_line(const or_options_t *options,
     }
 
     if (!validate_only) {
-      transport_add_from_config(&addr, port, smartlist_get(transport_list, 0),
-                                socks_ver);
-
-      log_info(LD_DIR, "Transport '%s' found at %s",
+      log_info(LD_DIR, "%s '%s' at %s.",
+               server ? "Server transport" : "Transport",
                transports, fmt_addrport(&addr, port));
+
+      if (!server) {
+        transport_add_from_config(&addr, port,
+                                  smartlist_get(transport_list, 0),
+                                  socks_ver);
+      }
     }
   }
 
@@ -5071,133 +5101,6 @@ get_options_for_server_transport(const char *transport)
   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.
- * If <b>validate_only</b> is 0, the line is well-formed, and it's a
- * managed proxy line, launch the managed proxy. */
-static int
-parse_server_transport_line(const or_options_t *options,
-                            const char *line, int validate_only)
-{
-  smartlist_t *items = NULL;
-  int r;
-  const char *transports=NULL;
-  smartlist_t *transport_list=NULL;
-  char *type=NULL;
-  char *addrport=NULL;
-  tor_addr_t addr;
-  uint16_t port = 0;
-
-  /* managed proxy options */
-  int is_managed=0;
-  char **proxy_argv=NULL;
-  char **tmp=NULL;
-  int proxy_argc,i;
-
-  int line_length;
-
-  items = smartlist_new();
-  smartlist_split_string(items, line, NULL,
-                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
-
-  line_length =  smartlist_len(items);
-  if (line_length < 3) {
-    log_warn(LD_CONFIG, "Too few arguments on ServerTransportPlugin line.");
-    goto err;
-  }
-
-  /* Get the first line element, split it to commas into
-     transport_list (in case it's multiple transports) and validate
-     the transport names. */
-  transports = smartlist_get(items, 0);
-  transport_list = smartlist_new();
-  smartlist_split_string(transport_list, transports, ",",
-                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
-  SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) {
-    if (!string_is_C_identifier(transport_name)) {
-      log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
-               transport_name);
-      goto err;
-    }
-  } SMARTLIST_FOREACH_END(transport_name);
-
-  type = smartlist_get(items, 1);
-
-  if (!strcmp(type, "exec")) {
-    is_managed=1;
-  } else if (!strcmp(type, "proxy")) {
-    is_managed=0;
-  } else {
-    log_warn(LD_CONFIG, "Strange ServerTransportPlugin type '%s'", type);
-    goto err;
-  }
-
-  if (is_managed && options->Sandbox) {
-    log_warn(LD_CONFIG, "Managed proxies are not compatible with Sandbox mode."
-             "(ServerTransportPlugin line was %s)", escaped(line));
-    goto err;
-  }
-
-  if (is_managed) { /* managed */
-    if (!validate_only) {
-      proxy_argc = line_length-2;
-      tor_assert(proxy_argc > 0);
-      proxy_argv = tor_calloc((proxy_argc + 1), sizeof(char *));
-      tmp = proxy_argv;
-
-      for (i=0;i<proxy_argc;i++) { /* store arguments */
-        *tmp++ = smartlist_get(items, 2);
-        smartlist_del_keeporder(items, 2);
-      }
-      *tmp = NULL; /*terminated with NULL, just like execve() likes it*/
-
-      /* kickstart the thing */
-      pt_kickstart_server_proxy(transport_list, proxy_argv);
-    }
-  } else { /* external */
-    if (smartlist_len(transport_list) != 1) {
-      log_warn(LD_CONFIG, "You can't have an external proxy with "
-               "more than one transports.");
-      goto err;
-    }
-
-    addrport = smartlist_get(items, 2);
-
-    if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
-      log_warn(LD_CONFIG, "Error parsing transport "
-               "address '%s'", addrport);
-      goto err;
-    }
-    if (!port) {
-      log_warn(LD_CONFIG,
-               "Transport address '%s' has no port.", addrport);
-      goto err;
-    }
-
-    if (!validate_only) {
-      log_info(LD_DIR, "Server transport '%s' at %s.",
-               transports, fmt_addrport(&addr, port));
-    }
-  }
-
-  r = 0;
-  goto done;
-
- err:
-  r = -1;
-
- done:
-  SMARTLIST_FOREACH(items, char*, s, tor_free(s));
-  smartlist_free(items);
-  if (transport_list) {
-    SMARTLIST_FOREACH(transport_list, char*, s, tor_free(s));
-    smartlist_free(transport_list);
-  }
-
-  return r;
-}
-
 /** Read the contents of a DirAuthority line from <b>line</b>. If
  * <b>validate_only</b> is 0, and the line is well-formed, and it
  * shares any bits with <b>required_type</b> or <b>required_type</b>

+ 3 - 0
src/or/config.h

@@ -141,6 +141,9 @@ STATIC int options_validate(or_options_t *old_options,
                             or_options_t *options,
                             or_options_t *default_options,
                             int from_setconf, char **msg);
+STATIC int parse_transport_line(const or_options_t *options,
+                                const char *line, int validate_only,
+                                int server);
 #endif
 
 #endif

+ 2 - 2
src/or/entrynodes.c

@@ -1919,8 +1919,8 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port,
 
 /** Return True if we have a bridge that uses a transport with name
  *  <b>transport_name</b>. */
-int
-transport_is_needed(const char *transport_name)
+MOCK_IMPL(int,
+transport_is_needed, (const char *transport_name))
 {
   if (!bridge_list)
     return 0;

+ 1 - 1
src/or/entrynodes.h

@@ -154,7 +154,7 @@ struct transport_t;
 int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
                                       const struct transport_t **transport);
 
-int transport_is_needed(const char *transport_name);
+MOCK_DECL(int, transport_is_needed, (const char *transport_name));
 int validate_pluggable_transports_config(void);
 
 double pathbias_get_close_success_count(entry_guard_t *guard);

+ 6 - 6
src/or/transports.c

@@ -326,9 +326,9 @@ transport_add(transport_t *t)
 /** Remember a new pluggable transport proxy at <b>addr</b>:<b>port</b>.
  *  <b>name</b> is set to the name of the protocol this proxy uses.
  *  <b>socks_ver</b> is set to the SOCKS version of the proxy. */
-int
-transport_add_from_config(const tor_addr_t *addr, uint16_t port,
-                          const char *name, int socks_ver)
+MOCK_IMPL(int,
+transport_add_from_config, (const tor_addr_t *addr, uint16_t port,
+                            const char *name, int socks_ver))
 {
   transport_t *t = transport_new(addr, port, name, socks_ver, NULL);
 
@@ -1456,9 +1456,9 @@ managed_proxy_create(const smartlist_t *transport_list,
  * Requires that proxy_argv be a NULL-terminated array of command-line
  * elements, containing at least one element.
  **/
-void
-pt_kickstart_proxy(const smartlist_t *transport_list,
-                   char **proxy_argv, int is_server)
+MOCK_IMPL(void,
+pt_kickstart_proxy, (const smartlist_t *transport_list,
+                     char **proxy_argv, int is_server))
 {
   managed_proxy_t *mp=NULL;
   transport_t *old_transport = NULL;

+ 6 - 4
src/or/transports.h

@@ -32,14 +32,16 @@ typedef struct transport_t {
 
 void mark_transport_list(void);
 void sweep_transport_list(void);
-int transport_add_from_config(const tor_addr_t *addr, uint16_t port,
-                               const char *name, int socks_ver);
+MOCK_DECL(int, transport_add_from_config,
+          (const tor_addr_t *addr, uint16_t port,
+           const char *name, int socks_ver));
 void transport_free(transport_t *transport);
 
 transport_t *transport_get_by_name(const char *name);
 
-void pt_kickstart_proxy(const smartlist_t *transport_list, char **proxy_argv,
-                        int is_server);
+MOCK_DECL(void, pt_kickstart_proxy,
+          (const smartlist_t *transport_list, char **proxy_argv,
+           int is_server));
 
 #define pt_kickstart_client_proxy(tl, pa)  \
   pt_kickstart_proxy(tl, pa, 0)

+ 248 - 0
src/test/test_config.c

@@ -14,6 +14,8 @@
 #include "test.h"
 #include "util.h"
 #include "address.h"
+#include "entrynodes.h"
+#include "transports.h"
 
 static void
 test_config_addressmap(void *arg)
@@ -552,6 +554,251 @@ test_config_parse_transport_options_line(void *arg)
   }
 }
 
+/* Mocks needed for the transport plugin line test */
+
+static void pt_kickstart_proxy_mock(const smartlist_t *transport_list,
+                                    char **proxy_argv, int is_server);
+static int transport_add_from_config_mock(const tor_addr_t *addr,
+                                          uint16_t port, const char *name,
+                                          int socks_ver);
+static int transport_is_needed_mock(const char *transport_name);
+
+static int pt_kickstart_proxy_mock_call_count = 0;
+static int transport_add_from_config_mock_call_count = 0;
+static int transport_is_needed_mock_call_count = 0;
+static int transport_is_needed_mock_return = 0;
+
+static void
+pt_kickstart_proxy_mock(const smartlist_t *transport_list,
+                        char **proxy_argv, int is_server)
+{
+  ++pt_kickstart_proxy_mock_call_count;
+}
+
+static int
+transport_add_from_config_mock(const tor_addr_t *addr,
+                               uint16_t port, const char *name,
+                               int socks_ver)
+{
+  ++transport_add_from_config_mock_call_count;
+
+  return 0;
+}
+
+static int
+transport_is_needed_mock(const char *transport_name)
+{
+  ++transport_is_needed_mock_call_count;
+
+  return transport_is_needed_mock_return;
+}
+
+/**
+ * Test parsing for the ClientTransportPlugin and ServerTransportPlugin config
+ * options.
+ */
+
+static void
+test_config_parse_transport_plugin_line(void *arg)
+{
+  or_options_t *options = get_options_mutable();
+  int r, tmp;
+  int old_pt_kickstart_proxy_mock_call_count;
+  int old_transport_add_from_config_mock_call_count;
+  int old_transport_is_needed_mock_call_count;
+
+  /* Bad transport lines - too short */
+  r = parse_transport_line(options, "bad", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options, "bad", 1, 1);
+  tt_assert(r < 0);
+  r = parse_transport_line(options, "bad bad", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options, "bad bad", 1, 1);
+  tt_assert(r < 0);
+
+  /* Test transport list parsing */
+  r = parse_transport_line(options,
+      "transport_1 exec /usr/bin/fake-transport", 1, 0);
+  tt_assert(r == 0);
+  r = parse_transport_line(options,
+   "transport_1 exec /usr/bin/fake-transport", 1, 1);
+  tt_assert(r == 0);
+  r = parse_transport_line(options,
+      "transport_1,transport_2 exec /usr/bin/fake-transport", 1, 0);
+  tt_assert(r == 0);
+  r = parse_transport_line(options,
+      "transport_1,transport_2 exec /usr/bin/fake-transport", 1, 1);
+  tt_assert(r == 0);
+  /* Bad transport identifiers */
+  r = parse_transport_line(options,
+      "transport_* exec /usr/bin/fake-transport", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options,
+      "transport_* exec /usr/bin/fake-transport", 1, 1);
+  tt_assert(r < 0);
+
+  /* Check SOCKS cases for client transport */
+  r = parse_transport_line(options,
+      "transport_1 socks4 1.2.3.4:567", 1, 0);
+  tt_assert(r == 0);
+  r = parse_transport_line(options,
+      "transport_1 socks5 1.2.3.4:567", 1, 0);
+  tt_assert(r == 0);
+  /* Proxy case for server transport */
+  r = parse_transport_line(options,
+      "transport_1 proxy 1.2.3.4:567", 1, 1);
+  tt_assert(r == 0);
+  /* Multiple-transport error exit */
+  r = parse_transport_line(options,
+      "transport_1,transport_2 socks5 1.2.3.4:567", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options,
+      "transport_1,transport_2 proxy 1.2.3.4:567", 1, 1);
+  /* No port error exit */
+  r = parse_transport_line(options,
+      "transport_1 socks5 1.2.3.4", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options,
+     "transport_1 proxy 1.2.3.4", 1, 1);
+  tt_assert(r < 0);
+  /* Unparsable address error exit */
+  r = parse_transport_line(options,
+      "transport_1 socks5 1.2.3:6x7", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options,
+      "transport_1 proxy 1.2.3:6x7", 1, 1);
+  tt_assert(r < 0);
+
+  /* "Strange {Client|Server}TransportPlugin field" error exit */
+  r = parse_transport_line(options,
+      "transport_1 foo bar", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options,
+      "transport_1 foo bar", 1, 1);
+  tt_assert(r < 0);
+
+  /* No sandbox mode error exit */
+  tmp = options->Sandbox;
+  options->Sandbox = 1;
+  r = parse_transport_line(options,
+      "transport_1 exec /usr/bin/fake-transport", 1, 0);
+  tt_assert(r < 0);
+  r = parse_transport_line(options,
+      "transport_1 exec /usr/bin/fake-transport", 1, 1);
+  tt_assert(r < 0);
+  options->Sandbox = tmp;
+
+  /*
+   * These final test cases cover code paths that only activate without
+   * validate_only, so they need mocks in place.
+   */
+  MOCK(pt_kickstart_proxy, pt_kickstart_proxy_mock);
+  old_pt_kickstart_proxy_mock_call_count =
+    pt_kickstart_proxy_mock_call_count;
+  r = parse_transport_line(options,
+      "transport_1 exec /usr/bin/fake-transport", 0, 1);
+  tt_assert(r == 0);
+  tt_assert(pt_kickstart_proxy_mock_call_count ==
+      old_pt_kickstart_proxy_mock_call_count + 1);
+  UNMOCK(pt_kickstart_proxy);
+
+  /* This one hits a log line in the !validate_only case only */
+  r = parse_transport_line(options,
+      "transport_1 proxy 1.2.3.4:567", 0, 1);
+  tt_assert(r == 0);
+
+  /* Check mocked client transport cases */
+  MOCK(pt_kickstart_proxy, pt_kickstart_proxy_mock);
+  MOCK(transport_add_from_config, transport_add_from_config_mock);
+  MOCK(transport_is_needed, transport_is_needed_mock);
+
+  /* Unnecessary transport case */
+  transport_is_needed_mock_return = 0;
+  old_pt_kickstart_proxy_mock_call_count =
+    pt_kickstart_proxy_mock_call_count;
+  old_transport_add_from_config_mock_call_count =
+    transport_add_from_config_mock_call_count;
+  old_transport_is_needed_mock_call_count =
+    transport_is_needed_mock_call_count;
+  r = parse_transport_line(options,
+      "transport_1 exec /usr/bin/fake-transport", 0, 0);
+  /* Should have succeeded */
+  tt_assert(r == 0);
+  /* transport_is_needed() should have been called */
+  tt_assert(transport_is_needed_mock_call_count ==
+      old_transport_is_needed_mock_call_count + 1);
+  /*
+   * pt_kickstart_proxy() and transport_add_from_config() should
+   * not have been called.
+   */
+  tt_assert(pt_kickstart_proxy_mock_call_count ==
+      old_pt_kickstart_proxy_mock_call_count);
+  tt_assert(transport_add_from_config_mock_call_count ==
+      old_transport_add_from_config_mock_call_count);
+
+  /* Necessary transport case */
+  transport_is_needed_mock_return = 1;
+  old_pt_kickstart_proxy_mock_call_count =
+    pt_kickstart_proxy_mock_call_count;
+  old_transport_add_from_config_mock_call_count =
+    transport_add_from_config_mock_call_count;
+  old_transport_is_needed_mock_call_count =
+    transport_is_needed_mock_call_count;
+  r = parse_transport_line(options,
+      "transport_1 exec /usr/bin/fake-transport", 0, 0);
+  /* Should have succeeded */
+  tt_assert(r == 0);
+  /*
+   * transport_is_needed() and pt_kickstart_proxy() should have been
+   * called.
+   */
+  tt_assert(pt_kickstart_proxy_mock_call_count ==
+      old_pt_kickstart_proxy_mock_call_count + 1);
+  tt_assert(transport_is_needed_mock_call_count ==
+      old_transport_is_needed_mock_call_count + 1);
+  /* transport_add_from_config() should not have been called. */
+  tt_assert(transport_add_from_config_mock_call_count ==
+      old_transport_add_from_config_mock_call_count);
+
+  /* proxy case */
+  transport_is_needed_mock_return = 1;
+  old_pt_kickstart_proxy_mock_call_count =
+    pt_kickstart_proxy_mock_call_count;
+  old_transport_add_from_config_mock_call_count =
+    transport_add_from_config_mock_call_count;
+  old_transport_is_needed_mock_call_count =
+    transport_is_needed_mock_call_count;
+  r = parse_transport_line(options,
+      "transport_1 socks5 1.2.3.4:567", 0, 0);
+  /* Should have succeeded */
+  tt_assert(r == 0);
+  /*
+   * transport_is_needed() and transport_add_from_config() should have
+   * been called.
+   */
+  tt_assert(transport_add_from_config_mock_call_count ==
+      old_transport_add_from_config_mock_call_count + 1);
+  tt_assert(transport_is_needed_mock_call_count ==
+      old_transport_is_needed_mock_call_count + 1);
+  /* pt_kickstart_proxy() should not have been called. */
+  tt_assert(pt_kickstart_proxy_mock_call_count ==
+      old_pt_kickstart_proxy_mock_call_count);
+
+  /* Done with mocked client transport cases */
+  UNMOCK(transport_is_needed);
+  UNMOCK(transport_add_from_config);
+  UNMOCK(pt_kickstart_proxy);
+
+ done:
+  /* Make sure we undo all mocks */
+  UNMOCK(pt_kickstart_proxy);
+  UNMOCK(transport_add_from_config);
+  UNMOCK(transport_is_needed);
+
+  return;
+}
+
 // Tests if an options with MyFamily fingerprints missing '$' normalises
 // them correctly and also ensure it also works with multiple fingerprints
 static void
@@ -596,6 +843,7 @@ struct testcase_t config_tests[] = {
   CONFIG_TEST(addressmap, 0),
   CONFIG_TEST(parse_bridge_line, 0),
   CONFIG_TEST(parse_transport_options_line, 0),
+  CONFIG_TEST(parse_transport_plugin_line, TT_FORK),
   CONFIG_TEST(check_or_create_data_subdir, TT_FORK),
   CONFIG_TEST(write_to_data_subdir, TT_FORK),
   CONFIG_TEST(fix_my_family, 0),