📄 dns.c
字号:
} else {
log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
safe_str(escaped_address),
escaped_safe_str(answer_buf));
}
tor_free(escaped_address);
} else if (type == DNS_PTR && count) {
char *escaped_address;
is_reverse = 1;
hostname = ((char**)addresses)[0];
status = DNS_RESOLVE_SUCCEEDED;
escaped_address = esc_for_log(string_address);
log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
safe_str(escaped_address),
escaped_safe_str(hostname));
tor_free(escaped_address);
} else if (count) {
log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.",
escaped_safe_str(string_address));
} else {
log_warn(LD_BUG, "eventdns returned no addresses or error for %s!",
escaped_safe_str(string_address));
}
} else {
if (evdns_err_is_transient(result))
status = DNS_RESOLVE_FAILED_TRANSIENT;
}
if (was_wildcarded) {
if (is_test_address(string_address)) {
/* Ick. We're getting redirected on known-good addresses. Our DNS
* server must really hate us. */
add_wildcarded_test_address(string_address);
}
}
if (result != DNS_ERR_SHUTDOWN)
dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
tor_free(string_address);
}
/** For eventdns: start resolving as necessary to find the target for
* <b>exitconn</b>. Returns -1 on error, -2 on transient error,
* 0 on "resolve launched." */
static int
launch_resolve(edge_connection_t *exitconn)
{
char *addr = tor_strdup(exitconn->_base.address);
struct in_addr in;
int r;
int options = get_options()->ServerDNSSearchDomains ? 0
: DNS_QUERY_NO_SEARCH;
/* What? Nameservers not configured? Sounds like a bug. */
if (!nameservers_configured) {
log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve "
"launched. Configuring.");
if (configure_nameservers(1) < 0)
return -1;
}
r = parse_inaddr_arpa_address(exitconn->_base.address, &in);
if (r == 0) {
log_info(LD_EXIT, "Launching eventdns request for %s",
escaped_safe_str(exitconn->_base.address));
r = evdns_resolve_ipv4(exitconn->_base.address, options,
evdns_callback, addr);
} else if (r == 1) {
log_info(LD_EXIT, "Launching eventdns reverse request for %s",
escaped_safe_str(exitconn->_base.address));
r = evdns_resolve_reverse(&in, DNS_QUERY_NO_SEARCH,
evdns_callback, addr);
} else if (r == -1) {
log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here.");
}
if (r) {
log_warn(LD_EXIT, "eventdns rejected address %s: error %d.",
escaped_safe_str(addr), r);
r = evdns_err_is_transient(r) ? -2 : -1;
tor_free(addr); /* There is no evdns request in progress; stop
* addr from getting leaked. */
}
return r;
}
/** How many requests for bogus addresses have we launched so far? */
static int n_wildcard_requests = 0;
/** Map from dotted-quad IP address in response to an int holding how many
* times we've seen it for a randomly generated (hopefully bogus) address. It
* would be easier to use definitely-invalid addresses (as specified by
* RFC2606), but see comment in dns_launch_wildcard_checks(). */
static strmap_t *dns_wildcard_response_count = NULL;
/** If present, a list of dotted-quad IP addresses that we are pretty sure our
* nameserver wants to return in response to requests for nonexistent domains.
*/
static smartlist_t *dns_wildcard_list = NULL;
/** True iff we've logged about a single address getting wildcarded.
* Subsequent warnings will be less severe. */
static int dns_wildcard_one_notice_given = 0;
/** True iff we've warned that our DNS server is wildcarding too many failures.
*/
static int dns_wildcard_notice_given = 0;
/** List of supposedly good addresses that are getting wildcarded to the
* same addresses as nonexistent addresses. */
static smartlist_t *dns_wildcarded_test_address_list = NULL;
/** True iff we've warned about a test address getting wildcarded */
static int dns_wildcarded_test_address_notice_given = 0;
/** True iff all addresses seem to be getting wildcarded. */
static int dns_is_completely_invalid = 0;
/** Called when we see <b>id</b> (a dotted quad) in response to a request for
* a hopefully bogus address. */
static void
wildcard_increment_answer(const char *id)
{
int *ip;
if (!dns_wildcard_response_count)
dns_wildcard_response_count = strmap_new();
ip = strmap_get(dns_wildcard_response_count, id); // may be null (0)
if (!ip) {
ip = tor_malloc_zero(sizeof(int));
strmap_set(dns_wildcard_response_count, id, ip);
}
++*ip;
if (*ip > 5 && n_wildcard_requests > 10) {
if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
if (!smartlist_string_isin(dns_wildcard_list, id)) {
log(dns_wildcard_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
"Your DNS provider has given \"%s\" as an answer for %d different "
"invalid addresses. Apparently they are hijacking DNS failures. "
"I'll try to correct for this by treating future occurrences of "
"\"%s\" as 'not found'.", id, *ip, id);
smartlist_add(dns_wildcard_list, tor_strdup(id));
}
if (!dns_wildcard_notice_given)
control_event_server_status(LOG_NOTICE, "DNS_HIJACKED");
dns_wildcard_notice_given = 1;
}
}
/** Note that a single test address (one believed to be good) seems to be
* getting redirected to the same IP as failures are. */
static void
add_wildcarded_test_address(const char *address)
{
int n, n_test_addrs;
if (!dns_wildcarded_test_address_list)
dns_wildcarded_test_address_list = smartlist_create();
if (smartlist_string_isin_case(dns_wildcarded_test_address_list, address))
return;
n_test_addrs = get_options()->ServerDNSTestAddresses ?
smartlist_len(get_options()->ServerDNSTestAddresses) : 0;
smartlist_add(dns_wildcarded_test_address_list, tor_strdup(address));
n = smartlist_len(dns_wildcarded_test_address_list);
if (n > n_test_addrs/2) {
log(dns_wildcarded_test_address_notice_given ? LOG_INFO : LOG_NOTICE,
LD_EXIT, "Your DNS provider tried to redirect \"%s\" to a junk "
"address. It has done this with %d test addresses so far. I'm "
"going to stop being an exit node for now, since our DNS seems so "
"broken.", address, n);
if (!dns_is_completely_invalid) {
dns_is_completely_invalid = 1;
mark_my_descriptor_dirty();
}
if (!dns_wildcarded_test_address_notice_given)
control_event_server_status(LOG_WARN, "DNS_USELESS");
dns_wildcarded_test_address_notice_given = 1;
}
}
/** Callback function when we get an answer (possibly failing) for a request
* for a (hopefully) nonexistent domain. */
static void
evdns_wildcard_check_callback(int result, char type, int count, int ttl,
void *addresses, void *arg)
{
(void)ttl;
++n_wildcard_requests;
if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
uint32_t *addrs = addresses;
int i;
char *string_address = arg;
for (i = 0; i < count; ++i) {
char answer_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
in.s_addr = addrs[i];
tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
wildcard_increment_answer(answer_buf);
}
log(dns_wildcard_one_notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
"Your DNS provider gave an answer for \"%s\", which "
"is not supposed to exist. Apparently they are hijacking "
"DNS failures. Trying to correct for this. We've noticed %d possibly "
"bad addresses so far.",
string_address, strmap_size(dns_wildcard_response_count));
dns_wildcard_one_notice_given = 1;
}
tor_free(arg);
}
/** Launch a single request for a nonexistent hostname consisting of between
* <b>min_len</b> and <b>max_len</b> random (plausible) characters followed by
* <b>suffix</b> */
static void
launch_wildcard_check(int min_len, int max_len, const char *suffix)
{
char *addr;
int r;
addr = crypto_random_hostname(min_len, max_len, "", suffix);
log_info(LD_EXIT, "Testing whether our DNS server is hijacking nonexistent "
"domains with request for bogus hostname \"%s\"", addr);
r = evdns_resolve_ipv4(/* This "addr" tells us which address to resolve */
addr,
DNS_QUERY_NO_SEARCH, evdns_wildcard_check_callback,
/* This "addr" is an argument to the callback*/ addr);
if (r) {
/* There is no evdns request in progress; stop addr from getting leaked */
tor_free(addr);
}
}
/** Launch attempts to resolve a bunch of known-good addresses (configured in
* ServerDNSTestAddresses). [Callback for a libevent timer] */
static void
launch_test_addresses(int fd, short event, void *args)
{
or_options_t *options = get_options();
(void)fd;
(void)event;
(void)args;
log_info(LD_EXIT, "Launching checks to see whether our nameservers like to "
"hijack *everything*.");
/* This situation is worse than the failure-hijacking situation. When this
* happens, we're no good for DNS requests at all, and we shouldn't really
* be an exit server.*/
if (!options->ServerDNSTestAddresses)
return;
SMARTLIST_FOREACH(options->ServerDNSTestAddresses, const char *, address,
{
int r = evdns_resolve_ipv4(address, DNS_QUERY_NO_SEARCH, evdns_callback,
tor_strdup(address));
if (r)
log_info(LD_EXIT, "eventdns rejected test address %s: error %d",
escaped_safe_str(address), r);
});
}
#define N_WILDCARD_CHECKS 2
/** Launch DNS requests for a few nonexistent hostnames and a few well-known
* hostnames, and see if we can catch our nameserver trying to hijack them and
* map them to a stupid "I couldn't find ggoogle.com but maybe you'd like to
* buy these lovely encyclopedias" page. */
static void
dns_launch_wildcard_checks(void)
{
int i;
log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
"to hijack DNS failures.");
for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
/* RFC2606 reserves these. Sadly, some DNS hijackers, in a silly attempt
* to 'comply' with rfc2606, refrain from giving A records for these.
* This is the standards-compliance equivalent of making sure that your
* crackhouse's elevator inspection certificate is up to date.
*/
launch_wildcard_check(2, 16, ".invalid");
launch_wildcard_check(2, 16, ".test");
/* These will break specs if there are ever any number of
* 8+-character top-level domains. */
launch_wildcard_check(8, 16, "");
/* Try some random .com/org/net domains. This will work fine so long as
* not too many resolve to the same place. */
launch_wildcard_check(8, 16, ".com");
launch_wildcard_check(8, 16, ".org");
launch_wildcard_check(8, 16, ".net");
}
}
/** If appropriate, start testing whether our DNS servers tend to lie to
* us. */
void
dns_launch_correctness_checks(void)
{
static struct event launch_event;
struct timeval timeout;
if (!get_options()->ServerDNSDetectHijacking)
return;
dns_launch_wildcard_checks();
/* Wait a while before launching requests for test addresses, so we can
* get the results from checking for wildcarding. */
evtimer_set(&launch_event, launch_test_addresses, NULL);
timeout.tv_sec = 30;
timeout.tv_usec = 0;
if (evtimer_add(&launch_event, &timeout)<0) {
log_warn(LD_BUG, "Couldn't add timer for checking for dns hijacking");
}
}
/** Return true iff our DNS servers lie to us too much to be trustd. */
int
dns_seems_to_be_broken(void)
{
return dns_is_completely_invalid;
}
/** Forget what we've previously learned about our DNS servers' correctness. */
void
dns_reset_correctness_checks(void)
{
if (dns_wildcard_response_count) {
strmap_free(dns_wildcard_response_count, _tor_free);
dns_wildcard_response_count = NULL;
}
n_wildcard_requests = 0;
if (dns_wildcard_list) {
SMARTLIST_FOREACH(dns_wildcard_list, char *, cp, tor_free(cp));
smartlist_clear(dns_wildcard_list);
}
if (dns_wildcarded_test_address_list) {
SMARTLIST_FOREACH(dns_wildcarded_test_address_list, char *, cp,
tor_free(cp));
smartlist_clear(dns_wildcarded_test_address_list);
}
dns_wildcard_one_notice_given = dns_wildcard_notice_given =
dns_wildcarded_test_address_notice_given = dns_is_completely_invalid = 0;
}
/** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
* returned in response to requests for nonexistent hostnames. */
static int
answer_is_wildcarded(const char *ip)
{
return dns_wildcard_list && smartlist_string_isin(dns_wildcard_list, ip);
}
/** Exit with an assertion if <b>resolve</b> is corrupt. */
static void
assert_resolve_ok(cached_resolve_t *resolve)
{
tor_assert(resolve);
tor_assert(resolve->magic == CACHED_RESOLVE_MAGIC);
tor_assert(strlen(resolve->address) < MAX_ADDRESSLEN);
tor_assert(tor_strisnonupper(resolve->address));
if (resolve->state != CACHE_STATE_PENDING) {
tor_assert(!resolve->pending_connections);
}
if (resolve->state == CACHE_STATE_PENDING ||
resolve->state == CACHE_STATE_DONE) {
tor_assert(!resolve->ttl);
if (resolve->is_reverse)
tor_assert(!resolve->result.hostname);
else
tor_assert(!resolve->result.addr);
}
}
#ifdef DEBUG_DNS_CACHE
/** Exit with an assertion if the DNS cache is corrupt. */
static void
_assert_cache_ok(void)
{
cached_resolve_t **resolve;
int bad_rep = _cache_map_HT_REP_IS_BAD(&cache_root);
if (bad_rep) {
log_err(LD_BUG, "Bad rep type %d on dns cache hash table", bad_rep);
tor_assert(!bad_rep);
}
HT_FOREACH(resolve, cache_map, &cache_root) {
assert_resolve_ok(*resolve);
tor_assert((*resolve)->state != CACHE_STATE_DONE);
}
if (!cached_resolve_pqueue)
return;
smartlist_pqueue_assert_ok(cached_resolve_pqueue,
_compare_cached_resolves_by_expiry);
SMARTLIST_FOREACH(cached_resolve_pqueue, cached_resolve_t *, res,
{
if (res->state == CACHE_STATE_DONE) {
cached_resolve_t *found = HT_FIND(cache_map, &cache_root, res);
tor_assert(!found || found != res);
} else {
cached_resolve_t *found = HT_FIND(cache_map, &cache_root, res);
tor_assert(found);
}
});
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -