[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [obfsproxy/master] Introduce managed.[ch] to the world.



commit d3ea7d5c0461d237ec3c1e437bc6609d0d0a1075
Author: George Kadianakis <desnacked@xxxxxxxxx>
Date:   Sat Aug 20 06:35:30 2011 +0200

    Introduce managed.[ch] to the world.
---
 src/managed.c |  670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/managed.h |   16 ++
 2 files changed, 686 insertions(+), 0 deletions(-)

diff --git a/src/managed.c b/src/managed.c
new file mode 100644
index 0000000..979bc38
--- /dev/null
+++ b/src/managed.c
@@ -0,0 +1,670 @@
+/* Copyright 2011 Nick Mathewson, George Kadianakis
+   See LICENSE for other credits and copying information
+*/
+
+#include "util.h"
+
+#define MANAGED_PRIVATE
+#include "managed.h"
+#include "network.h"
+#include "protocol.h"
+#include "main.h"
+
+#include "container.h"
+
+#include <event2/event.h>
+#include <event2/listener.h>
+
+#include <arpa/inet.h> /* for inet_ntoa() */
+
+/** Holds all the parameters provided by tor through environment
+    variables. */
+typedef struct managed_env_vars_t {
+  char *state_loc; /* where we should store our state */
+  char *conf_proto_version; /* managed proxy configuration protocols tor supports */
+  char *transports; /* pluggable transports tor wants us to launch */
+
+  /* server-only */
+  char *extended_port; /* extended OR port tor wants us to use */
+  char *or_port; /* ORPort that tor uses */
+  char *bindaddrs; /* address to listen for client proxy connections */
+} managed_env_vars_t;
+
+/** Holds data for this managed proxy. */
+typedef struct managed_proxy_t {
+  unsigned int is_server : 1; /* whether we are a server or a client */
+
+  managed_env_vars_t vars; /* environment variables */
+
+  smartlist_t *configs; /* configs created */
+} managed_proxy_t;
+
+/* Holds the status of the environment variables parsing, so that we
+   can report a granular error if something fails. */
+enum env_parsing_status {
+  ST_ENV_SMOOTH,
+  ST_ENV_FAIL_STATE_LOC,
+  ST_ENV_FAIL_CONF_PROTO_VER,
+  ST_ENV_FAIL_CLIENT_TRANSPORTS,
+  ST_ENV_FAIL_EXTENDED_PORT,
+  ST_ENV_FAIL_ORPORT,
+  ST_ENV_FAIL_BINDADDR,
+  ST_ENV_FAIL_SERVER_TRANSPORTS,
+};
+
+/* Holds the status of a listener launch. */
+enum launch_status {
+  ST_LAUNCH_SMOOTH,
+  ST_LAUNCH_FAIL_SETUP,
+  ST_LAUNCH_FAIL_LSN,
+};
+
+/** Managed proxy protocol strings */
+#define PROTO_ENV_ERROR "ENV-ERROR"
+#define PROTO_NEG_SUCCESS "VERSION"
+#define PROTO_NEG_FAIL "VERSION-ERROR no-version\n"
+#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\n"
+#define PROTO_SMETHODS_DONE "SMETHODS DONE\n"
+
+static int handle_environment(managed_proxy_t *proxy);
+static int conf_proto_version_negotiation(const managed_proxy_t *proxy);
+static int launch_listeners(const managed_proxy_t *proxy);
+static void print_method_line(const char *transport,
+                              const managed_proxy_t *proxy,
+                              config_t *cfg);
+static void print_method_error_line(const char *protocol,
+                                    const managed_proxy_t *proxy,
+                                    enum launch_status status);
+static const char *get_launch_error_message(enum launch_status status);
+
+static void print_method_done_line(const managed_proxy_t *proxy);
+
+static const char *get_env_parsing_error_message(enum env_parsing_status status);
+
+static void proxy_free(managed_proxy_t *proxy);
+static int is_supported_conf_protocol(const char *name);
+static void print_protocol_line(const char *format, ...);
+
+static int validate_environment(managed_proxy_t *proxy);
+
+const char *supported_conf_protocols[] = { "1" };
+const size_t n_supported_conf_protocols =
+  sizeof(supported_conf_protocols)/sizeof(supported_conf_protocols[0]);
+
+/**
+   This function fires up the managed proxy.
+   It first reads the environment that tor should have prepared, and then
+   launches the appropriate listeners.
+
+   Returns 0 if we managed to launch at least one proxy,
+   returns -1 if something went wrong or we didn't launch any proxies.
+*/
+int
+launch_managed_proxy(void)
+{
+  int r=-1;
+  managed_proxy_t *proxy = xzalloc(sizeof(managed_proxy_t));
+  proxy->configs = smartlist_create();
+
+  obfsproxy_init();
+
+  if (handle_environment(proxy) < 0)
+    goto done;
+
+  if (validate_environment(proxy) < 0)
+    goto done;
+
+  if (conf_proto_version_negotiation(proxy) < 0)
+    goto done;
+
+  if (launch_listeners(proxy) < 0)
+    goto done;
+
+  /* Kickstart libevent */
+  event_base_dispatch(get_event_base());
+
+  r=0;
+
+ done:
+  obfsproxy_cleanup();
+  proxy_free(proxy);
+
+  return r;
+}
+
+/**
+   Make sure that we got *all* the necessary environment variables off tor.
+*/
+static inline void
+assert_proxy_env(managed_proxy_t *proxy)
+{
+  obfs_assert(proxy);
+
+  obfs_assert(proxy->vars.state_loc);
+  obfs_assert(proxy->vars.conf_proto_version);
+  obfs_assert(proxy->vars.transports);
+  if (proxy->is_server) {
+    obfs_assert(proxy->vars.extended_port);
+    obfs_assert(proxy->vars.or_port);
+    obfs_assert(proxy->vars.bindaddrs);
+  } else {
+    obfs_assert(!proxy->vars.extended_port);
+    obfs_assert(!proxy->vars.or_port);
+    obfs_assert(!proxy->vars.bindaddrs);
+  }
+}
+
+/**
+   Validates the environment variables that we got off tor.
+*/
+static int
+validate_environment(managed_proxy_t *proxy)
+{
+  enum env_parsing_status status;
+
+  assert_proxy_env(proxy);
+
+  if (proxy->is_server) {
+    if (validate_bindaddrs(proxy->vars.bindaddrs, proxy->vars.transports) < 0) {
+      status = ST_ENV_FAIL_BINDADDR;
+      goto err;
+    }
+  }
+
+  return 0;
+
+ err:
+  print_protocol_line("%s %s\n", PROTO_ENV_ERROR,
+                      get_env_parsing_error_message(status));
+
+  return -1;
+}
+
+/**
+   Free memory allocated by 'proxy'.
+*/
+static void
+proxy_free(managed_proxy_t *proxy)
+{
+  free(proxy->vars.state_loc);
+  free(proxy->vars.conf_proto_version);
+  free(proxy->vars.transports);
+  free(proxy->vars.extended_port);
+  free(proxy->vars.or_port);
+  free(proxy->vars.bindaddrs);
+
+  SMARTLIST_FOREACH(proxy->configs, config_t *, cfg, config_free(cfg));
+  smartlist_free(proxy->configs);
+
+  free(proxy);
+}
+
+/**
+    Given a smartlist of listener configs; launch them all and print
+    method lines as appropriate.  Return 0 if at least a listener was
+    spawned, -1 otherwise.
+*/
+static int
+open_listeners_managed(const managed_proxy_t *proxy)
+{
+  int ret=-1;
+  const char *transport;
+
+  /* Open listeners for each configuration. */
+  SMARTLIST_FOREACH_BEGIN(proxy->configs, config_t *, cfg) {
+    transport = get_transport_name_from_config(cfg);
+
+    if (open_listeners(get_event_base(), cfg)) {
+      ret=0; /* success! launched at least one listener. */
+      print_method_line(transport, proxy, cfg);
+    } else { /* fail. print a method error line. */
+      print_method_error_line(transport, proxy, ST_LAUNCH_FAIL_LSN);
+    }
+  } SMARTLIST_FOREACH_END(cfg);
+
+  return ret;
+}
+
+/**
+   Launch all server listeners that tor wants us to launch.
+*/
+static int
+launch_server_listeners(const managed_proxy_t *proxy)
+{
+  int ret=-1;
+  int i, n_transports;
+  const char *transport = NULL;
+  const char *bindaddr_temp = NULL;
+  const char *bindaddr = NULL;
+  config_t *cfg = NULL;
+
+  /* list of transports */
+  smartlist_t *transports = smartlist_create();
+  /* a list of "<transport>-<bindaddr>" strings */
+  smartlist_t *bindaddrs = smartlist_create();
+
+  /* split the comma-separated transports/bindaddrs to their smartlists */
+  smartlist_split_string(transports, proxy->vars.transports, ",",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+  smartlist_split_string(bindaddrs, proxy->vars.bindaddrs, ",",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+  n_transports = smartlist_len(transports);
+
+  /* Iterate transports; match them with their bindaddr; create their
+     config_t. */
+  for (i=0;i<n_transports;i++) {
+    transport = smartlist_get(transports, i);
+    bindaddr_temp = smartlist_get(bindaddrs, i);
+
+    obfs_assert(strlen(bindaddr_temp) > strlen(transport)+1);
+
+    bindaddr = bindaddr_temp+strlen(transport)+1; /* +1 for the dash */
+
+    cfg = config_create_managed(proxy->is_server,
+                                transport, bindaddr, proxy->vars.or_port);
+    if (cfg) /* if a config was created; put it in the config smartlist */
+      smartlist_add(proxy->configs, cfg);
+    else /* otherwise, spit a method error line */
+      print_method_error_line(transport, proxy, ST_LAUNCH_FAIL_SETUP);
+  }
+
+  /* open listeners */
+  ret = open_listeners_managed(proxy);
+
+  SMARTLIST_FOREACH(bindaddrs, char *, cp, free(cp));
+  smartlist_free(bindaddrs);
+  SMARTLIST_FOREACH(transports, char *, cp, free(cp));
+  smartlist_free(transports);
+
+  print_method_done_line(proxy); /* print {C,S}METHODS DONE */
+
+  return ret;
+}
+
+/**
+   Launch all client listeners that tor wants us to launch.
+*/
+static int
+launch_client_listeners(const managed_proxy_t *proxy)
+{
+  int ret=-1;
+  config_t *cfg = NULL;
+
+  /* list of transports */
+  smartlist_t *transports = smartlist_create();
+
+  smartlist_split_string(transports, proxy->vars.transports, ",",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+  SMARTLIST_FOREACH_BEGIN(transports, char *, transport) {
+    /* clients should find their own listen port */
+    cfg = config_create_managed(proxy->is_server,
+                                transport, "127.0.0.1:0", proxy->vars.or_port);
+    if (cfg) /* if config was created; put it in the config smartlist */
+      smartlist_add(proxy->configs, cfg);
+    else
+        print_method_error_line(transport, proxy, ST_LAUNCH_FAIL_SETUP);
+  } SMARTLIST_FOREACH_END(transport);
+
+  /* open listeners */
+  ret = open_listeners_managed(proxy);
+
+  SMARTLIST_FOREACH(transports, char *, cp, free(cp));
+  smartlist_free(transports);
+
+  print_method_done_line(proxy); /* print {C,S}METHODS DONE */
+
+  return ret;
+}
+
+/**
+   Launch all listeners that tor wants us to launch.
+   Return 0 if at least a listener was launched, -1 otherwise.
+*/
+static inline int
+launch_listeners(const managed_proxy_t *proxy)
+{
+  if (proxy->is_server)
+    return launch_server_listeners(proxy);
+  else
+    return launch_client_listeners(proxy);
+}
+
+/**
+   Given:
+   * comma-separated list of "<transport>-<bindaddr>" strings in 'all_bindaddrs'.
+   * comma-separated list of "<transport>" strings in 'all_transports'.
+
+   Return:
+   * 0, if
+   - all <transport> strings in 'all_bindaddrs' match with <transport>
+     strings in 'all_transports' (order matters).
+   AND
+   - if all <bindaddr> strings in 'all_bindaddrs' are valid addrports.
+   * -1, otherwise.
+*/
+int
+validate_bindaddrs(const char *all_bindaddrs, const char *all_transports)
+{
+  int ret,i,n_bindaddrs,n_transports;
+  struct evutil_addrinfo *bindaddr_test;
+  char *bindaddr = NULL;
+  char *transport = NULL;
+
+  /* a list of "<proto>-<bindaddr>" strings */
+  smartlist_t *bindaddrs = smartlist_create();
+  /* a list holding (<proto>, <bindaddr>) for each 'bindaddrs' entry */
+  smartlist_t *split_bindaddr = NULL;
+  /* a list of "<proto>" strings */
+  smartlist_t *transports = smartlist_create();
+
+  smartlist_split_string(bindaddrs, all_bindaddrs, ",",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+  smartlist_split_string(transports, all_transports, ",",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+  n_bindaddrs = smartlist_len(bindaddrs);
+  n_transports = smartlist_len(transports);
+
+  if (n_bindaddrs != n_transports)
+    goto err;
+
+  for (i=0;i<n_bindaddrs;i++) {
+    bindaddr = smartlist_get(bindaddrs, i);
+    transport = smartlist_get(transports, i);
+
+    split_bindaddr = smartlist_create();
+    smartlist_split_string(split_bindaddr, bindaddr, "-",
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+    /* (<proto>, <bindaddr>) */
+    if (smartlist_len(split_bindaddr) != 2)
+      goto err;
+
+    /* "all <transport> strings in 'all_bindaddrs' match with <transport>
+       strings in 'all_transports' (order matters)." */
+    if (strcmp(smartlist_get(split_bindaddr,0), transport))
+      goto err;
+
+    /* "if all <bindaddr> strings in 'all_bindaddrs' are valid addrports." */
+    bindaddr_test = resolve_address_port(smartlist_get(split_bindaddr, 1),
+                                         1, 1, NULL);
+    if (!bindaddr_test)
+      goto err;
+
+    evutil_freeaddrinfo(bindaddr_test);
+    SMARTLIST_FOREACH(split_bindaddr, char *, cp, free(cp));
+    smartlist_free(split_bindaddr);
+    split_bindaddr = NULL;
+  }
+
+  ret = 0;
+  goto done;
+
+ err:
+  ret = -1;
+
+ done:
+  SMARTLIST_FOREACH(bindaddrs, char *, cp, free(cp));
+  smartlist_free(bindaddrs);
+  SMARTLIST_FOREACH(transports, char *, cp, free(cp));
+  smartlist_free(transports);
+  if (split_bindaddr) {
+    SMARTLIST_FOREACH(split_bindaddr, char *, cp, free(cp));
+    smartlist_free(split_bindaddr);
+  }
+
+  return ret;
+}
+
+/**
+   Read the environment variables defined in the
+   180-pluggable-transport.txt spec and fill 'proxy' accordingly.
+
+   Return 0 if all necessary environment variables were set properly.
+   Return -1 otherwise.
+*/
+static int
+handle_environment(managed_proxy_t *proxy)
+{
+  char *tmp;
+  enum env_parsing_status status=ST_ENV_SMOOTH;
+
+  tmp = getenv("TOR_PT_STATE_LOCATION");
+  if (!tmp) {
+    status = ST_ENV_FAIL_STATE_LOC;
+    goto err;
+  }
+  proxy->vars.state_loc = xstrdup(tmp);
+
+  tmp = getenv("TOR_PT_MANAGED_TRANSPORT_VER");
+  if (!tmp) {
+    status = ST_ENV_FAIL_CONF_PROTO_VER;
+    goto err;
+  }
+  proxy->vars.conf_proto_version = xstrdup(tmp);
+
+  tmp = getenv("TOR_PT_CLIENT_TRANSPORTS");
+  if (tmp) {
+    proxy->vars.transports = xstrdup(tmp);
+    proxy->is_server = 0;
+  } else {
+    proxy->is_server = 1;
+  }
+
+  if (proxy->is_server) {
+    tmp = getenv("TOR_PT_EXTENDED_SERVER_PORT");
+    if (!tmp) {
+      status = ST_ENV_FAIL_EXTENDED_PORT;
+      goto err;
+    }
+    proxy->vars.extended_port = xstrdup(tmp);
+
+    tmp = getenv("TOR_PT_ORPORT");
+    if (!tmp) {
+      status = ST_ENV_FAIL_ORPORT;
+      goto err;
+    }
+    proxy->vars.or_port = xstrdup(tmp);
+
+    tmp = getenv("TOR_PT_SERVER_BINDADDR");
+    if (tmp) {
+      proxy->vars.bindaddrs = xstrdup(tmp);
+    } else {
+      status = ST_ENV_FAIL_BINDADDR;
+      goto err;
+    }
+
+    tmp = getenv("TOR_PT_SERVER_TRANSPORTS");
+    if (!tmp) {
+      status = ST_ENV_FAIL_SERVER_TRANSPORTS;
+      goto err;
+    }
+    proxy->vars.transports = xstrdup(tmp);
+  }
+
+  return 0;
+
+ err:
+  print_protocol_line("%s %s\n", PROTO_ENV_ERROR,
+                      get_env_parsing_error_message(status));
+
+  return -1;
+}
+
+/**
+   Given the name of a protocol we tried to launch in 'protocol'
+   the managed proxy parameters in 'proxy' and a listener launch
+   status in 'status', print a line of the form:
+       CMETHOD-ERROR <methodname> "<errormessage>"
+   to signify a listener launching failure.
+*/
+static inline void
+print_method_error_line(const char *protocol,
+                        const managed_proxy_t *proxy,
+                        enum launch_status status)
+{
+  print_protocol_line("%s %s %s\n",
+                      proxy->is_server ?
+                      PROTO_SMETHOD_ERROR : PROTO_CMETHOD_ERROR,
+                      protocol,
+                      get_launch_error_message(status));
+}
+
+/**
+   Print a CMETHOD/SMETHOD line saying that we launched 'transport'
+   with 'cfg' under 'proxy'.
+*/
+static void
+print_method_line(const char *transport, const managed_proxy_t *proxy,
+                  config_t *cfg)
+{
+  struct evconnlistener *listener = get_evconnlistener_by_config(cfg);
+  obfs_assert(listener);
+
+  struct sockaddr_in saddr;
+  memset(&saddr,0,sizeof(struct sockaddr_in));
+  socklen_t slen = sizeof(saddr);
+
+  obfs_assert(!(getsockname(evconnlistener_get_fd(listener),
+                            (struct sockaddr *)&saddr, &slen) < 0));
+
+  if (proxy->is_server) {
+    print_protocol_line("%s %s %s:%hu\n",
+                        PROTO_SMETHOD,
+                        transport,
+                        inet_ntoa(saddr.sin_addr),
+                        ntohs(saddr.sin_port));
+  } else {
+    print_protocol_line("%s %s %s %s:%hu\n",
+                        PROTO_CMETHOD,
+                        transport,
+                        "socks5",
+                        inet_ntoa(saddr.sin_addr),
+                        ntohs(saddr.sin_port));
+
+  }
+}
+
+/**
+   Print the '{S,C}METHOD DONE' message.
+*/
+static inline void
+print_method_done_line(const managed_proxy_t *proxy)
+{
+  print_protocol_line("%s", proxy->is_server ?
+                      PROTO_SMETHODS_DONE : PROTO_CMETHODS_DONE);
+}
+
+/**
+   Return true if 'name' matches the name of a supported managed proxy
+   configuration protocol.
+*/
+static inline int
+is_supported_conf_protocol(const char *name) {
+  int f;
+  for (f=0;f<n_supported_conf_protocols;f++) {
+    if (!strcmp(name,supported_conf_protocols[f]))
+      return 1;
+  }
+  return 0;
+}
+
+/**
+   Finish the configuration protocol version negotiation by printing
+   whether we support any of the suggested configuration protocols in
+   stdout, according to the 180 spec.
+
+   Return:
+   * 0: if we actually found and selected a protocol.
+   * -1: if we couldn't find a common supported protocol or if we
+     couldn't even parse tor's supported protocol list.
+
+   XXX: in the future we should return the protocol version we
+   selected. let's keep it simple for now since we have just one
+   protocol version.
+*/
+static int
+conf_proto_version_negotiation(const managed_proxy_t *proxy)
+{
+  int r=-1;
+
+  smartlist_t *versions = smartlist_create();
+  smartlist_split_string(versions, proxy->vars.conf_proto_version, ",",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+  SMARTLIST_FOREACH_BEGIN(versions, char *, version) {
+    if (is_supported_conf_protocol(version)) {
+      print_protocol_line("%s %s\n", PROTO_NEG_SUCCESS, version);
+      r=0;
+      goto done;
+    }
+  } SMARTLIST_FOREACH_END(version);
+
+  /* we get here if we couldn't find a supported protocol */
+  print_protocol_line("%s", PROTO_NEG_FAIL);
+
+ done:
+  SMARTLIST_FOREACH(versions, char *, cp, free(cp));
+  smartlist_free(versions);
+
+  return r;
+}
+
+/**
+   Return a string containing the error we encountered while parsing
+   the environment variables, according to 'status'.
+*/
+static inline const char *
+get_env_parsing_error_message(enum env_parsing_status status)
+{
+  switch(status) {
+  case ST_ENV_SMOOTH: return "no error";
+  case ST_ENV_FAIL_STATE_LOC: return "failed on TOR_PT_STATE_LOCATION";
+  case ST_ENV_FAIL_CONF_PROTO_VER: return "failed on TOR_PT_MANAGED_TRANSPORT_VER";
+  case ST_ENV_FAIL_CLIENT_TRANSPORTS: return "failed on TOR_PT_CLIENT_TRANSPORTS";
+  case ST_ENV_FAIL_EXTENDED_PORT: return "failed on TOR_PT_EXTENDED_SERVER_PORT";
+  case ST_ENV_FAIL_ORPORT: return "failed on TOR_PT_ORPORT";
+  case ST_ENV_FAIL_BINDADDR: return "failed on TOR_PT_SERVER_BINDADDR";
+  case ST_ENV_FAIL_SERVER_TRANSPORTS: return "failed on TOR_PT_SERVER_TRANSPORTS";
+  default:
+    obfs_assert(0); return "UNKNOWN";
+  }
+}
+
+/**
+    Return a string containing the error we encountered while
+    launching a listener, according to 'status'.
+*/
+static inline const char *
+get_launch_error_message(enum launch_status status)
+{
+  switch (status) {
+  case ST_LAUNCH_SMOOTH:    return "no error";
+  case ST_LAUNCH_FAIL_SETUP:   return "could not setup protocol";
+  case ST_LAUNCH_FAIL_LSN:   return "could not launch listener";
+  default:
+    obfs_assert(0); return "UNKNOWN";
+  }
+}
+
+/**
+   This function is used for obfsproxy to communicate with tor.
+   Practically a wrapper of printf, it prints 'format' and the
+   fflushes stdout so that the output is always immediately available
+   to tor.
+*/
+static void
+print_protocol_line(const char *format, ...)
+{
+  va_list ap;
+  va_start(ap,format);
+  vprintf(format, ap);
+  fflush(stdout);
+  va_end(ap);
+}
diff --git a/src/managed.h b/src/managed.h
new file mode 100644
index 0000000..2b14c2b
--- /dev/null
+++ b/src/managed.h
@@ -0,0 +1,16 @@
+/* Copyright 2011 Nick Mathewson, George Kadianakis
+   See LICENSE for other credits and copying information
+*/
+
+#ifndef MANAGED_H
+#define MANAGED_H
+
+int launch_managed_proxy();
+
+#ifdef MANAGED_PRIVATE
+
+int validate_bindaddrs(const char *all_bindaddrs, const char *all_transports);
+
+#endif /* MANAGED_PRIVATE */
+
+#endif



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits