Browse Source

Add a unittest that reveals the offending case of #23862.

George Kadianakis 6 years ago
parent
commit
8c8d3b90f7
6 changed files with 122 additions and 7 deletions
  1. 1 3
      src/or/directory.c
  2. 3 0
      src/or/directory.h
  3. 3 3
      src/or/entrynodes.c
  4. 4 0
      src/or/entrynodes.h
  5. 1 1
      src/or/networkstatus.c
  6. 110 0
      src/test/test_routerlist.c

+ 1 - 3
src/or/directory.c

@@ -2237,8 +2237,6 @@ load_downloaded_routers(const char *body, smartlist_t *which,
   return added;
 }
 
-static int handle_response_fetch_consensus(dir_connection_t *,
-                                           const response_handler_args_t *);
 static int handle_response_fetch_certificate(dir_connection_t *,
                                              const response_handler_args_t *);
 static int handle_response_fetch_status_vote(dir_connection_t *,
@@ -2585,7 +2583,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
  * consensus document by checking the consensus, storing it, and marking
  * router requests as reachable.
  **/
-static int
+STATIC int
 handle_response_fetch_consensus(dir_connection_t *conn,
                                 const response_handler_args_t *args)
 {

+ 3 - 0
src/or/directory.h

@@ -194,6 +194,9 @@ STATIC void warn_disallowed_anonymous_compression_method(compress_method_t);
 STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
                                           const response_handler_args_t *args);
 
+STATIC int handle_response_fetch_consensus(dir_connection_t *conn,
+                                         const response_handler_args_t *args);
+
 #endif /* defined(DIRECTORY_PRIVATE) */
 
 #ifdef TOR_UNIT_TESTS

+ 3 - 3
src/or/entrynodes.c

@@ -2126,9 +2126,9 @@ circuit_guard_state_free(circuit_guard_state_t *state)
 
 /** Allocate and return a new circuit_guard_state_t to track the result
  * of using <b>guard</b> for a given operation. */
-static circuit_guard_state_t *
-circuit_guard_state_new(entry_guard_t *guard, unsigned state,
-                        entry_guard_restriction_t *rst)
+MOCK_IMPL(STATIC circuit_guard_state_t *,
+circuit_guard_state_new,(entry_guard_t *guard, unsigned state,
+                         entry_guard_restriction_t *rst))
 {
   circuit_guard_state_t *result;
 

+ 4 - 0
src/or/entrynodes.h

@@ -488,6 +488,10 @@ STATIC entry_guard_t *get_sampled_guard_with_id(guard_selection_t *gs,
 
 MOCK_DECL(STATIC time_t, randomize_time, (time_t now, time_t max_backdate));
 
+MOCK_DECL(STATIC circuit_guard_state_t *,
+          circuit_guard_state_new,(entry_guard_t *guard, unsigned state,
+                                   entry_guard_restriction_t *rst));
+
 STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs,
                                                 const node_t *node);
 STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs);

+ 1 - 1
src/or/networkstatus.c

@@ -1720,7 +1720,7 @@ networkstatus_set_current_consensus(const char *consensus,
 {
   networkstatus_t *c=NULL;
   int r, result = -1;
-  time_t now = time(NULL);
+  time_t now = approx_time();
   const or_options_t *options = get_options();
   char *unverified_fname = NULL, *consensus_fname = NULL;
   int flav = networkstatus_parse_flavor_name(flavor);

+ 110 - 0
src/test/test_routerlist.c

@@ -6,13 +6,17 @@
 #include <time.h>
 
 #define DIRVOTE_PRIVATE
+#define ENTRYNODES_PRIVATE
+#define DIRECTORY_PRIVATE
 #define NETWORKSTATUS_PRIVATE
+#define CONNECTION_PRIVATE
 #define ROUTERLIST_PRIVATE
 #define TOR_UNIT_TESTING
 #include "or.h"
 #include "config.h"
 #include "connection.h"
 #include "container.h"
+#include "control.h"
 #include "directory.h"
 #include "dirvote.h"
 #include "entrynodes.h"
@@ -22,10 +26,13 @@
 #include "policies.h"
 #include "router.h"
 #include "routerlist.h"
+#include "routerset.h"
 #include "routerparse.h"
 #include "shared_random.h"
+#include "statefile.h"
 #include "test.h"
 #include "test_dir_common.h"
+#include "log_test_helpers.h"
 
 void construct_consensus(char **consensus_text_md);
 
@@ -411,6 +418,107 @@ test_router_pick_directory_server_impl(void *arg)
   networkstatus_vote_free(con_md);
 }
 
+static or_state_t *dummy_state = NULL;
+static or_state_t *
+get_or_state_replacement(void)
+{
+  return dummy_state;
+}
+
+static void
+mock_directory_initiate_request(directory_request_t *req)
+{
+  (void)req;
+  return;
+}
+
+static circuit_guard_state_t *
+mock_circuit_guard_state_new(entry_guard_t *guard, unsigned state,
+                        entry_guard_restriction_t *rst)
+{
+  (void) guard;
+  (void) state;
+  (void) rst;
+  return NULL;
+}
+
+/** Test that we will use our directory guards to fetch mds even if we don't
+ *  have any dirinfo (tests bug #23862). */
+static void
+test_directory_guard_fetch_with_no_dirinfo(void *arg)
+{
+  int retval;
+  char *consensus_text_md = NULL;
+  or_options_t *options = get_options_mutable();
+
+  (void) arg;
+
+  /* Initialize the SRV subsystem */
+  sr_init(0);
+
+  /* Initialize the entry node configuration from the ticket */
+  options->UseEntryGuards = 1;
+  options->StrictNodes = 1;
+  get_options_mutable()->EntryNodes = routerset_new();
+  routerset_parse(get_options_mutable()->EntryNodes,
+                  "2121212121212121212121212121212121212121", "foo");
+
+  /* Mock some functions */
+  dummy_state = tor_malloc_zero(sizeof(or_state_t));
+  MOCK(get_or_state, get_or_state_replacement);
+  MOCK(directory_initiate_request, mock_directory_initiate_request);
+  /* we need to mock this one to avoid memleaks */
+  MOCK(circuit_guard_state_new, mock_circuit_guard_state_new);
+
+  /* Call guards_update_all() to simulate loading our state file (see
+   * entry_guards_load_guards_from_state() and ticket #23989). */
+  guards_update_all();
+
+  /* Test logic: Simulate the arrival of a new consensus when we have no
+   * dirinfo at all. Tor will need to fetch the mds from the consensus. Make
+   * sure that Tor will use the specified entry guard instead of relying on the
+   * fallback directories. */
+
+  /* Fixup the dirconn that will deliver the consensus */
+  dir_connection_t *conn = dir_connection_new(AF_INET);
+  tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001);
+  conn->base_.port = 8800;
+  TO_CONN(conn)->address = tor_strdup("127.0.0.1");
+  conn->base_.purpose = DIR_PURPOSE_FETCH_CONSENSUS;
+  conn->requested_resource = tor_strdup("ns");
+
+  /* Construct a consensus */
+  construct_consensus(&consensus_text_md);
+  tt_assert(consensus_text_md);
+
+  /* Place the consensus in the dirconn */
+  response_handler_args_t args;
+  memset(&args, 0, sizeof(response_handler_args_t));
+  args.status_code = 200;
+  args.body = consensus_text_md;
+  args.body_len = strlen(consensus_text_md);
+
+  /* Update approx time so that the consensus is considered live */
+  update_approx_time(time(NULL)+1010);
+
+  setup_capture_of_logs(LOG_DEBUG);
+
+  /* Now handle the consensus */
+  retval = handle_response_fetch_consensus(conn, &args);
+  tt_int_op(retval, OP_EQ, 0);
+
+  /* Make sure that our primary guard was chosen */
+  /* BUG #23862 falls back to dirserver list!! */
+  expect_log_msg_containing("falling back to dirserver list");
+
+ done:
+  tor_free(consensus_text_md);
+  tor_free(dummy_state);
+  connection_free_(TO_CONN(conn));
+  entry_guards_free_all();
+  teardown_capture_of_logs();
+}
+
 static connection_t *mocked_connection = NULL;
 
 /* Mock connection_get_by_type_addr_port_purpose by returning
@@ -494,6 +602,8 @@ struct testcase_t routerlist_tests[] = {
   NODE(launch_descriptor_downloads, 0),
   NODE(router_is_already_dir_fetching, TT_FORK),
   ROUTER(pick_directory_server_impl, TT_FORK),
+  { "directory_guard_fetch_with_no_dirinfo",
+    test_directory_guard_fetch_with_no_dirinfo, TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };