r8910@Kushana: nickm | 2006-09-22 12:14:05 -0400

Instead of just checking known-invalid addresses for DNS hijacking, we
 now check randomly generated addresses, and if too many of them map to
 the same IP, we assume that IP is the destination of a DNS hijack
 attempt.
 
 A little bird tells me that some DNS hijackers think that declining to
 give an A record for RFC2606 addresses (like .invalid and .example)
 makes them more standards compliant.  Standardswise, this is like an
 illicit brothel making sure that nobody has pulled the tags off the
 mattresss, but that doesn't get us out of working around it.
 


svn:r8465
This commit is contained in:
Nick Mathewson 2006-09-22 20:20:26 +00:00
parent bde5939ad3
commit 4218f09820
2 changed files with 77 additions and 22 deletions

View File

@ -12,7 +12,9 @@ Changes in version 0.1.2.2-alpha - 2006-??-??
o Minor features: o Minor features:
- Check for name servers (like Earthlink's) that hijack failing DNS - Check for name servers (like Earthlink's) that hijack failing DNS
requests and replace the 'no such server' answer with a "helpful" requests and replace the 'no such server' answer with a "helpful"
redirect to an advertising-driven search portal. [Resolves bug 330.] redirect to an advertising-driven search portal. We're a little clever
about this, in order to work around DNS hijackers who "helpfully"
decline to hijack known-invalid RFC2606 addresses. [Resolves bug 330.]
- When asked to resolve a hostname, don't use non-exit servers unless - When asked to resolve a hostname, don't use non-exit servers unless
requested to do so. This allows servers with broken DNS be useful to requested to do so. This allows servers with broken DNS be useful to
the network. the network.

View File

@ -575,7 +575,7 @@ dns_resolve(edge_connection_t *exitconn)
return -1; return -1;
} }
//log_notice(LD_EXIT, "Looks like an address %s", //log_notice(LD_EXIT, "Looks like an address %s",
// exitconn->_base.address); // exitconn->_base.address);
} }
/* now check the hash table to see if 'address' is already there. */ /* now check the hash table to see if 'address' is already there. */
@ -1580,11 +1580,50 @@ launch_resolve(edge_connection_t *exitconn)
return r ? -1 : 0; return r ? -1 : 0;
} }
/** If present, a list of dotted-quad IP addresses that our nameserver /** How many requests for bogus addresses have we launched so far? */
* apparently wants to return in response to requests for nonexistent domains. 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; static smartlist_t *dns_wildcard_list = NULL;
/** 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;
static int notice_given = 0;
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(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 trying to correct for this by treating future occurrences of "
"\"%s\" as 'not found'.", id, *ip, id);
smartlist_add(dns_wildcard_list, tor_strdup(id));
}
}
}
/** Callback function when we get an answer (possibly failing) for a request /** Callback function when we get an answer (possibly failing) for a request
* for a (hopefully) nonexistent domain. */ * for a (hopefully) nonexistent domain. */
static void static void
@ -1593,43 +1632,43 @@ eventdns_wildcard_check_callback(int result, char type, int count, int ttl,
{ {
static int notice_given = 0; static int notice_given = 0;
(void)ttl; (void)ttl;
++n_wildcard_requests;
if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) { if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
uint32_t *addrs = addresses; uint32_t *addrs = addresses;
int i; int i;
char *string_address = arg; char *string_address = arg;
if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
for (i = 0; i < count; ++i) { for (i = 0; i < count; ++i) {
char answer_buf[INET_NTOA_BUF_LEN+1]; char answer_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in; struct in_addr in;
in.s_addr = addrs[i]; in.s_addr = addrs[i];
tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf)); tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
if (!smartlist_string_isin(dns_wildcard_list, answer_buf)) wildcard_increment_answer(answer_buf);
smartlist_add(dns_wildcard_list, tor_strdup(answer_buf));
} }
log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT, log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
"Your DNS provider gave an answer for \"%s\", which " "Your DNS provider gave an answer for \"%s\", which "
"is not supposed to exist. Apparently they are hijacking " "is not supposed to exist. Apparently they are hijacking "
"DNS failures. Trying to correct for this. We've noticed %d bad " "DNS failures. Trying to correct for this. We've noticed %d possibly "
"addresses so far.", string_address, smartlist_len(dns_wildcard_list)); "bad addresses so far.",
string_address, strmap_size(dns_wildcard_response_count));
notice_given = 1; notice_given = 1;
} }
tor_free(arg); tor_free(arg);
} }
/** Launch a single request for a nonexistent hostname consisting of /** Launch a single request for a nonexistent hostname consisting of between
* <b>len</b> random (plausible) characters followed by <b>suffix</b> */ * <b>min_len</b> and <b>max_len</b> random (plausible) characters followed by
* <b>suffix</b> */
static void static void
launch_wildcard_check(int len, const char *suffix) launch_wildcard_check(int min_len, int max_len, const char *suffix)
{ {
char random_bytes[16], name[64], *addr; char random_bytes[20], name[64], *addr;
size_t n = (len*5+7)/8; size_t len;
int r; int r;
tor_assert(n <= sizeof(random_bytes)); len = min_len + crypto_rand_int(max_len-min_len+1);
if (crypto_rand(random_bytes, sizeof(random_bytes)) < 0)
if (crypto_rand(random_bytes, n) < 0)
return; return;
base32_encode(name, sizeof(name), random_bytes, n); base32_encode(name, sizeof(name), random_bytes, sizeof(random_bytes));
name[len] = '\0'; name[len] = '\0';
strlcat(name, suffix, sizeof(name)); strlcat(name, suffix, sizeof(name));
@ -1655,14 +1694,28 @@ dns_launch_wildcard_checks(void)
log_info(LD_EXIT, "Launching checks to see whether our nameservers like " log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
"to hijack DNS failures."); "to hijack DNS failures.");
for (i = 0; i < N_WILDCARD_CHECKS; ++i) { for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
/* RFC2606 reserves these */ /* RFC2606 reserves these. Sadly, some DNS hijackers, in a silly attempt
launch_wildcard_check(8, ".invalid"); * to 'comply' with rfc2606, refrain from giving A records for these.
launch_wildcard_check(8, ".test"); * This is the standards-complaince equivalent of making sure that your
* crackhouse's elevator inspection certificate is up to date.
*/
launch_wildcard_check(2, 16, "%s.invalid");
launch_wildcard_check(2, 16, "%s.test");
/* Thy somese 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, "%s.com");
launch_wildcard_check(8, 16, "%s.org");
launch_wildcard_check(8, 16, "%s.net");
} }
} }
/** Return true iff we have noticed that the dotted-quad <b>ip</b> has been /** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
* returned in response to a request for a nonexistent hostname. */ * returned in response to requests for nonexistent hostnames. */
static int static int
answer_is_wildcarded(const char *ip) answer_is_wildcarded(const char *ip)
{ {