aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/config/config.c96
-rw-r--r--src/feature/dirclient/dir_server_st.h6
-rw-r--r--src/feature/dirclient/dirclient.c38
-rw-r--r--src/feature/nodelist/dirlist.c147
-rw-r--r--src/feature/nodelist/dirlist.h35
-rw-r--r--src/test/include.am1
-rw-r--r--src/test/test.c2
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_dirauth_ports.c152
9 files changed, 472 insertions, 6 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c
index bfa258c904..4aa8845ae3 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -5466,6 +5466,77 @@ pt_parse_transport_line(const or_options_t *options,
return r;
}
+/**
+ * Parse a flag describing an extra dirport for a directory authority.
+ *
+ * Right now, the supported format is exactly:
+ * `{upload,download,voting}=http://[IP:PORT]/`.
+ * Other URL schemes, and other suffixes, might be supported in the future.
+ *
+ * Only call this function if `flag` starts with one of the above strings.
+ *
+ * Return 0 on success, and -1 on failure.
+ *
+ * If `ds` is provided, then add any parsed dirport to `ds`. If `ds` is NULL,
+ * take no action other than parsing.
+ **/
+static int
+parse_dirauth_dirport(dir_server_t *ds, const char *flag)
+{
+ tor_assert(flag);
+
+ auth_dirport_usage_t usage;
+
+ if (!strcasecmpstart(flag, "upload=")) {
+ usage = AUTH_USAGE_UPLOAD;
+ } else if (!strcasecmpstart(flag, "download=")) {
+ usage = AUTH_USAGE_DOWNLOAD;
+ } else if (!strcasecmpstart(flag, "vote=")) {
+ usage = AUTH_USAGE_VOTING;
+ } else {
+ // We shouldn't get called with a flag that we don't recognize.
+ tor_assert_nonfatal_unreached();
+ return -1;
+ }
+
+ const char *eq = strchr(flag, '=');
+ tor_assert(eq);
+ const char *target = eq + 1;
+
+ // Find the part inside the http://{....}/
+ if (strcmpstart(target, "http://")) {
+ log_warn(LD_CONFIG, "Unsupported URL scheme in authority flag %s", flag);
+ return -1;
+ }
+ const char *addr = target + strlen("http://");
+
+ const char *eos = strchr(addr, '/');
+ size_t addr_len;
+ if (eos && strcmp(eos, "/")) {
+ log_warn(LD_CONFIG, "Unsupported URL prefix in authority flag %s", flag);
+ return -1;
+ } else if (eos) {
+ addr_len = eos - addr;
+ } else {
+ addr_len = strlen(addr);
+ }
+
+ // Finally, parse the addr:port part.
+ char *addr_string = tor_strndup(addr, addr_len);
+ tor_addr_port_t dirport;
+ memset(&dirport, 0, sizeof(dirport));
+ int rv = tor_addr_port_parse(LOG_WARN, addr_string,
+ &dirport.addr, &dirport.port, -1);
+ if (ds != NULL && rv == 0) {
+ trusted_dir_server_add_dirport(ds, usage, &dirport);
+ } else if (rv == -1) {
+ log_warn(LD_CONFIG, "Unable to parse address in authority flag %s",flag);
+ }
+
+ tor_free(addr_string);
+ return rv;
+}
+
/** Read the contents of a DirAuthority line from <b>line</b>. If
* <b>validate_only</b> is 0, and the line is well-formed, and it
* shares any bits with <b>required_type</b> or <b>required_type</b>
@@ -5486,6 +5557,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
char v3_digest[DIGEST_LEN];
dirinfo_type_t type = 0;
double weight = 1.0;
+ smartlist_t *extra_dirports = smartlist_new();
memset(v3_digest, 0, sizeof(v3_digest));
@@ -5554,6 +5626,12 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
}
ipv6_addrport_ptr = &ipv6_addrport;
}
+ } else if (!strcasecmpstart(flag, "upload=") ||
+ !strcasecmpstart(flag, "download=") ||
+ !strcasecmpstart(flag, "vote=")) {
+ // We'll handle these after creating the authority object.
+ smartlist_add(extra_dirports, flag);
+ flag = NULL; // prevent double-free.
} else {
log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirAuthority line",
flag);
@@ -5597,6 +5675,13 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
goto err;
}
+ if (validate_only) {
+ SMARTLIST_FOREACH_BEGIN(extra_dirports, const char *, cp) {
+ if (parse_dirauth_dirport(NULL, cp) < 0)
+ goto err;
+ } SMARTLIST_FOREACH_END(cp);
+ }
+
if (!validate_only && (!required_type || required_type & type)) {
dir_server_t *ds;
if (required_type)
@@ -5608,16 +5693,23 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
ipv6_addrport_ptr,
digest, v3_digest, type, weight)))
goto err;
+
+ SMARTLIST_FOREACH_BEGIN(extra_dirports, const char *, cp) {
+ if (parse_dirauth_dirport(ds, cp) < 0)
+ goto err;
+ } SMARTLIST_FOREACH_END(cp);
dir_server_add(ds);
}
r = 0;
goto done;
- err:
+ err:
r = -1;
- done:
+ done:
+ SMARTLIST_FOREACH(extra_dirports, char*, s, tor_free(s));
+ smartlist_free(extra_dirports);
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
tor_free(addrport);
diff --git a/src/feature/dirclient/dir_server_st.h b/src/feature/dirclient/dir_server_st.h
index ed6b00647e..ac45f3787b 100644
--- a/src/feature/dirclient/dir_server_st.h
+++ b/src/feature/dirclient/dir_server_st.h
@@ -16,6 +16,8 @@
#include "core/or/or.h"
#include "feature/nodelist/routerstatus_st.h"
+struct smartlist_t;
+
/** Represents information about a single trusted or fallback directory
* server. */
struct dir_server_t {
@@ -48,6 +50,10 @@ struct dir_server_t {
time_t addr_current_at; /**< When was the document that we derived the
* address information from published? */
+ /** Authority only. Can be null. If present, a list of auth_dirport_t
+ * representing HTTP dirports for this authority. */
+ struct smartlist_t *auth_dirports;
+
routerstatus_t fake_status; /**< Used when we need to pass this trusted
* dir_server_t to
* directory_request_set_routerstatus.
diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c
index aa7759257f..24a32ef201 100644
--- a/src/feature/dirclient/dirclient.c
+++ b/src/feature/dirclient/dirclient.c
@@ -1119,6 +1119,7 @@ directory_request_set_routerstatus(directory_request_t *req,
{
req->routerstatus = status;
}
+
/**
* Helper: update the addresses, ports, and identities in <b>req</b>
* from the routerstatus object in <b>req</b>. Return 0 on success.
@@ -1161,7 +1162,7 @@ directory_request_set_dir_from_routerstatus(directory_request_t *req)
return -1;
}
- /* At this point, if we are a client making a direct connection to a
+ /* At this point, if we are a client making a direct connection to a
* directory server, we have selected a server that has at least one address
* allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
* selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
@@ -1176,6 +1177,37 @@ directory_request_set_dir_from_routerstatus(directory_request_t *req)
return -1;
}
+ /* One last thing: If we're talking to an authority, we might want to use
+ * a special HTTP port for it based on our purpose.
+ */
+ if (req->indirection == DIRIND_DIRECT_CONN && status->is_authority) {
+ const dir_server_t *ds = router_get_trusteddirserver_by_digest(
+ status->identity_digest);
+ if (ds) {
+ const tor_addr_port_t *v4 = NULL;
+ if (authdir_mode_v3(get_options())) {
+ // An authority connecting to another authority should always
+ // prefer the VOTING usage, if one is specifically configured.
+ v4 = trusted_dir_server_get_dirport_exact(
+ ds, AUTH_USAGE_VOTING, AF_INET);
+ }
+ if (! v4) {
+ // Everybody else should prefer a usage dependent on their
+ // the dir_purpose.
+ auth_dirport_usage_t usage =
+ auth_dirport_usage_for_purpose(req->dir_purpose);
+ v4 = trusted_dir_server_get_dirport(ds, usage, AF_INET);
+ }
+ tor_assert_nonfatal(v4);
+ if (v4) {
+ // XXXX We could, if we wanted, also select a v6 address. But a v4
+ // address must exist here, and we as a relay are required to support
+ // ipv4. So we just that.
+ tor_addr_port_copy(&use_dir_ap, v4);
+ }
+ }
+ }
+
directory_request_set_or_addr_port(req, &use_or_ap);
directory_request_set_dir_addr_port(req, &use_dir_ap);
directory_request_set_directory_id_digest(req, status->identity_digest);
@@ -1194,7 +1226,7 @@ directory_initiate_request,(directory_request_t *request))
tor_assert_nonfatal(
! directory_request_dir_contact_info_specified(request));
if (directory_request_set_dir_from_routerstatus(request) < 0) {
- return;
+ return; // or here XXXX
}
}
@@ -1309,6 +1341,8 @@ directory_initiate_request,(directory_request_t *request))
entry_guard_cancel(&guard_state);
}
+ // XXXX This is the case where we replace.
+
switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr,
port, &socket_error)) {
case -1:
diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c
index 1f18bd71a2..57b55f0127 100644
--- a/src/feature/nodelist/dirlist.c
+++ b/src/feature/nodelist/dirlist.c
@@ -43,6 +43,14 @@
#include "feature/dirclient/dir_server_st.h"
#include "feature/nodelist/node_st.h"
+/** Information about an (HTTP) dirport for a directory authority. */
+struct auth_dirport_t {
+ /** What is the intended usage for this dirport? One of AUTH_USAGE_* */
+ auth_dirport_usage_t usage;
+ /** What is the correct address/port ? */
+ tor_addr_port_t dirport;
+};
+
/** Global list of a dir_server_t object for each directory
* authority. */
static smartlist_t *trusted_dir_servers = NULL;
@@ -66,6 +74,11 @@ add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir)
/* IPv6 DirPort is not a thing yet for authorities. */
nodelist_add_addr_to_address_set(&dir->ipv6_addr, dir->ipv6_orport, 0);
}
+ if (dir->auth_dirports) {
+ SMARTLIST_FOREACH_BEGIN(dir->auth_dirports, const auth_dirport_t *, p) {
+ nodelist_add_addr_to_address_set(&p->dirport.addr, 0, p->dirport.port);
+ } SMARTLIST_FOREACH_END(p);
+ }
}
/** Go over the trusted directory server list and add their address(es) to the
@@ -256,7 +269,10 @@ MOCK_IMPL(int, router_digest_is_trusted_dir_type,
/** Return true iff the given address matches a trusted directory that matches
* at least one bit of type.
*
- * If type is NO_DIRINFO or ALL_DIRINFO, any authority is matched. */
+ * If type is NO_DIRINFO or ALL_DIRINFO, any authority is matched.
+ *
+ * Only ORPorts' addresses are considered.
+ */
bool
router_addr_is_trusted_dir_type(const tor_addr_t *addr, dirinfo_type_t type)
{
@@ -281,6 +297,42 @@ router_addr_is_trusted_dir_type(const tor_addr_t *addr, dirinfo_type_t type)
return false;
}
+/** Return an appropriate usage value describing which authdir port to use
+ * for a given directory connection purpose.
+ */
+auth_dirport_usage_t
+auth_dirport_usage_for_purpose(int purpose)
+{
+ switch (purpose) {
+ case DIR_PURPOSE_FETCH_SERVERDESC:
+ case DIR_PURPOSE_FETCH_EXTRAINFO:
+ case DIR_PURPOSE_FETCH_CONSENSUS:
+ case DIR_PURPOSE_FETCH_CERTIFICATE:
+ case DIR_PURPOSE_FETCH_MICRODESC:
+ return AUTH_USAGE_DOWNLOAD;
+
+ case DIR_PURPOSE_UPLOAD_DIR:
+ return AUTH_USAGE_UPLOAD;
+
+ case DIR_PURPOSE_UPLOAD_VOTE:
+ case DIR_PURPOSE_UPLOAD_SIGNATURES:
+ case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
+ case DIR_PURPOSE_FETCH_STATUS_VOTE:
+ return AUTH_USAGE_VOTING;
+
+ case DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2:
+ case DIR_PURPOSE_SERVER:
+ case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
+ case DIR_PURPOSE_FETCH_RENDDESC_V2:
+ case DIR_PURPOSE_UPLOAD_HSDESC:
+ case DIR_PURPOSE_FETCH_HSDESC:
+ case DIR_PURPOSE_HAS_FETCHED_HSDESC:
+ default:
+ tor_assert_nonfatal_unreached();
+ return AUTH_USAGE_LEGACY;
+ }
+}
+
/** Create a directory server at <b>address</b>:<b>port</b>, with OR identity
* key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL,
* add ourself. If <b>is_authority</b>, this is a directory authority. Return
@@ -357,6 +409,7 @@ dir_server_new(int is_authority,
ent->fake_status.ipv4_dirport = ent->ipv4_dirport;
ent->fake_status.ipv4_orport = ent->ipv4_orport;
ent->fake_status.ipv6_orport = ent->ipv6_orport;
+ ent->fake_status.is_authority = !! is_authority;
return ent;
}
@@ -404,10 +457,98 @@ trusted_dir_server_new(const char *nickname, const char *address,
ipv6_addrport,
digest,
v3_auth_digest, type, weight);
+
+ if (ipv4_dirport) {
+ tor_addr_port_t p;
+ memset(&p, 0, sizeof(p));
+ tor_addr_copy(&p.addr, &ipv4_addr);
+ p.port = ipv4_dirport;
+ trusted_dir_server_add_dirport(result, AUTH_USAGE_LEGACY, &p);
+ }
tor_free(hostname);
return result;
}
+/**
+ * Add @a dirport as an HTTP DirPort contact point for the directory authority
+ * @a ds, for use when contacting that authority for the given @a usage.
+ *
+ * Multiple ports of the same usage are allowed; if present, then only
+ * the first one of each address family is currently used.
+ */
+void
+trusted_dir_server_add_dirport(dir_server_t *ds,
+ auth_dirport_usage_t usage,
+ const tor_addr_port_t *dirport)
+{
+ tor_assert(ds);
+ tor_assert(dirport);
+
+ if (BUG(! ds->is_authority)) {
+ return;
+ }
+
+ if (ds->auth_dirports == NULL) {
+ ds->auth_dirports = smartlist_new();
+ }
+
+ auth_dirport_t *port = tor_malloc_zero(sizeof(auth_dirport_t));
+ port->usage = usage;
+ tor_addr_port_copy(&port->dirport, dirport);
+ smartlist_add(ds->auth_dirports, port);
+}
+
+/**
+ * Helper for trusted_dir_server_get_dirport: only return the exact requested
+ * usage type.
+ */
+const tor_addr_port_t *
+trusted_dir_server_get_dirport_exact(const dir_server_t *ds,
+ auth_dirport_usage_t usage,
+ int addr_family)
+{
+ tor_assert(ds);
+ tor_assert_nonfatal(addr_family == AF_INET || addr_family == AF_INET6);
+ if (ds->auth_dirports == NULL)
+ return NULL;
+
+ SMARTLIST_FOREACH_BEGIN(ds->auth_dirports, const auth_dirport_t *, port) {
+ if (port->usage == usage &&
+ tor_addr_family(&port->dirport.addr) == addr_family) {
+ return &port->dirport;
+ }
+ } SMARTLIST_FOREACH_END(port);
+
+ return NULL;
+}
+
+/**
+ * Return the DirPort of the authority @a ds for with the usage type
+ * @a usage and address family @a addr_family. If none is found, try
+ * again with an AUTH_USAGE_LEGACY dirport, if there is one. Return NULL
+ * if no port can be found.
+ */
+const tor_addr_port_t *
+trusted_dir_server_get_dirport(const dir_server_t *ds,
+ auth_dirport_usage_t usage,
+ int addr_family)
+{
+ const tor_addr_port_t *port;
+
+ while (1) {
+ port = trusted_dir_server_get_dirport_exact(ds, usage, addr_family);
+ if (port)
+ return port;
+
+ // If we tried LEGACY, there is no fallback from this point.
+ if (usage == AUTH_USAGE_LEGACY)
+ return NULL;
+
+ // Try again with LEGACY.
+ usage = AUTH_USAGE_LEGACY;
+ }
+}
+
/** Return a new dir_server_t for a fallback directory server at
* <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest
* <b>id_digest</b> */
@@ -447,6 +588,10 @@ dir_server_free_(dir_server_t *ds)
if (!ds)
return;
+ if (ds->auth_dirports) {
+ SMARTLIST_FOREACH(ds->auth_dirports, auth_dirport_t *, p, tor_free(p));
+ smartlist_free(ds->auth_dirports);
+ }
tor_free(ds->nickname);
tor_free(ds->description);
tor_free(ds->address);
diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h
index f744fecf92..3b4faf07af 100644
--- a/src/feature/nodelist/dirlist.h
+++ b/src/feature/nodelist/dirlist.h
@@ -11,6 +11,28 @@
#ifndef TOR_DIRLIST_H
#define TOR_DIRLIST_H
+typedef struct auth_dirport_t auth_dirport_t;
+/**
+ * Different usages for an authority's HTTP directory port.
+ *
+ * Historically, only legacy ports existed; proposal 330 added multiple types
+ * of dirport to better enable authorities to offload work and resist DoS
+ * attacks.
+ **/
+typedef enum auth_dirport_usage_t {
+ /** Flag for an authority's dirport that is intended for misc/legacy
+ * usage. May be used when no other dirport is available. */
+ AUTH_USAGE_LEGACY,
+ /** Flag for an authority's dirport that is intended for descriptor uploads
+ * only. */
+ AUTH_USAGE_UPLOAD,
+ /** Flag for an authority's dirport that is intended for voting only */
+ AUTH_USAGE_VOTING,
+ /** Flag for an authority's dirport that is intended for relay downloads
+ * only. */
+ AUTH_USAGE_DOWNLOAD,
+} auth_dirport_usage_t;
+
int get_n_authorities(dirinfo_type_t type);
const smartlist_t *router_get_trusted_dir_servers(void);
const smartlist_t *router_get_fallback_dir_servers(void);
@@ -18,6 +40,8 @@ smartlist_t *router_get_trusted_dir_servers_mutable(void);
smartlist_t *router_get_fallback_dir_servers_mutable(void);
void mark_all_dirservers_up(smartlist_t *server_list);
+auth_dirport_usage_t auth_dirport_usage_for_purpose(int purpose);
+
dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
dir_server_t *router_get_fallback_dirserver_by_digest(
const char *digest);
@@ -28,6 +52,14 @@ MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
MOCK_DECL(int, router_digest_is_trusted_dir_type,
(const char *digest, dirinfo_type_t type));
+const tor_addr_port_t *trusted_dir_server_get_dirport(const dir_server_t *ds,
+ auth_dirport_usage_t usage,
+ int addr_family);
+const tor_addr_port_t *trusted_dir_server_get_dirport_exact(
+ const dir_server_t *ds,
+ auth_dirport_usage_t usage,
+ int addr_family);
+
bool router_addr_is_trusted_dir_type(const tor_addr_t *addr,
dirinfo_type_t type);
#define router_addr_is_trusted_dir(d) \
@@ -41,6 +73,9 @@ dir_server_t *trusted_dir_server_new(const char *nickname, const char *address,
const tor_addr_port_t *addrport_ipv6,
const char *digest, const char *v3_auth_digest,
dirinfo_type_t type, double weight);
+void trusted_dir_server_add_dirport(dir_server_t *ds,
+ auth_dirport_usage_t usage,
+ const tor_addr_port_t *dirport);
dir_server_t *fallback_dir_server_new(const tor_addr_t *addr,
uint16_t dir_port, uint16_t or_port,
const tor_addr_port_t *addrport_ipv6,
diff --git a/src/test/include.am b/src/test/include.am
index d5dcebfaea..b5e121d1ad 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -170,6 +170,7 @@ src_test_test_SOURCES += \
src/test/test_crypto_rng.c \
src/test/test_data.c \
src/test/test_dir.c \
+ src/test/test_dirauth_ports.c \
src/test/test_dirvote.c \
src/test/test_dir_common.c \
src/test/test_dir_handle_get.c \
diff --git a/src/test/test.c b/src/test/test.c
index 40c053a660..9543a24376 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1,5 +1,4 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
-->a * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2021, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@@ -673,6 +672,7 @@ struct testgroup_t testgroups[] = {
{ "crypto/pem/", pem_tests },
{ "crypto/rng/", crypto_rng_tests },
{ "dir/", dir_tests },
+ { "dir/auth/ports/", dirauth_port_tests },
{ "dir/auth/process_descs/", process_descs_tests },
{ "dir/md/", microdesc_tests },
{ "dirauth/dirvote/", dirvote_tests},
diff --git a/src/test/test.h b/src/test/test.h
index f88bc98498..700aa70a4b 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -120,6 +120,7 @@ extern struct testcase_t crypto_ope_tests[];
extern struct testcase_t crypto_openssl_tests[];
extern struct testcase_t crypto_rng_tests[];
extern struct testcase_t crypto_tests[];
+extern struct testcase_t dirauth_port_tests[];
extern struct testcase_t dir_handle_get_tests[];
extern struct testcase_t dir_tests[];
extern struct testcase_t dirvote_tests[];
diff --git a/src/test/test_dirauth_ports.c b/src/test/test_dirauth_ports.c
new file mode 100644
index 0000000000..5dc0b0b631
--- /dev/null
+++ b/src/test/test_dirauth_ports.c
@@ -0,0 +1,152 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2021, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define CONFIG_PRIVATE
+
+#include "core/or/or.h"
+#include "feature/dirclient/dir_server_st.h"
+#include "feature/nodelist/dirlist.h"
+#include "app/config/config.h"
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+
+static void
+test_dirauth_port_parsing(void *arg)
+{
+ (void)arg;
+
+ // This one is okay.
+ int rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "upload=http://128.31.0.39:9131/ "
+ "download=http://128.31.0.39:9131 "
+ "vote=http://128.31.0.39:9131/ "
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 1);
+ tt_int_op(rv,OP_EQ,0);
+
+ // These have bad syntax.
+ setup_capture_of_logs(LOG_WARN);
+ rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "uploadx=http://128.31.0.39:9131/ "
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 1);
+ tt_int_op(rv,OP_EQ,0);
+ expect_log_msg_containing("Unrecognized flag");
+ mock_clean_saved_logs();
+
+ rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "upload=https://128.31.0.39:9131/ " // https is not recognized
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 1);
+ tt_int_op(rv,OP_EQ,-1);
+ expect_log_msg_containing("Unsupported URL scheme");
+ mock_clean_saved_logs();
+
+ rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "upload=http://128.31.0.39:9131/tor " // suffix is not supported
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 1);
+ tt_int_op(rv,OP_EQ,-1);
+ expect_log_msg_containing("Unsupported URL prefix");
+ mock_clean_saved_logs();
+
+ rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "upload=http://128.31.0.256:9131/ " // "256" is not ipv4.
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 1);
+ tt_int_op(rv,OP_EQ,-1);
+ expect_log_msg_containing("Unable to parse address");
+
+ rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "upload=http://xyz.example.com/ " // hostnames not supported.
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 1);
+ tt_int_op(rv,OP_EQ,-1);
+ expect_log_msg_containing("Unable to parse address");
+
+ done:
+ teardown_capture_of_logs();
+}
+
+static void
+test_dirauth_port_lookup(void *arg)
+{
+ (void)arg;
+
+ clear_dir_servers();
+
+ int rv = parse_dir_authority_line(
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "upload=http://128.31.0.40:9132/ "
+ "download=http://128.31.0.41:9133 "
+ "vote=http://128.31.0.42:9134/ "
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 0);
+ tt_int_op(rv,OP_EQ,0);
+
+ rv = parse_dir_authority_line(
+ "morgoth orport=9101 "
+ "v3ident=D586D18309DED4CDFFFFFFFFDB97EFA96D330566 "
+ "upload=http://128.31.0.43:9140/ "
+ "128.31.0.44:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ NO_DIRINFO, 0);
+ tt_int_op(rv,OP_EQ,0);
+
+ const smartlist_t *servers = router_get_trusted_dir_servers();
+ tt_assert(servers);
+ tt_int_op(smartlist_len(servers), OP_EQ, 2);
+ const dir_server_t *moria = smartlist_get(servers, 0);
+ const dir_server_t *morgoth = smartlist_get(servers, 1);
+ tt_str_op(moria->nickname, OP_EQ, "moria1");
+ tt_str_op(morgoth->nickname, OP_EQ, "morgoth");
+
+ const tor_addr_port_t *dirport;
+
+ dirport = trusted_dir_server_get_dirport(moria,
+ AUTH_USAGE_UPLOAD, AF_INET);
+ tt_int_op(dirport->port, OP_EQ, 9132);
+ dirport = trusted_dir_server_get_dirport(moria,
+ AUTH_USAGE_DOWNLOAD, AF_INET);
+ tt_int_op(dirport->port, OP_EQ, 9133);
+ dirport = trusted_dir_server_get_dirport(moria,
+ AUTH_USAGE_VOTING, AF_INET);
+ tt_int_op(dirport->port, OP_EQ, 9134);
+
+ dirport = trusted_dir_server_get_dirport(morgoth,
+ AUTH_USAGE_UPLOAD, AF_INET);
+ tt_int_op(dirport->port, OP_EQ, 9140);
+ dirport = trusted_dir_server_get_dirport(morgoth,
+ AUTH_USAGE_DOWNLOAD, AF_INET);
+ tt_int_op(dirport->port, OP_EQ, 9131); // fallback
+ dirport = trusted_dir_server_get_dirport(morgoth,
+ AUTH_USAGE_VOTING, AF_INET);
+ tt_int_op(dirport->port, OP_EQ, 9131); // fallback
+
+ done:
+ ;
+}
+
+#define T(name) \
+ { #name, test_dirauth_port_ ## name, TT_FORK, NULL, NULL }
+
+struct testcase_t dirauth_port_tests[] = {
+ T(parsing),
+ T(lookup),
+ END_OF_TESTCASES
+};