summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2017-10-24 19:35:28 -0400
committerNick Mathewson <nickm@torproject.org>2017-10-24 19:35:28 -0400
commit594cf92498c8ea12dc0b19f743d6b88d4a98f1eb (patch)
treec4358510a57824e1b21b5de6adfdfff30626e027
parent122a7f8848785e3cefef812749c7ec1844b26402 (diff)
parent02cde0d9398c43de3a67133b7982d9df6962fe32 (diff)
downloadtor-594cf92498c8ea12dc0b19f743d6b88d4a98f1eb.tar.gz
tor-594cf92498c8ea12dc0b19f743d6b88d4a98f1eb.zip
Merge branch 'feature18329_029_squashed' into maint-0.3.2
-rw-r--r--changes/feature183299
-rw-r--r--doc/tor.1.txt11
-rw-r--r--src/or/config.c62
-rw-r--r--src/or/config.h2
-rw-r--r--src/or/or.h4
-rw-r--r--src/or/router.c12
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test_config.c63
-rw-r--r--src/test/test_router.c109
10 files changed, 273 insertions, 1 deletions
diff --git a/changes/feature18329 b/changes/feature18329
new file mode 100644
index 0000000000..f534c76145
--- /dev/null
+++ b/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.
+
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index ba2bc13dad..e76efddcd5 100644
--- a/doc/tor.1.txt
+++ b/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
diff --git a/src/or/config.c b/src/or/config.c
index a2353b94db..b123d49358 100644
--- a/src/or/config.c
+++ b/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 ||
@@ -6584,6 +6595,55 @@ warn_client_dns_cache(const char *option, int disabling)
}
/**
+ * 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.
*
* Read entries of the "FooPort" type from the list <b>ports</b>. Syntax is
diff --git a/src/or/config.h b/src/or/config.h
index af945f16be..efdd8c59b0 100644
--- a/src/or/config.h
+++ b/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) */
diff --git a/src/or/or.h b/src/or/or.h
index 8c1ef478ed..d9ab815cbc 100644
--- a/src/or/or.h
+++ b/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;
diff --git a/src/or/router.c b/src/or/router.c
index 172531e70e..8ad5d038ef 100644
--- a/src/or/router.c
+++ b/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),
diff --git a/src/test/include.am b/src/test/include.am
index 4cc2fae4ca..50cee693ab 100644
--- a/src/test/include.am
+++ b/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 \
diff --git a/src/test/test.c b/src/test/test.c
index 368d874be9..da4f7a0553 100644
--- a/src/test/test.c
+++ b/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 },
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 58268705ea..68eb5419cd 100644
--- a/src/test/test_config.c
+++ b/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
};
diff --git a/src/test/test_router.c b/src/test/test_router.c
new file mode 100644
index 0000000000..64434035ef
--- /dev/null
+++ b/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
+};