Forráskód Böngészése

Merge branch 'bug6177_squashed'

Nick Mathewson 11 éve
szülő
commit
d4e2ccc387

+ 5 - 0
changes/bug6177

@@ -0,0 +1,5 @@
+  o Code simplification and refactoring:
+    - Add replaycache_t structure, functions and unit tests, for future use
+      in refactoring rend_service_introduce() for bug 6177.
+    - Refactor rend_service_introduce() to be more clear to read, improve,
+      debug, and test. Bug 6177.

+ 2 - 0
src/or/Makefile.am

@@ -48,6 +48,7 @@ libtor_a_SOURCES = \
 	rendmid.c				\
 	rendservice.c				\
 	rephist.c				\
+	replaycache.c				\
 	router.c				\
 	routerlist.c				\
 	routerparse.c				\
@@ -114,6 +115,7 @@ noinst_HEADERS = \
 	rendmid.h				\
 	rendservice.h				\
 	rephist.h				\
+	replaycache.h				\
 	router.h				\
 	routerlist.h				\
 	routerparse.h				\

+ 10 - 6
src/or/or.h

@@ -98,6 +98,7 @@
 #include "address.h"
 #include "compat_libevent.h"
 #include "ht.h"
+#include "replaycache.h"
 
 /* These signals are defined to help handle_control_signal work.
  */
@@ -4214,12 +4215,15 @@ typedef struct rend_intro_point_t {
    * intro point. */
   unsigned int rend_service_note_removing_intro_point_called : 1;
 
-  /** (Service side only) A digestmap recording the INTRODUCE2 cells
-   * this intro point's circuit has received.  Each key is the digest
-   * of the RSA-encrypted part of a received INTRODUCE2 cell; each
-   * value is a pointer to the time_t at which the cell was received.
-   * This digestmap is used to prevent replay attacks. */
-  digestmap_t *accepted_intro_rsa_parts;
+  /** (Service side only) A replay cache recording the RSA-encrypted parts
+   * of INTRODUCE2 cells this intro point's circuit has received.  This is
+   * used to prevent replay attacks. */
+  replaycache_t *accepted_intro_rsa_parts;
+
+  /** (Service side only) Count of INTRODUCE2 cells accepted from this
+   * intro point.
+   */
+  int accepted_introduce2_count;
 
   /** (Service side only) The time at which this intro point was first
    * published, or -1 if this intro point has not yet been

+ 1 - 1
src/or/rendcommon.c

@@ -439,7 +439,7 @@ rend_intro_point_free(rend_intro_point_t *intro)
   crypto_pk_free(intro->intro_key);
 
   if (intro->accepted_intro_rsa_parts != NULL) {
-    digestmap_free(intro->accepted_intro_rsa_parts, _tor_free);
+    replaycache_free(intro->accepted_intro_rsa_parts);
   }
 
   tor_free(intro);

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1010 - 238
src/or/rendservice.c


+ 73 - 0
src/or/rendservice.h

@@ -12,6 +12,64 @@
 #ifndef _TOR_RENDSERVICE_H
 #define _TOR_RENDSERVICE_H
 
+#include "or.h"
+
+typedef struct rend_intro_cell_s rend_intro_cell_t;
+
+#ifdef RENDSERVICE_PRIVATE
+
+/* This can be used for both INTRODUCE1 and INTRODUCE2 */
+
+struct rend_intro_cell_s {
+  /* Is this an INTRODUCE1 or INTRODUCE2? (set to 1 or 2) */
+  uint8_t type;
+  /* Public key digest */
+  uint8_t pk[DIGEST_LEN];
+  /* Optionally, store ciphertext here */
+  uint8_t *ciphertext;
+  ssize_t ciphertext_len;
+  /* Optionally, store plaintext */
+  uint8_t *plaintext;
+  ssize_t plaintext_len;
+  /* Have we parsed the plaintext? */
+  uint8_t parsed;
+  /* intro protocol version (0, 1, 2 or 3) */
+  uint8_t version;
+  /* Version-specific parts */
+  union {
+    struct {
+      /* Rendezvous point nickname */
+      uint8_t rp[20];
+    } v0;
+    struct {
+      /* Rendezvous point nickname or hex-encoded key digest */
+      uint8_t rp[42];
+    } v1;
+    struct {
+      /* The extend_info_t struct has everything v2 uses */
+      extend_info_t *extend_info;
+    } v2;
+    struct {
+      /* Auth type used */
+      uint8_t auth_type;
+      /* Length of auth data */
+      uint16_t auth_len;
+      /* Auth data */
+      uint8_t *auth_data;
+      /* timestamp */
+      uint32_t timestamp;
+      /* Rendezvous point's IP address/port, identity digest and onion key */
+      extend_info_t *extend_info;
+    } v3;
+  } u;
+  /* Rendezvous cookie */
+  uint8_t rc[REND_COOKIE_LEN];
+  /* Diffie-Hellman data */
+  uint8_t dh[DH_KEY_LEN];
+};
+
+#endif
+
 int num_rend_services(void);
 int rend_config_services(const or_options_t *options, int validate_only);
 int rend_service_load_all_keys(void);
@@ -27,6 +85,21 @@ int rend_service_intro_established(origin_circuit_t *circuit,
 void rend_service_rendezvous_has_opened(origin_circuit_t *circuit);
 int rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
                            size_t request_len);
+void rend_service_compact_intro(rend_intro_cell_t *request);
+int rend_service_decrypt_intro(rend_intro_cell_t *request,
+                               crypto_pk_t *key,
+                               char **err_msg_out);
+void rend_service_free_intro(rend_intro_cell_t *request);
+rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request,
+                                                   size_t request_len,
+                                                   uint8_t type,
+                                                   char **err_msg_out);
+int rend_service_parse_intro_plaintext(rend_intro_cell_t *intro,
+                                       char **err_msg_out);
+int rend_service_validate_intro_early(const rend_intro_cell_t *intro,
+                                      char **err_msg_out);
+int rend_service_validate_intro_late(const rend_intro_cell_t *intro,
+                                     char **err_msg_out);
 void rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc);
 int rend_service_set_connection_addr_port(edge_connection_t *conn,
                                           origin_circuit_t *circ);

