浏览代码

Merge remote-tracking branch 'asn2/bug3656'

Conflicts:
	src/common/util.c
	src/common/util.h
	src/or/config.h
	src/or/main.c
	src/test/test_util.c
Nick Mathewson 14 年之前
父节点
当前提交
ed39621a9d
共有 16 个文件被更改,包括 2141 次插入147 次删除
  1. 149 57
      src/common/util.c
  2. 22 5
      src/common/util.h
  3. 2 0
      src/or/Makefile.am
  4. 159 27
      src/or/circuitbuild.c
  5. 14 1
      src/or/circuitbuild.h
  6. 453 48
      src/or/config.c
  7. 5 0
      src/or/config.h
  8. 6 3
      src/or/connection.c
  9. 19 2
      src/or/main.c
  10. 5 0
      src/or/or.h
  11. 1048 0
      src/or/transports.c
  12. 105 0
      src/or/transports.h
  13. 1 0
      src/test/Makefile.am
  14. 2 0
      src/test/test.c
  15. 147 0
      src/test/test_pt.c
  16. 4 4
      src/test/test_util.c

+ 149 - 57
src/common/util.c

@@ -31,6 +31,7 @@
 #include <direct.h>
 #include <direct.h>
 #include <process.h>
 #include <process.h>
 #include <tchar.h>
 #include <tchar.h>
+#include <Winbase.h>
 #else
 #else
 #include <dirent.h>
 #include <dirent.h>
 #include <pwd.h>
 #include <pwd.h>
@@ -46,6 +47,7 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <assert.h>
 #include <assert.h>
+#include <signal.h>
 
 
 #ifdef HAVE_NETINET_IN_H
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #include <netinet/in.h>
@@ -769,6 +771,34 @@ find_str_at_start_of_line(const char *haystack, const char *needle)
   return NULL;
   return NULL;
 }
 }
 
 
+/** Returns true if <b>string</b> could be a C identifier.
+    A C identifier must begin with a letter or an underscore and the
+    rest of its characters can be letters, numbers or underscores. No
+    length limit is imposed. */
+int
+string_is_C_identifier(const char *string)
+{
+  size_t iter;
+  size_t length = strlen(string);
+  if (!length)
+    return 0;
+
+  for (iter = 0; iter < length ; iter++) {
+    if (iter == 0) {
+      if (!(TOR_ISALPHA(string[iter]) ||
+            string[iter] == '_'))
+        return 0;
+    } else {
+      if (!(TOR_ISALPHA(string[iter]) ||
+            TOR_ISDIGIT(string[iter]) ||
+            string[iter] == '_'))
+        return 0;
+    }
+  }
+
+  return 1;
+}
+
 /** Return true iff the 'len' bytes at 'mem' are all zero. */
 /** Return true iff the 'len' bytes at 'mem' are all zero. */
 int
 int
 tor_mem_is_zero(const char *mem, size_t len)
 tor_mem_is_zero(const char *mem, size_t len)
@@ -3129,6 +3159,28 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
 /* Maximum number of file descriptors, if we cannot get it via sysconf() */
 /* Maximum number of file descriptors, if we cannot get it via sysconf() */
 #define DEFAULT_MAX_FD 256
 #define DEFAULT_MAX_FD 256
 
 
+/** Terminate process running at PID <b>pid</b>.
+ *  Code borrowed from Python's os.kill. */
+int
+tor_terminate_process(pid_t pid)
+{
+#ifdef MS_WINDOWS
+  HANDLE handle;
+  /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
+     attempt to open and terminate the process. */
+  handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
+  if (!handle)
+    return -1;
+
+  if (!TerminateProcess(handle, 0))
+    return -1;
+  else
+    return 0;
+#else /* Unix */
+  return kill(pid, SIGTERM);
+#endif
+}
+
 #define CHILD_STATE_INIT 0
 #define CHILD_STATE_INIT 0
 #define CHILD_STATE_PIPE 1
 #define CHILD_STATE_PIPE 1
 #define CHILD_STATE_MAXFD 2
 #define CHILD_STATE_MAXFD 2
@@ -3140,8 +3192,6 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
 #define CHILD_STATE_EXEC 8
 #define CHILD_STATE_EXEC 8
 #define CHILD_STATE_FAILEXEC 9
 #define CHILD_STATE_FAILEXEC 9
 
 
-#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
-
 /** Start a program in the background. If <b>filename</b> contains a '/', then
 /** Start a program in the background. If <b>filename</b> contains a '/', then
  * it will be treated as an absolute or relative path.  Otherwise, on
  * it will be treated as an absolute or relative path.  Otherwise, on
  * non-Windows systems, the system path will be searched for <b>filename</b>.
  * non-Windows systems, the system path will be searched for <b>filename</b>.
@@ -3161,9 +3211,9 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
  * Python, and example code from
  * Python, and example code from
  * http://msdn.microsoft.com/en-us/library/ms682499%28v=vs.85%29.aspx.
  * http://msdn.microsoft.com/en-us/library/ms682499%28v=vs.85%29.aspx.
  */
  */
-
 int
 int
 tor_spawn_background(const char *const filename, const char **argv,
 tor_spawn_background(const char *const filename, const char **argv,
+                     const char **envp,
                      process_handle_t *process_handle)
                      process_handle_t *process_handle)
 {
 {
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
@@ -3372,7 +3422,10 @@ 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 */
-    execvp(filename, (char *const *) argv);
+    if (envp)
+      execve(filename, (char *const *) argv, (char*const*)envp);
+    else
+      execvp(filename, (char *const *) argv);
 
 
     /* If we got here, the exec or open(/dev/null) failed */
     /* If we got here, the exec or open(/dev/null) failed */
 
 
@@ -3763,6 +3816,7 @@ log_from_handle(HANDLE *pipe, int severity)
 }
 }
 
 
 #else
 #else
+
 /** Read from stream, and send lines to log at the specified log level.
 /** Read from stream, and send lines to log at the specified log level.
  * Returns 1 if stream is closed normally, -1 if there is a error reading, and
  * Returns 1 if stream is closed normally, -1 if there is a error reading, and
  * 0 otherwise. Handles lines from tor-fw-helper and
  * 0 otherwise. Handles lines from tor-fw-helper and
@@ -3773,72 +3827,110 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
               int *child_status)
               int *child_status)
 {
 {
   char buf[256];
   char buf[256];
+  enum stream_status r;
 
 
   for (;;) {
   for (;;) {
-    char *retval;
-    retval = fgets(buf, sizeof(buf), stream);
+    r = get_string_from_pipe(stream, buf, sizeof(buf) - 1);
 
 
-    if (NULL == retval) {
-      if (feof(stream)) {
-        /* Program has closed stream (probably it exited) */
-        /* TODO: check error */
-        fclose(stream);
-        return 1;
+    if (r == IO_STREAM_CLOSED) {
+      fclose(stream);
+      return 1;
+    } else if (r == IO_STREAM_EAGAIN) {
+      return 0;
+    } else if (r == IO_STREAM_TERM) {
+      fclose(stream);
+      return -1;
+    }
+
+    tor_assert(r == IO_STREAM_OKAY);
+
+    /* Check if buf starts with SPAWN_ERROR_MESSAGE */
+    if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
+      /* Parse error message */
+      int retval, child_state, saved_errno;
+      retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
+                          &child_state, &saved_errno);
+      if (retval == 2) {
+        log_warn(LD_GENERAL,
+                 "Failed to start child process \"%s\" in state %d: %s",
+                 executable, child_state, strerror(saved_errno));
+        if (child_status)
+          *child_status = 1;
       } else {
       } else {
-        if (EAGAIN == errno) {
-          /* Nothing more to read, try again next time */
-          return 0;
-        } else {
-          /* There was a problem, abandon this child process */
-          fclose(stream);
-          return -1;
-        }
+        /* Failed to parse message from child process, log it as a
+           warning */
+        log_warn(LD_GENERAL,
+                 "Unexpected message from port forwarding helper \"%s\": %s",
+                 executable, buf);
       }
       }
     } else {
     } else {
-      /* We have some data, log it and keep asking for more */
-      size_t len;
+      log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf);
+    }
+  }
 
 
-      len = strlen(buf);
-      if (buf[len - 1] == '\n') {
-        /* Remove the trailing newline */
-        buf[len - 1] = '\0';
-      } else {
-        /* No newline; check whether we overflowed the buffer */
-        if (!feof(stream))
-          log_warn(LD_GENERAL,
-                  "Line from port forwarding helper was truncated: %s", buf);
-          /* TODO: What to do with this error? */
-      }
+  /* We should never get here */
+  return -1;
+}
+#endif
 
 
-      /* Check if buf starts with SPAWN_ERROR_MESSAGE */
-      if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
-          /* Parse error message */
-          int retval, child_state, saved_errno;
-          retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
-                              &child_state, &saved_errno);
-          if (retval == 2) {
-              log_warn(LD_GENERAL,
-                "Failed to start child process \"%s\" in state %d: %s",
-                executable, child_state, strerror(saved_errno));
-              if (child_status)
-                  *child_status = 1;
-          } else {
-              /* Failed to parse message from child process, log it as a
-                 warning */
-              log_warn(LD_GENERAL,
-                "Unexpected message from port forwarding helper \"%s\": %s",
-                executable, buf);
-          }
+/** Reads from <b>stream</b> and stores input in <b>buf_out</b> making
+ *  sure it's below <b>count</b> bytes.
+ *  If the string has a trailing newline, we strip it off.
+ *
+ * This function is specifically created to handle input from managed
+ * proxies, according to the pluggable transports spec. Make sure it
+ * fits your needs before using it.
+ *
+ * Returns:
+ * IO_STREAM_CLOSED: If the stream is closed.
+ * IO_STREAM_EAGAIN: If there is nothing to read and we should check back
+ *  later.
+ * IO_STREAM_TERM: If something is wrong with the stream.
+ * IO_STREAM_OKAY: If everything went okay and we got a string
+ *  in <b>buf_out</b>. */
+enum stream_status
+get_string_from_pipe(FILE *stream, char *buf_out, size_t count)
+{
+  char *retval;
+  size_t len;
+
+  retval = fgets(buf_out, count, stream);
+
+  if (!retval) {
+    if (feof(stream)) {
+      /* Program has closed stream (probably it exited) */
+      /* TODO: check error */
+      return IO_STREAM_CLOSED;
+    } else {
+      if (EAGAIN == errno) {
+        /* Nothing more to read, try again next time */
+        return IO_STREAM_EAGAIN;
       } else {
       } else {
-          log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf);
+        /* There was a problem, abandon this child process */
+        return IO_STREAM_TERM;
       }
       }
     }
     }
+  } else {
+    len = strlen(buf_out);
+    tor_assert(len>0);
+
+    if (buf_out[len - 1] == '\n') {
+      /* Remove the trailing newline */
+      buf_out[len - 1] = '\0';
+    } else {
+      /* No newline; check whether we overflowed the buffer */
+      if (!feof(stream))
+        log_info(LD_GENERAL,
+                 "Line from stream was truncated: %s", buf_out);
+      /* TODO: What to do with this error? */
+    }
+
+    return IO_STREAM_OKAY;
   }
   }
 
 
   /* We should never get here */
   /* We should never get here */
-  return -1;
+  return IO_STREAM_TERM;
 }
 }
-#endif
 
 
 void
 void
 tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
 tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
@@ -3885,9 +3977,9 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
 
 
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
     /* Passing NULL as lpApplicationName makes Windows search for the .exe */
     /* Passing NULL as lpApplicationName makes Windows search for the .exe */
-    tor_spawn_background(NULL, argv, &child_handle);
+    tor_spawn_background(NULL, argv, NULL &child_handle);
 #else
 #else
