aboutsummaryrefslogtreecommitdiff
path: root/src/feature/relay
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/relay')
-rw-r--r--src/feature/relay/circuitbuild_relay.c549
-rw-r--r--src/feature/relay/circuitbuild_relay.h87
-rw-r--r--src/feature/relay/dns.c77
-rw-r--r--src/feature/relay/dns.h61
-rw-r--r--src/feature/relay/ext_orport.c73
-rw-r--r--src/feature/relay/ext_orport.h50
-rw-r--r--src/feature/relay/include.am16
-rw-r--r--src/feature/relay/onion_queue.c6
-rw-r--r--src/feature/relay/relay_config.c2
-rw-r--r--src/feature/relay/relay_find_addr.c133
-rw-r--r--src/feature/relay/relay_find_addr.h23
-rw-r--r--src/feature/relay/relay_handshake.c565
-rw-r--r--src/feature/relay/relay_handshake.h90
-rw-r--r--src/feature/relay/relay_periodic.c43
-rw-r--r--src/feature/relay/relay_stub.c1
-rw-r--r--src/feature/relay/relay_sys.c1
-rw-r--r--src/feature/relay/router.c225
-rw-r--r--src/feature/relay/router.h13
-rw-r--r--src/feature/relay/routerkeys.h82
-rw-r--r--src/feature/relay/selftest.c48
-rw-r--r--src/feature/relay/selftest.h46
21 files changed, 1931 insertions, 260 deletions
diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c
new file mode 100644
index 0000000000..b89866b477
--- /dev/null
+++ b/src/feature/relay/circuitbuild_relay.c
@@ -0,0 +1,549 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file circuitbuild_relay.c
+ * @brief Implements the details of exteding circuits (by relaying extend
+ * cells as create cells, and answering create cells).
+ *
+ * On the server side, this module handles the logic of responding to
+ * RELAY_EXTEND requests, using circuit_extend() and onionskin_answer().
+ *
+ * The shared client and server code is in core/or/circuitbuild.c.
+ **/
+
+#include "orconfig.h"
+#include "feature/relay/circuitbuild_relay.h"
+
+#include "lib/crypt_ops/crypto_rand.h"
+
+#include "core/or/or.h"
+#include "app/config/config.h"
+
+#include "core/crypto/relay_crypto.h"
+
+#include "core/or/cell_st.h"
+#include "core/or/circuit_st.h"
+#include "core/or/extend_info_st.h"
+#include "core/or/or_circuit_st.h"
+
+#include "core/or/channel.h"
+#include "core/or/circuitbuild.h"
+#include "core/or/circuitlist.h"
+#include "core/or/onion.h"
+#include "core/or/relay.h"
+
+#include "feature/nodelist/nodelist.h"
+
+#include "feature/relay/router.h"
+#include "feature/relay/routermode.h"
+#include "feature/relay/selftest.h"
+
+/* Before replying to an extend cell, check the state of the circuit
+ * <b>circ</b>, and the configured tor mode.
+ *
+ * <b>circ</b> must not be NULL.
+ *
+ * If the state and mode are valid, return 0.
+ * Otherwise, if they are invalid, log a protocol warning, and return -1.
+ */
+STATIC int
+circuit_extend_state_valid_helper(const struct circuit_t *circ)
+{
+ if (!server_mode(get_options())) {
+ circuitbuild_warn_client_extend();
+ return -1;
+ }
+
+ IF_BUG_ONCE(!circ) {
+ return -1;
+ }
+
+ if (circ->n_chan) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "n_chan already set. Bug/attack. Closing.");
+ return -1;
+ }
+
+ if (circ->n_hop) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "conn to next hop already launched. Bug/attack. Closing.");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Make sure the extend cell <b>ec</b> has an ed25519 link specifier.
+ *
+ * First, check that the RSA node id is valid.
+ * If the node id is valid, add the ed25519 link specifier (if required),
+ * and return 0.
+ *
+ * Otherwise, if the node id is invalid, log a protocol warning,
+ * and return -1.(And do not modify the extend cell.)
+ *
+ * Must be called before circuit_extend_lspec_valid_helper().
+ */
+STATIC int
+circuit_extend_add_ed25519_helper(struct extend_cell_t *ec)
+{
+ IF_BUG_ONCE(!ec) {
+ return -1;
+ }
+
+ /* Check if they asked us for 0000..0000. We support using
+ * an empty fingerprint for the first hop (e.g. for a bridge relay),
+ * but we don't want to let clients send us extend cells for empty
+ * fingerprints -- a) because it opens the user up to a mitm attack,
+ * and b) because it lets an attacker force the relay to hold open a
+ * new TLS connection for each extend request. */
+ if (tor_digest_is_zero((const char*)ec->node_id)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Client asked me to extend without specifying an id_digest.");
+ return -1;
+ }
+
+ /* Fill in ed_pubkey if it was not provided and we can infer it from
+ * our networkstatus */
+ if (ed25519_public_key_is_zero(&ec->ed_pubkey)) {
+ const node_t *node = node_get_by_id((const char*)ec->node_id);
+ const ed25519_public_key_t *node_ed_id = NULL;
+ if (node &&
+ node_supports_ed25519_link_authentication(node, 1) &&
+ (node_ed_id = node_get_ed25519_id(node))) {
+ ed25519_pubkey_copy(&ec->ed_pubkey, node_ed_id);
+ }
+ }
+
+ return 0;
+}
+
+/* Check if the address and port in the tor_addr_port_t <b>ap</b> are valid,
+ * and are allowed by the current ExtendAllowPrivateAddresses config.
+ *
+ * If they are valid, return true.
+ * Otherwise, if they are invalid, return false.
+ *
+ * If <b>log_zero_addrs</b> is true, log warnings about zero addresses at
+ * <b>log_level</b>. If <b>log_internal_addrs</b> is true, log warnings about
+ * internal addresses at <b>log_level</b>.
+ */
+static bool
+circuit_extend_addr_port_is_valid(const struct tor_addr_port_t *ap,
+ bool log_zero_addrs, bool log_internal_addrs,
+ int log_level)
+{
+ /* It's safe to print the family. But we don't want to print the address,
+ * unless specifically configured to do so. (Zero addresses aren't sensitive,
+ * But some internal addresses might be.)*/
+
+ if (!tor_addr_port_is_valid_ap(ap, 0)) {
+ if (log_zero_addrs) {
+ log_fn(log_level, LD_PROTOCOL,
+ "Client asked me to extend to a zero destination port or "
+ "%s address '%s'.",
+ fmt_addr_family(&ap->addr), safe_str(fmt_addrport_ap(ap)));
+ }
+ return false;
+ }
+
+ if (tor_addr_is_internal(&ap->addr, 0) &&
+ !get_options()->ExtendAllowPrivateAddresses) {
+ if (log_internal_addrs) {
+ log_fn(log_level, LD_PROTOCOL,
+ "Client asked me to extend to a private %s address '%s'.",
+ fmt_addr_family(&ap->addr),
+ safe_str(fmt_and_decorate_addr(&ap->addr)));
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/* Before replying to an extend cell, check the link specifiers in the extend
+ * cell <b>ec</b>, which was received on the circuit <b>circ</b>.
+ *
+ * If they are valid, return 0.
+ * Otherwise, if they are invalid, log a protocol warning, and return -1.
+ *
+ * Must be called after circuit_extend_add_ed25519_helper().
+ */
+STATIC int
+circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec,
+ const struct circuit_t *circ)
+{
+ IF_BUG_ONCE(!ec) {
+ return -1;
+ }
+
+ IF_BUG_ONCE(!circ) {
+ return -1;
+ }
+
+ /* Check the addresses, without logging */
+ const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv4,
+ false, false, 0);
+ const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv6,
+ false, false, 0);
+ /* We need at least one valid address */
+ if (!ipv4_valid && !ipv6_valid) {
+ /* Now, log the invalid addresses at protocol warning level */
+ circuit_extend_addr_port_is_valid(&ec->orport_ipv4,
+ true, true, LOG_PROTOCOL_WARN);
+ circuit_extend_addr_port_is_valid(&ec->orport_ipv6,
+ true, true, LOG_PROTOCOL_WARN);
+ /* And fail */
+ return -1;
+ } else if (!ipv4_valid) {
+ /* Always log unexpected internal addresses, but go on to use the other
+ * valid address */
+ circuit_extend_addr_port_is_valid(&ec->orport_ipv4,
+ false, true, LOG_PROTOCOL_WARN);
+ } else if (!ipv6_valid) {
+ circuit_extend_addr_port_is_valid(&ec->orport_ipv6,
+ false, true, LOG_PROTOCOL_WARN);
+ }
+
+ IF_BUG_ONCE(circ->magic != OR_CIRCUIT_MAGIC) {
+ return -1;
+ }
+
+ const channel_t *p_chan = CONST_TO_OR_CIRCUIT(circ)->p_chan;
+
+ IF_BUG_ONCE(!p_chan) {
+ return -1;
+ }
+
+ /* Next, check if we're being asked to connect to the hop that the
+ * extend cell came from. There isn't any reason for that, and it can
+ * assist circular-path attacks. */
+ if (tor_memeq(ec->node_id, p_chan->identity_digest, DIGEST_LEN)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Client asked me to extend back to the previous hop.");
+ return -1;
+ }
+
+ /* Check the previous hop Ed25519 ID too */
+ if (! ed25519_public_key_is_zero(&ec->ed_pubkey) &&
+ ed25519_pubkey_eq(&ec->ed_pubkey, &p_chan->ed25519_identity)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Client asked me to extend back to the previous hop "
+ "(by Ed25519 ID).");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* If possible, return a supported, non-NULL IP address.
+ *
+ * If both addresses are supported and non-NULL, choose one uniformly at
+ * random.
+ *
+ * If we have an IPv6-only extend, but IPv6 is not supported, returns NULL.
+ * If both addresses are NULL, also returns NULL. */
+STATIC const tor_addr_port_t *
+circuit_choose_ip_ap_for_extend(const tor_addr_port_t *ipv4_ap,
+ const tor_addr_port_t *ipv6_ap)
+{
+ const bool ipv6_supported = router_can_extend_over_ipv6(get_options());
+
+ /* If IPv6 is not supported, we can't use the IPv6 address. */
+ if (!ipv6_supported) {
+ ipv6_ap = NULL;
+ }
+
+ /* If there is no IPv6 address, IPv4 is always supported.
+ * Until clients include IPv6 ORPorts, and most relays support IPv6,
+ * this is the most common case. */
+ if (!ipv6_ap) {
+ return ipv4_ap;
+ }
+
+ /* If there is no IPv4 address, return the (possibly NULL) IPv6 address. */
+ if (!ipv4_ap) {
+ return ipv6_ap;
+ }
+
+ /* Now we have an IPv4 and an IPv6 address, and IPv6 is supported.
+ * So make an IPv6 connection at random, with probability 1 in N.
+ * 1 means "always IPv6 (and no IPv4)"
+ * 2 means "equal probability of IPv4 or IPv6"
+ * ... (and so on) ...
+ * (UINT_MAX - 1) means "almost always IPv4 (and almost never IPv6)"
+ * To disable IPv6, set ipv6_supported to 0.
+ */
+#define IPV6_CONNECTION_ONE_IN_N 2
+
+ bool choose_ipv6 = crypto_fast_rng_one_in_n(get_thread_fast_rng(),
+ IPV6_CONNECTION_ONE_IN_N);
+ if (choose_ipv6) {
+ return ipv6_ap;
+ } else {
+ return ipv4_ap;
+ }
+}
+
+/* When there is no open channel for an extend cell <b>ec</b>, set up the
+ * circuit <b>circ</b> to wait for a new connection.
+ *
+ * If <b>should_launch</b> is true, open a new connection. (Otherwise, we are
+ * already waiting for a new connection to the same relay.)
+ *
+ * Check if IPv6 extends are supported by our current configuration. If they
+ * are, new connections may be made over IPv4 or IPv6. (IPv4 connections are
+ * always supported.)
+ */
+STATIC void
+circuit_open_connection_for_extend(const struct extend_cell_t *ec,
+ struct circuit_t *circ,
+ int should_launch)
+{
+ /* We have to check circ first, so we can close it on all other failures */
+ IF_BUG_ONCE(!circ) {
+ /* We can't mark a NULL circuit for close. */
+ return;
+ }
+
+ /* Now we know that circ is not NULL */
+ IF_BUG_ONCE(!ec) {
+ circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+ return;
+ }
+
+ /* Check the addresses, without logging */
+ const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv4,
+ false, false, 0);
+ const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec->orport_ipv6,
+ false, false, 0);
+
+ IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) {
+ /* circuit_extend_lspec_valid_helper() should have caught this */
+ circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+ return;
+ }
+
+ const tor_addr_port_t *chosen_ap = circuit_choose_ip_ap_for_extend(
+ ipv4_valid ? &ec->orport_ipv4 : NULL,
+ ipv6_valid ? &ec->orport_ipv6 : NULL);
+ if (!chosen_ap) {
+ /* An IPv6-only extend, but IPv6 is not supported */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received IPv6-only extend, but we don't have an IPv6 ORPort.");
+ circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+ return;
+ }
+
+ circ->n_hop = extend_info_new(NULL /*nickname*/,
+ (const char*)ec->node_id,
+ &ec->ed_pubkey,
+ NULL, /*onion_key*/
+ NULL, /*curve25519_key*/
+ &chosen_ap->addr,
+ chosen_ap->port);
+
+ circ->n_chan_create_cell = tor_memdup(&ec->create_cell,
+ sizeof(ec->create_cell));
+
+ circuit_set_state(circ, CIRCUIT_STATE_CHAN_WAIT);
+
+ if (should_launch) {
+ /* we should try to open a connection */
+ channel_t *n_chan = channel_connect_for_circuit(
+ &circ->n_hop->addr,
+ circ->n_hop->port,
+ circ->n_hop->identity_digest,
+ &circ->n_hop->ed_identity);
+ if (!n_chan) {
+ log_info(LD_CIRC,"Launching n_chan failed. Closing circuit.");
+ circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
+ return;
+ }
+ log_debug(LD_CIRC,"connecting in progress (or finished). Good.");
+ }
+}
+
+/** Take the 'extend' <b>cell</b>, pull out addr/port plus the onion
+ * skin and identity digest for the next hop. If we're already connected,
+ * pass the onion skin to the next hop using a create cell; otherwise
+ * launch a new OR connection, and <b>circ</b> will notice when the
+ * connection succeeds or fails.
+ *
+ * Return -1 if we want to warn and tear down the circuit, else return 0.
+ */
+int
+circuit_extend(struct cell_t *cell, struct circuit_t *circ)
+{
+ channel_t *n_chan;
+ relay_header_t rh;
+ extend_cell_t ec;
+ const char *msg = NULL;
+ int should_launch = 0;
+
+ IF_BUG_ONCE(!cell) {
+ return -1;
+ }
+
+ IF_BUG_ONCE(!circ) {
+ return -1;
+ }
+
+ if (circuit_extend_state_valid_helper(circ) < 0)
+ return -1;
+
+ relay_header_unpack(&rh, cell->payload);
+
+ if (extend_cell_parse(&ec, rh.command,
+ cell->payload+RELAY_HEADER_SIZE,
+ rh.length) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Can't parse extend cell. Closing circuit.");
+ return -1;
+ }
+
+ if (circuit_extend_add_ed25519_helper(&ec) < 0)
+ return -1;
+
+ if (circuit_extend_lspec_valid_helper(&ec, circ) < 0)
+ return -1;
+
+ /* Check the addresses, without logging */
+ const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv4,
+ false, false, 0);
+ const int ipv6_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv6,
+ false, false, 0);
+ IF_BUG_ONCE(!ipv4_valid && !ipv6_valid) {
+ /* circuit_extend_lspec_valid_helper() should have caught this */
+ return -1;
+ }
+
+ n_chan = channel_get_for_extend((const char*)ec.node_id,
+ &ec.ed_pubkey,
+ ipv4_valid ? &ec.orport_ipv4.addr : NULL,
+ ipv6_valid ? &ec.orport_ipv6.addr : NULL,
+ &msg,
+ &should_launch);
+
+ if (!n_chan) {
+ /* We can't use fmt_addr*() twice in the same function call,
+ * because it uses a static buffer. */
+ log_debug(LD_CIRC|LD_OR, "Next router IPv4 (%s): %s.",
+ fmt_addrport_ap(&ec.orport_ipv4),
+ msg ? msg : "????");
+ log_debug(LD_CIRC|LD_OR, "Next router IPv6 (%s).",
+ fmt_addrport_ap(&ec.orport_ipv6));
+
+ circuit_open_connection_for_extend(&ec, circ, should_launch);
+
+ /* return success. The onion/circuit/etc will be taken care of
+ * automatically (may already have been) whenever n_chan reaches
+ * OR_CONN_STATE_OPEN.
+ */
+ return 0;
+ } else {
+ /* Connection is already established.
+ * So we need to extend the circuit to the next hop. */
+ tor_assert(!circ->n_hop);
+ circ->n_chan = n_chan;
+ log_debug(LD_CIRC,
+ "n_chan is %s.",
+ channel_get_canonical_remote_descr(n_chan));
+
+ if (circuit_deliver_create_cell(circ, &ec.create_cell, 1) < 0)
+ return -1;
+
+ return 0;
+ }
+}
+
+/** On a relay, accept a create cell, initialise a circuit, and send a
+ * created cell back.
+ *
+ * Given:
+ * - a response payload consisting of:
+ * - the <b>created_cell</b> and
+ * - an optional <b>rend_circ_nonce</b>, and
+ * - <b>keys</b> of length <b>keys_len</b>, which must be
+ * CPATH_KEY_MATERIAL_LEN;
+ * then:
+ * - initialize the circuit <b>circ</b>'s cryptographic material,
+ * - set the circuit's state to open, and
+ * - send a created cell back on that circuit.
+ *
+ * If we haven't found our ORPorts reachable yet, and the channel meets the
+ * necessary conditions, mark the relevant ORPorts as reachable.
+ *
+ * Returns -1 if cell or circuit initialisation fails.
+ */
+int
+onionskin_answer(struct or_circuit_t *circ,
+ const created_cell_t *created_cell,
+ const char *keys, size_t keys_len,
+ const uint8_t *rend_circ_nonce)
+{
+ cell_t cell;
+
+ IF_BUG_ONCE(!circ) {
+ return -1;
+ }
+
+ IF_BUG_ONCE(!created_cell) {
+ return -1;
+ }
+
+ IF_BUG_ONCE(!keys) {
+ return -1;
+ }
+
+ IF_BUG_ONCE(!rend_circ_nonce) {
+ return -1;
+ }
+
+ tor_assert(keys_len == CPATH_KEY_MATERIAL_LEN);
+
+ if (created_cell_format(&cell, created_cell) < 0) {
+ log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d).",
+ (int)created_cell->cell_type, (int)created_cell->handshake_len);
+ return -1;
+ }
+ cell.circ_id = circ->p_circ_id;
+
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+
+ log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.",
+ (unsigned int)get_uint32(keys),
+ (unsigned int)get_uint32(keys+20));
+ if (relay_crypto_init(&circ->crypto, keys, keys_len, 0, 0)<0) {
+ log_warn(LD_BUG,"Circuit initialization failed.");
+ return -1;
+ }
+
+ memcpy(circ->rend_circ_nonce, rend_circ_nonce, DIGEST_LEN);
+
+ int used_create_fast = (created_cell->cell_type == CELL_CREATED_FAST);
+
+ append_cell_to_circuit_queue(TO_CIRCUIT(circ),
+ circ->p_chan, &cell, CELL_DIRECTION_IN, 0);
+ log_debug(LD_CIRC,"Finished sending '%s' cell.",
+ used_create_fast ? "created_fast" : "created");
+
+ /* Ignore the local bit when ExtendAllowPrivateAddresses is set:
+ * it violates the assumption that private addresses are local.
+ * Also, many test networks run on local addresses, and
+ * TestingTorNetwork sets ExtendAllowPrivateAddresses. */
+ if ((!channel_is_local(circ->p_chan)
+ || get_options()->ExtendAllowPrivateAddresses)
+ && !channel_is_outgoing(circ->p_chan)) {
+ /* record that we could process create cells from a non-local conn
+ * that we didn't initiate; presumably this means that create cells
+ * can reach us too. */
+ router_orport_found_reachable();
+ }
+
+ return 0;
+}
diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h
new file mode 100644
index 0000000000..0783161538
--- /dev/null
+++ b/src/feature/relay/circuitbuild_relay.h
@@ -0,0 +1,87 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file circuitbuild_relay.h
+ * @brief Header for feature/relay/circuitbuild_relay.c
+ **/
+
+#ifndef TOR_FEATURE_RELAY_CIRCUITBUILD_RELAY_H
+#define TOR_FEATURE_RELAY_CIRCUITBUILD_RELAY_H
+
+#include "lib/cc/torint.h"
+#include "lib/log/log.h"
+
+#include "app/config/config.h"
+
+struct cell_t;
+struct created_cell_t;
+
+struct circuit_t;
+struct or_circuit_t;
+struct extend_cell_t;
+
+/* Log a protocol warning about getting an extend cell on a client. */
+static inline void
+circuitbuild_warn_client_extend(void)
+{
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Got an extend cell, but running as a client. Closing.");
+}
+
+#ifdef HAVE_MODULE_RELAY
+
+int circuit_extend(struct cell_t *cell, struct circuit_t *circ);
+
+int onionskin_answer(struct or_circuit_t *circ,
+ const struct created_cell_t *created_cell,
+ const char *keys, size_t keys_len,
+ const uint8_t *rend_circ_nonce);
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+static inline int
+circuit_extend(struct cell_t *cell, struct circuit_t *circ)
+{
+ (void)cell;
+ (void)circ;
+ circuitbuild_warn_client_extend();
+ return -1;
+}
+
+static inline int
+onionskin_answer(struct or_circuit_t *circ,
+ const struct created_cell_t *created_cell,
+ const char *keys, size_t keys_len,
+ const uint8_t *rend_circ_nonce)
+{
+ (void)circ;
+ (void)created_cell;
+ (void)keys;
+ (void)keys_len;
+ (void)rend_circ_nonce;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
+#ifdef TOR_UNIT_TESTS
+
+STATIC int circuit_extend_state_valid_helper(const struct circuit_t *circ);
+STATIC int circuit_extend_add_ed25519_helper(struct extend_cell_t *ec);
+STATIC int circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec,
+ const struct circuit_t *circ);
+STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend(
+ const tor_addr_port_t *ipv4_ap,
+ const tor_addr_port_t *ipv6_ap);
+STATIC void circuit_open_connection_for_extend(const struct extend_cell_t *ec,
+ struct circuit_t *circ,
+ int should_launch);
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* !defined(TOR_FEATURE_RELAY_CIRCUITBUILD_RELAY_H) */
diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c
index 7ab4ca0f45..b83bd9b758 100644
--- a/src/feature/relay/dns.c
+++ b/src/feature/relay/dns.c
@@ -146,9 +146,9 @@ cached_resolve_hash(cached_resolve_t *a)
}
HT_PROTOTYPE(cache_map, cached_resolve_t, node, cached_resolve_hash,
- cached_resolves_eq)
+ cached_resolves_eq);
HT_GENERATE2(cache_map, cached_resolve_t, node, cached_resolve_hash,
- cached_resolves_eq, 0.6, tor_reallocarray_, tor_free_)
+ cached_resolves_eq, 0.6, tor_reallocarray_, tor_free_);
/** Initialize the DNS cache. */
static void
@@ -268,22 +268,6 @@ has_dns_init_failed(void)
return nameserver_config_failed;
}
-/** Helper: Given a TTL from a DNS response, determine what TTL to give the
- * OP that asked us to resolve it, and how long to cache that record
- * ourselves. */
-uint32_t
-dns_clip_ttl(uint32_t ttl)
-{
- /* This logic is a defense against "DefectTor" DNS-based traffic
- * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf .
- * We only give two values: a "low" value and a "high" value.
- */
- if (ttl < MIN_DNS_TTL_AT_EXIT)
- return MIN_DNS_TTL_AT_EXIT;
- else
- return MAX_DNS_TTL_AT_EXIT;
-}
-
/** Helper: free storage held by an entry in the DNS cache. */
static void
free_cached_resolve_(cached_resolve_t *r)
@@ -521,7 +505,7 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type,
uint32_t ttl;
buf[0] = answer_type;
- ttl = dns_clip_ttl(conn->address_ttl);
+ ttl = clip_dns_ttl(conn->address_ttl);
switch (answer_type)
{
@@ -593,7 +577,7 @@ send_resolved_hostname_cell,(edge_connection_t *conn,
size_t namelen = strlen(hostname);
tor_assert(namelen < 256);
- ttl = dns_clip_ttl(conn->address_ttl);
+ ttl = clip_dns_ttl(conn->address_ttl);
buf[0] = RESOLVED_TYPE_HOSTNAME;
buf[1] = (uint8_t)namelen;
@@ -987,25 +971,6 @@ assert_connection_edge_not_dns_pending(edge_connection_t *conn)
#endif /* 1 */
}
-/** Log an error and abort if any connection waiting for a DNS resolve is
- * corrupted. */
-void
-assert_all_pending_dns_resolves_ok(void)
-{
- pending_connection_t *pend;
- cached_resolve_t **resolve;
-
- HT_FOREACH(resolve, cache_map, &cache_root) {
- for (pend = (*resolve)->pending_connections;
- pend;
- pend = pend->next) {
- assert_connection_ok(TO_CONN(pend->conn), 0);
- tor_assert(!SOCKET_OK(pend->conn->base_.s));
- tor_assert(!connection_in_array(TO_CONN(pend->conn)));
- }
- }
-}
-
/** Remove <b>conn</b> from the list of connections waiting for conn-\>address.
*/
void
@@ -1063,7 +1028,7 @@ connection_dns_remove(edge_connection_t *conn)
* the resolve for <b>address</b> itself, and remove any cached results for
* <b>address</b> from the cache.
*/
-MOCK_IMPL(void,
+MOCK_IMPL(STATIC void,
dns_cancel_pending_resolve,(const char *address))
{
pending_connection_t *pend;
@@ -1338,7 +1303,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve)
resolve->ttl_hostname < ttl)
ttl = resolve->ttl_hostname;
- set_expiry(new_resolve, time(NULL) + dns_clip_ttl(ttl));
+ set_expiry(new_resolve, time(NULL) + clip_dns_ttl(ttl));
}
assert_cache_ok();
@@ -1626,12 +1591,17 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
} else if (type == DNS_IPv6_AAAA && count) {
char answer_buf[TOR_ADDR_BUF_LEN];
char *escaped_address;
+ const char *ip_str;
struct in6_addr *addrs = addresses;
tor_addr_from_in6(&addr, &addrs[0]);
- tor_inet_ntop(AF_INET6, &addrs[0], answer_buf, sizeof(answer_buf));
+ ip_str = tor_inet_ntop(AF_INET6, &addrs[0], answer_buf,
+ sizeof(answer_buf));
escaped_address = esc_for_log(string_address);
- if (answer_is_wildcarded(answer_buf)) {
+ if (BUG(ip_str == NULL)) {
+ log_warn(LD_EXIT, "tor_inet_ntop() failed!");
+ result = DNS_ERR_NOTEXIST;
+ } else if (answer_is_wildcarded(answer_buf)) {
log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked "
"address %s; treating as a failure.",
safe_str(escaped_address),
@@ -1898,6 +1868,7 @@ evdns_wildcard_check_callback(int result, char type, int count, int ttl,
void *addresses, void *arg)
{
(void)ttl;
+ const char *ip_str;
++n_wildcard_requests;
if (result == DNS_ERR_NONE && count) {
char *string_address = arg;
@@ -1907,16 +1878,22 @@ evdns_wildcard_check_callback(int result, char type, int count, int ttl,
for (i = 0; i < count; ++i) {
char answer_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
+ int ntoa_res;
in.s_addr = addrs[i];
- tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
- wildcard_increment_answer(answer_buf);
+ ntoa_res = tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
+ tor_assert_nonfatal(ntoa_res >= 0);
+ if (ntoa_res > 0)
+ wildcard_increment_answer(answer_buf);
}
} else if (type == DNS_IPv6_AAAA) {
const struct in6_addr *addrs = addresses;
for (i = 0; i < count; ++i) {
char answer_buf[TOR_ADDR_BUF_LEN+1];
- tor_inet_ntop(AF_INET6, &addrs[i], answer_buf, sizeof(answer_buf));
- wildcard_increment_answer(answer_buf);
+ ip_str = tor_inet_ntop(AF_INET6, &addrs[i], answer_buf,
+ sizeof(answer_buf));
+ tor_assert_nonfatal(ip_str);
+ if (ip_str)
+ wildcard_increment_answer(answer_buf);
}
}
@@ -2051,12 +2028,12 @@ dns_launch_correctness_checks(void)
/* Wait a while before launching requests for test addresses, so we can
* get the results from checking for wildcarding. */
- if (! launch_event)
+ if (!launch_event)
launch_event = tor_evtimer_new(tor_libevent_get_base(),
launch_test_addresses, NULL);
timeout.tv_sec = 30;
timeout.tv_usec = 0;
- if (evtimer_add(launch_event, &timeout)<0) {
+ if (evtimer_add(launch_event, &timeout) < 0) {
log_warn(LD_BUG, "Couldn't add timer for checking for dns hijacking");
}
}
@@ -2188,7 +2165,7 @@ dns_cache_handle_oom(time_t now, size_t min_remove_bytes)
total_bytes_removed += bytes_removed;
/* Increase time_inc by a reasonable fraction. */
- time_inc += (MAX_DNS_TTL_AT_EXIT / 4);
+ time_inc += (MAX_DNS_TTL / 4);
} while (total_bytes_removed < min_remove_bytes);
return total_bytes_removed;
diff --git a/src/feature/relay/dns.h b/src/feature/relay/dns.h
index 2b1da8d126..120b75bf8d 100644
--- a/src/feature/relay/dns.h
+++ b/src/feature/relay/dns.h
@@ -12,29 +12,14 @@
#ifndef TOR_DNS_H
#define TOR_DNS_H
-/** Lowest value for DNS ttl that a server will give. */
-#define MIN_DNS_TTL_AT_EXIT (5*60)
-/** Highest value for DNS ttl that a server will give. */
-#define MAX_DNS_TTL_AT_EXIT (60*60)
-
-/** How long do we keep DNS cache entries before purging them (regardless of
- * their TTL)? */
-#define MAX_DNS_ENTRY_AGE (3*60*60)
-/** How long do we cache/tell clients to cache DNS records when no TTL is
- * known? */
-#define DEFAULT_DNS_TTL (30*60)
+#ifdef HAVE_MODULE_RELAY
int dns_init(void);
int has_dns_init_failed(void);
-void dns_free_all(void);
-uint32_t dns_clip_ttl(uint32_t ttl);
int dns_reset(void);
void connection_dns_remove(edge_connection_t *conn);
void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
-void assert_all_pending_dns_resolves_ok(void);
-MOCK_DECL(void,dns_cancel_pending_resolve,(const char *question));
int dns_resolve(edge_connection_t *exitconn);
-void dns_launch_correctness_checks(void);
int dns_seems_to_be_broken(void);
int dns_seems_to_be_broken_for_ipv6(void);
void dns_reset_correctness_checks(void);
@@ -42,6 +27,48 @@ size_t dns_cache_total_allocation(void);
void dump_dns_mem_usage(int severity);
size_t dns_cache_handle_oom(time_t now, size_t min_remove_bytes);
+/* These functions are only used within the feature/relay module, and don't
+ * need stubs. */
+void dns_free_all(void);
+void dns_launch_correctness_checks(void);
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#define dns_init() (0)
+#define dns_seems_to_be_broken() (0)
+#define has_dns_init_failed() (0)
+#define dns_cache_total_allocation() (0)
+
+#define dns_reset_correctness_checks() STMT_NIL
+
+#define assert_connection_edge_not_dns_pending(conn) \
+ ((void)(conn))
+#define dump_dns_mem_usage(severity)\
+ ((void)(severity))
+#define dns_cache_handle_oom(now, bytes) \
+ ((void)(now), (void)(bytes), 0)
+
+#define connection_dns_remove(conn) \
+ STMT_BEGIN \
+ (void)(conn); \
+ tor_assert_nonfatal_unreached(); \
+ STMT_END
+
+static inline int
+dns_reset(void)
+{
+ return 0;
+}
+static inline int
+dns_resolve(edge_connection_t *exitconn)
+{
+ (void)exitconn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
#ifdef DNS_PRIVATE
#include "feature/relay/dns_structs.h"
@@ -50,6 +77,7 @@ size_t number_of_configured_nameservers(void);
tor_addr_t *configured_nameserver_address(const size_t idx);
#endif
+MOCK_DECL(STATIC void,dns_cancel_pending_resolve,(const char *question));
MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn,
int is_resolve,or_circuit_t *oncirc, char **hostname_out,
int *made_connection_pending_out, cached_resolve_t **resolve_out));
@@ -74,4 +102,3 @@ launch_resolve,(cached_resolve_t *resolve));
#endif /* defined(DNS_PRIVATE) */
#endif /* !defined(TOR_DNS_H) */
-
diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c
index ce4e043dd7..cff5f42cc7 100644
--- a/src/feature/relay/ext_orport.c
+++ b/src/feature/relay/ext_orport.c
@@ -602,7 +602,7 @@ connection_ext_or_process_inbuf(or_connection_t *or_conn)
command->body, command->len) < 0)
goto err;
} else {
- log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).",
+ log_notice(LD_NET,"Got Extended ORPort command we don't recognize (%u).",
command->cmd);
}
@@ -652,6 +652,77 @@ connection_ext_or_start_auth(or_connection_t *or_conn)
return 0;
}
+/** Global map between Extended ORPort identifiers and OR
+ * connections. */
+static digestmap_t *orconn_ext_or_id_map = NULL;
+
+/** Remove the Extended ORPort identifier of <b>conn</b> from the
+ * global identifier list. Also, clear the identifier from the
+ * connection itself. */
+void
+connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
+{
+ or_connection_t *tmp;
+ if (!orconn_ext_or_id_map)
+ return;
+ if (!conn->ext_or_conn_id)
+ return;
+
+ tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
+ if (!tor_digest_is_zero(conn->ext_or_conn_id))
+ tor_assert(tmp == conn);
+
+ memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
+}
+
+#ifdef TOR_UNIT_TESTS
+/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
+ * connection is found. */
+or_connection_t *
+connection_or_get_by_ext_or_id(const char *id)
+{
+ if (!orconn_ext_or_id_map)
+ return NULL;
+ return digestmap_get(orconn_ext_or_id_map, id);
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/** Deallocate the global Extended ORPort identifier list */
+void
+connection_or_clear_ext_or_id_map(void)
+{
+ digestmap_free(orconn_ext_or_id_map, NULL);
+ orconn_ext_or_id_map = NULL;
+}
+
+/** Creates an Extended ORPort identifier for <b>conn</b> and deposits
+ * it into the global list of identifiers. */
+void
+connection_or_set_ext_or_identifier(or_connection_t *conn)
+{
+ char random_id[EXT_OR_CONN_ID_LEN];
+ or_connection_t *tmp;
+
+ if (!orconn_ext_or_id_map)
+ orconn_ext_or_id_map = digestmap_new();
+
+ /* Remove any previous identifiers: */
+ if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
+ connection_or_remove_from_ext_or_id_map(conn);
+
+ do {
+ crypto_rand(random_id, sizeof(random_id));
+ } while (digestmap_get(orconn_ext_or_id_map, random_id));
+
+ if (!conn->ext_or_conn_id)
+ conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
+
+ memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
+
+ tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
+ tor_assert(!tmp);
+}
+
/** Free any leftover allocated memory of the ext_orport.c subsystem. */
void
ext_orport_free_all(void)
diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h
index dbe89fce18..416c358397 100644
--- a/src/feature/relay/ext_orport.h
+++ b/src/feature/relay/ext_orport.h
@@ -31,26 +31,56 @@
#define EXT_OR_CONN_STATE_FLUSHING 5
#define EXT_OR_CONN_STATE_MAX_ 5
-int connection_ext_or_start_auth(or_connection_t *or_conn);
-
-ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
+#ifdef HAVE_MODULE_RELAY
-#define ext_or_cmd_free(cmd) \
- FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd))
+int connection_ext_or_start_auth(or_connection_t *or_conn);
-void ext_or_cmd_free_(ext_or_cmd_t *cmd);
void connection_or_set_ext_or_identifier(or_connection_t *conn);
void connection_or_remove_from_ext_or_id_map(or_connection_t *conn);
void connection_or_clear_ext_or_id_map(void);
-or_connection_t *connection_or_get_by_ext_or_id(const char *id);
-
int connection_ext_or_finished_flushing(or_connection_t *conn);
int connection_ext_or_process_inbuf(or_connection_t *or_conn);
+char *get_ext_or_auth_cookie_file_name(void);
+/* (No stub needed for these: they are only called within feature/relay.) */
int init_ext_or_cookie_authentication(int is_enabled);
-char *get_ext_or_auth_cookie_file_name(void);
void ext_orport_free_all(void);
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+static inline int
+connection_ext_or_start_auth(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+static inline int
+connection_ext_or_finished_flushing(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+static inline int
+connection_ext_or_process_inbuf(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+#define connection_or_set_ext_or_identifier(conn) \
+ ((void)(conn))
+#define connection_or_remove_from_ext_or_id_map(conn) \
+ ((void)(conn))
+#define connection_or_clear_ext_or_id_map() \
+ STMT_NIL
+
+#define get_ext_or_auth_cookie_file_name() \
+ (NULL)
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
#ifdef EXT_ORPORT_PRIVATE
STATIC int connection_write_ext_or_command(connection_t *conn,
uint16_t command,
@@ -60,9 +90,11 @@ STATIC int handle_client_auth_nonce(const char *client_nonce,
size_t client_nonce_len,
char **client_hash_out,
char **reply_out, size_t *reply_len_out);
+
#ifdef TOR_UNIT_TESTS
extern uint8_t *ext_or_auth_cookie;
extern int ext_or_auth_cookie_is_set;
+or_connection_t *connection_or_get_by_ext_or_id(const char *id);
#endif
#endif /* defined(EXT_ORPORT_PRIVATE) */
diff --git a/src/feature/relay/include.am b/src/feature/relay/include.am
index a4c025ae12..84bb1ff35e 100644
--- a/src/feature/relay/include.am
+++ b/src/feature/relay/include.am
@@ -1,32 +1,38 @@
# Legacy shared relay code: migrate to the relay module over time
LIBTOR_APP_A_SOURCES += \
- src/feature/relay/dns.c \
- src/feature/relay/ext_orport.c \
src/feature/relay/onion_queue.c \
- src/feature/relay/router.c \
- src/feature/relay/routerkeys.c \
- src/feature/relay/selftest.c
+ src/feature/relay/relay_find_addr.c \
+ src/feature/relay/router.c
# The Relay module.
# ADD_C_FILE: INSERT SOURCES HERE.
MODULE_RELAY_SOURCES = \
+ src/feature/relay/circuitbuild_relay.c \
+ src/feature/relay/dns.c \
+ src/feature/relay/ext_orport.c \
src/feature/relay/routermode.c \
src/feature/relay/relay_config.c \
+ src/feature/relay/relay_handshake.c \
src/feature/relay/relay_periodic.c \
src/feature/relay/relay_sys.c \
+ src/feature/relay/routerkeys.c \
+ src/feature/relay/selftest.c \
src/feature/relay/transport_config.c
# ADD_C_FILE: INSERT HEADERS HERE.
noinst_HEADERS += \
+ src/feature/relay/circuitbuild_relay.h \
src/feature/relay/dns.h \
src/feature/relay/dns_structs.h \
src/feature/relay/ext_orport.h \
src/feature/relay/onion_queue.h \
src/feature/relay/relay_config.h \
+ src/feature/relay/relay_handshake.h \
src/feature/relay/relay_periodic.h \
src/feature/relay/relay_sys.h \
+ src/feature/relay/relay_find_addr.h \
src/feature/relay/router.h \
src/feature/relay/routerkeys.h \
src/feature/relay/routermode.h \
diff --git a/src/feature/relay/onion_queue.c b/src/feature/relay/onion_queue.c
index ce2d41b7e1..3cbaa65d28 100644
--- a/src/feature/relay/onion_queue.c
+++ b/src/feature/relay/onion_queue.c
@@ -49,10 +49,12 @@ typedef struct onion_queue_t {
/** 5 seconds on the onion queue til we just send back a destroy */
#define ONIONQUEUE_WAIT_CUTOFF 5
+TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t);
+typedef struct onion_queue_head_t onion_queue_head_t;
+
/** Array of queues of circuits waiting for CPU workers. An element is NULL
* if that queue is empty.*/
-static TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t)
- ol_list[MAX_ONION_HANDSHAKE_TYPE+1] =
+static onion_queue_head_t ol_list[MAX_ONION_HANDSHAKE_TYPE+1] =
{ TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */
TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c
index 3e9961f47e..fac6a2f577 100644
--- a/src/feature/relay/relay_config.c
+++ b/src/feature/relay/relay_config.c
@@ -29,7 +29,6 @@
#include "core/mainloop/connection.h"
#include "core/mainloop/cpuworker.h"
#include "core/mainloop/mainloop.h"
-#include "core/or/circuitbuild.h"
#include "core/or/connection_or.h"
#include "core/or/port_cfg_st.h"
@@ -44,6 +43,7 @@
#include "feature/dircache/consdiffmgr.h"
#include "feature/relay/dns.h"
#include "feature/relay/routermode.h"
+#include "feature/relay/selftest.h"
/** Contents of most recently read DirPortFrontPage file. */
static char *global_dirfrontpagecontents = NULL;
diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c
new file mode 100644
index 0000000000..86cd799d42
--- /dev/null
+++ b/src/feature/relay/relay_find_addr.c
@@ -0,0 +1,133 @@
+/* Copyright (c) 2001-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file relay_find_addr.c
+ * \brief Implement mechanism for a relay to find its address.
+ **/
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "app/config/resolve_addr.h"
+
+#include "core/mainloop/mainloop.h"
+
+#include "feature/control/control_events.h"
+#include "feature/dircommon/dir_connection_st.h"
+#include "feature/relay/relay_find_addr.h"
+#include "feature/relay/router.h"
+#include "feature/relay/routermode.h"
+
+/** The most recently guessed value of our IP address, based on directory
+ * headers. */
+static tor_addr_t last_guessed_ip = TOR_ADDR_NULL;
+
+/** We failed to resolve our address locally, but we'd like to build
+ * a descriptor and publish / test reachability. If we have a guess
+ * about our address based on directory headers, answer it and return
+ * 0; else return -1. */
+static int
+router_guess_address_from_dir_headers(uint32_t *guess)
+{
+ if (!tor_addr_is_null(&last_guessed_ip)) {
+ *guess = tor_addr_to_ipv4h(&last_guessed_ip);
+ return 0;
+ }
+ return -1;
+}
+
+/** A directory server <b>d_conn</b> told us our IP address is
+ * <b>suggestion</b>.
+ * If this address is different from the one we think we are now, and
+ * if our computer doesn't actually know its IP address, then switch. */
+void
+router_new_address_suggestion(const char *suggestion,
+ const dir_connection_t *d_conn)
+{
+ tor_addr_t addr;
+ uint32_t cur = 0; /* Current IPv4 address. */
+ const or_options_t *options = get_options();
+
+ /* first, learn what the IP address actually is */
+ if (tor_addr_parse(&addr, suggestion) == -1) {
+ log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.",
+ escaped(suggestion));
+ return;
+ }
+
+ log_debug(LD_DIR, "Got X-Your-Address-Is: %s.", suggestion);
+
+ if (!server_mode(options)) {
+ tor_addr_copy(&last_guessed_ip, &addr);
+ return;
+ }
+
+ /* XXXX ipv6 */
+ cur = get_last_resolved_addr();
+ if (cur ||
+ resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) {
+ /* We're all set -- we already know our address. Great. */
+ tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we
+ need it later */
+ return;
+ }
+ if (tor_addr_is_internal(&addr, 0)) {
+ /* Don't believe anybody who says our IP is, say, 127.0.0.1. */
+ return;
+ }
+ if (tor_addr_eq(&d_conn->base_.addr, &addr)) {
+ /* Don't believe anybody who says our IP is their IP. */
+ log_debug(LD_DIR, "A directory server told us our IP address is %s, "
+ "but they are just reporting their own IP address. Ignoring.",
+ suggestion);
+ return;
+ }
+
+ /* Okay. We can't resolve our own address, and X-Your-Address-Is is giving
+ * us an answer different from what we had the last time we managed to
+ * resolve it. */
+ if (!tor_addr_eq(&last_guessed_ip, &addr)) {
+ control_event_server_status(LOG_NOTICE,
+ "EXTERNAL_ADDRESS ADDRESS=%s METHOD=DIRSERV",
+ suggestion);
+ log_addr_has_changed(LOG_NOTICE, &last_guessed_ip, &addr,
+ d_conn->base_.address);
+ ip_address_changed(0);
+ tor_addr_copy(&last_guessed_ip, &addr); /* router_rebuild_descriptor()
+ will fetch it */
+ }
+}
+
+/** Make a current best guess at our address, either because
+ * it's configured in torrc, or because we've learned it from
+ * dirserver headers. Place the answer in *<b>addr</b> and return
+ * 0 on success, else return -1 if we have no guess.
+ *
+ * If <b>cache_only</b> is true, just return any cached answers, and
+ * don't try to get any new answers.
+ */
+MOCK_IMPL(int,
+router_pick_published_address, (const or_options_t *options, uint32_t *addr,
+ int cache_only))
+{
+ /* First, check the cached output from resolve_my_address(). */
+ *addr = get_last_resolved_addr();
+ if (*addr)
+ return 0;
+
+ /* Second, consider doing a resolve attempt right here. */
+ if (!cache_only) {
+ if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) {
+ log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
+ return 0;
+ }
+ }
+
+ /* Third, check the cached output from router_new_address_suggestion(). */
+ if (router_guess_address_from_dir_headers(addr) >= 0)
+ return 0;
+
+ /* We have no useful cached answers. Return failure. */
+ return -1;
+}
diff --git a/src/feature/relay/relay_find_addr.h b/src/feature/relay/relay_find_addr.h
new file mode 100644
index 0000000000..ac51a977e6
--- /dev/null
+++ b/src/feature/relay/relay_find_addr.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file relay_find_addr.h
+ * \brief Header file for relay_find_addr.c.
+ **/
+
+#ifndef TOR_RELAY_FIND_ADDR_H
+#define TOR_RELAY_FIND_ADDR_H
+
+MOCK_DECL(int, router_pick_published_address,
+ (const or_options_t *options, uint32_t *addr, int cache_only));
+
+void router_new_address_suggestion(const char *suggestion,
+ const dir_connection_t *d_conn);
+
+#ifdef RELAY_FIND_ADDR_PRIVATE
+
+#endif /* RELAY_FIND_ADDR_PRIVATE */
+
+#endif /* TOR_RELAY_FIND_ADDR_H */
+
diff --git a/src/feature/relay/relay_handshake.c b/src/feature/relay/relay_handshake.c
new file mode 100644
index 0000000000..030dc94956
--- /dev/null
+++ b/src/feature/relay/relay_handshake.c
@@ -0,0 +1,565 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_handshake.c
+ * @brief Functions to implement the relay-only parts of our
+ * connection handshake.
+ *
+ * Some parts of our TLS link handshake are only done by relays (including
+ * bridges). Specifically, only relays need to send CERTS cells; only
+ * relays need to send or receive AUTHCHALLENGE cells, and only relays need to
+ * send or receive AUTHENTICATE cells.
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "feature/relay/relay_handshake.h"
+
+#include "app/config/config.h"
+#include "core/or/connection_or.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "trunnel/link_handshake.h"
+#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/torcert.h"
+
+#include "core/or/or_connection_st.h"
+#include "core/or/or_handshake_certs_st.h"
+#include "core/or/or_handshake_state_st.h"
+#include "core/or/var_cell_st.h"
+
+#include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
+
+/** Helper used to add an encoded certs to a cert cell */
+static void
+add_certs_cell_cert_helper(certs_cell_t *certs_cell,
+ uint8_t cert_type,
+ const uint8_t *cert_encoded,
+ size_t cert_len)
+{
+ tor_assert(cert_len <= UINT16_MAX);
+ certs_cell_cert_t *ccc = certs_cell_cert_new();
+ ccc->cert_type = cert_type;
+ ccc->cert_len = cert_len;
+ certs_cell_cert_setlen_body(ccc, cert_len);
+ memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len);
+
+ certs_cell_add_certs(certs_cell, ccc);
+}
+
+/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at
+ * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are
+ * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>.
+ * (If <b>cert</b> is NULL, take no action.) */
+static void
+add_x509_cert(certs_cell_t *certs_cell,
+ uint8_t cert_type,
+ const tor_x509_cert_t *cert)
+{
+ if (NULL == cert)
+ return;
+
+ const uint8_t *cert_encoded = NULL;
+ size_t cert_len;
+ tor_x509_cert_get_der(cert, &cert_encoded, &cert_len);
+
+ add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len);
+}
+
+/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object
+ * that we are building in <b>certs_cell</b>. Set its type field to
+ * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */
+static void
+add_ed25519_cert(certs_cell_t *certs_cell,
+ uint8_t cert_type,
+ const tor_cert_t *cert)
+{
+ if (NULL == cert)
+ return;
+
+ add_certs_cell_cert_helper(certs_cell, cert_type,
+ cert->encoded, cert->encoded_len);
+}
+
+#ifdef TOR_UNIT_TESTS
+int certs_cell_ed25519_disabled_for_testing = 0;
+#else
+#define certs_cell_ed25519_disabled_for_testing 0
+#endif
+
+/** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1
+ * on failure. */
+int
+connection_or_send_certs_cell(or_connection_t *conn)
+{
+ const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL;
+ tor_x509_cert_t *own_link_cert = NULL;
+ var_cell_t *cell;
+
+ certs_cell_t *certs_cell = NULL;
+
+ tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
+
+ if (! conn->handshake_state)
+ return -1;
+
+ const int conn_in_server_mode = ! conn->handshake_state->started_here;
+
+ /* Get the encoded values of the X509 certificates */
+ if (tor_tls_get_my_certs(conn_in_server_mode,
+ &global_link_cert, &id_cert) < 0)
+ return -1;
+
+ if (conn_in_server_mode) {
+ own_link_cert = tor_tls_get_own_cert(conn->tls);
+ }
+ tor_assert(id_cert);
+
+ certs_cell = certs_cell_new();
+
+ /* Start adding certs. First the link cert or auth1024 cert. */
+ if (conn_in_server_mode) {
+ tor_assert_nonfatal(own_link_cert);
+ add_x509_cert(certs_cell,
+ OR_CERT_TYPE_TLS_LINK, own_link_cert);
+ } else {
+ tor_assert(global_link_cert);
+ add_x509_cert(certs_cell,
+ OR_CERT_TYPE_AUTH_1024, global_link_cert);
+ }
+
+ /* Next the RSA->RSA ID cert */
+ add_x509_cert(certs_cell,
+ OR_CERT_TYPE_ID_1024, id_cert);
+
+ /* Next the Ed25519 certs */
+ add_ed25519_cert(certs_cell,
+ CERTTYPE_ED_ID_SIGN,
+ get_master_signing_key_cert());
+ if (conn_in_server_mode) {
+ tor_assert_nonfatal(conn->handshake_state->own_link_cert ||
+ certs_cell_ed25519_disabled_for_testing);
+ add_ed25519_cert(certs_cell,
+ CERTTYPE_ED_SIGN_LINK,
+ conn->handshake_state->own_link_cert);
+ } else {
+ add_ed25519_cert(certs_cell,
+ CERTTYPE_ED_SIGN_AUTH,
+ get_current_auth_key_cert());
+ }
+
+ /* And finally the crosscert. */
+ {
+ const uint8_t *crosscert=NULL;
+ size_t crosscert_len;
+ get_master_rsa_crosscert(&crosscert, &crosscert_len);
+ if (crosscert) {
+ add_certs_cell_cert_helper(certs_cell,
+ CERTTYPE_RSA1024_ID_EDID,
+ crosscert, crosscert_len);
+ }
+ }
+
+ /* We've added all the certs; make the cell. */
+ certs_cell->n_certs = certs_cell_getlen_certs(certs_cell);
+
+ ssize_t alloc_len = certs_cell_encoded_len(certs_cell);
+ tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX);
+ cell = var_cell_new(alloc_len);
+ cell->command = CELL_CERTS;
+ ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell);
+ tor_assert(enc_len > 0 && enc_len <= alloc_len);
+ cell->payload_len = enc_len;
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+ certs_cell_free(certs_cell);
+ tor_x509_cert_free(own_link_cert);
+
+ return 0;
+}
+
+#ifdef TOR_UNIT_TESTS
+int testing__connection_or_pretend_TLSSECRET_is_supported = 0;
+#else
+#define testing__connection_or_pretend_TLSSECRET_is_supported 0
+#endif
+
+/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that
+ * we can send and receive. */
+int
+authchallenge_type_is_supported(uint16_t challenge_type)
+{
+ switch (challenge_type) {
+ case AUTHTYPE_RSA_SHA256_TLSSECRET:
+#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS
+ return 1;
+#else
+ return testing__connection_or_pretend_TLSSECRET_is_supported;
+#endif
+ case AUTHTYPE_ED25519_SHA256_RFC5705:
+ return 1;
+ case AUTHTYPE_RSA_SHA256_RFC5705:
+ default:
+ return 0;
+ }
+}
+
+/** Return true iff <b>challenge_type_a</b> is one that we would rather
+ * use than <b>challenge_type_b</b>. */
+int
+authchallenge_type_is_better(uint16_t challenge_type_a,
+ uint16_t challenge_type_b)
+{
+ /* Any supported type is better than an unsupported one;
+ * all unsupported types are equally bad. */
+ if (!authchallenge_type_is_supported(challenge_type_a))
+ return 0;
+ if (!authchallenge_type_is_supported(challenge_type_b))
+ return 1;
+ /* It happens that types are superior in numerically ascending order.
+ * If that ever changes, this must change too. */
+ return (challenge_type_a > challenge_type_b);
+}
+
+/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0
+ * on success, -1 on failure. */
+int
+connection_or_send_auth_challenge_cell(or_connection_t *conn)
+{
+ var_cell_t *cell = NULL;
+ int r = -1;
+ tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
+
+ if (! conn->handshake_state)
+ return -1;
+
+ auth_challenge_cell_t *ac = auth_challenge_cell_new();
+
+ tor_assert(sizeof(ac->challenge) == 32);
+ crypto_rand((char*)ac->challenge, sizeof(ac->challenge));
+
+ if (authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET))
+ auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
+ /* Disabled, because everything that supports this method also supports
+ * the much-superior ED25519_SHA256_RFC5705 */
+ /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */
+ if (authchallenge_type_is_supported(AUTHTYPE_ED25519_SHA256_RFC5705))
+ auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705);
+ auth_challenge_cell_set_n_methods(ac,
+ auth_challenge_cell_getlen_methods(ac));
+
+ cell = var_cell_new(auth_challenge_cell_encoded_len(ac));
+ ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len,
+ ac);
+ if (len != cell->payload_len) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Encoded auth challenge cell length not as expected");
+ goto done;
+ /* LCOV_EXCL_STOP */
+ }
+ cell->command = CELL_AUTH_CHALLENGE;
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ r = 0;
+
+ done:
+ var_cell_free(cell);
+ auth_challenge_cell_free(ac);
+
+ return r;
+}
+
+/** Compute the main body of an AUTHENTICATE cell that a client can use
+ * to authenticate itself on a v3 handshake for <b>conn</b>. Return it
+ * in a var_cell_t.
+ *
+ * If <b>server</b> is true, only calculate the first
+ * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
+ * determined by the rest of the handshake, and which match the provided value
+ * exactly.
+ *
+ * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the
+ * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything
+ * that should be signed), but don't actually sign it.
+ *
+ * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the
+ * entire authenticator, signed with <b>signing_key</b>.
+ *
+ * Return the length of the cell body on success, and -1 on failure.
+ */
+var_cell_t *
+connection_or_compute_authenticate_cell_body(or_connection_t *conn,
+ const int authtype,
+ crypto_pk_t *signing_key,
+ const ed25519_keypair_t *ed_signing_key,
+ int server)
+{
+ auth1_t *auth = NULL;
+ auth_ctx_t *ctx = auth_ctx_new();
+ var_cell_t *result = NULL;
+ int old_tlssecrets_algorithm = 0;
+ const char *authtype_str = NULL;
+
+ int is_ed = 0;
+
+ /* assert state is reasonable XXXX */
+ switch (authtype) {
+ case AUTHTYPE_RSA_SHA256_TLSSECRET:
+ authtype_str = "AUTH0001";
+ old_tlssecrets_algorithm = 1;
+ break;
+ case AUTHTYPE_RSA_SHA256_RFC5705:
+ authtype_str = "AUTH0002";
+ break;
+ case AUTHTYPE_ED25519_SHA256_RFC5705:
+ authtype_str = "AUTH0003";
+ is_ed = 1;
+ break;
+ default:
+ tor_assert(0);
+ break;
+ }
+
+ auth = auth1_new();
+ ctx->is_ed = is_ed;
+
+ /* Type: 8 bytes. */
+ memcpy(auth1_getarray_type(auth), authtype_str, 8);
+
+ {
+ const tor_x509_cert_t *id_cert=NULL;
+ const common_digests_t *my_digests, *their_digests;
+ const uint8_t *my_id, *their_id, *client_id, *server_id;
+ if (tor_tls_get_my_certs(server, NULL, &id_cert))
+ goto err;
+ my_digests = tor_x509_cert_get_id_digests(id_cert);
+ their_digests =
+ tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert);
+ tor_assert(my_digests);
+ tor_assert(their_digests);
+ my_id = (uint8_t*)my_digests->d[DIGEST_SHA256];
+ their_id = (uint8_t*)their_digests->d[DIGEST_SHA256];
+
+ client_id = server ? their_id : my_id;
+ server_id = server ? my_id : their_id;
+
+ /* Client ID digest: 32 octets. */
+ memcpy(auth->cid, client_id, 32);
+
+ /* Server ID digest: 32 octets. */
+ memcpy(auth->sid, server_id, 32);
+ }
+
+ if (is_ed) {
+ const ed25519_public_key_t *my_ed_id, *their_ed_id;
+ if (!conn->handshake_state->certs->ed_id_sign) {
+ log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer.");
+ goto err;
+ }
+ my_ed_id = get_master_identity_key();
+ their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key;
+
+ const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey;
+ const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey;
+
+ memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN);
+ memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN);
+ }
+
+ {
+ crypto_digest_t *server_d, *client_d;
+ if (server) {
+ server_d = conn->handshake_state->digest_sent;
+ client_d = conn->handshake_state->digest_received;
+ } else {
+ client_d = conn->handshake_state->digest_sent;
+ server_d = conn->handshake_state->digest_received;
+ }
+
+ /* Server log digest : 32 octets */
+ crypto_digest_get_digest(server_d, (char*)auth->slog, 32);
+
+ /* Client log digest : 32 octets */
+ crypto_digest_get_digest(client_d, (char*)auth->clog, 32);
+ }
+
+ {
+ /* Digest of cert used on TLS link : 32 octets. */
+ tor_x509_cert_t *cert = NULL;
+ if (server) {
+ cert = tor_tls_get_own_cert(conn->tls);
+ } else {
+ cert = tor_tls_get_peer_cert(conn->tls);
+ }
+ if (!cert) {
+ log_warn(LD_OR, "Unable to find cert when making %s data.",
+ authtype_str);
+ goto err;
+ }
+
+ memcpy(auth->scert,
+ tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
+
+ tor_x509_cert_free(cert);
+ }
+
+ /* HMAC of clientrandom and serverrandom using master key : 32 octets */
+ if (old_tlssecrets_algorithm) {
+ if (tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Somebody asked us for an older TLS "
+ "authentication method (AUTHTYPE_RSA_SHA256_TLSSECRET) "
+ "which we don't support.");
+ }
+ } else {
+ char label[128];
+ tor_snprintf(label, sizeof(label),
+ "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str);
+ int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets,
+ auth->cid, sizeof(auth->cid),
+ label);
+ if (r < 0) {
+ if (r != -2)
+ log_warn(LD_BUG, "TLS key export failed for unknown reason.");
+ // If r == -2, this was openssl bug 7712.
+ goto err;
+ }
+ }
+
+ /* 8 octets were reserved for the current time, but we're trying to get out
+ * of the habit of sending time around willynilly. Fortunately, nothing
+ * checks it. That's followed by 16 bytes of nonce. */
+ crypto_rand((char*)auth->rand, 24);
+
+ ssize_t maxlen = auth1_encoded_len(auth, ctx);
+ if (ed_signing_key && is_ed) {
+ maxlen += ED25519_SIG_LEN;
+ } else if (signing_key && !is_ed) {
+ maxlen += crypto_pk_keysize(signing_key);
+ }
+
+ const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */
+ result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen);
+ uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN;
+ const size_t outlen = maxlen;
+ ssize_t len;
+
+ result->command = CELL_AUTHENTICATE;
+ set_uint16(result->payload, htons(authtype));
+
+ if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data.");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+
+ if (server) {
+ auth1_t *tmp = NULL;
+ ssize_t len2 = auth1_parse(&tmp, out, len, ctx);
+ if (!tmp) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that "
+ "we just encoded");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ result->payload_len = (tmp->end_of_signed - result->payload);
+
+ auth1_free(tmp);
+ if (len2 != len) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data.");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ goto done;
+ }
+
+ if (ed_signing_key && is_ed) {
+ ed25519_signature_t sig;
+ if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to sign ed25519 authentication data");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ auth1_setlen_sig(auth, ED25519_SIG_LEN);
+ memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN);
+
+ } else if (signing_key && !is_ed) {
+ auth1_setlen_sig(auth, crypto_pk_keysize(signing_key));
+
+ char d[32];
+ crypto_digest256(d, (char*)out, len, DIGEST_SHA256);
+ int siglen = crypto_pk_private_sign(signing_key,
+ (char*)auth1_getarray_sig(auth),
+ auth1_getlen_sig(auth),
+ d, 32);
+ if (siglen < 0) {
+ log_warn(LD_OR, "Unable to sign AUTH1 data.");
+ goto err;
+ }
+
+ auth1_setlen_sig(auth, siglen);
+ }
+
+ len = auth1_encode(out, outlen, auth, ctx);
+ if (len < 0) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to encode signed AUTH1 data.");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len);
+ result->payload_len = len + AUTH_CELL_HEADER_LEN;
+ set_uint16(result->payload+2, htons(len));
+
+ goto done;
+
+ err:
+ var_cell_free(result);
+ result = NULL;
+ done:
+ auth1_free(auth);
+ auth_ctx_free(ctx);
+ return result;
+}
+
+/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
+ * success, -1 on failure */
+MOCK_IMPL(int,
+connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype))
+{
+ var_cell_t *cell;
+ crypto_pk_t *pk = tor_tls_get_my_client_auth_key();
+ /* XXXX make sure we're actually supposed to send this! */
+
+ if (!pk) {
+ log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key");
+ return -1;
+ }
+ if (! authchallenge_type_is_supported(authtype)) {
+ log_warn(LD_BUG, "Tried to send authenticate cell with unknown "
+ "authentication type %d", authtype);
+ return -1;
+ }
+
+ cell = connection_or_compute_authenticate_cell_body(conn,
+ authtype,
+ pk,
+ get_current_auth_keypair(),
+ 0 /* not server */);
+ if (! cell) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!");
+ return -1;
+ }
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+
+ return 0;
+}
diff --git a/src/feature/relay/relay_handshake.h b/src/feature/relay/relay_handshake.h
new file mode 100644
index 0000000000..99a658cbcc
--- /dev/null
+++ b/src/feature/relay/relay_handshake.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_handshake.h
+ * @brief Header for feature/relay/relay_handshake.c
+ **/
+
+#ifndef TOR_CORE_OR_RELAY_HANDSHAKE_H
+#define TOR_CORE_OR_RELAY_HANDSHAKE_H
+
+#ifdef HAVE_MODULE_RELAY
+struct ed25519_keypair_t;
+
+int connection_or_send_certs_cell(or_connection_t *conn);
+int connection_or_send_auth_challenge_cell(or_connection_t *conn);
+
+var_cell_t *connection_or_compute_authenticate_cell_body(
+ or_connection_t *conn,
+ const int authtype,
+ crypto_pk_t *signing_key,
+ const struct ed25519_keypair_t *ed_signing_key,
+ int server);
+
+int authchallenge_type_is_supported(uint16_t challenge_type);
+int authchallenge_type_is_better(uint16_t challenge_type_a,
+ uint16_t challenge_type_b);
+
+MOCK_DECL(int,connection_or_send_authenticate_cell,
+ (or_connection_t *conn, int type));
+
+#ifdef TOR_UNIT_TESTS
+extern int certs_cell_ed25519_disabled_for_testing;
+#endif
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+static inline int
+connection_or_send_certs_cell(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+static inline int
+connection_or_send_auth_challenge_cell(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+static inline var_cell_t *
+connection_or_compute_authenticate_cell_body(
+ or_connection_t *conn,
+ const int authtype,
+ crypto_pk_t *signing_key,
+ const struct ed25519_keypair_t *ed_signing_key,
+ int server)
+{
+ (void)conn;
+ (void)authtype;
+ (void)signing_key;
+ (void)ed_signing_key;
+ (void)server;
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+
+#define authchallenge_type_is_supported(t) (0)
+#define authchallenge_type_is_better(a, b) (0)
+
+static inline int
+connection_or_send_authenticate_cell(or_connection_t *conn, int type)
+{
+ (void)conn;
+ (void)type;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+#ifdef TOR_UNIT_TESTS
+extern int certs_cell_ed25519_disabled_for_testing;
+#endif
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
+#endif /* !defined(TOR_CORE_OR_RELAY_HANDSHAKE_H) */
diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c
index b751323e0d..08ad110cf6 100644
--- a/src/feature/relay/relay_periodic.c
+++ b/src/feature/relay/relay_periodic.c
@@ -203,29 +203,34 @@ reachability_warnings_callback(time_t now, const or_options_t *options)
const routerinfo_t *me = router_get_my_routerinfo();
if (me && !check_whether_orport_reachable(options)) {
char *address = tor_dup_ip(me->addr);
- log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
- "its ORPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->or_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED ORADDRESS=%s:%d",
- address, me->or_port);
- tor_free(address);
+ if (address) {
+ log_warn(LD_CONFIG,
+ "Your server (%s:%d) has not managed to confirm that "
+ "its ORPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address, me->or_port);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED ORADDRESS=%s:%d",
+ address, me->or_port);
+ tor_free(address);
+ }
}
if (me && !check_whether_dirport_reachable(options)) {
char *address = tor_dup_ip(me->addr);
- log_warn(LD_CONFIG,
- "Your server (%s:%d) has not managed to confirm that its "
- "DirPort is reachable. Relays do not publish descriptors "
- "until their ORPort and DirPort are reachable. Please check "
- "your firewalls, ports, address, /etc/hosts file, etc.",
- address, me->dir_port);
- control_event_server_status(LOG_WARN,
- "REACHABILITY_FAILED DIRADDRESS=%s:%d",
- address, me->dir_port);
- tor_free(address);
+ if (address) {
+ log_warn(LD_CONFIG,
+ "Your server (%s:%d) has not managed to confirm that its "
+ "DirPort is reachable. Relays do not publish descriptors "
+ "until their ORPort and DirPort are reachable. Please check "
+ "your firewalls, ports, address, /etc/hosts file, etc.",
+ address, me->dir_port);
+ control_event_server_status(LOG_WARN,
+ "REACHABILITY_FAILED DIRADDRESS=%s:%d",
+ address, me->dir_port);
+ tor_free(address);
+ }
}
}
diff --git a/src/feature/relay/relay_stub.c b/src/feature/relay/relay_stub.c
index 42e08fcb6c..283aaf6e49 100644
--- a/src/feature/relay/relay_stub.c
+++ b/src/feature/relay/relay_stub.c
@@ -15,6 +15,7 @@
const struct subsys_fns_t sys_relay = {
.name = "relay",
+ SUBSYS_DECLARE_LOCATION(),
.supported = false,
.level = RELAY_SUBSYS_LEVEL,
};
diff --git a/src/feature/relay/relay_sys.c b/src/feature/relay/relay_sys.c
index 34489cf5aa..2e90740925 100644
--- a/src/feature/relay/relay_sys.c
+++ b/src/feature/relay/relay_sys.c
@@ -41,6 +41,7 @@ subsys_relay_shutdown(void)
const struct subsys_fns_t sys_relay = {
.name = "relay",
+ SUBSYS_DECLARE_LOCATION(),
.supported = true,
.level = RELAY_SUBSYS_LEVEL,
.initialize = subsys_relay_initialize,
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index e24e499971..34d8163c36 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -8,6 +8,7 @@
#include "core/or/or.h"
#include "app/config/config.h"
+#include "app/config/resolve_addr.h"
#include "app/config/statefile.h"
#include "app/main/main.h"
#include "core/mainloop/connection.h"
@@ -36,6 +37,7 @@
#include "feature/nodelist/torcert.h"
#include "feature/relay/dns.h"
#include "feature/relay/relay_config.h"
+#include "feature/relay/relay_find_addr.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
#include "feature/relay/routermode.h"
@@ -748,8 +750,8 @@ v3_authority_check_key_expiry(void)
}
/** Get the lifetime of an onion key in days. This value is defined by the
- * network consesus parameter "onion-key-rotation-days". Always returns a value
- * between <b>MIN_ONION_KEY_LIFETIME_DAYS</b> and
+ * network consensus parameter "onion-key-rotation-days". Always returns a
+ * value between <b>MIN_ONION_KEY_LIFETIME_DAYS</b> and
* <b>MAX_ONION_KEY_LIFETIME_DAYS</b>.
*/
static int
@@ -763,7 +765,7 @@ get_onion_key_rotation_days_(void)
}
/** Get the current lifetime of an onion key in seconds. This value is defined
- * by the network consesus parameter "onion-key-rotation-days", but the value
+ * by the network consensus parameter "onion-key-rotation-days", but the value
* is converted to seconds.
*/
int
@@ -773,7 +775,7 @@ get_onion_key_lifetime(void)
}
/** Get the grace period of an onion key in seconds. This value is defined by
- * the network consesus parameter "onion-key-grace-period-days", but the value
+ * the network consensus parameter "onion-key-grace-period-days", but the value
* is converted to seconds.
*/
int
@@ -1446,6 +1448,68 @@ router_get_advertised_or_port_by_af(const or_options_t *options,
return port;
}
+/** As router_get_advertised_or_port(), but returns the IPv6 address and
+ * port in ipv6_ap_out, which must not be NULL. Returns a null address and
+ * zero port, if no ORPort is found. */
+void
+router_get_advertised_ipv6_or_ap(const or_options_t *options,
+ tor_addr_port_t *ipv6_ap_out)
+{
+ /* Bug in calling function, we can't return a sensible result, and it
+ * shouldn't use the NULL pointer once we return. */
+ tor_assert(ipv6_ap_out);
+
+ /* If there is no valid IPv6 ORPort, return a null address and port. */
+ tor_addr_make_null(&ipv6_ap_out->addr, AF_INET6);
+ ipv6_ap_out->port = 0;
+
+ const tor_addr_t *addr = get_first_advertised_addr_by_type_af(
+ CONN_TYPE_OR_LISTENER,
+ AF_INET6);
+ const uint16_t port = router_get_advertised_or_port_by_af(
+ options,
+ AF_INET6);
+
+ if (!addr || port == 0) {
+ log_debug(LD_CONFIG, "There is no advertised IPv6 ORPort.");
+ return;
+ }
+
+ /* If the relay is configured using the default authorities, disallow
+ * internal IPs. Otherwise, allow them. For IPv4 ORPorts and DirPorts,
+ * this check is done in resolve_my_address(). See #33681. */
+ const int default_auth = using_default_dir_authorities(options);
+ if (tor_addr_is_internal(addr, 0) && default_auth) {
+ log_warn(LD_CONFIG,
+ "Unable to use configured IPv6 ORPort \"%s\" in a "
+ "descriptor. Skipping it. "
+ "Try specifying a globally reachable address explicitly.",
+ fmt_addrport(addr, port));
+ return;
+ }
+
+ tor_addr_copy(&ipv6_ap_out->addr, addr);
+ ipv6_ap_out->port = port;
+}
+
+/** Returns true if this router has an advertised IPv6 ORPort. */
+bool
+router_has_advertised_ipv6_orport(const or_options_t *options)
+{
+ tor_addr_port_t ipv6_ap;
+ router_get_advertised_ipv6_or_ap(options, &ipv6_ap);
+ return tor_addr_port_is_valid_ap(&ipv6_ap, 0);
+}
+
+/** Returns true if this router has an advertised IPv6 ORPort. */
+MOCK_IMPL(bool,
+router_can_extend_over_ipv6,(const or_options_t *options))
+{
+ /* We might add some extra checks here, such as ExtendAllowIPv6Addresses
+ * from ticket 33818. */
+ return router_has_advertised_ipv6_orport(options);
+}
+
/** Return the port that we should advertise as our DirPort;
* this is one of three possibilities:
* The one that is passed as <b>dirport</b> if the DirPort option is 0, or
@@ -1701,41 +1765,6 @@ router_get_descriptor_gen_reason(void)
return desc_gen_reason;
}
-static int router_guess_address_from_dir_headers(uint32_t *guess);
-
-/** Make a current best guess at our address, either because
- * it's configured in torrc, or because we've learned it from
- * dirserver headers. Place the answer in *<b>addr</b> and return
- * 0 on success, else return -1 if we have no guess.
- *
- * If <b>cache_only</b> is true, just return any cached answers, and
- * don't try to get any new answers.
- */
-MOCK_IMPL(int,
-router_pick_published_address,(const or_options_t *options, uint32_t *addr,
- int cache_only))
-{
- /* First, check the cached output from resolve_my_address(). */
- *addr = get_last_resolved_addr();
- if (*addr)
- return 0;
-
- /* Second, consider doing a resolve attempt right here. */
- if (!cache_only) {
- if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) {
- log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
- return 0;
- }
- }
-
- /* Third, check the cached output from router_new_address_suggestion(). */
- if (router_guess_address_from_dir_headers(addr) >= 0)
- return 0;
-
- /* We have no useful cached answers. Return failure. */
- return -1;
-}
-
/* Like router_check_descriptor_address_consistency, but specifically for the
* ORPort or DirPort.
* listener_type is either CONN_TYPE_OR_LISTENER or CONN_TYPE_DIR_LISTENER. */
@@ -1990,34 +2019,11 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
sizeof(curve25519_public_key_t));
/* For now, at most one IPv6 or-address is being advertised. */
- {
- const port_cfg_t *ipv6_orport = NULL;
- SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) {
- if (p->type == CONN_TYPE_OR_LISTENER &&
- ! p->server_cfg.no_advertise &&
- ! p->server_cfg.bind_ipv4_only &&
- tor_addr_family(&p->addr) == AF_INET6) {
- /* Like IPv4, if the relay is configured using the default
- * authorities, disallow internal IPs. Otherwise, allow them. */
- const int default_auth = using_default_dir_authorities(options);
- if (! tor_addr_is_internal(&p->addr, 0) || ! default_auth) {
- ipv6_orport = p;
- break;
- } else {
- char addrbuf[TOR_ADDR_BUF_LEN];
- log_warn(LD_CONFIG,
- "Unable to use configured IPv6 address \"%s\" in a "
- "descriptor. Skipping it. "
- "Try specifying a globally reachable address explicitly.",
- tor_addr_to_str(addrbuf, &p->addr, sizeof(addrbuf), 1));
- }
- }
- } SMARTLIST_FOREACH_END(p);
- if (ipv6_orport) {
- tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr);
- ri->ipv6_orport = ipv6_orport->port;
- }
- }
+ tor_addr_port_t ipv6_orport;
+ router_get_advertised_ipv6_or_ap(options, &ipv6_orport);
+ /* If there is no valid IPv6 ORPort, the address and port are null. */
+ tor_addr_copy(&ri->ipv6_addr, &ipv6_orport.addr);
+ ri->ipv6_orport = ipv6_orport.port;
ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key());
if (BUG(crypto_pk_get_digest(ri->identity_pkey,
@@ -2484,7 +2490,7 @@ check_descriptor_bandwidth_changed(time_t now)
/** Note at log level severity that our best guess of address has changed from
* <b>prev</b> to <b>cur</b>. */
-static void
+void
log_addr_has_changed(int severity,
const tor_addr_t *prev,
const tor_addr_t *cur,
@@ -2556,86 +2562,6 @@ check_descriptor_ipaddress_changed(time_t now)
tor_free(hostname);
}
-/** The most recently guessed value of our IP address, based on directory
- * headers. */
-static tor_addr_t last_guessed_ip = TOR_ADDR_NULL;
-
-/** A directory server <b>d_conn</b> told us our IP address is
- * <b>suggestion</b>.
- * If this address is different from the one we think we are now, and
- * if our computer doesn't actually know its IP address, then switch. */
-void
-router_new_address_suggestion(const char *suggestion,
- const dir_connection_t *d_conn)
-{
- tor_addr_t addr;
- uint32_t cur = 0; /* Current IPv4 address. */
- const or_options_t *options = get_options();
-
- /* first, learn what the IP address actually is */
- if (tor_addr_parse(&addr, suggestion) == -1) {
- log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.",
- escaped(suggestion));
- return;
- }
-
- log_debug(LD_DIR, "Got X-Your-Address-Is: %s.", suggestion);
-
- if (!server_mode(options)) {
- tor_addr_copy(&last_guessed_ip, &addr);
- return;
- }
-
- /* XXXX ipv6 */
- cur = get_last_resolved_addr();
- if (cur ||
- resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) {
- /* We're all set -- we already know our address. Great. */
- tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we
- need it later */
- return;
- }
- if (tor_addr_is_internal(&addr, 0)) {
- /* Don't believe anybody who says our IP is, say, 127.0.0.1. */
- return;
- }
- if (tor_addr_eq(&d_conn->base_.addr, &addr)) {
- /* Don't believe anybody who says our IP is their IP. */
- log_debug(LD_DIR, "A directory server told us our IP address is %s, "
- "but they are just reporting their own IP address. Ignoring.",
- suggestion);
- return;
- }
-
- /* Okay. We can't resolve our own address, and X-Your-Address-Is is giving
- * us an answer different from what we had the last time we managed to
- * resolve it. */
- if (!tor_addr_eq(&last_guessed_ip, &addr)) {
- control_event_server_status(LOG_NOTICE,
- "EXTERNAL_ADDRESS ADDRESS=%s METHOD=DIRSERV",
- suggestion);
- log_addr_has_changed(LOG_NOTICE, &last_guessed_ip, &addr,
- d_conn->base_.address);
- ip_address_changed(0);
- tor_addr_copy(&last_guessed_ip, &addr); /* router_rebuild_descriptor()
- will fetch it */
- }
-}
-
-/** We failed to resolve our address locally, but we'd like to build
- * a descriptor and publish / test reachability. If we have a guess
- * about our address based on directory headers, answer it and return
- * 0; else return -1. */
-static int
-router_guess_address_from_dir_headers(uint32_t *guess)
-{
- if (!tor_addr_is_null(&last_guessed_ip)) {
- *guess = tor_addr_to_ipv4h(&last_guessed_ip);
- return 0;
- }
- return -1;
-}
-
/** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short
* string describing the version of Tor and the operating system we're
* currently running on.
@@ -2858,6 +2784,9 @@ router_dump_router_to_string(routerinfo_t *router,
}
address = tor_dup_ip(router->addr);
+ if (!address)
+ goto err;
+
chunks = smartlist_new();
/* Generate the easy portion of the router descriptor. */
diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h
index 782609d8ab..50790a73dd 100644
--- a/src/feature/relay/router.h
+++ b/src/feature/relay/router.h
@@ -66,6 +66,10 @@ int init_keys_client(void);
uint16_t router_get_active_listener_port_by_type_af(int listener_type,
sa_family_t family);
uint16_t router_get_advertised_or_port(const or_options_t *options);
+void router_get_advertised_ipv6_or_ap(const or_options_t *options,
+ tor_addr_port_t *ipv6_ap_out);
+bool router_has_advertised_ipv6_orport(const or_options_t *options);
+MOCK_DECL(bool, router_can_extend_over_ipv6,(const or_options_t *options));
uint16_t router_get_advertised_or_port_by_af(const or_options_t *options,
sa_family_t family);
uint16_t router_get_advertised_dir_port(const or_options_t *options,
@@ -83,8 +87,6 @@ void mark_my_descriptor_dirty(const char *reason);
void check_descriptor_bandwidth_changed(time_t now);
void check_descriptor_ipaddress_changed(time_t now);
int router_has_bandwidth_to_be_dirserver(const or_options_t *options);
-void router_new_address_suggestion(const char *suggestion,
- const dir_connection_t *d_conn);
int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port);
MOCK_DECL(int, router_my_exit_policy_is_reject_star,(void));
MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
@@ -96,9 +98,6 @@ int router_digest_is_me(const char *digest);
const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
-MOCK_DECL(int,router_pick_published_address,(const or_options_t *options,
- uint32_t *addr,
- int cache_only));
int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
@@ -116,8 +115,10 @@ int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo,
const char *routerinfo_err_to_string(int err);
int routerinfo_err_is_transient(int err);
+void log_addr_has_changed(int severity, const tor_addr_t *prev,
+ const tor_addr_t *cur, const char *source);
+
void router_reset_warnings(void);
-void router_reset_reachability(void);
void router_free_all(void);
#ifdef ROUTER_PRIVATE
diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h
index c2475f195f..1fb5d724e9 100644
--- a/src/feature/relay/routerkeys.h
+++ b/src/feature/relay/routerkeys.h
@@ -11,6 +11,8 @@
#include "lib/crypt_ops/crypto_ed25519.h"
+#ifdef HAVE_MODULE_RELAY
+
const ed25519_public_key_t *get_master_identity_key(void);
MOCK_DECL(const ed25519_keypair_t *, get_master_signing_keypair,(void));
MOCK_DECL(const struct tor_cert_st *, get_master_signing_key_cert,(void));
@@ -24,6 +26,7 @@ void get_master_rsa_crosscert(const uint8_t **cert_out,
int router_ed25519_id_is_me(const ed25519_public_key_t *id);
+/* These are only used by router.c */
struct tor_cert_st *make_ntor_onion_key_crosscert(
const curve25519_keypair_t *onion_key,
const ed25519_public_key_t *master_id_key,
@@ -42,6 +45,85 @@ int generate_ed_link_cert(const or_options_t *options, time_t now, int force);
void routerkeys_free_all(void);
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#define router_ed25519_id_is_me(id) \
+ ((void)(id), 0)
+
+static inline void *
+relay_key_is_unavailable_(void)
+{
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+#define relay_key_is_unavailable(type) \
+ ((type)(relay_key_is_unavailable_()))
+
+// Many of these can be removed once relay_handshake.c is relay-only.
+#define get_current_auth_keypair() \
+ relay_key_is_unavailable(const ed25519_keypair_t *)
+#define get_master_signing_keypair() \
+ relay_key_is_unavailable(const ed25519_keypair_t *)
+#define get_current_link_cert_cert() \
+ relay_key_is_unavailable(const struct tor_cert_st *)
+#define get_current_auth_key_cert() \
+ relay_key_is_unavailable(const struct tor_cert_st *)
+#define get_master_signing_key_cert() \
+ relay_key_is_unavailable(const struct tor_cert_st *)
+#define get_master_rsa_crosscert(cert_out, size_out) \
+ STMT_BEGIN \
+ tor_assert_nonfatal_unreached(); \
+ *(cert_out) = NULL; \
+ *(size_out) = 0; \
+ STMT_END
+#define get_master_identity_key() \
+ relay_key_is_unavailable(const ed25519_public_key_t *)
+
+#define generate_ed_link_cert(options, now, force) \
+ ((void)(options), (void)(now), (void)(force), 0)
+#define should_make_new_ed_keys(options, now) \
+ ((void)(options), (void)(now), 0)
+
+// These can get removed once router.c becomes relay-only.
+static inline struct tor_cert_st *
+make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ time_t now, time_t lifetime,
+ int *sign_out)
+{
+ (void)onion_key;
+ (void)master_id_key;
+ (void)now;
+ (void)lifetime;
+ *sign_out = 0;
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+static inline uint8_t *
+make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ const crypto_pk_t *rsa_id_key,
+ int *len_out)
+{
+ (void)onion_key;
+ (void)master_id_key;
+ (void)rsa_id_key;
+ *len_out = 0;
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+
+/* This calls is used outside of relay mode, but only to implement
+ * CMD_KEY_EXPIRATION */
+#define log_cert_expiration() \
+ (puts("Not available: Tor has been compiled without relay support"), 0)
+/* This calls is used outside of relay mode, but only to implement
+ * CMD_KEYGEN. */
+#define load_ed_keys(x,y) \
+ (puts("Not available: Tor has been compiled without relay support"), 0)
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
#ifdef TOR_UNIT_TESTS
const ed25519_keypair_t *get_master_identity_keypair(void);
void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key);
diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c
index 29febdee82..18fe25b989 100644
--- a/src/feature/relay/selftest.c
+++ b/src/feature/relay/selftest.c
@@ -8,7 +8,7 @@
* \file selftest.c
* \brief Relay self-testing
*
- * Relays need to make sure that their own ports are reasonable, and estimate
+ * Relays need to make sure that their own ports are reachable, and estimate
* their own bandwidth, before publishing.
*/
@@ -213,6 +213,44 @@ router_do_reachability_checks(int test_or, int test_dir)
}
}
+/** We've decided to start our reachability testing. If all
+ * is set, log this to the user. Return 1 if we did, or 0 if
+ * we chose not to log anything. */
+int
+inform_testing_reachability(void)
+{
+ char dirbuf[128];
+ char *address;
+ const routerinfo_t *me = router_get_my_routerinfo();
+ if (!me)
+ return 0;
+
+ address = tor_dup_ip(me->addr);
+ if (!address)
+ return 0;
+
+ control_event_server_status(LOG_NOTICE,
+ "CHECKING_REACHABILITY ORADDRESS=%s:%d",
+ address, me->or_port);
+ if (me->dir_port) {
+ tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d",
+ address, me->dir_port);
+ control_event_server_status(LOG_NOTICE,
+ "CHECKING_REACHABILITY DIRADDRESS=%s:%d",
+ address, me->dir_port);
+ }
+ log_notice(LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... "
+ "(this may take up to %d minutes -- look for log "
+ "messages indicating success)",
+ address, me->or_port,
+ me->dir_port ? dirbuf : "",
+ me->dir_port ? "are" : "is",
+ TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60);
+
+ tor_free(address);
+ return 1;
+}
+
/** Annotate that we found our ORPort reachable. */
void
router_orport_found_reachable(void)
@@ -221,6 +259,10 @@ router_orport_found_reachable(void)
const or_options_t *options = get_options();
if (!can_reach_or_port && me) {
char *address = tor_dup_ip(me->addr);
+
+ if (!address)
+ return;
+
log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
"the outside. Excellent.%s",
options->PublishServerDescriptor_ != NO_DIRINFO
@@ -248,6 +290,10 @@ router_dirport_found_reachable(void)
const or_options_t *options = get_options();
if (!can_reach_dir_port && me) {
char *address = tor_dup_ip(me->addr);
+
+ if (!address)
+ return;
+
log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable "
"from the outside. Excellent.%s",
options->PublishServerDescriptor_ != NO_DIRINFO
diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h
index 94f305f203..f5babc95da 100644
--- a/src/feature/relay/selftest.h
+++ b/src/feature/relay/selftest.h
@@ -12,13 +12,57 @@
#ifndef TOR_SELFTEST_H
#define TOR_SELFTEST_H
+#ifdef HAVE_MODULE_RELAY
+
struct or_options_t;
int check_whether_orport_reachable(const struct or_options_t *options);
int check_whether_dirport_reachable(const struct or_options_t *options);
void router_do_reachability_checks(int test_or, int test_dir);
+void router_perform_bandwidth_test(int num_circs, time_t now);
+int inform_testing_reachability(void);
+
void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
-void router_perform_bandwidth_test(int num_circs, time_t now);
+
+void router_reset_reachability(void);
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#define check_whether_orport_reachable(opts) \
+ ((void)(opts), 0)
+#define check_whether_dirport_reachable(opts) \
+ ((void)(opts), 0)
+
+static inline void
+router_do_reachability_checks(int test_or, int test_dir)
+{
+ (void)test_or;
+ (void)test_dir;
+ tor_assert_nonfatal_unreached();
+}
+static inline void
+router_perform_bandwidth_test(int num_circs, time_t now)
+{
+ (void)num_circs;
+ (void)now;
+ tor_assert_nonfatal_unreached();
+}
+static inline int
+inform_testing_reachability(void)
+{
+ tor_assert_nonfatal_unreached();
+ return 0;
+}
+
+#define router_orport_found_reachable() \
+ STMT_NIL
+#define router_dirport_found_reachable() \
+ STMT_NIL
+
+#define router_reset_reachability() \
+ STMT_NIL
+
+#endif /* defined(HAVE_MODULE_RELAY) */
#endif /* !defined(TOR_SELFTEST_H) */