+ 215 - 0
src/or/replaycache.c

@@ -0,0 +1,215 @@
+ /* Copyright (c) 2012, The Tor Project, Inc. */
+ /* See LICENSE for licensing information */
+
+/*
+ * \file replaycache.c
+ *
+ * \brief Self-scrubbing replay cache for rendservice.c
+ */
+
+#define REPLAYCACHE_PRIVATE
+
+#include "or.h"
+#include "replaycache.h"
+
+/** Free the replaycache r and all of its entries.
+ */
+
+void
+replaycache_free(replaycache_t *r)
+{
+  if (!r) {
+    log_info(LD_BUG, "replaycache_free() called on NULL");
+    return;
+  }
+
+  if (r->digests_seen) digestmap_free(r->digests_seen, _tor_free);
+
+  tor_free(r);
+}
+
+/** Allocate a new, empty replay detection cache, where horizon is the time
+ * for entries to age out and interval is the time after which the cache
+ * should be scrubbed for old entries.
+ */
+
+replaycache_t *
+replaycache_new(time_t horizon, time_t interval)
+{
+  replaycache_t *r = NULL;
+
+  if (horizon < 0) {
+    log_info(LD_BUG, "replaycache_new() called with negative"
+        " horizon parameter");
+    goto err;
+  }
+
+  if (interval < 0) {
+    log_info(LD_BUG, "replaycache_new() called with negative interval"
+        " parameter");
+    interval = 0;
+  }
+
+  r = tor_malloc(sizeof(*r));
+  r->scrub_interval = interval;
+  r->scrubbed = 0;
+  r->horizon = horizon;
+  r->digests_seen = digestmap_new();
+
+ err:
+  return r;
+}
+
+/** See documentation for replaycache_add_and_test()
+ */
+
+int
+replaycache_add_and_test_internal(
+    time_t present, replaycache_t *r, const void *data, int len,
+    time_t *elapsed)
+{
+  int rv = 0;
+  char digest[DIGEST_LEN];
+  time_t *access_time;
+
+  /* sanity check */
+  if (present <= 0 || !r || !data || len <= 0) {
+    log_info(LD_BUG, "replaycache_add_and_test_internal() called with stupid"
+        " parameters; please fix this.");
+    goto done;
+  }
+
+  /* compute digest */
+  crypto_digest(digest, (const char *)data, len);
+
+  /* check map */
+  access_time = digestmap_get(r->digests_seen, digest);
+
+  /* seen before? */
+  if (access_time != NULL) {
+    /*
+     * If it's far enough in the past, no hit.  If the horizon is zero, we
+     * never expire.
+     */
+    if (*access_time >= present - r->horizon || r->horizon == 0) {
+      /* replay cache hit, return 1 */
+      rv = 1;
+      /* If we want to output an elapsed time, do so */
+      if (elapsed) {
+        if (present >= *access_time) {
+          *elapsed = present - *access_time;
+        } else {
+          /* We shouldn't really be seeing hits from the future, but... */
+          *elapsed = 0;
+        }
+      }
+    }
+    /*
+     * If it's ahead of the cached time, update
+     */
+    if (*access_time < present) {
+      *access_time = present;
+    }
+  } else {
+    /* No, so no hit and update the digest map with the current time */
+    access_time = tor_malloc(sizeof(*access_time));
+    *access_time = present;
+    digestmap_set(r->digests_seen, digest, access_time);
+  }
+
+  /* now scrub the cache if it's time */
+  replaycache_scrub_if_needed_internal(present, r);
+
+ done:
+  return rv;
+}
+
+/** See documentation for replaycache_scrub_if_needed()
+ */
+
+void
+replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
+{
+  digestmap_iter_t *itr = NULL;
+  const char *digest;
+  void *valp;
+  time_t *access_time;
+  char scrub_this;
+
+  /* sanity check */
+  if (!r || !(r->digests_seen)) {
+    log_info(LD_BUG, "replaycache_scrub_if_needed_internal() called with"
+        " stupid parameters; please fix this.");
+    return;
+  }
+
+  /* scrub time yet? (scrubbed == 0 indicates never scrubbed before) */
+  if (present - r->scrubbed < r->scrub_interval && r->scrubbed > 0) return;
+
+  /* if we're never expiring, don't bother scrubbing */
+  if (r->horizon == 0) return;
+
+  /* okay, scrub time */
+  itr = digestmap_iter_init(r->digests_seen);
+  while (!digestmap_iter_done(itr)) {
+    scrub_this = 0;
+    digestmap_iter_get(itr, &digest, &valp);
+    access_time = (time_t *)valp;
+    if (access_time) {
+      /* aged out yet? */
+      if (*access_time < present - r->horizon) scrub_this = 1;
+    } else {
+      /* Buh? Get rid of it, anyway */
+      log_info(LD_BUG, "replaycache_scrub_if_needed_internal() saw a NULL"
+          " entry in the digestmap.");
+      scrub_this = 1;
+    }
+
+    if (scrub_this) {
+      /* Advance the iterator and remove this one */
+      itr = digestmap_iter_next_rmv(r->digests_seen, itr);
+      /* Free the value removed */
+      tor_free(access_time);
+    } else {
+      /* Just advance the iterator */
+      itr = digestmap_iter_next(r->digests_seen, itr);
+    }
+  }
+
+  /* update scrubbed timestamp */
+  if (present > r->scrubbed) r->scrubbed = present;
+}
+
+/** Test the buffer of length len point to by data against the replay cache r;
+ * the digest of the buffer will be added to the cache at the current time,
+ * and the function will return 1 if it was already seen within the cache's
+ * horizon, or 0 otherwise.
+ */
+
+int
+replaycache_add_and_test(replaycache_t *r, const void *data, int len)
+{
+  return replaycache_add_and_test_internal(time(NULL), r, data, len, NULL);
+}
+
+/** Like replaycache_add_and_test(), but if it's a hit also return the time
+ * elapsed since this digest was last seen.
+ */
+
+int
+replaycache_add_test_and_elapsed(
+    replaycache_t *r, const void *data, int len, time_t *elapsed)
+{
+  return replaycache_add_and_test_internal(time(NULL), r, data, len, elapsed);
+}
+
+/** Scrub aged entries out of r if sufficiently long has elapsed since r was
+ * last scrubbed.
+ */
+
+void
+replaycache_scrub_if_needed(replaycache_t *r)
+{
+  replaycache_scrub_if_needed_internal(time(NULL), r);
+}
+

+ 66 - 0
src/or/replaycache.h

@@ -0,0 +1,66 @@
+/* Copyright (c) 2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file replaycache.h
+ * \brief Header file for replaycache.c.
+ **/
+
+#ifndef _TOR_REPLAYCACHE_H
+#define _TOR_REPLAYCACHE_H
+
+typedef struct replaycache_s replaycache_t;
+
+#ifdef REPLAYCACHE_PRIVATE
+
+struct replaycache_s {
+  /* Scrub interval */
+  time_t scrub_interval;
+  /* Last scrubbed */
+  time_t scrubbed;
+  /*
+   * Horizon
+   * (don't return true on digests in the cache but older than this)
+   */
+  time_t horizon;
+  /*
+   * Digest map: keys are digests, values are times the digest was last seen
+   */
+  digestmap_t *digests_seen;
+};
+
+#endif /* REPLAYCACHE_PRIVATE */
+
+/* replaycache_t free/new */
+
+void replaycache_free(replaycache_t *r);
+replaycache_t * replaycache_new(time_t horizon, time_t interval);
+
+#ifdef REPLAYCACHE_PRIVATE
+
+/*
+ * replaycache_t internal functions:
+ *
+ * These take the time to treat as the present as an argument for easy unit
+ * testing.  For everything else, use the wrappers below instead.
+ */
+
+int replaycache_add_and_test_internal(
+    time_t present, replaycache_t *r, const void *data, int len,
+    time_t *elapsed);
+void replaycache_scrub_if_needed_internal(
+    time_t present, replaycache_t *r);
+
+#endif /* REPLAYCACHE_PRIVATE */
+
+/*
+ * replaycache_t methods
+ */
+
+int replaycache_add_and_test(replaycache_t *r, const void *data, int len);
+int replaycache_add_test_and_elapsed(
+    replaycache_t *r, const void *data, int len, time_t *elapsed);
+void replaycache_scrub_if_needed(replaycache_t *r);
+
+#endif
+