-    tor_spawn_background(filename, argv, &child_handle);
+    tor_spawn_background(filename, argv, NULL, &child_handle);
 #endif
 #endif
     if (PROCESS_STATUS_ERROR == child_handle.status) {
     if (PROCESS_STATUS_ERROR == child_handle.status) {
       log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
       log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",

+ 22 - 5
src/common/util.h

@@ -205,6 +205,8 @@ const char *find_whitespace(const char *s) ATTR_PURE;
 const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE;
 const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE;
 const char *find_str_at_start_of_line(const char *haystack, const char *needle)
 const char *find_str_at_start_of_line(const char *haystack, const char *needle)
   ATTR_PURE;
   ATTR_PURE;
+int string_is_C_identifier(const char *string);
+
 int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE;
 int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE;
 int tor_digest_is_zero(const char *digest) ATTR_PURE;
 int tor_digest_is_zero(const char *digest) ATTR_PURE;
 int tor_digest256_is_zero(const char *digest) ATTR_PURE;
 int tor_digest256_is_zero(const char *digest) ATTR_PURE;
@@ -286,6 +288,16 @@ char *rate_limit_log(ratelim_t *lim, time_t now);
 ssize_t write_all(tor_socket_t fd, const char *buf, size_t count,int isSocket);
 ssize_t write_all(tor_socket_t fd, const char *buf, size_t count,int isSocket);
 ssize_t read_all(tor_socket_t fd, char *buf, size_t count, int isSocket);
 ssize_t read_all(tor_socket_t fd, char *buf, size_t count, int isSocket);
 
 
+/** Status of an I/O stream. */
+enum stream_status {
+  IO_STREAM_OKAY,
+  IO_STREAM_EAGAIN,
+  IO_STREAM_TERM,
+  IO_STREAM_CLOSED
+};
+
+enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count);
+
 /** Return values from file_status(); see that function's documentation
 /** Return values from file_status(); see that function's documentation
  * for details. */
  * for details. */
 typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR } file_status_t;
 typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR } file_status_t;
@@ -348,6 +360,13 @@ void write_pidfile(char *filename);
 void tor_check_port_forwarding(const char *filename,
 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);
 
 
+int tor_terminate_process(pid_t pid);
+typedef struct process_handle_s process_handle_t;
+int tor_spawn_background(const char *const filename, const char **argv,
+                         const char **envp, process_handle_t *process_handle);
+
+#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
+
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
 HANDLE load_windows_system_library(const TCHAR *library_name);
 HANDLE load_windows_system_library(const TCHAR *library_name);
 #endif
 #endif
@@ -361,7 +380,7 @@ HANDLE load_windows_system_library(const TCHAR *library_name);
 #define PROCESS_STATUS_NOTRUNNING 0
 #define PROCESS_STATUS_NOTRUNNING 0
 #define PROCESS_STATUS_RUNNING 1
 #define PROCESS_STATUS_RUNNING 1
 #define PROCESS_STATUS_ERROR -1
 #define PROCESS_STATUS_ERROR -1
-typedef struct process_handle_s {
+struct process_handle_s {
   int status;
   int status;
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
   HANDLE stdout_pipe;
   HANDLE stdout_pipe;
@@ -374,10 +393,7 @@ typedef struct process_handle_s {
   FILE *stderr_handle;
   FILE *stderr_handle;
   pid_t pid;
   pid_t pid;
 #endif // MS_WINDOWS
 #endif // MS_WINDOWS
-} process_handle_t;
-
-int tor_spawn_background(const char *const filename, const char **argv,
-                         process_handle_t *process_handle);
+};
 
 
 /* Return values of tor_get_exit_code() */
 /* Return values of tor_get_exit_code() */
 #define PROCESS_EXIT_RUNNING 1
 #define PROCESS_EXIT_RUNNING 1
@@ -399,6 +415,7 @@ ssize_t tor_read_all_from_process_stdout(
 ssize_t tor_read_all_from_process_stderr(
 ssize_t tor_read_all_from_process_stderr(
     const process_handle_t *process_handle, char *buf, size_t count);
     const process_handle_t *process_handle, char *buf, size_t count);
 char *tor_join_win_cmdline(const char *argv[]);
 char *tor_join_win_cmdline(const char *argv[]);
+
 void format_helper_exit_status(unsigned char child_state,
 void format_helper_exit_status(unsigned char child_state,
                                int saved_errno, char *hex_errno);
                                int saved_errno, char *hex_errno);
 
 

+ 2 - 0
src/or/Makefile.am

@@ -39,6 +39,7 @@ libtor_a_SOURCES = \
 	networkstatus.c				\
 	networkstatus.c				\
 	nodelist.c				\
 	nodelist.c				\
 	onion.c					\
 	onion.c					\
+	transports.c            \
 	policies.c				\
 	policies.c				\
 	reasons.c				\
 	reasons.c				\
 	relay.c					\
 	relay.c					\
@@ -104,6 +105,7 @@ noinst_HEADERS = \
 	ntmain.h				\
 	ntmain.h				\
 	onion.h					\
 	onion.h					\
 	or.h					\
 	or.h					\
+	transports.h            \
 	policies.h				\
 	policies.h				\
 	reasons.h				\
 	reasons.h				\
 	relay.h					\
 	relay.h					\

+ 159 - 27
src/or/circuitbuild.c

@@ -26,6 +26,7 @@
 #include "nodelist.h"
 #include "nodelist.h"
 #include "onion.h"
 #include "onion.h"
 #include "policies.h"
 #include "policies.h"
+#include "transports.h"
 #include "relay.h"
 #include "relay.h"
 #include "rephist.h"
 #include "rephist.h"
 #include "router.h"
 #include "router.h"
@@ -123,8 +124,6 @@ static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
 
 
 static void entry_guards_changed(void);
 static void entry_guards_changed(void);
 
 
-static const transport_t *transport_get_by_name(const char *name);
-static void transport_free(transport_t *transport);
 static void bridge_free(bridge_info_t *bridge);
 static void bridge_free(bridge_info_t *bridge);
 
 
 /**
 /**
@@ -4595,6 +4594,32 @@ bridge_free(bridge_info_t *bridge)
 /** A list of pluggable transports found in torrc. */
 /** A list of pluggable transports found in torrc. */
 static smartlist_t *transport_list = NULL;
 static smartlist_t *transport_list = NULL;
 
 
+/** Mark every entry of the transport list to be removed on our next call to
+ * sweep_transport_list unless it has first been un-marked. */
+void
+mark_transport_list(void)
+{
+  if (!transport_list)
+    transport_list = smartlist_create();
+  SMARTLIST_FOREACH(transport_list, transport_t *, t,
+                    t->marked_for_removal = 1);
+}
+
+/** Remove every entry of the transport list that was marked with
+ * mark_transport_list if it has not subsequently been un-marked. */
+void
+sweep_transport_list(void)
+{
+  if (!transport_list)
+    transport_list = smartlist_create();
+  SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, t) {
+    if (t->marked_for_removal) {
+      SMARTLIST_DEL_CURRENT(transport_list, t);
+      transport_free(t);
+    }
+  } SMARTLIST_FOREACH_END(t);
+}
+
 /** Initialize the pluggable transports list to empty, creating it if
 /** Initialize the pluggable transports list to empty, creating it if
  *  needed. */
  *  needed. */
 void
 void
@@ -4607,7 +4632,7 @@ clear_transport_list(void)
 }
 }
 
 
 /** Free the pluggable transport struct <b>transport</b>. */
 /** Free the pluggable transport struct <b>transport</b>. */
-static void
+void
 transport_free(transport_t *transport)
 transport_free(transport_t *transport)
 {
 {
   if (!transport)
   if (!transport)
@@ -4619,7 +4644,7 @@ transport_free(transport_t *transport)
 
 
 /** Returns the transport in our transport list that has the name <b>name</b>.
 /** Returns the transport in our transport list that has the name <b>name</b>.
  *  Else returns NULL. */
  *  Else returns NULL. */
-static const transport_t *
+transport_t *
 transport_get_by_name(const char *name)
 transport_get_by_name(const char *name)
 {
 {
   tor_assert(name);
   tor_assert(name);
@@ -4627,7 +4652,7 @@ transport_get_by_name(const char *name)
   if (!transport_list)
   if (!transport_list)
     return NULL;
     return NULL;
 
 
-  SMARTLIST_FOREACH_BEGIN(transport_list, const transport_t *, transport) {
+  SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, transport) {
     if (!strcmp(transport->name, name))
     if (!strcmp(transport->name, name))
       return transport;
       return transport;
   } SMARTLIST_FOREACH_END(transport);
   } SMARTLIST_FOREACH_END(transport);
@@ -4635,41 +4660,139 @@ transport_get_by_name(const char *name)
   return NULL;
   return NULL;
 }
 }
 
 
-/** 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.
- *
- *  Returns 0 on success, -1 on fail. */
-int
-transport_add_from_config(const tor_addr_t *addr, uint16_t port,
-                          const char *name, int socks_ver)
+/** Returns a transport_t struct for a transport proxy supporting the
+    protocol <b>name</b> listening at <b>addr</b>:<b>port</b> using
+    SOCKS version <b>socks_ver</b>. */
+transport_t *
+transport_create(const tor_addr_t *addr, uint16_t port,
+                 const char *name, int socks_ver)
 {
 {
-  transport_t *t;
-
-  if (transport_get_by_name(name)) { /* check for duplicate names */
-    log_warn(LD_CONFIG, "More than one transport has '%s' as "
-             "its name.", name);
-    return -1;
-  }
+  transport_t *t = tor_malloc_zero(sizeof(transport_t));
 
 
-  t = tor_malloc_zero(sizeof(transport_t));
   tor_addr_copy(&t->addr, addr);
   tor_addr_copy(&t->addr, addr);
   t->port = port;
   t->port = port;
   t->name = tor_strdup(name);
   t->name = tor_strdup(name);
   t->socks_version = socks_ver;
   t->socks_version = socks_ver;
 
 
-  if (!transport_list)
-    transport_list = smartlist_create();
+  return t;
+}
+
+/** Resolve any conflicts that the insertion of transport <b>t</b>
+ *  might cause.
+ *  Return 0 if <b>t</b> is OK and should be registered, 1 if there is
+ *  a transport identical to <b>t</b> already registered and -1 if
+ *  <b>t</b> cannot be added due to conflicts. */
+static int
+transport_resolve_conflicts(transport_t *t)
+{
+  /* This is how we resolve transport conflicts:
+
+     If there is already a transport with the same name and addrport,
+     we either have duplicate torrc lines OR we are here post-HUP and
+     this transport was here pre-HUP as well. In any case, mark the
+     old transport so that it doesn't get removed and ignore the new
+     one. Our caller has to free the new transport so we return '1' to
+     signify this.
+
+     If there is already a transport with the same name but different
+     addrport:
+     * if it's marked for removal, it means that it either has a lower
+     priority than 't' in torrc (otherwise the mark would have been
+     cleared by the paragraph above), or it doesn't exist at all in
+     the post-HUP torrc. We destroy the old transport and register 't'.
+     * if it's *not* marked for removal, it means that it was newly
+     added in the post-HUP torrc or that it's of higher priority, in
+     this case we ignore 't'. */
+  transport_t *t_tmp = transport_get_by_name(t->name);
+  if (t_tmp) { /* same name */
+    if (tor_addr_eq(&t->addr, &t_tmp->addr) && (t->port == t_tmp->port)) {
+      /* same name *and* addrport */
+      t_tmp->marked_for_removal = 0;
+      return 1;
+    } else { /* same name but different addrport */
+      if (t_tmp->marked_for_removal) { /* marked for removal */
+        log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' "
+                   "but there was already a transport marked for deletion at "
+                   "'%s:%u'. We deleted the old transport and registered the "
+                   "new one.", t->name, fmt_addr(&t->addr), t->port,
+                   fmt_addr(&t_tmp->addr), t_tmp->port);
+        smartlist_remove(transport_list, t_tmp);
+        transport_free(t_tmp);
+      } else { /* *not* marked for removal */
+        log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' "
+                   "but the same transport already exists at '%s:%u'. "
+                   "Skipping.", t->name, fmt_addr(&t->addr), t->port,
+                   fmt_addr(&t_tmp->addr), t_tmp->port);
+        return -1;
+      }
+    }
+  }
 
 
-  smartlist_add(transport_list, t);
   return 0;
   return 0;
 }
 }
 
 
