aboutsummaryrefslogtreecommitdiff
path: root/src/or/transports.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/transports.c')
-rw-r--r--src/or/transports.c801
1 files changed, 607 insertions, 194 deletions
diff --git a/src/or/transports.c b/src/or/transports.c
index 4ba239562..dc3075416 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Tor Project, Inc. */
+/* Copyright (c) 2011-2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -39,43 +39,49 @@
* 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
- * pointers to transport_t can be transformed into dangling pointers
- * at any point by the circuitbuild.c subsystem, and so we replace all
- * transport_t pointers with strings describing the transport names.
- * We can still go from a transport name to a transport_t using the
- * fact that each transport name uniquely identifies a transport_t.
+ * '{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 SIGHUP
- * occurs:</b>
+ * <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 SIGHUP rings
- * immediately after tor is launched.).
+ * 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 (using the <b>got_hup</b> element).
+ * 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 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 <b>transports_to_launch</b> 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.
+ * 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-SIGHUP torrc parsing, we unmark all transports
+ * 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.
@@ -90,18 +96,18 @@
#include "transports.h"
#include "util.h"
#include "router.h"
+#include "statefile.h"
+#include "entrynodes.h"
+#include "connection_or.h"
+#include "ext_orport.h"
+#include "control.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 managed_proxy_destroy(managed_proxy_t *mp,
- int also_terminate_process);
-
static void handle_finished_proxy(managed_proxy_t *mp);
-static void configure_proxy(managed_proxy_t *mp);
-
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)
@@ -119,14 +125,232 @@ static INLINE void free_execve_args(char **arg);
#define PROTO_CMETHODS_DONE "CMETHODS DONE"
#define PROTO_SMETHODS_DONE "SMETHODS DONE"
-/** Number of environment variables for managed proxy clients/servers. */
-#define ENVIRON_SIZE_CLIENT 3
-#define ENVIRON_SIZE_SERVER 7 /* XXX known to be too high, but that's ok */
-
/** 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. */
+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. */
+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. */
@@ -205,7 +429,7 @@ 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))
+ if (!smartlist_contains_string(mp->transports_to_launch, transport))
smartlist_add(mp->transports_to_launch, tor_strdup(transport));
}
@@ -217,11 +441,11 @@ 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.
+ mp->transports is populated with 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. */
+ 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);
@@ -229,11 +453,11 @@ proxy_needs_restart(const managed_proxy_t *mp)
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))
+ 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_t_l);
+ } SMARTLIST_FOREACH_END(t);
return 0;
@@ -245,6 +469,7 @@ proxy_needs_restart(const managed_proxy_t *mp)
* 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;
@@ -255,16 +480,17 @@ proxy_prepare_for_restart(managed_proxy_t *mp)
tor_process_handle_destroy(mp->process_handle, 1);
mp->process_handle = NULL;
- /* 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);
+ /* 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_name);
+ } SMARTLIST_FOREACH_END(t);
sweep_transport_list();
- /* free the transport names in mp->transports */
- SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name));
+ /* free the transport in mp->transports */
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
smartlist_clear(mp->transports);
/* flag it as an infant proxy so that it gets launched on next tick */
@@ -310,11 +536,11 @@ launch_managed_proxy(managed_proxy_t *mp)
}
/** 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(). */
+ * 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)!",
@@ -327,14 +553,15 @@ pt_configure_remaining_proxies(void)
assert_unconfigured_count_ok();
SMARTLIST_FOREACH_BEGIN(tmp, managed_proxy_t *, mp) {
- tor_assert(mp->conf_state != PT_PROTO_BROKEN ||
+ tor_assert(mp->conf_state != PT_PROTO_BROKEN &&
mp->conf_state != PT_PROTO_FAILED_LAUNCH);
- if (mp->got_hup) {
- mp->got_hup = 0;
+ 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;
- /* 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 '%s' for restart.",
mp->argv[0]);
@@ -350,24 +577,29 @@ pt_configure_remaining_proxies(void)
/* If the proxy is not fully configured, try to configure it
futher. */
if (!proxy_configuration_finished(mp))
- configure_proxy(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();
-}
-#ifdef _WIN32
+ if (at_least_a_proxy_config_finished)
+ mark_my_descriptor_dirty("configured managed proxies");
+}
-/** Attempt to continue configuring managed proxy <b>mp</b>. */
-static void
+/** 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 pos;
- char stdout_buf[200];
- smartlist_t *lines = NULL;
+ 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) {
@@ -375,34 +607,31 @@ configure_proxy(managed_proxy_t *mp)
mp->conf_state = PT_PROTO_FAILED_LAUNCH;
handle_finished_proxy(mp);
}
- return;
+ return 0;
}
tor_assert(mp->conf_state != PT_PROTO_INFANT);
tor_assert(mp->process_handle);
- pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle),
- stdout_buf, sizeof(stdout_buf) - 1, NULL);
- if (pos < 0) {
- log_notice(LD_GENERAL, "Failed to read data from managed proxy '%s'.",
- mp->argv[0]);
- mp->conf_state = PT_PROTO_BROKEN;
+ 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;
}
- if (pos == 0) /* proxy has nothing interesting to say. */
- return;
-
- /* End with a null even if there isn't a \r\n at the end */
- /* TODO: What if this is a partial line? */
- stdout_buf[pos] = '\0';
-
- /* Split up the buffer */
- lines = smartlist_new();
- tor_split_lines(lines, stdout_buf, pos);
-
/* Handle lines. */
- SMARTLIST_FOREACH_BEGIN(lines, const char *, line) {
+ SMARTLIST_FOREACH_BEGIN(proxy_output, const char *, line) {
handle_proxy_line(line, mp);
if (proxy_configuration_finished(mp))
goto done;
@@ -410,126 +639,66 @@ configure_proxy(managed_proxy_t *mp)
done:
/* if the proxy finished configuring, exit the loop. */
- if (proxy_configuration_finished(mp))
+ if (proxy_configuration_finished(mp)) {
handle_finished_proxy(mp);
-
- if (lines)
- smartlist_free(lines);
-}
-
-#else /* _WIN32 */
-
-/** 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) {
- if (launch_managed_proxy(mp) < 0) { /* launch fail */
- mp->conf_state = PT_PROTO_FAILED_LAUNCH;
- handle_finished_proxy(mp);
- }
- return;
+ configuration_finished = 1;
}
- tor_assert(mp->conf_state != PT_PROTO_INFANT);
- tor_assert(mp->process_handle);
-
- while (1) {
- r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle),
- 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_warn(LD_GENERAL, "Our communication channel with the managed proxy "
- "'%s' closed. Most probably application stopped running.",
- mp->argv[0]);
- mp->conf_state = PT_PROTO_BROKEN;
- } else { /* unknown stream status */
- log_warn(LD_BUG, "Unknown stream status '%d' while configuring managed "
- "proxy '%s'.", (int)r, mp->argv[0]);
- }
-
- /* if the proxy finished configuring, exit the loop. */
- if (proxy_configuration_finished(mp)) {
- handle_finished_proxy(mp);
- return;
- }
+ if (proxy_output) {
+ SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp));
+ smartlist_free(proxy_output);
}
-}
-#endif /* _WIN32 */
+ return configuration_finished;
+}
/** Register server managed proxy <b>mp</b> transports to state */
static void
-register_server_proxy(managed_proxy_t *mp)
+register_server_proxy(const 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_new();
-
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:%d'",
- t->name, fmt_addr(&t->addr), (int)t->port);
- smartlist_add(sm_tmp, tor_strdup(t->name));
+ 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);
-
- /* 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)
+register_client_proxy(const 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_new();
tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
+
SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
- r = transport_add(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(t);
+ transport_free(transport_tmp);
break;
case 0:
- log_info(LD_GENERAL, "Succesfully registered transport %s", t->name);
- smartlist_add(sm_tmp, tor_strdup(t->name));
+ 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, "Succesfully registered transport %s", t->name);
- smartlist_add(sm_tmp, tor_strdup(t->name));
- transport_free(t);
+ 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);
-
- 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)
+register_proxy(const managed_proxy_t *mp)
{
if (mp->is_server)
register_server_proxy(mp);
@@ -538,14 +707,11 @@ register_proxy(managed_proxy_t *mp)
}
/** Free memory allocated by managed proxy <b>mp</b>. */
-static void
+STATIC void
managed_proxy_destroy(managed_proxy_t *mp,
int also_terminate_process)
{
- 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));
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
/* free the transports smartlist */
smartlist_free(mp->transports);
@@ -555,7 +721,8 @@ managed_proxy_destroy(managed_proxy_t *mp,
smartlist_free(mp->transports_to_launch);
/* remove it from the list of managed proxies */
- smartlist_remove(managed_proxy_list, mp);
+ if (managed_proxy_list)
+ smartlist_remove(managed_proxy_list, mp);
/* free the argv */
free_execve_args(mp->argv);
@@ -592,7 +759,6 @@ handle_finished_proxy(managed_proxy_t *mp)
}
unconfigured_proxies_n--;
- tor_assert(unconfigured_proxies_n >= 0);
}
/** Return true if the configuration of the managed proxy <b>mp</b> is
@@ -623,7 +789,7 @@ handle_methods_done(const managed_proxy_t *mp)
/** Handle a configuration protocol <b>line</b> received from a
* managed proxy <b>mp</b>. */
-void
+STATIC void
handle_proxy_line(const char *line, managed_proxy_t *mp)
{
log_info(LD_GENERAL, "Got a line from managed proxy '%s': (%s)",
@@ -724,7 +890,7 @@ handle_proxy_line(const char *line, managed_proxy_t *mp)
}
/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
-void
+STATIC void
parse_env_error(const char *line)
{
/* (Length of the protocol string) plus (a space) and (the first char of
@@ -740,7 +906,7 @@ parse_env_error(const char *line)
/** Handles a VERSION <b>line</b>. Updates the configuration protocol
* version in <b>mp</b>. */
-int
+STATIC int
parse_version(const char *line, managed_proxy_t *mp)
{
if (strlen(line) < (strlen(PROTO_NEG_SUCCESS) + 2)) {
@@ -781,14 +947,14 @@ parse_method_error(const char *line, int is_server)
/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
* new transport in <b>mp</b>. */
-int
+STATIC int
parse_smethod_line(const char *line, managed_proxy_t *mp)
{
int r;
smartlist_t *items = NULL;
char *method_name=NULL;
-
+ char *args_string=NULL;
char *addrport=NULL;
tor_addr_t tor_addr;
char *address=NULL;
@@ -805,6 +971,9 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
+ /* Example of legit SMETHOD line:
+ SMETHOD obfs2 0.0.0.0:25612 ARGS:secret=supersekrit,key=superkey */
+
tor_assert(!strcmp(smartlist_get(items,0),PROTO_SMETHOD));
method_name = smartlist_get(items,1);
@@ -815,7 +984,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
}
addrport = smartlist_get(items, 2);
- if (tor_addr_port_split(LOG_PROTOCOL_WARN, addrport, &address, &port)<0) {
+ if (tor_addr_port_split(LOG_WARN, addrport, &address, &port)<0) {
log_warn(LD_CONFIG, "Error parsing transport "
"address '%s'", addrport);
goto err;
@@ -832,7 +1001,19 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
- transport = transport_new(&tor_addr, port, method_name, PROXY_NONE);
+ if (smartlist_len(items) > 3) {
+ /* 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, 3);
+ 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, method_name,
+ PROXY_NONE, args_string);
if (!transport)
goto err;
@@ -858,7 +1039,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
/** Parses a CMETHOD <b>line</b>, and if well-formed it registers
* the new transport in <b>mp</b>. */
-int
+STATIC int
parse_cmethod_line(const char *line, managed_proxy_t *mp)
{
int r;
@@ -907,7 +1088,7 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
}
addrport = smartlist_get(items, 3);
- if (tor_addr_port_split(LOG_PROTOCOL_WARN, addrport, &address, &port)<0) {
+ if (tor_addr_port_split(LOG_WARN, addrport, &address, &port)<0) {
log_warn(LD_CONFIG, "Error parsing transport "
"address '%s'", addrport);
goto err;
@@ -924,7 +1105,7 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
goto err;
}
- transport = transport_new(&tor_addr, port, method_name, socks_ver);
+ transport = transport_new(&tor_addr, port, method_name, socks_ver, NULL);
if (!transport)
goto err;
@@ -947,6 +1128,50 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
return r;
}
+/** Return a newly allocated string that tor should place in
+ * TOR_PT_SERVER_TRANSPORT_OPTIONS while configuring the server
+ * manged proxy in <b>mp</b>. Return NULL if no such options are found. */
+STATIC char *
+get_transport_options_for_server_proxy(const managed_proxy_t *mp)
+{
+ char *options_string = NULL;
+ smartlist_t *string_sl = smartlist_new();
+
+ tor_assert(mp->is_server);
+
+ /** Loop over the transports of the proxy. If we have options for
+ any of them, format them appropriately and place them in our
+ smartlist. Finally, join our smartlist to get the final
+ string. */
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) {
+ smartlist_t *options_tmp_sl = NULL;
+ options_tmp_sl = get_options_for_server_transport(transport);
+ if (!options_tmp_sl)
+ continue;
+
+ /** Loop over the options of this transport, escape them, and
+ place them in the smartlist. */
+ SMARTLIST_FOREACH_BEGIN(options_tmp_sl, const char *, options) {
+ char *escaped_opts = tor_escape_str_for_pt_args(options, ":;\\");
+ smartlist_add_asprintf(string_sl, "%s:%s",
+ transport, escaped_opts);
+ tor_free(escaped_opts);
+ } SMARTLIST_FOREACH_END(options);
+
+ SMARTLIST_FOREACH(options_tmp_sl, char *, c, tor_free(c));
+ smartlist_free(options_tmp_sl);
+ } SMARTLIST_FOREACH_END(transport);
+
+ if (smartlist_len(string_sl)) {
+ options_string = smartlist_join_strings(string_sl, ";", 0, NULL);
+ }
+
+ SMARTLIST_FOREACH(string_sl, char *, t, tor_free(t));
+ smartlist_free(string_sl);
+
+ return options_string;
+}
+
/** Return the string that tor should place in TOR_PT_SERVER_BINDADDR
* while configuring the server managed proxy in <b>mp</b>. The
* string is stored in the heap, and it's the the responsibility of
@@ -981,6 +1206,8 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
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
@@ -1018,8 +1245,10 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
{
char *orport_tmp =
get_first_listener_addrport_string(CONN_TYPE_OR_LISTENER);
- smartlist_add_asprintf(envs, "TOR_PT_ORPORT=%s", orport_tmp);
- tor_free(orport_tmp);
+ if (orport_tmp) {
+ smartlist_add_asprintf(envs, "TOR_PT_ORPORT=%s", orport_tmp);
+ tor_free(orport_tmp);
+ }
}
{
@@ -1028,18 +1257,46 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
tor_free(bindaddr_tmp);
}
+ {
+ char *server_transport_options =
+ get_transport_options_for_server_proxy(mp);
+ if (server_transport_options) {
+ smartlist_add_asprintf(envs, "TOR_PT_SERVER_TRANSPORT_OPTIONS=%s",
+ server_transport_options);
+ tor_free(server_transport_options);
+ }
+ }
+
/* XXX024 Remove the '=' here once versions of obfsproxy which
* assert that this env var exists are sufficiently dead.
*
* (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.) */
- smartlist_add(envs, tor_strdup("TOR_PT_EXTENDED_SERVER_PORT="));
+
+ 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=");
+ }
}
SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {
set_environment_variable_in_smartlist(merged_env_vars, env_var,
- _tor_free, 1);
+ tor_free_, 1);
} SMARTLIST_FOREACH_END(env_var);
env = process_environment_make(merged_env_vars);
@@ -1058,7 +1315,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
* <b>proxy_argv</b>.
*
* Requires that proxy_argv have at least one element. */
-static managed_proxy_t *
+STATIC managed_proxy_t *
managed_proxy_create(const smartlist_t *transport_list,
char **proxy_argv, int is_server)
{
@@ -1109,19 +1366,20 @@ pt_kickstart_proxy(const smartlist_t *transport_list,
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->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(transport_list, const char *, transport) {
old_transport = transport_get_by_name(transport);
if (old_transport)
@@ -1143,7 +1401,7 @@ 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_(*tmp++);
tor_free(arg);
}
@@ -1170,8 +1428,10 @@ pt_prepare_proxy_list_for_config_read(void)
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->got_hup = 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);
@@ -1181,6 +1441,153 @@ pt_prepare_proxy_list_for_config_read(void)
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) {
+ 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
@@ -1204,6 +1611,12 @@ sweep_proxy_list(void)
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