+ 2 - 0
src/test/Makefile.am

@@ -18,8 +18,10 @@ test_SOURCES = \
 	test_crypto.c \
 	test_data.c \
 	test_dir.c \
+	test_introduce.c \
 	test_microdesc.c \
 	test_pt.c \
+	test_replay.c \
 	test_util.c \
 	test_config.c \
 	tinytest.c

+ 4 - 0
src/test/test.c

@@ -1863,6 +1863,8 @@ extern struct testcase_t dir_tests[];
 extern struct testcase_t microdesc_tests[];
 extern struct testcase_t pt_tests[];
 extern struct testcase_t config_tests[];
+extern struct testcase_t introduce_tests[];
+extern struct testcase_t replaycache_tests[];
 
 static struct testgroup_t testgroups[] = {
   { "", test_array },
@@ -1875,6 +1877,8 @@ static struct testgroup_t testgroups[] = {
   { "dir/md/", microdesc_tests },
   { "pt/", pt_tests },
   { "config/", config_tests },
+  { "replaycache/", replaycache_tests },
+  { "introduce/", introduce_tests },
   END_OF_GROUPS
 };
 

+ 528 - 0
src/test/test_introduce.c

@@ -0,0 +1,528 @@
+/* Copyright (c) 2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "crypto.h"
+#include "or.h"
+#include "test.h"
+
+#define RENDSERVICE_PRIVATE
+#include "rendservice.h"
+
+extern const char AUTHORITY_SIGNKEY_1[];
+
+static uint8_t v0_test_plaintext[] =
+    /* 20 bytes of rendezvous point nickname */
+  { 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+    /* 20 bytes dummy rendezvous cookie */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 128 bytes dummy DH handshake data */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
+
+static uint8_t v1_test_plaintext[] =
+    /* Version byte */
+  { 0x01,
+    /* 42 bytes of dummy rendezvous point hex digest */
+    0x24, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30,
+    0x33, 0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30,
+    0x37, 0x30, 0x38, 0x30, 0x39, 0x30, 0x41, 0x30,
+    0x42, 0x30, 0x43, 0x30, 0x44, 0x30, 0x45, 0x30,
+    0x46, 0x31, 0x30, 0x31, 0x31, 0x31, 0x32, 0x31,
+    0x33, 0x00,
+    /* 20 bytes dummy rendezvous cookie */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 128 bytes dummy DH handshake data */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
+
+static uint8_t v2_test_plaintext[] =
+    /* Version byte */
+  { 0x02,
+    /* 4 bytes rendezvous point's IP address */
+    0xc0, 0xa8, 0x00, 0x01,
+    /* 2 bytes rendezvous point's OR port */
+    0x23, 0x5a,
+    /* 20 bytes dummy rendezvous point's identity digest */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 2 bytes length of onion key */
+    0x00, 0x8c,
+    /* Onion key (140 bytes taken from live test) */
+    0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xb1,
+    0xcd, 0x46, 0xa9, 0x18, 0xd2, 0x0f, 0x01, 0xf8,
+    0xb2, 0xad, 0xa4, 0x79, 0xb4, 0xbb, 0x4b, 0xf4,
+    0x54, 0x1e, 0x3f, 0x03, 0x54, 0xcf, 0x7c, 0xb6,
+    0xb5, 0xf0, 0xfe, 0xed, 0x4b, 0x7d, 0xd7, 0x61,
+    0xdb, 0x6d, 0xd9, 0x19, 0xe2, 0x72, 0x04, 0xaa,
+    0x3e, 0x89, 0x26, 0x14, 0x62, 0x9a, 0x6c, 0x11,
+    0x0b, 0x35, 0x99, 0x2c, 0x9f, 0x2c, 0x64, 0xa1,
+    0xd9, 0xe2, 0x88, 0xce, 0xf6, 0x54, 0xfe, 0x1d,
+    0x37, 0x5e, 0x6d, 0x73, 0x95, 0x54, 0x90, 0xf0,
+    0x7b, 0xfa, 0xd4, 0x44, 0xac, 0xb2, 0x23, 0x9f,
+    0x75, 0x36, 0xe2, 0x78, 0x62, 0x82, 0x80, 0xa4,
+    0x23, 0x22, 0xc9, 0xbf, 0xc4, 0x36, 0xd1, 0x31,
+    0x33, 0x8e, 0x64, 0xb4, 0xa9, 0x74, 0xa1, 0xcb,
+    0x42, 0x8d, 0x60, 0xc7, 0xbb, 0x8e, 0x6e, 0x0f,
+    0x36, 0x74, 0x8e, 0xf4, 0x08, 0x99, 0x06, 0x92,
+    0xb1, 0x3f, 0xb3, 0xdd, 0xed, 0xf7, 0xc9, 0x02,
+    0x03, 0x01, 0x00, 0x01,
+    /* 20 bytes dummy rendezvous cookie */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 128 bytes dummy DH handshake data */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
+
+static uint8_t v3_no_auth_test_plaintext[] =
+    /* Version byte */
+  { 0x03,
+    /* Auth type (0 for no auth len/auth data) */
+    0x00,
+    /* Timestamp */
+    0x50, 0x0b, 0xb5, 0xaa,
+    /* 4 bytes rendezvous point's IP address */
+    0xc0, 0xa8, 0x00, 0x01,
+    /* 2 bytes rendezvous point's OR port */
+    0x23, 0x5a,
+    /* 20 bytes dummy rendezvous point's identity digest */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 2 bytes length of onion key */
+    0x00, 0x8c,
+    /* Onion key (140 bytes taken from live test) */
+    0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xb1,
+    0xcd, 0x46, 0xa9, 0x18, 0xd2, 0x0f, 0x01, 0xf8,
+    0xb2, 0xad, 0xa4, 0x79, 0xb4, 0xbb, 0x4b, 0xf4,
+    0x54, 0x1e, 0x3f, 0x03, 0x54, 0xcf, 0x7c, 0xb6,
+    0xb5, 0xf0, 0xfe, 0xed, 0x4b, 0x7d, 0xd7, 0x61,
+    0xdb, 0x6d, 0xd9, 0x19, 0xe2, 0x72, 0x04, 0xaa,
+    0x3e, 0x89, 0x26, 0x14, 0x62, 0x9a, 0x6c, 0x11,
+    0x0b, 0x35, 0x99, 0x2c, 0x9f, 0x2c, 0x64, 0xa1,
+    0xd9, 0xe2, 0x88, 0xce, 0xf6, 0x54, 0xfe, 0x1d,
+    0x37, 0x5e, 0x6d, 0x73, 0x95, 0x54, 0x90, 0xf0,
+    0x7b, 0xfa, 0xd4, 0x44, 0xac, 0xb2, 0x23, 0x9f,
+    0x75, 0x36, 0xe2, 0x78, 0x62, 0x82, 0x80, 0xa4,
+    0x23, 0x22, 0xc9, 0xbf, 0xc4, 0x36, 0xd1, 0x31,
+    0x33, 0x8e, 0x64, 0xb4, 0xa9, 0x74, 0xa1, 0xcb,
+    0x42, 0x8d, 0x60, 0xc7, 0xbb, 0x8e, 0x6e, 0x0f,
+    0x36, 0x74, 0x8e, 0xf4, 0x08, 0x99, 0x06, 0x92,
+    0xb1, 0x3f, 0xb3, 0xdd, 0xed, 0xf7, 0xc9, 0x02,
+    0x03, 0x01, 0x00, 0x01,
+    /* 20 bytes dummy rendezvous cookie */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 128 bytes dummy DH handshake data */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
+
+static uint8_t v3_basic_auth_test_plaintext[] =
+    /* Version byte */
+  { 0x03,
+    /* Auth type (1 for REND_BASIC_AUTH) */
+    0x01,
+    /* Auth len (must be 16 bytes for REND_BASIC_AUTH) */
+    0x00, 0x10,
+    /* Auth data (a 16-byte dummy descriptor cookie) */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    /* Timestamp */
+    0x50, 0x0b, 0xb5, 0xaa,
+    /* 4 bytes rendezvous point's IP address */
+    0xc0, 0xa8, 0x00, 0x01,
+    /* 2 bytes rendezvous point's OR port */
+    0x23, 0x5a,
+    /* 20 bytes dummy rendezvous point's identity digest */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 2 bytes length of onion key */
+    0x00, 0x8c,
+    /* Onion key (140 bytes taken from live test) */
+    0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xb1,
+    0xcd, 0x46, 0xa9, 0x18, 0xd2, 0x0f, 0x01, 0xf8,
+    0xb2, 0xad, 0xa4, 0x79, 0xb4, 0xbb, 0x4b, 0xf4,
+    0x54, 0x1e, 0x3f, 0x03, 0x54, 0xcf, 0x7c, 0xb6,
+    0xb5, 0xf0, 0xfe, 0xed, 0x4b, 0x7d, 0xd7, 0x61,
+    0xdb, 0x6d, 0xd9, 0x19, 0xe2, 0x72, 0x04, 0xaa,
+    0x3e, 0x89, 0x26, 0x14, 0x62, 0x9a, 0x6c, 0x11,
+    0x0b, 0x35, 0x99, 0x2c, 0x9f, 0x2c, 0x64, 0xa1,
+    0xd9, 0xe2, 0x88, 0xce, 0xf6, 0x54, 0xfe, 0x1d,
+    0x37, 0x5e, 0x6d, 0x73, 0x95, 0x54, 0x90, 0xf0,
+    0x7b, 0xfa, 0xd4, 0x44, 0xac, 0xb2, 0x23, 0x9f,
+    0x75, 0x36, 0xe2, 0x78, 0x62, 0x82, 0x80, 0xa4,
+    0x23, 0x22, 0xc9, 0xbf, 0xc4, 0x36, 0xd1, 0x31,
+    0x33, 0x8e, 0x64, 0xb4, 0xa9, 0x74, 0xa1, 0xcb,
+    0x42, 0x8d, 0x60, 0xc7, 0xbb, 0x8e, 0x6e, 0x0f,
+    0x36, 0x74, 0x8e, 0xf4, 0x08, 0x99, 0x06, 0x92,
+    0xb1, 0x3f, 0xb3, 0xdd, 0xed, 0xf7, 0xc9, 0x02,
+    0x03, 0x01, 0x00, 0x01,
+    /* 20 bytes dummy rendezvous cookie */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x10, 0x11, 0x12, 0x13,
+    /* 128 bytes dummy DH handshake data */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
+
+static void do_decrypt_test(uint8_t *plaintext, size_t plaintext_len);
+static void do_early_parse_test(uint8_t *plaintext, size_t plaintext_len);
+static void do_late_parse_test(uint8_t *plaintext, size_t plaintext_len);
+static void do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase);
+static ssize_t make_intro_from_plaintext(
+    void *buf, size_t len, crypto_pk_t *key, void **cell_out);
+
+#define EARLY_PARSE_ONLY 1
+#define DECRYPT_ONLY 2
+#define ALL_PARSING 3
+
+static void
+do_early_parse_test(uint8_t *plaintext, size_t plaintext_len)
+{
+  do_parse_test(plaintext, plaintext_len, EARLY_PARSE_ONLY);
+}
+
+static void
+do_decrypt_test(uint8_t *plaintext, size_t plaintext_len)
+{
+  do_parse_test(plaintext, plaintext_len, DECRYPT_ONLY);
+}
+
+static void
+do_late_parse_test(uint8_t *plaintext, size_t plaintext_len)
+{
+  do_parse_test(plaintext, plaintext_len, ALL_PARSING);
+}
+
+/** Test utility function: checks that the <b>plaintext_len</b>-byte string at
+ * <b>plaintext</b> is at least superficially parseable.
+ */
+static void
+do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase)
+{
+  crypto_pk_t *k = NULL;
+  int r;
+  uint8_t *cell = NULL;
+  size_t cell_len;
+  rend_intro_cell_t *parsed_req = NULL;
+  char *err_msg = NULL;
+  char digest[DIGEST_LEN];
+
+  /* Get a key */
+  k = crypto_pk_new();
+  test_assert(k);
+  r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_1, -1);
+  test_assert(!r);
+
+  /* Get digest for future comparison */
+  r = crypto_pk_get_digest(k, digest);
+  test_assert(r >= 0);
+
+  /* Make a cell out of it */
+  r = make_intro_from_plaintext(
+      plaintext, plaintext_len,
+      k, (void **)(&cell));
+  test_assert(r > 0);
+  test_assert(cell);
+  cell_len = r;
+
+  /* Do early parsing */
+  parsed_req = rend_service_begin_parse_intro(cell, cell_len, 2, &err_msg);
+  test_assert(parsed_req);
+  test_assert(!err_msg);
+  test_memeq(parsed_req->pk, digest, DIGEST_LEN);
+  test_assert(parsed_req->ciphertext);
+  test_assert(parsed_req->ciphertext_len > 0);
+
+  if (phase == EARLY_PARSE_ONLY)
+    goto done;
+
+  /* Do decryption */
+  r = rend_service_decrypt_intro(parsed_req, k, &err_msg);
+  test_assert(!r);
+  test_assert(!err_msg);
+  test_assert(parsed_req->plaintext);
+  test_assert(parsed_req->plaintext_len > 0);
+
+  if (phase == DECRYPT_ONLY)
+    goto done;
+
+  /* Do late parsing */
+  r = rend_service_parse_intro_plaintext(parsed_req, &err_msg);
+  test_assert(!r);
+  test_assert(!err_msg);
+  test_assert(parsed_req->parsed);
+
+ done:
+  tor_free(cell);
+  crypto_pk_free(k);
+  rend_service_free_intro(parsed_req);
+  tor_free(err_msg);
+}
+
+/** Given the plaintext of the encrypted part of an INTRODUCE1/2 and a key,
+ * construct the encrypted cell for testing.
+ */
+
+static ssize_t
+make_intro_from_plaintext(
+    void *buf, size_t len, crypto_pk_t *key, void **cell_out)
+{
+  char *cell = NULL;
+  ssize_t cell_len = -1, r;
+  /* Assemble key digest and ciphertext, then construct the cell */
+  ssize_t ciphertext_size;
+
+  if (!(buf && key && len > 0 && cell_out)) goto done;
+
+  /*
+   * Figure out an upper bound on how big the ciphertext will be
+   * (see crypto_pk_public_hybrid_encrypt())
+   */
+  ciphertext_size = PKCS1_OAEP_PADDING_OVERHEAD;
+  ciphertext_size += crypto_pk_keysize(key);
+  ciphertext_size += CIPHER_KEY_LEN;
+  ciphertext_size += len;
+
+  /*
+   * Allocate space for the cell
+   */
+  cell = tor_malloc(DIGEST_LEN + ciphertext_size);
+
+  /* Compute key digest (will be first DIGEST_LEN octets of cell) */
+  r = crypto_pk_get_digest(key, cell);
+  test_assert(r >= 0);
+
+  /* Do encryption */
+  r = crypto_pk_public_hybrid_encrypt(
+      key, cell + DIGEST_LEN, ciphertext_size,
+      buf, len,
+      PK_PKCS1_OAEP_PADDING, 0);
+  test_assert(r >= 0);
+
+  /* Figure out cell length */
+  cell_len = DIGEST_LEN + r;
+
+  /* Output the cell */
+  *cell_out = cell;
+
+ done:
+  return cell_len;
+}
+
+/** Test v0 INTRODUCE2 parsing through decryption only
+ */
+
+static void
+test_introduce_decrypt_v0(void)
+{
+  do_decrypt_test(v0_test_plaintext, sizeof(v0_test_plaintext));
+}
+
+/** Test v1 INTRODUCE2 parsing through decryption only
+ */
+
+static void
+test_introduce_decrypt_v1(void)
+{
+  do_decrypt_test(v1_test_plaintext, sizeof(v1_test_plaintext));
+}
+
+/** Test v2 INTRODUCE2 parsing through decryption only
+ */
+
+static void
+test_introduce_decrypt_v2(void)
+{
+  do_decrypt_test(v2_test_plaintext, sizeof(v2_test_plaintext));
+}
+
+/** Test v3 INTRODUCE2 parsing through decryption only
+ */
+
+static void
+test_introduce_decrypt_v3(void)
+{
+  do_decrypt_test(
+      v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext));
+  do_decrypt_test(
+      v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext));
+}
+
+/** Test v0 INTRODUCE2 parsing through early parsing only
+ */
+
+static void
+test_introduce_early_parse_v0(void)
+{
+  do_early_parse_test(v0_test_plaintext, sizeof(v0_test_plaintext));
+}
+
+/** Test v1 INTRODUCE2 parsing through early parsing only
+ */
+
+static void
+test_introduce_early_parse_v1(void)
+{
+  do_early_parse_test(v1_test_plaintext, sizeof(v1_test_plaintext));
+}
+
+/** Test v2 INTRODUCE2 parsing through early parsing only
+ */
+
+static void
+test_introduce_early_parse_v2(void)
+{
+  do_early_parse_test(v2_test_plaintext, sizeof(v2_test_plaintext));
+}
+
+/** Test v3 INTRODUCE2 parsing through early parsing only
+ */
+
+static void
+test_introduce_early_parse_v3(void)
+{
+  do_early_parse_test(
+      v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext));
+  do_early_parse_test(
+      v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext));
+}
+
+/** Test v0 INTRODUCE2 parsing
+ */
+
+static void
+test_introduce_late_parse_v0(void)
+{
+  do_late_parse_test(v0_test_plaintext, sizeof(v0_test_plaintext));
+}
+
+/** Test v1 INTRODUCE2 parsing
+ */
+
+static void
+test_introduce_late_parse_v1(void)
+{
+  do_late_parse_test(v1_test_plaintext, sizeof(v1_test_plaintext));
+}
+
+/** Test v2 INTRODUCE2 parsing
+ */
+
+static void
+test_introduce_late_parse_v2(void)
+{
+  do_late_parse_test(v2_test_plaintext, sizeof(v2_test_plaintext));
+}
+
+/** Test v3 INTRODUCE2 parsing
+ */
+
+static void
+test_introduce_late_parse_v3(void)
+{
+  do_late_parse_test(
+      v3_no_auth_test_plaintext, sizeof(v3_no_auth_test_plaintext));
+  do_late_parse_test(
+      v3_basic_auth_test_plaintext, sizeof(v3_basic_auth_test_plaintext));
+}
+
+#define INTRODUCE_LEGACY(name) \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_introduce_ ## name }
+
+struct testcase_t introduce_tests[] = {
+  INTRODUCE_LEGACY(early_parse_v0),
+  INTRODUCE_LEGACY(early_parse_v1),
+  INTRODUCE_LEGACY(early_parse_v2),
+  INTRODUCE_LEGACY(early_parse_v3),
+  INTRODUCE_LEGACY(decrypt_v0),
+  INTRODUCE_LEGACY(decrypt_v1),
+  INTRODUCE_LEGACY(decrypt_v2),
+  INTRODUCE_LEGACY(decrypt_v3),
+  INTRODUCE_LEGACY(late_parse_v0),
+  INTRODUCE_LEGACY(late_parse_v1),
+  INTRODUCE_LEGACY(late_parse_v2),
+  INTRODUCE_LEGACY(late_parse_v3),
+  END_OF_TESTCASES
+};
+