-/** Warns the user of possible pluggable transport misconfiguration. */
-void
+/** Add transport <b>t</b> to the internal list of pluggable
+ *  transports.
+ *  Returns 0 if the transport was added correctly, 1 if the same
+ *  transport was already registered (in this case the caller must
+ *  free the transport) and -1 if there was an error.  */
+int
+transport_add(transport_t *t)
+{
+  int r;
+  tor_assert(t);
+
+  r = transport_resolve_conflicts(t);
+
+  switch (r) {
+  case 0: /* should register transport */
+    if (!transport_list)
+      transport_list = smartlist_create();
+    smartlist_add(transport_list, t);
+    return 0;
+  default: /* let our caller know the return code */
+    return r;
+  }
+}
+
+/** 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)
+{
+  transport_t *t = transport_create(addr, port, name, socks_ver);
+
+  int r = transport_add(t);
+
+  switch (r) {
+  case -1:
+  default:
+    log_notice(LD_GENERAL, "Could not add transport %s at %s:%u. Skipping.",
+               t->name, fmt_addr(&t->addr), t->port);
+    transport_free(t);
+    return -1;
+  case 1:
+    log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.",
+             t->name, fmt_addr(&t->addr), t->port);
+     transport_free(t); /* falling */
+     return 0;
+  case 0:
+    log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.",
+             t->name, fmt_addr(&t->addr), t->port);
+    return 0;
+  }
+}
+
+/** Warn the user of possible pluggable transport misconfiguration.
+ *  Return 0 if the validation happened, -1 if we should postpone the
+ *  validation. */
+int
 validate_pluggable_transports_config(void)
 validate_pluggable_transports_config(void)
 {
 {
-  if (bridge_list) {
+  /* Don't validate if managed proxies are not yet fully configured. */
+  if (bridge_list && !pt_proxies_configuration_pending()) {
     SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
     SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
       /* Skip bridges without transports. */
       /* Skip bridges without transports. */
       if (!b->transport_name)
       if (!b->transport_name)
@@ -4683,6 +4806,10 @@ validate_pluggable_transports_config(void)
                  "corresponding ClientTransportPlugin line.",
                  "corresponding ClientTransportPlugin line.",
                  b->transport_name);
                  b->transport_name);
     } SMARTLIST_FOREACH_END(b);
     } SMARTLIST_FOREACH_END(b);
+
+    return 0;
+  } else {
+    return -1;
   }
   }
 }
 }
 
 
@@ -4913,6 +5040,11 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
   if (!bridge_list)
   if (!bridge_list)
     return;
     return;
 
 
+  /* If we still have unconfigured managed proxies, don't go and
+     connect to a bridge. */
+  if (pt_proxies_configuration_pending())
+    return;
+
   SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
   SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
     {
     {
       if (!download_status_is_ready(&bridge->fetch_status, now,
       if (!download_status_is_ready(&bridge->fetch_status, now,

+ 14 - 1
src/or/circuitbuild.h

@@ -22,6 +22,9 @@ typedef struct {
   tor_addr_t addr;
   tor_addr_t addr;
   /** Port of proxy */
   /** Port of proxy */
   uint16_t port;
   uint16_t port;
+  /** Boolean: We are re-parsing our transport list, and we are going to remove
+   * this one if we don't find it in the list of configured transports. */
+  unsigned marked_for_removal : 1;
 } transport_t;
 } transport_t;
 
 
 char *circuit_list_path(origin_circuit_t *circ, int verbose);
 char *circuit_list_path(origin_circuit_t *circ, int verbose);
@@ -77,6 +80,9 @@ int getinfo_helper_entry_guards(control_connection_t *conn,
 
 
 void mark_bridge_list(void);
 void mark_bridge_list(void);
 void sweep_bridge_list(void);
 void sweep_bridge_list(void);
+void mark_transport_list(void);
+void sweep_transport_list(void);
+
 int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
 int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
 int node_is_a_configured_bridge(const node_t *node);
 int node_is_a_configured_bridge(const node_t *node);
 void learned_router_identity(const tor_addr_t *addr, uint16_t port,
 void learned_router_identity(const tor_addr_t *addr, uint16_t port,
@@ -142,9 +148,16 @@ int circuit_build_times_get_bw_scale(networkstatus_t *ns);
 void clear_transport_list(void);
 void clear_transport_list(void);
 int transport_add_from_config(const tor_addr_t *addr, uint16_t port,
 int transport_add_from_config(const tor_addr_t *addr, uint16_t port,
                                const char *name, int socks_ver);
                                const char *name, int socks_ver);
+int transport_add(transport_t *t);
+void transport_free(transport_t *transport);
+transport_t *transport_create(const tor_addr_t *addr, uint16_t port,
+                                      const char *name, int socks_ver);
+
 int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
 int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
                                       const transport_t **transport);
                                       const transport_t **transport);
-void validate_pluggable_transports_config(void);
+transport_t *transport_get_by_name(const char *name);
+
+int validate_pluggable_transports_config(void);
 
 
 #endif
 #endif
 
 

+ 453 - 48
src/or/config.c

@@ -33,7 +33,9 @@
 #include "rendservice.h"
 #include "rendservice.h"
 #include "rephist.h"
 #include "rephist.h"
 #include "router.h"
 #include "router.h"
+#include "util.h"
 #include "routerlist.h"
 #include "routerlist.h"
+#include "transports.h"
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
 #include <shlobj.h>
 #include <shlobj.h>
 #endif
 #endif
@@ -301,6 +303,7 @@ static config_var_t _option_vars[] = {
   V(HTTPProxyAuthenticator,      STRING,   NULL),
   V(HTTPProxyAuthenticator,      STRING,   NULL),
   V(HTTPSProxy,                  STRING,   NULL),
   V(HTTPSProxy,                  STRING,   NULL),
   V(HTTPSProxyAuthenticator,     STRING,   NULL),
   V(HTTPSProxyAuthenticator,     STRING,   NULL),
+  VAR("ServerTransportPlugin",   LINELIST, ServerTransportPlugin,  NULL),
   V(Socks4Proxy,                 STRING,   NULL),
   V(Socks4Proxy,                 STRING,   NULL),
   V(Socks5Proxy,                 STRING,   NULL),
   V(Socks5Proxy,                 STRING,   NULL),
   V(Socks5ProxyUsername,         STRING,   NULL),
   V(Socks5ProxyUsername,         STRING,   NULL),
@@ -477,6 +480,9 @@ static config_var_t _state_vars[] = {
   VAR("EntryGuardAddedBy",       LINELIST_S,  EntryGuards,             NULL),
   VAR("EntryGuardAddedBy",       LINELIST_S,  EntryGuards,             NULL),
   V(EntryGuards,                 LINELIST_V,  NULL),
   V(EntryGuards,                 LINELIST_V,  NULL),
 
 
+  VAR("TransportProxy",               LINELIST_S, TransportProxies, NULL),
+  V(TransportProxies,                 LINELIST_V, NULL),
+
   V(BWHistoryReadEnds,                ISOTIME,  NULL),
   V(BWHistoryReadEnds,                ISOTIME,  NULL),
   V(BWHistoryReadInterval,            UINT,     "900"),
   V(BWHistoryReadInterval,            UINT,     "900"),
   V(BWHistoryReadValues,              CSV,      ""),
   V(BWHistoryReadValues,              CSV,      ""),
@@ -503,7 +509,6 @@ static config_var_t _state_vars[] = {
   V(CircuitBuildAbandonedCount,       UINT,     "0"),
   V(CircuitBuildAbandonedCount,       UINT,     "0"),
   VAR("CircuitBuildTimeBin",          LINELIST_S, BuildtimeHistogram, NULL),
   VAR("CircuitBuildTimeBin",          LINELIST_S, BuildtimeHistogram, NULL),
   VAR("BuildtimeHistogram",           LINELIST_V, BuildtimeHistogram, NULL),
   VAR("BuildtimeHistogram",           LINELIST_V, BuildtimeHistogram, NULL),
-
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
 };
 };
 
 
@@ -581,6 +586,8 @@ static int check_nickname_list(const char *lst, const char *name, char **msg);
 
 
 static int parse_bridge_line(const char *line, int validate_only);
 static int parse_bridge_line(const char *line, int validate_only);
 static int parse_client_transport_line(const char *line, int validate_only);
 static int parse_client_transport_line(const char *line, int validate_only);
+
+static int parse_server_transport_line(const char *line, int validate_only);
 static int parse_dir_server_line(const char *line,
 static int parse_dir_server_line(const char *line,
                                  dirinfo_type_t required_type,
                                  dirinfo_type_t required_type,
                                  int validate_only);
                                  int validate_only);
@@ -1276,18 +1283,6 @@ options_act(const or_options_t *old_options)
   if (consider_adding_dir_authorities(options, old_options) < 0)
   if (consider_adding_dir_authorities(options, old_options) < 0)
     return -1;
     return -1;
 
 
-  clear_transport_list();
-  if (options->ClientTransportPlugin) {
-    for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
-      if (parse_client_transport_line(cl->value, 0)<0) {
-        log_warn(LD_BUG,
-                 "Previously validated ClientTransportPlugin line "
-                 "could not be added!");
-        return -1;
-      }
-    }
-  }
-
   if (options->Bridges) {
   if (options->Bridges) {
     mark_bridge_list();
     mark_bridge_list();
     for (cl = options->Bridges; cl; cl = cl->next) {
     for (cl = options->Bridges; cl; cl = cl->next) {
@@ -1300,11 +1295,6 @@ options_act(const or_options_t *old_options)
     sweep_bridge_list();
     sweep_bridge_list();
   }
   }
 
 
-  /* If we have pluggable transport related options enabled, see if we
-     should warn the user about potential configuration problems. */
-  if (options->Bridges || options->ClientTransportPlugin)
-    validate_pluggable_transports_config();
-
   if (running_tor && rend_config_services(options, 0)<0) {
   if (running_tor && rend_config_services(options, 0)<0) {
     log_warn(LD_BUG,
     log_warn(LD_BUG,
        "Previously validated hidden services line could not be added!");
        "Previously validated hidden services line could not be added!");
@@ -1324,6 +1314,32 @@ options_act(const or_options_t *old_options)
     rep_hist_load_mtbf_data(time(NULL));
     rep_hist_load_mtbf_data(time(NULL));
   }
   }
 
 
+  mark_transport_list();
+  pt_prepare_proxy_list_for_config_read();
+  if (options->ClientTransportPlugin) {
+    for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
+      if (parse_client_transport_line(cl->value, 0)<0) {
+        log_warn(LD_BUG,
+                 "Previously validated ClientTransportPlugin line "
+                 "could not be added!");
+        return -1;
+      }
+    }
+  }
+
+  if (options->ServerTransportPlugin) {
+    for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
+      if (parse_server_transport_line(cl->value, 0)<0) {
+        log_warn(LD_BUG,
+                 "Previously validated ServerTransportPlugin line "
+                 "could not be added!");
+        return -1;
+      }
+    }
+  }
+  sweep_transport_list();
+  sweep_proxy_list();
+
   /* Bail out at this point if we're not going to be a client or server:
   /* Bail out at this point if we're not going to be a client or server:
    * we want to not fork, and to log stuff to stderr. */
    * we want to not fork, and to log stuff to stderr. */
   if (!running_tor)
   if (!running_tor)
@@ -3727,14 +3743,19 @@ options_validate(or_options_t *old_options, or_options_t *options,
   if (options->UseBridges && !options->TunnelDirConns)
   if (options->UseBridges && !options->TunnelDirConns)
     REJECT("If you set UseBridges, you must set TunnelDirConns.");
     REJECT("If you set UseBridges, you must set TunnelDirConns.");
 
 
+  for (cl = options->Bridges; cl; cl = cl->next) {
+    if (parse_bridge_line(cl->value, 1)<0)
+      REJECT("Bridge line did not parse. See logs for details.");
+  }
+
   for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
   for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
     if (parse_client_transport_line(cl->value, 1)<0)
     if (parse_client_transport_line(cl->value, 1)<0)
       REJECT("Transport line did not parse. See logs for details.");
       REJECT("Transport line did not parse. See logs for details.");
   }
   }
 
 
-  for (cl = options->Bridges; cl; cl = cl->next) {
-    if (parse_bridge_line(cl->value, 1)<0)
-      REJECT("Bridge line did not parse. See logs for details.");
+  for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
+    if (parse_server_transport_line(cl->value, 1)<0)
+      REJECT("Server transport line did not parse. See logs for details.");
   }
   }
 
 
   if (options->ConstrainedSockets) {
   if (options->ConstrainedSockets) {
@@ -4702,72 +4723,243 @@ parse_bridge_line(const char *line, int validate_only)
   SMARTLIST_FOREACH(items, char*, s, tor_free(s));
   SMARTLIST_FOREACH(items, char*, s, tor_free(s));
   smartlist_free(items);
   smartlist_free(items);
   tor_free(addrport);
   tor_free(addrport);
-  tor_free(fingerprint);
   tor_free(transport_name);
   tor_free(transport_name);
+  tor_free(fingerprint);
   return r;
   return r;
 }
 }
 
 
 /** Read the contents of a ClientTransportPlugin line from
 /** Read the contents of a ClientTransportPlugin line from
  * <b>line</b>. Return 0 if the line is well-formed, and -1 if it
  * <b>line</b>. Return 0 if the line is well-formed, and -1 if it
- * isn't. If <b>validate_only</b> is 0, and the line is well-formed,
- * then add the transport described in the line to our internal
- * transport list.
-*/
+ * isn't.
+ *
+ * If <b>validate_only</b> is 0, and the line is well-formed:
+ * - 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
 static int
 parse_client_transport_line(const char *line, int validate_only)
 parse_client_transport_line(const char *line, int validate_only)
 {
 {
   smartlist_t *items = NULL;
   smartlist_t *items = NULL;
   int r;
   int r;
-  char *socks_ver_str=NULL;
-  char *name=NULL;
+  char *field2=NULL;
+
+  const char *transports=NULL;
+  smartlist_t *transport_list=NULL;
   char *addrport=NULL;
   char *addrport=NULL;
-  int socks_ver;
   tor_addr_t addr;
   tor_addr_t addr;
   uint16_t port = 0;
   uint16_t port = 0;
+  int socks_ver=PROXY_NONE;
+
+  /* managed proxy options */
+  int is_managed=0;
+  char **proxy_argv=NULL;
+  char **tmp=NULL;
+  int proxy_argc,i;
+
+  int line_length;
 
 
   items = smartlist_create();
   items = smartlist_create();
   smartlist_split_string(items, line, NULL,
   smartlist_split_string(items, line, NULL,
                          SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
                          SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
 
 
-  if (smartlist_len(items) < 3) {
+  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 ClientTransportPlugin line.");
     goto err;
     goto err;
   }
   }
 
 
-  name = smartlist_get(items, 0);
+  /* 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_create();
+  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);
 
 
-  socks_ver_str = smartlist_get(items, 1);
+  /* field2 is either a SOCKS version or "exec" */
+  field2 = smartlist_get(items, 1);
 
 
-  if (!strcmp(socks_ver_str,"socks4"))
+  if (!strcmp(field2,"socks4")) {
     socks_ver = PROXY_SOCKS4;
     socks_ver = PROXY_SOCKS4;
-  else if (!strcmp(socks_ver_str,"socks5"))
+  } else if (!strcmp(field2,"socks5")) {
     socks_ver = PROXY_SOCKS5;
     socks_ver = PROXY_SOCKS5;
-  else {
-    log_warn(LD_CONFIG, "Strange ClientTransportPlugin proxy type '%s'.",
-             socks_ver_str);
+  } else if (!strcmp(field2,"exec")) {
+    is_managed=1;
+  } else {
+    log_warn(LD_CONFIG, "Strange ClientTransportPlugin field '%s'.",
+             field2);
     goto err;
     goto err;
   }
   }
 
 
-  addrport = smartlist_get(items, 2);
+  if (is_managed) { /* managed */
+    if (!validate_only) {  /* if we are not just validating, use the
+                             rest of the line as the argv of the proxy
+                             to be launched */
+      proxy_argc = line_length-2;
+      tor_assert(proxy_argc > 0);
+      proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1));
+      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 NUL pointer, just like execve() likes it*/
 
 
-  if (tor_addr_port_parse(addrport, &addr, &port)<0) {
-    log_warn(LD_CONFIG, "Error parsing transport "
-              "address '%s'", addrport);
+      /* kickstart the thing */
+      pt_kickstart_client_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_parse(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) {
+      transport_add_from_config(&addr, port, smartlist_get(transport_list, 0),
+                                socks_ver);
+
+      log_info(LD_DIR, "Transport '%s' found at %s:%d",
+               transports, fmt_addr(&addr), (int)port);
+    }
+  }
+
+  r = 0;
+  goto done;
+
+ err:
+  r = -1;
+
+ done:
+  SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+  smartlist_free(items);
+  SMARTLIST_FOREACH(transport_list, char*, s, tor_free(s));
+  smartlist_free(transport_list);
+
+  return r;
+}
+
+/** 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 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_create();
+  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;
     goto err;
   }
   }
 
 
-  if (!port) {
-    log_warn(LD_CONFIG,
-              "Transport address '%s' has no port.", addrport);
+  /* 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_create();
+  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;
     goto err;
   }
   }
 
 
-  if (!validate_only) {
-    log_debug(LD_DIR, "Transport %s found at %s:%d", name,
-              fmt_addr(&addr), (int)port);
+  if (is_managed) { /* managed */
+    if (!validate_only) {
+      proxy_argc = line_length-2;
+      tor_assert(proxy_argc > 0);
+      proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1));
+      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 NUL pointer, 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;
+    }
 
 
-    if (transport_add_from_config(&addr, port, name, socks_ver) < 0)
+    addrport = smartlist_get(items, 2);
+
+    if (tor_addr_port_parse(addrport, &addr, &port)<0) {
+      log_warn(LD_CONFIG, "Error parsing transport "
+               "address '%s'", addrport);
       goto err;
       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:%d.",
+               transports, fmt_addr(&addr), (int)port);
+    }
   }
   }
 
 
   r = 0;
   r = 0;
@@ -4779,6 +4971,9 @@ parse_client_transport_line(const char *line, int validate_only)
  done:
  done:
   SMARTLIST_FOREACH(items, char*, s, tor_free(s));
   SMARTLIST_FOREACH(items, char*, s, tor_free(s));
   smartlist_free(items);
   smartlist_free(items);
+  SMARTLIST_FOREACH(transport_list, char*, s, tor_free(s));
+  smartlist_free(transport_list);
+
   return r;
   return r;
 }
 }
 
 
