[tor-commits] [tor/master] Add tests for the rend cache

nickm at torproject.org nickm at torproject.org
Tue Oct 6 15:32:41 UTC 2015


commit ade5005853c17b3ae5923c194680442e0f86db4d
Author: Ola Bini <ola at olabini.se>
Date:   Tue Sep 15 16:21:50 2015 +0200

    Add tests for the rend cache
---
 src/or/rendcache.c           |   37 +-
 src/or/rendcache.h           |   17 +-
 src/or/routerlist.c          |    5 +-
 src/or/routerlist.h          |    3 +-
 src/test/include.am          |    2 +
 src/test/rend_test_helpers.c |   65 +++
 src/test/rend_test_helpers.h |   12 +
 src/test/test.c              |    3 +-
 src/test/test_rendcache.c    | 1178 ++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 1296 insertions(+), 26 deletions(-)

diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index 542d322..800cc24 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -3,9 +3,10 @@
 
 /**
  * \file rendcache.c
- * \brief Hidden service desriptor cache.
+ * \brief Hidden service descriptor cache.
  **/
 
+#define RENDCACHE_PRIVATE
 #include "rendcache.h"
 
 #include "config.h"
@@ -15,11 +16,11 @@
 
 /** Map from service id (as generated by rend_get_service_id) to
  * rend_cache_entry_t. */
-static strmap_t *rend_cache = NULL;
+STATIC strmap_t *rend_cache = NULL;
 
 /** Map from descriptor id to rend_cache_entry_t; only for hidden service
  * directories. */
-static digestmap_t *rend_cache_v2_dir = NULL;
+STATIC digestmap_t *rend_cache_v2_dir = NULL;
 
 /** (Client side only) Map from service id to rend_cache_failure_t. This
  * cache is used to track intro point(IP) failures so we know when to keep
@@ -46,10 +47,10 @@ static digestmap_t *rend_cache_v2_dir = NULL;
  * This scheme allows us to not realy on the descriptor's timestamp (which
  * is rounded down to the hour) to know if we have a newer descriptor. We
  * only rely on the usability of intro points from an internal state. */
-static strmap_t *rend_cache_failure = NULL;
+STATIC strmap_t *rend_cache_failure = NULL;
 
 /** DOCDOC */
-static size_t rend_cache_total_allocation = 0;
+STATIC size_t rend_cache_total_allocation = 0;
 
 /** Initializes the service descriptor cache.
 */
@@ -62,7 +63,7 @@ rend_cache_init(void)
 }
 
 /** Return the approximate number of bytes needed to hold <b>e</b>. */
-static size_t
+STATIC size_t
 rend_cache_entry_allocation(const rend_cache_entry_t *e)
 {
   if (!e)
@@ -80,7 +81,7 @@ rend_cache_get_total_allocation(void)
 }
 
 /** Decrement the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
 rend_cache_decrement_allocation(size_t n)
 {
   static int have_underflowed = 0;
@@ -97,7 +98,7 @@ rend_cache_decrement_allocation(size_t n)
 }
 
 /** Increase the total bytes attributed to the rendezvous cache by n. */
-static void
+STATIC void
 rend_cache_increment_allocation(size_t n)
 {
   static int have_overflowed = 0;
@@ -113,7 +114,7 @@ rend_cache_increment_allocation(size_t n)
 }
 
 /** Helper: free a rend cache failure intro object. */
-static void
+STATIC void
 rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
 {
   if (entry == NULL) {
@@ -124,7 +125,7 @@ rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry)
 
 /** Allocate a rend cache failure intro object and return it. <b>failure</b>
  * is set into the object. This function can not fail. */
-static rend_cache_failure_intro_t *
+STATIC rend_cache_failure_intro_t *
 rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
 {
   rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry));
@@ -134,7 +135,7 @@ rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
 }
 
 /** Helper: free a rend cache failure object. */
-static void
+STATIC void
 rend_cache_failure_entry_free(rend_cache_failure_t *entry)
 {
   if (entry == NULL) {
@@ -160,7 +161,7 @@ rend_cache_failure_entry_free_(void *entry)
 
 /** Allocate a rend cache failure object and return it. This function can
  * not fail. */
-static rend_cache_failure_t *
+STATIC rend_cache_failure_t *
 rend_cache_failure_entry_new(void)
 {
   rend_cache_failure_t *entry = tor_malloc(sizeof(*entry));
@@ -170,7 +171,7 @@ rend_cache_failure_entry_new(void)
 
 /** Remove failure cache entry for the service ID in the given descriptor
  * <b>desc</b>. */
-static void
+STATIC void
 rend_cache_failure_remove(rend_service_descriptor_t *desc)
 {
   char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
@@ -190,7 +191,7 @@ rend_cache_failure_remove(rend_service_descriptor_t *desc)
 }
 
 /** Helper: free storage held by a single service descriptor cache entry. */
-static void
+STATIC void
 rend_cache_entry_free(rend_cache_entry_t *e)
 {
   if (!e)
@@ -304,7 +305,7 @@ rend_cache_failure_purge(void)
  * <b>identity</b> and service ID <b>service_id</b>. If found, the intro
  * failure is set in <b>intro_entry</b> else it stays untouched. Return 1
  * iff found else 0. */
-static int
+STATIC int
 cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
                            rend_cache_failure_intro_t **intro_entry)
 {
@@ -348,7 +349,7 @@ cache_failure_intro_dup(const rend_cache_failure_intro_t *entry)
 /** Add an intro point failure to the failure cache using the relay
  * <b>identity</b> and service ID <b>service_id</b>. Record the
  * <b>failure</b> in that object. */
-static void
+STATIC void
 cache_failure_intro_add(const uint8_t *identity, const char *service_id,
                         rend_intro_point_failure_t failure)
 {
@@ -372,7 +373,7 @@ cache_failure_intro_add(const uint8_t *identity, const char *service_id,
  * descriptor and kept into the failure cache. Then, each intro points that
  * are NOT in the descriptor but in the failure cache for the given
  * <b>service_id</b> are removed from the failure cache. */
-static void
+STATIC void
 validate_intro_point_failure(const rend_service_descriptor_t *desc,
                              const char *service_id)
 {
@@ -652,7 +653,6 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
     log_info(LD_REND, "Successfully stored service descriptor with desc ID "
              "'%s' and len %d.",
              safe_str(desc_id_base32), (int)encoded_size);
-
     /* Statistics: Note down this potentially new HS. */
     if (options->HiddenServiceStatistics) {
       rep_hist_stored_maybe_new_hs(e->parsed->pk);
@@ -887,4 +887,3 @@ rend_cache_store_v2_desc_as_client(const char *desc,
   tor_free(intro_content);
   return retval;
 }
-
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 0512058..8ba1f6f 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -76,5 +76,20 @@ void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
                                    const char *service_id);
 void rend_cache_failure_purge(void);
 
-#endif /* TOR_RENDCACHE_H */
 
+#ifdef RENDCACHE_PRIVATE
+STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e);
+STATIC void rend_cache_entry_free(rend_cache_entry_t *e);
+STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry);
+STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry);
+STATIC int cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, rend_cache_failure_intro_t **intro_entry);
+STATIC void rend_cache_decrement_allocation(size_t n);
+STATIC void rend_cache_increment_allocation(size_t n);
+STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure);
+STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void);
+STATIC void rend_cache_failure_remove(rend_service_descriptor_t *desc);
+STATIC void cache_failure_intro_add(const uint8_t *identity, const char *service_id, rend_intro_point_failure_t failure);
+STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc, const char *service_id);
+#endif
+
+#endif /* TOR_RENDCACHE_H */
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 8bd8039..93dc2fe 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -5184,8 +5184,8 @@ hid_serv_acting_as_directory(void)
 
 /** Return true if this node is responsible for storing the descriptor ID
  * in <b>query</b> and false otherwise. */
-int
-hid_serv_responsible_for_desc_id(const char *query)
+MOCK_IMPL(int, hid_serv_responsible_for_desc_id,
+          (const char *query))
 {
   const routerinfo_t *me;
   routerstatus_t *last_rs;
@@ -5208,4 +5208,3 @@ hid_serv_responsible_for_desc_id(const char *query)
   smartlist_free(responsible);
   return result;
 }
-
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 200533f..ec09f23 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -201,7 +201,7 @@ void refresh_all_country_info(void);
 int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
                                          const char *id);
 int hid_serv_acting_as_directory(void);
-int hid_serv_responsible_for_desc_id(const char *id);
+MOCK_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
 
 void list_pending_microdesc_downloads(digest256map_t *result);
 void launch_descriptor_downloads(int purpose,
@@ -243,4 +243,3 @@ MOCK_DECL(STATIC void, initiate_descriptor_downloads,
 #endif
 
 #endif
-
diff --git a/src/test/include.am b/src/test/include.am
index f7c0204..a9ad570 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -51,6 +51,7 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
 # matters a lot there, and is quite hard to debug if you forget to do it.
 
 src_test_test_SOURCES = \
+	src/test/rend_test_helpers.c \
 	src/test/test.c \
 	src/test/test_accounting.c \
 	src/test/test_addr.c \
@@ -87,6 +88,7 @@ src_test_test_SOURCES = \
 	src/test/test_pt.c \
 	src/test/test_relay.c \
 	src/test/test_relaycell.c \
+	src/test/test_rendcache.c \
 	src/test/test_replay.c \
 	src/test/test_routerkeys.c \
 	src/test/test_routerlist.c \
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
new file mode 100644
index 0000000..06e40a0
--- /dev/null
+++ b/src/test/rend_test_helpers.c
@@ -0,0 +1,65 @@
+#include "rend_test_helpers.h"
+
+#include "test.h"
+#include "rendcommon.h"
+
+/** TODO: Description */
+void
+generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc, char **service_id, int intro_points)
+{
+  rend_service_descriptor_t *generated = NULL;
+  smartlist_t *descs = smartlist_new();
+  time_t now;
+
+  now = time(NULL) + time_diff;
+  create_descriptor(&generated, service_id, intro_points);
+  generated->timestamp = now;
+
+  rend_encode_v2_descriptors(descs, generated, now, 0, REND_NO_AUTH, NULL, NULL);
+  *desc = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  smartlist_free(descs);
+  rend_service_descriptor_free(generated);
+}
+
+
+/** TODO: Description */
+void
+create_descriptor(rend_service_descriptor_t **generated, char **service_id, int intro_points)
+{
+  crypto_pk_t *pk1 = NULL;
+  crypto_pk_t *pk2 = NULL;
+  int i;
+
+  *service_id = tor_malloc(REND_SERVICE_ID_LEN_BASE32+1);
+  pk1 = pk_generate(0);
+  pk2 = pk_generate(1);
+
+  *generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  (*generated)->pk = crypto_pk_dup_key(pk1);
+  rend_get_service_id((*generated)->pk, *service_id);
+
+  (*generated)->version = 2;
+  (*generated)->protocols = 42;
+  (*generated)->intro_nodes = smartlist_new();
+
+  for (i = 0; i < intro_points; i++) {
+    rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+    crypto_pk_t *okey = pk_generate(2 + i);
+    intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
+    intro->extend_info->onion_key = okey;
+    crypto_pk_get_digest(intro->extend_info->onion_key,
+                         intro->extend_info->identity_digest);
+    intro->extend_info->nickname[0] = '$';
+    base16_encode(intro->extend_info->nickname + 1,
+                  sizeof(intro->extend_info->nickname) - 1,
+                  intro->extend_info->identity_digest, DIGEST_LEN);
+    tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536));
+    intro->extend_info->port = 1 + crypto_rand_int(65535);
+    intro->intro_key = crypto_pk_dup_key(pk2);
+    smartlist_add((*generated)->intro_nodes, intro);
+  }
+
+  crypto_pk_free(pk1);
+  crypto_pk_free(pk2);
+}
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
new file mode 100644
index 0000000..00a17be
--- /dev/null
+++ b/src/test/rend_test_helpers.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2014-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+
+#ifndef TOR_REND_TEST_HELPERS_H
+#define TOR_REND_TEST_HELPERS_H
+
+void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc, char **service_id, int intro_points);
+void create_descriptor(rend_service_descriptor_t **generated, char **service_id, int intro_points);
+
+#endif
diff --git a/src/test/test.c b/src/test/test.c
index e10e260..773764c 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1148,6 +1148,7 @@ extern struct testcase_t policy_tests[];
 extern struct testcase_t pt_tests[];
 extern struct testcase_t relay_tests[];
 extern struct testcase_t relaycell_tests[];
