relay: Split out relay-only circuit building

Move the relay-only circuit building functions into a new file.

Part of 33633.
This commit is contained in:
teor 2020-03-17 17:18:40 +10:00
parent fdba6ff0c0
commit 81687f5bc9
8 changed files with 338 additions and 233 deletions

View File

@ -19,7 +19,6 @@
**/
#include "core/or/or.h"
#include "core/or/channel.h"
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/connection_or.h"
#include "app/config/config.h"
@ -27,6 +26,7 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "core/or/onion.h"
#include "feature/relay/circuitbuild_relay.h"
#include "feature/relay/onion_queue.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"

View File

@ -21,8 +21,7 @@
* cells arrive, the client will invoke circuit_send_next_onion_skin() to send
* CREATE or RELAY_EXTEND cells.
*
* On the server side, this module also handles the logic of responding to
* RELAY_EXTEND requests, using circuit_extend().
* The server side is handled in feature/relay/circuitbuild_relay.c.
**/
#define CIRCUITBUILD_PRIVATE
@ -35,7 +34,6 @@
#include "core/crypto/onion_crypto.h"
#include "core/crypto/onion_fast.h"
#include "core/crypto/onion_tap.h"
#include "core/crypto/relay_crypto.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
#include "core/or/channel.h"
@ -84,13 +82,6 @@
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
uint16_t port,
const char *id_digest,
const ed25519_public_key_t *ed_id);
static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
static int circuit_send_first_onion_skin(origin_circuit_t *circ);
static int circuit_build_no_more_hops(origin_circuit_t *circ);
static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
@ -104,10 +95,10 @@ static const node_t *choose_good_middle_server(uint8_t purpose,
* and then calls command_setup_channel() to give it the right
* callbacks.
*/
static channel_t *
channel_t *
channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port,
const char *id_digest,
const ed25519_public_key_t *ed_id)
const struct ed25519_public_key_t *ed_id)
{
channel_t *chan;
@ -707,8 +698,9 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits)
* gave us via an EXTEND cell, so we shouldn't worry if we don't understand
* it. Return -1 if we failed to find a suitable circid, else return 0.
*/
static int
circuit_deliver_create_cell(circuit_t *circ, const create_cell_t *create_cell,
int
circuit_deliver_create_cell(circuit_t *circ,
const struct create_cell_t *create_cell,
int relayed)
{
cell_t cell;
@ -1166,164 +1158,6 @@ circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle)
}
}
/** 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(cell_t *cell, circuit_t *circ)
{
channel_t *n_chan;
relay_header_t rh;
extend_cell_t ec;
const char *msg = NULL;
int should_launch = 0;
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;
}
if (!server_mode(get_options())) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Got an extend cell, but running as a client. Closing.");
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 (!ec.orport_ipv4.port || tor_addr_is_null(&ec.orport_ipv4.addr)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend to zero destination port or addr.");
return -1;
}
if (tor_addr_is_internal(&ec.orport_ipv4.addr, 0) &&
!get_options()->ExtendAllowPrivateAddresses) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend to a private address");
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);
}
}
/* 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,
TO_OR_CIRCUIT(circ)->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,
&TO_OR_CIRCUIT(circ)->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;
}
n_chan = channel_get_for_extend((const char*)ec.node_id,
&ec.ed_pubkey,
&ec.orport_ipv4.addr,
&msg,
&should_launch);
if (!n_chan) {
log_debug(LD_CIRC|LD_OR,"Next router (%s): %s",
fmt_addrport(&ec.orport_ipv4.addr,ec.orport_ipv4.port),
msg?msg:"????");
circ->n_hop = extend_info_new(NULL /*nickname*/,
(const char*)ec.node_id,
&ec.ed_pubkey,
NULL, /*onion_key*/
NULL, /*curve25519_key*/
&ec.orport_ipv4.addr,
ec.orport_ipv4.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 */
n_chan = channel_connect_for_circuit(&ec.orport_ipv4.addr,
ec.orport_ipv4.port,
(const char*)ec.node_id,
&ec.ed_pubkey);
if (!n_chan) {
log_info(LD_CIRC,"Launching n_chan failed. Closing circuit.");
circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
return 0;
}
log_debug(LD_CIRC,"connecting in progress (or finished). Good.");
}
/* 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;
}
tor_assert(!circ->n_hop); /* Connection is already established. */
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;
}
/** A "created" cell <b>reply</b> came back to us on circuit <b>circ</b>.
* (The body of <b>reply</b> varies depending on what sort of handshake
* this is.)
@ -1433,61 +1267,6 @@ circuit_truncated(origin_circuit_t *circ, int reason)
#endif /* 0 */
}
/** Given a response payload and keys, initialize, then send a created
* cell back.
*/
int
onionskin_answer(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;
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;
}
/** Helper for new_route_len(). Choose a circuit length for purpose
* <b>purpose</b>: DEFAULT_ROUTE_LEN (+ 1 if someone else chose the
* exit). If someone else chose the exit, they could be colluding

View File

@ -32,15 +32,10 @@ void circuit_n_chan_done(channel_t *chan, int status,
int circuit_timeout_want_to_count_circ(const origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
void circuit_note_clock_jumped(int64_t seconds_elapsed, bool was_idle);
int circuit_extend(cell_t *cell, circuit_t *circ);
struct created_cell_t;
int circuit_finish_handshake(origin_circuit_t *circ,
const struct created_cell_t *created_cell);
int circuit_truncated(origin_circuit_t *circ, int reason);
int onionskin_answer(or_circuit_t *circ,
const struct created_cell_t *created_cell,
const char *keys, size_t keys_len,
const uint8_t *rend_circ_nonce);
MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now,
int *need_uptime,
int *need_capacity));
@ -76,6 +71,20 @@ const node_t *choose_good_entry_server(uint8_t purpose,
struct circuit_guard_state_t **guard_state_out);
void circuit_upgrade_circuits_from_guard_wait(void);
struct ed25519_public_key_t;
channel_t *
channel_connect_for_circuit(const tor_addr_t *addr,
uint16_t port,
const char *id_digest,
const struct ed25519_public_key_t *ed_id);
struct create_cell_t;
int
circuit_deliver_create_cell(circuit_t *circ,
const struct create_cell_t *create_cell,
int relayed);
#ifdef CIRCUITBUILD_PRIVATE
STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan);
STATIC int new_route_len(uint8_t purpose, extend_info_t *exit_ei,

View File

@ -54,6 +54,7 @@
#include "feature/nodelist/describe.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/relay/circuitbuild_relay.h"
#include "feature/relay/routermode.h"
#include "feature/stats/rephist.h"
#include "lib/crypt_ops/crypto_util.h"

View File

@ -66,6 +66,7 @@
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dircommon/directory.h"
#include "feature/relay/dns.h"
#include "feature/relay/circuitbuild_relay.h"
#include "feature/stats/geoip_stats.h"
#include "feature/hs/hs_cache.h"
#include "core/mainloop/mainloop.h"

View File

@ -0,0 +1,253 @@
/* 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 "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/routermode.h"
#include "feature/relay/selftest.h"
/** 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 (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;
}
if (!server_mode(get_options())) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Got an extend cell, but running as a client. Closing.");
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 (!ec.orport_ipv4.port || tor_addr_is_null(&ec.orport_ipv4.addr)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend to zero destination port or addr.");
return -1;
}
if (tor_addr_is_internal(&ec.orport_ipv4.addr, 0) &&
!get_options()->ExtendAllowPrivateAddresses) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Client asked me to extend to a private address");
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);
}
}
/* 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,
TO_OR_CIRCUIT(circ)->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,
&TO_OR_CIRCUIT(circ)->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;
}
n_chan = channel_get_for_extend((const char*)ec.node_id,
&ec.ed_pubkey,
&ec.orport_ipv4.addr,
&msg,
&should_launch);
if (!n_chan) {
log_debug(LD_CIRC|LD_OR,"Next router (%s): %s",
fmt_addrport(&ec.orport_ipv4.addr,ec.orport_ipv4.port),
msg?msg:"????");
circ->n_hop = extend_info_new(NULL /*nickname*/,
(const char*)ec.node_id,
&ec.ed_pubkey,
NULL, /*onion_key*/
NULL, /*curve25519_key*/
&ec.orport_ipv4.addr,
ec.orport_ipv4.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 */
n_chan = channel_connect_for_circuit(&ec.orport_ipv4.addr,
ec.orport_ipv4.port,
(const char*)ec.node_id,
&ec.ed_pubkey);
if (!n_chan) {
log_info(LD_CIRC,"Launching n_chan failed. Closing circuit.");
circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
return 0;
}
log_debug(LD_CIRC,"connecting in progress (or finished). Good.");
}
/* 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;
}
tor_assert(!circ->n_hop); /* Connection is already established. */
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;
}
/** Given a response payload and keys, initialize, then send a created
* cell back.
*/
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;
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;
}

View File

@ -0,0 +1,60 @@
/* 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"
struct cell_t;
struct created_cell_t;
struct circuit_t;
struct or_circuit_t;
#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
static inline int
circuit_extend(struct cell_t *cell, struct circuit_t *circ)
{
(void)cell;
(void)circ;
tor_assert_nonfatal_unreached();
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
#endif /* !defined(TOR_FEATURE_RELAY_CIRCUITBUILD_RELAY_H) */

View File

@ -8,6 +8,7 @@ LIBTOR_APP_A_SOURCES += \
# 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 \
@ -21,6 +22,7 @@ MODULE_RELAY_SOURCES = \
# 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 \