@@ -5721,6 +5916,69 @@ options_get_datadir_fname2_suffix(const or_options_t *options,
   return fname;
   return fname;
 }
 }
 
 
+/** Return true if <b>line</b> is a valid state TransportProxy line.
+ *  Return false otherwise. */
+static int
+state_transport_line_is_valid(const char *line)
+{
+  smartlist_t *items = NULL;
+  char *addrport=NULL;
+  tor_addr_t addr;
+  uint16_t port = 0;
+  int r;
+
+  items = smartlist_create();
+  smartlist_split_string(items, line, NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+  if (smartlist_len(items) != 2) {
+    log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line.");
+    goto err;
+  }
+
+  addrport = smartlist_get(items, 1);
+  if (tor_addr_port_parse(addrport, &addr, &port) < 0) {
+    log_warn(LD_CONFIG, "state: Could not parse addrport.");
+    goto err;
+  }
+
+  if (!port) {
+    log_warn(LD_CONFIG, "state: Transport line did not contain port.");
+    goto err;
+  }
+
+  r = 1;
+  goto done;
+
+ err:
+  r = 0;
+
+ done:
+  SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+  smartlist_free(items);
+  return r;
+}
+
+/** Return 0 if all TransportProxy lines in <b>state</b> are well
+ *  formed. Otherwise, return -1. */
+static int
+validate_transports_in_state(or_state_t *state)
+{
+  int broken = 0;
+  config_line_t *line;
+
+  for (line = state->TransportProxies ; line ; line = line->next) {
+    tor_assert(!strcmp(line->key, "TransportProxy"));
+    if (!state_transport_line_is_valid(line->value)<0)
+      broken = 1;
+  }
+
+  if (broken)
+    log_warn(LD_CONFIG, "state: State file seems to be broken.");
+
+  return 0;
+}
+
 /** Return 0 if every setting in <b>state</b> is reasonable, and a
 /** Return 0 if every setting in <b>state</b> is reasonable, and a
  * permissible transition from <b>old_state</b>.  Else warn and return -1.
  * permissible transition from <b>old_state</b>.  Else warn and return -1.
  * Should have no side effects, except for normalizing the contents of
  * Should have no side effects, except for normalizing the contents of
@@ -5739,6 +5997,9 @@ or_state_validate(or_state_t *old_state, or_state_t *state,
   if (entry_guards_parse_state(state, 0, msg)<0)
   if (entry_guards_parse_state(state, 0, msg)<0)
     return -1;
     return -1;
 
 
+  if (validate_transports_in_state(state)<0)
+    return -1;
+
   return 0;
   return 0;
 }
 }
 
 
@@ -5971,6 +6232,150 @@ or_state_save(time_t now)
   return 0;
   return 0;
 }
 }
 
 
+/** Return the config line for transport <b>transport</b> in the current state.
+ *  Return NULL if there is no config line for <b>transport</b>. */
+static config_line_t *
+get_transport_in_state_by_name(const char *transport)
+{
+  or_state_t *or_state = get_or_state();
+  config_line_t *line;
+  config_line_t *ret = NULL;
+  smartlist_t *items = NULL;
+
+  for (line = or_state->TransportProxies ; line ; line = line->next) {
+    tor_assert(!strcmp(line->key, "TransportProxy"));
+
+    items = smartlist_create();
+    smartlist_split_string(items, line->value, NULL,
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+    if (smartlist_len(items) != 2) /* broken state */
+      goto done;
+
+    if (!strcmp(smartlist_get(items, 0), transport)) {
+      ret = line;
+      goto done;
+    }
+
+    SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+    smartlist_free(items);
+    items = NULL;
+  }
+
+ done:
+  if (items) {
+    SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+    smartlist_free(items);
+  }
+  return ret;
+}
+
+/** Return string containing the address:port part of the
+ *  TransportProxy <b>line</b> for transport <b>transport</b>.
+ *  If the line is corrupted, return NULL. */
+static const char *
+get_transport_bindaddr(const char *line, const char *transport)
+{
+  char *line_tmp = NULL;
+
+  if (strlen(line) < strlen(transport) + 2) {
+    goto broken_state;
+  } else {
+    /* line should start with the name of the transport and a space.
+       (for example, "obfs2 127.0.0.1:47245") */
+    tor_asprintf(&line_tmp, "%s ", transport);
+    if (strcmpstart(line, line_tmp))
+      goto broken_state;
+
+    tor_free(line_tmp);
+    return (line+strlen(transport)+1);
+  }
+
+ broken_state:
+  tor_free(line_tmp);
+  return NULL;
+}
+
+/** Return a static string containing the address:port a proxy
+ *  transport should bind on. */
+const char *
+get_bindaddr_for_transport(const char *transport)
+{
+  static const char default_addrport[] = "127.0.0.1:0";
+  const char *bindaddr = NULL;
+
+  config_line_t *line = get_transport_in_state_by_name(transport);
+  if (!line)
+    return default_addrport;
+
+  bindaddr = get_transport_bindaddr(line->value, transport);
+
+  return bindaddr ? bindaddr : default_addrport;
+}
+
+/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
+    state */
+void
+save_transport_to_state(const char *transport,
+                        const tor_addr_t *addr, uint16_t port)
+{
+  or_state_t *state = get_or_state();
+
+  char *transport_addrport=NULL;
+
+  /** find where to write on the state */
+  config_line_t **next, *line;
+
+  /* see if this transport is already stored in state */
+  config_line_t *transport_line =
+    get_transport_in_state_by_name(transport);
+
+  if (transport_line) { /* if transport already exists in state... */
+    const char *prev_bindaddr = /* get its addrport... */
+      get_transport_bindaddr(transport_line->value, transport);
+    tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)port);
+
+    /* if transport in state has the same address as this one, life is good */
+    if (!strcmp(prev_bindaddr, transport_addrport)) {
+      log_info(LD_CONFIG, "Transport seems to have spawned on its usual "
+               "address:port.");
+      goto done;
+    } else { /* if addrport in state is different than the one we got */
+      log_info(LD_CONFIG, "Transport seems to have spawned on different "
+               "address:port. Let's update the state file with the new "
+               "address:port");
+      tor_free(transport_line->value); /* free the old line */
+      tor_asprintf(&transport_line->value, "%s %s:%d", transport,
+                   fmt_addr(addr),
+                   (int) port); /* replace old addrport line with new line */
+    }
+  } else { /* never seen this one before; save it in state for next time */
+    log_info(LD_CONFIG, "It's the first time we see this transport. "
+             "Let's save its address:port");
+    next = &state->TransportProxies;
+    /* find the last TransportProxy line in the state and point 'next'
+       right after it  */
+    line = state->TransportProxies;
+    while (line) {
+      next = &(line->next);
+      line = line->next;
+    }
+
+    /* allocate space for the new line and fill it in */
+    *next = line = tor_malloc_zero(sizeof(config_line_t));
+    line->key = tor_strdup("TransportProxy");
+    tor_asprintf(&line->value, "%s %s:%d", transport,
+                 fmt_addr(addr), (int) port);
+
+    next = &(line->next);
+  }
+
+  if (!get_options()->AvoidDiskWrites)
+    or_state_mark_dirty(state, 0);
+
+ done:
+  tor_free(transport_addrport);
+}
+
 /** Given a file name check to see whether the file exists but has not been
 /** Given a file name check to see whether the file exists but has not been
  * modified for a very long time.  If so, remove it. */
  * modified for a very long time.  If so, remove it. */
 void
 void

