diff options
Diffstat (limited to 'src/feature/relay/relay_config.c')
-rw-r--r-- | src/feature/relay/relay_config.c | 222 |
1 files changed, 214 insertions, 8 deletions
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index fac6a2f577..c4a5d7f572 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -36,6 +36,7 @@ #include "feature/nodelist/nickname.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" #include "feature/dirauth/authmode.h" @@ -132,12 +133,208 @@ port_warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname) } SMARTLIST_FOREACH_END(port); } +/** + * Return a static buffer describing the port number in @a port, which may + * CFG_AUTO_PORT. + **/ +static const char * +describe_portnum(int port) +{ + static char buf[16]; + if (port == CFG_AUTO_PORT) { + return "auto"; + } else { + tor_snprintf(buf, sizeof(buf), "%d", port); + return buf; + } +} + +/** Return a static buffer containing the human readable logging string that + * describes the given port object. */ +STATIC const char * +describe_relay_port(const port_cfg_t *port) +{ + IF_BUG_ONCE(!port) { + return "<null port>"; + } + + static char buf[256]; + const char *type, *addr; + + switch (port->type) { + case CONN_TYPE_OR_LISTENER: + type = "OR"; + break; + case CONN_TYPE_DIR_LISTENER: + type = "Dir"; + break; + case CONN_TYPE_EXT_OR_LISTENER: + type = "ExtOR"; + break; + default: + type = ""; + break; + } + + if (port->explicit_addr) { + addr = fmt_and_decorate_addr(&port->addr); + } else { + addr = ""; + } + + tor_snprintf(buf, sizeof(buf), "%sPort %s%s%s", + type, addr, (strlen(addr) > 0) ? ":" : "", + describe_portnum(port->port)); + return buf; +} + +/** Return true iff port p1 is equal to p2. + * + * This does a field by field comparaison. */ +static bool +port_cfg_eq(const port_cfg_t *p1, const port_cfg_t *p2) +{ + bool ret = true; + + tor_assert(p1); + tor_assert(p2); + + /* Address, port and type. */ + ret &= tor_addr_eq(&p1->addr, &p2->addr); + ret &= (p1->port == p2->port); + ret &= (p1->type == p2->type); + + /* Mode. */ + ret &= (p1->is_unix_addr == p2->is_unix_addr); + ret &= (p1->is_group_writable == p2->is_group_writable); + ret &= (p1->is_world_writable == p2->is_world_writable); + ret &= (p1->relax_dirmode_check == p2->relax_dirmode_check); + ret &= (p1->explicit_addr == p2->explicit_addr); + + /* Entry config flags. */ + ret &= tor_memeq(&p1->entry_cfg, &p2->entry_cfg, + sizeof(entry_port_cfg_t)); + /* Server config flags. */ + ret &= tor_memeq(&p1->server_cfg, &p2->server_cfg, + sizeof(server_port_cfg_t)); + /* Unix address path if any. */ + ret &= !strcmp(p1->unix_addr, p2->unix_addr); + + return ret; +} + +/** Attempt to find duplicate ORPort that would be superseded by another and + * remove them from the given ports list. This is possible if we have for + * instance: + * + * ORPort 9050 + * ORPort [4242::1]:9050 + * + * First one binds to both v4 and v6 address but second one is specific to an + * address superseding the global bind one. + * + * Another example is this one: + * + * ORPort 9001 + * ORPort [4242::1]:9002 + * ORPort [4242::2]:9003 + * + * In this case, all IPv4 and IPv6 are kept since we do allow multiple ORPorts + * but the published port will be the first explicit one if any to be + * published or else the implicit. + * + * The following is O(n^2) but it is done at bootstrap or config reload and + * the list is not very long usually. */ +STATIC void +remove_duplicate_orports(smartlist_t *ports) +{ + /* First we'll decide what to remove, then we'll remove it. */ + bool *removing = tor_calloc(smartlist_len(ports), sizeof(bool)); + + for (int i = 0; i < smartlist_len(ports); ++i) { + const port_cfg_t *current = smartlist_get(ports, i); + if (removing[i]) { + continue; + } + + /* Skip non ORPorts. */ + if (current->type != CONN_TYPE_OR_LISTENER) { + continue; + } + + for (int j = 0; j < smartlist_len(ports); ++j) { + const port_cfg_t *next = smartlist_get(ports, j); + + /* Avoid comparing the same object. */ + if (current == next) { + continue; + } + if (removing[j]) { + continue; + } + /* Skip non ORPorts. */ + if (next->type != CONN_TYPE_OR_LISTENER) { + continue; + } + /* Remove duplicates. */ + if (port_cfg_eq(current, next)) { + removing[j] = true; + continue; + } + /* Don't compare addresses of different family. */ + if (tor_addr_family(¤t->addr) != tor_addr_family(&next->addr)) { + continue; + } + /* At this point, we have a port of the same type and same address + * family. Now, we want to avoid comparing addresses that are different + * but are both explicit. As an example, these are not duplicates: + * + * ORPort 127.0.0.:9001 NoAdvertise + * ORPort 1.2.3.4:9001 NoListen + * + * Any implicit address must be considered for removal since an explicit + * one will always supersedes it. */ + if (!tor_addr_eq(¤t->addr, &next->addr) && + current->explicit_addr && next->explicit_addr) { + continue; + } + + /* Port value is the same so we either have a duplicate or a port that + * supersedes another. */ + if (current->port == next->port) { + /* Do not remove the explicit address. As stated before above, we keep + * explicit addresses which supersedes implicit ones. */ + if (!current->explicit_addr && next->explicit_addr) { + continue; + } + removing[j] = true; + char *next_str = tor_strdup(describe_relay_port(next)); + log_warn(LD_CONFIG, "Configuration port %s superseded by %s", + next_str, describe_relay_port(current)); + tor_free(next_str); + } + } + } + + /* Iterate over array in reverse order to keep indices valid. */ + for (int i = smartlist_len(ports)-1; i >= 0; --i) { + tor_assert(i < smartlist_len(ports)); + if (removing[i]) { + port_cfg_t *current = smartlist_get(ports, i); + smartlist_del_keeporder(ports, i); + port_cfg_free(current); + } + } + + tor_free(removing); +} + /** 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, +check_and_prune_server_ports(smartlist_t *ports, const or_options_t *options, int *n_low_ports_out) { @@ -158,6 +355,9 @@ check_server_ports(const smartlist_t *ports, int n_low_port = 0; int r = 0; + /* Remove possible duplicate ORPorts before inspecting the list. */ + remove_duplicate_orports(ports); + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { if (port->type == CONN_TYPE_DIR_LISTENER) { if (! port->server_cfg.no_advertise) @@ -270,6 +470,14 @@ port_parse_ports_relay(or_options_t *options, goto err; } if (port_parse_config(ports, + options->ORPort_lines, + "OR", CONN_TYPE_OR_LISTENER, + "[::]", 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, @@ -286,7 +494,7 @@ port_parse_ports_relay(or_options_t *options, goto err; } - if (check_server_ports(ports, options, &n_low_ports) < 0) { + if (check_and_prune_server_ports(ports, options, &n_low_ports) < 0) { *msg = tor_strdup("Misconfigured server ports"); goto err; } @@ -904,7 +1112,7 @@ options_validate_relay_mode(const or_options_t *old_options, "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"); + "https://bugs.torproject.org/tpo/core/tor/8742."); if (options->BridgeRelay && options->DirPort_set) { log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " @@ -1029,7 +1237,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, YES_IF_CHANGED_STRING(DataDirectory); YES_IF_CHANGED_STRING(Nickname); - YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(Address); YES_IF_CHANGED_LINELIST(ExitPolicy); YES_IF_CHANGED_BOOL(ExitRelay); YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); @@ -1114,8 +1322,6 @@ options_act_relay(const or_options_t *old_options) 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(); } @@ -1309,7 +1515,7 @@ options_act_relay_stats(const or_options_t *old_options, } if ((!old_options || !old_options->ConnDirectionStatistics) && options->ConnDirectionStatistics) { - rep_hist_conn_stats_init(now); + conn_stats_init(now); } if ((!old_options || !old_options->HiddenServiceStatistics) && options->HiddenServiceStatistics) { @@ -1339,7 +1545,7 @@ options_act_relay_stats(const or_options_t *old_options, rep_hist_exit_stats_term(); if (old_options && old_options->ConnDirectionStatistics && !options->ConnDirectionStatistics) - rep_hist_conn_stats_term(); + conn_stats_terminate(); return 0; } |