+extern struct testcase_t rend_cache_tests[];
 extern struct testcase_t replaycache_tests[];
 extern struct testcase_t router_tests[];
 extern struct testcase_t routerkeys_tests[];
@@ -1195,6 +1196,7 @@ struct testgroup_t testgroups[] = {
   { "pt/", pt_tests },
   { "relay/" , relay_tests },
   { "relaycell/", relaycell_tests },
+  { "rend_cache/", rend_cache_tests },
   { "replaycache/", replaycache_tests },
   { "routerkeys/", routerkeys_tests },
   { "routerlist/", routerlist_tests },
@@ -1208,4 +1210,3 @@ struct testgroup_t testgroups[] = {
   { "dns/", dns_tests },
   END_OF_GROUPS
 };
-
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
new file mode 100644
index 0000000..a005e66
--- /dev/null
+++ b/src/test/test_rendcache.c
@@ -0,0 +1,1178 @@
+/* Copyright (c) 2010-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "test.h"
+#define RENDCACHE_PRIVATE
+#include "rendcache.h"
+#include "router.h"
+#include "routerlist.h"
+#include "config.h"
+#include <openssl/rsa.h>
+#include "rend_test_helpers.h"
+
+#define NS_MODULE rend_cache
+
+static const int RECENT_TIME = -10;
+static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 10);
+static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 10;
+
+extern strmap_t *rend_cache;
+extern digestmap_t *rend_cache_v2_dir;
+extern strmap_t *rend_cache_failure;
+extern size_t rend_cache_total_allocation;
+
+static rend_data_t
+mock_rend_data(char *onion_address)
+{
+  rend_data_t rend_query;
+
+  memset(&rend_query, 0, sizeof(rend_query));
+  strncpy(rend_query.onion_address, onion_address, REND_SERVICE_ID_LEN_BASE32+1);
+  rend_query.auth_type = REND_NO_AUTH;
+  rend_query.hsdirs_fp = smartlist_new();
+  smartlist_add(rend_query.hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa", DIGEST_LEN));
+
+  return rend_query;
+}
+
+static void
+test_rend_cache_lookup_entry(void *data)
+{
+  int ret;
+  rend_data_t mock_rend_query;
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+  rend_cache_entry_t *entry = NULL;
+  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+  char *service_id = NULL;
+  (void)data;
+
+  rend_cache_init();
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+
+  ret = rend_cache_lookup_entry("abababababababab", 0, NULL);
+  tt_int_op(ret, OP_EQ, -ENOENT);
+
+  ret = rend_cache_lookup_entry("invalid query", 2, NULL);
+  tt_int_op(ret, OP_EQ, -EINVAL);
+
+  ret = rend_cache_lookup_entry("abababababababab", 2, NULL);
+  tt_int_op(ret, OP_EQ, -ENOENT);
+
+  ret = rend_cache_lookup_entry("abababababababab", 4224, NULL);
+  tt_int_op(ret, OP_EQ, -ENOENT);
+
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+
+  ret = rend_cache_lookup_entry(service_id, 2, NULL);
+  tt_int_op(ret, OP_EQ, 0);
+
+  ret = rend_cache_lookup_entry(service_id, 2, &entry);
+  tt_assert(entry);
+  tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+  tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+ done:
+  tor_free(desc_holder);
+  tor_free(entry);
+  tor_free(service_id);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client(void *data)
+{
+  rend_cache_store_status_t ret;
+  rend_data_t mock_rend_query;
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+  rend_cache_entry_t *entry = NULL;
+  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+  char *service_id = NULL;
+  char client_cookie[REND_DESC_COOKIE_LEN];
+  (void)data;
+
+  rend_cache_init();
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+
+  // Test success
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, &entry);
+
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+  tt_assert(entry);
+  tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
+  tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
+
+  // Test various failure modes
+
+  // TODO: a too long desc_id_base32 argument crashes the function
+  /* ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, "3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG3TOOLONG", &mock_rend_query, NULL); */
+  /* tt_int_op(ret, OP_EQ, RCS_BADDESC); */
+
+  // Test bad base32 failure
+  // This causes an assertion failure if we're running with assertions. But when doing coverage, we can test it.
+#ifdef TOR_COVERAGE
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, "!xqunszqnaolrrfmtzgaki7mxelgvkj", &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+#endif
+
+  // Test invalid descriptor
+  ret = rend_cache_store_v2_desc_as_client("invalid descriptor", "3xqunszqnaolrrfmtzgaki7mxelgvkje", &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+  // TODO: it doesn't seem to be possible to test invalid service ID condition.
+  // that means it is likely not possible to have that condition without earlier conditions failing first (such as signature checking of the desc)
+
+  // Test mismatch between service ID and onion address
+  rend_cache_init();
+  strncpy(mock_rend_query.onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+  // Test incorrect descriptor ID
+  rend_cache_init();
+  mock_rend_query = mock_rend_data(service_id);
+  desc_id_base32[0]++;
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+  desc_id_base32[0]--;
+
+  // Test too old descriptor
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+
+  generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+  // Test too new descriptor (in the future)
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+
+  generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+  // Test when a descriptor is already in the cache
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+  tor_free(entry);
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+
+  rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, &entry);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+  tt_assert(entry);
+
+  // Test unsuccessful decrypting of introduction points
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+  mock_rend_query = mock_rend_data(service_id);
+  mock_rend_query.auth_type = REND_BASIC_AUTH;
+  client_cookie[0] = 'A';
+  memcpy(mock_rend_query.descriptor_cookie, client_cookie, REND_DESC_COOKIE_LEN);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test successful run when we have REND_BASIC_AUTH but not cookie
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+  mock_rend_query = mock_rend_data(service_id);
+  mock_rend_query.auth_type = REND_BASIC_AUTH;
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when we have no introduction points
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 0);
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+  // Test when we have too many intro points
+  rend_cache_init();
+  tor_free(desc_holder);
+  tor_free(service_id);
+
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, MAX_INTRO_POINTS+1);
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+ done:
+  rend_encoded_v2_service_descriptor_free(desc_holder);
+  tor_free(entry);
+  tor_free(service_id);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_client_with_different_time(void *data)
+{
+  rend_cache_store_status_t ret;
+  rend_data_t mock_rend_query;
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+  rend_service_descriptor_t *generated = NULL;
+  smartlist_t *descs = smartlist_new();
+  time_t t;
+  char *service_id = NULL;
+  rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+  rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+  t = time(NULL);
+
+  create_descriptor(&generated, &service_id, 3);
+
+  generated->timestamp = t + RECENT_TIME;
+  rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0, REND_NO_AUTH, NULL, NULL);
+  desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  smartlist_free(descs);
+  descs = smartlist_new();
+
+  generated->timestamp = (t + RECENT_TIME) - 20;
+  rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0, REND_NO_AUTH, NULL, NULL);
+  desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  (void)data;
+  rend_cache_init();
+
+  // Test when a descriptor is already in the cache and it is newer than the one we submit
+  mock_rend_query = mock_rend_data(service_id);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder_newer->desc_id, DIGEST_LEN);
+  rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when an old descriptor is in the cache and we submit a newer one
+  rend_cache_init();
+  rend_cache_store_v2_desc_as_client(desc_holder_older->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  ret = rend_cache_store_v2_desc_as_client(desc_holder_newer->desc_str, desc_id_base32, &mock_rend_query, NULL);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+  rend_encoded_v2_service_descriptor_free(desc_holder_newer);
+  rend_encoded_v2_service_descriptor_free(desc_holder_older);
+  smartlist_free(descs);
+  rend_service_descriptor_free(generated);
+  tor_free(service_id);
+}
+
+
+#define NS_SUBMODULE lookup_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+  if(!mock_routerinfo) {
+    mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+  }
+
+  return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+  return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_lookup_v2_desc_as_dir(void *data)
+{
+  int ret;
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+  char *service_id = NULL;
+  const char *ret_desc = NULL;
+
+  (void)data;
+
+  NS_MOCK(router_get_my_routerinfo);
+  NS_MOCK(hid_serv_responsible_for_desc_id);
+
+  rend_cache_init();
+
+  // Test invalid base32
+  ret = rend_cache_lookup_v2_desc_as_dir("!bababababababab", NULL);
+  tt_int_op(ret, OP_EQ, -1);
+
+  // Test non-existent descriptor but well formed
+  ret = rend_cache_lookup_v2_desc_as_dir("3xqunszqnaolrrfmtzgaki7mxelgvkje", NULL);
+  tt_int_op(ret, OP_EQ, 0);
+
+  // Test existing descriptor
+  hid_serv_responsible_for_desc_id_response = 1;
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+  rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN);
+  ret = rend_cache_lookup_v2_desc_as_dir(desc_id_base32, &ret_desc);
+  tt_int_op(ret, OP_EQ, 1);
+  tt_assert(ret_desc);
+
+ done:
+  NS_UNMOCK(router_get_my_routerinfo);
+  NS_UNMOCK(hid_serv_responsible_for_desc_id);
+  tor_free(mock_routerinfo);
+}
+
+#undef NS_SUBMODULE
+
+#define NS_SUBMODULE store_v2_desc_as_dir
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static routerinfo_t *mock_routerinfo;
+static int hid_serv_responsible_for_desc_id_response;
+
+static const routerinfo_t *
+NS(router_get_my_routerinfo)(void)
+{
+  return mock_routerinfo;
+}
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+  return hid_serv_responsible_for_desc_id_response;
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir(void *data)
+{
+  (void)data;
+  rend_cache_store_status_t ret;
+  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
+  char *service_id = NULL;
+
+  NS_MOCK(router_get_my_routerinfo);
+  NS_MOCK(hid_serv_responsible_for_desc_id);
+
+  rend_cache_init();
+
+  // Test when we are not an HS dir
+  mock_routerinfo = NULL;
+  ret = rend_cache_store_v2_desc_as_dir("");
+  tt_int_op(ret, OP_EQ, RCS_NOTDIR);
+
+  // Test when we can't parse the descriptor
+  mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+  hid_serv_responsible_for_desc_id_response = 1;
+  ret = rend_cache_store_v2_desc_as_dir("unparseable");
+  tt_int_op(ret, OP_EQ, RCS_BADDESC);
+
+  // Test when we are not responsible for an HS
+  hid_serv_responsible_for_desc_id_response = 0;
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when we have an old descriptor
+  hid_serv_responsible_for_desc_id_response = 1;
+  generate_desc(TIME_IN_THE_PAST, &desc_holder, &service_id, 3);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when we have a descriptor in the future
+  generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when two descriptors
+  generate_desc(TIME_IN_THE_FUTURE, &desc_holder, &service_id, 3);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when asking for hidden service statistics  HiddenServiceStatistics
+  rend_cache_purge();
+  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
+  get_options_mutable()->HiddenServiceStatistics = 1;
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+  NS_UNMOCK(router_get_my_routerinfo);
+  NS_UNMOCK(hid_serv_responsible_for_desc_id);
+  tor_free(desc_holder);
+  tor_free(service_id);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_time(void *data)
+{
+  (void)data;
+
+  rend_cache_store_status_t ret;
+  rend_service_descriptor_t *generated = NULL;
+  smartlist_t *descs = smartlist_new();
+  time_t t;
+  char *service_id = NULL;
+  rend_encoded_v2_service_descriptor_t *desc_holder_newer;
+  rend_encoded_v2_service_descriptor_t *desc_holder_older;
+
+  NS_MOCK(router_get_my_routerinfo);
+  NS_MOCK(hid_serv_responsible_for_desc_id);
+
+  rend_cache_init();
+
+  t = time(NULL);
+
+  create_descriptor(&generated, &service_id, 3);
+  generated->timestamp = t + RECENT_TIME;
+  rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0, REND_NO_AUTH, NULL, NULL);
+  desc_holder_newer = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  smartlist_free(descs);
+  descs = smartlist_new();
+
+  generated->timestamp = (t + RECENT_TIME) - 20;
+  rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0, REND_NO_AUTH, NULL, NULL);
+  desc_holder_older = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  // Test when we have a newer descriptor stored
+  mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+  hid_serv_responsible_for_desc_id_response = 1;
+  rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+  // Test when we have an old descriptor stored
+  rend_cache_purge();
+  rend_cache_store_v2_desc_as_dir(desc_holder_older->desc_str);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder_newer->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+  NS_UNMOCK(router_get_my_routerinfo);
+  NS_UNMOCK(hid_serv_responsible_for_desc_id);
+}
+
+static void
+test_rend_cache_store_v2_desc_as_dir_with_different_content(void *data)
+{
+  (void)data;
+
+  rend_cache_store_status_t ret;
+  rend_service_descriptor_t *generated = NULL;
+  smartlist_t *descs = smartlist_new();
+  time_t t;
+  char *service_id = NULL;
+  rend_encoded_v2_service_descriptor_t *desc_holder_one;
+  rend_encoded_v2_service_descriptor_t *desc_holder_two;
+
+  NS_MOCK(router_get_my_routerinfo);
+  NS_MOCK(hid_serv_responsible_for_desc_id);
+
+  rend_cache_init();
+
+  t = time(NULL);
+
+  create_descriptor(&generated, &service_id, 3);
+  generated->timestamp = t + RECENT_TIME;
+  rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0, REND_NO_AUTH, NULL, NULL);
+  desc_holder_one = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  smartlist_free(descs);
+  descs = smartlist_new();
+
+  generated->timestamp = t + RECENT_TIME;
+  generated->protocols = 41;
+  rend_encode_v2_descriptors(descs, generated, t + RECENT_TIME, 0, REND_NO_AUTH, NULL, NULL);
+  desc_holder_two = ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0));
+
+  // Test when we have another descriptor stored, with a different descriptor
+  mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
+  hid_serv_responsible_for_desc_id_response = 1;
+  rend_cache_store_v2_desc_as_dir(desc_holder_one->desc_str);
+  ret = rend_cache_store_v2_desc_as_dir(desc_holder_two->desc_str);
+  tt_int_op(ret, OP_EQ, RCS_OKAY);
+
+ done:
+  NS_UNMOCK(router_get_my_routerinfo);
+  NS_UNMOCK(hid_serv_responsible_for_desc_id);
+}
+
+
+#undef NS_SUBMODULE
+
+static void
+test_rend_cache_init(void *data)
+{
+  (void)data;
+
+  tt_assert_msg(!rend_cache, "rend_cache should be NULL when starting");
+  tt_assert_msg(!rend_cache_v2_dir, "rend_cache_v2_dir should be NULL when starting");
+  tt_assert_msg(!rend_cache_failure, "rend_cache_failure should be NULL when starting");
+
+  rend_cache_init();
+
+  tt_assert_msg(rend_cache, "rend_cache should not be NULL after initing");
+  tt_assert_msg(rend_cache_v2_dir, "rend_cache_v2_dir should not be NULL after initing");
+  tt_assert_msg(rend_cache_failure, "rend_cache_failure should not be NULL after initing");
+
+  tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+  (void)0;
+}
+
+static void
+test_rend_cache_decrement_allocation(void *data)
+{
+  (void)data;
+
+  // Test when the cache has enough allocations
+  rend_cache_total_allocation = 10;
+  rend_cache_decrement_allocation(3);
+  tt_int_op(rend_cache_total_allocation, OP_EQ, 7);
+
+  // Test when there are not enough allocations
+  rend_cache_total_allocation = 1;
+  rend_cache_decrement_allocation(2);
+  tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+  // And again
+  rend_cache_decrement_allocation(2);
+  tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+
+ done:
+  (void)0;
+}
+
+static void
+test_rend_cache_increment_allocation(void *data)
+{
+  (void)data;
+
+  // Test when the cache is not overflowing
+  rend_cache_total_allocation = 5;
+  rend_cache_increment_allocation(3);
+  tt_int_op(rend_cache_total_allocation, OP_EQ, 8);
+
+  // Test when there are too many allocations
+  rend_cache_total_allocation = SIZE_MAX-1;
+  rend_cache_increment_allocation(2);
+  tt_int_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+  // And again
+  rend_cache_increment_allocation(2);
+  tt_int_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+
+ done:
+  (void)0;
+}
+
+
+static void
+test_rend_cache_failure_intro_entry_new(void *data)
+{
+  time_t now;
+  rend_cache_failure_intro_t *entry;
+  rend_intro_point_failure_t failure;
+
+  (void)data;
+
+  failure = INTRO_POINT_FAILURE_TIMEOUT;
+  now = time(NULL);
+  entry = rend_cache_failure_intro_entry_new(failure);
+
+  tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+  tt_int_op(entry->created_ts, OP_GE, now-5);
+  tt_int_op(entry->created_ts, OP_LE, now+5);
+
+ done:
+  tor_free(entry);
+}
+
+static void
+test_rend_cache_failure_intro_lookup(void *data)
+{
+  (void)data;
+  int ret;
+  rend_cache_failure_t *failure;
+  rend_cache_failure_intro_t *ip;
+  rend_cache_failure_intro_t *entry;
+
+  rend_cache_init();
+
+  failure = rend_cache_failure_entry_new();
+  ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  digestmap_set(failure->intro_failures, "ip1", ip);
+  strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+  // Test not found
+  ret = cache_failure_intro_lookup((const uint8_t *)"foo1", "foo2", NULL);
+  tt_int_op(ret, OP_EQ, 0);
+
+  // Test found with no intro failures in it
+  ret = cache_failure_intro_lookup((const uint8_t *)"ip2", "foo1", NULL);
+  tt_int_op(ret, OP_EQ, 0);
+
+  // Test found
+  ret = cache_failure_intro_lookup((const uint8_t *)"ip1", "foo1", NULL);
+  tt_int_op(ret, OP_EQ, 1);
+
+  // Test found and asking for entry
+  cache_failure_intro_lookup((const uint8_t *)"ip1", "foo1", &entry);
+  tt_assert(entry);
+  tt_assert(entry == ip);
+
+ done:
+  rend_cache_purge();
+}
+
+static void
+test_rend_cache_clean(void *data)
+{
+  rend_cache_entry_t *one, *two;
+  rend_service_descriptor_t *desc_one, *desc_two;
+  strmap_iter_t *iter = NULL;
+  const char *key;
+  void *val;
+
+  (void)data;
+
+  rend_cache_init();
+
+  // Test with empty rendcache
+  rend_cache_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+  // Test with two old entries
+  one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  one->parsed = desc_one;
+  two->parsed = desc_two;
+
+  desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+  desc_two->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+  desc_one->pk = pk_generate(0);
+  desc_two->pk = pk_generate(1);
+
+  strmap_set_lc(rend_cache, "foo1", one);
+  strmap_set_lc(rend_cache, "foo2", two);
+
+  rend_cache_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+  // Test with one old entry and one newer entry
+  one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  two = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc_two = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  one->parsed = desc_one;
+  two->parsed = desc_two;
+
+  desc_one->timestamp = (time(NULL) + TIME_IN_THE_PAST) - 10;
+  desc_two->timestamp = time(NULL) - 100;
+  desc_one->pk = pk_generate(0);
+  desc_two->pk = pk_generate(1);
+
+  strmap_set_lc(rend_cache, "foo1", one);
+  strmap_set_lc(rend_cache, "foo2", two);
+
+  rend_cache_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache), OP_EQ, 1);
+
+  iter = strmap_iter_init(rend_cache);
+  strmap_iter_get(iter, &key, &val);
+  tt_str_op(key, OP_EQ, "foo2");
+
+ done:
+  (void)1;
+}
+
+static void
+test_rend_cache_failure_entry_new(void *data)
+{
+  rend_cache_failure_t *failure;
+
+  (void)data;
+
+  failure = rend_cache_failure_entry_new();
+  tt_assert(failure);
+  tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 0);
+
+ done:
+  tor_free(failure);
+}
+
+static void
+test_rend_cache_failure_entry_free(void *data)
+{
+  (void)data;
+
+  // Test that it can deal with a NULL argument
+  rend_cache_failure_entry_free(NULL);
+
+ /* done: */
+ /*  (void)0; */
+}
+
+static void
+test_rend_cache_failure_clean(void *data)
+{
+  rend_cache_failure_t *failure;
+  rend_cache_failure_intro_t *ip_one, *ip_two;
+
+  (void)data;
+
+  rend_cache_init();
+
+  // Test with empty failure cache
+  rend_cache_failure_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+  // Test with one empty failure entry
+  failure = rend_cache_failure_entry_new();
+  strmap_set_lc(rend_cache_failure, "foo1", failure);
+  rend_cache_failure_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+  // Test with one new intro point
+  failure = rend_cache_failure_entry_new();
+  ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  digestmap_set(failure->intro_failures, "ip1", ip_one);
+  strmap_set_lc(rend_cache_failure, "foo1", failure);
+  rend_cache_failure_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+
+  // Test with one old intro point
+  rend_cache_failure_purge();
+  failure = rend_cache_failure_entry_new();
+  ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  ip_one->created_ts = time(NULL) - 7*60;
+  digestmap_set(failure->intro_failures, "ip1", ip_one);
+  strmap_set_lc(rend_cache_failure, "foo1", failure);
+  rend_cache_failure_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+
+  // Test with one old intro point and one new one
+  rend_cache_failure_purge();
+  failure = rend_cache_failure_entry_new();
+  ip_one = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  ip_one->created_ts = time(NULL) - 7*60;
+  digestmap_set(failure->intro_failures, "ip1", ip_one);
+  ip_two = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  ip_two->created_ts = time(NULL) - 2*60;
+  digestmap_set(failure->intro_failures, "ip2", ip_two);
+  strmap_set_lc(rend_cache_failure, "foo1", failure);
+  rend_cache_failure_clean(time(NULL));
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 1);
+  tt_int_op(digestmap_size(failure->intro_failures), OP_EQ, 1);
+
+ done:
+  (void)0;
+}
+
+
+static void
+test_rend_cache_failure_remove(void *data)
+{
+  rend_service_descriptor_t *desc;
+  (void)data;
+
+  rend_cache_init();
+
+  // Test that it deals well with a NULL desc
+  rend_cache_failure_remove(NULL);
+
+  // Test a descriptor that isn't in the cache
+  desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc->pk = pk_generate(0);
+  rend_cache_failure_remove(desc);
+
+  // There seems to not exist any way of getting rend_cache_failure_remove() to fail because of a problem with rend_get_service_id from here
+
+ /* done: */
+ /*  (void)0; */
+}
+
+static void
+test_rend_cache_free_all(void *data)
+{
+  rend_cache_failure_t *failure;
+  rend_cache_entry_t *one;
+  rend_service_descriptor_t *desc_one;
+
+  (void)data;
+
+  rend_cache_init();
+
+  failure = rend_cache_failure_entry_new();
+  strmap_set_lc(rend_cache_failure, "foo1", failure);
+
+  one = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  desc_one = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  one->parsed = desc_one;
+  desc_one->timestamp = time(NULL) + TIME_IN_THE_PAST;
+  desc_one->pk = pk_generate(0);
+  strmap_set_lc(rend_cache, "foo1", one);
+
+  rend_cache_free_all();
+
+  tt_assert(!rend_cache);
+  tt_assert(!rend_cache_v2_dir);
+  tt_assert(!rend_cache_failure);
+  tt_assert(!rend_cache_total_allocation);
+
+ done:
+  (void)0;
+}
+
+
+static void
+test_rend_cache_entry_free(void *data)
+{
+  (void)data;
+  rend_cache_entry_t *e;
+
+  // Handles NULL correctly
+  rend_cache_entry_free(NULL);
+
+  // Handles NULL descriptor correctly
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  rend_cache_entry_free(e);
+
+  // Handles non-NULL descriptor correctly
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  e->desc = (char *)malloc(10);
+  rend_cache_entry_free(e);
+
+ /* done: */
+ /*  (void)0; */
+}
+
+
+static void
+test_rend_cache_purge(void *data)
+{
+  strmap_t *our_rend_cache;
+
+  (void)data;
+
+  // Deals with a NULL rend_cache
+  rend_cache_purge();
+  tt_assert(rend_cache);
+  tt_int_op(strmap_size(rend_cache), OP_EQ, 0);
+
+  // Deals with existing rend_cache
+  rend_cache_init();
+
+  our_rend_cache = rend_cache;
+  rend_cache_purge();
+  tt_assert(rend_cache);
+  tt_assert(rend_cache == our_rend_cache);
+
+ done:
+  (void)0;
+}
+
+static void
+test_rend_cache_failure_intro_add(void *data)
+{
+  (void)data;
+  rend_cache_failure_t *fail_entry;
+  rend_cache_failure_intro_t *entry;
+
+  rend_cache_init();
+
+  // Adds non-existing entry
+  cache_failure_intro_add((const uint8_t *)"foo1", "foo2", INTRO_POINT_FAILURE_TIMEOUT);
+  fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+  tt_assert(fail_entry);
+  tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+  entry = digestmap_get(fail_entry->intro_failures, "foo1");
+  tt_assert(entry);
+
+  // Adds existing entry
+  cache_failure_intro_add((const uint8_t *)"foo1", "foo2", INTRO_POINT_FAILURE_TIMEOUT);
+  fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+  tt_assert(fail_entry);
+  tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+  entry = digestmap_get(fail_entry->intro_failures, "foo1");
+  tt_assert(entry);
+
+ done:
+  rend_cache_purge();
+}
+
+
+static void
+test_rend_cache_intro_failure_note(void *data)
+{
+  (void)data;
+  rend_cache_failure_t *fail_entry;
+  rend_cache_failure_intro_t *entry;
+
+  // Test not found
+  rend_cache_intro_failure_note(INTRO_POINT_FAILURE_TIMEOUT,(const uint8_t *)"foo1", "foo2");
+  fail_entry = strmap_get_lc(rend_cache_failure, "foo2");
+  tt_assert(fail_entry);
+  tt_int_op(digestmap_size(fail_entry->intro_failures), OP_EQ, 1);
+  entry = digestmap_get(fail_entry->intro_failures, "foo1");
+  tt_assert(entry);
+  tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_TIMEOUT);
+
+  // Test found
+  rend_cache_intro_failure_note(INTRO_POINT_FAILURE_UNREACHABLE,(const uint8_t *)"foo1", "foo2");
+  tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_UNREACHABLE);
+
+ done:
+  rend_cache_purge();
+}
+
+#define NS_SUBMODULE clean_v2_descs_as_dir
+NS_DECL(int, hid_serv_responsible_for_desc_id, (const char *id));
+
+static int hid_serv_responsible_for_desc_id_response;
+
+static int
+NS(hid_serv_responsible_for_desc_id)(const char *id)
+{
+  return hid_serv_responsible_for_desc_id_response;
+}
+
+
+static void
+test_rend_cache_clean_v2_descs_as_dir(void *data)
+{
+  rend_cache_entry_t *e;
+  time_t now;
+  rend_service_descriptor_t *desc;
+  now = time(NULL);
+
+  (void)data;
+
+  NS_MOCK(hid_serv_responsible_for_desc_id);
+  rend_cache_init();
+
+  // Test running with an empty cache
+  rend_cache_clean_v2_descs_as_dir(now, 0);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+  // Test with only one new entry
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  e->last_served = now;
+  desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc->timestamp = now;
+  desc->pk = pk_generate(0);
+  e->parsed = desc;
+  digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+  hid_serv_responsible_for_desc_id_response = 1;
+  rend_cache_clean_v2_descs_as_dir(now, 0);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+  // Test with one old entry
+  desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+  rend_cache_clean_v2_descs_as_dir(now, 0);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+  // Test with one entry that is not under the responsibility of this hidden service
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  e->last_served = now;
+  desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc->timestamp = now;
+  desc->pk = pk_generate(0);
+  e->parsed = desc;
+  digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+  hid_serv_responsible_for_desc_id_response = 0;
+  rend_cache_clean_v2_descs_as_dir(now, 0);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+  // Test with one entry that has an old last served
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000);
+  desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc->timestamp = now;
+  desc->pk = pk_generate(0);
+  e->parsed = desc;
+  digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+  hid_serv_responsible_for_desc_id_response = 1;
+  rend_cache_clean_v2_descs_as_dir(now, 0);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
+
+  // Test a run through asking for a large force_remove
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  e->last_served = now;
+  desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  desc->timestamp = now;
+  desc->pk = pk_generate(0);
+  e->parsed = desc;
+  digestmap_set(rend_cache_v2_dir, "abcde", e);
+
+  hid_serv_responsible_for_desc_id_response = 1;
+  rend_cache_clean_v2_descs_as_dir(now, 20000);
+  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
+
+
+
+ done:
+  NS_UNMOCK(hid_serv_responsible_for_desc_id);
+  rend_cache_purge();
+}
+
+#undef NS_SUBMODULE
+
+
+static void
+test_rend_cache_entry_allocation(void *data)
+{
+  (void)data;
+
+  int ret;
+  rend_cache_entry_t *e;
+
+  // Handles a null argument
+  ret = rend_cache_entry_allocation(NULL);
+  tt_int_op(ret, OP_EQ, 0);
+
+  // Handles a non-null argument
+  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+  ret = rend_cache_entry_allocation(e);
+  tt_int_op(ret, OP_EQ, 88);
+
+ done:
+  (void)0;
+}
+
+static void
+test_rend_cache_failure_intro_entry_free(void *data)
+{
+  (void)data;
+  rend_cache_failure_intro_t *entry;
+
+  // Handles a null argument
+  rend_cache_failure_intro_entry_free(NULL);
+
+  // Handles a non-null argument
+  entry = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  rend_cache_failure_intro_entry_free(entry);
+}
+
+
+static void
+test_rend_cache_failure_purge(void *data)
+{
+  (void)data;
+
+  // Handles a null failure cache
+  rend_cache_failure = NULL;
+
+  rend_cache_failure_purge();
+
+  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
+
+ done:
+  (void)0;
+}
+
+static void
+test_rend_cache_validate_intro_point_failure(void *data)
+{
+  (void)data;
+  rend_service_descriptor_t *desc = NULL;
+  char *service_id = NULL;
+  rend_intro_point_t *intro = NULL;
+  const uint8_t *identity = NULL;
+  rend_cache_failure_t *failure;
+  rend_cache_failure_intro_t *ip;
+
+  rend_cache_init();
+
+  create_descriptor(&desc, &service_id, 3);
+  desc->timestamp = time(NULL) + RECENT_TIME;
+
+  intro = (rend_intro_point_t *)smartlist_get(desc->intro_nodes, 0);
+  identity = (uint8_t *) intro->extend_info->identity_digest;
+
+  failure = rend_cache_failure_entry_new();
+  ip = rend_cache_failure_intro_entry_new(INTRO_POINT_FAILURE_TIMEOUT);
+  digestmap_set(failure->intro_failures, (char *)identity, ip);
+  strmap_set_lc(rend_cache_failure, service_id, failure);
+
+  // Test when we have an intro point in our cache
+  validate_intro_point_failure(desc, service_id);
+  tt_int_op(smartlist_len(desc->intro_nodes), OP_EQ, 2);
+
+ done:
+  rend_cache_purge();
+}
+
+struct testcase_t rend_cache_tests[] = {
+  { "init", test_rend_cache_init, 0, NULL, NULL },
+  { "decrement_allocation", test_rend_cache_decrement_allocation, 0, NULL, NULL },
+  { "increment_allocation", test_rend_cache_increment_allocation, 0, NULL, NULL },
+  { "clean", test_rend_cache_clean, TT_FORK, NULL, NULL },
+  { "clean_v2_descs_as_dir", test_rend_cache_clean_v2_descs_as_dir, 0, NULL, NULL },
+  { "entry_allocation", test_rend_cache_entry_allocation, 0, NULL, NULL },
+  { "entry_free", test_rend_cache_entry_free, 0, NULL, NULL },
+  { "failure_intro_entry_free", test_rend_cache_failure_intro_entry_free, 0, NULL, NULL },
+  { "free_all", test_rend_cache_free_all, 0, NULL, NULL },
+  { "purge", test_rend_cache_purge, 0, NULL, NULL },
+  { "failure_clean", test_rend_cache_failure_clean, 0, NULL, NULL },
+  { "failure_entry_new", test_rend_cache_failure_entry_new, 0, NULL, NULL },
+  { "failure_entry_free", test_rend_cache_failure_entry_free, 0, NULL, NULL },
+  { "failure_intro_add", test_rend_cache_failure_intro_add, 0, NULL, NULL },
+  { "failure_intro_entry_new", test_rend_cache_failure_intro_entry_new, 0, NULL, NULL },
+  { "failure_intro_lookup", test_rend_cache_failure_intro_lookup, 0, NULL, NULL },
+  { "failure_purge", test_rend_cache_failure_purge, 0, NULL, NULL },
+  { "failure_remove", test_rend_cache_failure_remove, 0, NULL, NULL },
+  { "intro_failure_note", test_rend_cache_intro_failure_note, 0, NULL, NULL },
+  { "lookup", test_rend_cache_lookup_entry, 0, NULL, NULL },
+  { "lookup_v2_desc_as_dir", test_rend_cache_lookup_v2_desc_as_dir, 0, NULL, NULL },
+  { "store_v2_desc_as_client", test_rend_cache_store_v2_desc_as_client, 0, NULL, NULL },
+  { "store_v2_desc_as_client_with_different_time", test_rend_cache_store_v2_desc_as_client_with_different_time, 0, NULL, NULL },
+  { "store_v2_desc_as_dir", test_rend_cache_store_v2_desc_as_dir, 0, NULL, NULL },
+  { "store_v2_desc_as_dir_with_different_time", test_rend_cache_store_v2_desc_as_dir_with_different_time, 0, NULL, NULL },
+  { "store_v2_desc_as_dir_with_different_content", test_rend_cache_store_v2_desc_as_dir_with_different_content, 0, NULL, NULL },
+  { "validate_intro_point_failure", test_rend_cache_validate_intro_point_failure, 0, NULL, NULL },
+  END_OF_TESTCASES
+};





More information about the tor-commits mailing list