+ 5 - 0
src/or/config.h

@@ -68,6 +68,11 @@ const smartlist_t *get_configured_client_ports(void);
 
 
 int options_need_geoip_info(const or_options_t *options,
 int options_need_geoip_info(const or_options_t *options,
                             const char **reason_out);
                             const char **reason_out);
+
+void save_transport_to_state(const char *transport_name,
+                             const tor_addr_t *addr, uint16_t port);
+const char *get_bindaddr_for_transport(const char *transport);
+
 int getinfo_helper_config(control_connection_t *conn,
 int getinfo_helper_config(control_connection_t *conn,
                           const char *question, char **answer,
                           const char *question, char **answer,
                           const char **errmsg);
                           const char **errmsg);

+ 6 - 3
src/or/connection.c

@@ -2020,9 +2020,12 @@ retry_all_listeners(smartlist_t *replaced_conns,
   return retval;
   return retval;
 }
 }
 
 
-/** Return 1 if we should apply rate limiting to <b>conn</b>,
- * and 0 otherwise. Right now this just checks if it's an internal
- * IP address or an internal connection. */
+/** Return 1 if we should apply rate limiting to <b>conn</b>, and 0
+ * otherwise.
+ * Right now this just checks if it's an internal IP address or an
+ * internal connection. We also check if the connection uses pluggable
+ * transports, since we should then limit it even if it comes from an
+ * internal IP address. */
 static int
 static int
 connection_is_rate_limited(connection_t *conn)
 connection_is_rate_limited(connection_t *conn)
 {
 {

+ 19 - 2
src/or/main.c

@@ -37,6 +37,7 @@
 #include "ntmain.h"
 #include "ntmain.h"
 #include "onion.h"
 #include "onion.h"
 #include "policies.h"
 #include "policies.h"
+#include "transports.h"
 #include "relay.h"
 #include "relay.h"
 #include "rendclient.h"
 #include "rendclient.h"
 #include "rendcommon.h"
 #include "rendcommon.h"
@@ -1086,7 +1087,9 @@ run_scheduled_events(time_t now)
   static int should_init_bridge_stats = 1;
   static int should_init_bridge_stats = 1;
   static time_t time_to_retry_dns_init = 0;
   static time_t time_to_retry_dns_init = 0;
   static time_t time_to_next_heartbeat = 0;
   static time_t time_to_next_heartbeat = 0;
+  static int has_validated_pt = 0;
   const or_options_t *options = get_options();
   const or_options_t *options = get_options();
+
   int is_server = server_mode(options);
   int is_server = server_mode(options);
   int i;
   int i;
   int have_dir_info;
   int have_dir_info;
@@ -1463,7 +1466,7 @@ run_scheduled_events(time_t now)
     }
     }
   }
   }
 
 
-  /** 10b. write bridge networkstatus file to disk */
+  /** 10. write bridge networkstatus file to disk */
   if (options->BridgeAuthoritativeDir &&
   if (options->BridgeAuthoritativeDir &&
       time_to_write_bridge_status_file < now) {
       time_to_write_bridge_status_file < now) {
     networkstatus_dump_bridge_status_to_file(now);
     networkstatus_dump_bridge_status_to_file(now);
@@ -1471,6 +1474,7 @@ run_scheduled_events(time_t now)
     time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
     time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
   }
   }
 
 
+  /** 11. check the port forwarding app */
   if (time_to_check_port_forwarding < now &&
   if (time_to_check_port_forwarding < now &&
       options->PortForwarding &&
       options->PortForwarding &&
       is_server) {
       is_server) {
@@ -1482,7 +1486,19 @@ run_scheduled_events(time_t now)
     time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
     time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
   }
   }
 
 
