Browse Source

Merge branch 'feature18329_029_squashed' into maint-0.3.2

Nick Mathewson 6 years ago
parent
commit
594cf92498
10 changed files with 273 additions and 1 deletions
  1. 9 0
      changes/feature18329
  2. 11 0
      doc/tor.1.txt
  3. 61 1
      src/or/config.c
  4. 2 0
      src/or/config.h
  5. 4 0
      src/or/or.h
  6. 12 0
      src/or/router.c
  7. 1 0
      src/test/include.am
  8. 1 0
      src/test/test.c
  9. 63 0
      src/test/test_config.c
  10. 109 0
      src/test/test_router.c

+ 9 - 0
changes/feature18329

@@ -0,0 +1,9 @@
+  o Minor features:
+    - Bridge relays can now set the BridgeDistribution config option to
+      add a "bridge-distribution-request" line to their bridge descriptor,
+      which tells BridgeDB how they'd like their bridge address to be
+      given out. (Note that as of Oct 2017, BridgeDB does not yet implement
+      this feature.) As a side benefit, this feature provides a way
+      to distinguish bridge descriptors from non-bridge descriptors.
+      Implements tickets 18329.
+

+ 11 - 0
doc/tor.1.txt

@@ -1670,6 +1670,17 @@ is non-zero):
     server descriptor to the bridge database, rather than
     to the public directory authorities.
 
+[[BridgeDistribution]] **BridgeDistribution** __string__::
+    If set along with BridgeRelay, Tor will include a new line in its
+    bridge descriptor which indicates to the BridgeDB service how it
+    would like its bridge address to be given out. Set it to "none" if
+    you want BridgeDB to avoid distributing your bridge address, or "any" to
+    let BridgeDB decide. (Default: any)
+ +
+    Note: as of Oct 2017, the BridgeDB part of this option is not yet
+    implemented.  Until BridgeDB is updated to obey this option, your
+    bridge will make this request, but it will not (yet) be obeyed.
+
 [[ContactInfo]] **ContactInfo** __email_address__::
     Administrative contact information for this relay or bridge. This line
     can be used to contact you if your relay or bridge is misconfigured or

+ 61 - 1
src/or/config.c

@@ -253,6 +253,7 @@ static config_var_t option_vars_[] = {
   V(BridgePassword,              STRING,   NULL),
   V(BridgeRecordUsageByCountry,  BOOL,     "1"),
   V(BridgeRelay,                 BOOL,     "0"),
+  V(BridgeDistribution,          STRING,   NULL),
   V(CellStatistics,              BOOL,     "0"),
   V(PaddingStatistics,           BOOL,     "1"),
   V(LearnCircuitBuildTimeout,    BOOL,     "1"),
@@ -731,7 +732,6 @@ static int parse_ports(or_options_t *options, int validate_only,
 static int check_server_ports(const smartlist_t *ports,
                               const or_options_t *options,
                               int *num_low_ports_out);
-
 static int validate_data_directory(or_options_t *options);
 static int write_configuration_file(const char *fname,
                                     const or_options_t *options);
@@ -3512,6 +3512,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
     REJECT("Relays cannot set ReducedConnectionPadding. ");
   }
 
+  if (options->BridgeDistribution) {
+    if (!options->BridgeRelay) {
+      REJECT("You set BridgeDistribution, but you didn't set BridgeRelay!");
+    }
+    if (check_bridge_distribution_setting(options->BridgeDistribution) < 0) {
+      REJECT("Invalid BridgeDistribution value.");
+    }
+  }
+
   if (options->MinUptimeHidServDirectoryV2 < 0) {
     log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at "
                         "least 0 seconds. Changing to 0.");
@@ -4686,6 +4695,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
       get_effective_bwburst(old_options) !=
         get_effective_bwburst(new_options) ||
       !opt_streq(old_options->ContactInfo, new_options->ContactInfo) ||
+      !opt_streq(old_options->BridgeDistribution,
+                 new_options->BridgeDistribution) ||
       !config_lines_eq(old_options->MyFamily, new_options->MyFamily) ||
       !opt_streq(old_options->AccountingStart, new_options->AccountingStart) ||
       old_options->AccountingMax != new_options->AccountingMax ||
@@ -6583,6 +6594,55 @@ warn_client_dns_cache(const char *option, int disabling)
       "to your destination.");
 }
 
