#define RESOLVE_ADDR_PRIVATE
#include "app/config/config.h"
#include "app/config/resolve_addr.h"
#include "core/mainloop/mainloop.h"
#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
#include "lib/encoding/confline.h"
#include "lib/net/gethostname.h"
#include "lib/net/resolve.h"
#define MAX_CONFIG_ADDRESS 2
#define IDX_NULL 0
#define IDX_IPV4 1
#define IDX_IPV6 2
#define IDX_SIZE 3
typedef enum {
FN_RET_OK = 0,
FN_RET_BAIL = 1,
FN_RET_NEXT = 2,
} fn_address_ret_t;
static tor_addr_t last_resolved_addrs[] =
{ TOR_ADDR_NULL, TOR_ADDR_NULL, TOR_ADDR_NULL };
CTASSERT(ARRAY_LENGTH(last_resolved_addrs) == IDX_SIZE);
static tor_addr_t last_suggested_addrs[] =
{ TOR_ADDR_NULL, TOR_ADDR_NULL, TOR_ADDR_NULL };
CTASSERT(ARRAY_LENGTH(last_suggested_addrs) == IDX_SIZE);
static bool last_addrs_configured[] = { false, false, false };
CTASSERT(ARRAY_LENGTH(last_addrs_configured) == IDX_SIZE);
static inline int
af_to_idx(const int family)
{
switch (family) {
case AF_INET:
return IDX_IPV4;
case AF_INET6:
return IDX_IPV6;
default:
tor_assert_nonfatal_unreached();
return IDX_NULL;
}
}
const char *
resolved_addr_method_to_str(const resolved_addr_method_t method)
{
switch (method) {
case RESOLVED_ADDR_NONE:
return "NONE";
case RESOLVED_ADDR_CONFIGURED:
return "CONFIGURED";
case RESOLVED_ADDR_CONFIGURED_ORPORT:
return "CONFIGURED_ORPORT";
case RESOLVED_ADDR_GETHOSTNAME:
return "GETHOSTNAME";
case RESOLVED_ADDR_INTERFACE:
return "INTERFACE";
case RESOLVED_ADDR_RESOLVED:
return "RESOLVED";
default:
tor_assert_nonfatal_unreached();
return "???";
}
}
bool
resolved_addr_is_configured(int family)
{
return last_addrs_configured[af_to_idx(family)];
}
void
resolved_addr_get_suggested(int family, tor_addr_t *addr_out)
{
tor_addr_copy(addr_out, &last_suggested_addrs[af_to_idx(family)]);
}
void
resolved_addr_set_suggested(const tor_addr_t *addr)
{
if (BUG(tor_addr_family(addr) != AF_INET &&
tor_addr_family(addr) != AF_INET6)) {
return;
}
const int idx = af_to_idx(tor_addr_family(addr));
if (tor_addr_is_null(&last_resolved_addrs[idx]) &&
!tor_addr_eq(&last_suggested_addrs[idx], addr)) {
log_notice(LD_CONFIG, "External address seen and suggested by a "
"directory authority: %s", fmt_addr(addr));
}
tor_addr_copy(&last_suggested_addrs[idx], addr);
}
void
resolved_addr_get_last(int family, tor_addr_t *addr_out)
{
tor_addr_copy(addr_out, &last_resolved_addrs[af_to_idx(family)]);
}
void
resolved_addr_reset_last(int family)
{
tor_addr_make_null(&last_resolved_addrs[af_to_idx(family)], family);
}
#define ERR_DEFAULT_DIRAUTH -1
#define ERR_ADDRESS_IS_INTERNAL -2
static int
address_can_be_used(const tor_addr_t *addr, const or_options_t *options,
int warn_severity, const bool explicit_ip)
{
tor_assert(addr);
if (!tor_addr_is_internal(addr, 0)) {
goto allow;
}
if (options->PublishServerDescriptor_ == NO_DIRINFO &&
(options->AssumeReachable ||
(tor_addr_family(addr) == AF_INET6 && options->AssumeReachableIPv6))) {
goto allow;
}
if (using_default_dir_authorities(options)) {
log_fn(warn_severity, LD_CONFIG,
"Address '%s' is a private IP address. Tor relays that use "
"the default DirAuthorities must have public IP addresses.",
fmt_addr(addr));
return ERR_DEFAULT_DIRAUTH;
}
if (!explicit_ip) {
log_fn(warn_severity, LD_CONFIG,
"Address %s was resolved and thus not explicitly "
"set. Even if DirAuthorities are custom, this is "
"not allowed.", fmt_addr(addr));
return ERR_ADDRESS_IS_INTERNAL;
}
allow:
return 0;
}
static fn_address_ret_t
get_address_from_config(const or_options_t *options, int warn_severity,
int family, resolved_addr_method_t *method_out,
char **hostname_out, tor_addr_t *addr_out)
{
int ret;
bool explicit_ip = false, resolve_failure = false;
int num_valid_addr = 0;
tor_assert(options);
tor_assert(addr_out);
tor_assert(method_out);
tor_assert(hostname_out);
*hostname_out = NULL;
*method_out = RESOLVED_ADDR_NONE;
log_debug(LD_CONFIG, "Attempting to get address from configuration");
if (!options->Address) {
log_info(LD_CONFIG, "No Address option found in configuration.");
return FN_RET_NEXT;
}
for (const config_line_t *cfg = options->Address; cfg != NULL;
cfg = cfg->next) {
int af;
tor_addr_t addr;
af = tor_addr_parse(&addr, cfg->value);
if (af == family) {
tor_addr_copy(addr_out, &addr);
*method_out = RESOLVED_ADDR_CONFIGURED;
explicit_ip = true;
num_valid_addr++;
continue;
} else if (af != -1) {
continue;
}
if (!tor_addr_lookup(cfg->value, family, &addr)) {
tor_addr_copy(addr_out, &addr);
*method_out = RESOLVED_ADDR_RESOLVED;
if (*hostname_out) {
tor_free(*hostname_out);
}
*hostname_out = tor_strdup(cfg->value);
explicit_ip = false;
num_valid_addr++;
continue;
} else {
resolve_failure = true;
log_fn(warn_severity, LD_CONFIG,
"Could not resolve local Address '%s'. Failing.", cfg->value);
continue;
}
}
if (!num_valid_addr) {
if (resolve_failure) {
return FN_RET_BAIL;
}
log_info(LD_CONFIG,
"No Address option found for family %s in configuration.",
fmt_af_family(family));
return FN_RET_NEXT;
}
if (num_valid_addr >= MAX_CONFIG_ADDRESS) {
log_fn(warn_severity, LD_CONFIG,
"Found %d Address statement of address family %s. "
"Only one is allowed.", num_valid_addr, fmt_af_family(family));
tor_free(*hostname_out);
return FN_RET_BAIL;
}
ret = address_can_be_used(addr_out, options, warn_severity, explicit_ip);
if (ret != 0) {
tor_free(*hostname_out);
return FN_RET_BAIL;
}
log_info(LD_CONFIG, "Address found in configuration: %s",
fmt_addr(addr_out));
return FN_RET_OK;
}
static fn_address_ret_t
get_address_from_hostname(const or_options_t *options, int warn_severity,
int family, resolved_addr_method_t *method_out,
char **hostname_out, tor_addr_t *addr_out)
{
int ret;
char hostname[256];
tor_assert(addr_out);
tor_assert(method_out);
*hostname_out = NULL;
*method_out = RESOLVED_ADDR_NONE;
log_debug(LD_CONFIG, "Attempting to get address from local hostname");
if (tor_gethostname(hostname, sizeof(hostname)) < 0) {
log_fn(warn_severity, LD_NET, "Error obtaining local hostname");
return FN_RET_BAIL;
}
if (tor_addr_lookup(hostname, family, addr_out)) {
log_fn(warn_severity, LD_NET,
"Could not resolve local hostname '%s'. Failing.", hostname);
return FN_RET_NEXT;
}
ret = address_can_be_used(addr_out, options, warn_severity, false);
if (ret == ERR_DEFAULT_DIRAUTH) {
return FN_RET_NEXT;
} else if (ret == ERR_ADDRESS_IS_INTERNAL) {
return FN_RET_BAIL;
}
*method_out = RESOLVED_ADDR_GETHOSTNAME;
*hostname_out = tor_strdup(hostname);
log_info(LD_CONFIG, "Address found from local hostname: %s",
fmt_addr(addr_out));
return FN_RET_OK;
}
static fn_address_ret_t
get_address_from_interface(const or_options_t *options, int warn_severity,
int family, resolved_addr_method_t *method_out,
char **hostname_out, tor_addr_t *addr_out)
{
int ret;
tor_assert(method_out);
tor_assert(hostname_out);
tor_assert(addr_out);
*method_out = RESOLVED_ADDR_NONE;
*hostname_out = NULL;
log_debug(LD_CONFIG, "Attempting to get address from network interface");
if (get_interface_address6(warn_severity, family, addr_out) < 0) {
log_fn(warn_severity, LD_CONFIG,
"Could not get local interface IP address.");
return FN_RET_NEXT;
}
ret = address_can_be_used(addr_out, options, warn_severity, false);
if (ret < 0) {
return FN_RET_NEXT;
}
*method_out = RESOLVED_ADDR_INTERFACE;
log_info(LD_CONFIG, "Address found from interface: %s", fmt_addr(addr_out));
return FN_RET_OK;
}
static fn_address_ret_t
get_address_from_orport(const or_options_t *options, int warn_severity,
int family, resolved_addr_method_t *method_out,
char **hostname_out, tor_addr_t *addr_out)
{
int ret;
const tor_addr_t *addr;
tor_assert(method_out);
tor_assert(hostname_out);
tor_assert(addr_out);
*method_out = RESOLVED_ADDR_NONE;
*hostname_out = NULL;
log_debug(LD_CONFIG, "Attempting to get address from ORPort");
if (!options->ORPort_set) {
log_info(LD_CONFIG, "No ORPort found in configuration.");
return FN_RET_NEXT;
}
addr = get_orport_addr(family);
if (!addr) {
return FN_RET_NEXT;
}
ret = address_can_be_used(addr, options, warn_severity, true);
if (ret < 0) {
return FN_RET_NEXT;
}
*method_out = RESOLVED_ADDR_CONFIGURED_ORPORT;
tor_addr_copy(addr_out, addr);
log_fn(warn_severity, LD_CONFIG, "Address found from ORPort: %s",
fmt_addr(addr_out));
return FN_RET_OK;
}
void
resolved_addr_set_last(const tor_addr_t *addr,
const resolved_addr_method_t method_used,
const char *hostname_used)
{
static bool have_resolved_once[] = { false, false, false };
CTASSERT(ARRAY_LENGTH(have_resolved_once) == IDX_SIZE);
bool *done_one_resolve;
bool have_hostname = false;
tor_addr_t *last_resolved;
tor_assert(addr);
have_hostname = (hostname_used != NULL);
int idx = af_to_idx(tor_addr_family(addr));
if (idx == IDX_NULL) {
return;
}
done_one_resolve = &have_resolved_once[idx];
last_resolved = &last_resolved_addrs[idx];
if (tor_addr_eq(last_resolved, addr)) {
return;
}
if (*done_one_resolve) {
log_notice(LD_NET,
"Your IP address seems to have changed to %s "
"(METHOD=%s%s%s). Updating.",
fmt_addr(addr),
resolved_addr_method_to_str(method_used),
have_hostname ? " HOSTNAME=" : "",
have_hostname ? hostname_used : "");
ip_address_changed(0);
}
control_event_server_status(LOG_NOTICE,
"EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s",
fmt_addr(addr),
resolved_addr_method_to_str(method_used),
have_hostname ? " HOSTNAME=" : "",
have_hostname ? hostname_used : "");
tor_addr_copy(last_resolved, addr);
*done_one_resolve = true;
last_addrs_configured[idx] = false;
if (method_used == RESOLVED_ADDR_CONFIGURED ||
method_used == RESOLVED_ADDR_CONFIGURED_ORPORT) {
last_addrs_configured[idx] = true;
}
}
typedef fn_address_ret_t
(*fn_address_t)(
const or_options_t *options, int warn_severity, int family,
resolved_addr_method_t *method_out, char **hostname_out,
tor_addr_t *addr_out);
static const fn_address_t fn_address_table[] =
{
get_address_from_config,
get_address_from_orport,
get_address_from_interface,
get_address_from_hostname,
};
static const size_t fn_address_table_len =
ARRAY_LENGTH(fn_address_table);
static const fn_address_t fn_address_table_auth[] =
{
get_address_from_config,
get_address_from_orport,
};
static const size_t fn_address_table_auth_len =
ARRAY_LENGTH(fn_address_table_auth);
bool
find_my_address(const or_options_t *options, int family, int warn_severity,
tor_addr_t *addr_out, resolved_addr_method_t *method_out,
char **hostname_out)
{
resolved_addr_method_t method_used = RESOLVED_ADDR_NONE;
char *hostname_used = NULL;
tor_addr_t my_addr;
const fn_address_t *table = fn_address_table;
size_t table_len = fn_address_table_len;
tor_assert(options);
tor_assert(addr_out);
tor_addr_make_unspec(addr_out);
if (method_out) *method_out = RESOLVED_ADDR_NONE;
if (hostname_out) *hostname_out = NULL;
if (family == AF_INET6 && options->AddressDisableIPv6) {
return false;
}
if (authdir_mode(options)) {
table = fn_address_table_auth;
table_len = fn_address_table_auth_len;
}
for (size_t idx = 0; idx < table_len; idx++) {
fn_address_ret_t ret = table[idx](options, warn_severity, family,
&method_used, &hostname_used, &my_addr);
if (ret == FN_RET_BAIL) {
return false;
} else if (ret == FN_RET_OK) {
goto found;
}
tor_assert(ret == FN_RET_NEXT);
}
log_fn(warn_severity, LD_CONFIG, "Unable to find our IP address.");
return false;
found:
resolved_addr_set_last(&my_addr, method_used, hostname_used);
if (method_out) {
*method_out = method_used;
}
if (hostname_out) {
*hostname_out = hostname_used;
} else {
tor_free(hostname_used);
}
tor_addr_copy(addr_out, &my_addr);
return true;
}
MOCK_IMPL(bool,
is_local_to_resolve_addr, (const tor_addr_t *addr))
{
const int family = tor_addr_family(addr);
const tor_addr_t *last_resolved_addr =
&last_resolved_addrs[af_to_idx(family)];
if (tor_addr_is_internal(addr, 0)) {
return true;
}
if (get_options()->EnforceDistinctSubnets == 0) {
return false;
}
switch (family) {
case AF_INET:
return tor_addr_compare_masked(addr, last_resolved_addr, 24,
CMP_SEMANTIC) == 0;
case AF_INET6:
return tor_addr_compare_masked(addr, last_resolved_addr, 48,
CMP_SEMANTIC) == 0;
break;
default:
return false;
}
}
#ifdef TOR_UNIT_TESTS
void
resolve_addr_reset_suggested(int family)
{
tor_addr_make_unspec(&last_suggested_addrs[af_to_idx(family)]);
}
#endif