-  /** 11. write the heartbeat message */
+  /** 11b. check pending unconfigured managed proxies */
+  if (pt_proxies_configuration_pending())
+    pt_configure_remaining_proxies();
+
+  /** 11c. validate pluggable transports configuration if we need to */
+  if (!has_validated_pt &&
+      (options->Bridges || options->ClientTransportPlugin)) {
+    if (validate_pluggable_transports_config() == 0) {
+      has_validated_pt = 1;
+    }
+  }
+
+  /** 12. write the heartbeat message */
   if (options->HeartbeatPeriod &&
   if (options->HeartbeatPeriod &&
       time_to_next_heartbeat < now) {
       time_to_next_heartbeat < now) {
     log_heartbeat(now);
     log_heartbeat(now);
@@ -2341,6 +2357,7 @@ tor_free_all(int postfork)
   clear_pending_onions();
   clear_pending_onions();
   circuit_free_all();
   circuit_free_all();
   entry_guards_free_all();
   entry_guards_free_all();
+  pt_free_all();
   connection_free_all();
   connection_free_all();
   buf_shrink_freelists(1);
   buf_shrink_freelists(1);
   memarea_clear_freelist();
   memarea_clear_freelist();

+ 5 - 0
src/or/or.h

@@ -2879,6 +2879,9 @@ typedef struct {
   config_line_t *ClientTransportPlugin; /**< List of client
   config_line_t *ClientTransportPlugin; /**< List of client
                                            transport plugins. */
                                            transport plugins. */
 
 
+  config_line_t *ServerTransportPlugin; /**< List of client
+                                           transport plugins. */
+
   int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
   int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
                     * this explicit so we can change how we behave in the
                     * this explicit so we can change how we behave in the
                     * future. */
                     * future. */
@@ -3350,6 +3353,8 @@ typedef struct {
   /** A list of Entry Guard-related configuration lines. */
   /** A list of Entry Guard-related configuration lines. */
   config_line_t *EntryGuards;
   config_line_t *EntryGuards;
 
 
+  config_line_t *TransportProxies;
+
   /** These fields hold information on the history of bandwidth usage for
   /** These fields hold information on the history of bandwidth usage for
    * servers.  The "Ends" fields hold the time when we last updated the
    * servers.  The "Ends" fields hold the time when we last updated the
    * bandwidth usage. The "Interval" fields hold the granularity, in seconds,
    * bandwidth usage. The "Interval" fields hold the granularity, in seconds,

+ 1048 - 0
src/or/transports.c

@@ -0,0 +1,1048 @@
+/* Copyright (c) 2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file transports.c
+ * \brief Pluggable Transports related code.
+ **/
+
+#define PT_PRIVATE
+#include "or.h"
+#include "config.h"
+#include "circuitbuild.h"
+#include "transports.h"
+#include "util.h"
+
+static void set_managed_proxy_environment(char ***envp,
+                                          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);
+
+static void handle_finished_proxy(managed_proxy_t *mp);
+static void configure_proxy(managed_proxy_t *mp);
+
+static void parse_method_error(const char *line, int is_server_method);
+#define parse_server_method_error(l) parse_method_error(l, 1)
+#define parse_client_method_error(l) parse_method_error(l, 0)
+
+static INLINE void free_execve_args(char **arg);
+
+/** Managed proxy protocol strings */
+#define PROTO_ENV_ERROR "ENV-ERROR"
+#define PROTO_NEG_SUCCESS "VERSION"
+#define PROTO_NEG_FAIL "VERSION-ERROR no-version"
+#define PROTO_CMETHOD "CMETHOD"
+#define PROTO_SMETHOD "SMETHOD"
+#define PROTO_CMETHOD_ERROR "CMETHOD-ERROR"
+#define PROTO_SMETHOD_ERROR "SMETHOD-ERROR"
+#define PROTO_CMETHODS_DONE "CMETHODS DONE"
+#define PROTO_SMETHODS_DONE "SMETHODS DONE"
+
+/* The smallest valid managed proxy protocol line that can
+   appear. It's the size of "VERSION 1" */
+#define SMALLEST_MANAGED_LINE_SIZE 9
+
+/** Number of environment variables for managed proxy clients/servers. */
+#define ENVIRON_SIZE_CLIENT 5
+#define ENVIRON_SIZE_SERVER 8
+
+/** The first and only supported - at the moment - configuration
+    protocol version. */
+#define PROTO_VERSION_ONE 1
+
+/** List of unconfigured managed proxies. */
+static smartlist_t *managed_proxy_list = NULL;
+/** Number of still unconfigured proxies. */
+static int unconfigured_proxies_n = 0;
+
+/** "The main idea is:"
+
+    Each managed proxy is represented by a 'managed_proxy_t'.
+    Each managed proxy can support multiple transports.
+    Each managed proxy gets configured through a multistep process.
+
+    'managed_proxy_list' contains all the managed proxies this tor
+    instance is supporting.
+    In the 'managed_proxy_list' there are 'unconfigured_proxies_n'
+    managed proxies that are still unconfigured.
+
+    In every run_scheduled_event() tick, we attempt to launch and then
+    configure the unconfiged managed proxies, using the configuration
+    protocol defined in the 180_pluggable_transport.txt proposal. A
+    managed proxy might need several ticks to get fully configured.
+
+    When a managed proxy is fully configured, we register all its
+    transports to the circuitbuild.c subsystem. At that point the
+    transports are owned by the circuitbuild.c subsystem.
+
+    When a managed proxy fails to follow the 180 configuration
+    protocol, it gets marked as broken and gets destroyed.
+
+    "In a little more technical detail:"
+
+    While we are serially parsing torrc, we store all the transports
+    that a proxy should spawn in its 'transports_to_launch' element.
+
+    When we finish reading the torrc, we spawn the managed proxy and
+    expect {S,C}METHOD lines from its output. We add transports
+    described by METHOD lines to its 'transports' element, as
+    'transport_t' structs.
+
+    When the managed proxy stops spitting METHOD lines (signified by a
+    '{S,C}METHODS DONE' message) we register all the transports
+    collected to the circuitbuild.c subsystem. At this point, the
+    'transport_t's can be transformed into dangling pointers at any
+    point by the circuitbuild.c subsystem, and so we replace all
+    'transport_t's with strings describing the transport names.  We
+    can still go from a transport name to a 'transport_t' using the
+    fact that transport names uniquely identify 'transport_t's.
+
+    "In even more technical detail I shall describe what happens when
+    the SIGHUP bell tolls:"
+
+    We immediately destroy all unconfigured proxies (We shouldn't have
+    unconfigured proxies in the first place, except when SIGHUP rings
+    immediately after tor is launched.).
+
+    We mark all managed proxies and transports to signify that they
+    must be removed if they don't contribute by the new torrc
+    (marked_for_removal).
+    We also mark all managed proxies to signify that they might need
+    to be restarted so that they end up supporting all the transports
+    the new torrc wants them to support (got_hup).
+    We also clear their 'transports_to_launch' list so that we can put
+    there the transports we need to launch according to the new torrc.
+
+    We then start parsing torrc again.
+
+    Everytime we encounter a transport line using a known pre-SIGHUP
+    managed proxy, we cleanse that proxy from the removal mark.
+
+    We also mark it as unconfigured so that on the next scheduled
+    events tick, we investigate whether we need to restart the proxy
+    so that it also spawns the new transports.
+    If the post-SIGHUP 'transports_to_launch' list is identical to the
+    pre-SIGHUP one, it means that no changes were introduced to this
+    proxy during the SIGHUP and no restart has to take place.
+
+    During the post-SIGHUP torrc parsing, we unmark all transports
+    spawned by managed proxies that we find in our torrc.
+    We do that so that if we don't need to restart a managed proxy, we
+    can continue using its old transports normally.
+    If we end up restarting the proxy, we destroy and unregister all
+    old transports from the circuitbuild.c subsystem.
+*/
+
+/** Return true if there are still unconfigured managed proxies. */
+int
+pt_proxies_configuration_pending(void)
+{
+  return !! unconfigured_proxies_n;
+}
+
+/** Return true if <b>mp</b> has the same argv as <b>proxy_argv</b> */
+static int
+managed_proxy_has_argv(const managed_proxy_t *mp, char **proxy_argv)
+{
+  char **tmp1=proxy_argv;
+  char **tmp2=mp->argv;
+
+  tor_assert(tmp1);
+  tor_assert(tmp2);
+
+  while (*tmp1 && *tmp2) {
+    if (strcmp(*tmp1++, *tmp2++))
+      return 0;
+  }
+
+  if (!*tmp1 && !*tmp2)
+    return 1;
+
+  return 0;
+}
+
+/** Return a managed proxy with the same argv as <b>proxy_argv</b>.
+ *  If no such managed proxy exists, return NULL. */
+static managed_proxy_t *
+get_managed_proxy_by_argv_and_type(char **proxy_argv, int is_server)
+{
+  if (!managed_proxy_list)
+    return NULL;
+
+  SMARTLIST_FOREACH_BEGIN(managed_proxy_list,  managed_proxy_t *, mp) {
+    if (managed_proxy_has_argv(mp, proxy_argv) &&
+        mp->is_server == is_server)
+      return mp;
+  } SMARTLIST_FOREACH_END(mp);
+
+  return NULL;
+}
+
+/** Add <b>transport</b> to managed proxy <b>mp</b>. */
+static void
+add_transport_to_proxy(const char *transport, managed_proxy_t *mp)
+{
+  tor_assert(mp->transports_to_launch);
+  if (!smartlist_string_isin(mp->transports_to_launch, transport))
+    smartlist_add(mp->transports_to_launch, tor_strdup(transport));
+}
+
+/** Called when a SIGHUP occurs. Returns true if managed proxy
+ *  <b>mp</b> needs to be restarted after the SIGHUP, based on the new
+ *  torrc. */
+static int
+proxy_needs_restart(const managed_proxy_t *mp)
+{
+  /* mp->transport_to_launch is populated with the names of the
+     transports that must be launched *after* the SIGHUP.
+     mp->transports is populated with the names of the transports that
+     were launched *before* the SIGHUP.
+
+     If the two lists contain the same strings, we don't need to
+     restart the proxy, since it already does what we want. */
+
+  tor_assert(smartlist_len(mp->transports_to_launch) > 0);
+  tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+
+  if (smartlist_len(mp->transports_to_launch) != smartlist_len(mp->transports))
+    goto needs_restart;
+
+  SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t_t_l) {
+    if (!smartlist_string_isin(mp->transports, t_t_l))
+      goto needs_restart;
+
+  } SMARTLIST_FOREACH_END(t_t_l);
+
+  return 0;
+
+ needs_restart:
+  return 1;
+}
+
+/** Managed proxy <b>mp</b> must be restarted. Do all the necessary
+ *  preparations and then flag its state so that it will be relaunched
+ *  in the next tick. */
+static void
+proxy_prepare_for_restart(managed_proxy_t *mp)
+{
+  transport_t *t_tmp = NULL;
+
+  tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+  tor_assert(mp->pid);
+
+  /* kill the old obfsproxy process */
+  tor_terminate_process(mp->pid);
+  mp->pid = 0;
+  fclose(mp->_stdout);
+
+  /* destroy all its old transports. we no longer use them. */
+  SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) {
+    t_tmp = transport_get_by_name(t_name);
+    if (t_tmp)
+      t_tmp->marked_for_removal = 1;
+  } SMARTLIST_FOREACH_END(t_name);
+  sweep_transport_list();
+
+  /* free the transport names in mp->transports */
+  SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name));
+  smartlist_clear(mp->transports);
+
+  /* flag it as an infant proxy so that it gets launched on next tick */
+  mp->conf_state = PT_PROTO_INFANT;
+}
+
+/** Launch managed proxy <b>mp</b>. */
+static int
+launch_managed_proxy(managed_proxy_t *mp)
+{
+  (void) mp;
+  (void) set_managed_proxy_environment;
+  return -1;
+#if 0
+  /* XXXX023 we must reenable this code for managed proxies to work.
+   *   "All it needs" is revision to work with the new tor_spawn_background
+   *   API. */
+  char **envp=NULL;
+  int pid;
+  process_handle_t proc;
+  FILE *stdout_read = NULL;
+  int stdout_pipe=-1, stderr_pipe=-1;
+
+  /* prepare the environment variables for the managed proxy */
+  set_managed_proxy_environment(&envp, mp);
+
+  pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
+                             (const char **)envp, &proc);
+  if (pid < 0) {
+    log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
+             mp->argv[0]);
+    return -1;
+  }
+
+  /* free the memory allocated by set_managed_proxy_environment(). */
+  free_execve_args(envp);
+
+  /* Set stdout/stderr pipes to be non-blocking */
+#ifdef _WIN32
+  {
+    u_long nonblocking = 1;
+    ioctlsocket(stdout_pipe, FIONBIO, &nonblocking);
+  }
+#else
+    fcntl(stdout_pipe, F_SETFL, O_NONBLOCK);
+#endif
+
+  /* Open the buffered IO streams */
+  stdout_read = fdopen(stdout_pipe, "r");
+
+  log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid);
+
+  mp->conf_state = PT_PROTO_LAUNCHED;
+  mp->_stdout = stdout_read;
+  mp->pid = pid;
+#endif
+  return 0;
+}
+
+/** Check if any of the managed proxies we are currently trying to
+ *  configure have anything new to say. This is called from
+ *  run_scheduled_events(). */
+void
+pt_configure_remaining_proxies(void)
+{
+  log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!",
+            unconfigured_proxies_n);
+  SMARTLIST_FOREACH_BEGIN(managed_proxy_list,  managed_proxy_t *, mp) {
+    tor_assert(mp->conf_state != PT_PROTO_BROKEN);
+
+    if (mp->got_hup) {
+      mp->got_hup = 0;
+
+    /* This proxy is marked by a SIGHUP. Check whether we need to
+       restart it. */
+      if (proxy_needs_restart(mp)) {
+        log_info(LD_GENERAL, "Preparing managed proxy for restart.");
+        proxy_prepare_for_restart(mp);
+        continue;
+      } else { /* it doesn't need to be restarted. */
+        log_info(LD_GENERAL, "Nothing changed for managed proxy after HUP: "
+                 "not restarting.");
+        unconfigured_proxies_n--;
+        tor_assert(unconfigured_proxies_n >= 0);
+      }
+
+      continue;
+    }
+
+    /* If the proxy is not fully configured, try to configure it
+       futher. */
+    if (!proxy_configuration_finished(mp))
+      configure_proxy(mp);
+
+  } SMARTLIST_FOREACH_END(mp);
+}
+
+/** Attempt to continue configuring managed proxy <b>mp</b>. */
+static void
+configure_proxy(managed_proxy_t *mp)
+{
+  enum stream_status r;
+  char stdout_buf[200];
+
+  /* if we haven't launched the proxy yet, do it now */
+  if (mp->conf_state == PT_PROTO_INFANT) {
+    launch_managed_proxy(mp);
+    return;
+  }
+
+  tor_assert(mp->conf_state != PT_PROTO_INFANT);
+
+  while (1) {
+    r = get_string_from_pipe(mp->_stdout, stdout_buf,
+                             sizeof(stdout_buf) - 1);
+
+    if (r  == IO_STREAM_OKAY) { /* got a line; handle it! */
+      handle_proxy_line((const char *)stdout_buf, mp);
+    } else if (r == IO_STREAM_EAGAIN) { /* check back later */
+      return;
+    } else if (r == IO_STREAM_CLOSED || r == IO_STREAM_TERM) { /* snap! */
+      log_notice(LD_GENERAL, "Managed proxy stream closed. "
+                 "Most probably application stopped running");
+      mp->conf_state = PT_PROTO_BROKEN;
+    } else { /* unknown stream status */
+      log_notice(LD_GENERAL, "Unknown stream status while configuring proxy.");
+    }
+
+    /* if the proxy finished configuring, exit the loop. */
+    if (proxy_configuration_finished(mp)) {
+      handle_finished_proxy(mp);
+      return;
+    }
+  }
+}
+
+/** Register server managed proxy <b>mp</b> transports to state */
+static void
+register_server_proxy(managed_proxy_t *mp)
+{
+  /* After we register this proxy's transports, we switch its
+     mp->transports to a list containing strings of its transport
+     names. (See transports.h) */
+  smartlist_t *sm_tmp = smartlist_create();
+
+  tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
+  SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
+    save_transport_to_state(t->name, &t->addr, t->port);
+    smartlist_add(sm_tmp, tor_strdup(t->name));
+  } SMARTLIST_FOREACH_END(t);
+
+  /* Since server proxies don't register their transports in the
+     circuitbuild.c subsystem, it's our duty to free them when we
+     switch mp->transports to strings. */
+  SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+  smartlist_free(mp->transports);
+
+  mp->transports = sm_tmp;
+}
+
+/** Register all the transports supported by client managed proxy
+ *  <b>mp</b> to the bridge subsystem. */
+static void
+register_client_proxy(managed_proxy_t *mp)
+{
+  int r;
+  /* After we register this proxy's transports, we switch its
+     mp->transports to a list containing strings of its transport
+     names. (See transports.h) */
+  smartlist_t *sm_tmp = smartlist_create();
+
+  tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
+  SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
+    r = transport_add(t);
+    switch (r) {
+    case -1:
+      log_notice(LD_GENERAL, "Could not add transport %s. Skipping.", t->name);
+      transport_free(t);
+      break;
+    case 0:
+      log_info(LD_GENERAL, "Succesfully registered transport %s", t->name);
+      smartlist_add(sm_tmp, tor_strdup(t->name));
+      break;
+    case 1:
+      log_info(LD_GENERAL, "Succesfully registered transport %s", t->name);
+      smartlist_add(sm_tmp, tor_strdup(t->name));
+      transport_free(t);
+      break;
+    }
+  } SMARTLIST_FOREACH_END(t);
+
+  smartlist_free(mp->transports);
+  mp->transports = sm_tmp;
+}
+
+/** Register the transports of managed proxy <b>mp</b>. */
+static INLINE void
+register_proxy(managed_proxy_t *mp)
+{
+  if (mp->is_server)
+    register_server_proxy(mp);
+  else
+    register_client_proxy(mp);
+}
+
+/** Free memory allocated by managed proxy <b>mp</b>. */
+static void
+managed_proxy_destroy(managed_proxy_t *mp)
+{
+  if (mp->conf_state != PT_PROTO_COMPLETED)
+    SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+  else
+    SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name));
+
+  /* free the transports smartlist */
+  smartlist_free(mp->transports);
+
+  /* free the transports_to_launch smartlist */
+  SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t));
+  smartlist_free(mp->transports_to_launch);
+
+  /* remove it from the list of managed proxies */
+  smartlist_remove(managed_proxy_list, mp);
+
+  /* close its stdout stream */
+  if (mp->_stdout)
+    fclose(mp->_stdout);
+
+  /* free the argv */
+  free_execve_args(mp->argv);
+
+  if (mp->pid)
+    tor_terminate_process(mp->pid);
+
+  tor_free(mp);
+}
+
+/** Handle a configured or broken managed proxy <b>mp</b>. */
+static void
+handle_finished_proxy(managed_proxy_t *mp)
+{
+  switch (mp->conf_state) {
+  case PT_PROTO_BROKEN: /* if broken: */
+    managed_proxy_destroy(mp); /* annihilate it. */
+    break;
+  case PT_PROTO_CONFIGURED: /* if configured correctly: */
+    register_proxy(mp); /* register its transports */
+    mp->conf_state = PT_PROTO_COMPLETED; /* and mark it as completed. */
+    break;
+  case PT_PROTO_INFANT:
+  case PT_PROTO_LAUNCHED:
+  case PT_PROTO_ACCEPTING_METHODS:
+  case PT_PROTO_COMPLETED:
+  default:
+    log_warn(LD_CONFIG, "Unexpected managed proxy state in "
+             "handle_finished_proxy().");
+    tor_assert(0);
+  }
+
+  unconfigured_proxies_n--;
+  tor_assert(unconfigured_proxies_n >= 0);
+}
+
+/** Return true if the configuration of the managed proxy <b>mp</b> is
+    finished. */
+static INLINE int
+proxy_configuration_finished(const managed_proxy_t *mp)
+{
+  return (mp->conf_state == PT_PROTO_CONFIGURED ||
+          mp->conf_state == PT_PROTO_BROKEN);
+}
+
+/** This function is called when a proxy sends an {S,C}METHODS DONE message. */
+static void
+handle_methods_done(const managed_proxy_t *mp)
+{
+  tor_assert(mp->transports);
+
+  if (smartlist_len(mp->transports) == 0)
+    log_notice(LD_GENERAL, "Proxy was spawned successfully, "
+               "but it didn't laucn any pluggable transport listeners!");
+
+  log_info(LD_CONFIG, "%s managed proxy configuration completed!",
+           mp->is_server ? "Server" : "Client");
+}
+
+/** Handle a configuration protocol <b>line</b> received from a
+ *  managed proxy <b>mp</b>. */
+void
+handle_proxy_line(const char *line, managed_proxy_t *mp)
+{
+  log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line);
+
+  if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) {
+    log_warn(LD_GENERAL, "Managed proxy configuration line is too small. "
+             "Discarding");
+    goto err;
+  }
+
+  if (!strcmpstart(line, PROTO_ENV_ERROR)) {
+    if (mp->conf_state != PT_PROTO_LAUNCHED)
+      goto err;
+
+    parse_env_error(line);
+    goto err;
+  } else if (!strcmpstart(line, PROTO_NEG_FAIL)) {
+    if (mp->conf_state != PT_PROTO_LAUNCHED)
+      goto err;
+
+    log_warn(LD_CONFIG, "Managed proxy could not pick a "
+             "configuration protocol version.");
+    goto err;
+  } else if (!strcmpstart(line, PROTO_NEG_SUCCESS)) {
+    if (mp->conf_state != PT_PROTO_LAUNCHED)
+      goto err;
+
+    if (parse_version(line,mp) < 0)
+      goto err;
+
+    tor_assert(mp->conf_protocol != 0);
+    mp->conf_state = PT_PROTO_ACCEPTING_METHODS;
+    return;
+  } else if (!strcmpstart(line, PROTO_CMETHODS_DONE)) {
+    if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+      goto err;
+
+    handle_methods_done(mp);
+
+    mp->conf_state = PT_PROTO_CONFIGURED;
+    return;
+  } else if (!strcmpstart(line, PROTO_SMETHODS_DONE)) {
+    if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+      goto err;
+
+    handle_methods_done(mp);
+
+    mp->conf_state = PT_PROTO_CONFIGURED;
+    return;
+  } else if (!strcmpstart(line, PROTO_CMETHOD_ERROR)) {
+    if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+      goto err;
+
+    parse_client_method_error(line);
+    goto err;
+  } else if (!strcmpstart(line, PROTO_SMETHOD_ERROR)) {
+    if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+      goto err;
+
+    parse_server_method_error(line);
+    goto err;
+  } else if (!strcmpstart(line, PROTO_CMETHOD)) {
+    if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+      goto err;
+
+    if (parse_cmethod_line(line, mp) < 0)
+      goto err;
+
+    return;
+  } else if (!strcmpstart(line, PROTO_SMETHOD)) {
+    if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+      goto err;
+
+    if (parse_smethod_line(line, mp) < 0)
+      goto err;
+
+    return;
+  } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) {
+    log_warn(LD_GENERAL, "Could not launch managed proxy executable!");
+    goto err;
+  }
+
+  log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line);
+
+ err:
+  mp->conf_state = PT_PROTO_BROKEN;
+  return;
+}
+
+/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
+void
+parse_env_error(const char *line)
+{
+  /* (Length of the protocol string) plus (a space) and (the first char of
+     the error message) */
+  if (strlen(line) < (strlen(PROTO_ENV_ERROR) + 2))
+    log_notice(LD_CONFIG, "Managed proxy sent us an %s without an error "
+               "message.", PROTO_ENV_ERROR);
+
+  log_warn(LD_CONFIG, "Managed proxy couldn't understand the "
+           "pluggable transport environment variables. (%s)",
+           line+strlen(PROTO_ENV_ERROR)+1);
+}
+
+/** Handles a VERSION <b>line</b>. Updates the configuration protocol
+ *  version in <b>mp</b>. */
+int
+parse_version(const char *line, managed_proxy_t *mp)
+{
+  if (strlen(line) < (strlen(PROTO_NEG_SUCCESS) + 2)) {
+    log_warn(LD_CONFIG, "Managed proxy sent us malformed %s line.",
+             PROTO_NEG_SUCCESS);
+    return -1;
+  }
+
+  if (strcmp("1", line+strlen(PROTO_NEG_SUCCESS)+1)) { /* hardcoded temp */
+    log_warn(LD_CONFIG, "Managed proxy tried to negotiate on version '%s'. "
+             "We only support version '1'", line+strlen(PROTO_NEG_SUCCESS)+1);
+    return -1;
+  }
+
+  mp->conf_protocol = PROTO_VERSION_ONE; /* temp. till more versions appear */
+  return 0;
+}
+
+/** Parses {C,S}METHOD-ERROR <b>line</b> and warns the user
+ *  accordingly.  If <b>is_server</b> it is an SMETHOD-ERROR,
+ *  otherwise it is a CMETHOD-ERROR. */
+static void
+parse_method_error(const char *line, int is_server)
+{
+  const char* error = is_server ?
+    PROTO_SMETHOD_ERROR : PROTO_CMETHOD_ERROR;
+
+  /* (Length of the protocol string) plus (a space) and (the first char of
+     the error message) */
+  if (strlen(line) < (strlen(error) + 2))
+    log_warn(LD_CONFIG, "Managed proxy sent us an %s without an error "
+             "message.", error);
+
+  log_warn(LD_CONFIG, "%s managed proxy encountered a method error. (%s)",
+           is_server ? "Server" : "Client",
+           line+strlen(error)+1);
+}
+
+/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
+ *  new transport in <b>mp</b>. */
+int
+parse_smethod_line(const char *line, managed_proxy_t *mp)
+{
+  int r;
+  smartlist_t *items = NULL;
+
+  char *method_name=NULL;
+
+  char *addrport=NULL;
+  tor_addr_t addr;
+  uint16_t port = 0;
+
+  transport_t *transport=NULL;
+
+  items = smartlist_create();
+  smartlist_split_string(items, line, NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+  if (smartlist_len(items) < 3) {
+    log_warn(LD_CONFIG, "Server managed proxy sent us a SMETHOD line "
+             "with too few arguments.");
+    goto err;
+  }
+
+  tor_assert(!strcmp(smartlist_get(items,0),PROTO_SMETHOD));
+
+  method_name = smartlist_get(items,1);
+  if (!string_is_C_identifier(method_name)) {
+    log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+             method_name);
+    goto err;
+  }
+
+  addrport = smartlist_get(items, 2);
+  if (tor_addr_port_parse(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;
+  }
+
+  transport = transport_create(&addr, port, method_name, PROXY_NONE);
+  if (!transport)
+    goto err;
+
+  smartlist_add(mp->transports, transport);
+
+  /* For now, notify the user so that he knows where the server
+     transport is listening. */
+  log_info(LD_CONFIG, "Server transport %s at %s:%d.",
+           method_name, fmt_addr(&addr), (int)port);
+
+  r=0;
+  goto done;
+
+ err:
+  r = -1;
+
+ done:
+  SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+  smartlist_free(items);
+  return r;
+}
+
+/** Parses a CMETHOD <b>line</b>, and if well-formed it registers
+ *  the new transport in <b>mp</b>. */
+int
+parse_cmethod_line(const char *line, managed_proxy_t *mp)
+{
+  int r;
+  smartlist_t *items = NULL;
+
+  char *method_name=NULL;
+
+  char *socks_ver_str=NULL;
+  int socks_ver=PROXY_NONE;
+
+  char *addrport=NULL;
+  tor_addr_t addr;
+  uint16_t port = 0;
+
+  transport_t *transport=NULL;
+
+  items = smartlist_create();
+  smartlist_split_string(items, line, NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+  if (smartlist_len(items) < 4) {
+    log_warn(LD_CONFIG, "Client managed proxy sent us a CMETHOD line "
+             "with too few arguments.");
+    goto err;
+  }
+
+  tor_assert(!strcmp(smartlist_get(items,0),PROTO_CMETHOD));
+
+  method_name = smartlist_get(items,1);
+  if (!string_is_C_identifier(method_name)) {
+    log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+             method_name);
+    goto err;
+  }
+
+  socks_ver_str = smartlist_get(items,2);
+
+  if (!strcmp(socks_ver_str,"socks4")) {
+    socks_ver = PROXY_SOCKS4;
+  } else if (!strcmp(socks_ver_str,"socks5")) {
+    socks_ver = PROXY_SOCKS5;
+  } else {
+    log_warn(LD_CONFIG, "Client managed proxy sent us a proxy protocol "
+             "we don't recognize. (%s)", socks_ver_str);
+    goto err;
+  }
+
+  addrport = smartlist_get(items, 3);
+  if (tor_addr_port_parse(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;
+  }
+
+  transport = transport_create(&addr, port, method_name, socks_ver);
+  if (!transport)
+    goto err;
+
+  smartlist_add(mp->transports, transport);
+
+  log_info(LD_CONFIG, "Transport %s at %s:%d with SOCKS %d. "
+           "Attached to managed proxy.",
+           method_name, fmt_addr(&addr), (int)port, socks_ver);
+
+  r=0;
+  goto done;
+
+ err:
+  r = -1;
+
+ done:
+  SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+  smartlist_free(items);
+  return r;
+}
+
+/** Return a string containing the address:port that <b>transport</b>
+ *  should use. It's the responsibility of the caller to free() the
+ *  received string. */
+static char *
+get_bindaddr_for_proxy(const managed_proxy_t *mp)
+{
+  char *bindaddr = NULL;
+  smartlist_t *string_tmp = smartlist_create();
+
+  tor_assert(mp->is_server);
+
+  SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t) {
+    tor_asprintf(&bindaddr, "%s-%s", t, get_bindaddr_for_transport(t));
+    smartlist_add(string_tmp, bindaddr);
+  } SMARTLIST_FOREACH_END(t);
+
+  bindaddr = smartlist_join_strings(string_tmp, ",", 0, NULL);
+
+  SMARTLIST_FOREACH(string_tmp, char *, t, tor_free(t));
+  smartlist_free(string_tmp);
+
+  return bindaddr;
+}
+
+/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */
+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 n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT;
+
+  /* allocate enough space for our env. vars and a NULL pointer */
+  *envp = tor_malloc(sizeof(char*)*(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);
+
+  tor_asprintf(tmp++, "HOME=%s", getenv("HOME"));
+  tor_asprintf(tmp++, "PATH=%s", getenv("PATH"));
+  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_proxy(mp);
+
+    /* XXX temp */
+    tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort);
+    tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
+    tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
+    /* XXX temp*/
+    tor_asprintf(tmp++, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200");
+  } 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);
+}
+
+/** 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
+ *  managed proxy. */
+static managed_proxy_t *
+managed_proxy_create(const smartlist_t *transport_list,
+                     char **proxy_argv, int is_server)
+{
+  managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t));
+  mp->conf_state = PT_PROTO_INFANT;
+  mp->is_server = is_server;
+  mp->argv = proxy_argv;
+  mp->transports = smartlist_create();
+
+  mp->transports_to_launch = smartlist_create();
+  SMARTLIST_FOREACH(transport_list, const char *, transport,
+                    add_transport_to_proxy(transport, mp));
+
+  /* register the managed proxy */
+  if (!managed_proxy_list)
+    managed_proxy_list = smartlist_create();
+  smartlist_add(managed_proxy_list, mp);
+  unconfigured_proxies_n++;
+
+  return mp;
+}
+
+/** Register <b>transport</b> using proxy with <b>proxy_argv</b> to
+ *  the managed proxy subsystem.
+ *  If <b>is_server</b> is true, then the proxy is a server proxy. */
+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;
+
+  mp = get_managed_proxy_by_argv_and_type(proxy_argv, is_server);
+
+  if (!mp) { /* we haven't seen this proxy before */
+    managed_proxy_create(transport_list, proxy_argv, is_server);
+
+  } else { /* known proxy. add its transport to its transport list */
+    if (mp->got_hup) {
+      /* If the managed proxy we found is marked by a SIGHUP, it means
+         that it's not useless and should be kept. If it's marked for
+         removal, unmark it and increase the unconfigured proxies so
+         that we try to restart it if we need to. Afterwards, check if
+         a transport_t for 'transport' used to exist before the SIGHUP
+         and make sure it doesn't get deleted because we might reuse
+         it. */
+      if (mp->marked_for_removal) {
+        mp->marked_for_removal = 0;
+        unconfigured_proxies_n++;
+      }
+
+      SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport) {
+        old_transport = transport_get_by_name(transport);
+        if (old_transport)
+          old_transport->marked_for_removal = 0;
+      } SMARTLIST_FOREACH_END(transport);
+    }
+
+    SMARTLIST_FOREACH(transport_list, const char *, transport,
+                      add_transport_to_proxy(transport, mp));
+    free_execve_args(proxy_argv);
+  }
+}
+
+/** Frees the array of pointers in <b>arg</b> used as arguments to
+    execve(2). */
+static INLINE void
+free_execve_args(char **arg)
+{
+  char **tmp = arg;
+  while (*tmp) /* use the fact that the last element of the array is a
+                  NULL pointer to know when to stop freeing */
+    _tor_free(*tmp++);
+
+  tor_free(arg);
+}
+
+/** Tor will read its config.
+ *  Prepare the managed proxy list so that proxies not used in the new
+ *  config will shutdown, and proxies that need to spawn different
+ *  transports will do so. */
+void
+pt_prepare_proxy_list_for_config_read(void)
+{
+  if (!managed_proxy_list)
+    return;
+
+  SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+    /* Destroy unconfigured proxies. */
+    if (mp->conf_state != PT_PROTO_COMPLETED) {
+        managed_proxy_destroy(mp);
+        unconfigured_proxies_n--;
+        continue;
+    }
+
+    tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+
+    mp->marked_for_removal = 1;
+    mp->got_hup = 1;
+    SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t));
+    smartlist_clear(mp->transports_to_launch);
+  } SMARTLIST_FOREACH_END(mp);
+
+  tor_assert(unconfigured_proxies_n == 0);
+}
+
+/** The tor config was read.
+ *  Destroy all managed proxies that were marked by a previous call to
+ *  prepare_proxy_list_for_config_read() and are not used by the new
+ *  config. */
+void
+sweep_proxy_list(void)
+{
+  if (!managed_proxy_list)
+    return;
+
+  SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+    if (mp->marked_for_removal) {
+      SMARTLIST_DEL_CURRENT(managed_proxy_list, mp);
+      managed_proxy_destroy(mp);
+    }
+  } SMARTLIST_FOREACH_END(mp);
+}
+
+/** Release all storage held by the pluggable transports subsystem. */
+void
+pt_free_all(void)
+{
+  if (managed_proxy_list) {
+    /* If the proxy is in PT_PROTO_COMPLETED, it has registered its
+       transports and it's the duty of the circuitbuild.c subsystem to
+       free them. Otherwise, it hasn't registered its transports yet
+       and we should free them here. */
+    SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp,
+                      managed_proxy_destroy(mp));
+
+    smartlist_free(managed_proxy_list);
+    managed_proxy_list=NULL;
+  }
+}
+

