summaryrefslogtreecommitdiff
path: root/src/feature/client/transports.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/client/transports.c')
-rw-r--r--src/feature/client/transports.c1738
1 files changed, 1738 insertions, 0 deletions
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
new file mode 100644
index 0000000000..1d3cb7b951
--- /dev/null
+++ b/src/feature/client/transports.c
@@ -0,0 +1,1738 @@
+/* Copyright (c) 2011-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file transports.c
+ * \brief Pluggable Transports related code.
+ *
+ * \details
+ * Each managed proxy is represented by a <b>managed_proxy_t</b>.
+ * 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.
+ *
+ * <b>In a little more detail:</b>
+ *
+ * While we are serially parsing torrc, we store all the transports
+ * that a proxy should spawn in its <em>transports_to_launch</em>
+ * 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 <em>transports</em> element, as
+ * transport_t structs.
+ *
+ * When the managed proxy stops spitting METHOD lines (signified by a
+ * '{S,C}METHODS DONE' message) we pass copies of its transports to
+ * the bridge subsystem. We keep copies of the 'transport_t's on the
+ * managed proxy to be able to associate the proxy with its
+ * transports, and we pass copies to the bridge subsystem so that
+ * transports can be associated with bridges.
+ * [ XXX We should try see whether the two copies are really needed
+ * and maybe cut it into a single copy of the 'transport_t' shared
+ * between the managed proxy and the bridge subsystem. Preliminary
+ * analysis shows that both copies are needed with the current code
+ * logic, because of race conditions that can cause dangling
+ * pointers. ]
+ *
+ * <b>In even more detail, this is what happens when a config read
+ * (like a SIGHUP or a SETCONF) occurs:</b>
+ *
+ * We immediately destroy all unconfigured proxies (We shouldn't have
+ * unconfigured proxies in the first place, except when the config
+ * read happens 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
+ * (we mark using the <b>marked_for_removal</b> element).
+ * 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
+ * (we mark using the <b>was_around_before_config_read</b> element).
+ * We also clear their <b>transports_to_launch</b> 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 managed proxy that
+ * was around before the config read, we cleanse that proxy from the
+ * removal mark. We also toggle the <b>check_if_restarts_needed</b>
+ * flag, so that on the next <b>pt_configure_remaining_proxies</b>
+ * tick, we investigate whether we need to restart the proxy so that
+ * it also spawns the new transports. If the post-config-read
+ * <b>transports_to_launch</b> list is identical to the pre-config-read
+ * one, it means that no changes were introduced to this proxy during
+ * the config read and no restart has to take place.
+ *
+ * During the post-config-read 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.
+ **/
+
+#define PT_PRIVATE
+#include "or/or.h"
+#include "or/bridges.h"
+#include "or/config.h"
+#include "or/connection.h"
+#include "or/circuitbuild.h"
+#include "or/transports.h"
+#include "or/router.h"
+#include "or/statefile.h"
+#include "or/connection_or.h"
+#include "or/ext_orport.h"
+#include "or/control.h"
+
+#include "lib/process/env.h"
+#include "lib/process/subprocess.h"
+
+static process_environment_t *
+create_managed_proxy_environment(const managed_proxy_t *mp);
+
+static inline int proxy_configuration_finished(const managed_proxy_t *mp);
+
+static void handle_finished_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)
+
+/** 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"
+#define PROTO_PROXY_DONE "PROXY DONE"
+#define PROTO_PROXY_ERROR "PROXY-ERROR"
+
+/** The first and only supported - at the moment - configuration
+ protocol version. */
+#define PROTO_VERSION_ONE 1
+
+/** A list of pluggable transports found in torrc. */
+static smartlist_t *transport_list = NULL;
+
+/** 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>. */
+STATIC transport_t *
+transport_new(const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver,
+ const char *extra_info_args)
+{
+ transport_t *t = tor_malloc_zero(sizeof(transport_t));
+
+ tor_addr_copy(&t->addr, addr);
+ t->port = port;
+ t->name = tor_strdup(name);
+ t->socks_version = socks_ver;
+ if (extra_info_args)
+ t->extra_info_args = tor_strdup(extra_info_args);
+
+ return t;
+}
+
+/** Free the pluggable transport struct <b>transport</b>. */
+void
+transport_free_(transport_t *transport)
+{
+ if (!transport)
+ return;
+
+ tor_free(transport->name);
+ tor_free(transport->extra_info_args);
+ tor_free(transport);
+}
+
+/** 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_new();
+ 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_new();
+ 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
+ * needed. */
+static void
+clear_transport_list(void)
+{
+ if (!transport_list)
+ transport_list = smartlist_new();
+ SMARTLIST_FOREACH(transport_list, transport_t *, t, transport_free(t));
+ smartlist_clear(transport_list);
+}
+
+/** Return a deep copy of <b>transport</b>. */
+static transport_t *
+transport_copy(const transport_t *transport)
+{
+ transport_t *new_transport = NULL;
+
+ tor_assert(transport);
+
+ new_transport = tor_malloc_zero(sizeof(transport_t));
+
+ new_transport->socks_version = transport->socks_version;
+ new_transport->name = tor_strdup(transport->name);
+ tor_addr_copy(&new_transport->addr, &transport->addr);
+ new_transport->port = transport->port;
+ new_transport->marked_for_removal = transport->marked_for_removal;
+
+ return new_transport;
+}
+
+/** Returns the transport in our transport list that has the name <b>name</b>.
+ * Else returns NULL. */
+MOCK_IMPL(transport_t *,
+transport_get_by_name,(const char *name))
+{
+ tor_assert(name);
+
+ if (!transport_list)
+ return NULL;
+
+ SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, transport) {
+ if (!strcmp(transport->name, name))
+ return transport;
+ } SMARTLIST_FOREACH_END(transport);
+
+ return NULL;
+}
+
+/** 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(const 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 */
+ char *new_transport_addrport =
+ tor_strdup(fmt_addrport(&t->addr, t->port));
+ if (t_tmp->marked_for_removal) { /* marked for removal */
+ log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s' "
+ "but there was already a transport marked for deletion at "
+ "'%s'. We deleted the old transport and registered the "
+ "new one.", t->name, new_transport_addrport,
+ fmt_addrport(&t_tmp->addr, t_tmp->port));
+ smartlist_remove(transport_list, t_tmp);
+ transport_free(t_tmp);
+ tor_free(new_transport_addrport);
+ } else { /* *not* marked for removal */
+ log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s' "
+ "but the same transport already exists at '%s'. "
+ "Skipping.", t->name, new_transport_addrport,
+ fmt_addrport(&t_tmp->addr, t_tmp->port));
+ tor_free(new_transport_addrport);
+ return -1;
+ }
+ tor_free(new_transport_addrport);
+ }
+ }
+
+ return 0;
+}
+
+/** 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. */
+static 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_new();
+ 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. */
+MOCK_IMPL(int,
+transport_add_from_config, (const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver))
+{
+ transport_t *t = transport_new(addr, port, name, socks_ver, NULL);
+
+ int r = transport_add(t);
+
+ switch (r) {
+ case -1:
+ default:
+ log_notice(LD_GENERAL, "Could not add transport %s at %s. Skipping.",
+ t->name, fmt_addrport(&t->addr, t->port));
+ transport_free(t);
+ return -1;
+ case 1:
+ log_info(LD_GENERAL, "Successfully registered transport %s at %s.",
+ t->name, fmt_addrport(&t->addr, t->port));
+ transport_free(t); /* falling */
+ return 0;
+ case 0:
+ log_info(LD_GENERAL, "Successfully registered transport %s at %s.",
+ t->name, fmt_addrport(&t->addr, t->port));
+ return 0;
+ }
+}
+
+/** List of unconfigured managed proxies. */
+static smartlist_t *managed_proxy_list = NULL;
+/** Number of still unconfigured proxies. */
+static int unconfigured_proxies_n = 0;
+/** Boolean: True iff we might need to restart some proxies. */
+static int check_if_restarts_needed = 0;
+
+/** Return true if there are still unconfigured managed proxies, or proxies
+ * that need restarting. */
+int
+pt_proxies_configuration_pending(void)
+{
+ return unconfigured_proxies_n || check_if_restarts_needed;
+}
+
+/** Assert that the unconfigured_proxies_n value correctly matches the number
+ * of proxies in a state other than PT_PROTO_COMPLETE. */
+static void
+assert_unconfigured_count_ok(void)
+{
+ int n_completed = 0;
+ if (!managed_proxy_list) {
+ tor_assert(unconfigured_proxies_n == 0);
+ return;
+ }
+
+ SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp, {
+ if (mp->conf_state == PT_PROTO_COMPLETED)
+ ++n_completed;
+ });
+
+ tor_assert(n_completed + unconfigured_proxies_n ==
+ smartlist_len(managed_proxy_list));
+}
+
+/** 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_contains_string(mp->transports_to_launch, transport))
+ smartlist_add_strdup(mp->transports_to_launch, 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)
+{
+ int ret = 1;
+ char* proxy_uri;
+
+ /* If the PT proxy config has changed, then all existing pluggable transports
+ * should be restarted.
+ */
+
+ proxy_uri = get_pt_proxy_uri();
+ if (strcmp_opt(proxy_uri, mp->proxy_uri) != 0)
+ goto needs_restart;
+
+ /* 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 transports that were
+ launched *before* the SIGHUP.
+
+ Check if all the transports that need to be launched are already
+ launched: */
+
+ 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, const transport_t *, t) {
+ if (!smartlist_contains_string(mp->transports_to_launch, t->name))
+ goto needs_restart;
+
+ } SMARTLIST_FOREACH_END(t);
+
+ ret = 0;
+ needs_restart:
+ tor_free(proxy_uri);
+ return ret;
+}
+
+/** 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);
+
+ /* destroy the process handle and terminate the process. */
+ tor_process_handle_destroy(mp->process_handle, 1);
+ mp->process_handle = NULL;
+
+ /* destroy all its registered transports, since we will no longer
+ use them. */
+ SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) {
+ t_tmp = transport_get_by_name(t->name);
+ if (t_tmp)
+ t_tmp->marked_for_removal = 1;
+ } SMARTLIST_FOREACH_END(t);
+ sweep_transport_list();
+
+ /* free the transport in mp->transports */
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+ smartlist_clear(mp->transports);
+
+ /* Reset the proxy's HTTPS/SOCKS proxy */
+ tor_free(mp->proxy_uri);
+ mp->proxy_uri = get_pt_proxy_uri();
+ mp->proxy_supported = 0;
+
+ /* flag it as an infant proxy so that it gets launched on next tick */
+ mp->conf_state = PT_PROTO_INFANT;
+ unconfigured_proxies_n++;
+}
+
+/** Launch managed proxy <b>mp</b>. */
+static int
+launch_managed_proxy(managed_proxy_t *mp)
+{
+ int retval;
+
+ process_environment_t *env = create_managed_proxy_environment(mp);
+
+#ifdef _WIN32
+ /* Passing NULL as lpApplicationName makes Windows search for the .exe */
+ retval = tor_spawn_background(NULL,
+ (const char **)mp->argv,
+ env,
+ &mp->process_handle);
+#else /* !(defined(_WIN32)) */
+ retval = tor_spawn_background(mp->argv[0],
+ (const char **)mp->argv,
+ env,
+ &mp->process_handle);
+#endif /* defined(_WIN32) */
+
+ process_environment_free(env);
+
+ if (retval == PROCESS_STATUS_ERROR) {
+ log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
+ mp->argv[0]);
+ return -1;
+ }
+
+ log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.",
+ mp->argv[0], tor_process_get_pid(mp->process_handle));
+
+ mp->conf_state = PT_PROTO_LAUNCHED;
+
+ return 0;
+}
+
+/** Check if any of the managed proxies we are currently trying to
+ * configure has anything new to say. */
+void
+pt_configure_remaining_proxies(void)
+{
+ int at_least_a_proxy_config_finished = 0;
+ smartlist_t *tmp = smartlist_new();
+
+ log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!",
+ unconfigured_proxies_n);
+
+ /* Iterate over tmp, not managed_proxy_list, since configure_proxy can
+ * remove elements from managed_proxy_list. */
+ smartlist_add_all(tmp, managed_proxy_list);
+
+ assert_unconfigured_count_ok();
+
+ SMARTLIST_FOREACH_BEGIN(tmp, managed_proxy_t *, mp) {
+ tor_assert(mp->conf_state != PT_PROTO_BROKEN &&
+ mp->conf_state != PT_PROTO_FAILED_LAUNCH);
+
+ if (mp->was_around_before_config_read) {
+ /* This proxy is marked by a config read. Check whether we need
+ to restart it. */
+
+ mp->was_around_before_config_read = 0;
+
+ if (proxy_needs_restart(mp)) {
+ log_info(LD_GENERAL, "Preparing managed proxy '%s' for restart.",
+ mp->argv[0]);
+ proxy_prepare_for_restart(mp);
+ } else { /* it doesn't need to be restarted. */
+ log_info(LD_GENERAL, "Nothing changed for managed proxy '%s' after "
+ "HUP: not restarting.", mp->argv[0]);
+ }
+
+ continue;
+ }
+
+ /* If the proxy is not fully configured, try to configure it
+ further. */
+ if (!proxy_configuration_finished(mp))
+ if (configure_proxy(mp) == 1)
+ at_least_a_proxy_config_finished = 1;
+
+ } SMARTLIST_FOREACH_END(mp);
+
+ smartlist_free(tmp);
+ check_if_restarts_needed = 0;
+ assert_unconfigured_count_ok();
+
+ if (at_least_a_proxy_config_finished)
+ mark_my_descriptor_dirty("configured managed proxies");
+}
+
+/** Attempt to continue configuring managed proxy <b>mp</b>.
+ * Return 1 if the transport configuration finished, and return 0
+ * otherwise (if we still have more configuring to do for this
+ * proxy). */
+STATIC int
+configure_proxy(managed_proxy_t *mp)
+{
+ int configuration_finished = 0;
+ smartlist_t *proxy_output = NULL;
+ enum stream_status stream_status = 0;
+
+ /* if we haven't launched the proxy yet, do it now */
+ if (mp->conf_state == PT_PROTO_INFANT) {
+ if (launch_managed_proxy(mp) < 0) { /* launch fail */
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ handle_finished_proxy(mp);
+ }
+ return 0;
+ }
+
+ tor_assert(mp->conf_state != PT_PROTO_INFANT);
+ tor_assert(mp->process_handle);
+
+ proxy_output =
+ tor_get_lines_from_handle(tor_process_get_stdout_pipe(mp->process_handle),
+ &stream_status);
+ if (!proxy_output) { /* failed to get input from proxy */
+ if (stream_status != IO_STREAM_EAGAIN) { /* bad stream status! */
+ mp->conf_state = PT_PROTO_BROKEN;
+ log_warn(LD_GENERAL, "The communication stream of managed proxy '%s' "
+ "is '%s'. Most probably the managed proxy stopped running. "
+ "This might be a bug of the managed proxy, a bug of Tor, or "
+ "a misconfiguration. Please enable logging on your managed "
+ "proxy and check the logs for errors.",
+ mp->argv[0], stream_status_to_string(stream_status));
+ }
+
+ goto done;
+ }
+
+ /* Handle lines. */
+ SMARTLIST_FOREACH_BEGIN(proxy_output, const char *, line) {
+ handle_proxy_line(line, mp);
+ if (proxy_configuration_finished(mp))
+ goto done;
+ } SMARTLIST_FOREACH_END(line);
+
+ done:
+ /* if the proxy finished configuring, exit the loop. */
+ if (proxy_configuration_finished(mp)) {
+ handle_finished_proxy(mp);
+ configuration_finished = 1;
+ }
+
+ if (proxy_output) {
+ SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp));
+ smartlist_free(proxy_output);
+ }
+
+ return configuration_finished;
+}
+
+/** Register server managed proxy <b>mp</b> transports to state */
+static void
+register_server_proxy(const managed_proxy_t *mp)
+{
+ 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);
+ log_notice(LD_GENERAL, "Registered server transport '%s' at '%s'",
+ t->name, fmt_addrport(&t->addr, t->port));
+ control_event_transport_launched("server", t->name, &t->addr, t->port);
+ } SMARTLIST_FOREACH_END(t);
+}
+
+/** Register all the transports supported by client managed proxy
+ * <b>mp</b> to the bridge subsystem. */
+static void
+register_client_proxy(const managed_proxy_t *mp)
+{
+ int r;
+
+ tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
+
+ SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
+ transport_t *transport_tmp = transport_copy(t);
+ r = transport_add(transport_tmp);
+ switch (r) {
+ case -1:
+ log_notice(LD_GENERAL, "Could not add transport %s. Skipping.", t->name);
+ transport_free(transport_tmp);
+ break;
+ case 0:
+ log_info(LD_GENERAL, "Successfully registered transport %s", t->name);
+ control_event_transport_launched("client", t->name, &t->addr, t->port);
+ break;
+ case 1:
+ log_info(LD_GENERAL, "Successfully registered transport %s", t->name);
+ control_event_transport_launched("client", t->name, &t->addr, t->port);
+ transport_free(transport_tmp);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(t);
+}
+
+/** Register the transports of managed proxy <b>mp</b>. */
+static inline void
+register_proxy(const 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,
+ int also_terminate_process)
+{
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+
+ /* 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 */
+ if (managed_proxy_list)
+ smartlist_remove(managed_proxy_list, mp);
+
+ /* free the argv */
+ free_execve_args(mp->argv);
+
+ /* free the outgoing proxy URI */
+ tor_free(mp->proxy_uri);
+
+ tor_process_handle_destroy(mp->process_handle, also_terminate_process);
+ mp->process_handle = NULL;
+
+ tor_free(mp);
+}
+
+/** Convert the tor proxy options to a URI suitable for TOR_PT_PROXY.
+ * Return a newly allocated string containing the URI, or NULL if no
+ * proxy is set. */
+STATIC char *
+get_pt_proxy_uri(void)
+{
+ const or_options_t *options = get_options();
+ char *uri = NULL;
+
+ if (options->Socks4Proxy || options->Socks5Proxy || options->HTTPSProxy) {
+ char addr[TOR_ADDR_BUF_LEN+1];
+
+ if (options->Socks4Proxy) {
+ tor_addr_to_str(addr, &options->Socks4ProxyAddr, sizeof(addr), 1);
+ tor_asprintf(&uri, "socks4a://%s:%d", addr, options->Socks4ProxyPort);
+ } else if (options->Socks5Proxy) {
+ tor_addr_to_str(addr, &options->Socks5ProxyAddr, sizeof(addr), 1);
+ if (!options->Socks5ProxyUsername && !options->Socks5ProxyPassword) {
+ tor_asprintf(&uri, "socks5://%s:%d", addr, options->Socks5ProxyPort);
+ } else {
+ tor_asprintf(&uri, "socks5://%s:%s@%s:%d",
+ options->Socks5ProxyUsername,
+ options->Socks5ProxyPassword,
+ addr, options->Socks5ProxyPort);
+ }
+ } else if (options->HTTPSProxy) {
+ tor_addr_to_str(addr, &options->HTTPSProxyAddr, sizeof(addr), 1);
+ if (!options->HTTPSProxyAuthenticator) {
+ tor_asprintf(&uri, "http://%s:%d", addr, options->HTTPSProxyPort);
+ } else {
+ tor_asprintf(&uri, "http://%s@%s:%d", options->HTTPSProxyAuthenticator,
+ addr, options->HTTPSProxyPort);
+ }
+ }
+ }
+
+ return uri;
+}
+
+/** 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, 1); /* annihilate it. */
+ break;
+ case PT_PROTO_FAILED_LAUNCH: /* if it failed before launching: */
+ managed_proxy_destroy(mp, 0); /* destroy it but don't terminate */
+ break;
+ case PT_PROTO_CONFIGURED: /* if configured correctly: */
+ if (mp->proxy_uri && !mp->proxy_supported) {
+ log_warn(LD_CONFIG, "Managed proxy '%s' did not configure the "
+ "specified outgoing proxy and will be terminated.",
+ mp->argv[0]);
+ managed_proxy_destroy(mp, 1); /* annihilate it. */
+ break;
+ }
+ 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 state '%d' of managed proxy '%s'.",
+ (int)mp->conf_state, mp->argv[0]);
+ tor_assert(0);
+ }
+
+ unconfigured_proxies_n--;
+}
+
+/** 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 ||
+ mp->conf_state == PT_PROTO_FAILED_LAUNCH);
+}
+
+/** 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, "Managed proxy '%s' was spawned successfully, "
+ "but it didn't launch any pluggable transport listeners!",
+ mp->argv[0]);
+
+ log_info(LD_CONFIG, "%s managed proxy '%s' configuration completed!",
+ mp->is_server ? "Server" : "Client",
+ mp->argv[0]);
+}
+
+/** Handle a configuration protocol <b>line</b> received from a
+ * managed proxy <b>mp</b>. */
+STATIC void
+handle_proxy_line(const char *line, managed_proxy_t *mp)
+{
+ log_info(LD_GENERAL, "Got a line from managed proxy '%s': (%s)",
+ mp->argv[0], line);
+
+ 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, PROTO_PROXY_DONE)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ if (mp->proxy_uri) {
+ mp->proxy_supported = 1;
+ return;
+ }
+
+ /* No proxy was configured, this should log */
+ } else if (!strcmpstart(line, PROTO_PROXY_ERROR)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ parse_proxy_error(line);
+ goto err;
+ } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) {
+ /* managed proxy launch failed: parse error message to learn why. */
+ int retval, child_state, saved_errno;
+ retval = tor_sscanf(line, SPAWN_ERROR_MESSAGE "%x/%x",
+ &child_state, &saved_errno);
+ if (retval == 2) {
+ log_warn(LD_GENERAL,
+ "Could not launch managed proxy executable at '%s' ('%s').",
+ mp->argv[0], strerror(saved_errno));
+ } else { /* failed to parse error message */
+ log_warn(LD_GENERAL,"Could not launch managed proxy executable at '%s'.",
+ mp->argv[0]);
+ }
+
+ mp->conf_state = PT_PROTO_FAILED_LAUNCH;
+ return;
+ }
+
+ log_notice(LD_GENERAL, "Unknown line received by managed proxy (%s).", line);
+ return;
+
+ err:
+ mp->conf_state = PT_PROTO_BROKEN;
+ log_warn(LD_CONFIG, "Managed proxy at '%s' failed the configuration protocol"
+ " and will be destroyed.", mp->argv[0]);
+}
+
+/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
+STATIC 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>. */
+STATIC 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);
+}
+
+/** A helper for parse_{c,s}method_line(), bootstraps its
+ * functionalities. If <b>is_smethod</b> is true then the
+ * the line to parse is a SMETHOD line otherwise it is a
+ * CMETHOD line*/
+static int
+parse_method_line_helper(const char *line,
+ managed_proxy_t *mp,
+ int is_smethod)
+{
+ int item_index = 0;
+ int r;
+
+ char *transport_name=NULL;
+ char *args_string=NULL;
+ char *addrport=NULL;
+ int socks_ver=PROXY_NONE;
+ char *address=NULL;
+ uint16_t port = 0;
+
+ const char *method_str = is_smethod ? PROTO_SMETHOD : PROTO_CMETHOD;
+ const int min_args_count = is_smethod ? 3 : 4;
+
+ tor_addr_t tor_addr;
+ transport_t *transport=NULL;
+ smartlist_t *items= smartlist_new();
+
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ if (smartlist_len(items) < min_args_count) {
+ log_warn(LD_CONFIG, "Managed proxy sent us a %s line "
+ "with too few arguments.", method_str);
+ goto err;
+ }
+
+ tor_assert(!strcmp(smartlist_get(items, item_index),method_str));
+ ++item_index;
+
+ transport_name = smartlist_get(items,item_index);
+ ++item_index;
+ if (!string_is_C_identifier(transport_name)) {
+ log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+ transport_name);
+ goto err;
+ }
+
+ /** Check for the proxy method sent to us in CMETHOD line. */
+ if (!is_smethod) {
+ const char *socks_ver_str = smartlist_get(items,item_index);
+ ++item_index;
+
+ 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, item_index);
+ ++item_index;
+ if (tor_addr_port_split(LOG_WARN, addrport, &address, &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 (tor_addr_parse(&tor_addr, address) < 0) {
+ log_warn(LD_CONFIG, "Error parsing transport address '%s'", address);
+ goto err;
+ }
+
+ /** Check for options in the SMETHOD line. */
+ if (is_smethod && smartlist_len(items) > min_args_count) {
+ /* Seems like there are also some [options] in the SMETHOD line.
+ Let's see if we can parse them. */
+ char *options_string = smartlist_get(items, item_index);
+ log_debug(LD_CONFIG, "Got options_string: %s", options_string);
+ if (!strcmpstart(options_string, "ARGS:")) {
+ args_string = options_string+strlen("ARGS:");
+ log_debug(LD_CONFIG, "Got ARGS: %s", args_string);
+ }
+ }
+
+ transport = transport_new(&tor_addr, port, transport_name,
+ socks_ver, args_string);
+
+ smartlist_add(mp->transports, transport);
+
+ /** Logs info about line parsing success for client or server */
+ if (is_smethod) {
+ log_info(LD_CONFIG, "Server transport %s at %s:%d.",
+ transport_name, address, (int)port);
+ } else {
+ log_info(LD_CONFIG, "Transport %s at %s:%d with SOCKS %d. "
+ "Attached to managed proxy.",
+ transport_name, address, (int)port, socks_ver);
+ }
+
+ r=0;
+ goto done;
+
+ err:
+ r = -1;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ tor_free(address);
+ return r;
+}
+
+/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
+ * new transport in <b>mp</b>. */
+STATIC int
+parse_smethod_line(const char *line, managed_proxy_t *mp)
+{
+ /* Example of legit SMETHOD line:
+ SMETHOD obfs2 0.0.0.0:25612 ARGS:secret=supersekrit,key=superkey */
+ return parse_method_line_helper(line, mp, 1);
+}
+
+/** Parses a CMETHOD <b>line</b>, and if well-formed it registers
+ * the new transport in <b>mp</b>. */
+STATIC int
+parse_cmethod_line(const char *line, managed_proxy_t *mp)
+{
+ /* Example of legit CMETHOD line:
+ CMETHOD obfs2 socks5 127.0.0.1:35713 */
+ return parse_method_line_helper(line, mp, 0);
+}
+
+/** Parses an PROXY-ERROR <b>line</b> and warns the user accordingly. */
+STATIC void
+parse_proxy_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_PROXY_ERROR) + 2))
+ log_notice(LD_CONFIG, "Managed proxy sent us an %s without an error "
+ "message.", PROTO_PROXY_ERROR);
+
+ log_warn(LD_CONFIG, "Managed proxy failed to configure the "
+ "pluggable transport's outgoing proxy. (%s)",
+ line+strlen(PROTO_PROXY_ERROR)+1);
+}
+
+/** Return a newly allocated string that tor should place in
+ * TOR_PT_SERVER_TRANSPORT_OPTIONS while configuring the server
+ * manged proxy in <b>mp</b>. Return NULL if no such options are found. */
+STATIC char *
+get_transport_options_for_server_proxy(const managed_proxy_t *mp)
+{
+ char *options_string = NULL;
+ smartlist_t *string_sl = smartlist_new();
+
+ tor_assert(mp->is_server);
+
+ /** Loop over the transports of the proxy. If we have options for
+ any of them, format them appropriately and place them in our
+ smartlist. Finally, join our smartlist to get the final
+ string. */
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) {
+ smartlist_t *options_tmp_sl = NULL;
+ options_tmp_sl = get_options_for_server_transport(transport);
+ if (!options_tmp_sl)
+ continue;
+
+ /** Loop over the options of this transport, escape them, and
+ place them in the smartlist. */
+ SMARTLIST_FOREACH_BEGIN(options_tmp_sl, const char *, options) {
+ char *escaped_opts = tor_escape_str_for_pt_args(options, ":;\\");
+ smartlist_add_asprintf(string_sl, "%s:%s",
+ transport, escaped_opts);
+ tor_free(escaped_opts);
+ } SMARTLIST_FOREACH_END(options);
+
+ SMARTLIST_FOREACH(options_tmp_sl, char *, c, tor_free(c));
+ smartlist_free(options_tmp_sl);
+ } SMARTLIST_FOREACH_END(transport);
+
+ if (smartlist_len(string_sl)) {
+ options_string = smartlist_join_strings(string_sl, ";", 0, NULL);
+ }
+
+ SMARTLIST_FOREACH(string_sl, char *, t, tor_free(t));
+ smartlist_free(string_sl);
+
+ return options_string;
+}
+
+/** Return the string that tor should place in TOR_PT_SERVER_BINDADDR
+ * while configuring the server managed proxy in <b>mp</b>. The
+ * string is stored in the heap, and it's the responsibility of
+ * the caller to deallocate it after its use. */
+static char *
+get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
+{
+ char *bindaddr_result = NULL;
+ char *bindaddr_tmp = NULL;
+ smartlist_t *string_tmp = smartlist_new();
+
+ tor_assert(mp->is_server);
+
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t) {
+ bindaddr_tmp = get_stored_bindaddr_for_server_transport(t);
+
+ smartlist_add_asprintf(string_tmp, "%s-%s", t, bindaddr_tmp);
+
+ tor_free(bindaddr_tmp);
+ } SMARTLIST_FOREACH_END(t);
+
+ bindaddr_result = smartlist_join_strings(string_tmp, ",", 0, NULL);
+
+ SMARTLIST_FOREACH(string_tmp, char *, t, tor_free(t));
+ smartlist_free(string_tmp);
+
+ return bindaddr_result;
+}
+
+/** Return a newly allocated process_environment_t * for <b>mp</b>'s
+ * process. */
+static process_environment_t *
+create_managed_proxy_environment(const managed_proxy_t *mp)
+{
+ const or_options_t *options = get_options();
+
+ /* Environment variables to be added to or set in mp's environment. */
+ smartlist_t *envs = smartlist_new();
+ /* XXXX The next time someone touches this code, shorten the name of
+ * set_environment_variable_in_smartlist, add a
+ * set_env_var_in_smartlist_asprintf function, and get rid of the
+ * silly extra envs smartlist. */
+
+ /* The final environment to be passed to mp. */
+ smartlist_t *merged_env_vars = get_current_process_environment_variables();
+
+ process_environment_t *env;
+
+ {
+ char *state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */
+ smartlist_add_asprintf(envs, "TOR_PT_STATE_LOCATION=%s", state_tmp);
+ tor_free(state_tmp);
+ }
+
+ smartlist_add_strdup(envs, "TOR_PT_MANAGED_TRANSPORT_VER=1");
+
+ {
+ char *transports_to_launch =
+ smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
+
+ smartlist_add_asprintf(envs,
+ mp->is_server ?
+ "TOR_PT_SERVER_TRANSPORTS=%s" :
+ "TOR_PT_CLIENT_TRANSPORTS=%s",
+ transports_to_launch);
+
+ tor_free(transports_to_launch);
+ }
+
+ if (mp->is_server) {
+ {
+ char *orport_tmp =
+ get_first_listener_addrport_string(CONN_TYPE_OR_LISTENER);
+ if (orport_tmp) {
+ smartlist_add_asprintf(envs, "TOR_PT_ORPORT=%s", orport_tmp);
+ tor_free(orport_tmp);
+ }
+ }
+
+ {
+ char *bindaddr_tmp = get_bindaddr_for_server_proxy(mp);
+ smartlist_add_asprintf(envs, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp);
+ tor_free(bindaddr_tmp);
+ }
+
+ {
+ char *server_transport_options =
+ get_transport_options_for_server_proxy(mp);
+ if (server_transport_options) {
+ smartlist_add_asprintf(envs, "TOR_PT_SERVER_TRANSPORT_OPTIONS=%s",
+ server_transport_options);
+ tor_free(server_transport_options);
+ }
+ }
+
+ /* XXXX Remove the '=' here once versions of obfsproxy which
+ * assert that this env var exists are sufficiently dead.
+ *
+ * (If we remove this line entirely, some joker will stick this
+ * variable in Tor's environment and crash PTs that try to parse
+ * it even when not run in server mode.) */
+
+ if (options->ExtORPort_lines) {
+ char *ext_or_addrport_tmp =
+ get_first_listener_addrport_string(CONN_TYPE_EXT_OR_LISTENER);
+ char *cookie_file_loc = get_ext_or_auth_cookie_file_name();
+
+ if (ext_or_addrport_tmp) {
+ smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s",
+ ext_or_addrport_tmp);
+ }
+ smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s",
+ cookie_file_loc);
+
+ tor_free(ext_or_addrport_tmp);
+ tor_free(cookie_file_loc);
+
+ } else {
+ smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
+ }
+
+ /* All new versions of tor will keep stdin open, so PTs can use it
+ * as a reliable termination detection mechanism.
+ */
+ smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1");
+ } else {
+ /* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the
+ * TOR_PT_PROXY line.
+ */
+
+ if (mp->proxy_uri) {
+ smartlist_add_asprintf(envs, "TOR_PT_PROXY=%s", mp->proxy_uri);
+ }
+ }
+
+ SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {
+ set_environment_variable_in_smartlist(merged_env_vars, env_var,
+ tor_free_, 1);
+ } SMARTLIST_FOREACH_END(env_var);
+
+ env = process_environment_make(merged_env_vars);
+
+ smartlist_free(envs);
+
+ SMARTLIST_FOREACH(merged_env_vars, void *, x, tor_free(x));
+ smartlist_free(merged_env_vars);
+
+ return env;
+}
+
+/** Create and return a new managed proxy for <b>transport</b> using
+ * <b>proxy_argv</b>. Also, add it to the global managed proxy list. If
+ * <b>is_server</b> is true, it's a server managed proxy. Takes ownership of
+ * <b>proxy_argv</b>.
+ *
+ * Requires that proxy_argv have at least one element. */
+STATIC managed_proxy_t *
+managed_proxy_create(const smartlist_t *with_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_new();
+ mp->proxy_uri = get_pt_proxy_uri();
+
+ mp->transports_to_launch = smartlist_new();
+ SMARTLIST_FOREACH(with_transport_list, const char *, transport,
+ add_transport_to_proxy(transport, mp));
+
+ /* register the managed proxy */
+ if (!managed_proxy_list)
+ managed_proxy_list = smartlist_new();
+ smartlist_add(managed_proxy_list, mp);
+ unconfigured_proxies_n++;
+
+ assert_unconfigured_count_ok();
+
+ return mp;
+}
+
+/** Register proxy with <b>proxy_argv</b>, supporting transports in
+ * <b>transport_list</b>, to the managed proxy subsystem.
+ * If <b>is_server</b> is true, then the proxy is a server proxy.
+ *
+ * Takes ownership of proxy_argv.
+ *
+ * Requires that proxy_argv be a NULL-terminated array of command-line
+ * elements, containing at least one element.
+ **/
+MOCK_IMPL(void,
+pt_kickstart_proxy, (const smartlist_t *with_transport_list,
+ char **proxy_argv, int is_server))
+{
+ managed_proxy_t *mp=NULL;
+ transport_t *old_transport = NULL;
+
+ if (!proxy_argv || !proxy_argv[0]) {
+ return;
+ }
+
+ mp = get_managed_proxy_by_argv_and_type(proxy_argv, is_server);
+
+ if (!mp) { /* we haven't seen this proxy before */
+ managed_proxy_create(with_transport_list, proxy_argv, is_server);
+
+ } else { /* known proxy. add its transport to its transport list */
+ if (mp->was_around_before_config_read) {
+ /* If this managed proxy was around even before we read the
+ config this time, it means that it was already enabled before
+ and is not useless and should be kept. If it's marked for
+ removal, unmark it and make sure that we check whether it
+ needs to be restarted. */
+ if (mp->marked_for_removal) {
+ mp->marked_for_removal = 0;
+ check_if_restarts_needed = 1;
+ }
+
+ /* For each new transport, check if the managed proxy used to
+ support it before the SIGHUP. If that was the case, make sure
+ it doesn't get removed because we might reuse it. */
+ SMARTLIST_FOREACH_BEGIN(with_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(with_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 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;
+
+ assert_unconfigured_count_ok();
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+ /* Destroy unconfigured proxies. */
+ if (mp->conf_state != PT_PROTO_COMPLETED) {
+ SMARTLIST_DEL_CURRENT(managed_proxy_list, mp);
+ managed_proxy_destroy(mp, 1);
+ unconfigured_proxies_n--;
+ continue;
+ }
+
+ tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+
+ /* Mark all proxies for removal, and also note that they have been
+ here before the config read. */
+ mp->marked_for_removal = 1;
+ mp->was_around_before_config_read = 1;
+ SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t));
+ smartlist_clear(mp->transports_to_launch);
+ } SMARTLIST_FOREACH_END(mp);
+
+ assert_unconfigured_count_ok();
+
+ tor_assert(unconfigured_proxies_n == 0);
+}
+
+/** Return a smartlist containing the ports where our pluggable
+ * transports are listening. */
+smartlist_t *
+get_transport_proxy_ports(void)
+{
+ smartlist_t *sl = NULL;
+
+ if (!managed_proxy_list)
+ return NULL;
+
+ /** XXX assume that external proxy ports have been forwarded
+ manually */
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) {
+ if (!mp->is_server || mp->conf_state != PT_PROTO_COMPLETED)
+ continue;
+
+ if (!sl) sl = smartlist_new();
+
+ tor_assert(mp->transports);
+ SMARTLIST_FOREACH(mp->transports, const transport_t *, t,
+ smartlist_add_asprintf(sl, "%u:%u", t->port, t->port));
+
+ } SMARTLIST_FOREACH_END(mp);
+
+ return sl;
+}
+
+/** Return the pluggable transport string that we should display in
+ * our extra-info descriptor. If we shouldn't display such a string,
+ * or we have nothing to display, return NULL. The string is
+ * allocated on the heap and it's the responsibility of the caller to
+ * free it. */
+char *
+pt_get_extra_info_descriptor_string(void)
+{
+ char *the_string = NULL;
+ smartlist_t *string_chunks = NULL;
+
+ if (!managed_proxy_list)
+ return NULL;
+
+ string_chunks = smartlist_new();
+
+ /* For each managed proxy, add its transports to the chunks list. */
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) {
+ if ((!mp->is_server) || (mp->conf_state != PT_PROTO_COMPLETED))
+ continue;
+
+ tor_assert(mp->transports);
+
+ SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) {
+ char *transport_args = NULL;
+
+ /* If the transport proxy returned "0.0.0.0" as its address, and
+ * we know our external IP address, use it. Otherwise, use the
+ * returned address. */
+ const char *addrport = NULL;
+ uint32_t external_ip_address = 0;
+ if (tor_addr_is_null(&t->addr) &&
+ router_pick_published_address(get_options(),
+ &external_ip_address, 0) >= 0) {
+ tor_addr_t addr;
+ tor_addr_from_ipv4h(&addr, external_ip_address);
+ addrport = fmt_addrport(&addr, t->port);
+ } else {
+ addrport = fmt_addrport(&t->addr, t->port);
+ }
+
+ /* If this transport has any arguments with it, prepend a space
+ to them so that we can add them to the transport line. */
+ if (t->extra_info_args)
+ tor_asprintf(&transport_args, " %s", t->extra_info_args);
+
+ smartlist_add_asprintf(string_chunks,
+ "transport %s %s%s",
+ t->name, addrport,
+ transport_args ? transport_args : "");
+ tor_free(transport_args);
+ } SMARTLIST_FOREACH_END(t);
+
+ } SMARTLIST_FOREACH_END(mp);
+
+ if (smartlist_len(string_chunks) == 0) {
+ smartlist_free(string_chunks);
+ return NULL;
+ }
+
+ /* Join all the chunks into the final string. */
+ the_string = smartlist_join_strings(string_chunks, "\n", 1, NULL);
+
+ SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s));
+ smartlist_free(string_chunks);
+
+ return the_string;
+}
+
+/** Stringify the SOCKS arguments in <b>socks_args</b> according to
+ * 180_pluggable_transport.txt. The string is allocated on the heap
+ * and it's the responsibility of the caller to free it after use. */
+char *
+pt_stringify_socks_args(const smartlist_t *socks_args)
+{
+ /* tmp place to store escaped socks arguments, so that we can
+ concatenate them up afterwards */
+ smartlist_t *sl_tmp = NULL;
+ char *escaped_string = NULL;
+ char *new_string = NULL;
+
+ tor_assert(socks_args);
+ tor_assert(smartlist_len(socks_args) > 0);
+
+ sl_tmp = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(socks_args, const char *, s) {
+ /* Escape ';' and '\'. */
+ escaped_string = tor_escape_str_for_pt_args(s, ";\\");
+ if (!escaped_string)
+ goto done;
+
+ smartlist_add(sl_tmp, escaped_string);
+ } SMARTLIST_FOREACH_END(s);
+
+ new_string = smartlist_join_strings(sl_tmp, ";", 0, NULL);
+
+ done:
+ SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
+ smartlist_free(sl_tmp);
+
+ return new_string;
+}
+
+/** Return a string of the SOCKS arguments that we should pass to the
+ * pluggable transports proxy in <b>addr</b>:<b>port</b> according to
+ * 180_pluggable_transport.txt. The string is allocated on the heap
+ * and it's the responsibility of the caller to free it after use. */
+char *
+pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr, uint16_t port)
+{
+ const smartlist_t *socks_args = NULL;
+
+ socks_args = get_socks_args_by_bridge_addrport(addr, port);
+ if (!socks_args)
+ return NULL;
+
+ return pt_stringify_socks_args(socks_args);
+}
+
+/** 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;
+ assert_unconfigured_count_ok();
+ 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, 1);
+ }
+ } SMARTLIST_FOREACH_END(mp);
+ assert_unconfigured_count_ok();
+}
+
+/** Release all storage held by the pluggable transports subsystem. */
+void
+pt_free_all(void)
+{
+ if (transport_list) {
+ clear_transport_list();
+ smartlist_free(transport_list);
+ transport_list = NULL;
+ }
+
+ 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, {
+ SMARTLIST_DEL_CURRENT(managed_proxy_list, mp);
+ managed_proxy_destroy(mp, 1);
+ });
+
+ smartlist_free(managed_proxy_list);
+ managed_proxy_list=NULL;
+ }
+}
+
+/** Return a newly allocated string equal to <b>string</b>, except that every
+ * character in <b>chars_to_escape</b> is preceded by a backslash. */
+char *
+tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape)
+{
+ char *new_string = NULL;
+ char *new_cp = NULL;
+ size_t length, new_length;
+
+ tor_assert(string);
+
+ length = strlen(string);
+
+ if (!length) /* If we were given the empty string, return the same. */
+ return tor_strdup("");
+ /* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) =>
+ (length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */
+ if (length > (SIZE_MAX - 1)/2) /* check for overflow */
+ return NULL;
+
+ /* this should be enough even if all characters must be escaped */
+ new_length = (length * 2) + 1;
+
+ new_string = new_cp = tor_malloc(new_length);
+
+ while (*string) {
+ if (strchr(chars_to_escape, *string))
+ *new_cp++ = '\\';
+
+ *new_cp++ = *string++;
+ }
+
+ *new_cp = '\0'; /* NUL-terminate the new string */
+
+ return new_string;
+}