mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-04 17:43:31 +01:00
1445 lines
47 KiB
C
1445 lines
47 KiB
C
/* Copyright (c) 2001 Matej Pfajfar.
|
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
* Copyright (c) 2007-2020, The Tor Project, Inc. */
|
|
/* See LICENSE for licensing information */
|
|
|
|
/**
|
|
* @file relay_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/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"
|
|
#include "feature/relay/selftest.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 newly
|
|
* allocated string describing 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. Does not set *<b>msg</b>.
|
|
* 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_warn(LD_CONFIG,
|
|
"Your ContactInfo config option is not set. Please strongly "
|
|
"consider setting it, so we can contact you if your relay is "
|
|
"misconfigured, end-of-life, or something else goes wrong. "
|
|
"It is also possible that your relay might get rejected from "
|
|
"the network due to a missing valid contact address.");
|
|
}
|
|
|
|
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"
|
|
*
|
|
* 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"
|
|
};
|
|
unsigned i;
|
|
for (i = 0; i < ARRAY_LENGTH(RECOGNIZED); ++i) {
|
|
if (!strcasecmp(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;
|
|
}
|