+ 105 - 0
src/or/transports.h

@@ -0,0 +1,105 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file transports.h
+ * \brief Headers for transports.c
+ **/
+
+#ifndef TOR_TRANSPORTS_H
+#define TOR_TRANSPORTS_H
+
+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)
+#define pt_kickstart_server_proxy(tl, pa) \
+  pt_kickstart_proxy(tl, pa, 1)
+
+void pt_configure_remaining_proxies(void);
+
+int pt_proxies_configuration_pending(void);
+
+void pt_free_all(void);
+
+void pt_prepare_proxy_list_for_config_read(void);
+void sweep_proxy_list(void);
+
+#ifdef PT_PRIVATE
+/** State of the managed proxy configuration protocol. */
+enum pt_proto_state {
+  PT_PROTO_INFANT, /* was just born */
+  PT_PROTO_LAUNCHED, /* was just launched */
+  PT_PROTO_ACCEPTING_METHODS, /* accepting methods */
+  PT_PROTO_CONFIGURED, /* configured successfully */
+  PT_PROTO_COMPLETED, /* configure and registered its transports */
+  PT_PROTO_BROKEN
+};
+
+/** Structure containing information of a managed proxy. */
+typedef struct {
+  enum pt_proto_state conf_state; /* the current configuration state */
+  char **argv; /* the cli arguments of this proxy */
+  int conf_protocol; /* the configuration protocol version used */
+
+  int is_server; /* is it a server proxy? */
+
+  FILE *_stdout; /* a stream to its stdout
+                   (closed in managed_proxy_destroy()) */
+
+  int pid; /* The Process ID this managed proxy is using. */
+
+  /** Boolean: We are re-parsing our config, and we are going to
+   * remove this managed proxy if we don't find it any transport
+   * plugins that use it. */
+  unsigned int marked_for_removal : 1;
+
+  /** Boolean: We got a SIGHUP while this proxy was running. We use
+   * this flag to signify that this proxy might need to be restarted
+   * so that it can listen for other transports according to the new
+   * torrc. */
+  unsigned int got_hup : 1;
+
+  /* transports to-be-launched by this proxy */
+  smartlist_t *transports_to_launch;
+
+  /* The 'transports' list contains all the transports this proxy has
+     launched.
+
+     Before a managed_proxy_t reaches the PT_PROTO_COMPLETED phase,
+     this smartlist contains a 'transport_t' for every transport it
+     has launched.
+
+     When the managed_proxy_t reaches the PT_PROTO_COMPLETED phase, it
+     registers all its transports to the circuitbuild.c subsystem. At
+     that point the 'transport_t's are owned by the circuitbuild.c
+     subsystem.
+
+     To avoid carrying dangling 'transport_t's in this smartlist,
+     right before the managed_proxy_t reaches the PT_PROTO_COMPLETED
+     phase we replace all 'transport_t's with strings of their
+     transport names.
+
+     So, tl;dr:
+     When (conf_state != PT_PROTO_COMPLETED) this list carries
+     (transport_t *).
+     When (conf_state == PT_PROTO_COMPLETED) this list carries
+     (char *).
+   */
+  smartlist_t *transports;
+} managed_proxy_t;
+
+int parse_cmethod_line(const char *line, managed_proxy_t *mp);
+int parse_smethod_line(const char *line, managed_proxy_t *mp);
+
+int parse_version(const char *line, managed_proxy_t *mp);
+void parse_env_error(const char *line);
+void handle_proxy_line(const char *line, managed_proxy_t *mp);
+
+#endif
+
+#endif
+

