commit c230ff4ca97ae6bf8ddbf2d19a1a7d33371cde3a
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Thu Mar 27 22:24:48 2014 -0400
Look at all of a RESOLVED cell; not just the first answer.
Also, stop accepting the old kind of RESOLVED cells with no TTL
fields; they haven't been sent since 0.1.1.6-alpha.
This patch won't work without the fix to #10468 -- it will break
DNSPorts unless they set the proper ipv4/6 flags on entry_connection_t.
---
src/or/connection_edge.c | 7 +-
src/or/connection_edge.h | 4 +
src/or/relay.c | 269 ++++++++++++++++++++++++++++++++++++++--------
src/or/relay.h | 13 +++
4 files changed, 241 insertions(+), 52 deletions(-)
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 630c3b9..1ecc553 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -62,11 +62,6 @@ static int connection_ap_process_natd(entry_connection_t *conn);
static int connection_exit_connect_dir(edge_connection_t *exitconn);
static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port);
static int connection_ap_supports_optimistic_data(const entry_connection_t *);
-static void connection_ap_handshake_socks_resolved_addr(
- entry_connection_t *conn,
- const tor_addr_t *answer,
- int ttl,
- time_t expires);
/** An AP stream has failed/finished. If it hasn't already sent back
* a socks reply, send one now (based on endreason). Also set
@@ -2065,7 +2060,7 @@ tell_controller_about_resolved_result(entry_connection_t *conn,
* As connection_ap_handshake_socks_resolved, but take a tor_addr_t to send
* as the answer.
*/
-static void
+void
connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn,
const tor_addr_t *answer,
int ttl,
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index e3a95ad..cbca31a 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -50,6 +50,10 @@ void connection_ap_handshake_socks_resolved(entry_connection_t *conn,
const uint8_t *answer,
int ttl,
time_t expires);
+void connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn,
+ const tor_addr_t *answer,
+ int ttl,
+ time_t expires);
int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
diff --git a/src/or/relay.c b/src/or/relay.c
index 3e28077..ed24397 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -1009,74 +1009,252 @@ connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
return 0;
}
-/** DOCDOC */
-static void
+/** Drop all storage held by <b>addr</b>. */
+STATIC void
+address_ttl_free(address_ttl_t *addr)
+{
+ if (!addr)
+ return;
+ tor_free(addr->hostname);
+ tor_free(addr);
+}
+
+/** Parse a resolved cell in <b>cell</b>, with parsed header in <b>rh</b>.
+ * Return -1 on parse error. On success, add one or more newly allocated
+ * address_ttl_t to <b>addresses_out</b>; set *<b>errcode_out</b> to
+ * one of 0, RESOLVED_TYPE_ERROR, or RESOLVED_TYPE_ERROR_TRANSIENT, and
+ * return 0. */
+STATIC int
+resolved_cell_parse(const cell_t *cell, const relay_header_t *rh,
+ smartlist_t *addresses_out, int *errcode_out)
+{
+ const uint8_t *cp;
+ uint8_t answer_type;
+ size_t answer_len;
+ address_ttl_t *addr;
+ size_t remaining;
+ int errcode = 0;
+ smartlist_t *addrs;
+
+ tor_assert(cell);
+ tor_assert(rh);
+ tor_assert(addresses_out);
+ tor_assert(errcode_out);
+
+ *errcode_out = 0;
+
+ if (rh->length > RELAY_PAYLOAD_SIZE)
+ return -1;
+
+ addrs = smartlist_new();
+
+ cp = cell->payload + RELAY_HEADER_SIZE;
+
+ remaining = rh->length;
+ while (remaining) {
+ const uint8_t *cp_orig = cp;
+ if (remaining < 2)
+ goto err;
+ answer_type = *cp++;
+ answer_len = *cp++;
+ if (remaining < 2 + answer_len + 4) {
+ goto err;
+ }
+ if (answer_type == RESOLVED_TYPE_IPV4) {
+ if (answer_len != 4) {
+ goto err;
+ }
+ addr = tor_malloc_zero(sizeof(*addr));
+ tor_addr_from_ipv4n(&addr->addr, get_uint32(cp));
+ cp += 4;
+ addr->ttl = ntohl(get_uint32(cp));
+ cp += 4;
+ smartlist_add(addrs, addr);
+ } else if (answer_type == RESOLVED_TYPE_IPV6) {
+ if (answer_len != 16)
+ goto err;
+ addr = tor_malloc_zero(sizeof(*addr));
+ tor_addr_from_ipv6_bytes(&addr->addr, (const char*) cp);
+ cp += 16;
+ addr->ttl = ntohl(get_uint32(cp));
+ cp += 4;
+ smartlist_add(addrs, addr);
+ } else if (answer_type == RESOLVED_TYPE_HOSTNAME) {
+ if (answer_len == 0) {
+ goto err;
+ }
+ addr = tor_malloc_zero(sizeof(*addr));
+ addr->hostname = tor_memdup_nulterm(cp, answer_len);
+ cp += answer_len;
+ addr->ttl = ntohl(get_uint32(cp));
+ cp += 4;
+ smartlist_add(addrs, addr);
+ } else if (answer_type == RESOLVED_TYPE_ERROR_TRANSIENT ||
+ answer_type == RESOLVED_TYPE_ERROR) {
+ errcode = answer_type;
+ /* Ignore the error contents */
+ cp += answer_len + 4;
+ } else {
+ cp += answer_len + 4;
+ }
+ tor_assert(((ssize_t)remaining) >= (cp - cp_orig));
+ remaining -= (cp - cp_orig);
+ }
+
+ if (errcode && smartlist_len(addrs) == 0) {
+ /* Report an error only if there were no results. */
+ *errcode_out = errcode;
+ }
+
+ smartlist_add_all(addresses_out, addrs);
+ smartlist_free(addrs);
+
+ return 0;
+
+ err:
+ /* On parse error, don't report any results */
+ SMARTLIST_FOREACH(addrs, address_ttl_t *, a, address_ttl_free(a));
+ smartlist_free(addrs);
+ return -1;
+}
+
+/** Helper for connection_edge_process_resolved_cell: given an error code,
+ * an entry_connection, and a list of address_ttl_t *, report the best answer
+ * to the entry_connection. */
+STATIC void
+connection_ap_handshake_socks_got_resolve_cell(entry_connection_t *conn,
+ int error_code,
+ smartlist_t *results)
+{
+ address_ttl_t *addr_ipv4 = NULL;
+ address_ttl_t *addr_ipv6 = NULL;
+ address_ttl_t *addr_hostname = NULL;
+ address_ttl_t *addr_best = NULL;
+
+ /* If it's an error code, that's easy. */
+ if (error_code) {
+ tor_assert(error_code == RESOLVED_TYPE_ERROR ||
+ error_code == RESOLVED_TYPE_ERROR_TRANSIENT);
+ connection_ap_handshake_socks_resolved(conn,
+ error_code,0,NULL,-1,-1);
+ return;
+ }
+
+ /* Get the first answer of each type. */
+ SMARTLIST_FOREACH_BEGIN(results, address_ttl_t *, addr) {
+ if (addr->hostname) {
+ if (!addr_hostname) {
+ addr_hostname = addr;
+ }
+ } else if (tor_addr_family(&addr->addr) == AF_INET) {
+ if (!addr_ipv4 && conn->ipv4_traffic_ok) {
+ addr_ipv4 = addr;
+ }
+ } else if (tor_addr_family(&addr->addr) == AF_INET6) {
+ if (!addr_ipv6 && conn->ipv6_traffic_ok) {
+ addr_ipv6 = addr;
+ }
+ }
+ } SMARTLIST_FOREACH_END(addr);
+
+ /* Now figure out which type we wanted to deliver. */
+ if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) {
+ if (addr_hostname) {
+ connection_ap_handshake_socks_resolved(conn,
+ RESOLVED_TYPE_HOSTNAME,
+ strlen(addr_hostname->hostname),
+ (uint8_t*)addr_hostname->hostname,
+ addr_hostname->ttl,-1);
+ } else {
+ connection_ap_handshake_socks_resolved(conn,
+ RESOLVED_TYPE_ERROR,0,NULL,-1,-1);
+ }
+ return;
+ }
+
+ if (conn->prefer_ipv6_traffic) {
+ addr_best = addr_ipv6 ? addr_ipv6 : addr_ipv4;
+ } else {
+ addr_best = addr_ipv4 ? addr_ipv4 : addr_ipv6;
+ }
+
+ /* Now convert it to the ugly old interface */
+ if (! addr_best) {
+ connection_ap_handshake_socks_resolved(conn,
+ RESOLVED_TYPE_ERROR,0,NULL,-1,-1);
+ return;
+ }
+
+ connection_ap_handshake_socks_resolved_addr(conn,
+ &addr_best->addr,
+ addr_best->ttl,
+ -1);
+
+ remap_event_helper(conn, &addr_best->addr);
+}
+
+/** Handle a RELAY_COMMAND_RESOLVED cell that we received on a non-open AP
+ * stream. */
+static int
connection_edge_process_resolved_cell(edge_connection_t *conn,
const cell_t *cell,
const relay_header_t *rh)
{
- int ttl;
- int answer_len;
- uint8_t answer_type;
entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
+ smartlist_t *resolved_addresses = NULL;
+ int errcode = 0;
+
if (conn->base_.state != AP_CONN_STATE_RESOLVE_WAIT) {
log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got a 'resolved' cell while "
"not in state resolve_wait. Dropping.");
- return;
+ return 0;
}
tor_assert(SOCKS_COMMAND_IS_RESOLVE(entry_conn->socks_request->command));
- answer_len = cell->payload[RELAY_HEADER_SIZE+1];
- if (rh->length < 2 || answer_len+2>rh->length) {
+
+ resolved_addresses = smartlist_new();
+ if (resolved_cell_parse(cell, rh, resolved_addresses, &errcode)) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Dropping malformed 'resolved' cell");
connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
- return;
- }
-
- answer_type = cell->payload[RELAY_HEADER_SIZE];
- if (rh->length >= answer_len+6)
- ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+
- 2+answer_len));
- else
- ttl = -1;
- if (answer_type == RESOLVED_TYPE_IPV4 ||
- answer_type == RESOLVED_TYPE_IPV6) {
- tor_addr_t addr;
- if (decode_address_from_payload(&addr, cell->payload+RELAY_HEADER_SIZE,
- rh->length) &&
- tor_addr_is_internal(&addr, 0) &&
- get_options()->ClientDNSRejectInternalAddresses) {
- log_info(LD_APP,"Got a resolve with answer %s. Rejecting.",
- fmt_addr(&addr));
+ goto done;
+ }
+
+ if (get_options()->ClientDNSRejectInternalAddresses) {
+ int orig_len = smartlist_len(resolved_addresses);
+ SMARTLIST_FOREACH_BEGIN(resolved_addresses, address_ttl_t *, addr) {
+ if (addr->hostname == NULL && tor_addr_is_internal(&addr->addr, 0)) {
+ log_info(LD_APP, "Got a resolved cell with answer %s; dropping that "
+ "answer.",
+ safe_str_client(fmt_addr(&addr->addr)));
+ address_ttl_free(addr);
+ SMARTLIST_DEL_CURRENT(resolved_addresses, addr);
+ }
+ } SMARTLIST_FOREACH_END(addr);
+ if (orig_len && smartlist_len(resolved_addresses) == 0) {
+ log_info(LD_APP, "Got a resolved cell with only private addresses; "
+ "dropping it.");
connection_ap_handshake_socks_resolved(entry_conn,
RESOLVED_TYPE_ERROR_TRANSIENT,
0, NULL, 0, TIME_MAX);
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_TORPROTOCOL);
- return;
+ goto done;
}
}
- connection_ap_handshake_socks_resolved(entry_conn,
- answer_type,
- cell->payload[RELAY_HEADER_SIZE+1], /*answer_len*/
- cell->payload+RELAY_HEADER_SIZE+2, /*answer*/
- ttl,
- -1);
- if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
- tor_addr_t addr;
- tor_addr_from_ipv4n(&addr,
- get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
- remap_event_helper(entry_conn, &addr);
- } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
- tor_addr_t addr;
- tor_addr_from_ipv6_bytes(&addr,
- (char*)(cell->payload+RELAY_HEADER_SIZE+2));
- remap_event_helper(entry_conn, &addr);
- }
+
+ connection_ap_handshake_socks_got_resolve_cell(entry_conn,
+ errcode,
+ resolved_addresses);
+
connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_DONE |
END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+ done:
+ SMARTLIST_FOREACH(resolved_addresses, address_ttl_t *, addr,
+ address_ttl_free(addr));
+ smartlist_free(resolved_addresses);
+ return 0;
}
/** An incoming relay cell has arrived from circuit <b>circ</b> to
@@ -1203,8 +1381,7 @@ connection_edge_process_relay_cell_not_open(
}
if (conn->base_.type == CONN_TYPE_AP &&
rh->command == RELAY_COMMAND_RESOLVED) {
- connection_edge_process_resolved_cell(conn, cell, rh);
- return 0;
+ return connection_edge_process_resolved_cell(conn, cell, rh);
}
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
diff --git a/src/or/relay.h b/src/or/relay.h
index 4e1c7f5..bd5c908 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -83,6 +83,19 @@ int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
#ifdef RELAY_PRIVATE
STATIC int connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
tor_addr_t *addr_out, int *ttl_out);
+/** An address-and-ttl tuple as yielded by resolved_cell_parse */
+typedef struct address_ttl_s {
+ tor_addr_t addr;
+ char *hostname;
+ int ttl;
+} address_ttl_t;
+STATIC void address_ttl_free(address_ttl_t *addr);
+STATIC int resolved_cell_parse(const cell_t *cell, const relay_header_t *rh,
+ smartlist_t *addresses_out, int *errcode_out);
+STATIC void connection_ap_handshake_socks_got_resolve_cell(
+ entry_connection_t *conn,
+ int error_code,
+ smartlist_t *results);
STATIC packed_cell_t *packed_cell_new(void);
STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue);
STATIC size_t cell_queues_get_total_allocation(void);