Commit 0f899518 authored by Nick Mathewson's avatar Nick Mathewson 🥄
Browse files

Make DNS resolve requests work for IPv6

* If there's an IPv4 and an IPv6 address, return both in the resolved
  cell.
* Treat all resolve requests as permitting IPv6, since by the spec they're
  allowed to, and by the code that won't break anything.
parent bb2145b4
...@@ -148,7 +148,8 @@ typedef struct cached_resolve_t { ...@@ -148,7 +148,8 @@ typedef struct cached_resolve_t {
char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
union { union {
uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful */ uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful.
* (In host order.) */
int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */ int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */
} result_ipv4; /**< Outcome of IPv4 lookup */ } result_ipv4; /**< Outcome of IPv4 lookup */
union { union {
...@@ -188,14 +189,16 @@ static void dns_found_answer(const char *address, uint8_t query_type, ...@@ -188,14 +189,16 @@ static void dns_found_answer(const char *address, uint8_t query_type,
const tor_addr_t *addr, const tor_addr_t *addr,
const char *hostname, const char *hostname,
uint32_t ttl); uint32_t ttl);
static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type); static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
const cached_resolve_t *resolve);
static int launch_resolve(cached_resolve_t *resolve); static int launch_resolve(cached_resolve_t *resolve);
static void add_wildcarded_test_address(const char *address); static void add_wildcarded_test_address(const char *address);
static int configure_nameservers(int force); static int configure_nameservers(int force);
static int answer_is_wildcarded(const char *ip); static int answer_is_wildcarded(const char *ip);
static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
or_circuit_t *oncirc, char **resolved_to_hostname, or_circuit_t *oncirc, char **resolved_to_hostname,
int *made_connection_pending_out); int *made_connection_pending_out,
cached_resolve_t **resolve_out);
static int set_exitconn_info_from_resolve(edge_connection_t *exitconn, static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
const cached_resolve_t *resolve, const cached_resolve_t *resolve,
char **hostname_out); char **hostname_out);
...@@ -593,55 +596,55 @@ purge_expired_resolves(time_t now) ...@@ -593,55 +596,55 @@ purge_expired_resolves(time_t now)
/** Send a response to the RESOLVE request of a connection. /** Send a response to the RESOLVE request of a connection.
* <b>answer_type</b> must be one of * <b>answer_type</b> must be one of
* RESOLVED_TYPE_(IPV4|IPV6|ERROR|ERROR_TRANSIENT|AUTO). * RESOLVED_TYPE_(AUTO|ERROR|ERROR_TRANSIENT|).
* *
* If <b>circ</b> is provided, and we have a cached answer, send the * If <b>circ</b> is provided, and we have a cached answer, send the
* answer back along circ; otherwise, send the answer back along * answer back along circ; otherwise, send the answer back along
* <b>conn</b>'s attached circuit. * <b>conn</b>'s attached circuit.
*/ */
static void static void
send_resolved_cell(edge_connection_t *conn, uint8_t answer_type) send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
const cached_resolve_t *resolved)
{ {
char buf[RELAY_PAYLOAD_SIZE]; char buf[RELAY_PAYLOAD_SIZE], *cp = buf;
size_t buflen; size_t buflen = 0;
uint32_t ttl; uint32_t ttl;
if (answer_type == RESOLVED_TYPE_AUTO) {
sa_family_t family = tor_addr_family(&conn->base_.addr);
if (family == AF_INET)
answer_type = RESOLVED_TYPE_IPV4;
else if (family == AF_INET6)
answer_type = RESOLVED_TYPE_IPV6;
else
answer_type = RESOLVED_TYPE_ERROR_TRANSIENT;
}
buf[0] = answer_type; buf[0] = answer_type;
ttl = dns_clip_ttl(conn->address_ttl); ttl = dns_clip_ttl(conn->address_ttl);
switch (answer_type) switch (answer_type)
{ {
case RESOLVED_TYPE_IPV4: case RESOLVED_TYPE_AUTO:
buf[1] = 4; if (resolved && resolved->res_status_ipv4 == RES_STATUS_DONE_OK) {
set_uint32(buf+2, tor_addr_to_ipv4n(&conn->base_.addr)); cp[0] = RESOLVED_TYPE_IPV4;
set_uint32(buf+6, htonl(ttl)); cp[1] = 4;
buflen = 10; set_uint32(cp+2, htonl(resolved->result_ipv4.addr_ipv4));
break; set_uint32(cp+6, htonl(ttl));
case RESOLVED_TYPE_IPV6: cp += 10;
{ }
const uint8_t *bytes = tor_addr_to_in6_addr8(&conn->base_.addr); if (resolved && resolved->res_status_ipv6 == RES_STATUS_DONE_OK) {
buf[1] = 16; const uint8_t *bytes = resolved->result_ipv6.addr_ipv6.s6_addr;
memcpy(buf+2, bytes, 16); cp[0] = RESOLVED_TYPE_IPV6;
set_uint32(buf+18, htonl(ttl)); cp[1] = 16;
buflen = 22; memcpy(cp+2, bytes, 16);
set_uint32(cp+18, htonl(ttl));
cp += 22;
}
if (cp != buf) {
buflen = cp - buf;
break;
} else {
answer_type = RESOLVED_TYPE_ERROR;
/* fall through. */
} }
break;
case RESOLVED_TYPE_ERROR_TRANSIENT: case RESOLVED_TYPE_ERROR_TRANSIENT:
case RESOLVED_TYPE_ERROR: case RESOLVED_TYPE_ERROR:
{ {
const char *errmsg = "Error resolving hostname"; const char *errmsg = "Error resolving hostname";
size_t msglen = strlen(errmsg); size_t msglen = strlen(errmsg);
buf[0] = answer_type;
buf[1] = msglen; buf[1] = msglen;
strlcpy(buf+2, errmsg, sizeof(buf)-2); strlcpy(buf+2, errmsg, sizeof(buf)-2);
set_uint32(buf+2+msglen, htonl(ttl)); set_uint32(buf+2+msglen, htonl(ttl));
...@@ -719,10 +722,11 @@ dns_resolve(edge_connection_t *exitconn) ...@@ -719,10 +722,11 @@ dns_resolve(edge_connection_t *exitconn)
int is_resolve, r; int is_resolve, r;
int made_connection_pending = 0; int made_connection_pending = 0;
char *hostname = NULL; char *hostname = NULL;
cached_resolve_t *resolve = NULL;
is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE; is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE;
r = dns_resolve_impl(exitconn, is_resolve, oncirc, &hostname, r = dns_resolve_impl(exitconn, is_resolve, oncirc, &hostname,
&made_connection_pending); &made_connection_pending, &resolve);
switch (r) { switch (r) {
case 1: case 1:
...@@ -733,7 +737,7 @@ dns_resolve(edge_connection_t *exitconn) ...@@ -733,7 +737,7 @@ dns_resolve(edge_connection_t *exitconn)
if (hostname) if (hostname)
send_resolved_hostname_cell(exitconn, hostname); send_resolved_hostname_cell(exitconn, hostname);
else else
send_resolved_cell(exitconn, RESOLVED_TYPE_AUTO); send_resolved_cell(exitconn, RESOLVED_TYPE_AUTO, resolve);
exitconn->on_circuit = NULL; exitconn->on_circuit = NULL;
} else { } else {
/* Add to the n_streams list; the calling function will send back a /* Add to the n_streams list; the calling function will send back a
...@@ -755,7 +759,8 @@ dns_resolve(edge_connection_t *exitconn) ...@@ -755,7 +759,8 @@ dns_resolve(edge_connection_t *exitconn)
* and stop everybody waiting for the same connection. */ * and stop everybody waiting for the same connection. */
if (is_resolve) { if (is_resolve) {
send_resolved_cell(exitconn, send_resolved_cell(exitconn,
(r == -1) ? RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT); (r == -1) ? RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT,
NULL);
} }
exitconn->on_circuit = NULL; exitconn->on_circuit = NULL;
...@@ -789,11 +794,14 @@ dns_resolve(edge_connection_t *exitconn) ...@@ -789,11 +794,14 @@ dns_resolve(edge_connection_t *exitconn)
* Set *<b>made_connection_pending_out</b> to true if we have placed * Set *<b>made_connection_pending_out</b> to true if we have placed
* <b>exitconn</b> on the list of pending connections for some resolve; set it * <b>exitconn</b> on the list of pending connections for some resolve; set it
* to false otherwise. * to false otherwise.
*
* Set *<b>resolve_out</b> to a cached resolve, if we found one.
*/ */
static int static int
dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
or_circuit_t *oncirc, char **hostname_out, or_circuit_t *oncirc, char **hostname_out,
int *made_connection_pending_out) int *made_connection_pending_out,
cached_resolve_t **resolve_out)
{ {
cached_resolve_t *resolve; cached_resolve_t *resolve;
cached_resolve_t search; cached_resolve_t search;
...@@ -891,6 +899,8 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, ...@@ -891,6 +899,8 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
exitconn->base_.s, exitconn->base_.s,
escaped_safe_str(resolve->address)); escaped_safe_str(resolve->address));
*resolve_out = resolve;
return set_exitconn_info_from_resolve(exitconn, resolve, hostname_out); return set_exitconn_info_from_resolve(exitconn, resolve, hostname_out);
case CACHE_STATE_DONE: case CACHE_STATE_DONE:
...@@ -940,6 +950,8 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn, ...@@ -940,6 +950,8 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn,
char **hostname_out) char **hostname_out)
{ {
int ipv4_ok, ipv6_ok, answer_with_ipv4, r; int ipv4_ok, ipv6_ok, answer_with_ipv4, r;
uint32_t begincell_flags;
const int is_resolve = exitconn->base_.purpose == EXIT_PURPOSE_RESOLVE;
tor_assert(exitconn); tor_assert(exitconn);
tor_assert(resolve); tor_assert(resolve);
...@@ -955,14 +967,22 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn, ...@@ -955,14 +967,22 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn,
/* If we're here then the connection wants one or either of ipv4, ipv6, and /* If we're here then the connection wants one or either of ipv4, ipv6, and
* we can give it one or both. */ * we can give it one or both. */
if (is_resolve) {
begincell_flags = BEGIN_FLAG_IPV6_OK;
} else {
begincell_flags = exitconn->begincell_flags;
}
ipv4_ok = (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) && ipv4_ok = (resolve->res_status_ipv4 == RES_STATUS_DONE_OK) &&
! (exitconn->begincell_flags & BEGIN_FLAG_IPV4_NOT_OK); ! (begincell_flags & BEGIN_FLAG_IPV4_NOT_OK);
ipv6_ok = (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) && ipv6_ok = (resolve->res_status_ipv6 == RES_STATUS_DONE_OK) &&
(exitconn->begincell_flags & BEGIN_FLAG_IPV6_OK) && (begincell_flags & BEGIN_FLAG_IPV6_OK) &&
get_options()->IPv6Exit; get_options()->IPv6Exit;
/* Now decide which one to actually give. */ /* Now decide which one to actually give. */
if (ipv4_ok && ipv6_ok) { if (ipv4_ok && ipv6_ok && is_resolve) {
answer_with_ipv4 = 1;
} else if (ipv4_ok && ipv6_ok) {
/* If we have both, see if our exit policy has an opinion. */ /* If we have both, see if our exit policy has an opinion. */
const uint16_t port = exitconn->base_.port; const uint16_t port = exitconn->base_.port;
int ipv4_allowed, ipv6_allowed; int ipv4_allowed, ipv6_allowed;
...@@ -978,7 +998,7 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn, ...@@ -978,7 +998,7 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn,
} else { } else {
/* Our exit policy would permit both. Answer with whichever the user /* Our exit policy would permit both. Answer with whichever the user
* prefers */ * prefers */
answer_with_ipv4 = !(exitconn->begincell_flags & answer_with_ipv4 = !(begincell_flags &
BEGIN_FLAG_IPV6_PREFERRED); BEGIN_FLAG_IPV6_PREFERRED);
} }
} else { } else {
...@@ -989,7 +1009,7 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn, ...@@ -989,7 +1009,7 @@ set_exitconn_info_from_resolve(edge_connection_t *exitconn,
answer_with_ipv4 = 0; answer_with_ipv4 = 0;
} else { } else {
/* Neither one was okay. Choose based on user preference. */ /* Neither one was okay. Choose based on user preference. */
answer_with_ipv4 = !(exitconn->begincell_flags & answer_with_ipv4 = !(begincell_flags &
BEGIN_FLAG_IPV6_PREFERRED); BEGIN_FLAG_IPV6_PREFERRED);
} }
} }
...@@ -1292,7 +1312,8 @@ inform_pending_connections(cached_resolve_t *resolve) ...@@ -1292,7 +1312,8 @@ inform_pending_connections(cached_resolve_t *resolve)
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
} else { } else {
send_resolved_cell(pendconn, r == -1 ? send_resolved_cell(pendconn, r == -1 ?
RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT); RESOLVED_TYPE_ERROR : RESOLVED_TYPE_ERROR_TRANSIENT,
NULL);
/* This detach must happen after we send the resolved cell. */ /* This detach must happen after we send the resolved cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn); circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
} }
...@@ -1321,7 +1342,7 @@ inform_pending_connections(cached_resolve_t *resolve) ...@@ -1321,7 +1342,7 @@ inform_pending_connections(cached_resolve_t *resolve)
if (pendconn->is_reverse_dns_lookup) if (pendconn->is_reverse_dns_lookup)
send_resolved_hostname_cell(pendconn, hostname); send_resolved_hostname_cell(pendconn, hostname);
else else
send_resolved_cell(pendconn, RESOLVED_TYPE_AUTO); send_resolved_cell(pendconn, RESOLVED_TYPE_AUTO, resolve);
circ = circuit_get_by_edge_conn(pendconn); circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ); tor_assert(circ);
circuit_detach_stream(circ, pendconn); circuit_detach_stream(circ, pendconn);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment