summaryrefslogtreecommitdiff
path: root/src/or/dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/dns.c')
-rw-r--r--src/or/dns.c159
1 files changed, 64 insertions, 95 deletions
diff --git a/src/or/dns.c b/src/or/dns.c
index c7adfbc971..0ad4c0f505 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -9,6 +9,42 @@
* This is implemented as a wrapper around Adam Langley's eventdns.c code.
* (We can't just use gethostbyname() and friends because we really need to
* be nonblocking.)
+ *
+ * There are three main cases when a Tor relay uses dns.c to launch a DNS
+ * request:
+ * <ol>
+ * <li>To check whether the DNS server is working more or less correctly.
+ * This happens via dns_launch_correctness_checks(). The answer is
+ * reported in the return value from later calls to
+ * dns_seems_to_be_broken().
+ * <li>When a client has asked the relay, in a RELAY_BEGIN cell, to connect
+ * to a given server by hostname. This happens via dns_resolve().
+ * <li>When a client has asked the rela, in a RELAY_RESOLVE cell, to look
+ * up a given server's IP address(es) by hostname. This also happens via
+ * dns_resolve().
+ * </ol>
+ *
+ * Each of these gets handled a little differently.
+ *
+ * To check for correctness, we look up some hostname we expect to exist and
+ * have real entries, some hostnames which we expect to definitely not exist,
+ * and some hostnames that we expect to probably not exist. If too many of
+ * the hostnames that shouldn't exist do exist, that's a DNS hijacking
+ * attempt. If too many of the hostnames that should exist have the same
+ * addresses as the ones that shouldn't exist, that's a very bad DNS hijacking
+ * attempt, or a very naughty captive portal. And if the hostnames that
+ * should exist simply don't exist, we probably have a broken nameserver.
+ *
+ * To handle client requests, we first check our cache for answers. If there
+ * isn't something up-to-date, we've got to launch A or AAAA requests as
+ * appropriate. How we handle responses to those in particular is a bit
+ * complex; see dns_lookup() and set_exitconn_info_from_resolve().
+ *
+ * When a lookup is finally complete, the inform_pending_connections()
+ * function will tell all of the streams that have been waiting for the
+ * resolve, by calling connection_exit_connect() if the client sent a
+ * RELAY_BEGIN cell, and by calling send_resolved_cell() or
+ * send_hostname_cell() if the client sent a RELAY_RESOLVE cell.
**/
#define DNS_PRIVATE
@@ -27,61 +63,8 @@
#include "router.h"
#include "ht.h"
#include "sandbox.h"
-#ifdef HAVE_EVENT2_DNS_H
#include <event2/event.h>
#include <event2/dns.h>
-#else
-#include <event.h>
-#include "eventdns.h"
-#ifndef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
-#define HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
-#endif
-#endif
-
-#ifndef HAVE_EVENT2_DNS_H
-struct evdns_base;
-struct evdns_request;
-#define evdns_base_new(x,y) tor_malloc(1)
-#define evdns_base_clear_nameservers_and_suspend(base) \
- evdns_clear_nameservers_and_suspend()
-#define evdns_base_search_clear(base) evdns_search_clear()
-#define evdns_base_set_default_outgoing_bind_address(base, a, len) \
- evdns_set_default_outgoing_bind_address((a),(len))
-#define evdns_base_resolv_conf_parse(base, options, fname) \
- evdns_resolv_conf_parse((options), (fname))
-#define evdns_base_count_nameservers(base) \
- evdns_count_nameservers()
-#define evdns_base_resume(base) \
- evdns_resume()
-#define evdns_base_config_windows_nameservers(base) \
- evdns_config_windows_nameservers()
-#define evdns_base_set_option_(base, opt, val) \
- evdns_set_option((opt),(val),DNS_OPTIONS_ALL)
-/* Note: our internal eventdns.c, plus Libevent 1.4, used a 1 return to
- * signify failure to launch a resolve. Libevent 2.0 uses a -1 return to
- * signify a failure on a resolve, though if we're on Libevent 2.0, we should
- * have event2/dns.h and never hit these macros. Regardless, 0 is success. */
-#define evdns_base_resolve_ipv4(base, addr, options, cb, ptr) \
- ((evdns_resolve_ipv4((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-#define evdns_base_resolve_ipv6(base, addr, options, cb, ptr) \
- ((evdns_resolve_ipv6((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-#define evdns_base_resolve_reverse(base, addr, options, cb, ptr) \
- ((evdns_resolve_reverse((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-#define evdns_base_resolve_reverse_ipv6(base, addr, options, cb, ptr) \
- ((evdns_resolve_reverse_ipv6((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-
-#elif defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER < 0x02000303
-#define evdns_base_set_option_(base, opt, val) \
- evdns_base_set_option((base), (opt),(val),DNS_OPTIONS_ALL)
-
-#else
-#define evdns_base_set_option_ evdns_base_set_option
-
-#endif
/** How long will we wait for an answer from the resolver before we decide
* that the resolver is wedged? */
@@ -260,29 +243,19 @@ has_dns_init_failed(void)
}
/** Helper: Given a TTL from a DNS response, determine what TTL to give the
- * OP that asked us to resolve it. */
+ * OP that asked us to resolve it, and how long to cache that record
+ * ourselves. */
uint32_t
dns_clip_ttl(uint32_t ttl)
{
- if (ttl < MIN_DNS_TTL)
- return MIN_DNS_TTL;
- else if (ttl > MAX_DNS_TTL)
- return MAX_DNS_TTL;
- else
- return ttl;
-}
-
-/** Helper: Given a TTL from a DNS response, determine how long to hold it in
- * our cache. */
-STATIC uint32_t
-dns_get_expiry_ttl(uint32_t ttl)
-{
- if (ttl < MIN_DNS_TTL)
- return MIN_DNS_TTL;
- else if (ttl > MAX_DNS_ENTRY_AGE)
- return MAX_DNS_ENTRY_AGE;
+ /* This logic is a defense against "DefectTor" DNS-based traffic
+ * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf .
+ * We only give two values: a "low" value and a "high" value.
+ */
+ if (ttl < MIN_DNS_TTL_AT_EXIT)
+ return MIN_DNS_TTL_AT_EXIT;
else
- return ttl;
+ return MAX_DNS_TTL_AT_EXIT;
}
/** Helper: free storage held by an entry in the DNS cache. */
@@ -353,7 +326,7 @@ cached_resolve_add_answer(cached_resolve_t *resolve,
resolve->result_ipv4.err_ipv4 = dns_result;
resolve->res_status_ipv4 = RES_STATUS_DONE_ERR;
}
-
+ resolve->ttl_ipv4 = ttl;
} else if (query_type == DNS_IPv6_AAAA) {
if (resolve->res_status_ipv6 != RES_STATUS_INFLIGHT)
return;
@@ -368,6 +341,7 @@ cached_resolve_add_answer(cached_resolve_t *resolve,
resolve->result_ipv6.err_ipv6 = dns_result;
resolve->res_status_ipv6 = RES_STATUS_DONE_ERR;
}
+ resolve->ttl_ipv6 = ttl;
}
}
@@ -548,6 +522,7 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type,
answer_type = RESOLVED_TYPE_ERROR;
/* fall through. */
}
+ /* Falls through. */
case RESOLVED_TYPE_ERROR_TRANSIENT:
case RESOLVED_TYPE_ERROR:
{
@@ -846,8 +821,14 @@ dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,
}
/** Given an exit connection <b>exitconn</b>, and a cached_resolve_t
- * <b>resolve</b> whose DNS lookups have all succeeded or failed, update the
- * appropriate fields (address_ttl and addr) of <b>exitconn</b>.
+ * <b>resolve</b> whose DNS lookups have all either succeeded or failed,
+ * update the appropriate fields (address_ttl and addr) of <b>exitconn</b>.
+ *
+ * The logic can be complicated here, since we might have launched both
+ * an A lookup and an AAAA lookup, and since either of those might have
+ * succeeded or failed, and since we want to answer a RESOLVE cell with
+ * a full answer but answer a BEGIN cell with whatever answer the client
+ * would accept <i>and</i> we could still connect to.
*
* If this is a reverse lookup, set *<b>hostname_out</b> to a newly allocated
* copy of the name resulting hostname.
@@ -1190,7 +1171,12 @@ dns_found_answer(const char *address, uint8_t query_type,
/** Given a pending cached_resolve_t that we just finished resolving,
* inform every connection that was waiting for the outcome of that
- * resolution. */
+ * resolution.
+ *
+ * Do this by sending a RELAY_RESOLVED cell (if the pending stream had sent us
+ * RELAY_RESOLVE cell), or by launching an exit connection (if the pending
+ * stream had send us a RELAY_BEGIN cell).
+ */
static void
inform_pending_connections(cached_resolve_t *resolve)
{
@@ -1323,7 +1309,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve)
resolve->ttl_hostname < ttl)
ttl = resolve->ttl_hostname;
- set_expiry(new_resolve, time(NULL) + dns_get_expiry_ttl(ttl));
+ set_expiry(new_resolve, time(NULL) + dns_clip_ttl(ttl));
}
assert_cache_ok();
@@ -1373,23 +1359,6 @@ configure_nameservers(int force)
}
}
-#ifdef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
- if (! tor_addr_is_null(&options->OutboundBindAddressIPv4_)) {
- int socklen;
- struct sockaddr_storage ss;
- socklen = tor_addr_to_sockaddr(&options->OutboundBindAddressIPv4_, 0,
- (struct sockaddr *)&ss, sizeof(ss));
- if (socklen <= 0) {
- log_warn(LD_BUG, "Couldn't convert outbound bind address to sockaddr."
- " Ignoring.");
- } else {
- evdns_base_set_default_outgoing_bind_address(the_evdns_base,
- (struct sockaddr *)&ss,
- socklen);
- }
- }
-#endif
-
evdns_set_log_fn(evdns_log_cb);
if (conf_fname) {
log_debug(LD_FS, "stat()ing %s", conf_fname);
@@ -1454,7 +1423,7 @@ configure_nameservers(int force)
}
#endif
-#define SET(k,v) evdns_base_set_option_(the_evdns_base, (k), (v))
+#define SET(k,v) evdns_base_set_option(the_evdns_base, (k), (v))
if (evdns_base_count_nameservers(the_evdns_base) == 1) {
SET("max-timeouts:", "16");