+/**
+ * Validate the configured bridge distribution method from a BridgeDistribution
+ * config line.
+ *
+ * The input <b>bd</b>, is a string taken from the BridgeDistribution config
+ * line (if present).  If the option wasn't set, return 0 immediately.  The
+ * BridgeDistribution option is then validated.  Currently valid, recognised
+ * options are:
+ *
+ * - "none"
+ * - "any"
+ * - "https"
+ * - "email"
+ * - "moat"
+ * - "hyphae"
+ *
+ * If the option string is unrecognised, a warning will be logged and 0 is
+ * returned.  If the option string contains an invalid character, -1 is
+ * returned.
+ **/
+STATIC int
+check_bridge_distribution_setting(const char *bd)
+{
+  if (bd == NULL)
+    return 0;
+
+  const char *RECOGNIZED[] = {
+    "none", "any", "https", "email", "moat", "hyphae"
+  };
+  unsigned i;
+  for (i = 0; i < ARRAY_LENGTH(RECOGNIZED); ++i) {
+    if (!strcmp(bd, RECOGNIZED[i]))
+      return 0;
+  }
+
+  const char *cp = bd;
+  //  Method = (KeywordChar | "_") +
+  while (TOR_ISALNUM(*cp) || *cp == '-' || *cp == '_')
+    ++cp;
+
+  if (*cp == 0) {
+    log_warn(LD_CONFIG, "Unrecognized BridgeDistribution value %s. I'll "
+           "assume you know what you are doing...", escaped(bd));
+    return 0; // we reached the end of the string; all is well
+  } else {
+    return -1; // we found a bad character in the string.
+  }
+}
+
 /**
  * Parse port configuration for a single port type.
  *

+ 2 - 0
src/or/config.h

@@ -203,6 +203,8 @@ STATIC int parse_port_config(smartlist_t *out,
                   const char *defaultaddr,
                   int defaultport,
                   const unsigned flags);
+
+STATIC int check_bridge_distribution_setting(const char *bd);
 #endif /* defined(CONFIG_PRIVATE) */
 
 #endif /* !defined(TOR_CONFIG_H) */

+ 4 - 0
src/or/or.h