+ 184 - 0
src/test/test_replay.c

@@ -0,0 +1,184 @@
+/* Copyright (c) 2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define REPLAYCACHE_PRIVATE
+
+#include "orconfig.h"
+#include "or.h"
+#include "replaycache.h"
+#include "test.h"
+
+static const char *test_buffer =
+  "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed do eiusmod"
+  " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim"
+  " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea"
+  " commodo consequat. Duis aute irure dolor in reprehenderit in voluptate"
+  " velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint"
+  " occaecat cupidatat non proident, sunt in culpa qui officia deserunt"
+  " mollit anim id est laborum.";
+
+static void
+test_replaycache_alloc(void)
+{
+  replaycache_t *r = NULL;
+
+  r = replaycache_new(600, 300);
+  test_assert(r != NULL);
+  if (!r) goto done;
+
+ done:
+  if (r) replaycache_free(r);
+
+  return;
+}
+
+static void
+test_replaycache_miss(void)
+{
+  replaycache_t *r = NULL;
+  int result;
+
+  r = replaycache_new(600, 300);
+  test_assert(r != NULL);
+  if (!r) goto done;
+
+  result =
+    replaycache_add_and_test_internal(1200, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 0);
+
+ done:
+  if (r) replaycache_free(r);
+
+  return;
+}
+
+static void
+test_replaycache_hit(void)
+{
+  replaycache_t *r = NULL;
+  int result;
+
+  r = replaycache_new(600, 300);
+  test_assert(r != NULL);
+  if (!r) goto done;
+
+  result =
+    replaycache_add_and_test_internal(1200, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 0);
+
+  result =
+    replaycache_add_and_test_internal(1300, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 1);
+
+ done:
+  if (r) replaycache_free(r);
+
+  return;
+}
+
+static void
+test_replaycache_age(void)
+{
+  replaycache_t *r = NULL;
+  int result;
+
+  r = replaycache_new(600, 300);
+  test_assert(r != NULL);
+  if (!r) goto done;
+
+  result =
+    replaycache_add_and_test_internal(1200, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 0);
+
+  result =
+    replaycache_add_and_test_internal(1300, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 1);
+
+  result =
+    replaycache_add_and_test_internal(3000, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 0);
+
+ done:
+  if (r) replaycache_free(r);
+
+  return;
+}
+
+static void
+test_replaycache_elapsed(void)
+{
+  replaycache_t *r = NULL;
+  int result;
+  time_t elapsed;
+
+  r = replaycache_new(600, 300);
+  test_assert(r != NULL);
+  if (!r) goto done;
+
+  result =
+    replaycache_add_and_test_internal(1200, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 0);
+
+  result =
+    replaycache_add_and_test_internal(1300, r, test_buffer,
+        strlen(test_buffer), &elapsed);
+  test_eq(result, 1);
+  test_eq(elapsed, 100);
+
+ done:
+  if (r) replaycache_free(r);
+
+  return;
+}
+
+static void
+test_replaycache_noexpire(void)
+{
+  replaycache_t *r = NULL;
+  int result;
+
+  r = replaycache_new(0, 0);
+  test_assert(r != NULL);
+  if (!r) goto done;
+
+  result =
+    replaycache_add_and_test_internal(1200, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 0);
+
+  result =
+    replaycache_add_and_test_internal(1300, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 1);
+
+  result =
+    replaycache_add_and_test_internal(3000, r, test_buffer,
+        strlen(test_buffer), NULL);
+  test_eq(result, 1);
+
+ done:
+  if (r) replaycache_free(r);
+
+  return;
+}
+
+#define REPLAYCACHE_LEGACY(name) \
+  { #name, legacy_test_helper, 0, &legacy_setup, test_replaycache_ ## name }
+
+struct testcase_t replaycache_tests[] = {
+  REPLAYCACHE_LEGACY(alloc),
+  REPLAYCACHE_LEGACY(miss),
+  REPLAYCACHE_LEGACY(hit),
+  REPLAYCACHE_LEGACY(age),
+  REPLAYCACHE_LEGACY(elapsed),
+  REPLAYCACHE_LEGACY(noexpire),
+  END_OF_TESTCASES
+};
+

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott