From cac5335195d3bb9a39af77886992ffa98b8c7817 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 31 Oct 2012 18:27:48 -0400 Subject: [PATCH] Get the client side of receiving an IPv6 address to work This makes it so we can handle getting an IPv6 in the 3 different formats we specified it for in RESOLVED cells, END_STREAM_REASON_EXITPOLICY cells, and CONNECTED cells. We don't cache IPv6 addresses yet, since proposal 205 isn't implemented. There's a refactored function for parsing connected cells; it has unit tests. --- src/or/addressmap.c | 35 +++++---- src/or/addressmap.h | 2 +- src/or/connection_edge.c | 15 +++- src/or/connection_edge.h | 1 + src/or/relay.c | 134 +++++++++++++++++++++++++++-------- src/or/relay.h | 2 + src/test/test_cell_formats.c | 114 +++++++++++++++++++++++++++++ 7 files changed, 254 insertions(+), 49 deletions(-) diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 4aa46fc6ec..45e29c1856 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -576,10 +576,7 @@ client_dns_set_addressmap_impl(origin_circuit_t *on_circ, const char *exitname, int ttl) { - /*
..exit\0 or just
\0 */ - char extendedaddress[MAX_SOCKS_ADDR_LEN+MAX_VERBOSE_NICKNAME_LEN+10]; - /* 123.123.123.123..exit\0 or just 123.123.123.123\0 */ - char extendedval[INET_NTOA_BUF_LEN+MAX_VERBOSE_NICKNAME_LEN+10]; + char *extendedaddress=NULL, *extendedval=NULL; (void)on_circ; tor_assert(address); @@ -594,18 +591,19 @@ client_dns_set_addressmap_impl(origin_circuit_t *on_circ, /* XXXX fails to ever get attempts to get an exit address of * google.com.digest[=~]nickname.exit; we need a syntax for this that * won't make strict RFC952-compliant applications (like us) barf. */ - tor_snprintf(extendedaddress, sizeof(extendedaddress), + tor_asprintf(&extendedaddress, "%s.%s.exit", address, exitname); - tor_snprintf(extendedval, sizeof(extendedval), + tor_asprintf(&extendedval, "%s.%s.exit", name, exitname); } else { - tor_snprintf(extendedaddress, sizeof(extendedaddress), + tor_asprintf(&extendedaddress, "%s", address); - tor_snprintf(extendedval, sizeof(extendedval), + tor_asprintf(&extendedval, "%s", name); } - addressmap_register(extendedaddress, tor_strdup(extendedval), + addressmap_register(extendedaddress, extendedval, time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0); + tor_free(extendedaddress); } /** Record the fact that address resolved to val. @@ -620,19 +618,26 @@ client_dns_set_addressmap_impl(origin_circuit_t *on_circ, */ void client_dns_set_addressmap(origin_circuit_t *on_circ, - const char *address, uint32_t val, + const char *address, + const tor_addr_t *val, const char *exitname, int ttl) { - struct in_addr in; - char valbuf[INET_NTOA_BUF_LEN]; + tor_addr_t addr_tmp; + char valbuf[TOR_ADDR_BUF_LEN]; tor_assert(address); + tor_assert(val); - if (tor_inet_aton(address, &in)) + if (tor_addr_parse(&addr_tmp, address) == 0) return; /* If address was an IP address already, don't add a mapping. */ - in.s_addr = htonl(val); - tor_inet_ntoa(&in,valbuf,sizeof(valbuf)); + + /* XXXXX For now, don't cache IPv6 addresses. */ + if (tor_addr_family(val) != AF_INET) + return; + + if (! tor_addr_to_str(valbuf, val, sizeof(valbuf), 0)) /* XXXX decorate? */ + return; client_dns_set_addressmap_impl(on_circ, address, valbuf, exitname, ttl); } diff --git a/src/or/addressmap.h b/src/or/addressmap.h index dd8fc9bcfc..9b07341479 100644 --- a/src/or/addressmap.h +++ b/src/or/addressmap.h @@ -29,7 +29,7 @@ int parse_virtual_addr_network(const char *val, int validate_only, int client_dns_incr_failures(const char *address); void client_dns_clear_failures(const char *address); void client_dns_set_addressmap(origin_circuit_t *on_circ, - const char *address, uint32_t val, + const char *address, const tor_addr_t *val, const char *exitname, int ttl); const char *addressmap_register_virtual_address(int type, char *new_address); void addressmap_get_mappings(smartlist_t *sl, time_t min_expires, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index dc7d863f49..969128fcfb 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1949,10 +1949,19 @@ connection_ap_handshake_socks_resolved(entry_connection_t *conn, if (CIRCUIT_IS_ORIGIN(circ)) /* should always be true */ origin_circ = TO_ORIGIN_CIRCUIT(circ); if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { - uint32_t a = ntohl(get_uint32(answer)); - if (a) { + tor_addr_t a; + tor_addr_from_ipv4n(&a, get_uint32(answer)); + if (! tor_addr_is_null(&a)) { client_dns_set_addressmap(origin_circ, - conn->socks_request->address, a, + conn->socks_request->address, &a, + conn->chosen_exit_name, ttl); + } + } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) { + tor_addr_t a; + tor_addr_from_ipv6_bytes(&a, (char*)answer); + if (! tor_addr_is_null(&a)) { + client_dns_set_addressmap(origin_circ, + conn->socks_request->address, &a, conn->chosen_exit_name, ttl); } } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) { diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 39f492de9c..9c4318ccfe 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -108,6 +108,7 @@ typedef struct begin_cell_t { int begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, uint8_t *end_reason_out); + #endif diff --git a/src/or/relay.c b/src/or/relay.c index 76e9d2550b..a05e955ecd 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -705,21 +705,37 @@ connection_ap_process_end_not_open( switch (reason) { case END_STREAM_REASON_EXITPOLICY: if (rh->length >= 5) { - uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); - int ttl; - if (!addr) { + tor_addr_t addr; + int ttl = -1; + tor_addr_make_unspec(&addr); + if (rh->length == 5 || rh->length == 9) { + tor_addr_from_ipv4n(&addr, + get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); + if (rh->length == 9) + ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5)); + } else if (rh->length == 17 || rh->length == 21) { + tor_addr_from_ipv6_bytes(&addr, + (char*)(cell->payload+RELAY_HEADER_SIZE+1)); + if (rh->length == 21) + ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17)); + } + if (tor_addr_is_null(&addr)) { log_info(LD_APP,"Address '%s' resolved to 0.0.0.0. Closing,", safe_str(conn->socks_request->address)); connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); return 0; } - if (rh->length >= 9) - ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5)); - else - ttl = -1; + if ((tor_addr_family(&addr) == AF_INET && !conn->ipv4_traffic_ok) || + (tor_addr_family(&addr) == AF_INET6 && !conn->ipv6_traffic_ok)) { + log_fn(LOG_PROTOCOL_WARN, LD_APP, + "Got an EXITPOLICY failure on a connection with a " + "mismatched family. Closing."); + connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); + return 0; + } if (get_options()->ClientDNSRejectInternalAddresses && - is_internal_IP(addr, 0)) { + tor_addr_is_internal(&addr, 0)) { log_info(LD_APP,"Address '%s' resolved to internal. Closing,", safe_str(conn->socks_request->address)); connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); @@ -727,7 +743,7 @@ connection_ap_process_end_not_open( } client_dns_set_addressmap(circ, - conn->socks_request->address, addr, + conn->socks_request->address, &addr, conn->chosen_exit_name, ttl); } /* check if he *ought* to have allowed it */ @@ -830,20 +846,56 @@ connection_ap_process_end_not_open( } /** Helper: change the socks_request->address field on conn to the - * dotted-quad representation of new_addr (given in host order), + * dotted-quad representation of new_addr, * and send an appropriate REMAP event. */ static void -remap_event_helper(entry_connection_t *conn, uint32_t new_addr) +remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr) { - struct in_addr in; - - in.s_addr = htonl(new_addr); - tor_inet_ntoa(&in, conn->socks_request->address, - sizeof(conn->socks_request->address)); + tor_addr_to_str(conn->socks_request->address, new_addr, + sizeof(conn->socks_request->address), + 0); /* XXXXX Should decorate be 1? */ control_event_stream_status(conn, STREAM_EVENT_REMAP, REMAP_STREAM_SOURCE_EXIT); } +/**DOCDOC*/ +int +connected_cell_parse(const relay_header_t *rh, const cell_t *cell, + tor_addr_t *addr_out, int *ttl_out) +{ + uint32_t bytes; + const uint8_t *payload = cell->payload + RELAY_HEADER_SIZE; + + tor_addr_make_unspec(addr_out); + *ttl_out = -1; + if (rh->length == 0) + return 0; + if (rh->length < 4) + return -1; + bytes = ntohl(get_uint32(payload)); + + /* If bytes is 0, this is maybe a v6 address. Otherwise it's a v4 address */ + if (bytes != 0) { + /* v4 address */ + tor_addr_from_ipv4h(addr_out, bytes); + if (rh->length >= 8) { + bytes = ntohl(get_uint32(payload + 4)); + if (bytes <= INT32_MAX) + *ttl_out = bytes; + } + } else { + if (rh->length < 25) /* 4 bytes of 0s, 1 addr, 16 ipv4, 4 ttl. */ + return -1; + if (get_uint8(payload + 4) != 6) + return -1; + tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5)); + bytes = ntohl(get_uint32(payload + 21)); + if (bytes <= INT32_MAX) + *ttl_out = (int) bytes; + } + return 0; +} + /** An incoming relay cell has arrived from circuit circ to * stream conn. * @@ -874,6 +926,8 @@ connection_edge_process_relay_cell_not_open( if (conn->base_.type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_CONNECTED) { + tor_addr_t addr; + int ttl; entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn); tor_assert(CIRCUIT_IS_ORIGIN(circ)); if (conn->base_.state != AP_CONN_STATE_CONNECT_WAIT) { @@ -884,27 +938,41 @@ connection_edge_process_relay_cell_not_open( conn->base_.state = AP_CONN_STATE_OPEN; log_info(LD_APP,"'connected' received after %d seconds.", (int)(time(NULL) - conn->base_.timestamp_lastread)); - if (rh->length >= 4) { - uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); - int ttl; - if (!addr || (get_options()->ClientDNSRejectInternalAddresses && - is_internal_IP(addr, 0))) { + if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_APP, + "Got a badly formatted connected cell. Closing."); + connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL); + connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL); + } + if (tor_addr_family(&addr) != AF_UNSPEC) { + const sa_family_t family = tor_addr_family(&addr); + if (tor_addr_is_null(&addr) || + (get_options()->ClientDNSRejectInternalAddresses && + tor_addr_is_internal(&addr, 0))) { log_info(LD_APP, "...but it claims the IP address was %s. Closing.", - fmt_addr32(addr)); + fmt_addr(&addr)); connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL); connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL); return 0; } - if (rh->length >= 8) - ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+4)); - else - ttl = -1; + + if ((family == AF_INET && ! entry_conn->ipv4_traffic_ok) || + (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) { + log_fn(LOG_PROTOCOL_WARN, LD_APP, + "Got a connected cell to %s with unsupported address family." + " Closing.", fmt_addr(&addr)); + connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL); + connection_mark_unattached_ap(entry_conn, + END_STREAM_REASON_TORPROTOCOL); + return 0; + } + client_dns_set_addressmap(TO_ORIGIN_CIRCUIT(circ), - entry_conn->socks_request->address, addr, + entry_conn->socks_request->address, &addr, entry_conn->chosen_exit_name, ttl); - remap_event_helper(entry_conn, addr); + remap_event_helper(entry_conn, &addr); } circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ)); /* don't send a socks reply to transparent conns */ @@ -994,8 +1062,14 @@ connection_edge_process_relay_cell_not_open( ttl, -1); if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { - uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2)); - remap_event_helper(entry_conn, addr); + 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_mark_unattached_ap(entry_conn, END_STREAM_REASON_DONE | diff --git a/src/or/relay.h b/src/or/relay.h index 0f7b45fef3..57400fdbd5 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -68,6 +68,8 @@ void circuit_clear_cell_queue(circuit_t *circ, channel_t *chan); #ifdef RELAY_PRIVATE int relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction, crypt_path_t **layer_hint, char *recognized); +int connected_cell_parse(const relay_header_t *rh, const cell_t *cell, + tor_addr_t *addr_out, int *ttl_out); #endif #endif diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index 2199a05fc3..3fac74519f 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -6,6 +6,7 @@ #include "orconfig.h" #define CONNECTION_EDGE_PRIVATE +#define RELAY_PRIVATE #include "or.h" #include "connection_edge.h" #include "relay.h" @@ -223,12 +224,125 @@ test_cfmt_begin_cells(void *arg) tor_free(bcell.address); } +static void +test_cfmt_connected_cells(void *arg) +{ + relay_header_t rh; + cell_t cell; + tor_addr_t addr; + int ttl, r; + (void)arg; + + /* Let's try an oldschool one with nothing in it. */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "", 0); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_UNSPEC); + tt_int_op(ttl, ==, -1); + + /* A slightly less oldschool one: only an IPv4 address */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "\x20\x30\x40\x50", 4); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_INET); + tt_str_op(fmt_addr(&addr), ==, "32.48.64.80"); + tt_int_op(ttl, ==, -1); + + /* Bogus but understandable: truncated TTL */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, "\x11\x12\x13\x14\x15", 5); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_INET); + tt_str_op(fmt_addr(&addr), ==, "17.18.19.20"); + tt_int_op(ttl, ==, -1); + + /* Regular IPv4 one: address and TTL */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x02\x03\x04\x05\x00\x00\x0e\x10", 8); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_INET); + tt_str_op(fmt_addr(&addr), ==, "2.3.4.5"); + tt_int_op(ttl, ==, 3600); + + /* IPv4 with too-big TTL */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x02\x03\x04\x05\xf0\x00\x00\x00", 8); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_INET); + tt_str_op(fmt_addr(&addr), ==, "2.3.4.5"); + tt_int_op(ttl, ==, -1); + + /* IPv6 (ttl is mandatory) */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x00\x00\x00\x00\x06" + "\x26\x07\xf8\xb0\x40\x0c\x0c\x02" + "\x00\x00\x00\x00\x00\x00\x00\x68" + "\x00\x00\x02\x58", 25); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_INET6); + tt_str_op(fmt_addr(&addr), ==, "2607:f8b0:400c:c02::68"); + tt_int_op(ttl, ==, 600); + + /* IPv6 (ttl too big) */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x00\x00\x00\x00\x06" + "\x26\x07\xf8\xb0\x40\x0c\x0c\x02" + "\x00\x00\x00\x00\x00\x00\x00\x68" + "\x90\x00\x02\x58", 25); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, 0); + tt_int_op(tor_addr_family(&addr), ==, AF_INET6); + tt_str_op(fmt_addr(&addr), ==, "2607:f8b0:400c:c02::68"); + tt_int_op(ttl, ==, -1); + + /* Bogus size: 3. */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x00\x01\x02", 3); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, -1); + + /* Bogus family: 7. */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x00\x00\x00\x00\x07" + "\x26\x07\xf8\xb0\x40\x0c\x0c\x02" + "\x00\x00\x00\x00\x00\x00\x00\x68" + "\x90\x00\x02\x58", 25); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, -1); + + /* Truncated IPv6. */ + make_relay_cell(&cell, RELAY_COMMAND_CONNECTED, + "\x00\x00\x00\x00\x06" + "\x26\x07\xf8\xb0\x40\x0c\x0c\x02" + "\x00\x00\x00\x00\x00\x00\x00\x68" + "\x00\x00\x02", 24); + relay_header_unpack(&rh, cell.payload); + r = connected_cell_parse(&rh, &cell, &addr, &ttl); + tt_int_op(r, ==, -1); + + done: + ; +} + #define TEST(name, flags) \ { #name, test_cfmt_ ## name, flags, 0, NULL } struct testcase_t cell_format_tests[] = { TEST(relay_header, 0), TEST(begin_cells, 0), + TEST(connected_cells, 0), END_OF_TESTCASES };