aboutsummaryrefslogtreecommitdiff
path: root/src/feature
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature')
-rw-r--r--src/feature/client/transports.c4
-rw-r--r--src/feature/dirauth/dirauth_config.c440
-rw-r--r--src/feature/dirauth/dirauth_config.h87
-rw-r--r--src/feature/dircache/dircache.c3
-rw-r--r--src/feature/relay/relay_config.c1440
-rw-r--r--src/feature/relay/relay_config.h188
-rw-r--r--src/feature/relay/router.c7
-rw-r--r--src/feature/relay/transport_config.c307
-rw-r--r--src/feature/relay/transport_config.h85
9 files changed, 2556 insertions, 5 deletions
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
index 3f731ac7d4..6537a4b2da 100644
--- a/src/feature/client/transports.c
+++ b/src/feature/client/transports.c
@@ -97,6 +97,8 @@
#include "core/or/circuitbuild.h"
#include "feature/client/transports.h"
#include "feature/relay/router.h"
+/* 31851: split the server transport code out of the client module */
+#include "feature/relay/transport_config.h"
#include "app/config/statefile.h"
#include "core/or/connection_or.h"
#include "feature/relay/ext_orport.h"
@@ -1279,7 +1281,7 @@ get_transport_options_for_server_proxy(const managed_proxy_t *mp)
string. */
SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, transport) {
smartlist_t *options_tmp_sl = NULL;
- options_tmp_sl = get_options_for_server_transport(transport);
+ options_tmp_sl = pt_get_options_for_server_transport(transport);
if (!options_tmp_sl)
continue;
diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c
new file mode 100644
index 0000000000..552f851461
--- /dev/null
+++ b/src/feature/dirauth/dirauth_config.c
@@ -0,0 +1,440 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file dirauth_config.c
+ * @brief Code to interpret the user's configuration of Tor's directory
+ * authority module.
+ **/
+
+#include "orconfig.h"
+#include "feature/dirauth/dirauth_config.h"
+
+#include "lib/encoding/confline.h"
+#include "lib/confmgt/confmgt.h"
+
+/* Required for dirinfo_type_t in or_options_t */
+#include "core/or/or.h"
+#include "app/config/config.h"
+
+#include "feature/dircommon/voting_schedule.h"
+#include "feature/stats/rephist.h"
+
+#include "feature/dirauth/authmode.h"
+#include "feature/dirauth/bwauth.h"
+#include "feature/dirauth/dirauth_periodic.h"
+#include "feature/dirauth/dirvote.h"
+#include "feature/dirauth/guardfraction.h"
+
+/* Copied from config.c, we will refactor later in 29211. */
+#define REJECT(arg) \
+ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
+#if defined(__GNUC__) && __GNUC__ <= 3
+#define COMPLAIN(args...) \
+ STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END
+#else
+#define COMPLAIN(args, ...) \
+ STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END
+#endif /* defined(__GNUC__) && __GNUC__ <= 3 */
+
+#define YES_IF_CHANGED_INT(opt) \
+ if (!CFG_EQ_INT(old_options, new_options, opt)) return 1;
+
+/**
+ * Legacy validation/normalization function for the dirauth mode options in
+ * options. Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_dirauth_mode(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (!authdir_mode(options))
+ return 0;
+
+ /* confirm that our address isn't broken, so we can complain now */
+ uint32_t tmp;
+ if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0)
+ REJECT("Failed to resolve/guess local address. See logs for details.");
+
+ if (!options->ContactInfo && !options->TestingTorNetwork)
+ REJECT("Authoritative directory servers must set ContactInfo");
+ if (!options->RecommendedClientVersions)
+ options->RecommendedClientVersions =
+ config_lines_dup(options->RecommendedVersions);
+ if (!options->RecommendedServerVersions)
+ options->RecommendedServerVersions =
+ config_lines_dup(options->RecommendedVersions);
+ if (options->VersioningAuthoritativeDir &&
+ (!options->RecommendedClientVersions ||
+ !options->RecommendedServerVersions))
+ REJECT("Versioning authoritative dir servers must set "
+ "Recommended*Versions.");
+
+ char *t;
+ /* Call these functions to produce warnings only. */
+ t = format_recommended_version_list(options->RecommendedClientVersions, 1);
+ tor_free(t);
+ t = format_recommended_version_list(options->RecommendedServerVersions, 1);
+ tor_free(t);
+
+ if (options->UseEntryGuards) {
+ log_info(LD_CONFIG, "Authoritative directory servers can't set "
+ "UseEntryGuards. Disabling.");
+ options->UseEntryGuards = 0;
+ }
+ if (!options->DownloadExtraInfo && authdir_mode_v3(options)) {
+ log_info(LD_CONFIG, "Authoritative directories always try to download "
+ "extra-info documents. Setting DownloadExtraInfo.");
+ options->DownloadExtraInfo = 1;
+ }
+ if (!(options->BridgeAuthoritativeDir ||
+ options->V3AuthoritativeDir))
+ REJECT("AuthoritativeDir is set, but none of "
+ "(Bridge/V3)AuthoritativeDir is set.");
+
+ /* If we have a v3bandwidthsfile and it's broken, complain on startup */
+ if (options->V3BandwidthsFile && !old_options) {
+ dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL,
+ NULL);
+ }
+ /* same for guardfraction file */
+ if (options->GuardfractionFile && !old_options) {
+ dirserv_read_guardfraction_file(options->GuardfractionFile, NULL);
+ }
+
+ if (!options->DirPort_set)
+ REJECT("Running as authoritative directory, but no DirPort set.");
+
+ if (!options->ORPort_set)
+ REJECT("Running as authoritative directory, but no ORPort set.");
+
+ if (options->ClientOnly)
+ REJECT("Running as authoritative directory, but ClientOnly also set.");
+
+ if (options->MinUptimeHidServDirectoryV2 < 0) {
+ log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at "
+ "least 0 seconds. Changing to 0.");
+ options->MinUptimeHidServDirectoryV2 = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the dirauth bandwidth options
+ * in options. Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_dirauth_bandwidth(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (!authdir_mode(options))
+ return 0;
+
+ if (config_ensure_bandwidth_cap(&options->AuthDirFastGuarantee,
+ "AuthDirFastGuarantee", msg) < 0)
+ return -1;
+ if (config_ensure_bandwidth_cap(&options->AuthDirGuardBWGuarantee,
+ "AuthDirGuardBWGuarantee", msg) < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the dirauth schedule options
+ * in options. Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_dirauth_schedule(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (!authdir_mode_v3(options))
+ return 0;
+
+ if (options->V3AuthVoteDelay + options->V3AuthDistDelay >=
+ options->V3AuthVotingInterval/2) {
+ REJECT("V3AuthVoteDelay plus V3AuthDistDelay must be less than half "
+ "V3AuthVotingInterval");
+ }
+
+ if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS) {
+ if (options->TestingTorNetwork) {
+ if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS_TESTING) {
+ REJECT("V3AuthVoteDelay is way too low.");
+ } else {
+ COMPLAIN("V3AuthVoteDelay is very low. "
+ "This may lead to failure to vote for a consensus.");
+ }
+ } else {
+ REJECT("V3AuthVoteDelay is way too low.");
+ }
+ }
+
+ if (options->V3AuthDistDelay < MIN_DIST_SECONDS) {
+ if (options->TestingTorNetwork) {
+ if (options->V3AuthDistDelay < MIN_DIST_SECONDS_TESTING) {
+ REJECT("V3AuthDistDelay is way too low.");
+ } else {
+ COMPLAIN("V3AuthDistDelay is very low. "
+ "This may lead to missing votes in a consensus.");
+ }
+ } else {
+ REJECT("V3AuthDistDelay is way too low.");
+ }
+ }
+
+ if (options->V3AuthNIntervalsValid < 2)
+ REJECT("V3AuthNIntervalsValid must be at least 2.");
+
+ if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL) {
+ if (options->TestingTorNetwork) {
+ if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL_TESTING) {
+ /* Unreachable, covered by earlier checks */
+ REJECT("V3AuthVotingInterval is insanely low."); /* LCOV_EXCL_LINE */
+ } else {
+ COMPLAIN("V3AuthVotingInterval is very low. "
+ "This may lead to failure to synchronise for a consensus.");
+ }
+ } else {
+ REJECT("V3AuthVotingInterval is insanely low.");
+ }
+ } else if (options->V3AuthVotingInterval > 24*60*60) {
+ REJECT("V3AuthVotingInterval is insanely high.");
+ } else if (((24*60*60) % options->V3AuthVotingInterval) != 0) {
+ COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours.");
+ }
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the dirauth testing options
+ * in options. Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_dirauth_testing(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (!authdir_mode(options))
+ return 0;
+
+ if (options->TestingAuthDirTimeToLearnReachability < 0) {
+ REJECT("TestingAuthDirTimeToLearnReachability must be non-negative.");
+ } else if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) {
+ COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high.");
+ }
+
+ if (!authdir_mode_v3(options))
+ return 0;
+
+ if (options->TestingV3AuthInitialVotingInterval
+ < MIN_VOTE_INTERVAL_TESTING_INITIAL) {
+ REJECT("TestingV3AuthInitialVotingInterval is insanely low.");
+ } else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) {
+ REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into "
+ "30 minutes.");
+ }
+
+ if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS_TESTING) {
+ REJECT("TestingV3AuthInitialVoteDelay is way too low.");
+ }
+
+ if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS_TESTING) {
+ REJECT("TestingV3AuthInitialDistDelay is way too low.");
+ }
+
+ if (options->TestingV3AuthInitialVoteDelay +
+ options->TestingV3AuthInitialDistDelay >=
+ options->TestingV3AuthInitialVotingInterval) {
+ REJECT("TestingV3AuthInitialVoteDelay plus TestingV3AuthInitialDistDelay "
+ "must be less than TestingV3AuthInitialVotingInterval");
+ }
+
+ if (options->TestingV3AuthVotingStartOffset >
+ MIN(options->TestingV3AuthInitialVotingInterval,
+ options->V3AuthVotingInterval)) {
+ REJECT("TestingV3AuthVotingStartOffset is higher than the voting "
+ "interval.");
+ } else if (options->TestingV3AuthVotingStartOffset < 0) {
+ REJECT("TestingV3AuthVotingStartOffset must be non-negative.");
+ }
+
+ return 0;
+}
+
+/**
+ * Return true if changing the configuration from <b>old</b> to <b>new</b>
+ * affects the timing of the voting subsystem
+ */
+static int
+options_transition_affects_dirauth_timing(const or_options_t *old_options,
+ const or_options_t *new_options)
+{
+ tor_assert(old_options);
+ tor_assert(new_options);
+
+ if (authdir_mode_v3(old_options) != authdir_mode_v3(new_options))
+ return 1;
+ if (! authdir_mode_v3(new_options))
+ return 0;
+
+ YES_IF_CHANGED_INT(V3AuthVotingInterval);
+ YES_IF_CHANGED_INT(V3AuthVoteDelay);
+ YES_IF_CHANGED_INT(V3AuthDistDelay);
+ YES_IF_CHANGED_INT(TestingV3AuthInitialVotingInterval);
+ YES_IF_CHANGED_INT(TestingV3AuthInitialVoteDelay);
+ YES_IF_CHANGED_INT(TestingV3AuthInitialDistDelay);
+ YES_IF_CHANGED_INT(TestingV3AuthVotingStartOffset);
+
+ return 0;
+}
+
+/** Fetch the active option list, and take dirauth actions based on it. All of
+ * the things we do should survive being done repeatedly. If present,
+ * <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_dirauth(const or_options_t *old_options)
+{
+ const or_options_t *options = get_options();
+
+ /* We may need to reschedule some dirauth stuff if our status changed. */
+ if (old_options) {
+ if (options_transition_affects_dirauth_timing(old_options, options)) {
+ voting_schedule_recalculate_timing(options, time(NULL));
+ reschedule_dirvote(options);
+ }
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take dirauth mtbf actions based on it.
+ * All of the things we do should survive being done repeatedly. If present,
+ * <b>old_options</b> contains the previous value of the options.
+ *
+ * Must be called immediately after a successful or_state_load().
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_dirauth_mtbf(const or_options_t *old_options)
+{
+ (void)old_options;
+
+ const or_options_t *options = get_options();
+ int running_tor = options->command == CMD_RUN_TOR;
+
+ if (!authdir_mode(options))
+ return 0;
+
+ /* Load dirauth state */
+ if (running_tor) {
+ rep_hist_load_mtbf_data(time(NULL));
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take dirauth statistics actions based
+ * on it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Sets <b>*print_notice_out</b> if we enabled stats, and need to print
+ * a stats log using options_act_relay_stats_msg().
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_dirauth_stats(const or_options_t *old_options,
+ bool *print_notice_out)
+{
+ if (BUG(!print_notice_out))
+ return -1;
+
+ const or_options_t *options = get_options();
+
+ if (authdir_mode_bridge(options)) {
+ time_t now = time(NULL);
+ int print_notice = 0;
+
+ if (!old_options || !authdir_mode_bridge(old_options)) {
+ rep_hist_desc_stats_init(now);
+ print_notice = 1;
+ }
+ if (print_notice)
+ *print_notice_out = 1;
+ }
+
+ /* If we used to have statistics enabled but we just disabled them,
+ stop gathering them. */
+ if (old_options && authdir_mode_bridge(old_options) &&
+ !authdir_mode_bridge(options))
+ rep_hist_desc_stats_term();
+
+ return 0;
+}
diff --git a/src/feature/dirauth/dirauth_config.h b/src/feature/dirauth/dirauth_config.h
new file mode 100644
index 0000000000..655ab0a7fa
--- /dev/null
+++ b/src/feature/dirauth/dirauth_config.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-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file dirauth_config.h
+ * @brief Header for feature/dirauth/dirauth_config.c
+ **/
+
+#ifndef TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H
+#define TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H
+
+typedef struct or_options_t or_options_t;
+
+#ifdef HAVE_MODULE_DIRAUTH
+
+#include "lib/cc/torint.h"
+
+int options_validate_dirauth_mode(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_dirauth_bandwidth(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_dirauth_schedule(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_dirauth_testing(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_act_dirauth(const or_options_t *old_options);
+int options_act_dirauth_mtbf(const or_options_t *old_options);
+int options_act_dirauth_stats(const or_options_t *old_options,
+ bool *print_notice_out);
+
+#else /* !defined(HAVE_MODULE_DIRAUTH) */
+
+/** When tor is compiled with the dirauth module disabled, it can't be
+ * configured as a directory authority.
+ *
+ * Returns -1 and sets msg to a newly allocated string, if AuthoritativeDir
+ * is set in options. Otherwise returns 0. */
+static inline int
+options_validate_dirauth_mode(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ /* Only check the primary option for now, #29211 will disable more
+ * options. */
+ if (options->AuthoritativeDir) {
+ /* REJECT() this configuration */
+ *msg = tor_strdup("This tor was built with dirauth mode disabled. "
+ "It can not be configured with AuthoritativeDir 1.");
+ return -1;
+ }
+
+ return 0;
+}
+
+#define options_validate_dirauth_bandwidth(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_dirauth_schedule(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_dirauth_testing(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_dirauth_testing(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+
+#define options_act_dirauth(old_options) \
+ (((void)(old_options)),0)
+#define options_act_dirauth_mtbf(old_options) \
+ (((void)(old_options)),0)
+
+#define options_act_dirauth_stats(old_options, print_notice_out) \
+ (((void)(old_options)),((void)(print_notice_out)),0)
+
+#endif /* defined(HAVE_MODULE_DIRAUTH) */
+
+#endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index 795f1b8ed7..9938f9426c 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -28,6 +28,7 @@
#include "feature/nodelist/authcert.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/routerlist.h"
+#include "feature/relay/relay_config.h"
#include "feature/relay/routermode.h"
#include "feature/rend/rendcache.h"
#include "feature/stats/geoip_stats.h"
@@ -478,7 +479,7 @@ static int
handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
{
(void) args; /* unused */
- const char *frontpage = get_dirportfrontpage();
+ const char *frontpage = relay_get_dirportfrontpage();
if (frontpage) {
size_t dlen;
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c
new file mode 100644
index 0000000000..275e0e6a68
--- /dev/null
+++ b/src/feature/relay/relay_config.c
@@ -0,0 +1,1440 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_config.c
+ * @brief Code to interpret the user's configuration of Tor's relay module.
+ **/
+
+#include "orconfig.h"
+#define RELAY_CONFIG_PRIVATE
+#include "feature/relay/relay_config.h"
+
+#include "lib/encoding/confline.h"
+#include "lib/confmgt/confmgt.h"
+
+#include "lib/container/smartlist.h"
+#include "lib/geoip/geoip.h"
+#include "lib/meminfo/meminfo.h"
+#include "lib/osinfo/uname.h"
+#include "lib/process/setuid.h"
+
+/* Required for dirinfo_type_t in or_options_t */
+#include "core/or/or.h"
+#include "app/config/config.h"
+
+#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"
+
+#include "feature/hibernate/hibernate.h"
+#include "feature/nodelist/nickname.h"
+#include "feature/stats/geoip_stats.h"
+#include "feature/stats/predict_ports.h"
+#include "feature/stats/rephist.h"
+
+#include "feature/dirauth/authmode.h"
+
+#include "feature/dircache/consdiffmgr.h"
+#include "feature/relay/dns.h"
+#include "feature/relay/routermode.h"
+
+/** Contents of most recently read DirPortFrontPage file. */
+static char *global_dirfrontpagecontents = NULL;
+
+/* Copied from config.c, we will refactor later in 29211. */
+#define REJECT(arg) \
+ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
+#if defined(__GNUC__) && __GNUC__ <= 3
+#define COMPLAIN(args...) \
+ STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END
+#else
+#define COMPLAIN(args, ...) \
+ STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END
+#endif /* defined(__GNUC__) && __GNUC__ <= 3 */
+
+/* Used in the various options_transition_affects* functions. */
+#define YES_IF_CHANGED_BOOL(opt) \
+ if (!CFG_EQ_BOOL(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_INT(opt) \
+ if (!CFG_EQ_INT(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_STRING(opt) \
+ if (!CFG_EQ_STRING(old_options, new_options, opt)) return 1;
+#define YES_IF_CHANGED_LINELIST(opt) \
+ if (!CFG_EQ_LINELIST(old_options, new_options, opt)) return 1;
+
+/** Return the contents of our frontpage string, or NULL if not configured. */
+MOCK_IMPL(const char*,
+relay_get_dirportfrontpage, (void))
+{
+ return global_dirfrontpagecontents;
+}
+
+/** Release all memory and resources held by global relay configuration
+ * structures.
+ */
+void
+relay_config_free_all(void)
+{
+ tor_free(global_dirfrontpagecontents);
+}
+
+/** Return the bandwidthrate that we are going to report to the authorities
+ * based on the config options. */
+uint32_t
+relay_get_effective_bwrate(const or_options_t *options)
+{
+ uint64_t bw = options->BandwidthRate;
+ if (bw > options->MaxAdvertisedBandwidth)
+ bw = options->MaxAdvertisedBandwidth;
+ if (options->RelayBandwidthRate > 0 && bw > options->RelayBandwidthRate)
+ bw = options->RelayBandwidthRate;
+ /* config_ensure_bandwidth_cap() makes sure that this cast can't overflow. */
+ return (uint32_t)bw;
+}
+
+/** Return the bandwidthburst that we are going to report to the authorities
+ * based on the config options. */
+uint32_t
+relay_get_effective_bwburst(const or_options_t *options)
+{
+ uint64_t bw = options->BandwidthBurst;
+ if (options->RelayBandwidthBurst > 0 && bw > options->RelayBandwidthBurst)
+ bw = options->RelayBandwidthBurst;
+ /* config_ensure_bandwidth_cap() makes sure that this cast can't overflow. */
+ return (uint32_t)bw;
+}
+
+/** Warn for every Extended ORPort port in <b>ports</b> that is on a
+ * publicly routable address. */
+void
+port_warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
+{
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type != CONN_TYPE_EXT_OR_LISTENER)
+ continue;
+ if (port->is_unix_addr)
+ continue;
+ /* XXX maybe warn even if address is RFC1918? */
+ if (!tor_addr_is_internal(&port->addr, 1)) {
+ log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. "
+ "This is not advised; this address is supposed to only be "
+ "exposed on localhost so that your pluggable transport "
+ "proxies can connect to it.",
+ fmt_addrport(&port->addr, port->port), portname);
+ }
+ } SMARTLIST_FOREACH_END(port);
+}
+
+/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal
+ * consistency and warn as appropriate. On Unix-based OSes, set
+ * *<b>n_low_ports_out</b> to the number of sub-1024 ports we will be
+ * binding, and warn if we may be unable to re-bind after hibernation. */
+static int
+check_server_ports(const smartlist_t *ports,
+ const or_options_t *options,
+ int *n_low_ports_out)
+{
+ if (BUG(!ports))
+ return -1;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!n_low_ports_out))
+ return -1;
+
+ int n_orport_advertised = 0;
+ int n_orport_advertised_ipv4 = 0;
+ int n_orport_listeners = 0;
+ int n_dirport_advertised = 0;
+ int n_dirport_listeners = 0;
+ int n_low_port = 0;
+ int r = 0;
+
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type == CONN_TYPE_DIR_LISTENER) {
+ if (! port->server_cfg.no_advertise)
+ ++n_dirport_advertised;
+ if (! port->server_cfg.no_listen)
+ ++n_dirport_listeners;
+ } else if (port->type == CONN_TYPE_OR_LISTENER) {
+ if (! port->server_cfg.no_advertise) {
+ ++n_orport_advertised;
+ if (port_binds_ipv4(port))
+ ++n_orport_advertised_ipv4;
+ }
+ if (! port->server_cfg.no_listen)
+ ++n_orport_listeners;
+ } else {
+ continue;
+ }
+#ifndef _WIN32
+ if (!port->server_cfg.no_listen && port->port < 1024)
+ ++n_low_port;
+#endif
+ } SMARTLIST_FOREACH_END(port);
+
+ if (n_orport_advertised && !n_orport_listeners) {
+ log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually "
+ "listening on one.");
+ r = -1;
+ }
+ if (n_orport_listeners && !n_orport_advertised) {
+ log_warn(LD_CONFIG, "We are listening on an ORPort, but not advertising "
+ "any ORPorts. This will keep us from building a %s "
+ "descriptor, and make us impossible to use.",
+ options->BridgeRelay ? "bridge" : "router");
+ r = -1;
+ }
+ if (n_dirport_advertised && !n_dirport_listeners) {
+ log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually "
+ "listening on one.");
+ r = -1;
+ }
+ if (n_dirport_advertised > 1) {
+ log_warn(LD_CONFIG, "Can't advertise more than one DirPort.");
+ r = -1;
+ }
+ if (n_orport_advertised && !n_orport_advertised_ipv4 &&
+ !options->BridgeRelay) {
+ log_warn(LD_CONFIG, "Configured public relay to listen only on an IPv6 "
+ "address. Tor needs to listen on an IPv4 address too.");
+ r = -1;
+ }
+
+ if (n_low_port && options->AccountingMax &&
+ (!have_capability_support() || options->KeepBindCapabilities == 0)) {
+ const char *extra = "";
+ if (options->KeepBindCapabilities == 0 && have_capability_support())
+ extra = ", and you have disabled KeepBindCapabilities.";
+ log_warn(LD_CONFIG,
+ "You have set AccountingMax to use hibernation. You have also "
+ "chosen a low DirPort or OrPort%s."
+ "This combination can make Tor stop "
+ "working when it tries to re-attach the port after a period of "
+ "hibernation. Please choose a different port or turn off "
+ "hibernation unless you know this combination will work on your "
+ "platform.", extra);
+ }
+
+ if (n_low_ports_out)
+ *n_low_ports_out = n_low_port;
+
+ return r;
+}
+
+/** Parse all relay ports from <b>options</b>. On success, add parsed ports to
+ * <b>ports</b>, and return 0. On failure, set *<b>msg</b> to a description
+ * of the problem and return -1.
+ **/
+int
+port_parse_ports_relay(or_options_t *options,
+ char **msg,
+ smartlist_t *ports_out,
+ int *have_low_ports_out)
+{
+ int retval = -1;
+ smartlist_t *ports = smartlist_new();
+ int n_low_ports = 0;
+
+ if (BUG(!options))
+ goto err;
+
+ if (BUG(!msg))
+ goto err;
+
+ if (BUG(!ports_out))
+ goto err;
+
+ if (BUG(!have_low_ports_out))
+ goto err;
+
+ if (options->ClientOnly) {
+ retval = 0;
+ goto err;
+ }
+
+ if (port_parse_config(ports,
+ options->ORPort_lines,
+ "OR", CONN_TYPE_OR_LISTENER,
+ "0.0.0.0", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid ORPort configuration");
+ goto err;
+ }
+ if (port_parse_config(ports,
+ options->ExtORPort_lines,
+ "ExtOR", CONN_TYPE_EXT_OR_LISTENER,
+ "127.0.0.1", 0,
+ CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) {
+ *msg = tor_strdup("Invalid ExtORPort configuration");
+ goto err;
+ }
+ if (port_parse_config(ports,
+ options->DirPort_lines,
+ "Dir", CONN_TYPE_DIR_LISTENER,
+ "0.0.0.0", 0,
+ CL_PORT_SERVER_OPTIONS) < 0) {
+ *msg = tor_strdup("Invalid DirPort configuration");
+ goto err;
+ }
+
+ if (check_server_ports(ports, options, &n_low_ports) < 0) {
+ *msg = tor_strdup("Misconfigured server ports");
+ goto err;
+ }
+
+ smartlist_add_all(ports_out, ports);
+ smartlist_free(ports);
+ ports = NULL;
+ retval = 0;
+
+ err:
+ if (*have_low_ports_out < 0)
+ *have_low_ports_out = (n_low_ports > 0);
+ if (ports) {
+ SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p));
+ smartlist_free(ports);
+ }
+ return retval;
+}
+
+/** Update the relay *Port_set values in <b>options</b> from <b>ports</b>. */
+void
+port_update_port_set_relay(or_options_t *options,
+ const smartlist_t *ports)
+{
+ if (BUG(!options))
+ return;
+
+ if (BUG(!ports))
+ return;
+
+ if (options->ClientOnly)
+ return;
+
+ /* Update the relay *Port_set options. The !! here is to force a boolean
+ * out of an integer. */
+ options->ORPort_set =
+ !! port_count_real_listeners(ports, CONN_TYPE_OR_LISTENER, 0);
+ options->DirPort_set =
+ !! port_count_real_listeners(ports, CONN_TYPE_DIR_LISTENER, 0);
+ options->ExtORPort_set =
+ !! port_count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER, 0);
+}
+
+/**
+ * Legacy validation function, which checks that the current OS is usable in
+ * relay mode, if options is set to a relay mode.
+ *
+ * Warns about OSes with potential issues. Always returns 0.
+ */
+int
+options_validate_relay_os(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (!server_mode(options))
+ return 0;
+
+ const char *uname = get_uname();
+
+ if (!strcmpstart(uname, "Windows 95") ||
+ !strcmpstart(uname, "Windows 98") ||
+ !strcmpstart(uname, "Windows Me")) {
+ log_warn(LD_CONFIG, "Tor is running as a server, but you are "
+ "running %s; this probably won't work. See "
+ "https://www.torproject.org/docs/faq.html#BestOSForRelay "
+ "for details.", uname);
+ }
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the relay info options.
+ * Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_relay_info(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (options->Nickname == NULL) {
+ if (server_mode(options)) {
+ options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME);
+ }
+ } else {
+ if (!is_legal_nickname(options->Nickname)) {
+ tor_asprintf(msg,
+ "Nickname '%s', nicknames must be between 1 and 19 characters "
+ "inclusive, and must contain only the characters [a-zA-Z0-9].",
+ options->Nickname);
+ return -1;
+ }
+ }
+
+ if (server_mode(options) && !options->ContactInfo)
+ log_notice(LD_CONFIG, "Your ContactInfo config option is not set. "
+ "Please consider setting it, so we can contact you if your server is "
+ "misconfigured or something else goes wrong.");
+
+ const char *ContactInfo = options->ContactInfo;
+ if (ContactInfo && !string_is_utf8(ContactInfo, strlen(ContactInfo)))
+ REJECT("ContactInfo config option must be UTF-8.");
+
+ return 0;
+}
+
+/** Parse an authority type from <b>options</b>-\>PublishServerDescriptor
+ * and write it to <b>options</b>-\>PublishServerDescriptor_. Treat "1"
+ * as "v3" unless BridgeRelay is 1, in which case treat it as "bridge".
+ * Treat "0" as "".
+ * Return 0 on success or -1 if not a recognized authority type (in which
+ * case the value of PublishServerDescriptor_ is undefined). */
+static int
+compute_publishserverdescriptor(or_options_t *options)
+{
+ smartlist_t *list = options->PublishServerDescriptor;
+ dirinfo_type_t *auth = &options->PublishServerDescriptor_;
+ *auth = NO_DIRINFO;
+ if (!list) /* empty list, answer is none */
+ return 0;
+ SMARTLIST_FOREACH_BEGIN(list, const char *, string) {
+ if (!strcasecmp(string, "v1"))
+ log_warn(LD_CONFIG, "PublishServerDescriptor v1 has no effect, because "
+ "there are no v1 directory authorities anymore.");
+ else if (!strcmp(string, "1"))
+ if (options->BridgeRelay)
+ *auth |= BRIDGE_DIRINFO;
+ else
+ *auth |= V3_DIRINFO;
+ else if (!strcasecmp(string, "v2"))
+ log_warn(LD_CONFIG, "PublishServerDescriptor v2 has no effect, because "
+ "there are no v2 directory authorities anymore.");
+ else if (!strcasecmp(string, "v3"))
+ *auth |= V3_DIRINFO;
+ else if (!strcasecmp(string, "bridge"))
+ *auth |= BRIDGE_DIRINFO;
+ else if (!strcasecmp(string, "hidserv"))
+ log_warn(LD_CONFIG,
+ "PublishServerDescriptor hidserv is invalid. See "
+ "PublishHidServDescriptors.");
+ else if (!strcasecmp(string, "") || !strcmp(string, "0"))
+ /* no authority */;
+ else
+ return -1;
+ } SMARTLIST_FOREACH_END(string);
+ return 0;
+}
+
+/**
+ * Validate the configured bridge distribution method from a BridgeDistribution
+ * config line.
+ *
+ * The input <b>bd</b>, is a string taken from the BridgeDistribution config
+ * line (if present). If the option wasn't set, return 0 immediately. The
+ * BridgeDistribution option is then validated. Currently valid, recognised
+ * options are:
+ *
+ * - "none"
+ * - "any"
+ * - "https"
+ * - "email"
+ * - "moat"
+ * - "hyphae"
+ *
+ * If the option string is unrecognised, a warning will be logged and 0 is
+ * returned. If the option string contains an invalid character, -1 is
+ * returned.
+ **/
+STATIC int
+check_bridge_distribution_setting(const char *bd)
+{
+ if (bd == NULL)
+ return 0;
+
+ const char *RECOGNIZED[] = {
+ "none", "any", "https", "email", "moat", "hyphae"
+ };
+ unsigned i;
+ for (i = 0; i < ARRAY_LENGTH(RECOGNIZED); ++i) {
+ if (!strcmp(bd, RECOGNIZED[i]))
+ return 0;
+ }
+
+ const char *cp = bd;
+ // Method = (KeywordChar | "_") +
+ while (TOR_ISALNUM(*cp) || *cp == '-' || *cp == '_')
+ ++cp;
+
+ if (*cp == 0) {
+ log_warn(LD_CONFIG, "Unrecognized BridgeDistribution value %s. I'll "
+ "assume you know what you are doing...", escaped(bd));
+ return 0; // we reached the end of the string; all is well
+ } else {
+ return -1; // we found a bad character in the string.
+ }
+}
+
+/**
+ * Legacy validation/normalization function for the bridge relay options.
+ * Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_publish_server(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (compute_publishserverdescriptor(options) < 0) {
+ tor_asprintf(msg, "Unrecognized value in PublishServerDescriptor");
+ return -1;
+ }
+
+ if ((options->BridgeRelay
+ || options->PublishServerDescriptor_ & BRIDGE_DIRINFO)
+ && (options->PublishServerDescriptor_ & V3_DIRINFO)) {
+ REJECT("Bridges are not supposed to publish router descriptors to the "
+ "directory authorities. Please correct your "
+ "PublishServerDescriptor line.");
+ }
+
+ if (options->BridgeDistribution) {
+ if (!options->BridgeRelay) {
+ REJECT("You set BridgeDistribution, but you didn't set BridgeRelay!");
+ }
+ if (check_bridge_distribution_setting(options->BridgeDistribution) < 0) {
+ REJECT("Invalid BridgeDistribution value.");
+ }
+ }
+
+ if (options->PublishServerDescriptor)
+ SMARTLIST_FOREACH(options->PublishServerDescriptor, const char *, pubdes, {
+ if (!strcmp(pubdes, "1") || !strcmp(pubdes, "0"))
+ if (smartlist_len(options->PublishServerDescriptor) > 1) {
+ COMPLAIN("You have passed a list of multiple arguments to the "
+ "PublishServerDescriptor option that includes 0 or 1. "
+ "0 or 1 should only be used as the sole argument. "
+ "This configuration will be rejected in a future release.");
+ break;
+ }
+ });
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the relay padding options.
+ * Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_relay_padding(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (!server_mode(options))
+ return 0;
+
+ if (options->ConnectionPadding != -1) {
+ REJECT("Relays must use 'auto' for the ConnectionPadding setting.");
+ }
+
+ if (options->ReducedConnectionPadding != 0) {
+ REJECT("Relays cannot set ReducedConnectionPadding. ");
+ }
+
+ if (options->CircuitPadding == 0) {
+ REJECT("Relays cannot set CircuitPadding to 0. ");
+ }
+
+ if (options->ReducedCircuitPadding == 1) {
+ REJECT("Relays cannot set ReducedCircuitPadding. ");
+ }
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the relay bandwidth options.
+ * Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_relay_bandwidth(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ /* 31851: the tests expect us to validate bandwidths, even when we are not
+ * in relay mode. */
+ if (config_ensure_bandwidth_cap(&options->MaxAdvertisedBandwidth,
+ "MaxAdvertisedBandwidth", msg) < 0)
+ return -1;
+ if (config_ensure_bandwidth_cap(&options->RelayBandwidthRate,
+ "RelayBandwidthRate", msg) < 0)
+ return -1;
+ if (config_ensure_bandwidth_cap(&options->RelayBandwidthBurst,
+ "RelayBandwidthBurst", msg) < 0)
+ return -1;
+ if (config_ensure_bandwidth_cap(&options->PerConnBWRate,
+ "PerConnBWRate", msg) < 0)
+ return -1;
+ if (config_ensure_bandwidth_cap(&options->PerConnBWBurst,
+ "PerConnBWBurst", msg) < 0)
+ return -1;
+
+ if (options->RelayBandwidthRate && !options->RelayBandwidthBurst)
+ options->RelayBandwidthBurst = options->RelayBandwidthRate;
+ if (options->RelayBandwidthBurst && !options->RelayBandwidthRate)
+ options->RelayBandwidthRate = options->RelayBandwidthBurst;
+
+ if (server_mode(options)) {
+ const unsigned required_min_bw =
+ public_server_mode(options) ?
+ RELAY_REQUIRED_MIN_BANDWIDTH : BRIDGE_REQUIRED_MIN_BANDWIDTH;
+ const char * const optbridge =
+ public_server_mode(options) ? "" : "bridge ";
+ if (options->BandwidthRate < required_min_bw) {
+ tor_asprintf(msg,
+ "BandwidthRate is set to %d bytes/second. "
+ "For %sservers, it must be at least %u.",
+ (int)options->BandwidthRate, optbridge,
+ required_min_bw);
+ return -1;
+ } else if (options->MaxAdvertisedBandwidth <
+ required_min_bw/2) {
+ tor_asprintf(msg,
+ "MaxAdvertisedBandwidth is set to %d bytes/second. "
+ "For %sservers, it must be at least %u.",
+ (int)options->MaxAdvertisedBandwidth, optbridge,
+ required_min_bw/2);
+ return -1;
+ }
+ if (options->RelayBandwidthRate &&
+ options->RelayBandwidthRate < required_min_bw) {
+ tor_asprintf(msg,
+ "RelayBandwidthRate is set to %d bytes/second. "
+ "For %sservers, it must be at least %u.",
+ (int)options->RelayBandwidthRate, optbridge,
+ required_min_bw);
+ return -1;
+ }
+ }
+
+ /* 31851: the tests expect us to validate bandwidths, even when we are not
+ * in relay mode. */
+ if (options->RelayBandwidthRate > options->RelayBandwidthBurst)
+ REJECT("RelayBandwidthBurst must be at least equal "
+ "to RelayBandwidthRate.");
+
+ /* if they set relaybandwidth* really high but left bandwidth*
+ * at the default, raise the defaults. */
+ if (options->RelayBandwidthRate > options->BandwidthRate)
+ options->BandwidthRate = options->RelayBandwidthRate;
+ if (options->RelayBandwidthBurst > options->BandwidthBurst)
+ options->BandwidthBurst = options->RelayBandwidthBurst;
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the relay bandwidth accounting
+ * options. Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_relay_accounting(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ /* 31851: the tests expect us to validate accounting, even when we are not
+ * in relay mode. */
+ if (accounting_parse_options(options, 1)<0)
+ REJECT("Failed to parse accounting options. See logs for details.");
+
+ if (options->AccountingMax) {
+ if (options->RendConfigLines && server_mode(options)) {
+ log_warn(LD_CONFIG, "Using accounting with a hidden service and an "
+ "ORPort is risky: your hidden service(s) and your public "
+ "address will all turn off at the same time, which may alert "
+ "observers that they are being run by the same party.");
+ } else if (config_count_key(options->RendConfigLines,
+ "HiddenServiceDir") > 1) {
+ log_warn(LD_CONFIG, "Using accounting with multiple hidden services is "
+ "risky: they will all turn off at the same time, which may "
+ "alert observers that they are being run by the same party.");
+ }
+ }
+
+ options->AccountingRule = ACCT_MAX;
+ if (options->AccountingRule_option) {
+ if (!strcmp(options->AccountingRule_option, "sum"))
+ options->AccountingRule = ACCT_SUM;
+ else if (!strcmp(options->AccountingRule_option, "max"))
+ options->AccountingRule = ACCT_MAX;
+ else if (!strcmp(options->AccountingRule_option, "in"))
+ options->AccountingRule = ACCT_IN;
+ else if (!strcmp(options->AccountingRule_option, "out"))
+ options->AccountingRule = ACCT_OUT;
+ else
+ REJECT("AccountingRule must be 'sum', 'max', 'in', or 'out'");
+ }
+
+ return 0;
+}
+
+/** Verify whether lst is a list of strings containing valid-looking
+ * comma-separated nicknames, or NULL. Will normalise <b>lst</b> to prefix '$'
+ * to any nickname or fingerprint that needs it. Also splits comma-separated
+ * list elements into multiple elements. Return 0 on success.
+ * Warn and return -1 on failure.
+ */
+static int
+normalize_nickname_list(config_line_t **normalized_out,
+ const config_line_t *lst, const char *name,
+ char **msg)
+{
+ if (!lst)
+ return 0;
+
+ config_line_t *new_nicknames = NULL;
+ config_line_t **new_nicknames_next = &new_nicknames;
+
+ const config_line_t *cl;
+ for (cl = lst; cl; cl = cl->next) {
+ const char *line = cl->value;
+ if (!line)
+ continue;
+
+ int valid_line = 1;
+ smartlist_t *sl = smartlist_new();
+ smartlist_split_string(sl, line, ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0);
+ SMARTLIST_FOREACH_BEGIN(sl, char *, s)
+ {
+ char *normalized = NULL;
+ if (!is_legal_nickname_or_hexdigest(s)) {
+ // check if first char is dollar
+ if (s[0] != '$') {
+ // Try again but with a dollar symbol prepended
+ char *prepended;
+ tor_asprintf(&prepended, "$%s", s);
+
+ if (is_legal_nickname_or_hexdigest(prepended)) {
+ // The nickname is valid when it's prepended, set it as the
+ // normalized version
+ normalized = prepended;
+ } else {
+ // Still not valid, free and fallback to error message
+ tor_free(prepended);
+ }
+ }
+
+ if (!normalized) {
+ tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
+ valid_line = 0;
+ break;
+ }
+ } else {
+ normalized = tor_strdup(s);
+ }
+
+ config_line_t *next = tor_malloc_zero(sizeof(*next));
+ next->key = tor_strdup(cl->key);
+ next->value = normalized;
+ next->next = NULL;
+
+ *new_nicknames_next = next;
+ new_nicknames_next = &next->next;
+ } SMARTLIST_FOREACH_END(s);
+
+ SMARTLIST_FOREACH(sl, char *, s, tor_free(s));
+ smartlist_free(sl);
+
+ if (!valid_line) {
+ config_free_lines(new_nicknames);
+ return -1;
+ }
+ }
+
+ *normalized_out = new_nicknames;
+
+ return 0;
+}
+
+#define ONE_MEGABYTE (UINT64_C(1) << 20)
+
+/* If we have less than 300 MB suggest disabling dircache */
+#define DIRCACHE_MIN_MEM_MB 300
+#define DIRCACHE_MIN_MEM_BYTES (DIRCACHE_MIN_MEM_MB*ONE_MEGABYTE)
+#define STRINGIFY(val) #val
+
+/** Create a warning message for emitting if we are a dircache but may not have
+ * enough system memory, or if we are not a dircache but probably should be.
+ * Return -1 when a message is returned in *msg*, else return 0. */
+STATIC int
+have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
+ char **msg)
+{
+ *msg = NULL;
+ /* XXX We should possibly be looking at MaxMemInQueues here
+ * unconditionally. Or we should believe total_mem unconditionally. */
+ if (total_mem == 0) {
+ if (get_total_system_memory(&total_mem) < 0) {
+ total_mem = options->MaxMemInQueues >= SIZE_MAX ?
+ SIZE_MAX : (size_t)options->MaxMemInQueues;
+ }
+ }
+ if (options->DirCache) {
+ if (total_mem < DIRCACHE_MIN_MEM_BYTES) {
+ if (options->BridgeRelay) {
+ tor_asprintf(msg, "Running a Bridge with less than %d MB of memory "
+ "is not recommended.", DIRCACHE_MIN_MEM_MB);
+ } else {
+ tor_asprintf(msg, "Being a directory cache (default) with less than "
+ "%d MB of memory is not recommended and may consume "
+ "most of the available resources. Consider disabling "
+ "this functionality by setting the DirCache option "
+ "to 0.", DIRCACHE_MIN_MEM_MB);
+ }
+ }
+ } else {
+ if (total_mem >= DIRCACHE_MIN_MEM_BYTES) {
+ *msg = tor_strdup("DirCache is disabled and we are configured as a "
+ "relay. We will not become a Guard.");
+ }
+ }
+ return *msg == NULL ? 0 : -1;
+}
+#undef STRINGIFY
+
+/**
+ * Legacy validation/normalization function for the relay mode options.
+ * Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_relay_mode(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (server_mode(options) && options->RendConfigLines)
+ log_warn(LD_CONFIG,
+ "Tor is currently configured as a relay and a hidden service. "
+ "That's not very secure: you should probably run your hidden service "
+ "in a separate Tor process, at least -- see "
+ "https://trac.torproject.org/8742");
+
+ if (options->BridgeRelay && options->DirPort_set) {
+ log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
+ "DirPort");
+ config_free_lines(options->DirPort_lines);
+ options->DirPort_lines = NULL;
+ options->DirPort_set = 0;
+ }
+
+ if (options->DirPort_set && !options->DirCache) {
+ REJECT("DirPort configured but DirCache disabled. DirPort requires "
+ "DirCache.");
+ }
+
+ if (options->BridgeRelay && !options->DirCache) {
+ REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires "
+ "DirCache.");
+ }
+
+ if (options->BridgeRelay == 1 && ! options->ORPort_set)
+ REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
+ "combination.");
+
+ if (server_mode(options)) {
+ char *dircache_msg = NULL;
+ if (have_enough_mem_for_dircache(options, 0, &dircache_msg)) {
+ log_warn(LD_CONFIG, "%s", dircache_msg);
+ tor_free(dircache_msg);
+ }
+ }
+
+ if (options->MyFamily_lines && options->BridgeRelay) {
+ log_warn(LD_CONFIG, "Listing a family for a bridge relay is not "
+ "supported: it can reveal bridge fingerprints to censors. "
+ "You should also make sure you aren't listing this bridge's "
+ "fingerprint in any other MyFamily.");
+ }
+ if (options->MyFamily_lines && !options->ContactInfo) {
+ log_warn(LD_CONFIG, "MyFamily is set but ContactInfo is not configured. "
+ "ContactInfo should always be set when MyFamily option is too.");
+ }
+ if (normalize_nickname_list(&options->MyFamily,
+ options->MyFamily_lines, "MyFamily", msg))
+ return -1;
+
+ if (options->ConstrainedSockets) {
+ if (options->DirPort_set) {
+ /* Providing cached directory entries while system TCP buffers are scarce
+ * will exacerbate the socket errors. Suggest that this be disabled. */
+ COMPLAIN("You have requested constrained socket buffers while also "
+ "serving directory entries via DirPort. It is strongly "
+ "suggested that you disable serving directory requests when "
+ "system TCP buffer resources are scarce.");
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Legacy validation/normalization function for the relay testing options
+ * in options. Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_relay_testing(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2)
+ REJECT("SigningKeyLifetime is too short.");
+ if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2)
+ REJECT("LinkCertLifetime is too short.");
+ if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2)
+ REJECT("TestingAuthKeyLifetime is too short.");
+
+ return 0;
+}
+
+/** Return 1 if any change from <b>old_options</b> to <b>new_options</b>
+ * will require us to rotate the CPU and DNS workers; else return 0. */
+static int
+options_transition_affects_workers(const or_options_t *old_options,
+ const or_options_t *new_options)
+{
+ YES_IF_CHANGED_STRING(DataDirectory);
+ YES_IF_CHANGED_INT(NumCPUs);
+ YES_IF_CHANGED_LINELIST(ORPort_lines);
+ YES_IF_CHANGED_BOOL(ServerDNSSearchDomains);
+ YES_IF_CHANGED_BOOL(SafeLogging_);
+ YES_IF_CHANGED_BOOL(ClientOnly);
+ YES_IF_CHANGED_BOOL(LogMessageDomains);
+ YES_IF_CHANGED_LINELIST(Logs);
+
+ if (server_mode(old_options) != server_mode(new_options) ||
+ public_server_mode(old_options) != public_server_mode(new_options) ||
+ dir_server_mode(old_options) != dir_server_mode(new_options))
+ return 1;
+
+ /* Nothing that changed matters. */
+ return 0;
+}
+
+/** Return 1 if any change from <b>old_options</b> to <b>new_options</b>
+ * will require us to generate a new descriptor; else return 0. */
+static int
+options_transition_affects_descriptor(const or_options_t *old_options,
+ const or_options_t *new_options)
+{
+ /* XXX We can be smarter here. If your DirPort isn't being
+ * published and you just turned it off, no need to republish. Etc. */
+
+ YES_IF_CHANGED_STRING(DataDirectory);
+ YES_IF_CHANGED_STRING(Nickname);
+ YES_IF_CHANGED_STRING(Address);
+ YES_IF_CHANGED_LINELIST(ExitPolicy);
+ YES_IF_CHANGED_BOOL(ExitRelay);
+ YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate);
+ YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces);
+ YES_IF_CHANGED_BOOL(IPv6Exit);
+ YES_IF_CHANGED_LINELIST(ORPort_lines);
+ YES_IF_CHANGED_LINELIST(DirPort_lines);
+ YES_IF_CHANGED_LINELIST(DirPort_lines);
+ YES_IF_CHANGED_BOOL(ClientOnly);
+ YES_IF_CHANGED_BOOL(DisableNetwork);
+ YES_IF_CHANGED_BOOL(PublishServerDescriptor_);
+ YES_IF_CHANGED_STRING(ContactInfo);
+ YES_IF_CHANGED_STRING(BridgeDistribution);
+ YES_IF_CHANGED_LINELIST(MyFamily);
+ YES_IF_CHANGED_STRING(AccountingStart);
+ YES_IF_CHANGED_INT(AccountingMax);
+ YES_IF_CHANGED_INT(AccountingRule);
+ YES_IF_CHANGED_BOOL(DirCache);
+ YES_IF_CHANGED_BOOL(AssumeReachable);
+
+ if (relay_get_effective_bwrate(old_options) !=
+ relay_get_effective_bwrate(new_options) ||
+ relay_get_effective_bwburst(old_options) !=
+ relay_get_effective_bwburst(new_options) ||
+ public_server_mode(old_options) != public_server_mode(new_options))
+ return 1;
+
+ return 0;
+}
+
+/** Fetch the active option list, and take relay actions based on it. All of
+ * the things we do should survive being done repeatedly. If present,
+ * <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay(const or_options_t *old_options)
+{
+ const or_options_t *options = get_options();
+
+ const int transition_affects_workers =
+ old_options && options_transition_affects_workers(old_options, options);
+
+ /* We want to reinit keys as needed before we do much of anything else:
+ keys are important, and other things can depend on them. */
+ if (transition_affects_workers ||
+ (authdir_mode_v3(options) && (!old_options ||
+ !authdir_mode_v3(old_options)))) {
+ if (init_keys() < 0) {
+ log_warn(LD_BUG,"Error initializing keys; exiting");
+ return -1;
+ }
+ }
+
+ if (server_mode(options)) {
+ static int cdm_initialized = 0;
+ if (cdm_initialized == 0) {
+ cdm_initialized = 1;
+ consdiffmgr_configure(NULL);
+ consdiffmgr_validate();
+ }
+ }
+
+ /* Check for transitions that need action. */
+ if (old_options) {
+ if (transition_affects_workers) {
+ log_info(LD_GENERAL,
+ "Worker-related options changed. Rotating workers.");
+ const int server_mode_turned_on =
+ server_mode(options) && !server_mode(old_options);
+ const int dir_server_mode_turned_on =
+ dir_server_mode(options) && !dir_server_mode(old_options);
+
+ if (server_mode_turned_on || dir_server_mode_turned_on) {
+ cpu_init();
+ }
+
+ if (server_mode_turned_on) {
+ ip_address_changed(0);
+ if (have_completed_a_circuit() || !any_predicted_circuits(time(NULL)))
+ inform_testing_reachability();
+ }
+ cpuworkers_rotate_keyinfo();
+ }
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take relay accounting actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay_accounting(const or_options_t *old_options)
+{
+ (void)old_options;
+
+ const or_options_t *options = get_options();
+
+ /* Set up accounting */
+ if (accounting_parse_options(options, 0)<0) {
+ // LCOV_EXCL_START
+ log_warn(LD_BUG,"Error in previously validated accounting options");
+ return -1;
+ // LCOV_EXCL_STOP
+ }
+ if (accounting_is_enabled(options))
+ configure_accounting(time(NULL));
+
+ return 0;
+}
+
+/** Fetch the active option list, and take relay bandwidth actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay_bandwidth(const or_options_t *old_options)
+{
+ const or_options_t *options = get_options();
+
+ /* Check for transitions that need action. */
+ if (old_options) {
+ if (options->PerConnBWRate != old_options->PerConnBWRate ||
+ options->PerConnBWBurst != old_options->PerConnBWBurst)
+ connection_or_update_token_buckets(get_connection_array(), options);
+
+ if (options->RelayBandwidthRate != old_options->RelayBandwidthRate ||
+ options->RelayBandwidthBurst != old_options->RelayBandwidthBurst)
+ connection_bucket_adjust(options);
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take bridge statistics actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_bridge_stats(const or_options_t *old_options)
+{
+ const or_options_t *options = get_options();
+
+/* How long should we delay counting bridge stats after becoming a bridge?
+ * We use this so we don't count clients who used our bridge thinking it is
+ * a relay. If you change this, don't forget to change the log message
+ * below. It's 4 hours (the time it takes to stop being used by clients)
+ * plus some extra time for clock skew. */
+#define RELAY_BRIDGE_STATS_DELAY (6 * 60 * 60)
+
+ /* Check for transitions that need action. */
+ if (old_options) {
+ if (! bool_eq(options->BridgeRelay, old_options->BridgeRelay)) {
+ int was_relay = 0;
+ if (options->BridgeRelay) {
+ time_t int_start = time(NULL);
+ if (config_lines_eq(old_options->ORPort_lines,options->ORPort_lines)) {
+ int_start += RELAY_BRIDGE_STATS_DELAY;
+ was_relay = 1;
+ }
+ geoip_bridge_stats_init(int_start);
+ log_info(LD_CONFIG, "We are acting as a bridge now. Starting new "
+ "GeoIP stats interval%s.", was_relay ? " in 6 "
+ "hours from now" : "");
+ } else {
+ geoip_bridge_stats_term();
+ log_info(LD_GENERAL, "We are no longer acting as a bridge. "
+ "Forgetting GeoIP stats.");
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take relay statistics actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Sets <b>*print_notice_out</b> if we enabled stats, and need to print
+ * a stats log using options_act_relay_stats_msg().
+ *
+ * If loading the GeoIP file failed, sets DirReqStatistics and
+ * EntryStatistics to 0. This breaks the normalization/act ordering
+ * introduced in 29211.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay_stats(const or_options_t *old_options,
+ bool *print_notice_out)
+{
+ if (BUG(!print_notice_out))
+ return -1;
+
+ or_options_t *options = get_options_mutable();
+
+ if (options->CellStatistics || options->DirReqStatistics ||
+ options->EntryStatistics || options->ExitPortStatistics ||
+ options->ConnDirectionStatistics ||
+ options->HiddenServiceStatistics) {
+ time_t now = time(NULL);
+ int print_notice = 0;
+
+ if ((!old_options || !old_options->CellStatistics) &&
+ options->CellStatistics) {
+ rep_hist_buffer_stats_init(now);
+ print_notice = 1;
+ }
+ if ((!old_options || !old_options->DirReqStatistics) &&
+ options->DirReqStatistics) {
+ if (geoip_is_loaded(AF_INET)) {
+ geoip_dirreq_stats_init(now);
+ print_notice = 1;
+ } else {
+ /* disable statistics collection since we have no geoip file */
+ /* 29211: refactor to avoid the normalisation/act inversion */
+ options->DirReqStatistics = 0;
+ if (options->ORPort_set)
+ log_notice(LD_CONFIG, "Configured to measure directory request "
+ "statistics, but no GeoIP database found. "
+ "Please specify a GeoIP database using the "
+ "GeoIPFile option.");
+ }
+ }
+ if ((!old_options || !old_options->EntryStatistics) &&
+ options->EntryStatistics && !should_record_bridge_info(options)) {
+ /* If we get here, we've started recording bridge info when we didn't
+ * do so before. Note that "should_record_bridge_info()" will
+ * always be false at this point, because of the earlier block
+ * that cleared EntryStatistics when public_server_mode() was false.
+ * We're leaving it in as defensive programming. */
+ if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) {
+ geoip_entry_stats_init(now);
+ print_notice = 1;
+ } else {
+ options->EntryStatistics = 0;
+ log_notice(LD_CONFIG, "Configured to measure entry node "
+ "statistics, but no GeoIP database found. "
+ "Please specify a GeoIP database using the "
+ "GeoIPFile option.");
+ }
+ }
+ if ((!old_options || !old_options->ExitPortStatistics) &&
+ options->ExitPortStatistics) {
+ rep_hist_exit_stats_init(now);
+ print_notice = 1;
+ }
+ if ((!old_options || !old_options->ConnDirectionStatistics) &&
+ options->ConnDirectionStatistics) {
+ rep_hist_conn_stats_init(now);
+ }
+ if ((!old_options || !old_options->HiddenServiceStatistics) &&
+ options->HiddenServiceStatistics) {
+ log_info(LD_CONFIG, "Configured to measure hidden service statistics.");
+ rep_hist_hs_stats_init(now);
+ }
+ if (print_notice)
+ *print_notice_out = 1;
+ }
+
+ /* If we used to have statistics enabled but we just disabled them,
+ stop gathering them. */
+ if (old_options && old_options->CellStatistics &&
+ !options->CellStatistics)
+ rep_hist_buffer_stats_term();
+ if (old_options && old_options->DirReqStatistics &&
+ !options->DirReqStatistics)
+ geoip_dirreq_stats_term();
+ if (old_options && old_options->EntryStatistics &&
+ !options->EntryStatistics)
+ geoip_entry_stats_term();
+ if (old_options && old_options->HiddenServiceStatistics &&
+ !options->HiddenServiceStatistics)
+ rep_hist_hs_stats_term();
+ if (old_options && old_options->ExitPortStatistics &&
+ !options->ExitPortStatistics)
+ rep_hist_exit_stats_term();
+ if (old_options && old_options->ConnDirectionStatistics &&
+ !options->ConnDirectionStatistics)
+ rep_hist_conn_stats_term();
+
+ return 0;
+}
+
+/** Print a notice about relay/dirauth stats being enabled. */
+void
+options_act_relay_stats_msg(void)
+{
+ log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
+ "the *-stats files that will first be written to the "
+ "data directory in 24 hours from now.");
+}
+
+/** Fetch the active option list, and take relay descriptor actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay_desc(const or_options_t *old_options)
+{
+ const or_options_t *options = get_options();
+
+ /* Since our options changed, we might need to regenerate and upload our
+ * server descriptor.
+ */
+ if (!old_options ||
+ options_transition_affects_descriptor(old_options, options))
+ mark_my_descriptor_dirty("config change");
+
+ return 0;
+}
+
+/** Fetch the active option list, and take relay DoS actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay_dos(const or_options_t *old_options)
+{
+ const or_options_t *options = get_options();
+
+ /* DoS mitigation subsystem only applies to public relay. */
+ if (public_server_mode(options)) {
+ /* If we are configured as a relay, initialize the subsystem. Even on HUP,
+ * this is safe to call as it will load data from the current options
+ * or/and the consensus. */
+ dos_init();
+ } else if (old_options && public_server_mode(old_options)) {
+ /* Going from relay to non relay, clean it up. */
+ dos_free_all();
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take dirport actions based on
+ * it. All of the things we do should survive being done repeatedly. If
+ * present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_relay_dir(const or_options_t *old_options)
+{
+ (void)old_options;
+
+ const or_options_t *options = get_options();
+
+ if (!public_server_mode(options))
+ return 0;
+
+ /* Load the webpage we're going to serve every time someone asks for '/' on
+ our DirPort. */
+ tor_free(global_dirfrontpagecontents);
+ if (options->DirPortFrontPage) {
+ global_dirfrontpagecontents =
+ read_file_to_str(options->DirPortFrontPage, 0, NULL);
+ if (!global_dirfrontpagecontents) {
+ log_warn(LD_CONFIG,
+ "DirPortFrontPage file '%s' not found. Continuing anyway.",
+ options->DirPortFrontPage);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/feature/relay/relay_config.h b/src/feature/relay/relay_config.h
new file mode 100644
index 0000000000..214f07efc2
--- /dev/null
+++ b/src/feature/relay/relay_config.h
@@ -0,0 +1,188 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_config.h
+ * @brief Header for feature/relay/relay_config.c
+ **/
+
+#ifndef TOR_FEATURE_RELAY_RELAY_CONFIG_H
+#define TOR_FEATURE_RELAY_RELAY_CONFIG_H
+
+typedef struct or_options_t or_options_t;
+
+#ifdef HAVE_MODULE_RELAY
+
+#include "lib/cc/torint.h"
+#include "lib/testsupport/testsupport.h"
+
+typedef struct smartlist_t smartlist_t;
+
+int options_validate_relay_mode(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+MOCK_DECL(const char*, relay_get_dirportfrontpage, (void));
+void relay_config_free_all(void);
+
+uint32_t relay_get_effective_bwrate(const or_options_t *options);
+uint32_t relay_get_effective_bwburst(const or_options_t *options);
+
+void port_warn_nonlocal_ext_orports(const smartlist_t *ports,
+ const char *portname);
+
+int port_parse_ports_relay(or_options_t *options,
+ char **msg,
+ smartlist_t *ports_out,
+ int *have_low_ports_out);
+void port_update_port_set_relay(or_options_t *options,
+ const smartlist_t *ports);
+
+int options_validate_relay_os(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_relay_info(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_publish_server(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_relay_padding(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_relay_bandwidth(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_relay_accounting(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_validate_relay_testing(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+int options_act_relay(const or_options_t *old_options);
+int options_act_relay_accounting(const or_options_t *old_options);
+int options_act_relay_bandwidth(const or_options_t *old_options);
+int options_act_bridge_stats(const or_options_t *old_options);
+
+int options_act_relay_stats(const or_options_t *old_options,
+ bool *print_notice_out);
+void options_act_relay_stats_msg(void);
+
+int options_act_relay_desc(const or_options_t *old_options);
+int options_act_relay_dos(const or_options_t *old_options);
+int options_act_relay_dir(const or_options_t *old_options);
+
+#ifdef RELAY_CONFIG_PRIVATE
+
+STATIC int check_bridge_distribution_setting(const char *bd);
+STATIC int have_enough_mem_for_dircache(const or_options_t *options,
+ size_t total_mem, char **msg);
+
+#endif /* defined(RELAY_CONFIG_PRIVATE) */
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#include "lib/cc/compat_compiler.h"
+
+/** When tor is compiled with the relay module disabled, it can't be
+ * configured as a relay or bridge.
+ *
+ * Always sets ClientOnly to 1.
+ *
+ * Returns -1 and sets msg to a newly allocated string, if ORPort, DirPort,
+ * DirCache, or BridgeRelay are set in options. Otherwise returns 0. */
+static inline int
+options_validate_relay_mode(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ /* Only check the primary options for now, #29211 will disable more
+ * options. These ORPort and DirPort checks are too strict, and will
+ * reject valid configs that disable ports, like "ORPort 0". */
+ if (options->DirCache ||
+ options->BridgeRelay ||
+ options->ORPort_lines ||
+ options->DirPort_lines) {
+ /* REJECT() this configuration */
+ *msg = tor_strdup("This tor was built with relay mode disabled. "
+ "It can not be configured with an ORPort, a DirPort, "
+ "DirCache 1, or BridgeRelay 1.");
+ return -1;
+ }
+
+ /* 31851 / 29211: Set this option the correct way */
+ options->ClientOnly = 1;
+
+ return 0;
+}
+
+#define relay_get_dirportfrontpage() \
+ (NULL)
+#define relay_config_free_all() \
+ STMT_BEGIN STMT_END
+
+#define relay_get_effective_bwrate(options) \
+ (((void)(options)),0)
+#define relay_get_effective_bwburst(options) \
+ (((void)(options)),0)
+
+#define port_warn_nonlocal_ext_orports(ports, portname) \
+ (((void)(ports)),((void)(portname)))
+
+#define port_parse_ports_relay(options, msg, ports_out, have_low_ports_out) \
+ (((void)(options)),((void)(msg)),((void)(ports_out)), \
+ ((void)(have_low_ports_out)),0)
+#define port_update_port_set_relay(options, ports) \
+ (((void)(options)),((void)(ports)))
+
+#define options_validate_relay_os(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_relay_info(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_publish_server(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_relay_padding(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_relay_bandwidth(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_relay_accounting(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_validate_relay_testing(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+
+#define options_act_relay(old_options) \
+ (((void)(old_options)),0)
+#define options_act_relay_accounting(old_options) \
+ (((void)(old_options)),0)
+#define options_act_relay_bandwidth(old_options) \
+ (((void)(old_options)),0)
+#define options_act_bridge_stats(old_options) \
+ (((void)(old_options)),0)
+
+#define options_act_relay_stats(old_options, print_notice_out) \
+ (((void)(old_options)),((void)(print_notice_out)),0)
+#define options_act_relay_stats_msg() \
+ STMT_BEGIN STMT_END
+
+#define options_act_relay_desc(old_options) \
+ (((void)(old_options)),0)
+#define options_act_relay_dos(old_options) \
+ (((void)(old_options)),0)
+#define options_act_relay_dir(old_options) \
+ (((void)(old_options)),0)
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
+#endif /* !defined(TOR_FEATURE_RELAY_RELAY_CONFIG_H) */
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index 5c79010934..7f80b288de 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -35,6 +35,7 @@
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/torcert.h"
#include "feature/relay/dns.h"
+#include "feature/relay/relay_config.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
#include "feature/relay/routermode.h"
@@ -1222,7 +1223,7 @@ router_should_be_dirserver(const or_options_t *options, int dir_port)
* much larger effect on output than input so there is no reason to turn it
* off if using AccountingRule in. */
int interval_length = accounting_get_interval_length();
- uint32_t effective_bw = get_effective_bwrate(options);
+ uint32_t effective_bw = relay_get_effective_bwrate(options);
uint64_t acc_bytes;
if (!interval_length) {
log_warn(LD_BUG, "An accounting interval is not allowed to be zero "
@@ -2041,10 +2042,10 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out))
ri->protocol_list = tor_strdup(protover_get_supported_protocols());
/* compute ri->bandwidthrate as the min of various options */
- ri->bandwidthrate = get_effective_bwrate(options);
+ ri->bandwidthrate = relay_get_effective_bwrate(options);
/* and compute ri->bandwidthburst similarly */
- ri->bandwidthburst = get_effective_bwburst(options);
+ ri->bandwidthburst = relay_get_effective_bwburst(options);
/* Report bandwidth, unless we're hibernating or shutting down */
ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
diff --git a/src/feature/relay/transport_config.c b/src/feature/relay/transport_config.c
new file mode 100644
index 0000000000..9d6be4bafd
--- /dev/null
+++ b/src/feature/relay/transport_config.c
@@ -0,0 +1,307 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file transport_config.c
+ * @brief Code to interpret the user's configuration of Tor's server
+ * pluggable transports.
+ **/
+
+#include "orconfig.h"
+#define RELAY_TRANSPORT_CONFIG_PRIVATE
+#include "feature/relay/transport_config.h"
+
+#include "lib/encoding/confline.h"
+#include "lib/encoding/keyval.h"
+
+#include "lib/container/smartlist.h"
+
+/* Required for dirinfo_type_t in or_options_t */
+#include "core/or/or.h"
+#include "app/config/config.h"
+
+#include "feature/relay/ext_orport.h"
+#include "feature/relay/routermode.h"
+
+/* Copied from config.c, we will refactor later in 29211. */
+#define REJECT(arg) \
+ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
+
+/** Given a ServerTransportListenAddr <b>line</b>, return its
+ * <address:port> string. Return NULL if the line was not
+ * well-formed.
+ *
+ * If <b>transport</b> is set, return NULL if the line is not
+ * referring to <b>transport</b>.
+ *
+ * The returned string is allocated on the heap and it's the
+ * responsibility of the caller to free it. */
+static char *
+get_bindaddr_from_transport_listen_line(const char *line,
+ const char *transport)
+{
+ smartlist_t *items = NULL;
+ const char *parsed_transport = NULL;
+ char *addrport = NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+
+ items = smartlist_new();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ if (smartlist_len(items) < 2) {
+ log_warn(LD_CONFIG,"Too few arguments on ServerTransportListenAddr line.");
+ goto err;
+ }
+
+ parsed_transport = smartlist_get(items, 0);
+ addrport = tor_strdup(smartlist_get(items, 1));
+
+ /* If 'transport' is given, check if it matches the one on the line */
+ if (transport && strcmp(transport, parsed_transport))
+ goto err;
+
+ /* Validate addrport */
+ if (tor_addr_port_parse(LOG_WARN, addrport, &addr, &port, -1)<0) {
+ log_warn(LD_CONFIG, "Error parsing ServerTransportListenAddr "
+ "address '%s'", addrport);
+ goto err;
+ }
+
+ goto done;
+
+ err:
+ tor_free(addrport);
+ addrport = NULL;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+
+ return addrport;
+}
+
+/** Given the name of a pluggable transport in <b>transport</b>, check
+ * the configuration file to see if the user has explicitly asked for
+ * it to listen on a specific port. Return a <address:port> string if
+ * so, otherwise NULL. */
+char *
+pt_get_bindaddr_from_config(const char *transport)
+{
+ config_line_t *cl;
+ const or_options_t *options = get_options();
+
+ for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) {
+ char *bindaddr =
+ get_bindaddr_from_transport_listen_line(cl->value, transport);
+ if (bindaddr)
+ return bindaddr;
+ }
+
+ return NULL;
+}
+
+/** Given a ServerTransportOptions <b>line</b>, return a smartlist
+ * with the options. Return NULL if the line was not well-formed.
+ *
+ * If <b>transport</b> is set, return NULL if the line is not
+ * referring to <b>transport</b>.
+ *
+ * The returned smartlist and its strings are allocated on the heap
+ * and it's the responsibility of the caller to free it. */
+STATIC smartlist_t *
+get_options_from_transport_options_line(const char *line,
+ const char *transport)
+{
+ smartlist_t *items = smartlist_new();
+ smartlist_t *pt_options = smartlist_new();
+ const char *parsed_transport = NULL;
+
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ if (smartlist_len(items) < 2) {
+ log_warn(LD_CONFIG,"Too few arguments on ServerTransportOptions line.");
+ goto err;
+ }
+
+ parsed_transport = smartlist_get(items, 0);
+ /* If 'transport' is given, check if it matches the one on the line */
+ if (transport && strcmp(transport, parsed_transport))
+ goto err;
+
+ SMARTLIST_FOREACH_BEGIN(items, const char *, option) {
+ if (option_sl_idx == 0) /* skip the transport field (first field)*/
+ continue;
+
+ /* validate that it's a k=v value */
+ if (!string_is_key_value(LOG_WARN, option)) {
+ log_warn(LD_CONFIG, "%s is not a k=v value.", escaped(option));
+ goto err;
+ }
+
+ /* add it to the options smartlist */
+ smartlist_add_strdup(pt_options, option);
+ log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option));
+ } SMARTLIST_FOREACH_END(option);
+
+ goto done;
+
+ err:
+ SMARTLIST_FOREACH(pt_options, char*, s, tor_free(s));
+ smartlist_free(pt_options);
+ pt_options = NULL;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+
+ return pt_options;
+}
+
+/** Given the name of a pluggable transport in <b>transport</b>, check
+ * the configuration file to see if the user has asked us to pass any
+ * parameters to the pluggable transport. Return a smartlist
+ * containing the parameters, otherwise NULL. */
+smartlist_t *
+pt_get_options_for_server_transport(const char *transport)
+{
+ config_line_t *cl;
+ const or_options_t *options = get_options();
+
+ for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
+ smartlist_t *options_sl =
+ get_options_from_transport_options_line(cl->value, transport);
+ if (options_sl)
+ return options_sl;
+ }
+
+ return NULL;
+}
+
+/**
+ * Legacy validation/normalization function for the server transport options.
+ * Uses old_options as the previous options.
+ *
+ * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
+ * on error.
+ */
+int
+options_validate_server_transport(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ if (BUG(!options))
+ return -1;
+
+ if (BUG(!msg))
+ return -1;
+
+ config_line_t *cl;
+
+ if (options->ServerTransportPlugin && !server_mode(options)) {
+ log_notice(LD_GENERAL, "Tor is not configured as a relay but you specified"
+ " a ServerTransportPlugin line (%s). The ServerTransportPlugin "
+ "line will be ignored.",
+ escaped(options->ServerTransportPlugin->value));
+ }
+
+ if (options->ServerTransportListenAddr && !options->ServerTransportPlugin) {
+ log_notice(LD_GENERAL, "You need at least a single managed-proxy to "
+ "specify a transport listen address. The "
+ "ServerTransportListenAddr line will be ignored.");
+ }
+
+ for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
+ if (pt_parse_transport_line(options, cl->value, 1, 1) < 0)
+ REJECT("Invalid server transport line. See logs for details.");
+ }
+
+ for (cl = options->ServerTransportListenAddr; cl; cl = cl->next) {
+ /** If get_bindaddr_from_transport_listen_line() fails with
+ 'transport' being NULL, it means that something went wrong
+ while parsing the ServerTransportListenAddr line. */
+ char *bindaddr = get_bindaddr_from_transport_listen_line(cl->value, NULL);
+ if (!bindaddr)
+ REJECT("ServerTransportListenAddr did not parse. See logs for details.");
+ tor_free(bindaddr);
+ }
+
+ for (cl = options->ServerTransportOptions; cl; cl = cl->next) {
+ /** If get_options_from_transport_options_line() fails with
+ 'transport' being NULL, it means that something went wrong
+ while parsing the ServerTransportOptions line. */
+ smartlist_t *options_sl =
+ get_options_from_transport_options_line(cl->value, NULL);
+ if (!options_sl)
+ REJECT("ServerTransportOptions did not parse. See logs for details.");
+
+ SMARTLIST_FOREACH(options_sl, char *, cp, tor_free(cp));
+ smartlist_free(options_sl);
+ }
+
+ return 0;
+}
+
+/** Fetch the active option list, and take server pluggable transport actions
+ * based on it. All of the things we do should survive being done repeatedly.
+ * If present, <b>old_options</b> contains the previous value of the options.
+ *
+ * Return 0 if all goes well, return -1 if it's time to die.
+ *
+ * Note: We haven't moved all the "act on new configuration" logic
+ * into the options_act* functions yet. Some is still in do_hup() and other
+ * places.
+ */
+int
+options_act_server_transport(const or_options_t *old_options)
+{
+ (void)old_options;
+
+ config_line_t *cl;
+ const or_options_t *options = get_options();
+ int running_tor = options->command == CMD_RUN_TOR;
+
+ /* If we are a bridge with a pluggable transport proxy but no
+ Extended ORPort, inform the user that they are missing out. */
+ if (options->ServerTransportPlugin &&
+ !options->ExtORPort_lines) {
+ log_notice(LD_CONFIG, "We use pluggable transports but the Extended "
+ "ORPort is disabled. Tor and your pluggable transports proxy "
+ "communicate with each other via the Extended ORPort so it "
+ "is suggested you enable it: it will also allow your Bridge "
+ "to collect statistics about its clients that use pluggable "
+ "transports. Please enable it using the ExtORPort torrc option "
+ "(e.g. set 'ExtORPort auto').");
+ }
+
+ /* If we have an ExtORPort, initialize its auth cookie. */
+ if (running_tor &&
+ init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
+ log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
+ return -1;
+ }
+
+ if (!options->DisableNetwork) {
+ if (options->ServerTransportPlugin) {
+ for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
+ if (pt_parse_transport_line(options, cl->value, 0, 1) < 0) {
+ // LCOV_EXCL_START
+ log_warn(LD_BUG,
+ "Previously validated ServerTransportPlugin line "
+ "could not be added!");
+ return -1;
+ // LCOV_EXCL_STOP
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/feature/relay/transport_config.h b/src/feature/relay/transport_config.h
new file mode 100644
index 0000000000..d3cceb3698
--- /dev/null
+++ b/src/feature/relay/transport_config.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file transport_config.h
+ * @brief Header for feature/relay/transport_config.c
+ **/
+
+#ifndef TOR_FEATURE_RELAY_TRANSPORT_CONFIG_H
+#define TOR_FEATURE_RELAY_TRANSPORT_CONFIG_H
+
+#ifdef HAVE_MODULE_RELAY
+
+#include "lib/testsupport/testsupport.h"
+
+typedef struct or_options_t or_options_t;
+typedef struct smartlist_t smartlist_t;
+
+int options_validate_server_transport(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg);
+
+char *pt_get_bindaddr_from_config(const char *transport);
+smartlist_t *pt_get_options_for_server_transport(const char *transport);
+
+int options_act_server_transport(const or_options_t *old_options);
+
+#ifdef RELAY_TRANSPORT_CONFIG_PRIVATE
+
+STATIC smartlist_t *get_options_from_transport_options_line(
+ const char *line,
+ const char *transport);
+
+#endif /* defined(RELAY_TRANSPORT_CONFIG_PRIVATE) */
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+/** When tor is compiled with the relay module disabled, it can't be
+ * configured with server pluggable transports.
+ *
+ * Returns -1 and sets msg to a newly allocated string, if ExtORPort,
+ * ServerTransportPlugin, ServerTransportListenAddr, or
+ * ServerTransportOptions are set in options. Otherwise returns 0. */
+static inline int
+options_validate_server_transport(const or_options_t *old_options,
+ or_options_t *options,
+ char **msg)
+{
+ (void)old_options;
+
+ /* These ExtORPort checks are too strict, and will reject valid configs
+ * that disable ports, like "ExtORPort 0". */
+ if (options->ServerTransportPlugin ||
+ options->ServerTransportListenAddr ||
+ options->ServerTransportOptions ||
+ options->ExtORPort_lines) {
+ /* REJECT() this configuration */
+ *msg = tor_strdup("This tor was built with relay mode disabled. "
+ "It can not be configured with an ExtORPort, "
+ "a ServerTransportPlugin, a ServerTransportListenAddr, "
+ "or ServerTransportOptions.");
+ return -1;
+ }
+
+ return 0;
+}
+
+#define pt_get_bindaddr_from_config(transport) \
+ (((void)(transport)),NULL)
+
+/* 31851: called from client/transports.c, but only from server code */
+#define pt_get_options_for_server_transport(transport) \
+ (((void)(transport)),NULL)
+
+#define options_validate_server_transport(old_options, options, msg) \
+ (((void)(old_options)),((void)(options)),((void)(msg)),0)
+#define options_act_server_transport(old_options) \
+ (((void)(old_options)),0)
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
+#endif /* !defined(TOR_FEATURE_RELAY_TRANSPORT_CONFIG_H) */