Browse Source

Unit tests for connection_edge_process_resolved_cell

Also rename a function to be more accurate (resolve->resolved)
Nick Mathewson 10 years ago
parent
commit
da908a593f
7 changed files with 282 additions and 29 deletions
  1. 6 6
      src/or/connection_edge.c
  2. 13 11
      src/or/connection_edge.h
  3. 8 8
      src/or/relay.c
  4. 3 4
      src/or/relay.h
  5. 1 0
      src/test/include.am
  6. 2 0
      src/test/test.c
  7. 249 0
      src/test/test_relaycell.c

+ 6 - 6
src/or/connection_edge.c

@@ -67,9 +67,9 @@ static int connection_ap_supports_optimistic_data(const entry_connection_t *);
  * a socks reply, send one now (based on endreason). Also set
  * has_sent_end to 1, and mark the conn.
  */
-void
-connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
-                               int line, const char *file)
+MOCK_IMPL(void,
+connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason,
+                                int line, const char *file))
 {
   connection_t *base_conn = ENTRY_TO_CONN(conn);
   edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
@@ -2093,13 +2093,13 @@ connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn,
  **/
 /* XXXX the use of the ttl and expires fields is nutty.  Let's make this
  * interface and those that use it less ugly. */
-void
-connection_ap_handshake_socks_resolved(entry_connection_t *conn,
+MOCK_IMPL(void,
+connection_ap_handshake_socks_resolved,(entry_connection_t *conn,
                                        int answer_type,
                                        size_t answer_len,
                                        const uint8_t *answer,
                                        int ttl,
-                                       time_t expires)
+                                       time_t expires))
 {
   char buf[384];
   size_t replylen;

+ 13 - 11
src/or/connection_edge.h

@@ -17,8 +17,9 @@
 #define connection_mark_unattached_ap(conn, endreason) \
   connection_mark_unattached_ap_((conn), (endreason), __LINE__, SHORT_FILE__)
 
-void connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
-                                    int line, const char *file);
+MOCK_DECL(void,connection_mark_unattached_ap_,
+          (entry_connection_t *conn, int endreason,
+           int line, const char *file));
 int connection_edge_reached_eof(edge_connection_t *conn);
 int connection_edge_process_inbuf(edge_connection_t *conn,
                                   int package_partial);
@@ -44,16 +45,17 @@ entry_connection_t  *connection_ap_make_link(connection_t *partner,
 void connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
                                          size_t replylen,
                                          int endreason);
-void connection_ap_handshake_socks_resolved(entry_connection_t *conn,
-                                            int answer_type,
-                                            size_t answer_len,
-                                            const uint8_t *answer,
-                                            int ttl,
-                                            time_t expires);
+MOCK_DECL(void,connection_ap_handshake_socks_resolved,
+          (entry_connection_t *conn,
+           int answer_type,
+           size_t answer_len,
+           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);
+                                                 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);

+ 8 - 8
src/or/relay.c

@@ -1121,10 +1121,10 @@ resolved_cell_parse(const cell_t *cell, const relay_header_t *rh,
 /** 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)
+static void
+connection_ap_handshake_socks_got_resolved_cell(entry_connection_t *conn,
+                                                int error_code,
+                                                smartlist_t *results)
 {
   address_ttl_t *addr_ipv4 = NULL;
   address_ttl_t *addr_ipv6 = NULL;
@@ -1195,7 +1195,7 @@ connection_ap_handshake_socks_got_resolve_cell(entry_connection_t *conn,
 
 /** Handle a RELAY_COMMAND_RESOLVED cell that we received on a non-open AP
  * stream. */
-static int
+STATIC int
 connection_edge_process_resolved_cell(edge_connection_t *conn,
                                       const cell_t *cell,
                                       const relay_header_t *rh)
@@ -1242,9 +1242,9 @@ connection_edge_process_resolved_cell(edge_connection_t *conn,
     }
   }
 
-  connection_ap_handshake_socks_got_resolve_cell(entry_conn,
-                                                 errcode,
-                                                 resolved_addresses);
+  connection_ap_handshake_socks_got_resolved_cell(entry_conn,
+                                                  errcode,
+                                                  resolved_addresses);
 
   connection_mark_unattached_ap(entry_conn,
                               END_STREAM_REASON_DONE |

+ 3 - 4
src/or/relay.h

@@ -92,10 +92,9 @@ typedef struct address_ttl_s {
 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 int connection_edge_process_resolved_cell(edge_connection_t *conn,
+                                                 const cell_t *cell,
+                                                 const relay_header_t *rh);
 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);

+ 1 - 0
src/test/include.am

@@ -35,6 +35,7 @@ src_test_test_SOURCES = \
 	src/test/test_oom.c \
 	src/test/test_options.c \
 	src/test/test_pt.c \
+	src/test/test_relaycell.c \
 	src/test/test_replay.c \
 	src/test/test_routerkeys.c \
 	src/test/test_socks.c \

+ 2 - 0
src/test/test.c

@@ -1623,6 +1623,7 @@ extern struct testcase_t pt_tests[];
 extern struct testcase_t config_tests[];
 extern struct testcase_t introduce_tests[];
 extern struct testcase_t replaycache_tests[];
+extern struct testcase_t relaycell_tests[];
 extern struct testcase_t cell_format_tests[];
 extern struct testcase_t circuitlist_tests[];
 extern struct testcase_t circuitmux_tests[];
@@ -1654,6 +1655,7 @@ static struct testgroup_t testgroups[] = {
   { "pt/", pt_tests },
   { "config/", config_tests },
   { "replaycache/", replaycache_tests },
+  { "relaycell/", relaycell_tests },
   { "introduce/", introduce_tests },
   { "circuitlist/", circuitlist_tests },
   { "circuitmux/", circuitmux_tests },

+ 249 - 0
src/test/test_relaycell.c

@@ -0,0 +1,249 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Unit tests for handling different kinds of relay cell */
+
+#define RELAY_PRIVATE
+#include "or.h"
+#include "config.h"
+#include "connection.h"
+#include "connection_edge.h"
+#include "relay.h"
+#include "test.h"
+
+static int srm_ncalls;
+static entry_connection_t *srm_conn;
+static int srm_atype;
+static size_t srm_alen;
+static int srm_answer_is_set;
+static uint8_t srm_answer[512];
+static int srm_ttl;
+static time_t srm_expires;
+
+/* Mock replacement for connection_ap_hannshake_socks_resolved() */
+static void
+socks_resolved_mock(entry_connection_t *conn,
+                    int answer_type,
+                    size_t answer_len,
+                    const uint8_t *answer,
+                    int ttl,
+                    time_t expires)
+{
+  srm_ncalls++;
+  srm_conn = conn;
+  srm_atype = answer_type;
+  srm_alen = answer_len;
+  if (answer) {
+    memset(srm_answer, 0, sizeof(srm_answer));
+    memcpy(srm_answer, answer, answer_len < 512 ? answer_len : 512);
+    srm_answer_is_set = 1;
+  } else {
+    srm_answer_is_set = 0;
+  }
+  srm_ttl = ttl;
+  srm_expires = expires;
+}
+
+static int mum_ncalls;
+static entry_connection_t *mum_conn;
+static int mum_endreason;
+
+/* Mock replacement for connection_mark_unattached_ap_() */
+static void
+mark_unattached_mock(entry_connection_t *conn, int endreason,
+                     int line, const char *file)
+{
+  ++mum_ncalls;
+  mum_conn = conn;
+  mum_endreason = endreason;
+  (void) line;
+  (void) file;
+}
+
+/* Tests for connection_edge_process_resolved_cell().
+
+   The point of ..process_resolved_cell() is to handle an incoming cell
+   on an entry connection, and call connection_mark_unattached_ap() and/or
+   connection_ap_handshake_socks_resolved().
+ */
+static void
+test_relaycell_resolved(void *arg)
+{
+  entry_connection_t *entryconn;
+  edge_connection_t *edgeconn;
+  cell_t cell;
+  relay_header_t rh;
+  int r;
+  or_options_t *options = get_options_mutable();
+
+#define SET_CELL(s) do {                                                \
+    memset(&cell, 0, sizeof(cell));                                     \
+    memset(&rh, 0, sizeof(rh));                                         \
+    memcpy(cell.payload + RELAY_HEADER_SIZE, (s), sizeof((s))-1);       \
+    rh.length = sizeof((s))-1;                                          \
+    rh.command = RELAY_COMMAND_RESOLVED;                                \
+  } while (0)
+#define MOCK_RESET() do {                       \
+    srm_ncalls = mum_ncalls = 0;                \
+  } while (0)
+#define ASSERT_MARK_CALLED(reason) do {         \
+    tt_int_op(mum_ncalls, ==, 1);               \
+    tt_ptr_op(mum_conn, ==, entryconn);         \
+    tt_int_op(mum_endreason, ==, (reason));     \
+  } while (0)
+#define ASSERT_RESOLVED_CALLED(atype, answer, ttl, expires) do {  \
+    tt_int_op(srm_ncalls, ==, 1);                                 \
+    tt_ptr_op(srm_conn, ==, entryconn);                           \
+    tt_int_op(srm_atype, ==, (atype));                            \
+    if (answer) {                                                 \
+      tt_int_op(srm_alen, ==, sizeof(answer)-1);                  \
+      tt_int_op(srm_alen, <, 512);                                \
+      tt_int_op(srm_answer_is_set, ==, 1);                        \
+      tt_mem_op(srm_answer, ==, answer, sizeof(answer)-1);        \
+    } else {                                                      \
+      tt_int_op(srm_answer_is_set, ==, 0);                        \
+    }                                                             \
+    tt_int_op(srm_ttl, ==, ttl);                                  \
+    tt_int_op(srm_expires, ==, expires);                          \
+  } while (0)
+
+  (void)arg;
+
+  MOCK(connection_mark_unattached_ap_, mark_unattached_mock);
+  MOCK(connection_ap_handshake_socks_resolved, socks_resolved_mock);
+
+  options->ClientDNSRejectInternalAddresses = 0;
+
+  SET_CELL(/* IPv4: 127.0.1.2, ttl 256 */
+           "\x04\x04\x7f\x00\x01\x02\x00\x00\x01\x00"
+           /* IPv4: 18.0.0.1, ttl 512 */
+           "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00"
+           /* IPv6: 2003::3, ttl 1024 */
+           "\x06\x10"
+           "\x20\x02\x00\x00\x00\x00\x00\x00"
+           "\x00\x00\x00\x00\x00\x00\x00\x03"
+           "\x00\x00\x04\x00");
+
+  entryconn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+  edgeconn = ENTRY_TO_EDGE_CONN(entryconn);
+
+  /* Try with connection in non-RESOLVE_WAIT state: cell gets ignored */
+  MOCK_RESET();
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  tt_int_op(srm_ncalls, ==, 0);
+  tt_int_op(mum_ncalls, ==, 0);
+
+  /* Now put it in the right state. */
+  ENTRY_TO_CONN(entryconn)->state = AP_CONN_STATE_RESOLVE_WAIT;
+  entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE;
+  entryconn->ipv4_traffic_ok = 1;
+  entryconn->ipv6_traffic_ok = 1;
+  entryconn->prefer_ipv6_traffic = 0;
+
+  /* We prefer ipv4, so we should get the first ipv4 answer */
+  MOCK_RESET();
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x7f\x00\x01\x02", 256, -1);
+
+  /* But we may be discarding private answers. */
+  MOCK_RESET();
+  options->ClientDNSRejectInternalAddresses = 1;
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x12\x00\x00\x01", 512, -1);
+
+  /* now prefer ipv6, and get the first ipv6 answer */
+  entryconn->prefer_ipv6_traffic = 1;
+  MOCK_RESET();
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV6,
+                         "\x20\x02\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x03",
+                         1024, -1);
+
+  /* With a cell that only has IPv4, we report IPv4 even if we prefer IPv6 */
+  MOCK_RESET();
+  SET_CELL("\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00");
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_IPV4, "\x12\x00\x00\x01", 512, -1);
+
+  /* But if we don't allow IPv4, we report nothing if the cell contains only
+   * ipv4 */
+  MOCK_RESET();
+  entryconn->ipv4_traffic_ok = 0;
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR, NULL, -1, -1);
+
+  /* If we wanted hostnames, we report nothing, since we only had IPs. */
+  MOCK_RESET();
+  entryconn->ipv4_traffic_ok = 1;
+  entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR, NULL, -1, -1);
+
+  /* A hostname cell is fine though. */
+  MOCK_RESET();
+  SET_CELL("\x00\x0fwww.example.com\x00\x01\x00\x00");
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_HOSTNAME, "www.example.com", 65536, -1);
+
+  /* error on malformed cell */
+  MOCK_RESET();
+  entryconn->socks_request->command = SOCKS_COMMAND_RESOLVE;
+  SET_CELL("\x04\x04\x01\x02\x03\x04"); /* no ttl */
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_TORPROTOCOL);
+  tt_int_op(srm_ncalls, ==, 0);
+
+  /* error on all addresses private */
+  MOCK_RESET();
+  SET_CELL(/* IPv4: 127.0.1.2, ttl 256 */
+           "\x04\x04\x7f\x00\x01\x02\x00\x00\x01\x00"
+           /* IPv4: 192.168.1.1, ttl 256 */
+           "\x04\x04\xc0\xa8\x01\x01\x00\x00\x01\x00");
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_TORPROTOCOL);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR_TRANSIENT, NULL, 0, TIME_MAX);
+
+  /* Legit error code */
+  MOCK_RESET();
+  SET_CELL("\xf0\x15" "quiet and meaningless" "\x00\x00\x0f\xff");
+  r = connection_edge_process_resolved_cell(edgeconn, &cell, &rh);
+  tt_int_op(r, ==, 0);
+  ASSERT_MARK_CALLED(END_STREAM_REASON_DONE|
+                     END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+  ASSERT_RESOLVED_CALLED(RESOLVED_TYPE_ERROR_TRANSIENT, NULL, -1, -1);
+
+ done:
+  UNMOCK(connection_mark_unattached_ap_);
+  UNMOCK(connection_ap_handshake_socks_resolved);
+}
+
+struct testcase_t relaycell_tests[] = {
+  { "resolved", test_relaycell_resolved, TT_FORK, NULL, NULL },
+  END_OF_TESTCASES
+};
+