+ 1 - 0
src/test/Makefile.am

@@ -19,6 +19,7 @@ test_SOURCES = \
 	test_data.c \
 	test_data.c \
 	test_dir.c \
 	test_dir.c \
 	test_microdesc.c \
 	test_microdesc.c \
+	test_pt.c \
 	test_util.c \
 	test_util.c \
 	tinytest.c
 	tinytest.c
 
 

+ 2 - 0
src/test/test.c

@@ -1941,6 +1941,7 @@ extern struct testcase_t container_tests[];
 extern struct testcase_t util_tests[];
 extern struct testcase_t util_tests[];
 extern struct testcase_t dir_tests[];
 extern struct testcase_t dir_tests[];
 extern struct testcase_t microdesc_tests[];
 extern struct testcase_t microdesc_tests[];
+extern struct testcase_t pt_tests[];
 
 
 static struct testgroup_t testgroups[] = {
 static struct testgroup_t testgroups[] = {
   { "", test_array },
   { "", test_array },
@@ -1951,6 +1952,7 @@ static struct testgroup_t testgroups[] = {
   { "util/", util_tests },
   { "util/", util_tests },
   { "dir/", dir_tests },
   { "dir/", dir_tests },
   { "dir/md/", microdesc_tests },
   { "dir/md/", microdesc_tests },
+  { "pt/", pt_tests },
   END_OF_GROUPS
   END_OF_GROUPS
 };
 };
 
 

+ 147 - 0
src/test/test_pt.c

@@ -0,0 +1,147 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define PT_PRIVATE
+#include "or.h"
+#include "transports.h"
+#include "circuitbuild.h"
+#include "test.h"
+
+static void
+reset_mp(managed_proxy_t *mp)
+{
+  mp->conf_state = PT_PROTO_LAUNCHED;
+  SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+  smartlist_clear(mp->transports);
+}
+
+static void
+test_pt_parsing(void)
+{
+  char line[200];
+
+  managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t));
+  mp->conf_state = PT_PROTO_INFANT;
+  mp->transports = smartlist_create();
+
+  /* incomplete cmethod */
+  strcpy(line,"CMETHOD trebuchet");
+  test_assert(parse_cmethod_line(line, mp) < 0);
+
+  reset_mp(mp);
+
+  /* wrong proxy type */
+  strcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999");
+  test_assert(parse_cmethod_line(line, mp) < 0);
+
+  reset_mp(mp);
+
+  /* wrong addrport */
+  strcpy(line,"CMETHOD trebuchet socks4 abcd");
+  test_assert(parse_cmethod_line(line, mp) < 0);
+
+  reset_mp(mp);
+
+  /* correct line */
+  strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+  test_assert(parse_cmethod_line(line, mp) == 0);
+  test_assert(smartlist_len(mp->transports));
+
+  reset_mp(mp);
+
+  /* incomplete smethod */
+  strcpy(line,"SMETHOD trebuchet");
+  test_assert(parse_smethod_line(line, mp) < 0);
+
+  reset_mp(mp);
+
+  /* wrong addr type */
+  strcpy(line,"SMETHOD trebuchet abcd");
+  test_assert(parse_smethod_line(line, mp) < 0);
+
+  reset_mp(mp);
+
+  /* cowwect */
+  strcpy(line,"SMETHOD trebuchy 127.0.0.1:1999");
+  test_assert(parse_smethod_line(line, mp) == 0);
+
+  reset_mp(mp);
+
+  /* unsupported version */
+  strcpy(line,"VERSION 666");
+  test_assert(parse_version(line, mp) < 0);
+
+  /* incomplete VERSION */
+  strcpy(line,"VERSION ");
+  test_assert(parse_version(line, mp) < 0);
+
+  /* correct VERSION */
+  strcpy(line,"VERSION 1");
+  test_assert(parse_version(line, mp) == 0);
+
+ done:
+  tor_free(mp);
+}
+
+static void
+test_pt_protocol(void)
+{
+  char line[200];
+
+  managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t));
+  mp->conf_state = PT_PROTO_LAUNCHED;
+  mp->transports = smartlist_create();
+
+  /* various wrong protocol runs: */
+
+  strcpy(line, "TEST TEST");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_BROKEN);
+
+  reset_mp(mp);
+
+  strcpy(line,"VERSION 1");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+
+  strcpy(line,"VERSION 1");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_BROKEN);
+
+  reset_mp(mp);
+
+  strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_BROKEN);
+
+  reset_mp(mp);
+
+  /* correct protocol run: */
+  strcpy(line,"VERSION 1");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+
+  strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+
+  strcpy(line,"CMETHODS DONE");
+  handle_proxy_line(line, mp);
+  test_assert(mp->conf_state == PT_PROTO_CONFIGURED);
+
+ done:
+  tor_free(mp);
+}
+
+#define PT_LEGACY(name)                                               \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_pt_ ## name }
+
+struct testcase_t pt_tests[] = {
+  PT_LEGACY(parsing),
+  PT_LEGACY(protocol),
+  END_OF_TESTCASES
+};
+

+ 4 - 4
src/test/test_util.c

@@ -1389,9 +1389,9 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
 
 
   /* Start the program */
   /* Start the program */
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
-  tor_spawn_background(NULL, argv, &process_handle);
+  tor_spawn_background(NULL, argv, NULL, &process_handle);
 #else
 #else
-  tor_spawn_background(argv[0], argv, &process_handle);
+  tor_spawn_background(argv[0], argv, NULL, &process_handle);
 #endif
 #endif
 
 
   tt_int_op(process_handle.status, ==, expected_status);
   tt_int_op(process_handle.status, ==, expected_status);
@@ -1506,9 +1506,9 @@ test_util_spawn_background_partial_read(void *ptr)
 
 
   /* Start the program */
   /* Start the program */
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
-  tor_spawn_background(NULL, argv, &process_handle);
+  tor_spawn_background(NULL, argv, NULL, &process_handle);
 #else
 #else
-  tor_spawn_background(argv[0], argv, &process_handle);
+  tor_spawn_background(argv[0], argv, NULL, &process_handle);
 #endif
 #endif
   tt_int_op(process_handle.status, ==, expected_status);
   tt_int_op(process_handle.status, ==, expected_status);