@@ -3770,6 +3770,10 @@ typedef struct {
   int BridgeAuthoritativeDir; /**< Boolean: is this an authoritative directory
                                * that aggregates bridge descriptors? */
 
+  /** If set on a bridge relay, it will include this value on a new
+   * "bridge-distribution-request" line in its bridge descriptor. */
+  char *BridgeDistribution;
+
   /** If set on a bridge authority, it will answer requests on its dirport
    * for bridge statuses -- but only if the requests use this password. */
   char *BridgePassword;

+ 12 - 0
src/or/router.c

@@ -2956,6 +2956,18 @@ router_dump_router_to_string(routerinfo_t *router,
     smartlist_add_asprintf(chunks, "contact %s\n", ci);
   }
 
+  if (options->BridgeRelay) {
+    const char *bd;
+    if (options->BridgeDistribution && strlen(options->BridgeDistribution)) {
+      bd = options->BridgeDistribution;
+    } else {
+      bd = "any";
+    }
+    if (strchr(bd, '\n') || strchr(bd, '\r'))
+      bd = escaped(bd);
+    smartlist_add_asprintf(chunks, "bridge-distribution-request %s\n", bd);
+  }
+
   if (router->onion_curve25519_pkey) {
     char kbuf[128];
     base64_encode(kbuf, sizeof(kbuf),

+ 1 - 0
src/test/include.am

@@ -146,6 +146,7 @@ src_test_test_SOURCES = \
 	src/test/test_relaycell.c \
 	src/test/test_rendcache.c \
 	src/test/test_replay.c \
+	src/test/test_router.c \
 	src/test/test_routerkeys.c \
 	src/test/test_routerlist.c \
 	src/test/test_routerset.c \

+ 1 - 0
src/test/test.c

@@ -1223,6 +1223,7 @@ struct testgroup_t testgroups[] = {
   { "relaycell/", relaycell_tests },
   { "rend_cache/", rend_cache_tests },
   { "replaycache/", replaycache_tests },
+  { "router/", router_tests },
   { "routerkeys/", routerkeys_tests },
   { "routerlist/", routerlist_tests },
   { "routerset/" , routerset_tests },

+ 63 - 0
src/test/test_config.c

@@ -5446,6 +5446,65 @@ test_config_dup_and_filter(void *arg)
   config_free_lines(line_dup);
 }
 
+/* If we're not configured to be a bridge, but we set
+ * BridgeDistribution, then options_validate () should return -1. */
+static void
+test_config_check_bridge_distribution_setting_not_a_bridge(void *arg) {
+  or_options_t* options = get_options_mutable();
+  or_options_t* old_options = options;
+  or_options_t* default_options = options;
+  char* message = (char*)("");
+  int ret;
+
+  (void)arg;
+
+  options->BridgeRelay = 0;
+  options->BridgeDistribution = (char*)("https");
+
+  ret = options_validate(old_options, options, default_options, 0, &message);
+
+  tt_int_op(ret, OP_EQ, -1);
+ done:
+  return;
+}
+
+/* If the BridgeDistribution setting was valid, 0 should be returned. */
+static void
+test_config_check_bridge_distribution_setting_valid(void *arg) {
+  int ret = check_bridge_distribution_setting("https");
+
+  (void)arg;
+
+  tt_int_op(ret, OP_EQ, 0);
+ done:
+  return;
+}
+
+/* If the BridgeDistribution setting was invalid, -1 should be returned. */
+static void
+test_config_check_bridge_distribution_setting_invalid(void *arg) {
+  int ret = check_bridge_distribution_setting("hyphens-are-not-allowed");
+
+  (void)arg;
+
+  tt_int_op(ret, OP_EQ, -1);
+ done:
+  return;
+}
+
+/* If the BridgeDistribution setting was unrecognised, a warning should be
+ * logged and 0 should be returned. */
+static void
+test_config_check_bridge_distribution_setting_unrecognised(void *arg) {
+  int ret = check_bridge_distribution_setting("unicorn");
+
+  (void)arg;
+
+  tt_int_op(ret, OP_EQ, 0);
+ done:
+  return;
+}
+
 #define CONFIG_TEST(name, flags)                          \
   { #name, test_config_ ## name, flags, NULL, NULL }
 
@@ -5489,6 +5548,10 @@ struct testcase_t config_tests[] = {
   CONFIG_TEST(include_flag_torrc_only, TT_FORK),
   CONFIG_TEST(include_flag_defaults_only, TT_FORK),
   CONFIG_TEST(dup_and_filter, 0),
+  CONFIG_TEST(check_bridge_distribution_setting_not_a_bridge, TT_FORK),
+  CONFIG_TEST(check_bridge_distribution_setting_valid, 0),
+  CONFIG_TEST(check_bridge_distribution_setting_invalid, 0),
+  CONFIG_TEST(check_bridge_distribution_setting_unrecognised, 0),
   END_OF_TESTCASES
 };
 

+ 109 - 0
src/test/test_router.c

@@ -0,0 +1,109 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* Copyright (c) 2017, isis agora lovecruft  */
+/* See LICENSE for licensing information     */
+
+/**
+ * \file test_router.c
+ * \brief Unittests for code in src/or/router.c
+ **/
+
+#include "or.h"
+#include "config.h"
+#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
+#include "router.h"
+#include "routerlist.h"
+
+/* Test suite stuff */
+#include "test.h"
+
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+
+static routerinfo_t* mock_routerinfo;
+
+static const routerinfo_t*
+NS(router_get_my_routerinfo)(void) {
+  crypto_pk_t* ident_key;
+  crypto_pk_t* tap_key;
+  time_t now;
+
+  if (!mock_routerinfo) {
+    /* Mock the published timestamp, otherwise router_dump_router_to_string()
+     * will poop its pants. */
+    time(&now);
+
+    /* We'll need keys, or router_dump_router_to_string() would return NULL. */
+    ident_key = pk_generate(0);
+    tap_key = pk_generate(0);
+
+    tor_assert(ident_key != NULL);
+    tor_assert(tap_key != NULL);
+
+    mock_routerinfo = tor_malloc_zero(sizeof(routerinfo_t));
+    mock_routerinfo->nickname = tor_strdup("ConlonNancarrow");
+    mock_routerinfo->addr = 123456789;
+    mock_routerinfo->or_port = 443;
+    mock_routerinfo->platform = tor_strdup("unittest");
+    mock_routerinfo->cache_info.published_on = now;
+    mock_routerinfo->identity_pkey = crypto_pk_dup_key(ident_key);
+    mock_routerinfo->onion_pkey = crypto_pk_dup_key(tap_key);
+    mock_routerinfo->bandwidthrate = 9001;
+    mock_routerinfo->bandwidthburst = 9002;
+  }
+
+  return mock_routerinfo;
+}
+
+/* If no distribution option was set, then check_bridge_distribution_setting()
+ * should have set it to "any". */
+static void
+test_router_dump_router_to_string_no_bridge_distribution_method(void *arg) {
+  const char* needle = "bridge-distribution-request any";
+  or_options_t* options = get_options_mutable();
+  routerinfo_t* router = NULL;
+  curve25519_keypair_t ntor_keypair;
+  ed25519_keypair_t signing_keypair;
+  char* desc = NULL;
+  char* found = NULL;
+  (void)arg;
+
+  NS_MOCK(router_get_my_routerinfo);
+
+  options->ORPort_set = 1;
+  options->BridgeRelay = 1;
+
+  /* Generate keys which router_dump_router_to_string() expects to exist. */
+  tt_int_op(0, ==, curve25519_keypair_generate(&ntor_keypair, 0));
+  tt_int_op(0, ==, ed25519_keypair_generate(&signing_keypair, 0));
+
+  /* Set up part of our routerinfo_t so that we don't trigger any other
+   * assertions in router_dump_router_to_string(). */
+  router = (routerinfo_t*)router_get_my_routerinfo();
+  tt_ptr_op(router, !=, NULL);
+
+  router->onion_curve25519_pkey = &ntor_keypair.pubkey;
+
+  /* Generate our server descriptor and ensure that the substring
+   * "bridge-distribution-request any" occurs somewhere within it. */
+  desc = router_dump_router_to_string(router,
+                                      router->identity_pkey,
+                                      router->onion_pkey,
+                                      &ntor_keypair,
+                                      &signing_keypair);
+  tt_ptr_op(desc, !=, NULL);
+  found = strstr(desc, needle);
+  tt_ptr_op(found, !=, NULL);
+
+ done:
+  NS_UNMOCK(router_get_my_routerinfo);
+
+  tor_free(desc);
+}
+
+#define ROUTER_TEST(name, flags)                          \
+  { #name, test_router_ ## name, flags, NULL, NULL }
+
+struct testcase_t router_tests[] = {
+  ROUTER_TEST(dump_router_to_string_no_bridge_distribution_method, TT_FORK),
+  END_OF_TESTCASES
+};