[tor-commits] [tor/master] hs-v2: Removal of service and relay support

dgoulet at torproject.org dgoulet at torproject.org
Fri Feb 19 21:22:23 UTC 2021


commit 2c865542b6d2e78d2c2942ecdc6acfe1d8fb24bf
Author: David Goulet <dgoulet at torproject.org>
Date:   Fri Feb 5 12:51:41 2021 -0500

    hs-v2: Removal of service and relay support
    
    This is unfortunately massive but both functionalities were extremely
    intertwined and it would have required us to actually change the HSv2 code in
    order to be able to split this into multiple commits.
    
    After this commit, there are still artefacts of v2 in the code but there is no
    more support for service, intro point and HSDir.
    
    The v2 support for rendezvous circuit is still available since that code is
    the same for the v3 and we will leave it in so if a client is able to
    rendezvous on v2 then it can still transfer traffic. Once the entire network
    has moved away from v2, we can remove v2 rendezvous point support.
    
    Related to #40266
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/app/config/config.c                            |   14 +-
 src/app/config/or_options_st.h                     |    2 +-
 src/app/main/main.c                                |    5 +-
 src/app/main/shutdown.c                            |    2 -
 src/core/mainloop/connection.c                     |   30 -
 src/core/mainloop/mainloop.c                       |    9 +-
 src/core/or/channel.c                              |    4 +-
 src/core/or/channelpadding.c                       |    4 +-
 src/core/or/circuitbuild.c                         |   10 +-
 src/core/or/circuitlist.c                          |   70 +-
 src/core/or/circuitlist.h                          |   10 +-
 src/core/or/circuitstats.c                         |    6 +-
 src/core/or/circuituse.c                           |   47 +-
 src/core/or/connection_edge.c                      |   13 +-
 src/core/or/connection_or.c                        |    4 +-
 src/core/or/cpath_build_state_st.h                 |    5 -
 src/core/or/edge_connection_st.h                   |    3 -
 src/core/or/or.h                                   |   54 -
 src/core/or/origin_circuit_st.h                    |    3 -
 src/core/or/relay.c                                |    9 +-
 src/feature/control/control.c                      |    8 +-
 src/feature/control/control_cmd.c                  |  306 +-
 src/feature/control/control_cmd.h                  |    6 +-
 src/feature/control/control_events.c               |  112 -
 src/feature/control/control_events.h               |    7 -
 src/feature/control/control_fmt.c                  |   10 +-
 src/feature/control/control_getinfo.c              |    1 -
 src/feature/dircache/dircache.c                    |   58 -
 src/feature/dirclient/dirclient.c                  |  203 +-
 src/feature/dirclient/dirclient.h                  |    4 -
 src/feature/dircommon/dir_connection_st.h          |    3 -
 src/feature/dircommon/directory.c                  |    3 -
 src/feature/dircommon/directory.h                  |   15 +-
 src/feature/hs/hs_cache.c                          |   81 +-
 src/feature/hs/hs_cache.h                          |   20 +-
 src/feature/hs/hs_cell.c                           |   78 +-
 src/feature/hs/hs_circuit.c                        |  157 +-
 src/feature/hs/hs_client.c                         |    7 -
 src/feature/hs/hs_common.c                         |  261 +-
 src/feature/hs/hs_common.h                         |   23 +-
 src/feature/hs/hs_config.c                         |   55 +-
 src/feature/hs/hs_descriptor.c                     |    1 -
 src/feature/hs/hs_service.c                        |   97 +-
 src/feature/hs/hs_service.h                        |    5 +
 src/feature/nodelist/nodelist.c                    |    2 -
 src/feature/relay/selftest.c                       |    4 +-
 src/feature/rend/feature_rend.md                   |    7 -
 src/feature/rend/include.am                        |   14 +-
 src/feature/rend/rend_authorized_client_st.h       |   22 -
 .../rend/rend_encoded_v2_service_descriptor_st.h   |   21 -
 src/feature/rend/rend_intro_point_st.h             |   81 -
 src/feature/rend/rend_service_descriptor_st.h      |   38 -
 src/feature/rend/rendcache.c                       | 1029 -----
 src/feature/rend/rendcache.h                       |  132 -
 src/feature/rend/rendcommon.c                      |  944 +----
 src/feature/rend/rendcommon.h                      |   53 -
 src/feature/rend/rendparse.c                       |  612 ---
 src/feature/rend/rendparse.h                       |   32 -
 src/feature/rend/rendservice.c                     | 4403 --------------------
 src/feature/rend/rendservice.h                     |  215 -
 src/test/fuzz/fuzz_hsdescv2.c                      |   52 -
 src/test/fuzz/include.am                           |   58 -
 src/test/include.am                                |    5 -
 src/test/rend_test_helpers.c                       |   99 -
 src/test/rend_test_helpers.h                       |   16 -
 src/test/test.c                                    |  131 -
 src/test/test.h                                    |    3 -
 src/test/test_config.c                             |    1 -
 src/test/test_connection.c                         |   87 -
 src/test/test_controller.c                         |  161 -
 src/test/test_dir.c                                |   29 -
 src/test/test_dir_handle_get.c                     |  198 -
 src/test/test_entryconn.c                          |    3 -
 src/test/test_hs.c                                 | 1003 -----
 src/test/test_hs_cache.c                           |    3 -
 src/test/test_hs_client.c                          |  139 +-
 src/test/test_hs_config.c                          |  175 +-
 src/test/test_hs_control.c                         |   11 +-
 src/test/test_hs_intropoint.c                      |  138 +-
 src/test/test_hs_service.c                         |   21 +-
 src/test/test_introduce.c                          |  539 ---
 src/test/test_rendcache.c                          | 1248 ------
 82 files changed, 274 insertions(+), 13280 deletions(-)

diff --git a/src/app/config/config.c b/src/app/config/config.c
index dec4890e70..abb408767c 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -103,8 +103,6 @@
 #include "feature/relay/routermode.h"
 #include "feature/relay/relay_config.h"
 #include "feature/relay/transport_config.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
 #include "lib/geoip/geoip.h"
 #include "feature/stats/geoip_stats.h"
 #include "lib/compress/compress.h"
@@ -2089,7 +2087,7 @@ options_act,(const or_options_t *old_options))
     return -1;
   }
 
-  if (rend_non_anonymous_mode_enabled(options)) {
+  if (hs_service_non_anonymous_mode_enabled(options)) {
     log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run "
              "in a non-anonymous mode. It will provide NO ANONYMITY.");
   }
@@ -3199,7 +3197,7 @@ options_validate_single_onion(or_options_t *options, char **msg)
   }
 
   /* Now that we've checked that the two options are consistent, we can safely
-   * call the rend_service_* functions that abstract these options. */
+   * call the hs_service_* functions that abstract these options. */
 
   /* If you run an anonymous client with an active Single Onion service, the
    * client loses anonymity. */
@@ -3208,13 +3206,13 @@ options_validate_single_onion(or_options_t *options, char **msg)
                                options->NATDPort_set ||
                                options->DNSPort_set ||
                                options->HTTPTunnelPort_set);
-  if (rend_service_non_anonymous_mode_enabled(options) && client_port_set) {
+  if (hs_service_non_anonymous_mode_enabled(options) && client_port_set) {
     REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as "
            "an anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or "
            "revert HiddenServiceNonAnonymousMode to 0.");
   }
 
-  if (rend_service_allow_non_anonymous_connection(options)
+  if (hs_service_allow_non_anonymous_connection(options)
       && options->UseEntryGuards) {
     /* Single Onion services only use entry guards when uploading descriptors;
      * all other connections are one-hop. Further, Single Onions causes the
@@ -3564,7 +3562,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
 
   if (!(options->UseEntryGuards) &&
       (options->RendConfigLines != NULL) &&
-      !rend_service_allow_non_anonymous_connection(options)) {
+      !hs_service_allow_non_anonymous_connection(options)) {
     log_warn(LD_CONFIG,
              "UseEntryGuards is disabled, but you have configured one or more "
              "hidden services on this Tor instance.  Your hidden services "
@@ -3607,7 +3605,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg)
   }
 
   /* Single Onion Services: non-anonymous hidden services */
-  if (rend_service_non_anonymous_mode_enabled(options)) {
+  if (hs_service_non_anonymous_mode_enabled(options)) {
     log_warn(LD_CONFIG,
              "HiddenServiceNonAnonymousMode is set. Every hidden service on "
              "this tor instance is NON-ANONYMOUS. If "
diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h
index f9c6dae2b6..90302eae7b 100644
--- a/src/app/config/or_options_st.h
+++ b/src/app/config/or_options_st.h
@@ -336,7 +336,7 @@ struct or_options_t {
   /* Makes hidden service clients and servers non-anonymous on this tor
    * instance. Allows the non-anonymous HiddenServiceSingleHopMode. Enables
    * non-anonymous behaviour in the hidden service protocol.
-   * Use rend_service_non_anonymous_mode_enabled() instead of using this option
+   * Use hs_service_non_anonymous_mode_enabled() instead of using this option
    * directly.
    */
   int HiddenServiceNonAnonymousMode;
diff --git a/src/app/main/main.c b/src/app/main/main.c
index e7ffb31b4f..902ff66f6d 100644
--- a/src/app/main/main.c
+++ b/src/app/main/main.c
@@ -44,6 +44,7 @@
 #include "feature/dirparse/routerparse.h"
 #include "feature/hibernate/hibernate.h"
 #include "feature/hs/hs_dos.h"
+#include "feature/hs/hs_service.h"
 #include "feature/nodelist/authcert.h"
 #include "feature/nodelist/networkstatus.h"
 #include "feature/nodelist/routerlist.h"
@@ -51,8 +52,6 @@
 #include "feature/relay/ext_orport.h"
 #include "feature/relay/routerkeys.h"
 #include "feature/relay/routermode.h"
-#include "feature/rend/rendcache.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/predict_ports.h"
 #include "feature/stats/bwhist.h"
 #include "feature/stats/rephist.h"
@@ -427,7 +426,6 @@ dumpstats(int severity)
   dumpmemusage(severity);
 
   rep_hist_dump_stats(now,severity);
-  rend_service_dump_stats(severity);
   hs_service_dump_stats(severity);
 }
 
@@ -553,7 +551,6 @@ tor_init(int argc, char *argv[])
   rep_hist_init();
   bwhist_init();
   /* Initialize the service cache. */
-  rend_cache_init();
   addressmap_init(); /* Init the client dns cache. Do it always, since it's
                       * cheap. */
 
diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c
index fe80a92991..921f84143f 100644
--- a/src/app/main/shutdown.c
+++ b/src/app/main/shutdown.c
@@ -45,7 +45,6 @@
 #include "feature/nodelist/routerlist.h"
 #include "feature/relay/ext_orport.h"
 #include "feature/relay/relay_config.h"
-#include "feature/rend/rendcache.h"
 #include "feature/stats/bwhist.h"
 #include "feature/stats/geoip_stats.h"
 #include "feature/stats/rephist.h"
@@ -118,7 +117,6 @@ tor_free_all(int postfork)
   networkstatus_free_all();
   addressmap_free_all();
   dirserv_free_all();
-  rend_cache_free_all();
   rep_hist_free_all();
   bwhist_free_all();
   circuit_free_all();
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 376994f1c1..9bf9f32eaa 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -897,7 +897,6 @@ connection_free_minimal(connection_t *conn)
     }
   }
   if (CONN_IS_EDGE(conn)) {
-    rend_data_free(TO_EDGE_CONN(conn)->rend_data);
     hs_ident_edge_conn_free(TO_EDGE_CONN(conn)->hs_ident);
   }
   if (conn->type == CONN_TYPE_CONTROL) {
@@ -926,7 +925,6 @@ connection_free_minimal(connection_t *conn)
     tor_compress_free(dir_conn->compress_state);
     dir_conn_clear_spool(dir_conn);
 
-    rend_data_free(dir_conn->rend_data);
     hs_ident_dir_conn_free(dir_conn->hs_ident);
     if (dir_conn->guard_state) {
       /* Cancel before freeing, if it's still there. */
@@ -4804,34 +4802,6 @@ connection_get_by_type_nonlinked,(int type))
   CONN_GET_TEMPLATE(conn, conn->type == type && !conn->linked);
 }
 
-/** Return a connection of type <b>type</b> that has rendquery equal
- * to <b>rendquery</b>, and that is not marked for close. If state
- * is non-zero, conn must be of that state too.
- */
-connection_t *
-connection_get_by_type_state_rendquery(int type, int state,
-                                       const char *rendquery)
-{
-  tor_assert(type == CONN_TYPE_DIR ||
-             type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
-  tor_assert(rendquery);
-
-  CONN_GET_TEMPLATE(conn,
-       (conn->type == type &&
-        (!state || state == conn->state)) &&
-        (
-         (type == CONN_TYPE_DIR &&
-          TO_DIR_CONN(conn)->rend_data &&
-          !rend_cmp_service_ids(rendquery,
-                    rend_data_get_address(TO_DIR_CONN(conn)->rend_data)))
-         ||
-              (CONN_IS_EDGE(conn) &&
-               TO_EDGE_CONN(conn)->rend_data &&
-               !rend_cmp_service_ids(rendquery,
-                    rend_data_get_address(TO_EDGE_CONN(conn)->rend_data)))
-         ));
-}
-
 /** Return a new smartlist of dir_connection_t * from get_connection_array()
  * that satisfy conn_test on connection_t *conn_var, and dirconn_test on
  * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not
diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
index f30545eef0..ba87e62af7 100644
--- a/src/core/mainloop/mainloop.c
+++ b/src/core/mainloop/mainloop.c
@@ -91,8 +91,6 @@
 #include "feature/relay/routerkeys.h"
 #include "feature/relay/routermode.h"
 #include "feature/relay/selftest.h"
-#include "feature/rend/rendcache.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/geoip_stats.h"
 #include "feature/stats/predict_ports.h"
 #include "feature/stats/connstats.h"
@@ -1468,8 +1466,7 @@ get_my_roles(const or_options_t *options)
   int is_relay = server_mode(options);
   int is_dirauth = authdir_mode_v3(options);
   int is_bridgeauth = authdir_mode_bridge(options);
-  int is_hidden_service = !!hs_service_get_num_services() ||
-                          !!rend_num_services();
+  int is_hidden_service = !!hs_service_get_num_services();
   int is_dirserver = dir_server_mode(options);
   int sending_control_events = control_any_per_second_event_enabled();
 
@@ -1826,7 +1823,7 @@ check_network_participation_callback(time_t now, const or_options_t *options)
   /* If we're running an onion service, we can't become dormant. */
   /* XXXX this would be nice to change, so that we can be dormant with a
    * service. */
-  if (hs_service_get_num_services() || rend_num_services()) {
+  if (hs_service_get_num_services()) {
     goto found_activity;
   }
 
@@ -2013,7 +2010,6 @@ clean_caches_callback(time_t now, const or_options_t *options)
 {
   /* Remove old information from rephist and the rend cache. */
   rep_history_clean(now - options->RephistTrackTime);
-  rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
   hs_cache_clean_as_client(now);
   hs_cache_clean_as_dir(now);
   microdesc_cache_rebuild(NULL, 0);
@@ -2032,7 +2028,6 @@ rend_cache_failure_clean_callback(time_t now, const or_options_t *options)
   /* We don't keep entries that are more than five minutes old so we try to
    * clean it as soon as we can since we want to make sure the client waits
    * as little as possible for reachability reasons. */
-  rend_cache_failure_clean(now);
   hs_cache_client_intro_state_clean(now);
   return 30;
 }
diff --git a/src/core/or/channel.c b/src/core/or/channel.c
index 26c93d169f..c0c5f5e1d1 100644
--- a/src/core/or/channel.c
+++ b/src/core/or/channel.c
@@ -71,12 +71,12 @@
 #include "core/or/relay.h"
 #include "core/or/scheduler.h"
 #include "feature/client/entrynodes.h"
+#include "feature/hs/hs_service.h"
 #include "feature/nodelist/dirlist.h"
 #include "feature/nodelist/networkstatus.h"
 #include "feature/nodelist/nodelist.h"
 #include "feature/nodelist/routerlist.h"
 #include "feature/relay/router.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/geoip_stats.h"
 #include "feature/stats/rephist.h"
 #include "lib/evloop/timers.h"
@@ -1897,7 +1897,7 @@ channel_do_open_actions(channel_t *chan)
     if (!get_options()->ConnectionPadding) {
       /* Disable if torrc disabled */
       channelpadding_disable_padding_on_channel(chan);
-    } else if (rend_service_allow_non_anonymous_connection(get_options()) &&
+    } else if (hs_service_allow_non_anonymous_connection(get_options()) &&
                !networkstatus_get_param(NULL,
                                         CHANNELPADDING_SOS_PARAM,
                                         CHANNELPADDING_SOS_DEFAULT, 0, 1)) {
diff --git a/src/core/or/channelpadding.c b/src/core/or/channelpadding.c
index d0c43e8bdc..441545b98b 100644
--- a/src/core/or/channelpadding.c
+++ b/src/core/or/channelpadding.c
@@ -27,8 +27,8 @@
 #include "feature/relay/router.h"
 #include "feature/relay/routermode.h"
 #include "lib/time/compat_time.h"
-#include "feature/rend/rendservice.h"
 #include "lib/evloop/timers.h"
+#include "feature/hs/hs_service.h"
 
 #include "core/or/cell_st.h"
 #include "core/or/or_connection_st.h"
@@ -744,7 +744,7 @@ channelpadding_decide_to_pad_channel(channel_t *chan)
     return CHANNELPADDING_WONTPAD;
   }
 
-  if (rend_service_allow_non_anonymous_connection(options) &&
+  if (hs_service_allow_non_anonymous_connection(options) &&
       !consensus_nf_pad_single_onion) {
     /* If the consensus just changed values, this channel may still
      * think padding is enabled. Negotiate it off. */
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index c0c918abe4..03af7e3e82 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -69,7 +69,6 @@
 #include "feature/relay/router.h"
 #include "feature/relay/routermode.h"
 #include "feature/relay/selftest.h"
-#include "feature/rend/rendcommon.h"
 #include "feature/stats/predict_ports.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/trace/events.h"
@@ -1331,16 +1330,13 @@ circuit_truncated(origin_circuit_t *circ, int reason)
  *     CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
  *
  *   - A hidden service connecting to a rendezvous point, which the
- *     client picked (CIRCUIT_PURPOSE_S_CONNECT_REND, via
- *     rend_service_receive_introduction() and
- *     rend_service_relaunch_rendezvous)
+ *     client picked (CIRCUIT_PURPOSE_S_CONNECT_REND.
  *
  * There are currently two situations where we picked the exit node
  * ourselves, making DEFAULT_ROUTE_LEN a safe circuit length:
  *
  *   - We are a hidden service connecting to an introduction point
- *     (CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, via
- *     rend_service_launch_establish_intro())
+ *     (CIRCUIT_PURPOSE_S_ESTABLISH_INTRO).
  *
  *   - We are a router testing its own reachabiity
  *     (CIRCUIT_PURPOSE_TESTING, via router_do_reachability_checks())
@@ -2030,7 +2026,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
 
   if (state->onehop_tunnel) {
     log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel%s.",
-              (rend_allow_non_anonymous_connection(get_options()) ?
+              (hs_service_allow_non_anonymous_connection(get_options()) ?
                ", or intro or rendezvous connection" : ""));
     state->desired_path_len = 1;
   } else {
diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c
index f6d9fcf534..19e1902560 100644
--- a/src/core/or/circuitlist.c
+++ b/src/core/or/circuitlist.c
@@ -43,7 +43,6 @@
  * For hidden services, we need to be able to look up introduction point
  * circuits and rendezvous circuits by cookie, key, etc.  These are
  * currently handled with linear searches in
- * circuit_get_ready_rend_circuit_by_rend_data(),
  * circuit_get_next_by_pk_and_purpose(), and with hash lookups in
  * circuit_get_rendezvous() and circuit_get_intro_point().
  *
@@ -77,6 +76,7 @@
 #include "feature/dircommon/directory.h"
 #include "feature/client/entrynodes.h"
 #include "core/mainloop/mainloop.h"
+#include "feature/hs/hs_cache.h"
 #include "feature/hs/hs_circuit.h"
 #include "feature/hs/hs_circuitmap.h"
 #include "feature/hs/hs_ident.h"
@@ -88,7 +88,6 @@
 #include "core/or/policies.h"
 #include "core/or/relay.h"
 #include "core/crypto/relay_crypto.h"
-#include "feature/rend/rendcache.h"
 #include "feature/rend/rendcommon.h"
 #include "feature/stats/predict_ports.h"
 #include "feature/stats/bwhist.h"
@@ -135,7 +134,6 @@ static smartlist_t *circuits_pending_other_guards = NULL;
  * circuit_mark_for_close and which are waiting for circuit_about_to_free. */
 static smartlist_t *circuits_pending_close = NULL;
 
-static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
 static void circuit_about_to_free_atexit(circuit_t *circ);
 static void circuit_about_to_free(circuit_t *circ);
 
@@ -1163,8 +1161,6 @@ circuit_free_(circuit_t *circ)
 
     if (ocirc->build_state) {
         extend_info_free(ocirc->build_state->chosen_exit);
-        cpath_free(ocirc->build_state->pending_final_cpath);
-        cpath_ref_decref(ocirc->build_state->service_pending_final_cpath_ref);
     }
     tor_free(ocirc->build_state);
 
@@ -1177,7 +1173,6 @@ circuit_free_(circuit_t *circ)
     circuit_clear_cpath(ocirc);
 
     crypto_pk_free(ocirc->intro_key);
-    rend_data_free(ocirc->rend_data);
 
     /* Finally, free the identifier of the circuit and nullify it so multiple
      * cleanup will work. */
@@ -1354,18 +1349,6 @@ circuit_free_all(void)
   HT_CLEAR(chan_circid_map, &chan_circid_map);
 }
 
-/** Release a crypt_path_reference_t*, which may be NULL. */
-static void
-cpath_ref_decref(crypt_path_reference_t *cpath_ref)
-{
-  if (cpath_ref != NULL) {
-    if (--(cpath_ref->refcount) == 0) {
-      cpath_free(cpath_ref->cpath);
-      tor_free(cpath_ref);
-    }
-  }
-}
-
 /** A helper function for circuit_dump_by_conn() below. Log a bunch
  * of information about circuit <b>circ</b>.
  */
@@ -1684,37 +1667,6 @@ circuit_unlink_all_from_channel(channel_t *chan, int reason)
   smartlist_free(detached);
 }
 
-/** Return a circ such that
- *  - circ-\>rend_data-\>onion_address is equal to
- *    <b>rend_data</b>-\>onion_address,
- *  - circ-\>rend_data-\>rend_cookie is equal to
- *    <b>rend_data</b>-\>rend_cookie, and
- *  - circ-\>purpose is equal to CIRCUIT_PURPOSE_C_REND_READY.
- *
- * Return NULL if no such circuit exists.
- */
-origin_circuit_t *
-circuit_get_ready_rend_circ_by_rend_data(const rend_data_t *rend_data)
-{
-  SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
-    if (!circ->marked_for_close &&
-        circ->purpose == CIRCUIT_PURPOSE_C_REND_READY) {
-      origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
-      if (ocirc->rend_data == NULL) {
-        continue;
-      }
-      if (!rend_cmp_service_ids(rend_data_get_address(rend_data),
-                                rend_data_get_address(ocirc->rend_data)) &&
-          tor_memeq(ocirc->rend_data->rend_cookie,
-                    rend_data->rend_cookie,
-                    REND_COOKIE_LEN))
-        return ocirc;
-    }
-  }
-  SMARTLIST_FOREACH_END(circ);
-  return NULL;
-}
-
 /** Return the first introduction circuit originating from the global circuit
  * list after <b>start</b> or at the start of the list if <b>start</b> is
  * NULL. Return NULL if no circuit is found.
@@ -1811,14 +1763,10 @@ circuit_get_next_service_rp_circ(origin_circuit_t *start)
 }
 
 /** Return the first circuit originating here in global_circuitlist after
- * <b>start</b> whose purpose is <b>purpose</b>, and where <b>digest</b> (if
- * set) matches the private key digest of the rend data associated with the
- * circuit. Return NULL if no circuit is found. If <b>start</b> is NULL,
- * begin at the start of the list.
- */
+ * <b>start</b> whose purpose is <b>purpose</b>. Return NULL if no circuit is
+ * found. If <b>start</b> is NULL, begin at the start of the list. */
 origin_circuit_t *
-circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
-                                   const uint8_t *digest, uint8_t purpose)
+circuit_get_next_by_purpose(origin_circuit_t *start, uint8_t purpose)
 {
   int idx;
   smartlist_t *lst = circuit_get_global_list();
@@ -1830,7 +1778,6 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
 
   for ( ; idx < smartlist_len(lst); ++idx) {
     circuit_t *circ = smartlist_get(lst, idx);
-    origin_circuit_t *ocirc;
 
     if (circ->marked_for_close)
       continue;
@@ -1841,12 +1788,7 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
     if (BUG(!CIRCUIT_PURPOSE_IS_ORIGIN(circ->purpose))) {
       break;
     }
-    ocirc = TO_ORIGIN_CIRCUIT(circ);
-    if (!digest)
-      return ocirc;
-    if (rend_circuit_pk_digest_eq(ocirc, digest)) {
-      return ocirc;
-    }
+    return TO_ORIGIN_CIRCUIT(circ);
   }
   return NULL;
 }
@@ -2670,7 +2612,7 @@ circuits_handle_oom(size_t current_allocation)
              tor_zlib_get_total_allocation(),
              tor_zstd_get_total_allocation(),
              tor_lzma_get_total_allocation(),
-             rend_cache_get_total_allocation());
+             hs_cache_get_total_allocation());
 
   {
     size_t mem_target = (size_t)(get_options()->MaxMemInQueues *
diff --git a/src/core/or/circuitlist.h b/src/core/or/circuitlist.h
index 3178e6cd0d..b600f9646a 100644
--- a/src/core/or/circuitlist.h
+++ b/src/core/or/circuitlist.h
@@ -60,9 +60,7 @@
  *     to becoming open, or they are open and have sent the
  *     establish_rendezvous cell but haven't received an ack.
  *   circuits that are c_rend_ready are open and have received a
- *     rend ack, but haven't heard from the service yet. if they have a
- *     buildstate->pending_final_cpath then they're expecting a
- *     cell from the service, else they're not.
+ *     rend ack, but haven't heard from the service yet.
  *   circuits that are c_rend_ready_intro_acked are open, and
  *     some intro circ has sent its intro and received an ack.
  *   circuits that are c_rend_joined are open, have heard from
@@ -206,10 +204,8 @@ int circuit_id_in_use_on_channel(circid_t circ_id, channel_t *chan);
 circuit_t *circuit_get_by_edge_conn(edge_connection_t *conn);
 void circuit_unlink_all_from_channel(channel_t *chan, int reason);
 origin_circuit_t *circuit_get_by_global_id(uint32_t id);
-origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data(
-  const rend_data_t *rend_data);
-origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
-                                     const uint8_t *digest, uint8_t purpose);
+origin_circuit_t *circuit_get_next_by_purpose(origin_circuit_t *start,
+                                              uint8_t purpose);
 origin_circuit_t *circuit_get_next_intro_circ(const origin_circuit_t *start,
                                               bool want_client_circ);
 origin_circuit_t *circuit_get_next_service_rp_circ(origin_circuit_t *start);
diff --git a/src/core/or/circuitstats.c b/src/core/or/circuitstats.c
index 7f3b5007b3..d6729eb11f 100644
--- a/src/core/or/circuitstats.c
+++ b/src/core/or/circuitstats.c
@@ -34,7 +34,6 @@
 #include "lib/crypt_ops/crypto_rand.h"
 #include "core/mainloop/mainloop.h"
 #include "feature/nodelist/networkstatus.h"
-#include "feature/rend/rendservice.h"
 #include "feature/relay/router.h"
 #include "app/config/statefile.h"
 #include "core/or/circuitlist.h"
@@ -43,6 +42,7 @@
 #include "lib/time/tvdiff.h"
 #include "lib/encoding/confline.h"
 #include "feature/dirauth/authmode.h"
+#include "feature/hs/hs_service.h"
 #include "feature/relay/relay_periodic.h"
 
 #include "core/or/crypt_path_st.h"
@@ -145,8 +145,8 @@ circuit_build_times_disabled_(const or_options_t *options,
      *
      * If we fix both of these issues someday, we should test
      * these modes with LearnCircuitBuildTimeout on again. */
-    int single_onion_disabled = rend_service_allow_non_anonymous_connection(
-                                                                      options);
+    int single_onion_disabled = hs_service_allow_non_anonymous_connection(
+                                                               options);
 
     if (consensus_disabled || config_disabled || dirauth_disabled ||
         state_disabled || single_onion_disabled) {
diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c
index 26c4711a5b..b00d24407a 100644
--- a/src/core/or/circuituse.c
+++ b/src/core/or/circuituse.c
@@ -58,8 +58,6 @@
 #include "feature/nodelist/routerlist.h"
 #include "feature/relay/routermode.h"
 #include "feature/relay/selftest.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/predict_ports.h"
 #include "lib/math/fp.h"
 #include "lib/time/tvdiff.h"
@@ -83,16 +81,6 @@ static int
 circuit_matches_with_rend_stream(const edge_connection_t *edge_conn,
                                  const origin_circuit_t *origin_circ)
 {
-  /* Check if this is a v2 rendezvous circ/stream */
-  if ((edge_conn->rend_data && !origin_circ->rend_data) ||
-      (!edge_conn->rend_data && origin_circ->rend_data) ||
-      (edge_conn->rend_data && origin_circ->rend_data &&
-       rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
-                            rend_data_get_address(origin_circ->rend_data)))) {
-    /* this circ is not for this conn */
-    return 0;
-  }
-
   /* Check if this is a v3 rendezvous circ/stream */
   if ((edge_conn->hs_ident && !origin_circ->hs_ident) ||
       (!edge_conn->hs_ident && origin_circ->hs_ident) ||
@@ -688,8 +676,7 @@ circuit_expire_building(void)
           /* c_rend_ready circs measure age since timestamp_dirty,
            * because that's set when they switch purposes
            */
-          if (TO_ORIGIN_CIRCUIT(victim)->rend_data ||
-              TO_ORIGIN_CIRCUIT(victim)->hs_ident ||
+          if (TO_ORIGIN_CIRCUIT(victim)->hs_ident ||
               victim->timestamp_dirty > cutoff.tv_sec)
             continue;
           break;
@@ -896,7 +883,7 @@ circuit_log_ancient_one_hop_circuits(int age)
       continue;
     /* Single Onion Services deliberately make long term one-hop intro
      * and rendezvous connections. Don't log the established ones. */
-    if (rend_service_allow_non_anonymous_connection(options) &&
+    if (hs_service_allow_non_anonymous_connection(options) &&
         (circ->purpose == CIRCUIT_PURPOSE_S_INTRO ||
          circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED))
       continue;
@@ -1141,7 +1128,7 @@ needs_exit_circuits(time_t now, int *needs_uptime, int *needs_capacity)
 STATIC int
 needs_hs_server_circuits(time_t now, int num_uptime_internal)
 {
-  if (!rend_num_services() && !hs_service_get_num_services()) {
+  if (!hs_service_get_num_services()) {
     /* No services, we don't need anything. */
     goto no_need;
   }
@@ -2013,14 +2000,6 @@ circuit_purpose_is_hs_vanguards(const uint8_t purpose)
   return (purpose == CIRCUIT_PURPOSE_HS_VANGUARDS);
 }
 
-/** Return true iff the given circuit is an HS v2 circuit. */
-bool
-circuit_is_hs_v2(const circuit_t *circ)
-{
-  return (CIRCUIT_IS_ORIGIN(circ) &&
-          (CONST_TO_ORIGIN_CIRCUIT(circ)->rend_data != NULL));
-}
-
 /** Return true iff the given circuit is an HS v3 circuit. */
 bool
 circuit_is_hs_v3(const circuit_t *circ)
@@ -2451,11 +2430,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
         connection_ap_mark_as_waiting_for_renddesc(conn);
         return 0;
       }
-      log_info(LD_REND,"Chose %s as intro point for '%s'.",
-               extend_info_describe(extend_info),
-               (edge_conn->rend_data) ?
-               safe_str_client(rend_data_get_address(edge_conn->rend_data)) :
-               "service");
+      log_info(LD_REND,"Chose %s as intro point for service",
+               extend_info_describe(extend_info));
     }
 
     /* If we have specified a particular exit node for our
@@ -2579,10 +2555,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
       rep_hist_note_used_internal(time(NULL), need_uptime, 1);
       if (circ) {
         const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
-        if (edge_conn->rend_data) {
-          /* write the service_id into circ */
-          circ->rend_data = rend_data_dup(edge_conn->rend_data);
-        } else if (edge_conn->hs_ident) {
+        if (edge_conn->hs_ident) {
           circ->hs_ident =
             hs_ident_circuit_new(&edge_conn->hs_ident->identity_pk);
         }
@@ -2829,13 +2802,9 @@ connection_ap_get_nonrend_circ_purpose(const entry_connection_t *conn)
   if (base_conn->linked_conn &&
       base_conn->linked_conn->type == CONN_TYPE_DIR) {
     /* Set a custom purpose for hsdir activity */
-    if (base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2 ||
-       base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_HSDESC) {
+    if (base_conn->linked_conn->purpose == DIR_PURPOSE_UPLOAD_HSDESC) {
       return CIRCUIT_PURPOSE_S_HSDIR_POST;
-    } else if (base_conn->linked_conn->purpose
-                 == DIR_PURPOSE_FETCH_RENDDESC_V2 ||
-               base_conn->linked_conn->purpose
-                 == DIR_PURPOSE_FETCH_HSDESC) {
+    } else if (base_conn->linked_conn->purpose == DIR_PURPOSE_FETCH_HSDESC) {
       return CIRCUIT_PURPOSE_C_HSDIR_GET;
     }
   }
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index 37cc24672e..b407fd4b1b 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -97,7 +97,6 @@
 #include "feature/relay/router.h"
 #include "feature/relay/routermode.h"
 #include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/predict_ports.h"
 #include "feature/stats/rephist.h"
 #include "lib/buf/buffers.h"
@@ -3823,13 +3822,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
   conn->base_.address = tor_strdup("(rendezvous)");
   conn->base_.state = EXIT_CONN_STATE_CONNECTING;
 
-  /* The circuit either has an hs identifier for v3+ or a rend_data for legacy
-   * service. */
-  if (origin_circ->rend_data) {
-    conn->rend_data = rend_data_dup(origin_circ->rend_data);
-    tor_assert(connection_edge_is_rendezvous_stream(conn));
-    ret = rend_service_set_connection_addr_port(conn, origin_circ);
-  } else if (origin_circ->hs_ident) {
+  if (origin_circ->hs_ident) {
     /* Setup the identifier to be the one for the circuit service. */
     conn->hs_ident =
       hs_ident_edge_conn_new(&origin_circ->hs_ident->identity_pk);
@@ -4392,10 +4385,8 @@ int
 connection_edge_is_rendezvous_stream(const edge_connection_t *conn)
 {
   tor_assert(conn);
-  /* It should not be possible to set both of these structs */
-  tor_assert_nonfatal(!(conn->rend_data && conn->hs_ident));
 
-  if (conn->rend_data || conn->hs_ident) {
+  if (conn->hs_ident) {
     return 1;
   }
   return 0;
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index 40c4441de6..fdae8ea19c 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -66,6 +66,7 @@
 #include "feature/nodelist/torcert.h"
 #include "core/or/channelpadding.h"
 #include "feature/dirauth/authmode.h"
+#include "feature/hs/hs_service.h"
 
 #include "core/or/cell_st.h"
 #include "core/or/cell_queue_st.h"
@@ -1979,7 +1980,8 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
                                                    conn->identity_digest);
     const int is_authority_fingerprint = router_digest_is_trusted_dir(
                                                    conn->identity_digest);
-    const int non_anonymous_mode = rend_non_anonymous_mode_enabled(options);
+    const int non_anonymous_mode =
+      hs_service_non_anonymous_mode_enabled(options);
     int severity;
     const char *extra_log = "";
 
diff --git a/src/core/or/cpath_build_state_st.h b/src/core/or/cpath_build_state_st.h
index eb8e97edc5..e31af4c8ed 100644
--- a/src/core/or/cpath_build_state_st.h
+++ b/src/core/or/cpath_build_state_st.h
@@ -30,11 +30,6 @@ struct cpath_build_state_t {
    * These are for encrypted dir conns that exit to this router, not
    * for arbitrary exits from the circuit. */
   unsigned int onehop_tunnel : 1;
-  /** The crypt_path_t to append after rendezvous: used for rendezvous. */
-  crypt_path_t *pending_final_cpath;
-  /** A ref-counted reference to the crypt_path_t to append after
-   * rendezvous; used on the service side. */
-  crypt_path_reference_t *service_pending_final_cpath_ref;
   /** How many times has building a circuit for this task failed? */
   int failure_count;
   /** At what time should we give up on this task? */
diff --git a/src/core/or/edge_connection_st.h b/src/core/or/edge_connection_st.h
index 9b2f031b9d..e850c40755 100644
--- a/src/core/or/edge_connection_st.h
+++ b/src/core/or/edge_connection_st.h
@@ -33,9 +33,6 @@ struct edge_connection_t {
   /** A pointer to which node in the circ this conn exits at.  Set for AP
    * connections and for hidden service exit connections. */
   struct crypt_path_t *cpath_layer;
-  /** What rendezvous service are we querying for (if an AP) or providing (if
-   * an exit)? */
-  rend_data_t *rend_data;
 
   /* Hidden service connection identifier for edge connections. Used by the HS
    * client-side code to identify client SOCKS connections and by the
diff --git a/src/core/or/or.h b/src/core/or/or.h
index 182ebc48eb..5bade00128 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -415,60 +415,6 @@ typedef struct rend_service_authorization_t {
   rend_auth_type_t auth_type;
 } rend_service_authorization_t;
 
-/** Client- and server-side data that is used for hidden service connection
- * establishment. Not all fields contain data depending on where this struct
- * is used. */
-typedef struct rend_data_t {
-  /* Hidden service protocol version of this base object. */
-  uint32_t version;
-
-  /** List of HSDir fingerprints on which this request has been sent to. This
-   * contains binary identity digest of the directory of size DIGEST_LEN. */
-  smartlist_t *hsdirs_fp;
-
-  /** Rendezvous cookie used by both, client and service. */
-  char rend_cookie[REND_COOKIE_LEN];
-
-  /** Number of streams associated with this rendezvous circuit. */
-  int nr_streams;
-} rend_data_t;
-
-typedef struct rend_data_v2_t {
-  /* Rendezvous base data. */
-  rend_data_t base_;
-
-  /** Onion address (without the .onion part) that a client requests. */
-  char onion_address[REND_SERVICE_ID_LEN_BASE32+1];
-
-  /** Descriptor ID for each replicas computed from the onion address. If
-   * the onion address is empty, this array MUST be empty. We keep them so
-   * we know when to purge our entry in the last hsdir request table. */
-  char descriptor_id[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN];
-
-  /** (Optional) descriptor cookie that is used by a client. */
-  char descriptor_cookie[REND_DESC_COOKIE_LEN];
-
-  /** Authorization type for accessing a service used by a client. */
-  rend_auth_type_t auth_type;
-
-  /** Descriptor ID for a client request. The control port command HSFETCH
-   * uses this. It's set if the descriptor query should only use this
-   * descriptor ID. */
-  char desc_id_fetch[DIGEST_LEN];
-
-  /** Hash of the hidden service's PK used by a service. */
-  char rend_pk_digest[DIGEST_LEN];
-} rend_data_v2_t;
-
-/* From a base rend_data_t object <b>d</d>, return the v2 object. */
-static inline
-rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d)
-{
-  tor_assert(d);
-  tor_assert(d->version == 2);
-  return DOWNCAST(rend_data_v2_t, d);
-}
-
 /* Stub because we can't include hs_ident.h. */
 struct hs_ident_edge_conn_t;
 struct hs_ident_dir_conn_t;
diff --git a/src/core/or/origin_circuit_st.h b/src/core/or/origin_circuit_st.h
index a45a6573dc..4822760c8d 100644
--- a/src/core/or/origin_circuit_st.h
+++ b/src/core/or/origin_circuit_st.h
@@ -128,9 +128,6 @@ struct origin_circuit_t {
    */
   crypt_path_t *cpath;
 
-  /** Holds all rendezvous data on either client or service side. */
-  rend_data_t *rend_data;
-
   /** Holds hidden service identifier on either client or service side. This
    * is for both introduction and rendezvous circuit. */
   struct hs_ident_circuit_t *hs_ident;
diff --git a/src/core/or/relay.c b/src/core/or/relay.c
index f986883370..32d6ca731a 100644
--- a/src/core/or/relay.c
+++ b/src/core/or/relay.c
@@ -78,7 +78,6 @@
 #include "core/or/reasons.h"
 #include "core/or/relay.h"
 #include "core/crypto/relay_crypto.h"
-#include "feature/rend/rendcache.h"
 #include "feature/rend/rendcommon.h"
 #include "feature/nodelist/describe.h"
 #include "feature/nodelist/routerlist.h"
@@ -2711,8 +2710,8 @@ cell_queues_check_size(void)
   alloc += half_streams_get_total_allocation();
   alloc += buf_get_total_allocation();
   alloc += tor_compress_get_total_allocation();
-  const size_t rend_cache_total = rend_cache_get_total_allocation();
-  alloc += rend_cache_total;
+  const size_t hs_cache_total = hs_cache_get_total_allocation();
+  alloc += hs_cache_total;
   const size_t geoip_client_cache_total =
     geoip_client_cache_total_allocation();
   alloc += geoip_client_cache_total;
@@ -2724,9 +2723,9 @@ cell_queues_check_size(void)
       /* If we're spending over 20% of the memory limit on hidden service
        * descriptors, free them until we're down to 10%. Do the same for geoip
        * client cache. */
-      if (rend_cache_total > get_options()->MaxMemInQueues / 5) {
+      if (hs_cache_total > get_options()->MaxMemInQueues / 5) {
         const size_t bytes_to_remove =
-          rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10);
+          hs_cache_total - (size_t)(get_options()->MaxMemInQueues / 10);
         alloc -= hs_cache_handle_oom(now, bytes_to_remove);
       }
       if (geoip_client_cache_total > get_options()->MaxMemInQueues / 5) {
diff --git a/src/feature/control/control.c b/src/feature/control/control.c
index 2aebe1aac6..2cb20b700f 100644
--- a/src/feature/control/control.c
+++ b/src/feature/control/control.c
@@ -48,8 +48,8 @@
 #include "feature/control/control_cmd.h"
 #include "feature/control/control_events.h"
 #include "feature/control/control_proto.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
+#include "feature/hs/hs_common.h"
+#include "feature/hs/hs_service.h"
 #include "lib/evloop/procmon.h"
 
 #include "feature/control/control_connection_st.h"
@@ -240,9 +240,7 @@ connection_control_closed(control_connection_t *conn)
    */
   if (conn->ephemeral_onion_services) {
     SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) {
-      if (rend_valid_v2_service_id(cp)) {
-        rend_service_del_ephemeral(cp);
-      } else if (hs_address_is_valid(cp)) {
+      if (hs_address_is_valid(cp)) {
         hs_service_del_ephemeral(cp);
       } else {
         /* An invalid .onion in our list should NEVER happen */
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c
index e88f17de09..009105bb20 100644
--- a/src/feature/control/control_cmd.c
+++ b/src/feature/control/control_cmd.c
@@ -38,8 +38,6 @@
 #include "feature/nodelist/routerinfo.h"
 #include "feature/nodelist/routerlist.h"
 #include "feature/rend/rendcommon.h"
-#include "feature/rend/rendparse.h"
-#include "feature/rend/rendservice.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/crypt_ops/crypto_util.h"
 #include "lib/encoding/confline.h"
@@ -53,9 +51,6 @@
 #include "feature/control/control_connection_st.h"
 #include "feature/nodelist/node_st.h"
 #include "feature/nodelist/routerinfo_st.h"
-#include "feature/rend/rend_authorized_client_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
 
 #include "src/app/config/statefile.h"
 
@@ -1442,31 +1437,15 @@ handle_control_hsfetch(control_connection_t *conn,
                        const control_cmd_args_t *args)
 
 {
-  char digest[DIGEST_LEN], *desc_id = NULL;
+  char *desc_id = NULL;
   smartlist_t *hsdirs = NULL;
-  static const char *v2_str = "v2-";
-  const size_t v2_str_len = strlen(v2_str);
-  rend_data_t *rend_query = NULL;
   ed25519_public_key_t v3_pk;
   uint32_t version;
   const char *hsaddress = NULL;
 
   /* Extract the first argument (either HSAddress or DescID). */
   const char *arg1 = smartlist_get(args->args, 0);
-  /* Test if it's an HS address without the .onion part. */
-  if (rend_valid_v2_service_id(arg1)) {
-    hsaddress = arg1;
-    version = HS_VERSION_TWO;
-  } else if (strcmpstart(arg1, v2_str) == 0 &&
-             rend_valid_descriptor_id(arg1 + v2_str_len) &&
-             base32_decode(digest, sizeof(digest), arg1 + v2_str_len,
-                           REND_DESC_ID_V2_LEN_BASE32) ==
-                sizeof(digest)) {
-    /* We have a well formed version 2 descriptor ID. Keep the decoded value
-     * of the id. */
-    desc_id = digest;
-    version = HS_VERSION_TWO;
-  } else if (hs_address_is_valid(arg1)) {
+  if (hs_address_is_valid(arg1)) {
     hsaddress = arg1;
     version = HS_VERSION_THREE;
     hs_parse_address(hsaddress, &v3_pk, NULL, NULL);
@@ -1495,15 +1474,6 @@ handle_control_hsfetch(control_connection_t *conn,
     }
   }
 
-  if (version == HS_VERSION_TWO) {
-    rend_query = rend_data_client_create(hsaddress, desc_id, NULL,
-                                         REND_NO_AUTH);
-    if (rend_query == NULL) {
-      control_write_endreply(conn, 551, "Error creating the HS query");
-      goto done;
-    }
-  }
-
   /* Using a descriptor ID, we force the user to provide at least one
    * hsdir server using the SERVER= option. */
   if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) {
@@ -1526,7 +1496,6 @@ handle_control_hsfetch(control_connection_t *conn,
  done:
   /* Contains data pointer that we don't own thus no cleanup. */
   smartlist_free(hsdirs);
-  rend_data_free(rend_query);
   return 0;
 }
 
@@ -1547,7 +1516,6 @@ handle_control_hspost(control_connection_t *conn,
 {
   smartlist_t *hs_dirs = NULL;
   const char *encoded_desc = args->cmddata;
-  size_t encoded_desc_len = args->cmddata_len;
   const char *onion_address = NULL;
   const config_line_t *line;
 
@@ -1587,44 +1555,6 @@ handle_control_hspost(control_connection_t *conn,
     goto done;
   }
 
-  /* From this point on, it is only v2. */
-
-  /*  parse it. */
-  rend_encoded_v2_service_descriptor_t *desc =
-      tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
-  desc->desc_str = tor_memdup_nulterm(encoded_desc, encoded_desc_len);
-
-  rend_service_descriptor_t *parsed = NULL;
-  char *intro_content = NULL;
-  size_t intro_size;
-  size_t encoded_size;
-  const char *next_desc;
-  if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
-                                        &intro_size, &encoded_size,
-                                        &next_desc, desc->desc_str, 1)) {
-    /* Post the descriptor. */
-    char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-    if (!rend_get_service_id(parsed->pk, serviceid)) {
-      smartlist_t *descs = smartlist_new();
-      smartlist_add(descs, desc);
-
-      /* We are about to trigger HS descriptor upload so send the OK now
-       * because after that 650 event(s) are possible so better to have the
-       * 250 OK before them to avoid out of order replies. */
-      send_control_done(conn);
-
-      /* Trigger the descriptor upload */
-      directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
-      smartlist_free(descs);
-    }
-
-    rend_service_descriptor_free(parsed);
-  } else {
-    control_write_endreply(conn, 554, "Invalid descriptor");
-  }
-
-  tor_free(intro_content);
-  rend_encoded_v2_service_descriptor_free(desc);
  done:
   smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
   return 0;
@@ -1640,7 +1570,6 @@ handle_control_hspost(control_connection_t *conn,
  * The port_cfgs is a list of service port. Ownership transferred to service.
  * The max_streams refers to the MaxStreams= key.
  * The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
- * The auth_type is the authentication type of the clients in auth_clients.
  * The ownership of that list is transferred to the service.
  *
  * On success (RSAE_OKAY), the address_out points to a newly allocated string
@@ -1650,8 +1579,7 @@ STATIC hs_service_add_ephemeral_status_t
 add_onion_helper_add_service(int hs_version,
                              add_onion_secret_key_t *pk,
                              smartlist_t *port_cfgs, int max_streams,
-                             int max_streams_close_circuit, int auth_type,
-                             smartlist_t *auth_clients,
+                             int max_streams_close_circuit,
                              smartlist_t *auth_clients_v3, char **address_out)
 {
   hs_service_add_ephemeral_status_t ret;
@@ -1661,11 +1589,6 @@ add_onion_helper_add_service(int hs_version,
   tor_assert(address_out);
 
   switch (hs_version) {
-  case HS_VERSION_TWO:
-    ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams,
-                                     max_streams_close_circuit, auth_type,
-                                     auth_clients, address_out);
-    break;
   case HS_VERSION_THREE:
     ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
                                    max_streams_close_circuit,
@@ -1711,16 +1634,14 @@ handle_control_add_onion(control_connection_t *conn,
    * material first, since there's no reason to touch that at all if any of
    * the other arguments are malformed.
    */
+  rend_auth_type_t auth_type = REND_NO_AUTH;
   smartlist_t *port_cfgs = smartlist_new();
-  smartlist_t *auth_clients = NULL;
-  smartlist_t *auth_created_clients = NULL;
   smartlist_t *auth_clients_v3 = NULL;
   smartlist_t *auth_clients_v3_str = NULL;
   int discard_pk = 0;
   int detach = 0;
   int max_streams = 0;
   int max_streams_close_circuit = 0;
-  rend_auth_type_t auth_type = REND_NO_AUTH;
   int non_anonymous = 0;
   const config_line_t *arg;
 
@@ -1758,7 +1679,6 @@ handle_control_add_onion(control_connection_t *conn,
       static const char *discard_flag = "DiscardPK";
       static const char *detach_flag = "Detach";
       static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
-      static const char *basicauth_flag = "BasicAuth";
       static const char *v3auth_flag = "V3Auth";
       static const char *non_anonymous_flag = "NonAnonymous";
 
@@ -1778,8 +1698,6 @@ handle_control_add_onion(control_connection_t *conn,
           detach = 1;
         } else if (!strcasecmp(flag, max_s_close_flag)) {
           max_streams_close_circuit = 1;
-        } else if (!strcasecmp(flag, basicauth_flag)) {
-          auth_type = REND_BASIC_AUTH;
         } else if (!strcasecmp(flag, v3auth_flag)) {
           auth_type = REND_V3_AUTH;
         } else if (!strcasecmp(flag, non_anonymous_flag)) {
@@ -1795,36 +1713,6 @@ handle_control_add_onion(control_connection_t *conn,
       smartlist_free(flags);
       if (bad)
         goto out;
-
-    } else if (!strcasecmp(arg->key, "ClientAuth")) {
-      int created = 0;
-      rend_authorized_client_t *client =
-        add_onion_helper_clientauth(arg->value, &created, conn);
-      if (!client) {
-        goto out;
-      }
-
-      if (auth_clients != NULL) {
-        int bad = 0;
-        SMARTLIST_FOREACH_BEGIN(auth_clients, rend_authorized_client_t *, ac) {
-          if (strcmp(ac->client_name, client->client_name) == 0) {
-            bad = 1;
-            break;
-          }
-        } SMARTLIST_FOREACH_END(ac);
-        if (bad) {
-          control_write_endreply(conn, 512, "Duplicate name in ClientAuth");
-          rend_authorized_client_free(client);
-          goto out;
-        }
-      } else {
-        auth_clients = smartlist_new();
-        auth_created_clients = smartlist_new();
-      }
-      smartlist_add(auth_clients, client);
-      if (created) {
-        smartlist_add(auth_created_clients, client);
-      }
     } else if (!strcasecmp(arg->key, "ClientAuthV3")) {
       hs_service_authorized_client_t *client_v3 =
                              parse_authorized_client_key(arg->value, LOG_INFO);
@@ -1848,31 +1736,14 @@ handle_control_add_onion(control_connection_t *conn,
   if (smartlist_len(port_cfgs) == 0) {
     control_write_endreply(conn, 512, "Missing 'Port' argument");
     goto out;
-  } else if (auth_type == REND_NO_AUTH &&
-             (auth_clients != NULL && auth_clients_v3 != NULL)) {
+  } else if (auth_type == REND_NO_AUTH && auth_clients_v3 != NULL) {
     control_write_endreply(conn, 512, "No auth type specified");
     goto out;
-  } else if (auth_type != REND_NO_AUTH &&
-             (auth_clients == NULL && auth_clients_v3 == NULL)) {
+  } else if (auth_type != REND_NO_AUTH && auth_clients_v3 == NULL) {
     control_write_endreply(conn, 512, "No auth clients specified");
     goto out;
-  } else if ((auth_type == REND_BASIC_AUTH &&
-              smartlist_len(auth_clients) > 512) ||
-             (auth_type == REND_STEALTH_AUTH &&
-              smartlist_len(auth_clients) > 16)) {
-    control_write_endreply(conn, 512, "Too many auth clients");
-    goto out;
-  } else if ((auth_type == REND_BASIC_AUTH ||
-              auth_type == REND_STEALTH_AUTH) && auth_clients_v3) {
-    control_write_endreply(conn, 512,
-                        "ClientAuthV3 does not support basic or stealth auth");
-    goto out;
-  } else if (auth_type == REND_V3_AUTH && auth_clients) {
-    control_write_endreply(conn, 512, "ClientAuth does not support v3 auth");
-    goto out;
-
-  } else if (non_anonymous != rend_service_non_anonymous_mode_enabled(
-                                                              get_options())) {
+  } else if (non_anonymous != hs_service_non_anonymous_mode_enabled(
+                                                            get_options())) {
     /* If we failed, and the non-anonymous flag is set, Tor must be in
      * anonymous hidden service mode.
      * The error message changes based on the current Tor config:
@@ -1899,29 +1770,15 @@ handle_control_add_onion(control_connection_t *conn,
     goto out;
   }
 
-  /* We can't mix ClientAuth and Version 3 Onion Services, or ClientAuthV3 and
-   * Version 2. If that's the case, send back an error. */
-  if (hs_version == HS_VERSION_THREE && auth_clients) {
-    control_write_endreply(conn, 513, "ClientAuth not supported");
-    goto out;
-  }
-  if (hs_version == HS_VERSION_TWO && auth_clients_v3) {
-    control_write_endreply(conn, 513, "ClientAuthV3 not supported");
-    goto out;
-  }
-
-  /* Create the HS, using private key pk, client authentication auth_type,
-   * the list of auth_clients, and port config port_cfg.
-   * rend_service_add_ephemeral() will take ownership of pk and port_cfg,
-   * regardless of success/failure.
-   */
+  /* Create the HS, using private key pk and port config port_cfg.
+   * hs_service_add_ephemeral() will take ownership of pk and port_cfg,
+   * regardless of success/failure. */
   char *service_id = NULL;
-  int ret =
-      add_onion_helper_add_service(hs_version, &pk, port_cfgs, max_streams,
-                                   max_streams_close_circuit, auth_type,
-                                   auth_clients, auth_clients_v3, &service_id);
-  port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
-  auth_clients = NULL; /* so is auth_clients */
+  int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs,
+                                         max_streams,
+                                         max_streams_close_circuit,
+                                         auth_clients_v3, &service_id);
+  port_cfgs = NULL; /* port_cfgs is now owned by the hs_service code. */
   auth_clients_v3 = NULL; /* so is auth_clients_v3 */
   switch (ret) {
   case RSAE_OKAY:
@@ -1943,17 +1800,6 @@ handle_control_add_onion(control_connection_t *conn,
       control_printf_midreply(conn, 250, "PrivateKey=%s:%s",
                               key_new_alg, key_new_blob);
     }
-    if (auth_created_clients) {
-      SMARTLIST_FOREACH(auth_created_clients, rend_authorized_client_t *, ac, {
-        char *encoded = rend_auth_encode_cookie(ac->descriptor_cookie,
-                                                auth_type);
-        tor_assert(encoded);
-        control_printf_midreply(conn, 250, "ClientAuth=%s:%s",
-                                ac->client_name, encoded);
-        memwipe(encoded, 0, strlen(encoded));
-        tor_free(encoded);
-      });
-    }
     if (auth_clients_v3_str) {
       SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str, {
         control_printf_midreply(conn, 250, "ClientAuthV3=%s", client_str);
@@ -1990,12 +1836,6 @@ handle_control_add_onion(control_connection_t *conn,
                       hs_port_config_free(p));
     smartlist_free(port_cfgs);
   }
-
-  if (auth_clients) {
-    SMARTLIST_FOREACH(auth_clients, rend_authorized_client_t *, ac,
-                      rend_authorized_client_free(ac));
-    smartlist_free(auth_clients);
-  }
   if (auth_clients_v3) {
     SMARTLIST_FOREACH(auth_clients_v3, hs_service_authorized_client_t *, ac,
                       service_authorized_client_free(ac));
@@ -2007,10 +1847,6 @@ handle_control_add_onion(control_connection_t *conn,
     smartlist_free(auth_clients_v3_str);
   }
 
-  if (auth_created_clients) {
-    // Do not free entries; they are the same as auth_clients
-    smartlist_free(auth_created_clients);
-  }
   return 0;
 }
 
@@ -2034,7 +1870,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
                         control_connection_t *conn)
 {
   smartlist_t *key_args = smartlist_new();
-  crypto_pk_t *pk = NULL;
   const char *key_new_alg = NULL;
   char *key_new_blob = NULL;
   int ret = -1;
@@ -2048,27 +1883,12 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
   /* The format is "KeyType:KeyBlob". */
   static const char *key_type_new = "NEW";
   static const char *key_type_best = "BEST";
-  static const char *key_type_rsa1024 = "RSA1024";
   static const char *key_type_ed25519_v3 = "ED25519-V3";
 
   const char *key_type = smartlist_get(key_args, 0);
   const char *key_blob = smartlist_get(key_args, 1);
 
-  if (!strcasecmp(key_type_rsa1024, key_type)) {
-    /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
-    pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob));
-    if (!pk) {
-      control_write_endreply(conn, 512, "Failed to decode RSA key");
-      goto err;
-    }
-    if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
-      crypto_pk_free(pk);
-      control_write_endreply(conn, 512, "Invalid RSA key size");
-      goto err;
-    }
-    decoded_key->v2 = pk;
-    *hs_version = HS_VERSION_TWO;
-  } else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
+  if (!strcasecmp(key_type_ed25519_v3, key_type)) {
     /* parsing of private ed25519 key */
     /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
     ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
@@ -2082,27 +1902,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
     *hs_version = HS_VERSION_THREE;
   } else if (!strcasecmp(key_type_new, key_type)) {
     /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
-    if (!strcasecmp(key_type_rsa1024, key_blob)) {
-      /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */
-      pk = crypto_pk_new();
-      if (crypto_pk_generate_key(pk)) {
-        control_printf_endreply(conn, 551, "Failed to generate %s key",
-                                key_type_rsa1024);
-        goto err;
-      }
-      if (!discard_pk) {
-        if (crypto_pk_base64_encode_private(pk, &key_new_blob)) {
-          crypto_pk_free(pk);
-          control_printf_endreply(conn, 551, "Failed to encode %s key",
-                                  key_type_rsa1024);
-          goto err;
-        }
-        key_new_alg = key_type_rsa1024;
-      }
-      decoded_key->v2 = pk;
-      *hs_version = HS_VERSION_TWO;
-    } else if (!strcasecmp(key_type_ed25519_v3, key_blob) ||
-               !strcasecmp(key_type_best, key_blob)) {
+    if (!strcasecmp(key_type_ed25519_v3, key_blob) ||
+        !strcasecmp(key_type_best, key_blob)) {
       /* "ED25519-V3", ed25519 key, also currently "BEST" by default. */
       ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
       if (ed25519_secret_key_generate(sk, 1) < 0) {
@@ -2151,68 +1952,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
   return ret;
 }
 
-/** Helper function to handle parsing a ClientAuth argument to the
- * ADD_ONION command.  Return a new rend_authorized_client_t, or NULL
- * and an optional control protocol error message on failure.  The
- * caller is responsible for freeing the returned auth_client.
- *
- * If 'created' is specified, it will be set to 1 when a new cookie has
- * been generated.
- *
- * Note: conn is only used for writing control replies. For testing
- * purposes, it can be NULL if control_write_reply() is appropriately
- * mocked.
- */
-STATIC rend_authorized_client_t *
-add_onion_helper_clientauth(const char *arg, int *created,
-                            control_connection_t *conn)
-{
-  int ok = 0;
-
-  tor_assert(arg);
-  tor_assert(created);
-
-  smartlist_t *auth_args = smartlist_new();
-  rend_authorized_client_t *client =
-    tor_malloc_zero(sizeof(rend_authorized_client_t));
-  smartlist_split_string(auth_args, arg, ":", 0, 0);
-  if (smartlist_len(auth_args) < 1 || smartlist_len(auth_args) > 2) {
-    control_write_endreply(conn, 512, "Invalid ClientAuth syntax");
-    goto err;
-  }
-  client->client_name = tor_strdup(smartlist_get(auth_args, 0));
-  if (smartlist_len(auth_args) == 2) {
-    char *decode_err_msg = NULL;
-    if (rend_auth_decode_cookie(smartlist_get(auth_args, 1),
-                                client->descriptor_cookie,
-                                NULL, &decode_err_msg) < 0) {
-      tor_assert(decode_err_msg);
-      control_write_endreply(conn, 512, decode_err_msg);
-      tor_free(decode_err_msg);
-      goto err;
-    }
-    *created = 0;
-  } else {
-    crypto_rand((char *) client->descriptor_cookie, REND_DESC_COOKIE_LEN);
-    *created = 1;
-  }
-
-  if (!rend_valid_client_name(client->client_name)) {
-    control_write_endreply(conn, 512, "Invalid name in ClientAuth");
-    goto err;
-  }
-
-  ok = 1;
- err:
-  SMARTLIST_FOREACH(auth_args, char *, item, tor_free(item));
-  smartlist_free(auth_args);
-  if (!ok) {
-    rend_authorized_client_free(client);
-    client = NULL;
-  }
-  return client;
-}
-
 static const control_cmd_syntax_t del_onion_syntax = {
   .min_args = 1, .max_args = 1,
 };
@@ -2228,9 +1967,7 @@ handle_control_del_onion(control_connection_t *conn,
   tor_assert(smartlist_len(args) == 1);
 
   const char *service_id = smartlist_get(args, 0);
-  if (rend_valid_v2_service_id(service_id)) {
-    hs_version = HS_VERSION_TWO;
-  } else if (hs_address_is_valid(service_id)) {
+  if (hs_address_is_valid(service_id)) {
     hs_version = HS_VERSION_THREE;
   } else {
     control_write_endreply(conn, 512, "Malformed Onion Service id");
@@ -2261,9 +1998,6 @@ handle_control_del_onion(control_connection_t *conn,
   } else {
     int ret = -1;
     switch (hs_version) {
-    case HS_VERSION_TWO:
-      ret = rend_service_del_ephemeral(service_id);
-      break;
     case HS_VERSION_THREE:
       ret = hs_service_del_ephemeral(service_id);
       break;
diff --git a/src/feature/control/control_cmd.h b/src/feature/control/control_cmd.h
index b3c1d5cb2f..f21dc65edd 100644
--- a/src/feature/control/control_cmd.h
+++ b/src/feature/control/control_cmd.h
@@ -99,13 +99,9 @@ STATIC hs_service_add_ephemeral_status_t add_onion_helper_add_service(
                              int hs_version,
                              add_onion_secret_key_t *pk,
                              smartlist_t *port_cfgs, int max_streams,
-                             int max_streams_close_circuit, int auth_type,
-                             smartlist_t *auth_clients,
+                             int max_streams_close_circuit,
                              smartlist_t *auth_clients_v3, char **address_out);
 
-STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg,
-                                   int *created, control_connection_t *conn);
-
 STATIC control_cmd_args_t *control_cmd_parse_args(
                                    const char *command,
                                    const control_cmd_syntax_t *syntax,
diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c
index b38b7a4f42..2e192c98ad 100644
--- a/src/feature/control/control_events.c
+++ b/src/feature/control/control_events.c
@@ -2066,8 +2066,6 @@ control_event_hs_descriptor_upload(const char *onion_address,
 /** send HS_DESC event after got response from hs directory.
  *
  * NOTE: this is an internal function used by following functions:
- * control_event_hsv2_descriptor_received
- * control_event_hsv2_descriptor_failed
  * control_event_hsv3_descriptor_failed
  *
  * So do not call this function directly.
@@ -2138,82 +2136,6 @@ control_event_hs_descriptor_upload_end(const char *action,
   tor_free(reason_field);
 }
 
-/** For an HS descriptor query <b>rend_data</b>, using the
- * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out
- * which descriptor ID in the query is the right one.
- *
- * Return a pointer of the binary descriptor ID found in the query's object
- * or NULL if not found. */
-static const char *
-get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
-{
-  int replica;
-  const char *desc_id = NULL;
-  const rend_data_v2_t *rend_data_v2 = TO_REND_DATA_V2(rend_data);
-
-  /* Possible if the fetch was done using a descriptor ID. This means that
-   * the HSFETCH command was used. */
-  if (!tor_digest_is_zero(rend_data_v2->desc_id_fetch)) {
-    desc_id = rend_data_v2->desc_id_fetch;
-    goto end;
-  }
-
-  /* Without a directory fingerprint at this stage, we can't do much. */
-  if (hsdir_fp == NULL) {
-     goto end;
-  }
-
-  /* OK, we have an onion address so now let's find which descriptor ID
-   * is the one associated with the HSDir fingerprint. */
-  for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
-       replica++) {
-    const char *digest = rend_data_get_desc_id(rend_data, replica, NULL);
-
-    SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) {
-      if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) {
-        /* Found it! This descriptor ID is the right one. */
-        desc_id = digest;
-        goto end;
-      }
-    } SMARTLIST_FOREACH_END(fingerprint);
-  }
-
- end:
-  return desc_id;
-}
-
-/** send HS_DESC RECEIVED event
- *
- * called when we successfully received a hidden service descriptor.
- */
-void
-control_event_hsv2_descriptor_received(const char *onion_address,
-                                       const rend_data_t *rend_data,
-                                       const char *hsdir_id_digest)
-{
-  char *desc_id_field = NULL;
-  const char *desc_id;
-
-  if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) {
-    return;
-  }
-
-  desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
-  if (desc_id != NULL) {
-    char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-    /* Set the descriptor ID digest to base32 so we can send it. */
-    base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
-                  DIGEST_LEN);
-    /* Extra whitespace is needed before the value. */
-    tor_asprintf(&desc_id_field, " %s", desc_id_base32);
-  }
-
-  event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
-                                  TO_REND_DATA_V2(rend_data)->auth_type,
-                                  hsdir_id_digest, NULL);
-  tor_free(desc_id_field);
-}
-
 /* Send HS_DESC RECEIVED event
  *
  * Called when we successfully received a hidden service descriptor. */
@@ -2253,40 +2175,6 @@ control_event_hs_descriptor_uploaded(const char *id_digest,
                                          id_digest, NULL);
 }
 
-/** Send HS_DESC event to inform controller that query <b>rend_data</b>
- * failed to retrieve hidden service descriptor from directory identified by
- * <b>id_digest</b>. If NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL,
- * add it to REASON= field.
- */
-void
-control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
-                                     const char *hsdir_id_digest,
-                                     const char *reason)
-{
-  char *desc_id_field = NULL;
-  const char *desc_id;
-
-  if (BUG(!rend_data)) {
-    return;
-  }
-
-  desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
-  if (desc_id != NULL) {
-    char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-    /* Set the descriptor ID digest to base32 so we can send it. */
-    base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
-                  DIGEST_LEN);
-    /* Extra whitespace is needed before the value. */
-    tor_asprintf(&desc_id_field, " %s", desc_id_base32);
-  }
-
-  event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data),
-                                  desc_id_field,
-                                  TO_REND_DATA_V2(rend_data)->auth_type,
-                                  hsdir_id_digest, reason);
-  tor_free(desc_id_field);
-}
-
 /** Send HS_DESC event to inform controller that the query to
  * <b>onion_address</b> failed to retrieve hidden service descriptor
  * <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If
diff --git a/src/feature/control/control_events.h b/src/feature/control/control_events.h
index e499c037ba..d20091e662 100644
--- a/src/feature/control/control_events.h
+++ b/src/feature/control/control_events.h
@@ -202,13 +202,6 @@ void control_event_hs_descriptor_upload_end(const char *action,
                                             const char *reason);
 void control_event_hs_descriptor_uploaded(const char *hs_dir,
                                           const char *onion_address);
-/* Hidden service v2 HS_DESC specific. */
-void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
-                                          const char *id_digest,
-                                          const char *reason);
-void control_event_hsv2_descriptor_received(const char *onion_address,
-                                            const rend_data_t *rend_data,
-                                            const char *id_digest);
 /* Hidden service v3 HS_DESC specific. */
 void control_event_hsv3_descriptor_failed(const char *onion_address,
                                           const char *desc_id,
diff --git a/src/feature/control/control_fmt.c b/src/feature/control/control_fmt.c
index 014427c5b5..cb0673ee7d 100644
--- a/src/feature/control/control_fmt.c
+++ b/src/feature/control/control_fmt.c
@@ -122,15 +122,11 @@ circuit_describe_status_for_controller(origin_circuit_t *circ)
     }
   }
 
-  if (circ->rend_data != NULL || circ->hs_ident != NULL) {
+  if (circ->hs_ident != NULL) {
     char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
     const char *onion_address;
-    if (circ->rend_data) {
-      onion_address = rend_data_get_address(circ->rend_data);
-    } else {
-      hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
-      onion_address = addr;
-    }
+    hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
+    onion_address = addr;
     smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address);
   }
 
diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c
index 75d5418d19..29032111ef 100644
--- a/src/feature/control/control_getinfo.c
+++ b/src/feature/control/control_getinfo.c
@@ -47,7 +47,6 @@
 #include "feature/relay/router.h"
 #include "feature/relay/routermode.h"
 #include "feature/relay/selftest.h"
-#include "feature/rend/rendcache.h"
 #include "feature/stats/geoip_stats.h"
 #include "feature/stats/predict_ports.h"
 #include "feature/stats/rephist.h"
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index 00bb0abf23..013fd1f9ae 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -31,7 +31,6 @@
 #include "feature/nodelist/routerlist.h"
 #include "feature/relay/relay_config.h"
 #include "feature/relay/routermode.h"
-#include "feature/rend/rendcache.h"
 #include "feature/stats/geoip_stats.h"
 #include "feature/stats/rephist.h"
 #include "lib/compress/compress.h"
@@ -353,8 +352,6 @@ static int handle_get_descriptor(dir_connection_t *conn,
                                 const get_handler_args_t *args);
 static int handle_get_keys(dir_connection_t *conn,
                                 const get_handler_args_t *args);
-static int handle_get_hs_descriptor_v2(dir_connection_t *conn,
-                                       const get_handler_args_t *args);
 static int handle_get_robots(dir_connection_t *conn,
                                 const get_handler_args_t *args);
 static int handle_get_networkstatus_bridges(dir_connection_t *conn,
@@ -373,7 +370,6 @@ static const url_table_ent_t url_table[] = {
   { "/tor/server/", 1, handle_get_descriptor },
   { "/tor/extra/", 1, handle_get_descriptor },
   { "/tor/keys/", 1, handle_get_keys },
-  { "/tor/rendezvous2/", 1, handle_get_hs_descriptor_v2 },
   { "/tor/hs/3/", 1, handle_get_hs_descriptor_v3 },
   { "/tor/robots.txt", 0, handle_get_robots },
   { "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges },
@@ -1347,44 +1343,6 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
   return 0;
 }
 
-/** Helper function for GET /tor/rendezvous2/
- */
-static int
-handle_get_hs_descriptor_v2(dir_connection_t *conn,
-                            const get_handler_args_t *args)
-{
-  const char *url = args->url;
-  if (connection_dir_is_encrypted(conn)) {
-    /* Handle v2 rendezvous descriptor fetch request. */
-    const char *descp;
-    const char *query = url + strlen("/tor/rendezvous2/");
-    if (rend_valid_descriptor_id(query)) {
-      log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
-               safe_str(escaped(query)));
-      switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
-        case 1: /* valid */
-          write_http_response_header(conn, strlen(descp), NO_METHOD, 0);
-          connection_buf_add(descp, strlen(descp), TO_CONN(conn));
-          break;
-        case 0: /* well-formed but not present */
-          write_short_http_response(conn, 404, "Not found");
-          break;
-        case -1: /* not well-formed */
-          write_short_http_response(conn, 400, "Bad request");
-          break;
-      }
-    } else { /* not well-formed */
-      write_short_http_response(conn, 400, "Bad request");
-    }
-    goto done;
-  } else {
-    /* Not encrypted! */
-    write_short_http_response(conn, 404, "Not found");
-  }
- done:
-  return 0;
-}
-
 /** Helper function for GET `/tor/hs/3/...`. Only for version 3.
  */
 STATIC int
@@ -1626,22 +1584,6 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
   }
   log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
 
-  /* Handle v2 rendezvous service publish request. */
-  if (connection_dir_is_encrypted(conn) &&
-      !strcmpstart(url,"/tor/rendezvous2/publish")) {
-    if (rend_cache_store_v2_desc_as_dir(body) < 0) {
-      log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.",
-               (int)body_len,
-               connection_describe_peer(TO_CONN(conn)));
-      write_short_http_response(conn, 400,
-                             "Invalid v2 service descriptor rejected");
-    } else {
-      write_short_http_response(conn, 200, "Service descriptor (v2) stored");
-      log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
-    }
-    goto done;
-  }
-
   /* Handle HS descriptor publish request. We force an anonymous connection
    * (which also tests for encrypted). We do not allow single-hop client to
    * post a descriptor onto an HSDir. */
diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c
index dd7af9dbfc..c5b0d19dd7 100644
--- a/src/feature/dirclient/dirclient.c
+++ b/src/feature/dirclient/dirclient.c
@@ -47,9 +47,7 @@
 #include "feature/relay/relay_find_addr.h"
 #include "feature/relay/routermode.h"
 #include "feature/relay/selftest.h"
-#include "feature/rend/rendcache.h"
 #include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/predict_ports.h"
 
 #include "lib/cc/ctassert.h"
@@ -66,7 +64,6 @@
 #include "feature/nodelist/networkstatus_st.h"
 #include "feature/nodelist/node_st.h"
 #include "feature/nodelist/routerinfo_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
 
 /** Maximum size, in bytes, for any directory object that we've downloaded. */
 #define MAX_DIR_DL_SIZE ((1<<24)-1) /* 16 MB - 1 */
@@ -119,10 +116,6 @@ dir_conn_purpose_to_string(int purpose)
       return "status vote fetch";
     case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
       return "consensus signature fetch";
-    case DIR_PURPOSE_FETCH_RENDDESC_V2:
-      return "hidden-service v2 descriptor fetch";
-    case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
-      return "hidden-service v2 descriptor upload";
     case DIR_PURPOSE_FETCH_HSDESC:
       return "hidden-service descriptor fetch";
     case DIR_PURPOSE_UPLOAD_HSDESC:
@@ -949,7 +942,6 @@ directory_request_new(uint8_t dir_purpose)
   tor_assert(dir_purpose >= DIR_PURPOSE_MIN_);
   tor_assert(dir_purpose <= DIR_PURPOSE_MAX_);
   tor_assert(dir_purpose != DIR_PURPOSE_SERVER);
-  tor_assert(dir_purpose != DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2);
   tor_assert(dir_purpose != DIR_PURPOSE_HAS_FETCHED_HSDESC);
 
   directory_request_t *result = tor_malloc_zero(sizeof(*result));
@@ -1086,21 +1078,6 @@ directory_request_add_header(directory_request_t *req,
 {
   config_line_prepend(&req->additional_headers, key, val);
 }
-/**
- * Set an object containing HS data to be associated with this request.  Note
- * that only an alias to <b>query</b> is stored, so the <b>query</b> object
- * must outlive the request.
- */
-void
-directory_request_set_rend_query(directory_request_t *req,
-                                 const rend_data_t *query)
-{
-  if (query) {
-    tor_assert(req->dir_purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 ||
-               req->dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2);
-  }
-  req->rend_query = query;
-}
 /**
  * Set an object containing HS connection identifier to be associated with
  * this request. Note that only an alias to <b>ident</b> is stored, so the
@@ -1249,7 +1226,6 @@ directory_initiate_request,(directory_request_t *request))
   const uint8_t router_purpose = request->router_purpose;
   const dir_indirection_t indirection = request->indirection;
   const char *resource = request->resource;
-  const rend_data_t *rend_query = request->rend_query;
   const hs_ident_dir_conn_t *hs_ident = request->hs_ident;
   circuit_guard_state_t *guard_state = request->guard_state;
 
@@ -1285,7 +1261,7 @@ directory_initiate_request,(directory_request_t *request))
 
   if (purpose_needs_anonymity(dir_purpose, router_purpose, resource)) {
     tor_assert(anonymized_connection ||
-               rend_non_anonymous_mode_enabled(options));
+               hs_service_non_anonymous_mode_enabled(options));
   }
 
   /* use encrypted begindir connections for everything except relays
@@ -1337,15 +1313,7 @@ directory_initiate_request,(directory_request_t *request))
   /* XXXX This is a bad name for this field now. */
   conn->dirconn_direct = !anonymized_connection;
 
-  /* copy rendezvous data, if any */
-  if (rend_query) {
-    /* We can't have both v2 and v3+ identifier. */
-    tor_assert_nonfatal(!hs_ident);
-    conn->rend_data = rend_data_dup(rend_query);
-  }
   if (hs_ident) {
-    /* We can't have both v2 and v3+ identifier. */
-    tor_assert_nonfatal(!rend_query);
     conn->hs_ident = hs_ident_dir_conn_dup(hs_ident);
   }
 
@@ -1680,13 +1648,6 @@ directory_send_command(dir_connection_t *conn,
       httpcommand = "POST";
       url = tor_strdup("/tor/post/consensus-signature");
       break;
-    case DIR_PURPOSE_FETCH_RENDDESC_V2:
-      tor_assert(resource);
-      tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32);
-      tor_assert(!payload);
-      httpcommand = "GET";
-      tor_asprintf(&url, "/tor/rendezvous2/%s", resource);
-      break;
     case DIR_PURPOSE_FETCH_HSDESC:
       tor_assert(resource);
       tor_assert(strlen(resource) <= ED25519_BASE64_LEN);
@@ -1694,12 +1655,6 @@ directory_send_command(dir_connection_t *conn,
       httpcommand = "GET";
       tor_asprintf(&url, "/tor/hs/3/%s", resource);
       break;
-    case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
-      tor_assert(!resource);
-      tor_assert(payload);
-      httpcommand = "POST";
-      url = tor_strdup("/tor/rendezvous2/publish");
-      break;
     case DIR_PURPOSE_UPLOAD_HSDESC:
       tor_assert(resource);
       tor_assert(payload);
@@ -1843,10 +1798,6 @@ static int handle_response_upload_vote(dir_connection_t *,
                                        const response_handler_args_t *);
 static int handle_response_upload_signatures(dir_connection_t *,
                                              const response_handler_args_t *);
-static int handle_response_fetch_renddesc_v2(dir_connection_t *,
-                                             const response_handler_args_t *);
-static int handle_response_upload_renddesc_v2(dir_connection_t *,
-                                              const response_handler_args_t *);
 static int handle_response_upload_hsdesc(dir_connection_t *,
                                          const response_handler_args_t *);
 
@@ -2193,9 +2144,6 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
     case DIR_PURPOSE_FETCH_MICRODESC:
       rv = handle_response_fetch_microdesc(conn, &args);
       break;
-    case DIR_PURPOSE_FETCH_RENDDESC_V2:
-      rv = handle_response_fetch_renddesc_v2(conn, &args);
-      break;
     case DIR_PURPOSE_UPLOAD_DIR:
       rv = handle_response_upload_dir(conn, &args);
       break;
@@ -2205,9 +2153,6 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
     case DIR_PURPOSE_UPLOAD_VOTE:
       rv = handle_response_upload_vote(conn, &args);
       break;
-    case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
-      rv = handle_response_upload_renddesc_v2(conn, &args);
-      break;
     case DIR_PURPOSE_UPLOAD_HSDESC:
       rv = handle_response_upload_hsdesc(conn, &args);
       break;
@@ -2800,152 +2745,6 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
   return 0;
 }
 
-/**
- * Handler function: processes a response to a request for a v2 hidden service
- * descriptor.
- **/
-static int
-handle_response_fetch_renddesc_v2(dir_connection_t *conn,
-                                  const response_handler_args_t *args)
-{
-  tor_assert(conn->base_.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2);
-  const int status_code = args->status_code;
-  const char *reason = args->reason;
-  const char *body = args->body;
-  const size_t body_len = args->body_len;
-
-#define SEND_HS_DESC_FAILED_EVENT(reason)                               \
-  (control_event_hsv2_descriptor_failed(conn->rend_data,                \
-                                        conn->identity_digest,          \
-                                        reason))
-#define SEND_HS_DESC_FAILED_CONTENT()                                   \
-  (control_event_hs_descriptor_content(                                 \
-                                rend_data_get_address(conn->rend_data), \
-                                conn->requested_resource,               \
-                                conn->identity_digest,                  \
-                                NULL))
-
-  tor_assert(conn->rend_data);
-  log_info(LD_REND,"Received rendezvous descriptor (body size %d, status %d "
-           "(%s))",
-           (int)body_len, status_code, escaped(reason));
-  switch (status_code) {
-  case 200:
-    {
-      rend_cache_entry_t *entry = NULL;
-
-      if (rend_cache_store_v2_desc_as_client(body,
-                                             conn->requested_resource,
-                                             conn->rend_data, &entry) < 0) {
-        log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
-                 "Retrying at another directory.");
-        /* We'll retry when connection_about_to_close_connection()
-         * cleans this dir conn up. */
-        SEND_HS_DESC_FAILED_EVENT("BAD_DESC");
-        SEND_HS_DESC_FAILED_CONTENT();
-      } else {
-        char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
-        /* Should never be NULL here if we found the descriptor. */
-        tor_assert(entry);
-        rend_get_service_id(entry->parsed->pk, service_id);
-
-        /* success. notify pending connections about this. */
-        log_info(LD_REND, "Successfully fetched v2 rendezvous "
-                 "descriptor.");
-        control_event_hsv2_descriptor_received(service_id,
-                                               conn->rend_data,
-                                               conn->identity_digest);
-        control_event_hs_descriptor_content(service_id,
-                                            conn->requested_resource,
-                                            conn->identity_digest,
-                                            body);
-        conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2;
-        memwipe(service_id, 0, sizeof(service_id));
-      }
-      break;
-    }
-  case 404:
-    /* Not there. We'll retry when
-     * connection_about_to_close_connection() cleans this conn up. */
-    log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: "
-             "Retrying at another directory.");
-    SEND_HS_DESC_FAILED_EVENT("NOT_FOUND");
-    SEND_HS_DESC_FAILED_CONTENT();
-    break;
-  case 400:
-    log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
-             "http status 400 (%s). Dirserver didn't like our "
-             "v2 rendezvous query? Retrying at another directory.",
-             escaped(reason));
-    SEND_HS_DESC_FAILED_EVENT("QUERY_REJECTED");
-    SEND_HS_DESC_FAILED_CONTENT();
-    break;
-  default:
-    log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
-             "http status %d (%s) response unexpected while "
-             "fetching v2 hidden service descriptor (server %s). "
-             "Retrying at another directory.",
-             status_code, escaped(reason),
-             connection_describe_peer(TO_CONN(conn)));
-    SEND_HS_DESC_FAILED_EVENT("UNEXPECTED");
-    SEND_HS_DESC_FAILED_CONTENT();
-    break;
-  }
-
-  return 0;
-}
-
-/**
- * Handler function: processes a response to a POST request to upload a v2
- * hidden service descriptor.
- **/
-static int
-handle_response_upload_renddesc_v2(dir_connection_t *conn,
-                                   const response_handler_args_t *args)
-{
-  tor_assert(conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2);
-  const int status_code = args->status_code;
-  const char *reason = args->reason;
-
-#define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason)                        \
-    (control_event_hs_descriptor_upload_failed(                         \
-                                conn->identity_digest,                  \
-                                rend_data_get_address(conn->rend_data), \
-                                reason))
-
-  log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
-           "(%s))",
-           status_code, escaped(reason));
-  /* Without the rend data, we'll have a problem identifying what has been
-   * uploaded for which service. */
-  tor_assert(conn->rend_data);
-  switch (status_code) {
-  case 200:
-    log_info(LD_REND,
-             "Uploading rendezvous descriptor: finished with status "
-             "200 (%s)", escaped(reason));
-    control_event_hs_descriptor_uploaded(conn->identity_digest,
-                                   rend_data_get_address(conn->rend_data));
-    rend_service_desc_has_uploaded(conn->rend_data);
-    break;
-  case 400:
-    log_warn(LD_REND,"http status 400 (%s) response from dirserver "
-             "%s. Malformed rendezvous descriptor?",
-             escaped(reason), connection_describe_peer(TO_CONN(conn)));
-    SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
-    break;
-  default:
-    log_warn(LD_REND,"http status %d (%s) response unexpected (server "
-             "%s).",
-             status_code, escaped(reason),
-             connection_describe_peer(TO_CONN(conn)));
-    SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
-    break;
-  }
-
-  return 0;
-}
-
 /**
  * Handler function: processes a response to a POST request to upload an
  * hidden service descriptor.
diff --git a/src/feature/dirclient/dirclient.h b/src/feature/dirclient/dirclient.h
index 096b197526..519cbb1211 100644
--- a/src/feature/dirclient/dirclient.h
+++ b/src/feature/dirclient/dirclient.h
@@ -74,8 +74,6 @@ void directory_request_set_payload(directory_request_t *req,
                                    size_t payload_len);
 void directory_request_set_if_modified_since(directory_request_t *req,
                                              time_t if_modified_since);
-void directory_request_set_rend_query(directory_request_t *req,
-                                      const rend_data_t *query);
 void directory_request_upload_set_hs_ident(directory_request_t *req,
                                            const hs_ident_dir_conn_t *ident);
 void directory_request_fetch_set_hs_ident(directory_request_t *req,
@@ -125,8 +123,6 @@ struct directory_request_t {
   size_t payload_len;
   /** Value to send in an if-modified-since header, or 0 for none. */
   time_t if_modified_since;
-  /** Hidden-service-specific information v2. */
-  const rend_data_t *rend_query;
   /** Extra headers to append to the request */
   struct config_line_t *additional_headers;
   /** Hidden-service-specific information for v3+. */
diff --git a/src/feature/dircommon/dir_connection_st.h b/src/feature/dircommon/dir_connection_st.h
index 12230e6741..958dc623d4 100644
--- a/src/feature/dircommon/dir_connection_st.h
+++ b/src/feature/dircommon/dir_connection_st.h
@@ -42,9 +42,6 @@ struct dir_connection_t {
   /** The compression object doing on-the-fly compression for spooled data. */
   struct tor_compress_state_t *compress_state;
 
-  /** What rendezvous service are we querying for? */
-  rend_data_t *rend_data;
-
   /* Hidden service connection identifier for dir connections: Used by HS
      client-side code to fetch HS descriptors, and by the service-side code to
      upload descriptors. */
diff --git a/src/feature/dircommon/directory.c b/src/feature/dircommon/directory.c
index 0029eb37a1..f264fd0750 100644
--- a/src/feature/dircommon/directory.c
+++ b/src/feature/dircommon/directory.c
@@ -142,9 +142,6 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose,
     case DIR_PURPOSE_FETCH_MICRODESC:
       return 0;
     case DIR_PURPOSE_HAS_FETCHED_HSDESC:
-    case DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2:
-    case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
-    case DIR_PURPOSE_FETCH_RENDDESC_V2:
     case DIR_PURPOSE_FETCH_HSDESC:
     case DIR_PURPOSE_UPLOAD_HSDESC:
       return 1;
diff --git a/src/feature/dircommon/directory.h b/src/feature/dircommon/directory.h
index 0aa2ff53ef..5e4b097816 100644
--- a/src/feature/dircommon/directory.h
+++ b/src/feature/dircommon/directory.h
@@ -30,10 +30,7 @@ const dir_connection_t *CONST_TO_DIR_CONN(const connection_t *c);
 #define DIR_CONN_STATE_SERVER_WRITING 6
 #define DIR_CONN_STATE_MAX_ 6
 
-#define DIR_PURPOSE_MIN_ 4
-/** A connection to a directory server: set after a v2 rendezvous
- * descriptor is downloaded. */
-#define DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2 4
+#define DIR_PURPOSE_MIN_ 6
 /** A connection to a directory server: download one or more server
  * descriptors. */
 #define DIR_PURPOSE_FETCH_SERVERDESC 6
@@ -61,12 +58,9 @@ const dir_connection_t *CONST_TO_DIR_CONN(const connection_t *c);
 
 /** Purpose for connection at a directory server. */
 #define DIR_PURPOSE_SERVER 16
-/** A connection to a hidden service directory server: upload a v2 rendezvous
- * descriptor. */
-#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 17
-/** A connection to a hidden service directory server: download a v2 rendezvous
- * descriptor. */
-#define DIR_PURPOSE_FETCH_RENDDESC_V2 18
+
+/** Value 17 and 18 were onion service v2 purposes. */
+
 /** A connection to a directory server: download a microdescriptor. */
 #define DIR_PURPOSE_FETCH_MICRODESC 19
 /** A connection to a hidden service directory: upload a v3 descriptor. */
@@ -84,7 +78,6 @@ const dir_connection_t *CONST_TO_DIR_CONN(const connection_t *c);
   ((p)==DIR_PURPOSE_UPLOAD_DIR ||               \
    (p)==DIR_PURPOSE_UPLOAD_VOTE ||              \
    (p)==DIR_PURPOSE_UPLOAD_SIGNATURES ||        \
-   (p)==DIR_PURPOSE_UPLOAD_RENDDESC_V2 ||       \
    (p)==DIR_PURPOSE_UPLOAD_HSDESC)
 
 enum compress_method_t;
diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c
index 765323df0d..ac43e78767 100644
--- a/src/feature/hs/hs_cache.c
+++ b/src/feature/hs/hs_cache.c
@@ -19,13 +19,15 @@
 #include "feature/hs/hs_descriptor.h"
 #include "feature/nodelist/microdesc.h"
 #include "feature/nodelist/networkstatus.h"
-#include "feature/rend/rendcache.h"
 #include "feature/stats/rephist.h"
 
 #include "feature/hs/hs_cache.h"
 
 #include "feature/nodelist/networkstatus_st.h"
 
+/* Total counter of the cache size. */
+static size_t hs_cache_total_allocation = 0;
+
 static int cached_client_descriptor_has_expired(time_t now,
            const hs_cache_client_descriptor_t *cached_desc);
 
@@ -164,7 +166,7 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
      * remove the entry we currently have from our cache so we can then
      * store the new one. */
     remove_v3_desc_as_dir(cache_entry);
-    rend_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry));
+    hs_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry));
     cache_dir_desc_free(cache_entry);
   }
   /* Store the descriptor we just got. We are sure here that either we
@@ -174,7 +176,7 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
 
   /* Update our total cache size with this entry for the OOM. This uses the
    * old HS protocol cache subsystem for which we are tied with. */
-  rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
+  hs_cache_increment_allocation(cache_get_dir_entry_size(desc));
 
   /* Update HSv3 statistics */
   if (get_options()->HiddenServiceStatistics) {
@@ -259,7 +261,7 @@ cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
     /* Entry is not in the cache anymore, destroy it. */
     cache_dir_desc_free(entry);
     /* Update our cache entry allocation size for the OOM. */
-    rend_cache_decrement_allocation(entry_size);
+    hs_cache_decrement_allocation(entry_size);
     /* Logging. */
     {
       char key_b64[BASE64_DIGEST256_LEN + 1];
@@ -336,12 +338,6 @@ hs_cache_lookup_as_dir(uint32_t version, const char *query,
 void
 hs_cache_clean_as_dir(time_t now)
 {
-  time_t cutoff;
-
-  /* Start with v2 cache cleaning. */
-  cutoff = now - rend_cache_max_entry_lifetime();
-  rend_cache_clean_v2_descs_as_dir(cutoff);
-
   /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
    * to compute the cutoff by itself using the lifetime value. */
   cache_clean_v3_as_dir(now, 0);
@@ -387,7 +383,7 @@ remove_v3_desc_as_client(const hs_cache_client_descriptor_t *desc)
   tor_assert(desc);
   digest256map_remove(hs_cache_v3_client, desc->key.pubkey);
   /* Update cache size with this entry for the OOM handler. */
-  rend_cache_decrement_allocation(cache_get_client_entry_size(desc));
+  hs_cache_decrement_allocation(cache_get_client_entry_size(desc));
 }
 
 /** Store a given descriptor in our cache. */
@@ -397,7 +393,7 @@ store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
   tor_assert(desc);
   digest256map_set(hs_cache_v3_client, desc->key.pubkey, desc);
   /* Update cache size with this entry for the OOM handler. */
-  rend_cache_increment_allocation(cache_get_client_entry_size(desc));
+  hs_cache_increment_allocation(cache_get_client_entry_size(desc));
 }
 
 /** Query our cache and return the entry or NULL if not found or if expired. */
@@ -796,7 +792,7 @@ cache_clean_v3_as_client(time_t now)
     cache_client_desc_free(entry);
     /* Update our OOM. We didn't use the remove() function because we are in
      * a loop so we have to explicitly decrement. */
-    rend_cache_decrement_allocation(entry_size);
+    hs_cache_decrement_allocation(entry_size);
     /* Logging. */
     {
       char key_b64[BASE64_DIGEST256_LEN + 1];
@@ -934,8 +930,6 @@ hs_cache_remove_as_client(const ed25519_public_key_t *key)
 void
 hs_cache_clean_as_client(time_t now)
 {
-  /* Start with v2 cache cleaning. */
-  rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
   /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
    * to compute the cutoff by itself using the lifetime value. */
   cache_clean_v3_as_client(now);
@@ -952,7 +946,7 @@ hs_cache_purge_as_client(void)
     cache_client_desc_free(entry);
     /* Update our OOM. We didn't use the remove() function because we are in
      * a loop so we have to explicitly decrement. */
-    rend_cache_decrement_allocation(entry_size);
+    hs_cache_decrement_allocation(entry_size);
   } DIGEST256MAP_FOREACH_END;
 
   log_info(LD_REND, "Hidden service client descriptor cache purged.");
@@ -1074,19 +1068,16 @@ hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
 
   /* The algorithm is as follow. K is the oldest expected descriptor age.
    *
-   *   1) Deallocate all entries from v2 cache that are older than K hours.
-   *      1.1) If the amount of remove bytes has been reached, stop.
-   *   2) Deallocate all entries from v3 cache that are older than K hours
+   *   1) Deallocate all entries from v3 cache that are older than K hours
    *      2.1) If the amount of remove bytes has been reached, stop.
-   *   3) Set K = K - RendPostPeriod and repeat process until K is < 0.
+   *   2) Set K = K - RendPostPeriod and repeat process until K is < 0.
    *
    * This ends up being O(Kn).
    */
 
   /* Set K to the oldest expected age in seconds which is the maximum
-   * lifetime of a cache entry. We'll use the v2 lifetime because it's much
-   * bigger than the v3 thus leading to cleaning older descriptors. */
-  k = rend_cache_max_entry_lifetime();
+   * lifetime of a cache entry. */
+  k = hs_cache_max_entry_lifetime();
 
   do {
     time_t cutoff;
@@ -1099,9 +1090,6 @@ hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
     /* Compute a cutoff value with K and the current time. */
     cutoff = now - k;
 
-    /* Start by cleaning the v2 cache with that cutoff. */
-    bytes_removed += rend_cache_clean_v2_descs_as_dir(cutoff);
-
     if (bytes_removed < min_remove_bytes) {
       /* We haven't remove enough bytes so clean v3 cache. */
       bytes_removed += cache_clean_v3_as_dir(now, cutoff);
@@ -1150,4 +1138,45 @@ hs_cache_free_all(void)
   digest256map_free(hs_cache_client_intro_state,
                     cache_client_intro_state_free_void);
   hs_cache_client_intro_state = NULL;
+  hs_cache_total_allocation = 0;
+}
+
+/* Return total size of the cache. */
+size_t
+hs_cache_get_total_allocation(void)
+{
+  return hs_cache_total_allocation;
+}
+
+/** Decrement the total bytes attributed to the rendezvous cache by n. */
+void
+hs_cache_decrement_allocation(size_t n)
+{
+  static int have_underflowed = 0;
+
+  if (hs_cache_total_allocation >= n) {
+    hs_cache_total_allocation -= n;
+  } else {
+    hs_cache_total_allocation = 0;
+    if (! have_underflowed) {
+      have_underflowed = 1;
+      log_warn(LD_BUG, "Underflow in hs_cache_decrement_allocation");
+    }
+  }
+}
+
+/** Increase the total bytes attributed to the rendezvous cache by n. */
+void
+hs_cache_increment_allocation(size_t n)
+{
+  static int have_overflowed = 0;
+  if (hs_cache_total_allocation <= SIZE_MAX - n) {
+    hs_cache_total_allocation += n;
+  } else {
+    hs_cache_total_allocation = SIZE_MAX;
+    if (! have_overflowed) {
+      have_overflowed = 1;
+      log_warn(LD_BUG, "Overflow in hs_cache_increment_allocation");
+    }
+  }
 }
diff --git a/src/feature/hs/hs_cache.h b/src/feature/hs/hs_cache.h
index bb3c77f224..e8165569db 100644
--- a/src/feature/hs/hs_cache.h
+++ b/src/feature/hs/hs_cache.h
@@ -21,6 +21,14 @@ struct ed25519_public_key_t;
 /** This is the maximum time an introduction point state object can stay in the
  * client cache in seconds (2 mins or 120 seconds). */
 #define HS_CACHE_CLIENT_INTRO_STATE_MAX_AGE (2 * 60)
+/** How old do we let hidden service descriptors get before discarding
+ * them as too old? */
+#define HS_CACHE_MAX_AGE (2*24*60*60)
+/** How wrong do we assume our clock may be when checking whether hidden
+ * services are too old or too new? */
+#define HS_CACHE_MAX_SKEW (24*60*60)
+/** How old do we keep an intro point failure entry in the failure cache? */
+#define HS_CACHE_FAILURE_MAX_AGE (5*60)
 
 /** Introduction point state. */
 typedef struct hs_cache_intro_state_t {
@@ -57,7 +65,6 @@ typedef struct hs_cache_dir_descriptor_t {
   /** Descriptor plaintext information. Obviously, we can't decrypt the
    * encrypted part of the descriptor. */
   hs_desc_plaintext_data_t *plaintext_data;
-
   /** Encoded descriptor which is basically in text form. It's a NUL terminated
    * string thus safe to strlen(). */
   char *encoded_desc;
@@ -65,6 +72,13 @@ typedef struct hs_cache_dir_descriptor_t {
 
 /* Public API */
 
+/* Return maximum lifetime in seconds of a cache entry. */
+static inline time_t
+hs_cache_max_entry_lifetime(void)
+{
+  return HS_CACHE_MAX_AGE + HS_CACHE_MAX_SKEW;
+}
+
 void hs_cache_init(void);
 void hs_cache_free_all(void);
 void hs_cache_clean_as_dir(time_t now);
@@ -102,6 +116,10 @@ void hs_cache_client_intro_state_purge(void);
 
 bool hs_cache_client_new_auth_parse(const ed25519_public_key_t *service_pk);
 
+size_t hs_cache_get_total_allocation(void);
+void hs_cache_decrement_allocation(size_t n);
+void hs_cache_increment_allocation(size_t n);
+
 #ifdef HS_CACHE_PRIVATE
 #include "lib/crypt_ops/crypto_ed25519.h"
 
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index 8bdaa4922a..01dd39e231 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -9,7 +9,6 @@
 #include "core/or/or.h"
 #include "app/config/config.h"
 #include "lib/crypt_ops/crypto_util.h"
-#include "feature/rend/rendservice.h"
 #include "feature/hs_common/replaycache.h"
 
 #include "feature/hs/hs_cell.h"
@@ -194,37 +193,10 @@ parse_introduce2_encrypted(const uint8_t *decrypted_data,
   return NULL;
 }
 
-/** Build a legacy ESTABLISH_INTRO cell with the given circuit nonce and RSA
- * encryption key. The encoded cell is put in cell_out that MUST at least be
- * of the size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on
- * success else a negative value and cell_out is untouched. */
-static ssize_t
-build_legacy_establish_intro(const char *circ_nonce, crypto_pk_t *enc_key,
-                             uint8_t *cell_out)
-{
-  ssize_t cell_len;
-
-  tor_assert(circ_nonce);
-  tor_assert(enc_key);
-  tor_assert(cell_out);
-
-  memwipe(cell_out, 0, RELAY_PAYLOAD_SIZE);
-
-  cell_len = rend_service_encode_establish_intro_cell((char*)cell_out,
-                                                      RELAY_PAYLOAD_SIZE,
-                                                      enc_key, circ_nonce);
-  return cell_len;
-}
-
 /** Parse an INTRODUCE2 cell from payload of size payload_len for the given
  * service and circuit which are used only for logging purposes. The resulting
  * parsed cell is put in cell_ptr_out.
  *
- * This function only parses prop224 INTRODUCE2 cells even when the intro point
- * is a legacy intro point. That's because intro points don't actually care
- * about the contents of the introduce cell. Legacy INTRODUCE cells are only
- * used by the legacy system now.
- *
  * Return 0 on success else a negative value and cell_ptr_out is untouched. */
 static int
 parse_introduce2_cell(const hs_service_t *service,
@@ -457,28 +429,6 @@ introduce1_set_auth_key(trn_cell_introduce1_t *cell,
          data->auth_pk->pubkey, trn_cell_introduce1_getlen_auth_key(cell));
 }
 
-/** Set the legacy ID field in the INTRODUCE1 cell from the given data. */
-static void
-introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
-                         const hs_cell_introduce1_data_t *data)
-{
-  tor_assert(cell);
-  tor_assert(data);
-
-  if (data->is_legacy) {
-    uint8_t digest[DIGEST_LEN];
-    if (BUG(crypto_pk_get_digest(data->legacy_key, (char *) digest) < 0)) {
-      return;
-    }
-    memcpy(trn_cell_introduce1_getarray_legacy_key_id(cell),
-           digest, trn_cell_introduce1_getlen_legacy_key_id(cell));
-  } else {
-    /* We have to zeroed the LEGACY_KEY_ID field. */
-    memset(trn_cell_introduce1_getarray_legacy_key_id(cell), 0,
-           trn_cell_introduce1_getlen_legacy_key_id(cell));
-  }
-}
-
 /** Build and add to the given DoS cell extension the given parameter type and
  * value. */
 static void
@@ -608,8 +558,7 @@ build_establish_intro_extensions(const hs_service_config_t *service_config,
 /** Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
  * object. The encoded cell is put in cell_out that MUST at least be of the
  * size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on success else
- * a negative value and cell_out is untouched. This function also supports
- * legacy cell creation. */
+ * a negative value and cell_out is untouched. */
 ssize_t
 hs_cell_build_establish_intro(const char *circ_nonce,
                               const hs_service_config_t *service_config,
@@ -625,16 +574,6 @@ hs_cell_build_establish_intro(const char *circ_nonce,
   tor_assert(service_config);
   tor_assert(ip);
 
-  /* Quickly handle the legacy IP. */
-  if (ip->base.is_only_legacy) {
-    tor_assert(ip->legacy_key);
-    cell_len = build_legacy_establish_intro(circ_nonce, ip->legacy_key,
-                                            cell_out);
-    tor_assert(cell_len <= RELAY_PAYLOAD_SIZE);
-    /* Success or not we are done here. */
-    goto done;
-  }
-
   /* Build the extensions, if any. */
   extensions = build_establish_intro_extensions(service_config, ip);
 
@@ -1022,9 +961,6 @@ hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
   trn_cell_extension_set_num(ext, 0);
   trn_cell_introduce1_set_extensions(cell, ext);
 
-  /* Set the legacy ID field. */
-  introduce1_set_legacy_id(cell, data);
-
   /* Set the authentication key. */
   introduce1_set_auth_key(cell, data);
 
@@ -1067,18 +1003,6 @@ hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len)
 
   tor_assert(payload);
 
-  /* If it is a legacy IP, rend-spec.txt specifies that a ACK is 0 byte and a
-   * NACK is 1 byte. We can't use the legacy function for this so we have to
-   * do a special case. */
-  if (payload_len <= 1) {
-    if (payload_len == 0) {
-      ret = TRUNNEL_HS_INTRO_ACK_STATUS_SUCCESS;
-    } else {
-      ret = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
-    }
-    goto end;
-  }
-
   if (trn_cell_introduce_ack_parse(&cell, payload, payload_len) < 0) {
     log_info(LD_REND, "Invalid INTRODUCE_ACK cell. Unable to parse it.");
     goto end;
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index b246ab423c..548e1cbe2a 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -28,7 +28,6 @@
 #include "feature/hs/hs_service.h"
 #include "feature/nodelist/describe.h"
 #include "feature/nodelist/nodelist.h"
-#include "feature/rend/rendservice.h"
 #include "feature/stats/rephist.h"
 #include "lib/crypt_ops/crypto_dh.h"
 #include "lib/crypt_ops/crypto_rand.h"
@@ -105,57 +104,6 @@ create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len,
   return cpath;
 }
 
-/** We are a v2 legacy HS client: Create and return a crypt path for the hidden
- * service on the other side of the rendezvous circuit <b>circ</b>. Initialize
- * the crypt path crypto using the body of the RENDEZVOUS1 cell at
- * <b>rend_cell_body</b> (which must be at least DH1024_KEY_LEN+DIGEST_LEN
- * bytes).
- */
-static crypt_path_t *
-create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body)
-{
-  crypt_path_t *hop = NULL;
-  char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
-
-  /* first DH1024_KEY_LEN bytes are g^y from the service. Finish the dh
-   * handshake...*/
-  tor_assert(circ->build_state);
-  tor_assert(circ->build_state->pending_final_cpath);
-  hop = circ->build_state->pending_final_cpath;
-
-  tor_assert(hop->rend_dh_handshake_state);
-  if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, hop->rend_dh_handshake_state,
-                               (char*)rend_cell_body, DH1024_KEY_LEN,
-                               keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
-    log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
-    goto err;
-  }
-  /* ... and set up cpath. */
-  if (cpath_init_circuit_crypto(hop,
-                                keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
-                                0, 0) < 0)
-    goto err;
-
-  /* Check whether the digest is right... */
-  if (tor_memneq(keys, rend_cell_body+DH1024_KEY_LEN, DIGEST_LEN)) {
-    log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
-    goto err;
-  }
-
-  /* clean up the crypto stuff we just made */
-  crypto_dh_free(hop->rend_dh_handshake_state);
-  hop->rend_dh_handshake_state = NULL;
-
-  goto done;
-
- err:
-  hop = NULL;
-
- done:
-  memwipe(keys, 0, sizeof(keys));
-  return hop;
-}
-
 /** Append the final <b>hop</b> to the cpath of the rend <b>circ</b>, and mark
  * <b>circ</b> ready for use to transfer HS relay cells. */
 static void
@@ -184,13 +132,6 @@ finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop,
   /* Append the hop to the cpath of this circuit */
   cpath_extend_linked_list(&circ->cpath, hop);
 
-  /* In legacy code, 'pending_final_cpath' points to the final hop we just
-   * appended to the cpath. We set the original pointer to NULL so that we
-   * don't double free it. */
-  if (circ->build_state) {
-    circ->build_state->pending_final_cpath = NULL;
-  }
-
   /* Finally, mark circuit as ready to be used for client streams */
   if (!is_service_side) {
     circuit_try_attaching_streams(circ);
@@ -198,7 +139,7 @@ finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop,
 }
 
 /** For a given circuit and a service introduction point object, register the
- * intro circuit to the circuitmap. This supports legacy intro point. */
+ * intro circuit to the circuitmap. */
 static void
 register_intro_circ(const hs_service_intro_point_t *ip,
                     origin_circuit_t *circ)
@@ -206,13 +147,8 @@ register_intro_circ(const hs_service_intro_point_t *ip,
   tor_assert(ip);
   tor_assert(circ);
 
-  if (ip->base.is_only_legacy) {
-    hs_circuitmap_register_intro_circ_v2_service_side(circ,
-                                                      ip->legacy_key_digest);
-  } else {
-    hs_circuitmap_register_intro_circ_v3_service_side(circ,
-                                         &ip->auth_key_kp.pubkey);
-  }
+  hs_circuitmap_register_intro_circ_v3_service_side(circ,
+                                                    &ip->auth_key_kp.pubkey);
 }
 
 /** Return the number of opened introduction circuit for the given circuit that
@@ -605,10 +541,6 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip,
 
   /* Populate the introduce1 data object. */
   memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t));
-  if (ip->legacy.key != NULL) {
-    intro1_data->is_legacy = 1;
-    intro1_data->legacy_key = ip->legacy.key;
-  }
   intro1_data->auth_pk = &ip->auth_key_cert->signed_key;
   intro1_data->enc_pk = &ip->enc_key;
   intro1_data->subcredential = subcredential;
@@ -635,8 +567,8 @@ cleanup_on_close_client_circ(circuit_t *circ)
   if (circuit_is_hs_v3(circ)) {
     hs_client_circuit_cleanup_on_close(circ);
   }
-  /* It is possible the circuit has an HS purpose but no identifier (rend_data
-   * or hs_ident). Thus possible that this passes through. */
+  /* It is possible the circuit has an HS purpose but no identifier (hs_ident).
+   * Thus possible that this passes through. */
 }
 
 /** Helper: cleanup function for client circuit. This is for every HS version.
@@ -649,8 +581,8 @@ cleanup_on_free_client_circ(circuit_t *circ)
   if (circuit_is_hs_v3(circ)) {
     hs_client_circuit_cleanup_on_free(circ);
   }
-  /* It is possible the circuit has an HS purpose but no identifier (rend_data
-   * or hs_ident). Thus possible that this passes through. */
+  /* It is possible the circuit has an HS purpose but no identifier (hs_ident).
+   * Thus possible that this passes through. */
 }
 
 /* ========== */
@@ -664,12 +596,7 @@ hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip)
 {
   tor_assert(ip);
 
-  if (ip->base.is_only_legacy) {
-    return hs_circuitmap_get_intro_circ_v2_service_side(ip->legacy_key_digest);
-  } else {
-    return hs_circuitmap_get_intro_circ_v3_service_side(
-                                        &ip->auth_key_kp.pubkey);
-  }
+  return hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
 }
 
 /** Return an introduction point established circuit matching the given intro
@@ -682,12 +609,7 @@ hs_circ_service_get_established_intro_circ(const hs_service_intro_point_t *ip)
 
   tor_assert(ip);
 
-  if (ip->base.is_only_legacy) {
-    circ = hs_circuitmap_get_intro_circ_v2_service_side(ip->legacy_key_digest);
-  } else {
-    circ = hs_circuitmap_get_intro_circ_v3_service_side(
-                                        &ip->auth_key_kp.pubkey);
-  }
+  circ = hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
 
   /* Only return circuit if it is established. */
   return (circ && TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO) ?
@@ -695,8 +617,7 @@ hs_circ_service_get_established_intro_circ(const hs_service_intro_point_t *ip)
 }
 
 /** Called when we fail building a rendezvous circuit at some point other than
- * the last hop: launches a new circuit to the same rendezvous point. This
- * supports legacy service.
+ * the last hop: launches a new circuit to the same rendezvous point.
  *
  * We currently relaunch connections to rendezvous points if:
  * - A rendezvous circuit timed out before connecting to RP.
@@ -726,8 +647,6 @@ hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ)
   /* Legacy services don't have a hidden service ident. */
   if (circ->hs_ident) {
     retry_service_rendezvous_point(circ);
-  } else {
-    rend_service_relaunch_rendezvous(circ);
   }
 
  done:
@@ -762,9 +681,7 @@ hs_circ_launch_intro_point(hs_service_t *service,
     goto end;
   }
   /* We only use a one-hop path on the first attempt. If the first attempt
-   * fails, we use a 3-hop path for reachability / reliability.
-   * (Unlike v2, retries is incremented by the caller before it calls this
-   * function.) */
+   * fails, we use a 3-hop path for reachability / reliability. */
   if (direct_conn && ip->circuit_retries == 1) {
     circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
   }
@@ -952,10 +869,8 @@ hs_circ_handle_intro_established(const hs_service_t *service,
   }
 
   /* Try to parse the payload into a cell making sure we do actually have a
-   * valid cell. For a legacy node, it's an empty payload so as long as we
-   * have the cell, we are good. */
-  if (!ip->base.is_only_legacy &&
-      hs_cell_parse_intro_established(payload, payload_len) < 0) {
+   * valid cell. */
+  if (hs_cell_parse_intro_established(payload, payload_len) < 0) {
     log_warn(LD_REND, "Unable to parse the INTRO_ESTABLISHED cell on "
                       "circuit %u for service %s",
              TO_CIRCUIT(circ)->n_circ_id,
@@ -1112,31 +1027,6 @@ hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
   return 0;
 }
 
-/** We are a v2 legacy HS client and we just received a RENDEZVOUS1 cell
- * <b>rend_cell_body</b> on <b>circ</b>. Finish up the DH key exchange and then
- * extend the crypt path of <b>circ</b> so that the hidden service is on the
- * other side. */
-int
-hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
-                                             const uint8_t *rend_cell_body)
-{
-
-  if (BUG(!circuit_purpose_is_correct_for_rend(
-                                      TO_CIRCUIT(circ)->purpose, 0))) {
-    return -1;
-  }
-
-  crypt_path_t *hop = create_rend_cpath_legacy(circ, rend_cell_body);
-  if (!hop) {
-    log_warn(LD_GENERAL, "Couldn't get v2 cpath.");
-    return -1;
-  }
-
-  finalize_rend_circuit(circ, hop, 0);
-
-  return 0;
-}
-
 /** Given the introduction circuit intro_circ, the rendezvous circuit
  * rend_circ, a descriptor intro point object ip and the service's
  * subcredential, send an INTRODUCE1 cell on intro_circ.
@@ -1381,31 +1271,20 @@ hs_circ_is_rend_sent_in_intro1(const origin_circuit_t *circ)
    * confirmed rendezsvous circuit but without an introduction ACK. */
   tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_REND_READY);
 
-  /* The v2 and v3 circuit are handled differently:
-   *
-   * v2: A circ's pending_final_cpath field is non-NULL iff it is a rend circ
-   * and we have tried to send an INTRODUCE1 cell specifying it. Thus, if the
-   * pending_final_cpath field *is* NULL, then we want to not spare it.
-   *
-   * v3: When the INTRODUCE1 cell is sent, the introduction encryption public
+  /* When the INTRODUCE1 cell is sent, the introduction encryption public
    * key is copied in the rendezvous circuit hs identifier. If it is a valid
    * key, we know that this circuit is waiting the ACK on the introduction
    * circuit. We want to _not_ spare the circuit if the key was never set. */
 
-  if (circ->rend_data) {
-    /* v2. */
-    if (circ->build_state && circ->build_state->pending_final_cpath != NULL) {
-      return true;
-    }
-  } else if (circ->hs_ident) {
+  if (circ->hs_ident) {
     /* v3. */
     if (curve25519_public_key_is_ok(&circ->hs_ident->intro_enc_pk)) {
       return true;
     }
   } else {
-    /* A circuit with an HS purpose without an hs_ident or rend_data in theory
-     * can not happen. In case, scream loudly and return false to the caller
-     * that the rendezvous was not sent in the INTRO1 cell. */
+    /* A circuit with an HS purpose without an hs_ident in theory can not
+     * happen. In case, scream loudly and return false to the caller that the
+     * rendezvous was not sent in the INTRO1 cell. */
     tor_assert_nonfatal_unreached();
   }
 
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 28bbe72459..f1c17f4f90 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -1950,11 +1950,6 @@ hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn)
 {
   tor_assert(connection_edge_is_rendezvous_stream(conn));
 
-  if (BUG(conn->rend_data && conn->hs_ident)) {
-    log_warn(LD_BUG, "Stream had both rend_data and hs_ident..."
-             "Prioritizing hs_ident");
-  }
-
   if (conn->hs_ident) { /* It's v3: pass it to the prop224 handler */
     note_connection_attempt_succeeded(conn->hs_ident);
     return;
@@ -2094,8 +2089,6 @@ hs_client_circuit_has_opened(origin_circuit_t *circ)
 {
   tor_assert(circ);
 
-  /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
-   * identifier hs_ident. Can't be both. */
   switch (TO_CIRCUIT(circ)->purpose) {
   case CIRCUIT_PURPOSE_C_INTRODUCING:
     if (circ->hs_ident) {
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index 55cc4d5518..ae4a9cd970 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -33,7 +33,6 @@
 #include "feature/nodelist/nodelist.h"
 #include "feature/nodelist/routerset.h"
 #include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
 #include "feature/relay/routermode.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/crypt_ops/crypto_util.h"
@@ -337,258 +336,6 @@ hs_get_start_time_of_next_time_period(time_t now)
   return (time_t)(start_of_next_tp_in_mins * 60 + time_period_rotation_offset);
 }
 
-/** Create a new rend_data_t for a specific given <b>version</b>.
- * Return a pointer to the newly allocated data structure. */
-static rend_data_t *
-rend_data_alloc(uint32_t version)
-{
-  rend_data_t *rend_data = NULL;
-
-  switch (version) {
-  case HS_VERSION_TWO:
-  {
-    rend_data_v2_t *v2 = tor_malloc_zero(sizeof(*v2));
-    v2->base_.version = HS_VERSION_TWO;
-    v2->base_.hsdirs_fp = smartlist_new();
-    rend_data = &v2->base_;
-    break;
-  }
-  default:
-    tor_assert(0);
-    break;
-  }
-
-  return rend_data;
-}
-
-/** Free all storage associated with <b>data</b> */
-void
-rend_data_free_(rend_data_t *data)
-{
-  if (!data) {
-    return;
-  }
-  /* By using our allocation function, this should always be set. */
-  tor_assert(data->hsdirs_fp);
-  /* Cleanup the HSDir identity digest. */
-  SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d));
-  smartlist_free(data->hsdirs_fp);
-  /* Depending on the version, cleanup. */
-  switch (data->version) {
-  case HS_VERSION_TWO:
-  {
-    rend_data_v2_t *v2_data = TO_REND_DATA_V2(data);
-    tor_free(v2_data);
-    break;
-  }
-  default:
-    tor_assert(0);
-  }
-}
-
-/** Allocate and return a deep copy of <b>data</b>. */
-rend_data_t *
-rend_data_dup(const rend_data_t *data)
-{
-  rend_data_t *data_dup = NULL;
-  smartlist_t *hsdirs_fp = smartlist_new();
-
-  tor_assert(data);
-  tor_assert(data->hsdirs_fp);
-
-  SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp,
-                    smartlist_add(hsdirs_fp, tor_memdup(fp, DIGEST_LEN)));
-
-  switch (data->version) {
-  case HS_VERSION_TWO:
-  {
-    rend_data_v2_t *v2_data = tor_memdup(TO_REND_DATA_V2(data),
-                                         sizeof(*v2_data));
-    data_dup = &v2_data->base_;
-    data_dup->hsdirs_fp = hsdirs_fp;
-    break;
-  }
-  default:
-    tor_assert(0);
-    break;
-  }
-
-  return data_dup;
-}
-
-/** Compute the descriptor ID for each HS descriptor replica and save them. A
- * valid onion address must be present in the <b>rend_data</b>.
- *
- * Return 0 on success else -1. */
-static int
-compute_desc_id(rend_data_t *rend_data)
-{
-  int ret = 0;
-  unsigned replica;
-  time_t now = time(NULL);
-
-  tor_assert(rend_data);
-
-  switch (rend_data->version) {
-  case HS_VERSION_TWO:
-  {
-    rend_data_v2_t *v2_data = TO_REND_DATA_V2(rend_data);
-    /* Compute descriptor ID for each replicas. */
-    for (replica = 0; replica < ARRAY_LENGTH(v2_data->descriptor_id);
-         replica++) {
-      ret = rend_compute_v2_desc_id(v2_data->descriptor_id[replica],
-                                    v2_data->onion_address,
-                                    v2_data->descriptor_cookie,
-                                    now, replica);
-      if (ret < 0) {
-        goto end;
-      }
-    }
-    break;
-  }
-  default:
-    tor_assert(0);
-  }
-
- end:
-  return ret;
-}
-
-/** Allocate and initialize a rend_data_t object for a service using the
- * provided arguments. All arguments are optional (can be NULL), except from
- * <b>onion_address</b> which MUST be set. The <b>pk_digest</b> is the hash of
- * the service private key. The <b>cookie</b> is the rendezvous cookie and
- * <b>auth_type</b> is which authentiation this service is configured with.
- *
- * Return a valid rend_data_t pointer. This only returns a version 2 object of
- * rend_data_t. */
-rend_data_t *
-rend_data_service_create(const char *onion_address, const char *pk_digest,
-                         const uint8_t *cookie, rend_auth_type_t auth_type)
-{
-  /* Create a rend_data_t object for version 2. */
-  rend_data_t *rend_data = rend_data_alloc(HS_VERSION_TWO);
-  rend_data_v2_t *v2= TO_REND_DATA_V2(rend_data);
-
-  /* We need at least one else the call is wrong. */
-  tor_assert(onion_address != NULL);
-
-  if (pk_digest) {
-    memcpy(v2->rend_pk_digest, pk_digest, sizeof(v2->rend_pk_digest));
-  }
-  if (cookie) {
-    memcpy(rend_data->rend_cookie, cookie, sizeof(rend_data->rend_cookie));
-  }
-
-  strlcpy(v2->onion_address, onion_address, sizeof(v2->onion_address));
-  v2->auth_type = auth_type;
-
-  return rend_data;
-}
-
-/** Allocate and initialize a rend_data_t object for a client request using the
- * given arguments. Either an onion address or a descriptor ID is needed. Both
- * can be given but in this case only the onion address will be used to make
- * the descriptor fetch. The <b>cookie</b> is the rendezvous cookie and
- * <b>auth_type</b> is which authentiation the service is configured with.
- *
- * Return a valid rend_data_t pointer or NULL on error meaning the
- * descriptor IDs couldn't be computed from the given data. */
-rend_data_t *
-rend_data_client_create(const char *onion_address, const char *desc_id,
-                        const char *cookie, rend_auth_type_t auth_type)
-{
-  /* Create a rend_data_t object for version 2. */
-  rend_data_t *rend_data = rend_data_alloc(HS_VERSION_TWO);
-  rend_data_v2_t *v2= TO_REND_DATA_V2(rend_data);
-
-  /* We need at least one else the call is wrong. */
-  tor_assert(onion_address != NULL || desc_id != NULL);
-
-  if (cookie) {
-    memcpy(v2->descriptor_cookie, cookie, sizeof(v2->descriptor_cookie));
-  }
-  if (desc_id) {
-    memcpy(v2->desc_id_fetch, desc_id, sizeof(v2->desc_id_fetch));
-  }
-  if (onion_address) {
-    strlcpy(v2->onion_address, onion_address, sizeof(v2->onion_address));
-    if (compute_desc_id(rend_data) < 0) {
-      goto error;
-    }
-  }
-
-  v2->auth_type = auth_type;
-
-  return rend_data;
-
- error:
-  rend_data_free(rend_data);
-  return NULL;
-}
-
-/** Return the onion address from the rend data. Depending on the version,
- * the size of the address can vary but it's always NUL terminated. */
-const char *
-rend_data_get_address(const rend_data_t *rend_data)
-{
-  tor_assert(rend_data);
-
-  switch (rend_data->version) {
-  case HS_VERSION_TWO:
-    return TO_REND_DATA_V2(rend_data)->onion_address;
-  default:
-    /* We should always have a supported version. */
-    tor_assert_unreached();
-  }
-}
-
-/** Return the descriptor ID for a specific replica number from the rend
- * data. The returned data is a binary digest and depending on the version its
- * size can vary. The size of the descriptor ID is put in <b>len_out</b> if
- * non NULL. */
-const char *
-rend_data_get_desc_id(const rend_data_t *rend_data, uint8_t replica,
-                      size_t *len_out)
-{
-  tor_assert(rend_data);
-
-  switch (rend_data->version) {
-  case HS_VERSION_TWO:
-    tor_assert(replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS);
-    if (len_out) {
-      *len_out = DIGEST_LEN;
-    }
-    return TO_REND_DATA_V2(rend_data)->descriptor_id[replica];
-  default:
-    /* We should always have a supported version. */
-    tor_assert_unreached();
-  }
-}
-
-/** Return the public key digest using the given <b>rend_data</b>. The size of
- * the digest is put in <b>len_out</b> (if set) which can differ depending on
- * the version. */
-const uint8_t *
-rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out)
-{
-  tor_assert(rend_data);
-
-  switch (rend_data->version) {
-  case HS_VERSION_TWO:
-  {
-    const rend_data_v2_t *v2_data = TO_REND_DATA_V2(rend_data);
-    if (len_out) {
-      *len_out = sizeof(v2_data->rend_pk_digest);
-    }
-    return (const uint8_t *) v2_data->rend_pk_digest;
-  }
-  default:
-    /* We should always have a supported version. */
-    tor_assert_unreached();
-  }
-}
-
 /** Using the given time period number, compute the disaster shared random
  * value and put it in srv_out. It MUST be at least DIGEST256_LEN bytes. */
 static void
@@ -1981,9 +1728,7 @@ hs_dec_rdv_stream_counter(origin_circuit_t *circ)
 {
   tor_assert(circ);
 
-  if (circ->rend_data) {
-    circ->rend_data->nr_streams--;
-  } else if (circ->hs_ident) {
+  if (circ->hs_ident) {
     circ->hs_ident->num_rdv_streams--;
   } else {
     /* Should not be called if this circuit is not for hidden service. */
@@ -1998,9 +1743,7 @@ hs_inc_rdv_stream_counter(origin_circuit_t *circ)
 {
   tor_assert(circ);
 
-  if (circ->rend_data) {
-    circ->rend_data->nr_streams++;
-  } else if (circ->hs_ident) {
+  if (circ->hs_ident) {
     circ->hs_ident->num_rdv_streams++;
   } else {
     /* Should not be called if this circuit is not for hidden service. */
diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h
index 894b0e4844..5ddc6fd2d8 100644
--- a/src/feature/hs/hs_common.h
+++ b/src/feature/hs/hs_common.h
@@ -19,13 +19,10 @@ struct ed25519_keypair_t;
 /* Trunnel */
 #include "trunnel/ed25519_cert.h"
 
-/** Protocol version 2. Use this instead of hardcoding "2" in the code base,
- * this adds a clearer semantic to the value when used. */
-#define HS_VERSION_TWO 2
 /** Version 3 of the protocol (prop224). */
 #define HS_VERSION_THREE 3
 /** Earliest version we support. */
-#define HS_VERSION_MIN HS_VERSION_TWO
+#define HS_VERSION_MIN HS_VERSION_THREE
 /** Latest version we support. */
 #define HS_VERSION_MAX HS_VERSION_THREE
 
@@ -194,24 +191,6 @@ void hs_build_blinded_keypair(const struct ed25519_keypair_t *kp,
                               struct ed25519_keypair_t *kp_out);
 int hs_service_requires_uptime_circ(const smartlist_t *ports);
 
-void rend_data_free_(rend_data_t *data);
-#define rend_data_free(data) \
-  FREE_AND_NULL(rend_data_t, rend_data_free_, (data))
-rend_data_t *rend_data_dup(const rend_data_t *data);
-rend_data_t *rend_data_client_create(const char *onion_address,
-                                     const char *desc_id,
-                                     const char *cookie,
-                                     rend_auth_type_t auth_type);
-rend_data_t *rend_data_service_create(const char *onion_address,
-                                      const char *pk_digest,
-                                      const uint8_t *cookie,
-                                      rend_auth_type_t auth_type);
-const char *rend_data_get_address(const rend_data_t *rend_data);
-const char *rend_data_get_desc_id(const rend_data_t *rend_data,
-                                  uint8_t replica, size_t *len_out);
-const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data,
-                                       size_t *len_out);
-
 routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32);
 
 struct hs_subcredential_t;
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index b100acfcd4..e2e1756f21 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -28,7 +28,6 @@
 #include "feature/hs/hs_client.h"
 #include "feature/hs/hs_ob.h"
 #include "feature/hs/hs_service.h"
-#include "feature/rend/rendservice.h"
 #include "lib/encoding/confline.h"
 #include "lib/conf/confdecl.h"
 #include "lib/confmgt/confmgt.h"
@@ -101,23 +100,6 @@ stage_services(smartlist_t *service_list)
 {
   tor_assert(service_list);
 
-  /* This is v2 specific. Trigger service pruning which will make sure the
-   * just configured services end up in the main global list. It should only
-   * be done in non validation mode because v2 subsystem handles service
-   * object differently. */
-  rend_service_prune_list();
-
-  /* Cleanup v2 service from the list, we don't need those object anymore
-   * because we validated them all against the others and we want to stage
-   * only >= v3 service. And remember, v2 has a different object type which is
-   * shadow copied from an hs_service_t type. */
-  SMARTLIST_FOREACH_BEGIN(service_list, hs_service_t *, s) {
-    if (s->config.version == HS_VERSION_TWO) {
-      SMARTLIST_DEL_CURRENT(service_list, s);
-      hs_service_free(s);
-    }
-  } SMARTLIST_FOREACH_END(s);
-
   /* This is >= v3 specific. Using the newly configured service list, stage
    * them into our global state. Every object ownership is lost after. */
   hs_service_stage_services(service_list);
@@ -145,8 +127,7 @@ service_is_duplicate_in_list(const smartlist_t *service_list,
   /* XXX: Validate if we have any service that has the given service dir path.
    * This has two problems:
    *
-   * a) It's O(n^2), but the same comment from the bottom of
-   *    rend_config_services() should apply.
+   * a) It's O(n^2)
    *
    * b) We only compare directory paths as strings, so we can't
    *    detect two distinct paths that specify the same directory
@@ -269,15 +250,6 @@ config_has_invalid_options(const config_line_t *line_,
     NULL /* End marker. */
   };
 
-  const char *opts_exclude_v2[] = {
-    "HiddenServiceExportCircuitID",
-    "HiddenServiceEnableIntroDoSDefense",
-    "HiddenServiceEnableIntroDoSRatePerSec",
-    "HiddenServiceEnableIntroDoSBurstPerSec",
-    "HiddenServiceOnionBalanceInstance",
-    NULL /* End marker. */
-  };
-
   /* Defining the size explicitly allows us to take advantage of the compiler
    * which warns us if we ever bump the max version but forget to grow this
    * array. The plus one is because we have a version 0 :). */
@@ -286,7 +258,7 @@ config_has_invalid_options(const config_line_t *line_,
   } exclude_lists[HS_VERSION_MAX + 1] = {
     { NULL }, /* v0. */
     { NULL }, /* v1. */
-    { opts_exclude_v2 }, /* v2 */
+    { NULL }, /* v2. */
     { opts_exclude_v3 }, /* v3. */
   };
 
@@ -310,16 +282,6 @@ config_has_invalid_options(const config_line_t *line_,
                             "version %" PRIu32 " of service in %s",
                  opt, service->config.version,
                  service->config.directory_path);
-
-        if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
-          /* Special case this v2 option so that we can offer alternatives.
-           * If more such special cases appear, it would be good to
-           * generalize the exception mechanism here. */
-          log_warn(LD_CONFIG, "For v3 onion service client authorization, "
-                   "please read the 'CLIENT AUTHORIZATION' section in the "
-                   "manual.");
-        }
-
         ret = 1;
         /* Continue the loop so we can find all possible options. */
         continue;
@@ -521,7 +483,7 @@ config_generic_service(const hs_opts_t *hs_opts,
 
   /* Check if we are configured in non anonymous mode meaning every service
    * becomes a single onion service. */
-  if (rend_service_non_anonymous_mode_enabled(options)) {
+  if (hs_service_non_anonymous_mode_enabled(options)) {
     config->is_single_onion = 1;
   }
 
@@ -594,8 +556,7 @@ config_service(config_line_t *line, const or_options_t *options,
     service->config.version = config_learn_service_version(service);
   }
 
-  /* We make sure that this set of options for a service are valid that is for
-   * instance an option only for v2 is not used for v3. */
+  /* We make sure that this set of options for a service are valid. */
   if (config_has_invalid_options(line->next, service)) {
     goto err;
   }
@@ -604,9 +565,6 @@ config_service(config_line_t *line, const or_options_t *options,
    * start just after the service directory line so once we hit another
    * directory line, the function knows that it has to stop parsing. */
   switch (service->config.version) {
-  case HS_VERSION_TWO:
-    ret = rend_config_service(hs_opts, options, &service->config);
-    break;
   case HS_VERSION_THREE:
     ret = config_service_v3(hs_opts, &service->config);
     break;
@@ -687,11 +645,6 @@ hs_config_service_all(const or_options_t *options, int validate_only)
      * services. We don't need those objects anymore. */
     SMARTLIST_FOREACH(new_service_list, hs_service_t *, s,
                       hs_service_free(s));
-    /* For the v2 subsystem, the configuration function adds the service
-     * object to the staging list and it is transferred in the main list
-     * through the prunning process. In validation mode, we thus have to purge
-     * the staging list so it's not kept in memory as valid service. */
-    rend_service_free_staging_list();
   }
 
   /* Success. Note that the service list has no ownership of its content. */
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 0656224e48..0faa91f871 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -64,7 +64,6 @@
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/crypt_ops/crypto_util.h"
 #include "feature/dirparse/parsecommon.h"
-#include "feature/rend/rendcache.h"
 #include "feature/hs/hs_cache.h"
 #include "feature/hs/hs_config.h"
 #include "feature/nodelist/torcert.h" /* tor_cert_encode_ed22519() */
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index b33013ba1f..79734a67d5 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -29,7 +29,6 @@
 #include "feature/nodelist/nickname.h"
 #include "feature/nodelist/node_select.h"
 #include "feature/nodelist/nodelist.h"
-#include "feature/rend/rendservice.h"
 #include "lib/crypt_ops/crypto_ope.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/crypt_ops/crypto_util.h"
@@ -2666,8 +2665,6 @@ run_housekeeping_event(time_t now)
 static void
 run_build_descriptor_event(time_t now)
 {
-  /* For v2 services, this step happens in the upload event. */
-
   /* Run v3+ events. */
   /* We start by rotating the descriptors only if needed. */
   rotate_all_descriptors(now);
@@ -2840,11 +2837,6 @@ run_build_circuit_event(time_t now)
     return;
   }
 
-  /* Run v2 check. */
-  if (rend_num_services() > 0) {
-    rend_consider_services_intro_points(now);
-  }
-
   /* Run v3+ check. */
   FOR_EACH_SERVICE_BEGIN(service) {
     /* For introduction circuit, we need to make sure we don't stress too much
@@ -3280,13 +3272,6 @@ refresh_service_descriptor(const hs_service_t *service,
 STATIC void
 run_upload_descriptor_event(time_t now)
 {
-  /* v2 services use the same function for descriptor creation and upload so
-   * we do everything here because the intro circuits were checked before. */
-  if (rend_num_services() > 0) {
-    rend_consider_services_upload(now);
-    rend_consider_descriptor_republication();
-  }
-
   /* Run v3+ check. */
   FOR_EACH_SERVICE_BEGIN(service) {
     FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
@@ -3615,6 +3600,54 @@ service_encode_descriptor(const hs_service_t *service,
 /* Public API */
 /* ========== */
 
+/* Are HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode consistent?
+ */
+static int
+hs_service_non_anonymous_mode_consistent(const or_options_t *options)
+{
+  /* !! is used to make these options boolean */
+  return (!! options->HiddenServiceSingleHopMode ==
+          !! options->HiddenServiceNonAnonymousMode);
+}
+
+/* Do the options allow onion services to make direct (non-anonymous)
+ * connections to introduction or rendezvous points?
+ * Must only be called after options_validate_single_onion() has successfully
+ * checked onion service option consistency.
+ * Returns true if tor is in HiddenServiceSingleHopMode. */
+int
+hs_service_allow_non_anonymous_connection(const or_options_t *options)
+{
+  tor_assert(hs_service_non_anonymous_mode_consistent(options));
+  return options->HiddenServiceSingleHopMode ? 1 : 0;
+}
+
+/* Do the options allow us to reveal the exact startup time of the onion
+ * service?
+ * Single Onion Services prioritise availability over hiding their
+ * startup time, as their IP address is publicly discoverable anyway.
+ * Must only be called after options_validate_single_onion() has successfully
+ * checked onion service option consistency.
+ * Returns true if tor is in non-anonymous hidden service mode. */
+int
+hs_service_reveal_startup_time(const or_options_t *options)
+{
+  tor_assert(hs_service_non_anonymous_mode_consistent(options));
+  return hs_service_non_anonymous_mode_enabled(options);
+}
+
+/* Is non-anonymous mode enabled using the HiddenServiceNonAnonymousMode
+ * config option?
+ * Must only be called after options_validate_single_onion() has successfully
+ * checked onion service option consistency.
+ */
+int
+hs_service_non_anonymous_mode_enabled(const or_options_t *options)
+{
+  tor_assert(hs_service_non_anonymous_mode_consistent(options));
+  return options->HiddenServiceNonAnonymousMode ? 1 : 0;
+}
+
 /** Called when a circuit was just cleaned up. This is done right before the
  * circuit is marked for close. */
 void
@@ -3641,7 +3674,7 @@ hs_service_circuit_cleanup_on_close(const circuit_t *circ)
   }
 }
 
-/** This is called every time the service map (v2 or v3) changes that is if an
+/** This is called every time the service map changes that is if an
  * element is added or removed. */
 void
 hs_service_map_has_changed(void)
@@ -3992,9 +4025,6 @@ hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
   tor_assert(file_list);
   tor_assert(dir_list);
 
-  /* Add files and dirs for legacy services. */
-  rend_services_add_filenames_to_lists(file_list, dir_list);
-
   /* Add files and dirs for v3+. */
   FOR_EACH_SERVICE_BEGIN(service) {
     /* Skip ephemeral service, they don't touch the disk. */
@@ -4046,9 +4076,6 @@ hs_service_receive_introduce2(origin_circuit_t *circ, const uint8_t *payload,
   if (circ->hs_ident) {
     ret = service_handle_introduce2(circ, payload, payload_len);
     hs_stats_note_introduce2_cell(1);
-  } else {
-    ret = rend_service_receive_introduction(circ, payload, payload_len);
-    hs_stats_note_introduce2_cell(0);
   }
 
  done:
@@ -4075,12 +4102,8 @@ hs_service_receive_intro_established(origin_circuit_t *circ,
     goto err;
   }
 
-  /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
-   * identifier hs_ident. Can't be both. */
   if (circ->hs_ident) {
     ret = service_handle_intro_established(circ, payload, payload_len);
-  } else {
-    ret = rend_service_intro_established(circ, payload, payload_len);
   }
 
   if (ret < 0) {
@@ -4099,21 +4122,15 @@ hs_service_circuit_has_opened(origin_circuit_t *circ)
 {
   tor_assert(circ);
 
-  /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
-   * identifier hs_ident. Can't be both. */
   switch (TO_CIRCUIT(circ)->purpose) {
   case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
     if (circ->hs_ident) {
       service_intro_circ_has_opened(circ);
-    } else {
-      rend_service_intro_has_opened(circ);
     }
     break;
   case CIRCUIT_PURPOSE_S_CONNECT_REND:
     if (circ->hs_ident) {
       service_rendezvous_circ_has_opened(circ);
-    } else {
-      rend_service_rendezvous_has_opened(circ);
     }
     break;
   default:
@@ -4141,11 +4158,6 @@ hs_service_get_version_from_key(const hs_service_t *service)
     version = HS_VERSION_THREE;
     goto end;
   }
-  /* Version 2 check. */
-  if (rend_service_key_on_disk(directory_path)) {
-    version = HS_VERSION_TWO;
-    goto end;
-  }
 
  end:
   return version;
@@ -4156,13 +4168,6 @@ hs_service_get_version_from_key(const hs_service_t *service)
 int
 hs_service_load_all_keys(void)
 {
-  /* Load v2 service keys if we have v2. */
-  if (rend_num_services() != 0) {
-    if (rend_service_load_all_keys(NULL) < 0) {
-      goto err;
-    }
-  }
-
   /* Load or/and generate them for v3+. */
   SMARTLIST_FOREACH_BEGIN(hs_service_staging_list, hs_service_t *, service) {
     /* Ignore ephemeral service, they already have their keys set. */
@@ -4362,9 +4367,6 @@ hs_service_init(void)
   tor_assert(!hs_service_map);
   tor_assert(!hs_service_staging_list);
 
-  /* v2 specific. */
-  rend_service_init();
-
   hs_service_map = tor_malloc_zero(sizeof(struct hs_service_ht));
   HT_INIT(hs_service_ht, hs_service_map);
 
@@ -4375,7 +4377,6 @@ hs_service_init(void)
 void
 hs_service_free_all(void)
 {
-  rend_service_free_all();
   service_free_all();
   hs_config_free_all();
 }
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index 54d646d3e4..be01ce3cfb 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -398,6 +398,11 @@ service_authorized_client_free_(hs_service_authorized_client_t *client);
   FREE_AND_NULL(hs_service_authorized_client_t, \
                            service_authorized_client_free_, (c))
 
+/* Config options. */
+int hs_service_allow_non_anonymous_connection(const or_options_t *options);
+int hs_service_non_anonymous_mode_enabled(const or_options_t *options);
+int hs_service_reveal_startup_time(const or_options_t *options);
+
 #ifdef HS_SERVICE_PRIVATE
 
 #ifdef TOR_UNIT_TESTS
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index 7387f0d1d3..f807a34449 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -64,7 +64,6 @@
 #include "feature/nodelist/routerlist.h"
 #include "feature/nodelist/routerset.h"
 #include "feature/nodelist/torcert.h"
-#include "feature/rend/rendservice.h"
 #include "lib/encoding/binascii.h"
 #include "lib/err/backtrace.h"
 #include "lib/geoip/geoip.h"
@@ -2471,7 +2470,6 @@ void
 router_dir_info_changed(void)
 {
   need_to_update_have_min_dir_info = 1;
-  rend_hsdir_routers_changed();
   hs_service_dir_info_changed();
   hs_client_dir_info_changed();
 }
diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c
index 137c478fef..1b438b0330 100644
--- a/src/feature/relay/selftest.c
+++ b/src/feature/relay/selftest.c
@@ -526,8 +526,8 @@ router_perform_bandwidth_test(int num_circs, time_t now)
   origin_circuit_t *circ = NULL;
 
   log_notice(LD_OR,"Performing bandwidth self-test...done.");
-  while ((circ = circuit_get_next_by_pk_and_purpose(circ, NULL,
-                                              CIRCUIT_PURPOSE_TESTING))) {
+  while ((circ = circuit_get_next_by_purpose(circ,
+                                             CIRCUIT_PURPOSE_TESTING))) {
     /* dump cells_per_circuit drop cells onto this circ */
     int i = cells_per_circuit;
     if (circ->base_.state != CIRCUIT_STATE_OPEN)
diff --git a/src/feature/rend/feature_rend.md b/src/feature/rend/feature_rend.md
deleted file mode 100644
index bfd8ae3dbc..0000000000
--- a/src/feature/rend/feature_rend.md
+++ /dev/null
@@ -1,7 +0,0 @@
- at dir /feature/rend
- at brief feature/rend: version 2 (old) hidden services
-
-This directory implements the v2 onion service protocol,
-as specified in
-[rend-spec-v2.txt](https://gitweb.torproject.org/torspec.git/tree/rend-spec-v2.txt).
-
diff --git a/src/feature/rend/include.am b/src/feature/rend/include.am
index 8ad85bd7b1..d338869b5b 100644
--- a/src/feature/rend/include.am
+++ b/src/feature/rend/include.am
@@ -1,20 +1,10 @@
 
 # ADD_C_FILE: INSERT SOURCES HERE.
 LIBTOR_APP_A_SOURCES += 			\
-	src/feature/rend/rendcache.c		\
 	src/feature/rend/rendcommon.c		\
-	src/feature/rend/rendmid.c		\
-	src/feature/rend/rendparse.c		\
-	src/feature/rend/rendservice.c
+	src/feature/rend/rendmid.c
 
 # ADD_C_FILE: INSERT HEADERS HERE.
 noinst_HEADERS +=					\
-	src/feature/rend/rend_authorized_client_st.h	\
-	src/feature/rend/rend_encoded_v2_service_descriptor_st.h	\
-	src/feature/rend/rend_intro_point_st.h		\
-	src/feature/rend/rend_service_descriptor_st.h	\
-	src/feature/rend/rendcache.h			\
 	src/feature/rend/rendcommon.h			\
-	src/feature/rend/rendmid.h			\
-	src/feature/rend/rendparse.h			\
-	src/feature/rend/rendservice.h
+	src/feature/rend/rendmid.h
diff --git a/src/feature/rend/rend_authorized_client_st.h b/src/feature/rend/rend_authorized_client_st.h
deleted file mode 100644
index c6a6676da9..0000000000
--- a/src/feature/rend/rend_authorized_client_st.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * @file rend_authorized_client_st.h
- * @brief Hidden-service authorized client structure.
- **/
-
-#ifndef REND_AUTHORIZED_CLIENT_ST_H
-#define REND_AUTHORIZED_CLIENT_ST_H
-
-/** Hidden-service side configuration of client authorization. */
-struct rend_authorized_client_t {
-  char *client_name;
-  uint8_t descriptor_cookie[REND_DESC_COOKIE_LEN];
-  crypto_pk_t *client_key;
-};
-
-#endif /* !defined(REND_AUTHORIZED_CLIENT_ST_H) */
diff --git a/src/feature/rend/rend_encoded_v2_service_descriptor_st.h b/src/feature/rend/rend_encoded_v2_service_descriptor_st.h
deleted file mode 100644
index fea91b876a..0000000000
--- a/src/feature/rend/rend_encoded_v2_service_descriptor_st.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * @file rend_encoded_v2_service_descriptor_st.h
- * @brief Encoded v2 HS descriptor structure.
- **/
-
-#ifndef REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H
-#define REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H
-
-/** ASCII-encoded v2 hidden service descriptor. */
-struct rend_encoded_v2_service_descriptor_t {
-  char desc_id[DIGEST_LEN]; /**< Descriptor ID. */
-  char *desc_str; /**< Descriptor string. */
-};
-
-#endif /* !defined(REND_ENCODED_V2_SERVICE_DESCRIPTOR_ST_H) */
diff --git a/src/feature/rend/rend_intro_point_st.h b/src/feature/rend/rend_intro_point_st.h
deleted file mode 100644
index 4f0aa01523..0000000000
--- a/src/feature/rend/rend_intro_point_st.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * @file rend_intro_point_st.h
- * @brief v2 hidden service introduction point structure.
- **/
-
-#ifndef REND_INTRO_POINT_ST_H
-#define REND_INTRO_POINT_ST_H
-
-struct replaycache_t;
-struct crypto_pk_t;
-
-/** Introduction point information.  Used both in rend_service_t (on
- * the service side) and in rend_service_descriptor_t (on both the
- * client and service side). */
-struct rend_intro_point_t {
-  extend_info_t *extend_info; /**< Extend info for connecting to this
-                               * introduction point via a multi-hop path. */
-  struct crypto_pk_t *intro_key; /**< Introduction key that replaces the
-                                  * service key, if this descriptor is V2. */
-
-  /** (Client side only) Flag indicating that a timeout has occurred
-   * after sending an INTRODUCE cell to this intro point.  After a
-   * timeout, an intro point should not be tried again during the same
-   * hidden service connection attempt, but it may be tried again
-   * during a future connection attempt. */
-  unsigned int timed_out : 1;
-
-  /** (Client side only) The number of times we have failed to build a
-   * circuit to this intro point for some reason other than our
-   * circuit-build timeout.  See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */
-  unsigned int unreachable_count : 3;
-
-  /** (Service side only) Flag indicating that this intro point was
-   * included in the last HS descriptor we generated. */
-  unsigned int listed_in_last_desc : 1;
-
-  /** (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. */
-  struct 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) Maximum number of INTRODUCE2 cells that this IP
-   * will accept. This is a random value between
-   * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and
-   * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */
-  int max_introductions;
-
-  /** (Service side only) The time at which this intro point was first
-   * published, or -1 if this intro point has not yet been
-   * published. */
-  time_t time_published;
-
-  /** (Service side only) The time at which this intro point should
-   * (start to) expire, or -1 if we haven't decided when this intro
-   * point should expire. */
-  time_t time_to_expire;
-
-  /** (Service side only) The amount of circuit creation we've made to this
-   * intro point. This is incremented every time we do a circuit relaunch on
-   * this object which is triggered when the circuit dies but the node is
-   * still in the consensus. After MAX_INTRO_POINT_CIRCUIT_RETRIES, we give
-   * up on it. */
-  unsigned int circuit_retries;
-
-  /** (Service side only) Set if this intro point has an established circuit
-   * and unset if it doesn't. */
-  unsigned int circuit_established:1;
-};
-
-#endif /* !defined(REND_INTRO_POINT_ST_H) */
diff --git a/src/feature/rend/rend_service_descriptor_st.h b/src/feature/rend/rend_service_descriptor_st.h
deleted file mode 100644
index 80c8034f46..0000000000
--- a/src/feature/rend/rend_service_descriptor_st.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * @file rend_service_descriptor_st.h
- * @brief Parsed v2 HS descriptor structure.
- **/
-
-#ifndef REND_SERVICE_DESCRIPTOR_ST_H
-#define REND_SERVICE_DESCRIPTOR_ST_H
-
-#define REND_PROTOCOL_VERSION_BITMASK_WIDTH 16
-
-/** Information used to connect to a hidden service.  Used on both the
- * service side and the client side. */
-struct rend_service_descriptor_t {
-  crypto_pk_t *pk; /**< This service's public key. */
-  int version; /**< Version of the descriptor format: 0 or 2. */
-  time_t timestamp; /**< Time when the descriptor was generated. */
-  /** Bitmask: which introduce/rendezvous protocols are supported?
-   * (We allow bits '0', '1', '2' and '3' to be set.) */
-  unsigned protocols : REND_PROTOCOL_VERSION_BITMASK_WIDTH;
-  /** List of the service's introduction points.  Elements are removed if
-   * introduction attempts fail. */
-  smartlist_t *intro_nodes;
-  /** Has descriptor been uploaded to all hidden service directories? */
-  int all_uploads_performed;
-  /** List of hidden service directories to which an upload request for
-   * this descriptor could be sent. Smartlist exists only when at least one
-   * of the previous upload requests failed (otherwise it's not important
-   * to know which uploads succeeded and which not). */
-  smartlist_t *successful_uploads;
-};
-
-#endif /* !defined(REND_SERVICE_DESCRIPTOR_ST_H) */
diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c
deleted file mode 100644
index a471c8f463..0000000000
--- a/src/feature/rend/rendcache.c
+++ /dev/null
@@ -1,1029 +0,0 @@
-/* Copyright (c) 2015-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rendcache.c
- * \brief Hidden service descriptor cache.
- **/
-
-#define RENDCACHE_PRIVATE
-#include "feature/rend/rendcache.h"
-
-#include "app/config/config.h"
-#include "feature/stats/rephist.h"
-#include "feature/nodelist/routerlist.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendparse.h"
-
-#include "core/or/extend_info_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-
-#include "lib/ctime/di_ops.h"
-
-/** Map from service id (as generated by rend_get_service_id) to
- * rend_cache_entry_t. */
-STATIC strmap_t *rend_cache = NULL;
-
-/** Map from service id to rend_cache_entry_t; only for hidden services. */
-static strmap_t *rend_cache_local_service = NULL;
-
-/** Map from descriptor id to rend_cache_entry_t; only for hidden service
- * directories. */
-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
- * or discard a new descriptor we just fetched. Here is a description of the
- * cache behavior.
- *
- * Every time tor discards an IP (ex: receives a NACK), we add an entry to
- * this cache noting the identity digest of the IP and it's failure type for
- * the service ID. The reason we indexed this cache by service ID is to
- * differentiate errors that can occur only for a specific service like a
- * NACK for instance. It applies for one but maybe not for the others.
- *
- * Once a service descriptor is fetched and considered valid, each IP is
- * looked up in this cache and if present, it is discarded from the fetched
- * descriptor. At the end, all IP(s) in the cache, for a specific service
- * ID, that were NOT present in the descriptor are removed from this cache.
- * Which means that if at least one IP was not in this cache, thus usable,
- * it's considered a new descriptor so we keep it. Else, if all IPs were in
- * this cache, we discard the descriptor as it's considered unusable.
- *
- * Once a descriptor is removed from the rend cache or expires, the entry
- * in this cache is also removed for the service ID.
- *
- * This scheme allows us to not rely 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;
-
-/* DOCDOC */
-STATIC size_t rend_cache_total_allocation = 0;
-
-/** Initializes the service descriptor cache.
-*/
-void
-rend_cache_init(void)
-{
-  rend_cache = strmap_new();
-  rend_cache_v2_dir = digestmap_new();
-  rend_cache_local_service = strmap_new();
-  rend_cache_failure = strmap_new();
-}
-
-/** Return the approximate number of bytes needed to hold <b>e</b>. */
-STATIC size_t
-rend_cache_entry_allocation(const rend_cache_entry_t *e)
-{
-  if (!e)
-    return 0;
-
-  /* This doesn't count intro_nodes or key size */
-  return sizeof(*e) + e->len + sizeof(*e->parsed);
-}
-
-/* DOCDOC */
-size_t
-rend_cache_get_total_allocation(void)
-{
-  return rend_cache_total_allocation;
-}
-
-/** Decrement the total bytes attributed to the rendezvous cache by n. */
-void
-rend_cache_decrement_allocation(size_t n)
-{
-  static int have_underflowed = 0;
-
-  if (rend_cache_total_allocation >= n) {
-    rend_cache_total_allocation -= n;
-  } else {
-    rend_cache_total_allocation = 0;
-    if (! have_underflowed) {
-      have_underflowed = 1;
-      log_warn(LD_BUG, "Underflow in rend_cache_decrement_allocation");
-    }
-  }
-}
-
-/** Increase the total bytes attributed to the rendezvous cache by n. */
-void
-rend_cache_increment_allocation(size_t n)
-{
-  static int have_overflowed = 0;
-  if (rend_cache_total_allocation <= SIZE_MAX - n) {
-    rend_cache_total_allocation += n;
-  } else {
-    rend_cache_total_allocation = SIZE_MAX;
-    if (! have_overflowed) {
-      have_overflowed = 1;
-      log_warn(LD_BUG, "Overflow in rend_cache_increment_allocation");
-    }
-  }
-}
-
-/** Helper: free a rend cache failure intro object. */
-STATIC void
-rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t *entry)
-{
-  if (entry == NULL) {
-    return;
-  }
-  tor_free(entry);
-}
-
-static void
-rend_cache_failure_intro_entry_free_void(void *entry)
-{
-  rend_cache_failure_intro_entry_free_(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 *
-rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure)
-{
-  rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry));
-  entry->failure_type = failure;
-  entry->created_ts = time(NULL);
-  return entry;
-}
-
-/** Helper: free a rend cache failure object. */
-STATIC void
-rend_cache_failure_entry_free_(rend_cache_failure_t *entry)
-{
-  if (entry == NULL) {
-    return;
-  }
-
-  /* Free and remove every intro failure object. */
-  digestmap_free(entry->intro_failures,
-                 rend_cache_failure_intro_entry_free_void);
-
-  tor_free(entry);
-}
-
-/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(),
- * which requires a function pointer whose argument is void*). */
-STATIC void
-rend_cache_failure_entry_free_void(void *entry)
-{
-  rend_cache_failure_entry_free_(entry);
-}
-
-/** Allocate a rend cache failure object and return it. This function can
- * not fail. */
-STATIC rend_cache_failure_t *
-rend_cache_failure_entry_new(void)
-{
-  rend_cache_failure_t *entry = tor_malloc(sizeof(*entry));
-  entry->intro_failures = digestmap_new();
-  return entry;
-}
-
-/** Remove failure cache entry for the service ID in the given descriptor
- * <b>desc</b>. */
-STATIC void
-rend_cache_failure_remove(rend_service_descriptor_t *desc)
-{
-  char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
-  rend_cache_failure_t *entry;
-
-  if (desc == NULL) {
-    return;
-  }
-  if (rend_get_service_id(desc->pk, service_id) < 0) {
-    return;
-  }
-  entry = strmap_get_lc(rend_cache_failure, service_id);
-  if (entry != NULL) {
-    strmap_remove_lc(rend_cache_failure, service_id);
-    rend_cache_failure_entry_free(entry);
-  }
-}
-
-/** Helper: free storage held by a single service descriptor cache entry. */
-STATIC void
-rend_cache_entry_free_(rend_cache_entry_t *e)
-{
-  if (!e)
-    return;
-  rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
-  /* We are about to remove a descriptor from the cache so remove the entry
-   * in the failure cache. */
-  rend_cache_failure_remove(e->parsed);
-  rend_service_descriptor_free(e->parsed);
-  tor_free(e->desc);
-  tor_free(e);
-}
-
-/** Helper: deallocate a rend_cache_entry_t.  (Used with strmap_free(), which
- * requires a function pointer whose argument is void*). */
-static void
-rend_cache_entry_free_void(void *p)
-{
-  rend_cache_entry_free_(p);
-}
-
-/** Check if a failure cache entry exists for the given intro point. */
-bool
-rend_cache_intro_failure_exists(const char *service_id,
-                                const uint8_t *intro_identity)
-{
-  tor_assert(service_id);
-  tor_assert(intro_identity);
-
-  return cache_failure_intro_lookup(intro_identity, service_id, NULL);
-}
-
-/** Free all storage held by the service descriptor cache. */
-void
-rend_cache_free_all(void)
-{
-  strmap_free(rend_cache, rend_cache_entry_free_void);
-  digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_void);
-  strmap_free(rend_cache_local_service, rend_cache_entry_free_void);
-  strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void);
-  rend_cache = NULL;
-  rend_cache_v2_dir = NULL;
-  rend_cache_local_service = NULL;
-  rend_cache_failure = NULL;
-  rend_cache_total_allocation = 0;
-}
-
-/** Remove all entries that re REND_CACHE_FAILURE_MAX_AGE old. This is
- * called every second.
- *
- * We have to clean these regularly else if for whatever reasons an hidden
- * service goes offline and a client tries to connect to it during that
- * time, a failure entry is created and the client will be unable to connect
- * for a while even though the service has return online.  */
-void
-rend_cache_failure_clean(time_t now)
-{
-  time_t cutoff = now - REND_CACHE_FAILURE_MAX_AGE;
-  STRMAP_FOREACH_MODIFY(rend_cache_failure, key,
-                        rend_cache_failure_t *, ent) {
-    /* Free and remove every intro failure object that match the cutoff. */
-    DIGESTMAP_FOREACH_MODIFY(ent->intro_failures, ip_key,
-                             rend_cache_failure_intro_t *, ip_ent) {
-      if (ip_ent->created_ts < cutoff) {
-        rend_cache_failure_intro_entry_free(ip_ent);
-        MAP_DEL_CURRENT(ip_key);
-      }
-    } DIGESTMAP_FOREACH_END;
-    /* If the entry is now empty of intro point failures, remove it. */
-    if (digestmap_isempty(ent->intro_failures)) {
-      rend_cache_failure_entry_free(ent);
-      MAP_DEL_CURRENT(key);
-    }
-  } STRMAP_FOREACH_END;
-}
-
-/** Removes all old entries from the client or service descriptor cache.
-*/
-void
-rend_cache_clean(time_t now, rend_cache_type_t cache_type)
-{
-  strmap_iter_t *iter;
-  const char *key;
-  void *val;
-  rend_cache_entry_t *ent;
-  time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
-  strmap_t *cache = NULL;
-
-  if (cache_type == REND_CACHE_TYPE_CLIENT) {
-    cache = rend_cache;
-  } else if (cache_type == REND_CACHE_TYPE_SERVICE)  {
-    cache = rend_cache_local_service;
-  }
-  tor_assert(cache);
-
-  for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) {
-    strmap_iter_get(iter, &key, &val);
-    ent = (rend_cache_entry_t*)val;
-    if (ent->parsed->timestamp < cutoff) {
-      iter = strmap_iter_next_rmv(cache, iter);
-      rend_cache_entry_free(ent);
-    } else {
-      iter = strmap_iter_next(cache, iter);
-    }
-  }
-}
-
-/** Remove ALL entries from the rendezvous service descriptor cache.
-*/
-void
-rend_cache_purge(void)
-{
-  if (rend_cache) {
-    log_info(LD_REND, "Purging HS v2 descriptor cache");
-    strmap_free(rend_cache, rend_cache_entry_free_void);
-  }
-  rend_cache = strmap_new();
-}
-
-/** Remove ALL entries from the failure cache. This is also called when a
- * NEWNYM signal is received. */
-void
-rend_cache_failure_purge(void)
-{
-  if (rend_cache_failure) {
-    log_info(LD_REND, "Purging HS v2 failure cache");
-    strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void);
-  }
-  rend_cache_failure = strmap_new();
-}
-
-/** Lookup the rend failure cache using a relay identity digest in
- * <b>identity</b> which has DIGEST_LEN bytes and service ID <b>service_id</b>
- * which is a null-terminated string. If @a intro_entry is provided, then it
- * is set to the entry on success, and to NULL on failure.
- * Return 1 iff found else 0. */
-STATIC int
-cache_failure_intro_lookup(const uint8_t *identity, const char *service_id,
-                           rend_cache_failure_intro_t **intro_entry)
-{
-  rend_cache_failure_t *elem;
-  rend_cache_failure_intro_t *intro_elem;
-
-  tor_assert(rend_cache_failure);
-
-  if (intro_entry) {
-    *intro_entry = NULL;
-  }
-
-  /* Lookup descriptor and return it. */
-  elem = strmap_get_lc(rend_cache_failure, service_id);
-  if (elem == NULL) {
-    goto not_found;
-  }
-  intro_elem = digestmap_get(elem->intro_failures, (char *) identity);
-  if (intro_elem == NULL) {
-    goto not_found;
-  }
-  if (intro_entry) {
-    *intro_entry = intro_elem;
-  }
-  return 1;
- not_found:
-  return 0;
-}
-
-/** Allocate a new cache failure intro object and copy the content from
- * <b>entry</b> to this newly allocated object. Return it. */
-static rend_cache_failure_intro_t *
-cache_failure_intro_dup(const rend_cache_failure_intro_t *entry)
-{
-  rend_cache_failure_intro_t *ent_dup =
-    rend_cache_failure_intro_entry_new(entry->failure_type);
-  ent_dup->created_ts = entry->created_ts;
-  return ent_dup;
-}
-
-/** 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
-cache_failure_intro_add(const uint8_t *identity, const char *service_id,
-                        rend_intro_point_failure_t failure)
-{
-  rend_cache_failure_t *fail_entry;
-  rend_cache_failure_intro_t *entry, *old_entry;
-
-  /* Make sure we have a failure object for this service ID and if not,
-   * create it with this new intro failure entry. */
-  fail_entry = strmap_get_lc(rend_cache_failure, service_id);
-  if (fail_entry == NULL) {
-    fail_entry = rend_cache_failure_entry_new();
-    /* Add failure entry to global rend failure cache. */
-    strmap_set_lc(rend_cache_failure, service_id, fail_entry);
-  }
-  entry = rend_cache_failure_intro_entry_new(failure);
-  old_entry = digestmap_set(fail_entry->intro_failures,
-                            (char *) identity, entry);
-  /* This _should_ be NULL, but in case it isn't, free it. */
-  rend_cache_failure_intro_entry_free(old_entry);
-}
-
-/** Using a parsed descriptor <b>desc</b>, check if the introduction points
- * are present in the failure cache and if so they are removed from the
- * 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
-validate_intro_point_failure(const rend_service_descriptor_t *desc,
-                             const char *service_id)
-{
-  rend_cache_failure_t *new_entry, *cur_entry;
-  /* New entry for the service ID that will be replacing the one in the
-   * failure cache since we have a new descriptor. In the case where all
-   * intro points are removed, we are assured that the new entry is the same
-   * as the current one. */
-  new_entry = tor_malloc(sizeof(*new_entry));
-  new_entry->intro_failures = digestmap_new();
-
-  tor_assert(desc);
-
-  SMARTLIST_FOREACH_BEGIN(desc->intro_nodes, rend_intro_point_t *, intro) {
-    int found;
-    rend_cache_failure_intro_t *entry;
-    const uint8_t *identity =
-      (uint8_t *) intro->extend_info->identity_digest;
-
-    found = cache_failure_intro_lookup(identity, service_id, &entry);
-    if (found) {
-      /* Dup here since it will be freed at the end when removing the
-       * original entry in the cache. */
-      rend_cache_failure_intro_t *ent_dup = cache_failure_intro_dup(entry);
-      /* This intro point is in our cache, discard it from the descriptor
-       * because chances are that it's unusable. */
-      SMARTLIST_DEL_CURRENT(desc->intro_nodes, intro);
-      /* Keep it for our new entry. */
-      digestmap_set(new_entry->intro_failures, (char *) identity, ent_dup);
-      /* Only free it when we're done looking at it. */
-      rend_intro_point_free(intro);
-      continue;
-    }
-  } SMARTLIST_FOREACH_END(intro);
-
-  /* Swap the failure entry in the cache and free the current one. */
-  cur_entry = strmap_get_lc(rend_cache_failure, service_id);
-  if (cur_entry != NULL) {
-    rend_cache_failure_entry_free(cur_entry);
-  }
-  strmap_set_lc(rend_cache_failure, service_id, new_entry);
-}
-
-/** Note down an intro failure in the rend failure cache using the type of
- * failure in <b>failure</b> for the relay identity digest in
- * <b>identity</b> and service ID <b>service_id</b>. If an entry already
- * exists in the cache, the failure type is changed with <b>failure</b>. */
-void
-rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
-                              const uint8_t *identity,
-                              const char *service_id)
-{
-  int found;
-  rend_cache_failure_intro_t *entry;
-
-  found = cache_failure_intro_lookup(identity, service_id, &entry);
-  if (!found) {
-    cache_failure_intro_add(identity, service_id, failure);
-  } else {
-    /* Replace introduction point failure with this one. */
-    entry->failure_type = failure;
-  }
-}
-
-/** Remove all old v2 descriptors and those for which this hidden service
- * directory is not responsible for any more. The cutoff is the time limit for
- * which we want to keep the cache entry. In other words, any entry created
- * before will be removed. */
-size_t
-rend_cache_clean_v2_descs_as_dir(time_t cutoff)
-{
-  digestmap_iter_t *iter;
-  size_t bytes_removed = 0;
-
-  for (iter = digestmap_iter_init(rend_cache_v2_dir);
-       !digestmap_iter_done(iter); ) {
-    const char *key;
-    void *val;
-    rend_cache_entry_t *ent;
-    digestmap_iter_get(iter, &key, &val);
-    ent = val;
-    if (ent->parsed->timestamp < cutoff) {
-      char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-      base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN);
-      log_info(LD_REND, "Removing descriptor with ID '%s' from cache",
-               safe_str_client(key_base32));
-      bytes_removed += rend_cache_entry_allocation(ent);
-      iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
-      rend_cache_entry_free(ent);
-    } else {
-      iter = digestmap_iter_next(rend_cache_v2_dir, iter);
-    }
-  }
-
-  return bytes_removed;
-}
-
-/** Lookup in the client cache the given service ID <b>query</b> for
- * <b>version</b>.
- *
- * Return 0 if found and if <b>e</b> is non NULL, set it with the entry
- * found. Else, a negative value is returned and <b>e</b> is untouched.
- * -EINVAL means that <b>query</b> is not a valid service id.
- * -ENOENT means that no entry in the cache was found. */
-int
-rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
-{
-  int ret = 0;
-  char key[REND_SERVICE_ID_LEN_BASE32 + 2]; /* <version><query>\0 */
-  rend_cache_entry_t *entry = NULL;
-  static const int default_version = 2;
-
-  tor_assert(query);
-
-  /* This is possible if we are in the shutdown process and the cache was
-   * freed while some other subsystem might do a lookup to the cache for
-   * cleanup reasons such HS circuit cleanup for instance. */
-  if (!rend_cache) {
-    ret = -ENOENT;
-    goto end;
-  }
-
-  if (!rend_valid_v2_service_id(query)) {
-    ret = -EINVAL;
-    goto end;
-  }
-
-  switch (version) {
-    case 0:
-      log_warn(LD_REND, "Cache lookup of a v0 renddesc is deprecated.");
-      break;
-    case 2:
-      /* Default is version 2. */
-    default:
-      tor_snprintf(key, sizeof(key), "%d%s", default_version, query);
-      entry = strmap_get_lc(rend_cache, key);
-      break;
-  }
-  if (!entry) {
-    ret = -ENOENT;
-    goto end;
-  }
-  tor_assert(entry->parsed && entry->parsed->intro_nodes);
-
-  if (e) {
-    *e = entry;
-  }
-
- end:
-  return ret;
-}
-
-/*
- * Lookup the v2 service descriptor with the service ID <b>query</b> in the
- * local service descriptor cache. Return 0 if found and if <b>e</b> is
- * non NULL, set it with the entry found. Else, a negative value is returned
- * and <b>e</b> is untouched.
- * -EINVAL means that <b>query</b> is not a valid service id.
- * -ENOENT means that no entry in the cache was found. */
-int
-rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
-{
-  int ret = 0;
-  rend_cache_entry_t *entry = NULL;
-
-  tor_assert(rend_cache_local_service);
-  tor_assert(query);
-
-  if (!rend_valid_v2_service_id(query)) {
-    ret = -EINVAL;
-    goto end;
-  }
-
-  /* Lookup descriptor and return. */
-  entry = strmap_get_lc(rend_cache_local_service, query);
-  if (!entry) {
-    ret = -ENOENT;
-    goto end;
-  }
-
-  if (e) {
-    *e = entry;
-  }
-
- end:
-  return ret;
-}
-
-/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
- * copy the pointer to it to *<b>desc</b>.  Return 1 on success, 0 on
- * well-formed-but-not-found, and -1 on failure.
- */
-int
-rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
-{
-  rend_cache_entry_t *e;
-  char desc_id_digest[DIGEST_LEN];
-  tor_assert(rend_cache_v2_dir);
-  if (base32_decode(desc_id_digest, DIGEST_LEN,
-                    desc_id, REND_DESC_ID_V2_LEN_BASE32) != DIGEST_LEN) {
-    log_fn(LOG_PROTOCOL_WARN, LD_REND,
-           "Rejecting v2 rendezvous descriptor request -- descriptor ID "
-           "has wrong length or illegal characters: %s",
-           safe_str(desc_id));
-    return -1;
-  }
-  /* Lookup descriptor and return. */
-  e = digestmap_get(rend_cache_v2_dir, desc_id_digest);
-  if (e) {
-    *desc = e->desc;
-    e->last_served = approx_time();
-    return 1;
-  }
-  return 0;
-}
-
-/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the
- * local rend cache. Don't attempt to decrypt the included list of introduction
- * points (as we don't have a descriptor cookie for it).
- *
- * If we have a newer descriptor with the same ID, ignore this one.
- * If we have an older descriptor with the same ID, replace it.
- *
- * Return 0 on success, or -1 if we couldn't parse any of them.
- *
- * We should only call this function for public (e.g. non bridge) relays.
- */
-int
-rend_cache_store_v2_desc_as_dir(const char *desc)
-{
-  const or_options_t *options = get_options();
-  rend_service_descriptor_t *parsed;
-  char desc_id[DIGEST_LEN];
-  char *intro_content;
-  size_t intro_size;
-  size_t encoded_size;
-  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-  int number_parsed = 0, number_stored = 0;
-  const char *current_desc = desc;
-  const char *next_desc;
-  rend_cache_entry_t *e;
-  time_t now = time(NULL);
-  tor_assert(rend_cache_v2_dir);
-  tor_assert(desc);
-  while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
-                                          &intro_size, &encoded_size,
-                                          &next_desc, current_desc, 1) >= 0) {
-    number_parsed++;
-    /* We don't care about the introduction points. */
-    tor_free(intro_content);
-    /* For pretty log statements. */
-    base32_encode(desc_id_base32, sizeof(desc_id_base32),
-                  desc_id, DIGEST_LEN);
-    /* Is descriptor too old? */
-    if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
-      log_info(LD_REND, "Service descriptor with desc ID %s is too old.",
-               safe_str(desc_id_base32));
-      goto skip;
-    }
-    /* Is descriptor too far in the future? */
-    if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
-      log_info(LD_REND, "Service descriptor with desc ID %s is too far in the "
-               "future.",
-               safe_str(desc_id_base32));
-      goto skip;
-    }
-    /* Do we already have a newer descriptor? */
-    e = digestmap_get(rend_cache_v2_dir, desc_id);
-    if (e && e->parsed->timestamp > parsed->timestamp) {
-      log_info(LD_REND, "We already have a newer service descriptor with the "
-               "same desc ID %s and version.",
-               safe_str(desc_id_base32));
-      goto skip;
-    }
-    /* Do we already have this descriptor? */
-    if (e && !strcmp(desc, e->desc)) {
-      log_info(LD_REND, "We already have this service descriptor with desc "
-               "ID %s.", safe_str(desc_id_base32));
-      goto skip;
-    }
-    /* Store received descriptor. */
-    if (!e) {
-      e = tor_malloc_zero(sizeof(rend_cache_entry_t));
-      digestmap_set(rend_cache_v2_dir, desc_id, e);
-      /* Treat something just uploaded as having been served a little
-       * while ago, so that flooding with new descriptors doesn't help
-       * too much.
-       */
-      e->last_served = approx_time() - 3600;
-    } else {
-      rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
-      rend_service_descriptor_free(e->parsed);
-      tor_free(e->desc);
-    }
-    e->parsed = parsed;
-    e->desc = tor_strndup(current_desc, encoded_size);
-    e->len = encoded_size;
-    rend_cache_increment_allocation(rend_cache_entry_allocation(e));
-    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_hsdir_stored_maybe_new_v2_onion(e->parsed->pk);
-    }
-
-    number_stored++;
-    goto advance;
- skip:
-    rend_service_descriptor_free(parsed);
- advance:
-    /* advance to next descriptor, if available. */
-    current_desc = next_desc;
-    /* check if there is a next descriptor. */
-    if (!current_desc ||
-        strcmpstart(current_desc, "rendezvous-service-descriptor "))
-      break;
-  }
-  if (!number_parsed) {
-    log_info(LD_REND, "Could not parse any descriptor.");
-    return -1;
-  }
-  log_info(LD_REND, "Parsed %d and added %d descriptor%s.",
-           number_parsed, number_stored, number_stored != 1 ? "s" : "");
-  return 0;
-}
-
-/** Parse the v2 service descriptor in <b>desc</b> and store it to the
-* local service rend cache. Don't attempt to decrypt the included list of
-* introduction points.
-*
-* If we have a newer descriptor with the same ID, ignore this one.
-* If we have an older descriptor with the same ID, replace it.
-*
-* Return 0 on success, or -1 if we couldn't understand the descriptor.
-*/
-int
-rend_cache_store_v2_desc_as_service(const char *desc)
-{
-  rend_service_descriptor_t *parsed = NULL;
-  char desc_id[DIGEST_LEN];
-  char *intro_content = NULL;
-  size_t intro_size;
-  size_t encoded_size;
-  const char *next_desc;
-  char service_id[REND_SERVICE_ID_LEN_BASE32+1];
-  rend_cache_entry_t *e;
-  int retval = -1;
-  tor_assert(rend_cache_local_service);
-  tor_assert(desc);
-
-  /* Parse the descriptor. */
-  if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
-                                       &intro_size, &encoded_size,
-                                       &next_desc, desc, 0) < 0) {
-    log_warn(LD_REND, "Could not parse descriptor.");
-    goto err;
-  }
-  /* Compute service ID from public key. */
-  if (rend_get_service_id(parsed->pk, service_id)<0) {
-    log_warn(LD_REND, "Couldn't compute service ID.");
-    goto err;
-  }
-
-  /* Do we already have a newer descriptor? Allow new descriptors with a
-     rounded timestamp equal to or newer than the current descriptor */
-  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service,
-                                          service_id);
-  if (e && e->parsed->timestamp > parsed->timestamp) {
-    log_info(LD_REND, "We already have a newer service descriptor for "
-             "service ID %s.", safe_str_client(service_id));
-    goto okay;
-  }
-  /* We don't care about the introduction points. */
-  tor_free(intro_content);
-  if (!e) {
-    e = tor_malloc_zero(sizeof(rend_cache_entry_t));
-    strmap_set_lc(rend_cache_local_service, service_id, e);
-  } else {
-    rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
-    rend_service_descriptor_free(e->parsed);
-    tor_free(e->desc);
-  }
-  e->parsed = parsed;
-  e->desc = tor_malloc_zero(encoded_size + 1);
-  strlcpy(e->desc, desc, encoded_size + 1);
-  e->len = encoded_size;
-  rend_cache_increment_allocation(rend_cache_entry_allocation(e));
-  log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
-            safe_str_client(service_id), (int)encoded_size);
-  return 0;
-
- okay:
-  retval = 0;
-
- err:
-  rend_service_descriptor_free(parsed);
-  tor_free(intro_content);
-  return retval;
-}
-
-/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
- * of introduction points with <b>descriptor_cookie</b> (which may also be
- * <b>NULL</b> if decryption is not necessary), and store the descriptor to
- * the local cache under its version and service id.
- *
- * If we have a newer v2 descriptor with the same ID, ignore this one.
- * If we have an older descriptor with the same ID, replace it.
- * If the descriptor's service ID does not match
- * <b>rend_query</b>-\>onion_address, reject it.
- *
- * If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>,
- * reject it.
- *
- * Return 0 on success, or -1 if we rejected the descriptor.
- * If entry is not NULL, set it with the cache entry pointer of the descriptor.
- */
-int
-rend_cache_store_v2_desc_as_client(const char *desc,
-                                   const char *desc_id_base32,
-                                   const rend_data_t *rend_query,
-                                   rend_cache_entry_t **entry)
-{
-  /*XXXX this seems to have a bit of duplicate code with
-   * rend_cache_store_v2_desc_as_dir().  Fix that. */
-  /* Though having similar elements, both functions were separated on
-   * purpose:
-   * - dirs don't care about encoded/encrypted introduction points, clients
-   *   do.
-   * - dirs store descriptors in a separate cache by descriptor ID, whereas
-   *   clients store them by service ID; both caches are different data
-   *   structures and have different access methods.
-   * - dirs store a descriptor only if they are responsible for its ID,
-   *   clients do so in every way (because they have requested it before).
-   * - dirs can process multiple concatenated descriptors which is required
-   *   for replication, whereas clients only accept a single descriptor.
-   * Thus, combining both methods would result in a lot of if statements
-   * which probably would not improve, but worsen code readability. -KL */
-  rend_service_descriptor_t *parsed = NULL;
-  char desc_id[DIGEST_LEN];
-  char *intro_content = NULL;
-  size_t intro_size;
-  size_t encoded_size;
-  const char *next_desc;
-  time_t now = time(NULL);
-  char key[REND_SERVICE_ID_LEN_BASE32+2];
-  char service_id[REND_SERVICE_ID_LEN_BASE32+1];
-  char want_desc_id[DIGEST_LEN];
-  rend_cache_entry_t *e;
-  int retval = -1;
-  rend_data_v2_t *rend_data = TO_REND_DATA_V2(rend_query);
-
-  tor_assert(rend_cache);
-  tor_assert(desc);
-  tor_assert(desc_id_base32);
-  memset(want_desc_id, 0, sizeof(want_desc_id));
-  if (entry) {
-    *entry = NULL;
-  }
-  if (base32_decode(want_desc_id, sizeof(want_desc_id),
-                    desc_id_base32, strlen(desc_id_base32)) !=
-      sizeof(want_desc_id)) {
-    log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.",
-             escaped_safe_str_client(desc_id_base32));
-    goto err;
-  }
-  /* Parse the descriptor. */
-  if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
-                                       &intro_size, &encoded_size,
-                                       &next_desc, desc, 0) < 0) {
-    log_warn(LD_REND, "Could not parse descriptor.");
-    goto err;
-  }
-  /* Compute service ID from public key. */
-  if (rend_get_service_id(parsed->pk, service_id)<0) {
-    log_warn(LD_REND, "Couldn't compute service ID.");
-    goto err;
-  }
-  if (rend_data->onion_address[0] != '\0' &&
-      strcmp(rend_data->onion_address, service_id)) {
-    log_warn(LD_REND, "Received service descriptor for service ID %s; "
-             "expected descriptor for service ID %s.",
-             service_id, safe_str(rend_data->onion_address));
-    goto err;
-  }
-  if (tor_memneq(desc_id, want_desc_id, DIGEST_LEN)) {
-    log_warn(LD_REND, "Received service descriptor for %s with incorrect "
-             "descriptor ID.", service_id);
-    goto err;
-  }
-
-  /* Decode/decrypt introduction points. */
-  if (intro_content && intro_size > 0) {
-    int n_intro_points;
-    if (rend_data->auth_type != REND_NO_AUTH &&
-        !safe_mem_is_zero(rend_data->descriptor_cookie,
-                          sizeof(rend_data->descriptor_cookie))) {
-      char *ipos_decrypted = NULL;
-      size_t ipos_decrypted_size;
-      if (rend_decrypt_introduction_points(&ipos_decrypted,
-                                           &ipos_decrypted_size,
-                                           rend_data->descriptor_cookie,
-                                           intro_content,
-                                           intro_size) < 0) {
-        log_warn(LD_REND, "Failed to decrypt introduction points. We are "
-                 "probably unable to parse the encoded introduction points.");
-      } else {
-        /* Replace encrypted with decrypted introduction points. */
-        log_info(LD_REND, "Successfully decrypted introduction points.");
-        tor_free(intro_content);
-        intro_content = ipos_decrypted;
-        intro_size = ipos_decrypted_size;
-      }
-    }
-    n_intro_points = rend_parse_introduction_points(parsed, intro_content,
-                                                    intro_size);
-    if (n_intro_points <= 0) {
-      log_warn(LD_REND, "Failed to parse introduction points. Either the "
-               "service has published a corrupt descriptor or you have "
-               "provided invalid authorization data.");
-      goto err;
-    } else if (n_intro_points > MAX_INTRO_POINTS) {
-      log_warn(LD_REND, "Found too many introduction points on a hidden "
-               "service descriptor for %s. This is probably a (misguided) "
-               "attempt to improve reliability, but it could also be an "
-               "attempt to do a guard enumeration attack. Rejecting.",
-               safe_str_client(service_id));
-
-      goto err;
-    }
-  } else {
-    log_info(LD_REND, "Descriptor does not contain any introduction points.");
-    parsed->intro_nodes = smartlist_new();
-  }
-  /* We don't need the encoded/encrypted introduction points any longer. */
-  tor_free(intro_content);
-  /* Is descriptor too old? */
-  if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
-    log_warn(LD_REND, "Service descriptor with service ID %s is too old.",
-             safe_str_client(service_id));
-    goto err;
-  }
-  /* Is descriptor too far in the future? */
-  if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
-    log_warn(LD_REND, "Service descriptor with service ID %s is too far in "
-             "the future.", safe_str_client(service_id));
-    goto err;
-  }
-  /* Do we have the same exact copy already in our cache? */
-  tor_snprintf(key, sizeof(key), "2%s", service_id);
-  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
-  if (e && !strcmp(desc, e->desc)) {
-    log_info(LD_REND,"We already have this service descriptor %s.",
-             safe_str_client(service_id));
-    goto okay;
-  }
-  /* Verify that we are not replacing an older descriptor. It's important to
-   * avoid an evil HSDir serving old descriptor. We validate if the
-   * timestamp is greater than and not equal because it's a rounded down
-   * timestamp to the hour so if the descriptor changed in the same hour,
-   * the rend cache failure will tell us if we have a new descriptor. */
-  if (e && e->parsed->timestamp > parsed->timestamp) {
-    log_info(LD_REND, "We already have a new enough service descriptor for "
-             "service ID %s with the same desc ID and version.",
-             safe_str_client(service_id));
-    goto okay;
-  }
-  /* Lookup our failure cache for intro point that might be unusable. */
-  validate_intro_point_failure(parsed, service_id);
-  /* It's now possible that our intro point list is empty, which means that
-   * this descriptor is useless to us because intro points have all failed
-   * somehow before. Discard the descriptor. */
-  if (smartlist_len(parsed->intro_nodes) == 0) {
-    log_info(LD_REND, "Service descriptor with service ID %s has no "
-             "usable intro points. Discarding it.",
-             safe_str_client(service_id));
-    goto err;
-  }
-  /* Now either purge the current one and replace its content or create a
-   * new one and add it to the rend cache. */
-  if (!e) {
-    e = tor_malloc_zero(sizeof(rend_cache_entry_t));
-    strmap_set_lc(rend_cache, key, e);
-  } else {
-    rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
-    rend_cache_failure_remove(e->parsed);
-    rend_service_descriptor_free(e->parsed);
-    tor_free(e->desc);
-  }
-  e->parsed = parsed;
-  e->desc = tor_malloc_zero(encoded_size + 1);
-  strlcpy(e->desc, desc, encoded_size + 1);
-  e->len = encoded_size;
-  rend_cache_increment_allocation(rend_cache_entry_allocation(e));
-  log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
-            safe_str_client(service_id), (int)encoded_size);
-  if (entry) {
-    *entry = e;
-  }
-  return 0;
-
- okay:
-  if (entry) {
-    *entry = e;
-  }
-  retval = 0;
-
- err:
-  rend_service_descriptor_free(parsed);
-  tor_free(intro_content);
-  return retval;
-}
diff --git a/src/feature/rend/rendcache.h b/src/feature/rend/rendcache.h
deleted file mode 100644
index 45410610b4..0000000000
--- a/src/feature/rend/rendcache.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/* Copyright (c) 2015-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rendcache.h
- * \brief Header file for rendcache.c
- **/
-
-#ifndef TOR_RENDCACHE_H
-#define TOR_RENDCACHE_H
-
-#include "core/or/or.h"
-#include "feature/rend/rendcommon.h"
-
-/** How old do we let hidden service descriptors get before discarding
- * them as too old? */
-#define REND_CACHE_MAX_AGE (2*24*60*60)
-/** How wrong do we assume our clock may be when checking whether hidden
- * services are too old or too new? */
-#define REND_CACHE_MAX_SKEW (24*60*60)
-/** How old do we keep an intro point failure entry in the failure cache? */
-#define REND_CACHE_FAILURE_MAX_AGE (5*60)
-
-/* Do not allow more than this many introduction points in a hidden service
- * descriptor */
-#define MAX_INTRO_POINTS 10
-
-/** A cached rendezvous descriptor. */
-typedef struct rend_cache_entry_t {
-  size_t len; /**< Length of <b>desc</b> */
-  time_t last_served; /**< When did we last write this one to somebody?
-                       * (HSDir only) */
-  char *desc; /**< Service descriptor */
-  rend_service_descriptor_t *parsed; /**< Parsed value of 'desc' */
-} rend_cache_entry_t;
-
-/* Introduction point failure type. */
-typedef struct rend_cache_failure_intro_t {
-  /* When this intro point failure occurred thus we allocated this object and
-   * cache it. */
-  time_t created_ts;
-  rend_intro_point_failure_t failure_type;
-} rend_cache_failure_intro_t;
-
-/** Cache failure object indexed by service ID. */
-typedef struct rend_cache_failure_t {
-  /* Contains rend_cache_failure_intro_t indexed by identity digest. */
-  digestmap_t *intro_failures;
-} rend_cache_failure_t;
-
-typedef enum {
-  REND_CACHE_TYPE_CLIENT  = 1,
-  REND_CACHE_TYPE_SERVICE = 2,
-} rend_cache_type_t;
-
-/* Return maximum lifetime in seconds of a cache entry. */
-static inline time_t
-rend_cache_max_entry_lifetime(void)
-{
-  return REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW;
-}
-
-void rend_cache_init(void);
-void rend_cache_clean(time_t now, rend_cache_type_t cache_type);
-void rend_cache_failure_clean(time_t now);
-size_t rend_cache_clean_v2_descs_as_dir(time_t cutoff);
-void rend_cache_purge(void);
-void rend_cache_free_all(void);
-int rend_cache_lookup_entry(const char *query, int version,
-                            rend_cache_entry_t **entry_out);
-int rend_cache_lookup_v2_desc_as_service(const char *query,
-                                         rend_cache_entry_t **entry_out);
-int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
-
-int rend_cache_store_v2_desc_as_dir(const char *desc);
-int rend_cache_store_v2_desc_as_service(const char *desc);
-int rend_cache_store_v2_desc_as_client(const char *desc,
-                                       const char *desc_id_base32,
-                                       const rend_data_t *rend_query,
-                                       rend_cache_entry_t **entry);
-size_t rend_cache_get_total_allocation(void);
-
-bool rend_cache_intro_failure_exists(const char *service_id,
-                                     const uint8_t *intro_identity);
-void rend_cache_intro_failure_note(rend_intro_point_failure_t failure,
-                                   const uint8_t *identity,
-                                   const char *service_id);
-void rend_cache_failure_purge(void);
-void rend_cache_decrement_allocation(size_t n);
-void rend_cache_increment_allocation(size_t n);
-
-#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);
-#define rend_cache_entry_free(e) \
-  FREE_AND_NULL(rend_cache_entry_t, rend_cache_entry_free_, (e))
-STATIC void rend_cache_failure_intro_entry_free_(rend_cache_failure_intro_t
-                                                 *entry);
-#define rend_cache_failure_intro_entry_free(e)                          \
-  FREE_AND_NULL(rend_cache_failure_intro_t,                   \
-                          rend_cache_failure_intro_entry_free_, (e))
-STATIC void rend_cache_failure_entry_free_(rend_cache_failure_t *entry);
-#define rend_cache_failure_entry_free(e)                        \
-  FREE_AND_NULL(rend_cache_failure_t,                 \
-                          rend_cache_failure_entry_free_, (e))
-STATIC int cache_failure_intro_lookup(const uint8_t *identity,
-                                      const char *service_id,
-                                      rend_cache_failure_intro_t
-                                      **intro_entry);
-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);
-
-STATIC void rend_cache_failure_entry_free_void(void *entry);
-
-#ifdef TOR_UNIT_TESTS
-extern strmap_t *rend_cache;
-extern strmap_t *rend_cache_failure;
-extern digestmap_t *rend_cache_v2_dir;
-extern size_t rend_cache_total_allocation;
-#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* defined(RENDCACHE_PRIVATE) */
-
-#endif /* !defined(TOR_RENDCACHE_H) */
-
diff --git a/src/feature/rend/rendcommon.c b/src/feature/rend/rendcommon.c
index 275ee52968..c512e3e670 100644
--- a/src/feature/rend/rendcommon.c
+++ b/src/feature/rend/rendcommon.c
@@ -11,763 +11,22 @@
 #define RENDCOMMON_PRIVATE
 
 #include "core/or/or.h"
-#include "core/or/circuitbuild.h"
+
+#include "app/config/config.h"
+
 #include "core/or/circuitlist.h"
 #include "core/or/circuituse.h"
-#include "core/or/extendinfo.h"
-#include "app/config/config.h"
-#include "feature/control/control_events.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "lib/crypt_ops/crypto_util.h"
+
 #include "feature/hs/hs_client.h"
 #include "feature/hs/hs_common.h"
 #include "feature/hs/hs_intropoint.h"
-#include "feature/nodelist/networkstatus.h"
-#include "feature/rend/rendcache.h"
 #include "feature/rend/rendcommon.h"
 #include "feature/rend/rendmid.h"
-#include "feature/rend/rendparse.h"
-#include "feature/rend/rendservice.h"
-#include "feature/stats/rephist.h"
-#include "feature/hs_common/replaycache.h"
-#include "feature/relay/router.h"
-#include "feature/nodelist/routerlist.h"
-#include "feature/dirparse/signing.h"
 
+#include "core/or/circuit_st.h"
 #include "core/or/cpath_build_state_st.h"
 #include "core/or/crypt_path_st.h"
-#include "core/or/extend_info_st.h"
-#include "feature/nodelist/networkstatus_st.h"
 #include "core/or/origin_circuit_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-#include "feature/nodelist/routerstatus_st.h"
-
-/** Return 0 if one and two are the same service ids, else -1 or 1 */
-int
-rend_cmp_service_ids(const char *one, const char *two)
-{
-  return strcasecmp(one,two);
-}
-
-/** Free the storage held by the service descriptor <b>desc</b>.
- */
-void
-rend_service_descriptor_free_(rend_service_descriptor_t *desc)
-{
-  if (!desc)
-    return;
-  if (desc->pk)
-    crypto_pk_free(desc->pk);
-  if (desc->intro_nodes) {
-    SMARTLIST_FOREACH(desc->intro_nodes, rend_intro_point_t *, intro,
-      rend_intro_point_free(intro););
-    smartlist_free(desc->intro_nodes);
-  }
-  if (desc->successful_uploads) {
-    SMARTLIST_FOREACH(desc->successful_uploads, char *, c, tor_free(c););
-    smartlist_free(desc->successful_uploads);
-  }
-  tor_free(desc);
-}
-
-/** Length of the descriptor cookie that is used for versioned hidden
- * service descriptors. */
-#define REND_DESC_COOKIE_LEN 16
-
-/** Length of the replica number that is used to determine the secret ID
- * part of versioned hidden service descriptors. */
-#define REND_REPLICA_LEN 1
-
-/** Compute the descriptor ID for <b>service_id</b> of length
- * <b>REND_SERVICE_ID_LEN</b> and <b>secret_id_part</b> of length
- * <b>DIGEST_LEN</b>, and write it to <b>descriptor_id_out</b> of length
- * <b>DIGEST_LEN</b>. */
-void
-rend_get_descriptor_id_bytes(char *descriptor_id_out,
-                             const char *service_id,
-                             const char *secret_id_part)
-{
-  crypto_digest_t *digest = crypto_digest_new();
-  crypto_digest_add_bytes(digest, service_id, REND_SERVICE_ID_LEN);
-  crypto_digest_add_bytes(digest, secret_id_part, DIGEST_LEN);
-  crypto_digest_get_digest(digest, descriptor_id_out, DIGEST_LEN);
-  crypto_digest_free(digest);
-}
-
-/** Compute the secret ID part for time_period,
- * a <b>descriptor_cookie</b> of length
- * <b>REND_DESC_COOKIE_LEN</b> which may also be <b>NULL</b> if no
- * descriptor_cookie shall be used, and <b>replica</b>, and write it to
- * <b>secret_id_part</b> of length DIGEST_LEN. */
-static void
-get_secret_id_part_bytes(char *secret_id_part, uint32_t time_period,
-                         const char *descriptor_cookie, uint8_t replica)
-{
-  crypto_digest_t *digest = crypto_digest_new();
-  time_period = htonl(time_period);
-  crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t));
-  if (descriptor_cookie) {
-    crypto_digest_add_bytes(digest, descriptor_cookie,
-                            REND_DESC_COOKIE_LEN);
-  }
-  crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN);
-  crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN);
-  crypto_digest_free(digest);
-}
-
-/** Return the time period for time <b>now</b> plus a potentially
- * intended <b>deviation</b> of one or more periods, based on the first byte
- * of <b>service_id</b>. */
-static uint32_t
-get_time_period(time_t now, uint8_t deviation, const char *service_id)
-{
-  /* The time period is the number of REND_TIME_PERIOD_V2_DESC_VALIDITY
-   * intervals that have passed since the epoch, offset slightly so that
-   * each service's time periods start and end at a fraction of that
-   * period based on their first byte. */
-  return (uint32_t)
-    (now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256)
-    / REND_TIME_PERIOD_V2_DESC_VALIDITY + deviation;
-}
-
-/** Compute the time in seconds that a descriptor that is generated
- * <b>now</b> for <b>service_id</b> will be valid. */
-static uint32_t
-get_seconds_valid(time_t now, const char *service_id)
-{
-  uint32_t result = REND_TIME_PERIOD_V2_DESC_VALIDITY -
-    ((uint32_t)
-     (now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256)
-     % REND_TIME_PERIOD_V2_DESC_VALIDITY);
-  return result;
-}
-
-/** Compute the binary <b>desc_id_out</b> (DIGEST_LEN bytes long) for a given
- * base32-encoded <b>service_id</b> and optional unencoded
- * <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
- * at time <b>now</b> for replica number
- * <b>replica</b>. <b>desc_id</b> needs to have <b>DIGEST_LEN</b> bytes
- * free. Return 0 for success, -1 otherwise. */
-int
-rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
-                        const char *descriptor_cookie, time_t now,
-                        uint8_t replica)
-{
-  char service_id_binary[REND_SERVICE_ID_LEN];
-  char secret_id_part[DIGEST_LEN];
-  uint32_t time_period;
-  if (!service_id ||
-      strlen(service_id) != REND_SERVICE_ID_LEN_BASE32) {
-    log_warn(LD_REND, "Could not compute v2 descriptor ID: "
-                      "Illegal service ID: %s",
-             safe_str(service_id));
-    return -1;
-  }
-  if (replica >= REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) {
-    log_warn(LD_REND, "Could not compute v2 descriptor ID: "
-                      "Replica number out of range: %d", replica);
-    return -1;
-  }
-  /* Convert service ID to binary. */
-  if (base32_decode(service_id_binary, REND_SERVICE_ID_LEN,
-                    service_id, REND_SERVICE_ID_LEN_BASE32) !=
-      REND_SERVICE_ID_LEN) {
-    log_warn(LD_REND, "Could not compute v2 descriptor ID: "
-                      "Illegal characters or wrong length for service ID: %s",
-             safe_str_client(service_id));
-    return -1;
-  }
-  /* Calculate current time-period. */
-  time_period = get_time_period(now, 0, service_id_binary);
-  /* Calculate secret-id-part = h(time-period | desc-cookie | replica). */
-  get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
-                           replica);
-  /* Calculate descriptor ID: H(permanent-id | secret-id-part) */
-  rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part);
-  return 0;
-}
-
-/** Encode the introduction points in <b>desc</b> and write the result to a
- * newly allocated string pointed to by <b>encoded</b>. Return 0 for
- * success, -1 otherwise. */
-static int
-rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc)
-{
-  size_t unenc_len;
-  char *unenc = NULL;
-  size_t unenc_written = 0;
-  int i;
-  int r = -1;
-  /* Assemble unencrypted list of introduction points. */
-  unenc_len = smartlist_len(desc->intro_nodes) * 1000; /* too long, but ok. */
-  unenc = tor_malloc_zero(unenc_len);
-  for (i = 0; i < smartlist_len(desc->intro_nodes); i++) {
-    char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1];
-    char *onion_key = NULL;
-    size_t onion_key_len;
-    crypto_pk_t *intro_key;
-    char *service_key = NULL;
-    char *address = NULL;
-    size_t service_key_len;
-    int res;
-    rend_intro_point_t *intro = smartlist_get(desc->intro_nodes, i);
-    /* Obtain extend info with introduction point details. */
-    extend_info_t *info = intro->extend_info;
-    /* Encode introduction point ID. */
-    base32_encode(id_base32, sizeof(id_base32),
-                  info->identity_digest, DIGEST_LEN);
-    /* Encode onion key. */
-    if (crypto_pk_write_public_key_to_string(info->onion_key, &onion_key,
-                                             &onion_key_len) < 0) {
-      log_warn(LD_REND, "Could not write onion key.");
-      goto done;
-    }
-    /* Encode intro key. */
-    intro_key = intro->intro_key;
-    if (!intro_key ||
-      crypto_pk_write_public_key_to_string(intro_key, &service_key,
-                                           &service_key_len) < 0) {
-      log_warn(LD_REND, "Could not write intro key.");
-      tor_free(onion_key);
-      goto done;
-    }
-    /* Assemble everything for this introduction point. */
-    const tor_addr_port_t *orport = extend_info_get_orport(info, AF_INET);
-    IF_BUG_ONCE(!orport) {
-      /* There must be an IPv4 address for v2 hs. */
-      goto done;
-    }
-    address = tor_addr_to_str_dup(&orport->addr);
-    res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written,
-                         "introduction-point %s\n"
-                         "ip-address %s\n"
-                         "onion-port %d\n"
-                         "onion-key\n%s"
-                         "service-key\n%s",
-                       id_base32,
-                       address,
-                       orport->port,
-                       onion_key,
-                       service_key);
-    tor_free(address);
-    tor_free(onion_key);
-    tor_free(service_key);
-    if (res < 0) {
-      log_warn(LD_REND, "Not enough space for writing introduction point "
-                        "string.");
-      goto done;
-    }
-    /* Update total number of written bytes for unencrypted intro points. */
-    unenc_written += res;
-  }
-  /* Finalize unencrypted introduction points. */
-  if (unenc_len < unenc_written + 2) {
-    log_warn(LD_REND, "Not enough space for finalizing introduction point "
-                      "string.");
-    goto done;
-  }
-  unenc[unenc_written++] = '\n';
-  unenc[unenc_written++] = 0;
-  *encoded = unenc;
-  r = 0;
- done:
-  if (r<0)
-    tor_free(unenc);
-  return r;
-}
-
-/** Encrypt the encoded introduction points in <b>encoded</b> using
- * authorization type  'basic' with <b>client_cookies</b> and write the
- * result to a newly allocated string pointed to by <b>encrypted_out</b> of
- * length <b>encrypted_len_out</b>. Return 0 for success, -1 otherwise. */
-static int
-rend_encrypt_v2_intro_points_basic(char **encrypted_out,
-                                   size_t *encrypted_len_out,
-                                   const char *encoded,
-                                   smartlist_t *client_cookies)
-{
-  int r = -1, i, pos, enclen, client_blocks;
-  size_t len, client_entries_len;
-  char *enc = NULL, iv[CIPHER_IV_LEN], *client_part = NULL,
-       session_key[CIPHER_KEY_LEN];
-  smartlist_t *encrypted_session_keys = NULL;
-  crypto_digest_t *digest;
-  crypto_cipher_t *cipher;
-  tor_assert(encoded);
-  tor_assert(client_cookies && smartlist_len(client_cookies) > 0);
-
-  /* Generate session key. */
-  crypto_rand(session_key, CIPHER_KEY_LEN);
-
-  /* Determine length of encrypted introduction points including session
-   * keys. */
-  client_blocks = 1 + ((smartlist_len(client_cookies) - 1) /
-                       REND_BASIC_AUTH_CLIENT_MULTIPLE);
-  client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
-                       REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
-  len = 2 + client_entries_len + CIPHER_IV_LEN + strlen(encoded);
-  if (client_blocks >= 256) {
-    log_warn(LD_REND, "Too many clients in introduction point string.");
-    goto done;
-  }
-  enc = tor_malloc_zero(len);
-  enc[0] = 0x01; /* type of authorization. */
-  enc[1] = (uint8_t)client_blocks;
-
-  /* Encrypt with random session key. */
-  enclen = crypto_cipher_encrypt_with_iv(session_key,
-      enc + 2 + client_entries_len,
-      CIPHER_IV_LEN + strlen(encoded), encoded, strlen(encoded));
-
-  if (enclen < 0) {
-    log_warn(LD_REND, "Could not encrypt introduction point string.");
-    goto done;
-  }
-  memcpy(iv, enc + 2 + client_entries_len, CIPHER_IV_LEN);
-
-  /* Encrypt session key for cookies, determine client IDs, and put both
-   * in a smartlist. */
-  encrypted_session_keys = smartlist_new();
-  SMARTLIST_FOREACH_BEGIN(client_cookies, const char *, cookie) {
-    client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
-    /* Encrypt session key. */
-    cipher = crypto_cipher_new(cookie);
-    if (crypto_cipher_encrypt(cipher, client_part +
-                                  REND_BASIC_AUTH_CLIENT_ID_LEN,
-                              session_key, CIPHER_KEY_LEN) < 0) {
-      log_warn(LD_REND, "Could not encrypt session key for client.");
-      crypto_cipher_free(cipher);
-      tor_free(client_part);
-      goto done;
-    }
-    crypto_cipher_free(cipher);
-
-    /* Determine client ID. */
-    digest = crypto_digest_new();
-    crypto_digest_add_bytes(digest, cookie, REND_DESC_COOKIE_LEN);
-    crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
-    crypto_digest_get_digest(digest, client_part,
-                             REND_BASIC_AUTH_CLIENT_ID_LEN);
-    crypto_digest_free(digest);
-
-    /* Put both together. */
-    smartlist_add(encrypted_session_keys, client_part);
-  } SMARTLIST_FOREACH_END(cookie);
-
-  /* Add some fake client IDs and encrypted session keys. */
-  for (i = (smartlist_len(client_cookies) - 1) %
-           REND_BASIC_AUTH_CLIENT_MULTIPLE;
-       i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) {
-    client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
-    crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
-    smartlist_add(encrypted_session_keys, client_part);
-  }
-  /* Sort smartlist and put elements in result in order. */
-  smartlist_sort_digests(encrypted_session_keys);
-  pos = 2;
-  SMARTLIST_FOREACH(encrypted_session_keys, const char *, entry, {
-    memcpy(enc + pos, entry, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
-    pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
-  });
-  *encrypted_out = enc;
-  *encrypted_len_out = len;
-  enc = NULL; /* prevent free. */
-  r = 0;
- done:
-  tor_free(enc);
-  if (encrypted_session_keys) {
-    SMARTLIST_FOREACH(encrypted_session_keys, char *, d, tor_free(d););
-    smartlist_free(encrypted_session_keys);
-  }
-  return r;
-}
-
-/** Encrypt the encoded introduction points in <b>encoded</b> using
- * authorization type 'stealth' with <b>descriptor_cookie</b> of length
- * REND_DESC_COOKIE_LEN and write the result to a newly allocated string
- * pointed to by <b>encrypted_out</b> of length <b>encrypted_len_out</b>.
- * Return 0 for success, -1 otherwise. */
-static int
-rend_encrypt_v2_intro_points_stealth(char **encrypted_out,
-                                     size_t *encrypted_len_out,
-                                     const char *encoded,
-                                     const char *descriptor_cookie)
-{
-  int r = -1, enclen;
-  char *enc;
-  tor_assert(encoded);
-  tor_assert(descriptor_cookie);
-
-  enc = tor_malloc_zero(1 + CIPHER_IV_LEN + strlen(encoded));
-  enc[0] = 0x02; /* Auth type */
-  enclen = crypto_cipher_encrypt_with_iv(descriptor_cookie,
-                                         enc + 1,
-                                         CIPHER_IV_LEN+strlen(encoded),
-                                         encoded, strlen(encoded));
-  if (enclen < 0) {
-    log_warn(LD_REND, "Could not encrypt introduction point string.");
-    goto done;
-  }
-  *encrypted_out = enc;
-  *encrypted_len_out = enclen;
-  enc = NULL; /* prevent free */
-  r = 0;
- done:
-  tor_free(enc);
-  return r;
-}
-
-/** Attempt to parse the given <b>desc_str</b> and return true if this
- * succeeds, false otherwise. */
-STATIC int
-rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc)
-{
-  rend_service_descriptor_t *test_parsed = NULL;
-  char test_desc_id[DIGEST_LEN];
-  char *test_intro_content = NULL;
-  size_t test_intro_size;
-  size_t test_encoded_size;
-  const char *test_next;
-  int res = rend_parse_v2_service_descriptor(&test_parsed, test_desc_id,
-                                         &test_intro_content,
-                                         &test_intro_size,
-                                         &test_encoded_size,
-                                         &test_next, desc->desc_str, 1);
-  rend_service_descriptor_free(test_parsed);
-  tor_free(test_intro_content);
-  return (res >= 0);
-}
-
-/** Free the storage held by an encoded v2 service descriptor. */
-void
-rend_encoded_v2_service_descriptor_free_(
-  rend_encoded_v2_service_descriptor_t *desc)
-{
-  if (!desc)
-    return;
-  tor_free(desc->desc_str);
-  tor_free(desc);
-}
-
-/** Free the storage held by an introduction point info. */
-void
-rend_intro_point_free_(rend_intro_point_t *intro)
-{
-  if (!intro)
-    return;
-
-  extend_info_free(intro->extend_info);
-  crypto_pk_free(intro->intro_key);
-
-  if (intro->accepted_intro_rsa_parts != NULL) {
-    replaycache_free(intro->accepted_intro_rsa_parts);
-  }
-
-  tor_free(intro);
-}
-
-/** Encode a set of rend_encoded_v2_service_descriptor_t's for <b>desc</b>
- * at time <b>now</b> using <b>service_key</b>, depending on
- * <b>auth_type</b> a <b>descriptor_cookie</b> and a list of
- * <b>client_cookies</b> (which are both <b>NULL</b> if no client
- * authorization is performed), and <b>period</b> (e.g. 0 for the current
- * period, 1 for the next period, etc.) and add them to the existing list
- * <b>descs_out</b>; return the number of seconds that the descriptors will
- * be found by clients, or -1 if the encoding was not successful. */
-int
-rend_encode_v2_descriptors(smartlist_t *descs_out,
-                           rend_service_descriptor_t *desc, time_t now,
-                           uint8_t period, rend_auth_type_t auth_type,
-                           crypto_pk_t *client_key,
-                           smartlist_t *client_cookies)
-{
-  char service_id[DIGEST_LEN];
-  char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
-  uint32_t time_period;
-  char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
-       *descriptor_cookie = NULL;
-  size_t ipos_len = 0, ipos_encrypted_len = 0;
-  int k;
-  uint32_t seconds_valid;
-  crypto_pk_t *service_key;
-  if (!desc) {
-    log_warn(LD_BUG, "Could not encode v2 descriptor: No desc given.");
-    return -1;
-  }
-  service_key = (auth_type == REND_STEALTH_AUTH) ? client_key : desc->pk;
-  tor_assert(service_key);
-  if (auth_type == REND_STEALTH_AUTH) {
-    descriptor_cookie = smartlist_get(client_cookies, 0);
-    tor_assert(descriptor_cookie);
-  }
-  /* Obtain service_id from public key. */
-  if (crypto_pk_get_digest(service_key, service_id) < 0) {
-    log_warn(LD_BUG, "Couldn't compute service key digest.");
-    return -1;
-  }
-  /* Calculate current time-period. */
-  time_period = get_time_period(now, period, service_id);
-  /* Determine how many seconds the descriptor will be valid. */
-  seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY +
-                  get_seconds_valid(now, service_id);
-  /* Assemble, possibly encrypt, and encode introduction points. */
-  if (smartlist_len(desc->intro_nodes) > 0) {
-    if (rend_encode_v2_intro_points(&ipos, desc) < 0) {
-      log_warn(LD_REND, "Encoding of introduction points did not succeed.");
-      return -1;
-    }
-    switch (auth_type) {
-      case REND_NO_AUTH:
-        ipos_len = strlen(ipos);
-        break;
-      case REND_BASIC_AUTH:
-        if (rend_encrypt_v2_intro_points_basic(&ipos_encrypted,
-                                               &ipos_encrypted_len, ipos,
-                                               client_cookies) < 0) {
-          log_warn(LD_REND, "Encrypting of introduction points did not "
-                            "succeed.");
-          tor_free(ipos);
-          return -1;
-        }
-        tor_free(ipos);
-        ipos = ipos_encrypted;
-        ipos_len = ipos_encrypted_len;
-        break;
-      case REND_STEALTH_AUTH:
-        if (rend_encrypt_v2_intro_points_stealth(&ipos_encrypted,
-                                                 &ipos_encrypted_len, ipos,
-                                                 descriptor_cookie) < 0) {
-          log_warn(LD_REND, "Encrypting of introduction points did not "
-                            "succeed.");
-          tor_free(ipos);
-          return -1;
-        }
-        tor_free(ipos);
-        ipos = ipos_encrypted;
-        ipos_len = ipos_encrypted_len;
-        break;
-      case REND_V3_AUTH:
-        break; /* v3 service, break. */
-      default:
-        log_warn(LD_REND|LD_BUG, "Unrecognized authorization type %d",
-                 (int)auth_type);
-        tor_free(ipos);
-        return -1;
-    }
-    /* Base64-encode introduction points. */
-    ipos_base64 = tor_calloc(ipos_len, 2);
-    if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len,
-                      BASE64_ENCODE_MULTILINE)<0) {
-      log_warn(LD_REND, "Could not encode introduction point string to "
-               "base64. length=%d", (int)ipos_len);
-      tor_free(ipos_base64);
-      tor_free(ipos);
-      return -1;
-    }
-    tor_free(ipos);
-  }
-  /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
-  for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
-    char secret_id_part[DIGEST_LEN];
-    char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1];
-    char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-    char *permanent_key = NULL;
-    size_t permanent_key_len;
-    char published[ISO_TIME_LEN+1];
-    int i;
-    char protocol_versions_string[16]; /* max len: "0,1,2,3,4,5,6,7\0" */
-    size_t protocol_versions_written;
-    size_t desc_len;
-    char *desc_str = NULL;
-    int result = 0;
-    size_t written = 0;
-    char desc_digest[DIGEST_LEN];
-    rend_encoded_v2_service_descriptor_t *enc =
-      tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
-    /* Calculate secret-id-part = h(time-period | cookie | replica). */
-    get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
-                             k);
-    base32_encode(secret_id_part_base32, sizeof(secret_id_part_base32),
-                  secret_id_part, DIGEST_LEN);
-    /* Calculate descriptor ID. */
-    rend_get_descriptor_id_bytes(enc->desc_id, service_id, secret_id_part);
-    base32_encode(desc_id_base32, sizeof(desc_id_base32),
-                  enc->desc_id, DIGEST_LEN);
-    /* PEM-encode the public key */
-    if (crypto_pk_write_public_key_to_string(service_key, &permanent_key,
-                                             &permanent_key_len) < 0) {
-      log_warn(LD_BUG, "Could not write public key to string.");
-      rend_encoded_v2_service_descriptor_free(enc);
-      goto err;
-    }
-    /* Encode timestamp. */
-    format_iso_time(published, desc->timestamp);
-    /* Write protocol-versions bitmask to comma-separated value string. */
-    protocol_versions_written = 0;
-    for (i = 0; i < 8; i++) {
-      if (desc->protocols & 1 << i) {
-        tor_snprintf(protocol_versions_string + protocol_versions_written,
-                     16 - protocol_versions_written, "%d,", i);
-        protocol_versions_written += 2;
-      }
-    }
-    if (protocol_versions_written)
-      protocol_versions_string[protocol_versions_written - 1] = '\0';
-    else
-      protocol_versions_string[0]= '\0';
-    /* Assemble complete descriptor. */
-    desc_len = 2000 + smartlist_len(desc->intro_nodes) * 1000; /* far too long,
-                                                                  but okay.*/
-    enc->desc_str = desc_str = tor_malloc_zero(desc_len);
-    result = tor_snprintf(desc_str, desc_len,
-             "rendezvous-service-descriptor %s\n"
-             "version 2\n"
-             "permanent-key\n%s"
-             "secret-id-part %s\n"
-             "publication-time %s\n"
-             "protocol-versions %s\n",
-        desc_id_base32,
-        permanent_key,
-        secret_id_part_base32,
-        published,
-        protocol_versions_string);
-    tor_free(permanent_key);
-    if (result < 0) {
-      log_warn(LD_BUG, "Descriptor ran out of room.");
-      rend_encoded_v2_service_descriptor_free(enc);
-      goto err;
-    }
-    written = result;
-    /* Add introduction points. */
-    if (ipos_base64) {
-      result = tor_snprintf(desc_str + written, desc_len - written,
-                            "introduction-points\n"
-                            "-----BEGIN MESSAGE-----\n%s"
-                            "-----END MESSAGE-----\n",
-                            ipos_base64);
-      if (result < 0) {
-        log_warn(LD_BUG, "could not write introduction points.");
-        rend_encoded_v2_service_descriptor_free(enc);
-        goto err;
-      }
-      written += result;
-    }
-    /* Add signature. */
-    strlcpy(desc_str + written, "signature\n", desc_len - written);
-    written += strlen(desc_str + written);
-    if (crypto_digest(desc_digest, desc_str, written) < 0) {
-      log_warn(LD_BUG, "could not create digest.");
-      rend_encoded_v2_service_descriptor_free(enc);
-      goto err;
-    }
-    if (router_append_dirobj_signature(desc_str + written,
-                                       desc_len - written,
-                                       desc_digest, DIGEST_LEN,
-                                       service_key) < 0) {
-      log_warn(LD_BUG, "Couldn't sign desc.");
-      rend_encoded_v2_service_descriptor_free(enc);
-      goto err;
-    }
-    written += strlen(desc_str+written);
-    if (written+2 > desc_len) {
-        log_warn(LD_BUG, "Could not finish desc.");
-        rend_encoded_v2_service_descriptor_free(enc);
-        goto err;
-    }
-    desc_str[written++] = 0;
-    /* Check if we can parse our own descriptor. */
-    if (!rend_desc_v2_is_parsable(enc)) {
-      log_warn(LD_BUG, "Could not parse my own descriptor: %s", desc_str);
-      rend_encoded_v2_service_descriptor_free(enc);
-      goto err;
-    }
-    smartlist_add(descs_out, enc);
-    /* Add the uploaded descriptor to the local service's descriptor cache */
-    rend_cache_store_v2_desc_as_service(enc->desc_str);
-    base32_encode(service_id_base32, sizeof(service_id_base32),
-          service_id, REND_SERVICE_ID_LEN);
-    control_event_hs_descriptor_created(service_id_base32, desc_id_base32, k);
-  }
-
-  log_info(LD_REND, "Successfully encoded a v2 descriptor and "
-                    "confirmed that it is parsable.");
-  goto done;
-
- err:
-  SMARTLIST_FOREACH(descs_out, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d););
-  smartlist_clear(descs_out);
-  seconds_valid = -1;
-
- done:
-  tor_free(ipos_base64);
-  return seconds_valid;
-}
-
-/** Sets <b>out</b> to the first 10 bytes of the digest of <b>pk</b>,
- * base32 encoded.  NUL-terminates out.  (We use this string to
- * identify services in directory requests and .onion URLs.)
- */
-int
-rend_get_service_id(crypto_pk_t *pk, char *out)
-{
-  char buf[DIGEST_LEN];
-  tor_assert(pk);
-  if (crypto_pk_get_digest(pk, buf) < 0)
-    return -1;
-  base32_encode(out, REND_SERVICE_ID_LEN_BASE32+1, buf, REND_SERVICE_ID_LEN);
-  return 0;
-}
-
-/** Return true iff <b>query</b> is a syntactically valid service ID (as
- * generated by rend_get_service_id).  */
-int
-rend_valid_v2_service_id(const char *query)
-{
-  if (strlen(query) != REND_SERVICE_ID_LEN_BASE32)
-    return 0;
-
-  if (strspn(query, BASE32_CHARS) != REND_SERVICE_ID_LEN_BASE32)
-    return 0;
-
-  return 1;
-}
-
-/** Return true iff <b>query</b> is a syntactically valid descriptor ID.
- * (as generated by rend_get_descriptor_id_bytes). */
-int
-rend_valid_descriptor_id(const char *query)
-{
-  if (strlen(query) != REND_DESC_ID_V2_LEN_BASE32) {
-    goto invalid;
-  }
-  if (strspn(query, BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
-    goto invalid;
-  }
-
-  return 1;
-
- invalid:
-  return 0;
-}
-
-/** Return true iff <b>client_name</b> is a syntactically valid name
- * for rendezvous client authentication. */
-int
-rend_valid_client_name(const char *client_name)
-{
-  size_t len = strlen(client_name);
-  if (len < 1 || len > REND_CLIENTNAME_MAX_LEN) {
-    return 0;
-  }
-  if (strspn(client_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
-    return 0;
-  }
-
-  return 1;
-}
 
 /** Called when we get a rendezvous-related relay cell on circuit
  * <b>circ</b>.  Dispatch on rendezvous relay command. */
@@ -842,168 +101,6 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
              command);
 }
 
-/** Determine the routers that are responsible for <b>id</b> (binary) and
- * add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>.
- * Return -1 if we're returning an empty smartlist, else return 0.
- */
-int
-hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
-                                     const char *id)
-{
-  int start, found, n_added = 0, i;
-  networkstatus_t *c = networkstatus_get_latest_consensus();
-  if (!c || !smartlist_len(c->routerstatus_list)) {
-    log_info(LD_REND, "We don't have a consensus, so we can't perform v2 "
-             "rendezvous operations.");
-    return -1;
-  }
-  tor_assert(id);
-  start = networkstatus_vote_find_entry_idx(c, id, &found);
-  if (start == smartlist_len(c->routerstatus_list)) start = 0;
-  i = start;
-  do {
-    routerstatus_t *r = smartlist_get(c->routerstatus_list, i);
-    if (r->is_hs_dir) {
-      smartlist_add(responsible_dirs, r);
-      if (++n_added == REND_NUMBER_OF_CONSECUTIVE_REPLICAS)
-        return 0;
-    }
-    if (++i == smartlist_len(c->routerstatus_list))
-      i = 0;
-  } while (i != start);
-
-  /* Even though we don't have the desired number of hidden service
-   * directories, be happy if we got any. */
-  return smartlist_len(responsible_dirs) ? 0 : -1;
-}
-
-/* Length of the 'extended' auth cookie used to encode auth type before
- * base64 encoding. */
-#define REND_DESC_COOKIE_LEN_EXT (REND_DESC_COOKIE_LEN + 1)
-/* Length of the zero-padded auth cookie when base64 encoded. These two
- * padding bytes always (A=) are stripped off of the returned cookie. */
-#define REND_DESC_COOKIE_LEN_EXT_BASE64 (REND_DESC_COOKIE_LEN_BASE64 + 2)
-
-/** Encode a client authorization descriptor cookie.
- * The result of this function is suitable for use in the HidServAuth
- * option.  The trailing padding characters are removed, and the
- * auth type is encoded into the cookie.
- *
- * Returns a new base64-encoded cookie. This function cannot fail.
- * The caller is responsible for freeing the returned value.
- */
-char *
-rend_auth_encode_cookie(const uint8_t *cookie_in, rend_auth_type_t auth_type)
-{
-  uint8_t extended_cookie[REND_DESC_COOKIE_LEN_EXT];
-  char *cookie_out = tor_malloc_zero(REND_DESC_COOKIE_LEN_EXT_BASE64 + 1);
-  int re;
-
-  tor_assert(cookie_in);
-
-  memcpy(extended_cookie, cookie_in, REND_DESC_COOKIE_LEN);
-  extended_cookie[REND_DESC_COOKIE_LEN] = ((int)auth_type - 1) << 4;
-  re = base64_encode(cookie_out, REND_DESC_COOKIE_LEN_EXT_BASE64 + 1,
-                     (const char *) extended_cookie, REND_DESC_COOKIE_LEN_EXT,
-                     0);
-  tor_assert(re == REND_DESC_COOKIE_LEN_EXT_BASE64);
-
-  /* Remove the trailing 'A='.  Auth type is encoded in the high bits
-   * of the last byte, so the last base64 character will always be zero
-   * (A).  This is subtly different behavior from base64_encode_nopad. */
-  cookie_out[REND_DESC_COOKIE_LEN_BASE64] = '\0';
-  memwipe(extended_cookie, 0, sizeof(extended_cookie));
-  return cookie_out;
-}
-
-/** Decode a base64-encoded client authorization descriptor cookie.
- * The descriptor_cookie can be truncated to REND_DESC_COOKIE_LEN_BASE64
- * characters (as given to clients), or may include the two padding
- * characters (as stored by the service).
- *
- * The result is stored in REND_DESC_COOKIE_LEN bytes of cookie_out.
- * The rend_auth_type_t decoded from the cookie is stored in the
- * optional auth_type_out parameter.
- *
- * Return 0 on success, or -1 on error.  The caller is responsible for
- * freeing the returned err_msg.
- */
-int
-rend_auth_decode_cookie(const char *cookie_in, uint8_t *cookie_out,
-                        rend_auth_type_t *auth_type_out, char **err_msg_out)
-{
-  uint8_t descriptor_cookie_decoded[REND_DESC_COOKIE_LEN_EXT + 1] = { 0 };
-  char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_EXT_BASE64 + 1];
-  const char *descriptor_cookie = cookie_in;
-  char *err_msg = NULL;
-  int auth_type_val = 0;
-  int res = -1;
-  int decoded_len;
-
-  size_t len = strlen(descriptor_cookie);
-  if (len == REND_DESC_COOKIE_LEN_BASE64) {
-    /* Add a trailing zero byte to make base64-decoding happy. */
-    tor_snprintf(descriptor_cookie_base64ext,
-                 sizeof(descriptor_cookie_base64ext),
-                 "%sA=", descriptor_cookie);
-    descriptor_cookie = descriptor_cookie_base64ext;
-  } else if (len != REND_DESC_COOKIE_LEN_EXT_BASE64) {
-    tor_asprintf(&err_msg, "Authorization cookie has wrong length: %s",
-                 escaped(cookie_in));
-    goto err;
-  }
-
-  decoded_len = base64_decode((char *) descriptor_cookie_decoded,
-                              sizeof(descriptor_cookie_decoded),
-                              descriptor_cookie,
-                              REND_DESC_COOKIE_LEN_EXT_BASE64);
-  if (decoded_len != REND_DESC_COOKIE_LEN &&
-      decoded_len != REND_DESC_COOKIE_LEN_EXT) {
-    tor_asprintf(&err_msg, "Authorization cookie has invalid characters: %s",
-                 escaped(cookie_in));
-    goto err;
-  }
-
-  if (auth_type_out) {
-    auth_type_val = (descriptor_cookie_decoded[REND_DESC_COOKIE_LEN] >> 4) + 1;
-    if (auth_type_val < 1 || auth_type_val > 2) {
-      tor_asprintf(&err_msg, "Authorization cookie type is unknown: %s",
-                   escaped(cookie_in));
-      goto err;
-    }
-    *auth_type_out = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
-  }
-
-  memcpy(cookie_out, descriptor_cookie_decoded, REND_DESC_COOKIE_LEN);
-  res = 0;
- err:
-  if (err_msg_out) {
-    *err_msg_out = err_msg;
-  } else {
-    tor_free(err_msg);
-  }
-  memwipe(descriptor_cookie_decoded, 0, sizeof(descriptor_cookie_decoded));
-  memwipe(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext));
-  return res;
-}
-
-/* Is this a rend client or server that allows direct (non-anonymous)
- * connections?
- * Onion services can be configured to start in this mode for single onion. */
-int
-rend_allow_non_anonymous_connection(const or_options_t* options)
-{
-  return rend_service_allow_non_anonymous_connection(options);
-}
-
-/* Is this a rend client or server in non-anonymous mode?
- * Onion services can be configured to start in this mode for single onion. */
-int
-rend_non_anonymous_mode_enabled(const or_options_t *options)
-{
-  return rend_service_non_anonymous_mode_enabled(options);
-}
-
 /* Make sure that tor only builds one-hop circuits when they would not
  * compromise user anonymity.
  *
@@ -1022,35 +119,6 @@ assert_circ_anonymity_ok(const origin_circuit_t *circ,
   tor_assert(circ->build_state);
 
   if (circ->build_state->onehop_tunnel) {
-    tor_assert(rend_allow_non_anonymous_connection(options));
-  }
-}
-
-/* Return 1 iff the given <b>digest</b> of a permenanent hidden service key is
- * equal to the digest in the origin circuit <b>ocirc</b> of its rend data .
- * If the rend data doesn't exist, 0 is returned. This function is agnostic to
- * the rend data version. */
-int
-rend_circuit_pk_digest_eq(const origin_circuit_t *ocirc,
-                          const uint8_t *digest)
-{
-  size_t rend_pk_digest_len;
-  const uint8_t *rend_pk_digest;
-
-  tor_assert(ocirc);
-  tor_assert(digest);
-
-  if (ocirc->rend_data == NULL) {
-    goto no_match;
-  }
-
-  rend_pk_digest = rend_data_get_pk_digest(ocirc->rend_data,
-                                           &rend_pk_digest_len);
-  if (tor_memeq(rend_pk_digest, digest, rend_pk_digest_len)) {
-    goto match;
+    tor_assert(hs_service_allow_non_anonymous_connection(options));
   }
- no_match:
-  return 0;
- match:
-  return 1;
 }
diff --git a/src/feature/rend/rendcommon.h b/src/feature/rend/rendcommon.h
index d8281e0578..502d594940 100644
--- a/src/feature/rend/rendcommon.h
+++ b/src/feature/rend/rendcommon.h
@@ -18,65 +18,12 @@ typedef enum rend_intro_point_failure_t {
   INTRO_POINT_FAILURE_UNREACHABLE = 2,
 } rend_intro_point_failure_t;
 
-int rend_cmp_service_ids(const char *one, const char *two);
-
 void rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
                              int command, size_t length,
                              const uint8_t *payload);
 
-void rend_service_descriptor_free_(rend_service_descriptor_t *desc);
-#define rend_service_descriptor_free(desc) \
-  FREE_AND_NULL(rend_service_descriptor_t, rend_service_descriptor_free_, \
-                (desc))
-int rend_get_service_id(crypto_pk_t *pk, char *out);
-void rend_encoded_v2_service_descriptor_free_(
-                               rend_encoded_v2_service_descriptor_t *desc);
-#define rend_encoded_v2_service_descriptor_free(desc) \
-  FREE_AND_NULL(rend_encoded_v2_service_descriptor_t, \
-                rend_encoded_v2_service_descriptor_free_, (desc))
-void rend_intro_point_free_(rend_intro_point_t *intro);
-#define rend_intro_point_free(intro) \
-  FREE_AND_NULL(rend_intro_point_t, rend_intro_point_free_, (intro))
-
-int rend_valid_v2_service_id(const char *query);
-int rend_valid_descriptor_id(const char *query);
-int rend_valid_client_name(const char *client_name);
-int rend_encode_v2_descriptors(smartlist_t *descs_out,
-                               rend_service_descriptor_t *desc, time_t now,
-                               uint8_t period, rend_auth_type_t auth_type,
-                               crypto_pk_t *client_key,
-                               smartlist_t *client_cookies);
-int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
-                            const char *descriptor_cookie,
-                            time_t now, uint8_t replica);
-void rend_get_descriptor_id_bytes(char *descriptor_id_out,
-                                  const char *service_id,
-                                  const char *secret_id_part);
-int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
-                                         const char *id);
-
-int rend_circuit_pk_digest_eq(const origin_circuit_t *ocirc,
-                              const uint8_t *digest);
-
-char *rend_auth_encode_cookie(const uint8_t *cookie_in,
-                              rend_auth_type_t auth_type);
-int rend_auth_decode_cookie(const char *cookie_in,
-                            uint8_t *cookie_out,
-                            rend_auth_type_t *auth_type_out,
-                            char **err_msg_out);
-
-int rend_allow_non_anonymous_connection(const or_options_t* options);
-int rend_non_anonymous_mode_enabled(const or_options_t *options);
-
 void assert_circ_anonymity_ok(const origin_circuit_t *circ,
                               const or_options_t *options);
 
-#ifdef RENDCOMMON_PRIVATE
-
-STATIC int
-rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc);
-
-#endif /* defined(RENDCOMMON_PRIVATE) */
-
 #endif /* !defined(TOR_RENDCOMMON_H) */
 
diff --git a/src/feature/rend/rendparse.c b/src/feature/rend/rendparse.c
deleted file mode 100644
index c28add5ca9..0000000000
--- a/src/feature/rend/rendparse.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rendparse.c
- * \brief Code to parse and validate v2 hidden service descriptors.
- **/
-
-#include "core/or/or.h"
-#include "core/or/extendinfo.h"
-#include "feature/dirparse/parsecommon.h"
-#include "feature/dirparse/sigcommon.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendparse.h"
-#include "lib/memarea/memarea.h"
-
-#include "core/or/extend_info_st.h"
-#include "feature/rend/rend_authorized_client_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-
-/** List of tokens recognized in rendezvous service descriptors */
-static token_rule_t desc_token_table[] = {
-  T1_START("rendezvous-service-descriptor", R_RENDEZVOUS_SERVICE_DESCRIPTOR,
-           EQ(1), NO_OBJ),
-  T1("version", R_VERSION, EQ(1), NO_OBJ),
-  T1("permanent-key", R_PERMANENT_KEY, NO_ARGS, NEED_KEY_1024),
-  T1("secret-id-part", R_SECRET_ID_PART, EQ(1), NO_OBJ),
-  T1("publication-time", R_PUBLICATION_TIME, CONCAT_ARGS, NO_OBJ),
-  T1("protocol-versions", R_PROTOCOL_VERSIONS, EQ(1), NO_OBJ),
-  T01("introduction-points", R_INTRODUCTION_POINTS, NO_ARGS, NEED_OBJ),
-  T1_END("signature", R_SIGNATURE, NO_ARGS, NEED_OBJ),
-  END_OF_TABLE
-};
-
-/** List of tokens recognized in the (encrypted) list of introduction points of
- * rendezvous service descriptors */
-static token_rule_t ipo_token_table[] = {
-  T1_START("introduction-point", R_IPO_IDENTIFIER, EQ(1), NO_OBJ),
-  T1("ip-address", R_IPO_IP_ADDRESS, EQ(1), NO_OBJ),
-  T1("onion-port", R_IPO_ONION_PORT, EQ(1), NO_OBJ),
-  T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024),
-  T1("service-key", R_IPO_SERVICE_KEY, NO_ARGS, NEED_KEY_1024),
-  END_OF_TABLE
-};
-
-/** List of tokens recognized in the (possibly encrypted) list of introduction
- * points of rendezvous service descriptors */
-static token_rule_t client_keys_token_table[] = {
-  T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
-  T1("descriptor-cookie", C_DESCRIPTOR_COOKIE, EQ(1), NO_OBJ),
-  T01("client-key", C_CLIENT_KEY, NO_ARGS, NEED_SKEY_1024),
-  END_OF_TABLE
-};
-
-/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>,
- * write the parsed descriptor to the newly allocated *<b>parsed_out</b>, the
- * binary descriptor ID of length DIGEST_LEN to <b>desc_id_out</b>, the
- * encrypted introduction points to the newly allocated
- * *<b>intro_points_encrypted_out</b>, their encrypted size to
- * *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor
- * to *<b>encoded_size_out</b>, and a pointer to the possibly next
- * descriptor to *<b>next_out</b>; return 0 for success (including validation)
- * and -1 for failure.
- *
- * If <b>as_hsdir</b> is 1, we're parsing this as an HSDir, and we should
- * be strict about time formats.
- */
-int
-rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
-                                 char *desc_id_out,
-                                 char **intro_points_encrypted_out,
-                                 size_t *intro_points_encrypted_size_out,
-                                 size_t *encoded_size_out,
-                                 const char **next_out, const char *desc,
-                                 int as_hsdir)
-{
-  rend_service_descriptor_t *result =
-                            tor_malloc_zero(sizeof(rend_service_descriptor_t));
-  char desc_hash[DIGEST_LEN];
-  const char *eos;
-  smartlist_t *tokens = smartlist_new();
-  directory_token_t *tok;
-  char secret_id_part[DIGEST_LEN];
-  int i, version, num_ok=1;
-  smartlist_t *versions;
-  char public_key_hash[DIGEST_LEN];
-  char test_desc_id[DIGEST_LEN];
-  memarea_t *area = NULL;
-  const int strict_time_fmt = as_hsdir;
-
-  tor_assert(desc);
-  /* Check if desc starts correctly. */
-  if (strcmpstart(desc, "rendezvous-service-descriptor ")) {
-    log_info(LD_REND, "Descriptor does not start correctly.");
-    goto err;
-  }
-  /* Compute descriptor hash for later validation. */
-  if (router_get_hash_impl(desc, strlen(desc), desc_hash,
-                           "rendezvous-service-descriptor ",
-                           "\nsignature", '\n', DIGEST_SHA1) < 0) {
-    log_warn(LD_REND, "Couldn't compute descriptor hash.");
-    goto err;
-  }
-  /* Determine end of string. */
-  eos = strstr(desc, "\nrendezvous-service-descriptor ");
-  if (!eos)
-    eos = desc + strlen(desc);
-  else
-    eos = eos + 1;
-  /* Check length. */
-  if (eos-desc > REND_DESC_MAX_SIZE) {
-    /* XXXX+ If we are parsing this descriptor as a server, this
-     * should be a protocol warning. */
-    log_warn(LD_REND, "Descriptor length is %d which exceeds "
-             "maximum rendezvous descriptor size of %d bytes.",
-             (int)(eos-desc), REND_DESC_MAX_SIZE);
-    goto err;
-  }
-  /* Tokenize descriptor. */
-  area = memarea_new();
-  if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) {
-    log_warn(LD_REND, "Error tokenizing descriptor.");
-    goto err;
-  }
-  /* Set next to next descriptor, if available. */
-  *next_out = eos;
-  /* Set length of encoded descriptor. */
-  *encoded_size_out = eos - desc;
-  /* Check min allowed length of token list. */
-  if (smartlist_len(tokens) < 7) {
-    log_warn(LD_REND, "Impossibly short descriptor.");
-    goto err;
-  }
-  /* Parse base32-encoded descriptor ID. */
-  tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
-  tor_assert(tok == smartlist_get(tokens, 0));
-  tor_assert(tok->n_args == 1);
-  if (!rend_valid_descriptor_id(tok->args[0])) {
-    log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
-    goto err;
-  }
-  if (base32_decode(desc_id_out, DIGEST_LEN,
-                    tok->args[0], REND_DESC_ID_V2_LEN_BASE32) != DIGEST_LEN) {
-    log_warn(LD_REND,
-             "Descriptor ID has wrong length or illegal characters: %s",
-             tok->args[0]);
-    goto err;
-  }
-  /* Parse descriptor version. */
-  tok = find_by_keyword(tokens, R_VERSION);
-  tor_assert(tok->n_args == 1);
-  result->version =
-    (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &num_ok, NULL);
-  if (result->version != 2 || !num_ok) {
-    /* If it's <2, it shouldn't be under this format.  If the number
-     * is greater than 2, we bumped it because we broke backward
-     * compatibility.  See how version numbers in our other formats
-     * work. */
-    log_warn(LD_REND, "Unrecognized descriptor version: %s",
-             escaped(tok->args[0]));
-    goto err;
-  }
-  /* Parse public key. */
-  tok = find_by_keyword(tokens, R_PERMANENT_KEY);
-  result->pk = tok->key;
-  tok->key = NULL; /* Prevent free */
-  /* Parse secret ID part. */
-  tok = find_by_keyword(tokens, R_SECRET_ID_PART);
-  tor_assert(tok->n_args == 1);
-  if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 ||
-      strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) {
-    log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
-    goto err;
-  }
-  if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) !=
-      DIGEST_LEN) {
-    log_warn(LD_REND,
-             "Secret ID part has wrong length or illegal characters: %s",
-             tok->args[0]);
-    goto err;
-  }
-  /* Parse publication time -- up-to-date check is done when storing the
-   * descriptor. */
-  tok = find_by_keyword(tokens, R_PUBLICATION_TIME);
-  tor_assert(tok->n_args == 1);
-  if (parse_iso_time_(tok->args[0], &result->timestamp,
-                      strict_time_fmt, 0) < 0) {
-    log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]);
-    goto err;
-  }
-  /* Parse protocol versions. */
-  tok = find_by_keyword(tokens, R_PROTOCOL_VERSIONS);
-  tor_assert(tok->n_args == 1);
-  versions = smartlist_new();
-  smartlist_split_string(versions, tok->args[0], ",",
-                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
-  for (i = 0; i < smartlist_len(versions); i++) {
-    version = (int) tor_parse_long(smartlist_get(versions, i),
-                                   10, 0, INT_MAX, &num_ok, NULL);
-    if (!num_ok) /* It's a string; let's ignore it. */
-      continue;
-    if (version >= REND_PROTOCOL_VERSION_BITMASK_WIDTH)
-      /* Avoid undefined left-shift behaviour. */
-      continue;
-    result->protocols |= 1 << version;
-  }
-  SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp));
-  smartlist_free(versions);
-  /* Parse encrypted introduction points. Don't verify. */
-  tok = find_opt_by_keyword(tokens, R_INTRODUCTION_POINTS);
-  if (tok) {
-    if (strcmp(tok->object_type, "MESSAGE")) {
-      log_warn(LD_DIR, "Bad object type: introduction points should be of "
-               "type MESSAGE");
-      goto err;
-    }
-    *intro_points_encrypted_out = tor_memdup(tok->object_body,
-                                             tok->object_size);
-    *intro_points_encrypted_size_out = tok->object_size;
-  } else {
-    *intro_points_encrypted_out = NULL;
-    *intro_points_encrypted_size_out = 0;
-  }
-  /* Parse and verify signature. */
-  tok = find_by_keyword(tokens, R_SIGNATURE);
-  if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0,
-                            "v2 rendezvous service descriptor") < 0)
-    goto err;
-  /* Verify that descriptor ID belongs to public key and secret ID part. */
-  if (crypto_pk_get_digest(result->pk, public_key_hash) < 0) {
-    log_warn(LD_REND, "Unable to compute rend descriptor public key digest");
-    goto err;
-  }
-  rend_get_descriptor_id_bytes(test_desc_id, public_key_hash,
-                               secret_id_part);
-  if (tor_memneq(desc_id_out, test_desc_id, DIGEST_LEN)) {
-    log_warn(LD_REND, "Parsed descriptor ID does not match "
-             "computed descriptor ID.");
-    goto err;
-  }
-  goto done;
- err:
-  rend_service_descriptor_free(result);
-  result = NULL;
- done:
-  if (tokens) {
-    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
-    smartlist_free(tokens);
-  }
-  if (area)
-    memarea_drop_all(area);
-  *parsed_out = result;
-  if (result)
-    return 0;
-  return -1;
-}
-
-/** Decrypt the encrypted introduction points in <b>ipos_encrypted</b> of
- * length <b>ipos_encrypted_size</b> using <b>descriptor_cookie</b> and
- * write the result to a newly allocated string that is pointed to by
- * <b>ipos_decrypted</b> and its length to <b>ipos_decrypted_size</b>.
- * Return 0 if decryption was successful and -1 otherwise. */
-int
-rend_decrypt_introduction_points(char **ipos_decrypted,
-                                 size_t *ipos_decrypted_size,
-                                 const char *descriptor_cookie,
-                                 const char *ipos_encrypted,
-                                 size_t ipos_encrypted_size)
-{
-  tor_assert(ipos_encrypted);
-  tor_assert(descriptor_cookie);
-  if (ipos_encrypted_size < 2) {
-    log_warn(LD_REND, "Size of encrypted introduction points is too "
-                      "small.");
-    return -1;
-  }
-  if (ipos_encrypted[0] == (int)REND_BASIC_AUTH) {
-    char iv[CIPHER_IV_LEN], client_id[REND_BASIC_AUTH_CLIENT_ID_LEN],
-         session_key[CIPHER_KEY_LEN], *dec;
-    int declen, client_blocks;
-    size_t pos = 0, len, client_entries_len;
-    crypto_digest_t *digest;
-    crypto_cipher_t *cipher;
-    client_blocks = (int) ipos_encrypted[1];
-    client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
-                         REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
-    if (ipos_encrypted_size < 2 + client_entries_len + CIPHER_IV_LEN + 1) {
-      log_warn(LD_REND, "Size of encrypted introduction points is too "
-                        "small.");
-      return -1;
-    }
-    memcpy(iv, ipos_encrypted + 2 + client_entries_len, CIPHER_IV_LEN);
-    digest = crypto_digest_new();
-    crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN);
-    crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
-    crypto_digest_get_digest(digest, client_id,
-                             REND_BASIC_AUTH_CLIENT_ID_LEN);
-    crypto_digest_free(digest);
-    for (pos = 2; pos < 2 + client_entries_len;
-         pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN) {
-      if (tor_memeq(ipos_encrypted + pos, client_id,
-                  REND_BASIC_AUTH_CLIENT_ID_LEN)) {
-        /* Attempt to decrypt introduction points. */
-        cipher = crypto_cipher_new(descriptor_cookie);
-        if (crypto_cipher_decrypt(cipher, session_key, ipos_encrypted
-                                  + pos + REND_BASIC_AUTH_CLIENT_ID_LEN,
-                                  CIPHER_KEY_LEN) < 0) {
-          log_warn(LD_REND, "Could not decrypt session key for client.");
-          crypto_cipher_free(cipher);
-          return -1;
-        }
-        crypto_cipher_free(cipher);
-
-        len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN;
-        dec = tor_malloc_zero(len + 1);
-        declen = crypto_cipher_decrypt_with_iv(session_key, dec, len,
-            ipos_encrypted + 2 + client_entries_len,
-            ipos_encrypted_size - 2 - client_entries_len);
-
-        if (declen < 0) {
-          log_warn(LD_REND, "Could not decrypt introduction point string.");
-          tor_free(dec);
-          return -1;
-        }
-        if (fast_memcmpstart(dec, declen, "introduction-point ")) {
-          log_warn(LD_REND, "Decrypted introduction points don't "
-                            "look like we could parse them.");
-          tor_free(dec);
-          continue;
-        }
-        *ipos_decrypted = dec;
-        *ipos_decrypted_size = declen;
-        return 0;
-      }
-    }
-    log_warn(LD_REND, "Could not decrypt introduction points. Please "
-             "check your authorization for this service!");
-    return -1;
-  } else if (ipos_encrypted[0] == (int)REND_STEALTH_AUTH) {
-    char *dec;
-    int declen;
-    if (ipos_encrypted_size < CIPHER_IV_LEN + 2) {
-      log_warn(LD_REND, "Size of encrypted introduction points is too "
-                        "small.");
-      return -1;
-    }
-    dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1 + 1);
-
-    declen = crypto_cipher_decrypt_with_iv(descriptor_cookie, dec,
-                                           ipos_encrypted_size -
-                                               CIPHER_IV_LEN - 1,
-                                           ipos_encrypted + 1,
-                                           ipos_encrypted_size - 1);
-
-    if (declen < 0) {
-      log_warn(LD_REND, "Decrypting introduction points failed!");
-      tor_free(dec);
-      return -1;
-    }
-    *ipos_decrypted = dec;
-    *ipos_decrypted_size = declen;
-    return 0;
-  } else {
-    log_warn(LD_REND, "Unknown authorization type number: %d",
-             ipos_encrypted[0]);
-    return -1;
-  }
-}
-
-/** Parse the encoded introduction points in <b>intro_points_encoded</b> of
- * length <b>intro_points_encoded_size</b> and write the result to the
- * descriptor in <b>parsed</b>; return the number of successfully parsed
- * introduction points or -1 in case of a failure. */
-int
-rend_parse_introduction_points(rend_service_descriptor_t *parsed,
-                               const char *intro_points_encoded,
-                               size_t intro_points_encoded_size)
-{
-  const char *current_ipo, *end_of_intro_points;
-  smartlist_t *tokens = NULL;
-  directory_token_t *tok;
-  rend_intro_point_t *intro;
-  extend_info_t *info;
-  int result, num_ok=1;
-  memarea_t *area = NULL;
-  tor_assert(parsed);
-  /** Function may only be invoked once. */
-  tor_assert(!parsed->intro_nodes);
-  if (!intro_points_encoded || intro_points_encoded_size == 0) {
-    log_warn(LD_REND, "Empty or zero size introduction point list");
-    goto err;
-  }
-  /* Consider one intro point after the other. */
-  current_ipo = intro_points_encoded;
-  end_of_intro_points = intro_points_encoded + intro_points_encoded_size;
-  tokens = smartlist_new();
-  parsed->intro_nodes = smartlist_new();
-  area = memarea_new();
-
-  while (!fast_memcmpstart(current_ipo, end_of_intro_points-current_ipo,
-                      "introduction-point ")) {
-    /* Determine end of string. */
-    const char *eos = tor_memstr(current_ipo, end_of_intro_points-current_ipo,
-                                 "\nintroduction-point ");
-    if (!eos)
-      eos = end_of_intro_points;
-    else
-      eos = eos+1;
-    tor_assert(eos <= intro_points_encoded+intro_points_encoded_size);
-    /* Free tokens and clear token list. */
-    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
-    smartlist_clear(tokens);
-    memarea_clear(area);
-    /* Tokenize string. */
-    if (tokenize_string(area, current_ipo, eos, tokens, ipo_token_table, 0)) {
-      log_warn(LD_REND, "Error tokenizing introduction point");
-      goto err;
-    }
-    /* Advance to next introduction point, if available. */
-    current_ipo = eos;
-    /* Check minimum allowed length of introduction point. */
-    if (smartlist_len(tokens) < 5) {
-      log_warn(LD_REND, "Impossibly short introduction point.");
-      goto err;
-    }
-    /* Allocate new intro point and extend info. */
-    intro = tor_malloc_zero(sizeof(rend_intro_point_t));
-    info = intro->extend_info =
-      extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0);
-    /* Parse identifier. */
-    tok = find_by_keyword(tokens, R_IPO_IDENTIFIER);
-    if (base32_decode(info->identity_digest, DIGEST_LEN,
-                      tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) !=
-        DIGEST_LEN) {
-      log_warn(LD_REND,
-               "Identity digest has wrong length or illegal characters: %s",
-               tok->args[0]);
-      rend_intro_point_free(intro);
-      goto err;
-    }
-    /* Write identifier to nickname. */
-    info->nickname[0] = '$';
-    base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
-                  info->identity_digest, DIGEST_LEN);
-    /* Parse IP address. */
-    tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS);
-    tor_addr_t addr;
-    if (tor_addr_parse(&addr, tok->args[0])<0) {
-      log_warn(LD_REND, "Could not parse introduction point address.");
-      rend_intro_point_free(intro);
-      goto err;
-    }
-    if (tor_addr_family(&addr) != AF_INET) {
-      log_warn(LD_REND, "Introduction point address was not ipv4.");
-      rend_intro_point_free(intro);
-      goto err;
-    }
-
-    /* Parse onion port. */
-    tok = find_by_keyword(tokens, R_IPO_ONION_PORT);
-    uint16_t port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535,
-                                           &num_ok,NULL);
-    if (!port || !num_ok) {
-      log_warn(LD_REND, "Introduction point onion port %s is invalid",
-               escaped(tok->args[0]));
-      rend_intro_point_free(intro);
-      goto err;
-    }
-
-    /* Add the address and port. */
-    extend_info_add_orport(info, &addr, port);
-
-    /* Parse onion key. */
-    tok = find_by_keyword(tokens, R_IPO_ONION_KEY);
-    if (!crypto_pk_public_exponent_ok(tok->key)) {
-      log_warn(LD_REND,
-               "Introduction point's onion key had invalid exponent.");
-      rend_intro_point_free(intro);
-      goto err;
-    }
-    info->onion_key = tok->key;
-    tok->key = NULL; /* Prevent free */
-    /* Parse service key. */
-    tok = find_by_keyword(tokens, R_IPO_SERVICE_KEY);
-    if (!crypto_pk_public_exponent_ok(tok->key)) {
-      log_warn(LD_REND,
-               "Introduction point key had invalid exponent.");
-      rend_intro_point_free(intro);
-      goto err;
-    }
-    intro->intro_key = tok->key;
-    tok->key = NULL; /* Prevent free */
-    /* Add extend info to list of introduction points. */
-    smartlist_add(parsed->intro_nodes, intro);
-  }
-  result = smartlist_len(parsed->intro_nodes);
-  goto done;
-
- err:
-  result = -1;
-
- done:
-  /* Free tokens and clear token list. */
-  if (tokens) {
-    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
-    smartlist_free(tokens);
-  }
-  if (area)
-    memarea_drop_all(area);
-
-  return result;
-}
-
-/** Parse the content of a client_key file in <b>ckstr</b> and add
- * rend_authorized_client_t's for each parsed client to
- * <b>parsed_clients</b>. Return the number of parsed clients as result
- * or -1 for failure. */
-int
-rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
-{
-  int result = -1;
-  smartlist_t *tokens;
-  directory_token_t *tok;
-  const char *current_entry = NULL;
-  memarea_t *area = NULL;
-  char *err_msg = NULL;
-  if (!ckstr || strlen(ckstr) == 0)
-    return -1;
-  tokens = smartlist_new();
-  /* Begin parsing with first entry, skipping comments or whitespace at the
-   * beginning. */
-  area = memarea_new();
-  current_entry = eat_whitespace(ckstr);
-  while (!strcmpstart(current_entry, "client-name ")) {
-    rend_authorized_client_t *parsed_entry;
-    /* Determine end of string. */
-    const char *eos = strstr(current_entry, "\nclient-name ");
-    if (!eos)
-      eos = current_entry + strlen(current_entry);
-    else
-      eos = eos + 1;
-    /* Free tokens and clear token list. */
-    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
-    smartlist_clear(tokens);
-    memarea_clear(area);
-    /* Tokenize string. */
-    if (tokenize_string(area, current_entry, eos, tokens,
-                        client_keys_token_table, 0)) {
-      log_warn(LD_REND, "Error tokenizing client keys file.");
-      goto err;
-    }
-    /* Advance to next entry, if available. */
-    current_entry = eos;
-    /* Check minimum allowed length of token list. */
-    if (smartlist_len(tokens) < 2) {
-      log_warn(LD_REND, "Impossibly short client key entry.");
-      goto err;
-    }
-    /* Parse client name. */
-    tok = find_by_keyword(tokens, C_CLIENT_NAME);
-    tor_assert(tok == smartlist_get(tokens, 0));
-    tor_assert(tok->n_args == 1);
-
-    if (!rend_valid_client_name(tok->args[0])) {
-      log_warn(LD_CONFIG, "Illegal client name: %s. (Length must be "
-               "between 1 and %d, and valid characters are "
-               "[A-Za-z0-9+-_].)", tok->args[0], REND_CLIENTNAME_MAX_LEN);
-      goto err;
-    }
-    /* Check if client name is duplicate. */
-    if (strmap_get(parsed_clients, tok->args[0])) {
-      log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains a "
-               "duplicate client name: '%s'. Ignoring.", tok->args[0]);
-      goto err;
-    }
-    parsed_entry = tor_malloc_zero(sizeof(rend_authorized_client_t));
-    parsed_entry->client_name = tor_strdup(tok->args[0]);
-    strmap_set(parsed_clients, parsed_entry->client_name, parsed_entry);
-    /* Parse client key. */
-    tok = find_opt_by_keyword(tokens, C_CLIENT_KEY);
-    if (tok) {
-      parsed_entry->client_key = tok->key;
-      tok->key = NULL; /* Prevent free */
-    }
-
-    /* Parse descriptor cookie. */
-    tok = find_by_keyword(tokens, C_DESCRIPTOR_COOKIE);
-    tor_assert(tok->n_args == 1);
-    if (rend_auth_decode_cookie(tok->args[0], parsed_entry->descriptor_cookie,
-                                NULL, &err_msg) < 0) {
-      tor_assert(err_msg);
-      log_warn(LD_REND, "%s", err_msg);
-      tor_free(err_msg);
-      goto err;
-    }
-  }
-  result = strmap_size(parsed_clients);
-  goto done;
- err:
-  result = -1;
- done:
-  /* Free tokens and clear token list. */
-  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
-  smartlist_free(tokens);
-  if (area)
-    memarea_drop_all(area);
-  return result;
-}
diff --git a/src/feature/rend/rendparse.h b/src/feature/rend/rendparse.h
deleted file mode 100644
index 75109c204d..0000000000
--- a/src/feature/rend/rendparse.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rendparse.h
- * \brief Header file for rendparse.c.
- **/
-
-#ifndef TOR_REND_PARSE_H
-#define TOR_REND_PARSE_H
-
-int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
-                                     char *desc_id_out,
-                                     char **intro_points_encrypted_out,
-                                     size_t *intro_points_encrypted_size_out,
-                                     size_t *encoded_size_out,
-                                     const char **next_out, const char *desc,
-                                     int as_hsdir);
-int rend_decrypt_introduction_points(char **ipos_decrypted,
-                                     size_t *ipos_decrypted_size,
-                                     const char *descriptor_cookie,
-                                     const char *ipos_encrypted,
-                                     size_t ipos_encrypted_size);
-int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
-                                   const char *intro_points_encoded,
-                                   size_t intro_points_encoded_size);
-int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
-
-#endif /* !defined(TOR_REND_PARSE_H) */
diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c
deleted file mode 100644
index a744f6f93a..0000000000
--- a/src/feature/rend/rendservice.c
+++ /dev/null
@@ -1,4403 +0,0 @@
-/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rendservice.c
- * \brief The hidden-service side of rendezvous functionality.
- **/
-
-#define RENDSERVICE_PRIVATE
-
-#include "core/or/or.h"
-
-#include "app/config/config.h"
-#include "core/mainloop/mainloop.h"
-#include "core/or/circuitbuild.h"
-#include "core/or/circuitlist.h"
-#include "core/or/circuituse.h"
-#include "core/or/extendinfo.h"
-#include "core/or/policies.h"
-#include "core/or/relay.h"
-#include "core/or/crypt_path.h"
-#include "feature/client/circpathbias.h"
-#include "feature/control/control_events.h"
-#include "feature/dirclient/dirclient.h"
-#include "feature/dircommon/directory.h"
-#include "feature/hs/hs_common.h"
-#include "feature/hs/hs_config.h"
-#include "feature/hs_common/replaycache.h"
-#include "feature/keymgt/loadkey.h"
-#include "feature/nodelist/describe.h"
-#include "feature/nodelist/networkstatus.h"
-#include "feature/nodelist/nickname.h"
-#include "feature/nodelist/node_select.h"
-#include "feature/nodelist/nodelist.h"
-#include "feature/nodelist/routerset.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendparse.h"
-#include "feature/rend/rendservice.h"
-#include "feature/stats/predict_ports.h"
-#include "lib/crypt_ops/crypto_dh.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "lib/crypt_ops/crypto_util.h"
-#include "lib/encoding/confline.h"
-#include "lib/net/resolve.h"
-
-#include "core/or/cpath_build_state_st.h"
-#include "core/or/crypt_path_st.h"
-#include "core/or/crypt_path_reference_st.h"
-#include "core/or/edge_connection_st.h"
-#include "core/or/extend_info_st.h"
-#include "feature/hs/hs_opts_st.h"
-#include "feature/nodelist/networkstatus_st.h"
-#include "core/or/origin_circuit_st.h"
-#include "feature/rend/rend_authorized_client_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-#include "feature/nodelist/routerstatus_st.h"
-
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-struct rend_service_t;
-static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro,
-                                            const char *pk_digest);
-static rend_intro_point_t *find_intro_point(origin_circuit_t *circ);
-static rend_intro_point_t *find_expiring_intro_point(
-    struct rend_service_t *service, origin_circuit_t *circ);
-
-static extend_info_t *find_rp_for_intro(
-    const rend_intro_cell_t *intro,
-    char **err_msg_out);
-
-static int intro_point_accepted_intro_count(rend_intro_point_t *intro);
-static int intro_point_should_expire_now(rend_intro_point_t *intro,
-                                         time_t now);
-static int rend_service_derive_key_digests(struct rend_service_t *s);
-static int rend_service_load_keys(struct rend_service_t *s);
-static int rend_service_load_auth_keys(struct rend_service_t *s,
-                                       const char *hfname);
-static struct rend_service_t *rend_service_get_by_pk_digest(
-    const char* digest);
-static struct rend_service_t *rend_service_get_by_service_id(const char *id);
-static const char *rend_service_escaped_dir(
-    const struct rend_service_t *s);
-
-static ssize_t rend_service_parse_intro_for_v0_or_v1(
-    rend_intro_cell_t *intro,
-    const uint8_t *buf,
-    size_t plaintext_len,
-    char **err_msg_out);
-static ssize_t rend_service_parse_intro_for_v2(
-    rend_intro_cell_t *intro,
-    const uint8_t *buf,
-    size_t plaintext_len,
-    char **err_msg_out);
-static ssize_t rend_service_parse_intro_for_v3(
-    rend_intro_cell_t *intro,
-    const uint8_t *buf,
-    size_t plaintext_len,
-    char **err_msg_out);
-
-static int rend_service_check_private_dir(const or_options_t *options,
-                                          const rend_service_t *s,
-                                          int create);
-static const smartlist_t* rend_get_service_list(
-                                  const smartlist_t* substitute_service_list);
-static smartlist_t* rend_get_service_list_mutable(
-                                  smartlist_t* substitute_service_list);
-static int rend_max_intro_circs_per_period(unsigned int n_intro_points_wanted);
-
-/* Hidden service directory file names:
- * new file names should be added to rend_service_add_filenames_to_list()
- * for sandboxing purposes. */
-static const char *private_key_fname = "private_key";
-static const char *hostname_fname = "hostname";
-static const char *client_keys_fname = "client_keys";
-static const char *sos_poison_fname = "onion_service_non_anonymous";
-
-/** A list of rend_service_t's for services run on this OP. */
-static smartlist_t *rend_service_list = NULL;
-/** A list of rend_service_t's for services run on this OP which is used as a
- * staging area before they are put in the main list in order to prune dying
- * service on config reload. */
-static smartlist_t *rend_service_staging_list = NULL;
-
-/** Helper: log the deprecation warning for version 2 only once. */
-static void
-log_once_deprecation_warning(void)
-{
-  static bool logged_once = false;
-  if (!logged_once) {
-    log_warn(LD_REND, "DEPRECATED: Onion service version 2 are deprecated. "
-             "Please use version 3 which is the default now. "
-             "Currently, version 2 is planned to be obsolete in "
-             "the Tor version 0.4.6 stable series.");
-    logged_once = true;
-  }
-}
-/** Macro to make it very explicit that we are warning about deprecation. */
-#define WARN_ONCE_DEPRECATION() log_once_deprecation_warning()
-
-/* Like rend_get_service_list_mutable, but returns a read-only list. */
-static const smartlist_t*
-rend_get_service_list(const smartlist_t* substitute_service_list)
-{
-  /* It is safe to cast away the const here, because
-   * rend_get_service_list_mutable does not actually modify the list */
-  return rend_get_service_list_mutable((smartlist_t*)substitute_service_list);
-}
-
-/* Return a mutable list of hidden services.
- * If substitute_service_list is not NULL, return it.
- * Otherwise, check if the global rend_service_list is non-NULL, and if so,
- * return it.
- * Otherwise, log a BUG message and return NULL.
- * */
-static smartlist_t*
-rend_get_service_list_mutable(smartlist_t* substitute_service_list)
-{
-  if (substitute_service_list) {
-    return substitute_service_list;
-  }
-
-  /* If no special service list is provided, then just use the global one. */
-
-  if (BUG(!rend_service_list)) {
-    /* No global HS list, which is a programmer error. */
-    return NULL;
-  }
-
-  return rend_service_list;
-}
-
-/** Tells if onion service <b>s</b> is ephemeral.
- */
-static unsigned int
-rend_service_is_ephemeral(const struct rend_service_t *s)
-{
-  return (s->directory == NULL);
-}
-
-/** Returns a escaped string representation of the service, <b>s</b>.
- */
-static const char *
-rend_service_escaped_dir(const struct rend_service_t *s)
-{
-  return rend_service_is_ephemeral(s) ? "[EPHEMERAL]" : escaped(s->directory);
-}
-
-/** Return the number of rendezvous services we have configured. */
-int
-rend_num_services(void)
-{
-  if (!rend_service_list)
-    return 0;
-  return smartlist_len(rend_service_list);
-}
-
-/** Helper: free storage held by a single service authorized client entry. */
-void
-rend_authorized_client_free_(rend_authorized_client_t *client)
-{
-  if (!client)
-    return;
-  if (client->client_key)
-    crypto_pk_free(client->client_key);
-  if (client->client_name)
-    memwipe(client->client_name, 0, strlen(client->client_name));
-  tor_free(client->client_name);
-  memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
-  tor_free(client);
-}
-
-/** Helper for strmap_free. */
-static void
-rend_authorized_client_free_void(void *authorized_client)
-{
-  rend_authorized_client_free_(authorized_client);
-}
-
-/** Release the storage held by <b>service</b>.
- */
-STATIC void
-rend_service_free_(rend_service_t *service)
-{
-  if (!service)
-    return;
-
-  tor_free(service->directory);
-  if (service->ports) {
-    SMARTLIST_FOREACH(service->ports, hs_port_config_t*, p,
-                      hs_port_config_free(p));
-    smartlist_free(service->ports);
-  }
-  if (service->private_key)
-    crypto_pk_free(service->private_key);
-  if (service->intro_nodes) {
-    SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro,
-      rend_intro_point_free(intro););
-    smartlist_free(service->intro_nodes);
-  }
-  if (service->expiring_nodes) {
-    SMARTLIST_FOREACH(service->expiring_nodes, rend_intro_point_t *, intro,
-                      rend_intro_point_free(intro););
-    smartlist_free(service->expiring_nodes);
-  }
-
-  rend_service_descriptor_free(service->desc);
-  if (service->clients) {
-    SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, c,
-      rend_authorized_client_free(c););
-    smartlist_free(service->clients);
-  }
-  if (service->accepted_intro_dh_parts) {
-    replaycache_free(service->accepted_intro_dh_parts);
-  }
-  tor_free(service);
-}
-
-/* Release all the storage held in rend_service_staging_list. */
-void
-rend_service_free_staging_list(void)
-{
-  if (rend_service_staging_list) {
-    SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t*, ptr,
-                      rend_service_free(ptr));
-    smartlist_free(rend_service_staging_list);
-    rend_service_staging_list = NULL;
-  }
-}
-
-/** Release all the storage held in both rend_service_list and
- * rend_service_staging_list. */
-void
-rend_service_free_all(void)
-{
-  if (rend_service_list) {
-    SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
-                      rend_service_free(ptr));
-    smartlist_free(rend_service_list);
-    rend_service_list = NULL;
-  }
-  rend_service_free_staging_list();
-}
-
-/* Initialize the subsystem. */
-void
-rend_service_init(void)
-{
-  tor_assert(!rend_service_list);
-  tor_assert(!rend_service_staging_list);
-
-  rend_service_list = smartlist_new();
-  rend_service_staging_list = smartlist_new();
-}
-
-/* Validate a <b>service</b>. Use the <b>service_list</b> to make sure there
- * is no duplicate entry for the given service object. Return 0 if valid else
- * -1 if not.*/
-static int
-rend_validate_service(const smartlist_t *service_list,
-                      const rend_service_t *service)
-{
-  tor_assert(service_list);
-  tor_assert(service);
-
-  if (service->max_streams_per_circuit < 0) {
-    log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max "
-                        "streams per circuit.",
-             rend_service_escaped_dir(service));
-    goto invalid;
-  }
-
-  if (service->max_streams_close_circuit < 0 ||
-      service->max_streams_close_circuit > 1) {
-    log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid "
-                        "max streams handling.",
-             rend_service_escaped_dir(service));
-    goto invalid;
-  }
-
-  if (service->auth_type != REND_NO_AUTH &&
-      (!service->clients || smartlist_len(service->clients) == 0)) {
-    log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but "
-                        "no clients.",
-             rend_service_escaped_dir(service));
-    goto invalid;
-  }
-
-  if (!service->ports || !smartlist_len(service->ports)) {
-    log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.",
-             rend_service_escaped_dir(service));
-    goto invalid;
-  }
-
-  /* Valid. */
-  return 0;
- invalid:
-  return -1;
-}
-
-/** Add it to <b>service_list</b>, or to the global rend_service_list if
- * <b>service_list</b> is NULL.  Return 0 on success.  On failure, free
- * <b>service</b> and return -1. Takes ownership of <b>service</b>. */
-static int
-rend_add_service(smartlist_t *service_list, rend_service_t *service)
-{
-  int i;
-  hs_port_config_t *p;
-
-  tor_assert(service);
-
-  smartlist_t *s_list = rend_get_service_list_mutable(service_list);
-  /* We must have a service list, even if it's a temporary one, so we can
-   * check for duplicate services */
-  if (BUG(!s_list)) {
-    rend_service_free(service);
-    return -1;
-  }
-
-  service->intro_nodes = smartlist_new();
-  service->expiring_nodes = smartlist_new();
-
-  log_debug(LD_REND,"Configuring service with directory %s",
-            rend_service_escaped_dir(service));
-  for (i = 0; i < smartlist_len(service->ports); ++i) {
-    p = smartlist_get(service->ports, i);
-    if (!(p->is_unix_addr)) {
-      log_debug(LD_REND,
-                "Service maps port %d to %s",
-                p->virtual_port,
-                fmt_addrport(&p->real_addr, p->real_port));
-    } else {
-#ifdef HAVE_SYS_UN_H
-      log_debug(LD_REND,
-                "Service maps port %d to socket at \"%s\"",
-                p->virtual_port, p->unix_addr);
-#else
-      log_warn(LD_BUG,
-               "Service maps port %d to an AF_UNIX socket, but we "
-               "have no AF_UNIX support on this platform.  This is "
-               "probably a bug.",
-               p->virtual_port);
-      rend_service_free(service);
-      return -1;
-#endif /* defined(HAVE_SYS_UN_H) */
-    }
-  }
-  /* The service passed all the checks */
-  tor_assert(s_list);
-  smartlist_add(s_list, service);
-
-  /* Notify that our global service list has changed only if this new service
-   * went into our global list. If not, when we move service from the staging
-   * list to the new list, a notify is triggered. */
-  if (s_list == rend_service_list) {
-    hs_service_map_has_changed();
-  }
-  return 0;
-}
-
-/* Copy relevant data from service src to dst while pruning the service lists.
- * This should only be called during the pruning process which takes existing
- * services and copy their data to the newly configured services. The src
- * service replaycache will be set to NULL after this call. */
-static void
-copy_service_on_prunning(rend_service_t *dst, rend_service_t *src)
-{
-  tor_assert(dst);
-  tor_assert(src);
-
-  /* Keep the timestamps for when the content changed and the next upload
-   * time so we can properly upload the descriptor if needed for the new
-   * service object. */
-  dst->desc_is_dirty = src->desc_is_dirty;
-  dst->next_upload_time = src->next_upload_time;
-  /* Move the replaycache to the new object. */
-  dst->accepted_intro_dh_parts = src->accepted_intro_dh_parts;
-  src->accepted_intro_dh_parts = NULL;
-  /* Copy intro point information to destination service. */
-  dst->intro_period_started = src->intro_period_started;
-  dst->n_intro_circuits_launched = src->n_intro_circuits_launched;
-  dst->n_intro_points_wanted = src->n_intro_points_wanted;
-}
-
-/* Helper: Actual implementation of the pruning on reload which we've
- * decoupled in order to make the unit test workeable without ugly hacks.
- * Furthermore, this function does NOT free any memory but will nullify the
- * temporary list pointer whatever happens. */
-STATIC void
-rend_service_prune_list_impl_(void)
-{
-  origin_circuit_t *ocirc = NULL;
-  smartlist_t *surviving_services, *old_service_list, *new_service_list;
-
-  /* When pruning our current service list, we must have a staging list that
-   * contains what we want to check else it's a code flow error. */
-  tor_assert(rend_service_staging_list);
-
-  /* We are about to prune the current list of its dead service so set the
-   * semantic for that list to be the "old" one. */
-  old_service_list = rend_service_list;
-  /* The staging list is now the "new" list so set this semantic. */
-  new_service_list = rend_service_staging_list;
-  /* After this, whatever happens, we'll use our new list. */
-  rend_service_list = new_service_list;
-  /* Finally, nullify the staging list pointer as we don't need it anymore
-   * and it needs to be NULL before the next reload. */
-  rend_service_staging_list = NULL;
-  /* Nothing to prune if we have no service list so stop right away. */
-  if (!old_service_list) {
-    return;
-  }
-
-  /* This contains all _existing_ services that survives the relaod that is
-   * that haven't been removed from the configuration. The difference between
-   * this list and the new service list is that the new list can possibly
-   * contain newly configured service that have no introduction points opened
-   * yet nor key material loaded or generated. */
-  surviving_services = smartlist_new();
-
-  /* Preserve the existing ephemeral services.
-   *
-   * This is the ephemeral service equivalent of the "Copy introduction
-   * points to new services" block, except there's no copy required since
-   * the service structure isn't regenerated.
-   *
-   * After this is done, all ephemeral services will be:
-   *  * Removed from old_service_list, so the equivalent non-ephemeral code
-   *    will not attempt to preserve them.
-   *  * Added to the new_service_list (that previously only had the
-   *    services listed in the configuration).
-   *  * Added to surviving_services, which is the list of services that
-   *    will NOT have their intro point closed.
-   */
-  SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
-    if (rend_service_is_ephemeral(old)) {
-      SMARTLIST_DEL_CURRENT(old_service_list, old);
-      smartlist_add(surviving_services, old);
-      smartlist_add(new_service_list, old);
-    }
-  } SMARTLIST_FOREACH_END(old);
-
-  /* Copy introduction points to new services. This is O(n^2), but it's only
-   * called on reconfigure, so it's ok performance wise. */
-  SMARTLIST_FOREACH_BEGIN(new_service_list, rend_service_t *, new) {
-    SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
-      /* Skip ephemeral services as we only want to copy introduction points
-       * from current services to newly configured one that already exists.
-       * The same directory means it's the same service. */
-      if (rend_service_is_ephemeral(new) || rend_service_is_ephemeral(old) ||
-          strcmp(old->directory, new->directory)) {
-        continue;
-      }
-      smartlist_add_all(new->intro_nodes, old->intro_nodes);
-      smartlist_clear(old->intro_nodes);
-      smartlist_add_all(new->expiring_nodes, old->expiring_nodes);
-      smartlist_clear(old->expiring_nodes);
-
-      /* Copy needed information from old to new. */
-      copy_service_on_prunning(new, old);
-
-      /* This regular service will survive the closing IPs step after. */
-      smartlist_add(surviving_services, old);
-      break;
-    } SMARTLIST_FOREACH_END(old);
-  } SMARTLIST_FOREACH_END(new);
-
-  /* For every service introduction circuit we can find, see if we have a
-   * matching surviving configured service. If not, close the circuit. */
-  while ((ocirc = circuit_get_next_intro_circ(ocirc, false))) {
-    int keep_it = 0;
-    if (ocirc->rend_data == NULL) {
-      /* This is a v3 circuit, ignore it. */
-      continue;
-    }
-    SMARTLIST_FOREACH_BEGIN(surviving_services, const rend_service_t *, s) {
-      if (rend_circuit_pk_digest_eq(ocirc, (uint8_t *) s->pk_digest)) {
-        /* Keep this circuit as we have a matching configured service. */
-        keep_it = 1;
-        break;
-      }
-    } SMARTLIST_FOREACH_END(s);
-    if (keep_it) {
-      continue;
-    }
-    log_info(LD_REND, "Closing intro point %s for service %s.",
-             safe_str_client(extend_info_describe(
-                                        ocirc->build_state->chosen_exit)),
-             safe_str_client(rend_data_get_address(ocirc->rend_data)));
-    /* Reason is FINISHED because service has been removed and thus the
-     * circuit is considered old/unneeded. */
-    circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
-  }
-  smartlist_free(surviving_services);
-  /* Notify that our global service list has changed. */
-  hs_service_map_has_changed();
-}
-
-/* Try to prune our main service list using the temporary one that we just
- * loaded and parsed successfully. The pruning process decides which onion
- * services to keep and which to discard after a reload. */
-void
-rend_service_prune_list(void)
-{
-  smartlist_t *old_service_list = rend_service_list;
-
-  if (!rend_service_staging_list) {
-    rend_service_staging_list = smartlist_new();
-  }
-
-  rend_service_prune_list_impl_();
-  if (old_service_list) {
-    /* Every remaining service in the old list have been removed from the
-     * configuration so clean them up safely. */
-    SMARTLIST_FOREACH(old_service_list, rend_service_t *, s,
-                      rend_service_free(s));
-    smartlist_free(old_service_list);
-  }
-}
-
-/* Copy all the relevant data that the hs_service object contains over to the
- * rend_service_t object. The reason to do so is because when configuring a
- * service, we go through a generic handler that creates an hs_service_t
- * object which so we have to copy the parsed values to a rend service object
- * which is version 2 specific. */
-static void
-service_config_shadow_copy(rend_service_t *service,
-                           hs_service_config_t *config)
-{
-  tor_assert(service);
-  tor_assert(config);
-
-  service->directory = tor_strdup(config->directory_path);
-  service->dir_group_readable = config->dir_group_readable;
-  service->allow_unknown_ports = config->allow_unknown_ports;
-  /* This value can't go above HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT (65535)
-   * if the code flow is right so this cast is safe. But just in case, we'll
-   * check it. */
-  service->max_streams_per_circuit = (int) config->max_streams_per_rdv_circuit;
-  if (BUG(config->max_streams_per_rdv_circuit >
-          HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT)) {
-    service->max_streams_per_circuit = HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT;
-  }
-  service->max_streams_close_circuit = config->max_streams_close_circuit;
-  service->n_intro_points_wanted = config->num_intro_points;
-  /* Switching ownership of the ports to the rend service object. */
-  smartlist_add_all(service->ports, config->ports);
-  smartlist_free(config->ports);
-  config->ports = NULL;
-}
-
-/* Parse the hidden service configuration from <b>hs_opts</b> using the
- * already configured generic service configuration in <b>config</b>. This
- * function will translate the config object to a rend_service_t and add it to
- * the temporary list if valid. If <b>validate_only</b> is set, parse, warn
- * and return as normal but don't actually add the service to the list. */
-int
-rend_config_service(const hs_opts_t *hs_opts,
-                    const or_options_t *options,
-                    hs_service_config_t *config)
-{
-  rend_service_t *service = NULL;
-
-  tor_assert(options);
-  tor_assert(hs_opts);
-  tor_assert(config);
-
-  /* We are about to configure a version 2 service. Warn of deprecation. */
-  WARN_ONCE_DEPRECATION();
-
-  /* Use the staging service list so that we can check then do the pruning
-   * process using the main list at the end. */
-  if (rend_service_staging_list == NULL) {
-    rend_service_staging_list = smartlist_new();
-  }
-
-  /* Initialize service. */
-  service = tor_malloc_zero(sizeof(rend_service_t));
-  service->intro_period_started = time(NULL);
-  service->ports = smartlist_new();
-  /* From the hs_service object which has been used to load the generic
-   * options, we'll copy over the useful data to the rend_service_t object. */
-  service_config_shadow_copy(service, config);
-
-  /* Number of introduction points. */
-  if (hs_opts->HiddenServiceNumIntroductionPoints > NUM_INTRO_POINTS_MAX) {
-    log_warn(LD_CONFIG, "HiddenServiceNumIntroductionPoints must be "
-             "between 0 and %d, not %d.",
-             NUM_INTRO_POINTS_MAX,
-             hs_opts->HiddenServiceNumIntroductionPoints);
-    goto err;
-  }
-  service->n_intro_points_wanted = hs_opts->HiddenServiceNumIntroductionPoints;
-  log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s",
-           service->n_intro_points_wanted, escaped(service->directory));
-
-  /* Client authorization */
-  if (hs_opts->HiddenServiceAuthorizeClient) {
-    /* Parse auth type and comma-separated list of client names and add a
-     * rend_authorized_client_t for each client to the service's list
-     * of authorized clients. */
-    smartlist_t *type_names_split, *clients;
-    const char *authname;
-    type_names_split = smartlist_new();
-    smartlist_split_string(type_names_split,
-                           hs_opts->HiddenServiceAuthorizeClient, " ", 0, 2);
-    if (smartlist_len(type_names_split) < 1) {
-      log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
-               "should have been prevented when parsing the "
-               "configuration.");
-      smartlist_free(type_names_split);
-      goto err;
-    }
-    authname = smartlist_get(type_names_split, 0);
-    if (!strcasecmp(authname, "basic")) {
-      service->auth_type = REND_BASIC_AUTH;
-    } else if (!strcasecmp(authname, "stealth")) {
-      service->auth_type = REND_STEALTH_AUTH;
-    } else {
-      log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
-               "unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
-               "are recognized.",
-               (char *) smartlist_get(type_names_split, 0));
-      SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
-      smartlist_free(type_names_split);
-      goto err;
-    }
-    service->clients = smartlist_new();
-    if (smartlist_len(type_names_split) < 2) {
-      log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
-               "auth-type '%s', but no client names.",
-               service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
-      SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
-      smartlist_free(type_names_split);
-      goto err;
-    }
-    clients = smartlist_new();
-    smartlist_split_string(clients, smartlist_get(type_names_split, 1),
-                             ",", SPLIT_SKIP_SPACE, 0);
-    SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
-    smartlist_free(type_names_split);
-    /* Remove duplicate client names. */
-    {
-      int num_clients = smartlist_len(clients);
-      smartlist_sort_strings(clients);
-      smartlist_uniq_strings(clients);
-      if (smartlist_len(clients) < num_clients) {
-        log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
-                 "duplicate client name(s); removing.",
-                 num_clients - smartlist_len(clients));
-      }
-    }
-    SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name) {
-      rend_authorized_client_t *client;
-      if (!rend_valid_client_name(client_name)) {
-        log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
-                            "illegal client name: '%s'. Names must be "
-                            "between 1 and %d characters and contain "
-                            "only [A-Za-z0-9+_-].",
-                 client_name, REND_CLIENTNAME_MAX_LEN);
-        SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
-        smartlist_free(clients);
-        goto err;
-      }
-      client = tor_malloc_zero(sizeof(rend_authorized_client_t));
-      client->client_name = tor_strdup(client_name);
-      smartlist_add(service->clients, client);
-      log_debug(LD_REND, "Adding client name '%s'", client_name);
-    } SMARTLIST_FOREACH_END(client_name);
-    SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
-    smartlist_free(clients);
-    /* Ensure maximum number of clients. */
-    if ((service->auth_type == REND_BASIC_AUTH &&
-         smartlist_len(service->clients) > 512) ||
-        (service->auth_type == REND_STEALTH_AUTH &&
-         smartlist_len(service->clients) > 16)) {
-      log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
-                          "client authorization entries, but only a "
-                          "maximum of %d entries is allowed for "
-                          "authorization type '%s'.",
-               smartlist_len(service->clients),
-               service->auth_type == REND_BASIC_AUTH ? 512 : 16,
-               service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
-      goto err;
-    }
-  }
-
-  /* Validate the service just parsed. */
-  if (rend_validate_service(rend_service_staging_list, service) < 0) {
-    /* Service is in the staging list so don't try to free it. */
-    goto err;
-  }
-
-  /* Add it to the temporary list which we will use to prune our current
-   * list if any after configuring all services. */
-  if (rend_add_service(rend_service_staging_list, service) < 0) {
-    /* The object has been freed on error already. */
-    service = NULL;
-    goto err;
-  }
-
-  return 0;
- err:
-  rend_service_free(service);
-  return -1;
-}
-
-/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, using
- * client authorization <b>auth_type</b> and an optional list of
- * rend_authorized_client_t in <b>auth_clients</b>, with
- * <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit,
- * and circuit closure on max streams being exceeded set by
- * <b>max_streams_close_circuit</b>.
- *
- * Ownership of pk, ports, and auth_clients is passed to this routine.
- * Regardless of success/failure, callers should not touch these values
- * after calling this routine, and may assume that correct cleanup has
- * been done on failure.
- *
- * Return an appropriate hs_service_add_ephemeral_status_t.
- */
-hs_service_add_ephemeral_status_t
-rend_service_add_ephemeral(crypto_pk_t *pk,
-                           smartlist_t *ports,
-                           int max_streams_per_circuit,
-                           int max_streams_close_circuit,
-                           rend_auth_type_t auth_type,
-                           smartlist_t *auth_clients,
-                           char **service_id_out)
-{
-  *service_id_out = NULL;
-  /* Allocate the service structure, and initialize the key, and key derived
-   * parameters.
-   */
-  rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t));
-  s->directory = NULL; /* This indicates the service is ephemeral. */
-  s->private_key = pk;
-  s->auth_type = auth_type;
-  s->clients = auth_clients;
-  s->ports = ports;
-  s->intro_period_started = time(NULL);
-  s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
-  s->max_streams_per_circuit = max_streams_per_circuit;
-  s->max_streams_close_circuit = max_streams_close_circuit;
-  if (rend_service_derive_key_digests(s) < 0) {
-    rend_service_free(s);
-    return RSAE_BADPRIVKEY;
-  }
-
-  if (!s->ports || smartlist_len(s->ports) == 0) {
-    log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified.");
-    rend_service_free(s);
-    return RSAE_BADVIRTPORT;
-  }
-  if (s->auth_type != REND_NO_AUTH &&
-      (!s->clients || smartlist_len(s->clients) == 0)) {
-    log_warn(LD_CONFIG, "At least one authorized client must be specified.");
-    rend_service_free(s);
-    return RSAE_BADAUTH;
-  }
-
-  /* Enforcing pk/id uniqueness should be done by rend_service_load_keys(), but
-   * it's not, see #14828.
-   */
-  if (rend_service_get_by_pk_digest(s->pk_digest)) {
-    log_warn(LD_CONFIG, "Onion Service private key collides with an "
-             "existing service.");
-    rend_service_free(s);
-    return RSAE_ADDREXISTS;
-  }
-  if (rend_service_get_by_service_id(s->service_id)) {
-    log_warn(LD_CONFIG, "Onion Service id collides with an existing service.");
-    rend_service_free(s);
-    return RSAE_ADDREXISTS;
-  }
-
-  /* Initialize the service. */
-  if (rend_add_service(NULL, s)) {
-    return RSAE_INTERNAL;
-  }
-  *service_id_out = tor_strdup(s->service_id);
-
-  log_debug(LD_CONFIG, "Added ephemeral Onion Service: %s", s->service_id);
-  return RSAE_OKAY;
-}
-
-/** Remove the ephemeral service <b>service_id</b> if possible.  Returns 0 on
- * success, and -1 on failure.
- */
-int
-rend_service_del_ephemeral(const char *service_id)
-{
-  rend_service_t *s;
-  if (!rend_valid_v2_service_id(service_id)) {
-    log_warn(LD_CONFIG, "Requested malformed Onion Service id for removal.");
-    return -1;
-  }
-  if ((s = rend_service_get_by_service_id(service_id)) == NULL) {
-    log_warn(LD_CONFIG, "Requested non-existent Onion Service id for "
-             "removal.");
-    return -1;
-  }
-  if (!rend_service_is_ephemeral(s)) {
-    log_warn(LD_CONFIG, "Requested non-ephemeral Onion Service for removal.");
-    return -1;
-  }
-
-  /* Kill the intro point circuit for the Onion Service, and remove it from
-   * the list.  Closing existing connections is the application's problem.
-   *
-   * XXX: As with the comment in rend_config_services(), a nice abstraction
-   * would be ideal here, but for now just duplicate the code.
-   */
-  SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
-    if (!circ->marked_for_close &&
-        (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
-         circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
-      origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
-      if (oc->rend_data == NULL ||
-          !rend_circuit_pk_digest_eq(oc, (uint8_t *) s->pk_digest)) {
-        continue;
-      }
-      log_debug(LD_REND, "Closing intro point %s for service %s.",
-                safe_str_client(extend_info_describe(
-                                          oc->build_state->chosen_exit)),
-                rend_data_get_address(oc->rend_data));
-      circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
-    }
-  } SMARTLIST_FOREACH_END(circ);
-  smartlist_remove(rend_service_list, s);
-  /* Notify that we just removed a service from our global list. */
-  hs_service_map_has_changed();
-  rend_service_free(s);
-
-  log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id);
-
-  return 0;
-}
-
-/* There can be 1 second's delay due to second_elapsed_callback, and perhaps
- * another few seconds due to blocking calls. */
-#define INTRO_CIRC_RETRY_PERIOD_SLOP 10
-
-/** Log information about the intro point creation rate and current intro
- * points for service, upgrading the log level from min_severity to warn if
- * we have stopped launching new intro point circuits. */
-static void
-rend_log_intro_limit(const rend_service_t *service, int min_severity)
-{
-  int exceeded_limit = (service->n_intro_circuits_launched >=
-                        rend_max_intro_circs_per_period(
-                                              service->n_intro_points_wanted));
-  int severity = min_severity;
-  /* We stopped creating circuits */
-  if (exceeded_limit) {
-    severity = LOG_WARN;
-  }
-  time_t intro_period_elapsed = time(NULL) - service->intro_period_started;
-  tor_assert_nonfatal(intro_period_elapsed >= 0);
-  {
-    char *msg;
-    static ratelim_t rlimit = RATELIM_INIT(INTRO_CIRC_RETRY_PERIOD);
-    if ((msg = rate_limit_log(&rlimit, approx_time()))) {
-      log_fn(severity, LD_REND,
-             "Hidden service %s %s %d intro points in the last %d seconds. "
-             "Intro circuit launches are limited to %d per %d seconds.%s",
-             service->service_id,
-             exceeded_limit ? "exceeded launch limit with" : "launched",
-             service->n_intro_circuits_launched,
-             (int)intro_period_elapsed,
-             rend_max_intro_circs_per_period(service->n_intro_points_wanted),
-             INTRO_CIRC_RETRY_PERIOD, msg);
-      rend_service_dump_stats(severity);
-      tor_free(msg);
-    }
-  }
-}
-
-/** Replace the old value of <b>service</b>-\>desc with one that reflects
- * the other fields in service.
- */
-static void
-rend_service_update_descriptor(rend_service_t *service)
-{
-  rend_service_descriptor_t *d;
-  int i;
-
-  rend_service_descriptor_free(service->desc);
-  service->desc = NULL;
-
-  d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
-  d->pk = crypto_pk_dup_key(service->private_key);
-  d->timestamp = time(NULL);
-  d->timestamp -= d->timestamp % 3600; /* Round down to nearest hour */
-  d->intro_nodes = smartlist_new();
-  /* Support intro protocols 2 and 3. */
-  d->protocols = (1 << 2) + (1 << 3);
-
-  for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
-    rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
-    rend_intro_point_t *intro_desc;
-
-    /* This intro point won't be listed in the descriptor... */
-    intro_svc->listed_in_last_desc = 0;
-
-    /* circuit_established is set in rend_service_intro_established(), and
-     * checked every second in rend_consider_services_intro_points(), so it's
-     * safe to use it here */
-    if (!intro_svc->circuit_established) {
-      continue;
-    }
-
-    /* ...unless this intro point is listed in the descriptor. */
-    intro_svc->listed_in_last_desc = 1;
-
-    /* We have an entirely established intro circuit.  Publish it in
-     * our descriptor. */
-    intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t));
-    intro_desc->extend_info = extend_info_dup(intro_svc->extend_info);
-    if (intro_svc->intro_key)
-      intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key);
-    smartlist_add(d->intro_nodes, intro_desc);
-
-    if (intro_svc->time_published == -1) {
-      /* We are publishing this intro point in a descriptor for the
-       * first time -- note the current time in the service's copy of
-       * the intro point. */
-      intro_svc->time_published = time(NULL);
-    }
-  }
-
-  /* Check that we have the right number of intro points */
-  unsigned int have_intro = (unsigned int)smartlist_len(d->intro_nodes);
-  if (have_intro != service->n_intro_points_wanted) {
-    int severity;
-    /* Getting less than we wanted or more than we're allowed is serious */
-    if (have_intro < service->n_intro_points_wanted ||
-        have_intro > NUM_INTRO_POINTS_MAX) {
-      severity = LOG_WARN;
-    } else {
-      /* Getting more than we wanted is weird, but less of a problem */
-      severity = LOG_NOTICE;
-    }
-    log_fn(severity, LD_REND, "Hidden service %s wanted %d intro points, but "
-           "descriptor was updated with %d instead.",
-           service->service_id,
-           service->n_intro_points_wanted, have_intro);
-    /* Now log an informative message about how we might have got here. */
-    rend_log_intro_limit(service, severity);
-  }
-}
-
-/* Allocate and return a string containing the path to file_name in
- * service->directory. Asserts that service has a directory.
- * This function will never return NULL.
- * The caller must free this path. */
-static char *
-rend_service_path(const rend_service_t *service, const char *file_name)
-{
-  tor_assert(service->directory);
-  return hs_path_from_filename(service->directory, file_name);
-}
-
-/* Allocate and return a string containing the path to the single onion
- * service poison file in service->directory. Asserts that service has a
- * directory.
- * The caller must free this path. */
-STATIC char *
-rend_service_sos_poison_path(const rend_service_t *service)
-{
-  return rend_service_path(service, sos_poison_fname);
-}
-
-/** Return True if hidden services <b>service</b> has been poisoned by single
- * onion mode. */
-static int
-service_is_single_onion_poisoned(const rend_service_t *service)
-{
-  char *poison_fname = NULL;
-  file_status_t fstatus;
-
-  /* Passing a NULL service is a bug */
-  if (BUG(!service)) {
-    return 0;
-  }
-
-  if (rend_service_is_ephemeral(service)) {
-    return 0;
-  }
-
-  poison_fname = rend_service_sos_poison_path(service);
-
-  fstatus = file_status(poison_fname);
-  tor_free(poison_fname);
-
-  /* If this fname is occupied, the hidden service has been poisoned.
-   * fstatus can be FN_ERROR if the service directory does not exist, in that
-   * case, there is obviously no private key. */
-  if (fstatus == FN_FILE || fstatus == FN_EMPTY) {
-    return 1;
-  }
-
-  return 0;
-}
-
-/* Return 1 if the private key file for service exists and has a non-zero size,
- * and 0 otherwise. */
-static int
-rend_service_private_key_exists(const rend_service_t *service)
-{
-  char *private_key_path = rend_service_path(service, private_key_fname);
-  const file_status_t private_key_status = file_status(private_key_path);
-  tor_free(private_key_path);
-  /* Only non-empty regular private key files could have been used before.
-   * fstatus can be FN_ERROR if the service directory does not exist, in that
-   * case, there is obviously no private key. */
-  return private_key_status == FN_FILE;
-}
-
-/** Check the single onion service poison state of the directory for s:
- * - If the service is poisoned, and we are in Single Onion Mode,
- *   return 0,
- * - If the service is not poisoned, and we are not in Single Onion Mode,
- *   return 0,
- * - Otherwise, the poison state is invalid: the service was created in one
- *   mode, and is being used in the other, return -1.
- * Hidden service directories without keys are always considered consistent.
- * They will be poisoned after their directory is created (if needed). */
-STATIC int
-rend_service_verify_single_onion_poison(const rend_service_t* s,
-                                        const or_options_t* options)
-{
-  /* Passing a NULL service is a bug */
-  if (BUG(!s)) {
-    return -1;
-  }
-
-  /* Ephemeral services are checked at ADD_ONION time */
-  if (BUG(rend_service_is_ephemeral(s))) {
-    return -1;
-  }
-
-  /* Service is expected to have a directory */
-  if (BUG(!s->directory)) {
-    return -1;
-  }
-
-  /* Services without keys are always ok - their keys will only ever be used
-   * in the current mode */
-  if (!rend_service_private_key_exists(s)) {
-    return 0;
-  }
-
-  /* The key has been used before in a different mode */
-  if (service_is_single_onion_poisoned(s) !=
-      rend_service_non_anonymous_mode_enabled(options)) {
-    return -1;
-  }
-
-  /* The key exists and is consistent with the current mode */
-  return 0;
-}
-
-/*** Helper for rend_service_poison_new_single_onion_dir(). Add a file to
- * the hidden service directory for s that marks it as a single onion service.
- * Tor must be in single onion mode before calling this function, and the
- * service directory must already have been created.
- * Returns 0 when a directory is successfully poisoned, or if it is already
- * poisoned. Returns -1 on a failure to read the directory or write the poison
- * file, or if there is an existing private key file in the directory. (The
- * service should have been poisoned when the key was created.) */
-static int
-poison_new_single_onion_hidden_service_dir_impl(const rend_service_t *service,
-                                                const or_options_t* options)
-{
-  /* Passing a NULL service is a bug */
-  if (BUG(!service)) {
-    return -1;
-  }
-
-  /* We must only poison directories if we're in Single Onion mode */
-  tor_assert(rend_service_non_anonymous_mode_enabled(options));
-
-  int fd;
-  int retval = -1;
-  char *poison_fname = NULL;
-
-  if (rend_service_is_ephemeral(service)) {
-    log_info(LD_REND, "Ephemeral HS started in non-anonymous mode.");
-    return 0;
-  }
-
-  /* Make sure we're only poisoning new hidden service directories */
-  if (rend_service_private_key_exists(service)) {
-    log_warn(LD_BUG, "Tried to single onion poison a service directory after "
-             "the private key was created.");
-    return -1;
-  }
-
-  /* Make sure the directory was created before calling this function. */
-  if (BUG(hs_check_service_private_dir(options->User, service->directory,
-                                       service->dir_group_readable, 0) < 0))
-    return -1;
-
-  poison_fname = rend_service_sos_poison_path(service);
-
-  switch (file_status(poison_fname)) {
-  case FN_DIR:
-  case FN_ERROR:
-    log_warn(LD_FS, "Can't read single onion poison file \"%s\"",
-             poison_fname);
-    goto done;
-  case FN_FILE: /* single onion poison file already exists. NOP. */
-  case FN_EMPTY: /* single onion poison file already exists. NOP. */
-    log_debug(LD_FS, "Tried to re-poison a single onion poisoned file \"%s\"",
-              poison_fname);
-    break;
-  case FN_NOENT:
-    fd = tor_open_cloexec(poison_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
-    if (fd < 0) {
-      log_warn(LD_FS, "Could not create single onion poison file %s",
-               poison_fname);
-      goto done;
-    }
-    close(fd);
-    break;
-  default:
-    tor_assert(0);
-  }
-
-  retval = 0;
-
- done:
-  tor_free(poison_fname);
-
-  return retval;
-}
-
-/** We just got launched in Single Onion Mode. That's a non-anonymous mode for
- * hidden services. If s is new, we should mark its hidden service
- * directory appropriately so that it is never launched as a location-private
- * hidden service. (New directories don't have private key files.)
- * Return 0 on success, -1 on fail. */
-STATIC int
-rend_service_poison_new_single_onion_dir(const rend_service_t *s,
-                                         const or_options_t* options)
-{
-  /* Passing a NULL service is a bug */
-  if (BUG(!s)) {
-    return -1;
-  }
-
-  /* We must only poison directories if we're in Single Onion mode */
-  tor_assert(rend_service_non_anonymous_mode_enabled(options));
-
-  /* Ephemeral services aren't allowed in non-anonymous mode */
-  if (BUG(rend_service_is_ephemeral(s))) {
-    return -1;
-  }
-
-  /* Service is expected to have a directory */
-  if (BUG(!s->directory)) {
-    return -1;
-  }
-
-  if (!rend_service_private_key_exists(s)) {
-    if (poison_new_single_onion_hidden_service_dir_impl(s, options)
-        < 0) {
-      return -1;
-    }
-  }
-
-  return 0;
-}
-
-/* Return true iff the given service identity key is present on disk. This is
- * used to try to learn the service version during configuration time. */
-int
-rend_service_key_on_disk(const char *directory_path)
-{
-  int ret = 0;
-  char *fname;
-  crypto_pk_t *pk = NULL;
-
-  tor_assert(directory_path);
-
-  /* Load key */
-  fname = hs_path_from_filename(directory_path, private_key_fname);
-  pk = init_key_from_file(fname, 0, LOG_DEBUG, NULL);
-  if (pk) {
-    ret = 1;
-  }
-
-  crypto_pk_free(pk);
-  tor_free(fname);
-  return ret;
-}
-
-/** Load and/or generate private keys for all hidden services, possibly
- * including keys for client authorization.
- * If a <b>service_list</b> is provided, treat it as the list of hidden
- * services (used in unittests). Otherwise, require that rend_service_list is
- * not NULL.
- * Return 0 on success, -1 on failure. */
-int
-rend_service_load_all_keys(const smartlist_t *service_list)
-{
-  /* Use service_list for unit tests */
-  const smartlist_t *s_list = rend_get_service_list(service_list);
-  if (BUG(!s_list)) {
-    return -1;
-  }
-
-  SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) {
-    if (s->private_key)
-      continue;
-    log_info(LD_REND, "Loading hidden-service keys from %s",
-             rend_service_escaped_dir(s));
-
-    if (rend_service_load_keys(s) < 0)
-      return -1;
-  } SMARTLIST_FOREACH_END(s);
-
-  return 0;
-}
-
-/** Add to <b>lst</b> every filename used by <b>s</b>. */
-static void
-rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s)
-{
-  tor_assert(lst);
-  tor_assert(s);
-  tor_assert(s->directory);
-  smartlist_add(lst, rend_service_path(s, private_key_fname));
-  smartlist_add(lst, rend_service_path(s, hostname_fname));
-  smartlist_add(lst, rend_service_path(s, client_keys_fname));
-  smartlist_add(lst, rend_service_sos_poison_path(s));
-}
-
-/** Add to <b>open_lst</b> every filename used by a configured hidden service,
- * and to <b>stat_lst</b> every directory used by a configured hidden
- * service */
-void
-rend_services_add_filenames_to_lists(smartlist_t *open_lst,
-                                     smartlist_t *stat_lst)
-{
-  if (!rend_service_list)
-    return;
-  SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
-    if (!rend_service_is_ephemeral(s)) {
-      rend_service_add_filenames_to_list(open_lst, s);
-      smartlist_add_strdup(stat_lst, s->directory);
-    }
-  } SMARTLIST_FOREACH_END(s);
-}
-
-/** Derive all rend_service_t internal material based on the service's key.
- * Returns 0 on success, -1 on failure.
- */
-static int
-rend_service_derive_key_digests(struct rend_service_t *s)
-{
-  if (rend_get_service_id(s->private_key, s->service_id)<0) {
-    log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
-    return -1;
-  }
-  if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
-    log_warn(LD_BUG, "Couldn't compute hash of public key.");
-    return -1;
-  }
-
-  return 0;
-}
-
-/** Make sure that the directory for <b>s</b> is private, using the config in
- * <b>options</b>.
- * If <b>create</b> is true:
- *  - if the directory exists, change permissions if needed,
- *  - if the directory does not exist, create it with the correct permissions.
- * If <b>create</b> is false:
- *  - if the directory exists, check permissions,
- *  - if the directory does not exist, check if we think we can create it.
- * Return 0 on success, -1 on failure. */
-static int
-rend_service_check_private_dir(const or_options_t *options,
-                               const rend_service_t *s,
-                               int create)
-{
-  /* Passing a NULL service is a bug */
-  if (BUG(!s)) {
-    return -1;
-  }
-
-  /* Check/create directory */
-  if (hs_check_service_private_dir(options->User, s->directory,
-                                   s->dir_group_readable, create) < 0) {
-    return -1;
-  }
-
-  /* Check if the hidden service key exists, and was created in a different
-   * single onion service mode, and refuse to launch if it has.
-   * This is safe to call even when create is false, as it ignores missing
-   * keys and directories: they are always valid.
-   */
-  if (rend_service_verify_single_onion_poison(s, options) < 0) {
-    /* We can't use s->service_id here, as the key may not have been loaded */
-    log_warn(LD_GENERAL, "We are configured with "
-             "HiddenServiceNonAnonymousMode %d, but the hidden "
-             "service key in directory %s was created in %s mode. "
-             "This is not allowed.",
-             rend_service_non_anonymous_mode_enabled(options) ? 1 : 0,
-             rend_service_escaped_dir(s),
-             rend_service_non_anonymous_mode_enabled(options) ?
-             "an anonymous" : "a non-anonymous"
-             );
-    return -1;
-  }
-
-  /* Poison new single onion directories immediately after they are created,
-   * so that we never accidentally launch non-anonymous hidden services
-   * thinking they are anonymous. Any keys created later will end up with the
-   * correct poisoning state.
-   */
-  if (create && rend_service_non_anonymous_mode_enabled(options)) {
-    static int logged_warning = 0;
-
-    if (rend_service_poison_new_single_onion_dir(s, options) < 0) {
-      log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous"
-               ".");
-      return -1;
-    }
-
-    if (!logged_warning) {
-      /* The keys for these services are linked to the server IP address */
-      log_notice(LD_REND, "The configured onion service directories have been "
-                 "used in single onion mode. They can not be used for "
-                 "anonymous hidden services.");
-      logged_warning = 1;
-    }
-  }
-
-  return 0;
-}
-
-/** Load and/or generate private keys for the hidden service <b>s</b>,
- * possibly including keys for client authorization.  Return 0 on success, -1
- * on failure. */
-static int
-rend_service_load_keys(rend_service_t *s)
-{
-  char *fname = NULL;
-  char buf[128];
-
-  /* Create the directory if needed which will also poison it in case of
-   * single onion service. */
-  if (rend_service_check_private_dir(get_options(), s, 1) < 0)
-    goto err;
-
-  /* Load key */
-  fname = rend_service_path(s, private_key_fname);
-  s->private_key = init_key_from_file(fname, 1, LOG_ERR, NULL);
-
-  if (!s->private_key)
-    goto err;
-
-  if (rend_service_derive_key_digests(s) < 0)
-    goto err;
-
-  tor_free(fname);
-  /* Create service file */
-  fname = rend_service_path(s, hostname_fname);
-
-  tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
-  if (write_str_to_file_if_not_equal(fname, buf)) {
-    log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
-    goto err;
-  }
-#ifndef _WIN32
-  if (s->dir_group_readable) {
-    /* Also verify hostname file created with group read. */
-    if (chmod(fname, 0640))
-      log_warn(LD_FS,"Unable to make hidden hostname file %s group-readable.",
-               fname);
-  }
-#endif /* !defined(_WIN32) */
-
-  /* If client authorization is configured, load or generate keys. */
-  if (s->auth_type != REND_NO_AUTH) {
-    if (rend_service_load_auth_keys(s, fname) < 0) {
-      goto err;
-    }
-  }
-
-  int r = 0;
-  goto done;
- err:
-  r = -1;
- done:
-  memwipe(buf, 0, sizeof(buf));
-  tor_free(fname);
-  return r;
-}
-
-/** Load and/or generate client authorization keys for the hidden service
- * <b>s</b>, which stores its hostname in <b>hfname</b>.  Return 0 on success,
- * -1 on failure. */
-static int
-rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
-{
-  int r = 0;
-  char *cfname = NULL;
-  char *client_keys_str = NULL;
-  strmap_t *parsed_clients = strmap_new();
-  FILE *cfile, *hfile;
-  open_file_t *open_cfile = NULL, *open_hfile = NULL;
-  char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
-  char service_id[16+1];
-  char buf[1500];
-
-  /* Load client keys and descriptor cookies, if available. */
-  cfname = rend_service_path(s, client_keys_fname);
-  client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
-  if (client_keys_str) {
-    if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
-      log_warn(LD_CONFIG, "Previously stored client_keys file could not "
-               "be parsed.");
-      goto err;
-    } else {
-      log_info(LD_CONFIG, "Parsed %d previously stored client entries.",
-               strmap_size(parsed_clients));
-    }
-  }
-
-  /* Prepare client_keys and hostname files. */
-  if (!(cfile = start_writing_to_stdio_file(cfname,
-                                            OPEN_FLAGS_REPLACE | O_TEXT,
-                                            0600, &open_cfile))) {
-    log_warn(LD_CONFIG, "Could not open client_keys file %s",
-             escaped(cfname));
-    goto err;
-  }
-
-  if (!(hfile = start_writing_to_stdio_file(hfname,
-                                            OPEN_FLAGS_REPLACE | O_TEXT,
-                                            0600, &open_hfile))) {
-    log_warn(LD_CONFIG, "Could not open hostname file %s", escaped(hfname));
-    goto err;
-  }
-
-  /* Either use loaded keys for configured clients or generate new
-   * ones if a client is new. */
-  SMARTLIST_FOREACH_BEGIN(s->clients, rend_authorized_client_t *, client) {
-    rend_authorized_client_t *parsed =
-      strmap_get(parsed_clients, client->client_name);
-    int written;
-    size_t len;
-    /* Copy descriptor cookie from parsed entry or create new one. */
-    if (parsed) {
-      memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
-             REND_DESC_COOKIE_LEN);
-    } else {
-      crypto_rand((char *) client->descriptor_cookie, REND_DESC_COOKIE_LEN);
-    }
-    /* For compatibility with older tor clients, this does not
-     * truncate the padding characters, unlike rend_auth_encode_cookie.  */
-    if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
-                      (char *) client->descriptor_cookie,
-                      REND_DESC_COOKIE_LEN, 0) < 0) {
-      log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
-      goto err;
-    }
-    /* Copy client key from parsed entry or create new one if required. */
-    if (parsed && parsed->client_key) {
-      client->client_key = crypto_pk_dup_key(parsed->client_key);
-    } else if (s->auth_type == REND_STEALTH_AUTH) {
-      /* Create private key for client. */
-      crypto_pk_t *prkey = NULL;
-      if (!(prkey = crypto_pk_new())) {
-        log_warn(LD_BUG,"Error constructing client key");
-        goto err;
-      }
-      if (crypto_pk_generate_key(prkey)) {
-        log_warn(LD_BUG,"Error generating client key");
-        crypto_pk_free(prkey);
-        goto err;
-      }
-      if (! crypto_pk_is_valid_private_key(prkey)) {
-        log_warn(LD_BUG,"Generated client key seems invalid");
-        crypto_pk_free(prkey);
-        goto err;
-      }
-      client->client_key = prkey;
-    }
-    /* Add entry to client_keys file. */
-    written = tor_snprintf(buf, sizeof(buf),
-                           "client-name %s\ndescriptor-cookie %s\n",
-                           client->client_name, desc_cook_out);
-    if (written < 0) {
-      log_warn(LD_BUG, "Could not write client entry.");
-      goto err;
-    }
-    if (client->client_key) {
-      char *client_key_out = NULL;
-      if (crypto_pk_write_private_key_to_string(client->client_key,
-                                                &client_key_out, &len) != 0) {
-        log_warn(LD_BUG, "Internal error: "
-                 "crypto_pk_write_private_key_to_string() failed.");
-        goto err;
-      }
-      if (rend_get_service_id(client->client_key, service_id)<0) {
-        log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
-        /*
-         * len is string length, not buffer length, but last byte is NUL
-         * anyway.
-         */
-        memwipe(client_key_out, 0, len);
-        tor_free(client_key_out);
-        goto err;
-      }
-      written = tor_snprintf(buf + written, sizeof(buf) - written,
-                             "client-key\n%s", client_key_out);
-      memwipe(client_key_out, 0, len);
-      tor_free(client_key_out);
-      if (written < 0) {
-        log_warn(LD_BUG, "Could not write client entry.");
-        goto err;
-      }
-    } else {
-      strlcpy(service_id, s->service_id, sizeof(service_id));
-    }
-
-    if (fputs(buf, cfile) < 0) {
-      log_warn(LD_FS, "Could not append client entry to file: %s",
-               strerror(errno));
-      goto err;
-    }
-
-    /* Add line to hostname file. This is not the same encoding as in
-     * client_keys. */
-    char *encoded_cookie = rend_auth_encode_cookie(client->descriptor_cookie,
-                                                   s->auth_type);
-    if (!encoded_cookie) {
-      log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
-      goto err;
-    }
-    tor_snprintf(buf, sizeof(buf), "%s.onion %s # client: %s\n",
-                 service_id, encoded_cookie, client->client_name);
-    memwipe(encoded_cookie, 0, strlen(encoded_cookie));
-    tor_free(encoded_cookie);
-
-    if (fputs(buf, hfile)<0) {
-      log_warn(LD_FS, "Could not append host entry to file: %s",
-               strerror(errno));
-      goto err;
-    }
-  } SMARTLIST_FOREACH_END(client);
-
-  finish_writing_to_file(open_cfile);
-  finish_writing_to_file(open_hfile);
-
-  goto done;
- err:
-  r = -1;
-  if (open_cfile)
-    abort_writing_to_file(open_cfile);
-  if (open_hfile)
-    abort_writing_to_file(open_hfile);
- done:
-  if (client_keys_str) {
-    memwipe(client_keys_str, 0, strlen(client_keys_str));
-    tor_free(client_keys_str);
-  }
-  strmap_free(parsed_clients, rend_authorized_client_free_void);
-
-  if (cfname) {
-    memwipe(cfname, 0, strlen(cfname));
-    tor_free(cfname);
-  }
-
-  /* Clear stack buffers that held key-derived material. */
-  memwipe(buf, 0, sizeof(buf));
-  memwipe(desc_cook_out, 0, sizeof(desc_cook_out));
-  memwipe(service_id, 0, sizeof(service_id));
-
-  return r;
-}
-
-/** Return the service whose public key has a digest of <b>digest</b>, or
- * NULL if no such service exists.
- */
-static rend_service_t *
-rend_service_get_by_pk_digest(const char* digest)
-{
-  SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
-                    if (tor_memeq(s->pk_digest,digest,DIGEST_LEN))
-                        return s);
-  return NULL;
-}
-
-/** Return the service whose service id is <b>id</b>, or NULL if no such
- * service exists.
- */
-static struct rend_service_t *
-rend_service_get_by_service_id(const char *id)
-{
-  tor_assert(strlen(id) == REND_SERVICE_ID_LEN_BASE32);
-  SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s, {
-    if (tor_memeq(s->service_id, id, REND_SERVICE_ID_LEN_BASE32))
-      return s;
-  });
-  return NULL;
-}
-
-/** Check client authorization of a given <b>descriptor_cookie</b> of
- * length <b>cookie_len</b> for <b>service</b>. Return 1 for success
- * and 0 for failure. */
-static int
-rend_check_authorization(rend_service_t *service,
-                         const char *descriptor_cookie,
-                         size_t cookie_len)
-{
-  rend_authorized_client_t *auth_client = NULL;
-  tor_assert(service);
-  tor_assert(descriptor_cookie);
-  if (!service->clients) {
-    log_warn(LD_BUG, "Can't check authorization for a service that has no "
-                     "authorized clients configured.");
-    return 0;
-  }
-
-  if (cookie_len != REND_DESC_COOKIE_LEN) {
-    log_info(LD_REND, "Descriptor cookie is %lu bytes, but we expected "
-                      "%lu bytes. Dropping cell.",
-             (unsigned long)cookie_len, (unsigned long)REND_DESC_COOKIE_LEN);
-    return 0;
-  }
-
-  /* Look up client authorization by descriptor cookie. */
-  SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, client, {
-    if (tor_memeq(client->descriptor_cookie, descriptor_cookie,
-                REND_DESC_COOKIE_LEN)) {
-      auth_client = client;
-      break;
-    }
-  });
-  if (!auth_client) {
-    char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
-    base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64),
-                  descriptor_cookie, REND_DESC_COOKIE_LEN, 0);
-    log_info(LD_REND, "No authorization found for descriptor cookie '%s'! "
-                      "Dropping cell!",
-             descriptor_cookie_base64);
-    return 0;
-  }
-
-  /* Allow the request. */
-  log_info(LD_REND, "Client %s authorized for service %s.",
-           auth_client->client_name, service->service_id);
-  return 1;
-}
-
-/* Can this service make a direct connection to ei?
- * It must be a single onion service, and the firewall rules must allow ei. */
-static int
-rend_service_use_direct_connection(const or_options_t* options,
-                                   const extend_info_t* ei)
-{
-  /* We'll connect directly all reachable addresses, whether preferred or not.
-   * The prefer_ipv6 argument to reachable_addr_allows_addr is
-   * ignored, because pref_only is 0. */
-  const tor_addr_port_t *ap = extend_info_get_orport(ei, AF_INET);
-  if (!ap)
-    return 0;
-  return (rend_service_allow_non_anonymous_connection(options) &&
-          reachable_addr_allows_addr(&ap->addr, ap->port,
-                                               FIREWALL_OR_CONNECTION, 0, 0));
-}
-
-/* Like rend_service_use_direct_connection, but to a node. */
-static int
-rend_service_use_direct_connection_node(const or_options_t* options,
-                                        const node_t* node)
-{
-  /* We'll connect directly all reachable addresses, whether preferred or not.
-   */
-  return (rend_service_allow_non_anonymous_connection(options) &&
-          reachable_addr_allows_node(node, FIREWALL_OR_CONNECTION, 0));
-}
-
-/******
- * Handle cells
- ******/
-
-/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
- * rendezvous point.
- */
-int
-rend_service_receive_introduction(origin_circuit_t *circuit,
-                                  const uint8_t *request,
-                                  size_t request_len)
-{
-  /* Global status stuff */
-  int status = 0, result;
-  const or_options_t *options = get_options();
-  char *err_msg = NULL;
-  int err_msg_severity = LOG_WARN;
-  const char *stage_descr = NULL, *rend_pk_digest;
-  int reason = END_CIRC_REASON_TORPROTOCOL;
-  /* Service/circuit/key stuff we can learn before parsing */
-  char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  rend_service_t *service = NULL;
-  rend_intro_point_t *intro_point = NULL;
-  crypto_pk_t *intro_key = NULL;
-  /* Parsed cell */
-  rend_intro_cell_t *parsed_req = NULL;
-  /* Rendezvous point */
-  extend_info_t *rp = NULL;
-  /* XXX not handled yet */
-  char buf[RELAY_PAYLOAD_SIZE];
-  char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
-  int i;
-  crypto_dh_t *dh = NULL;
-  origin_circuit_t *launched = NULL;
-  crypt_path_t *cpath = NULL;
-  char hexcookie[9];
-  int circ_needs_uptime;
-  time_t now = time(NULL);
-  time_t elapsed;
-  int replay;
-  ssize_t keylen;
-
-  /* Do some initial validation and logging before we parse the cell */
-  if (circuit->base_.purpose != CIRCUIT_PURPOSE_S_INTRO) {
-    log_warn(LD_PROTOCOL,
-             "Got an INTRODUCE2 over a non-introduction circuit %u.",
-             (unsigned) circuit->base_.n_circ_id);
-    goto err;
-  }
-
-  assert_circ_anonymity_ok(circuit, options);
-  tor_assert(circuit->rend_data);
-  /* XXX: This is version 2 specific (only one supported). */
-  rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
-
-  /* We'll use this in a bazillion log messages */
-  base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
-                rend_pk_digest, REND_SERVICE_ID_LEN);
-
-  /* look up service depending on circuit. */
-  service = rend_service_get_by_pk_digest(rend_pk_digest);
-  if (!service) {
-    log_warn(LD_BUG,
-             "Internal error: Got an INTRODUCE2 cell on an intro "
-             "circ for an unrecognized service %s.",
-             escaped(serviceid));
-    goto err;
-  }
-
-  intro_point = find_intro_point(circuit);
-  if (intro_point == NULL) {
-    intro_point = find_expiring_intro_point(service, circuit);
-    if (intro_point == NULL) {
-      log_warn(LD_BUG,
-               "Internal error: Got an INTRODUCE2 cell on an "
-               "intro circ (for service %s) with no corresponding "
-               "rend_intro_point_t.",
-               escaped(serviceid));
-      goto err;
-    }
-  }
-
-  log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %u.",
-           escaped(serviceid), (unsigned)circuit->base_.n_circ_id);
-
-  /* use intro key instead of service key. */
-  intro_key = circuit->intro_key;
-
-  tor_free(err_msg);
-  stage_descr = NULL;
-
-  stage_descr = "early parsing";
-  /* Early parsing pass (get pk, ciphertext); type 2 is INTRODUCE2 */
-  parsed_req =
-    rend_service_begin_parse_intro(request, request_len, 2, &err_msg);
-  if (!parsed_req) {
-    goto log_error;
-  } else if (err_msg) {
-    log_info(LD_REND, "%s on circ %u.", err_msg,
-             (unsigned)circuit->base_.n_circ_id);
-    tor_free(err_msg);
-  }
-
-  /* make sure service replay caches are present */
-  if (!service->accepted_intro_dh_parts) {
-    service->accepted_intro_dh_parts =
-      replaycache_new(REND_REPLAY_TIME_INTERVAL,
-                      REND_REPLAY_TIME_INTERVAL);
-  }
-
-  if (!intro_point->accepted_intro_rsa_parts) {
-    intro_point->accepted_intro_rsa_parts = replaycache_new(0, 0);
-  }
-
-  /* check for replay of PK-encrypted portion. */
-  keylen = crypto_pk_keysize(intro_key);
-  replay = replaycache_add_test_and_elapsed(
-    intro_point->accepted_intro_rsa_parts,
-    parsed_req->ciphertext, MIN(parsed_req->ciphertext_len, keylen),
-    &elapsed);
-
-  if (replay) {
-    log_warn(LD_REND,
-             "Possible replay detected! We received an "
-             "INTRODUCE2 cell with same PK-encrypted part %d "
-             "seconds ago.  Dropping cell.",
-             (int)elapsed);
-    goto err;
-  }
-
-  stage_descr = "decryption";
-  /* Now try to decrypt it */
-  result = rend_service_decrypt_intro(parsed_req, intro_key, &err_msg);
-  if (result < 0) {
-    goto log_error;
-  } else if (err_msg) {
-    log_info(LD_REND, "%s on circ %u.", err_msg,
-             (unsigned)circuit->base_.n_circ_id);
-    tor_free(err_msg);
-  }
-
-  stage_descr = "late parsing";
-  /* Parse the plaintext */
-  result = rend_service_parse_intro_plaintext(parsed_req, &err_msg);
-  if (result < 0) {
-    goto log_error;
-  } else if (err_msg) {
-    log_info(LD_REND, "%s on circ %u.", err_msg,
-             (unsigned)circuit->base_.n_circ_id);
-    tor_free(err_msg);
-  }
-
-  stage_descr = "late validation";
-  /* Validate the parsed plaintext parts */
-  result = rend_service_validate_intro_late(parsed_req, &err_msg);
-  if (result < 0) {
-    goto log_error;
-  } else if (err_msg) {
-    log_info(LD_REND, "%s on circ %u.", err_msg,
-             (unsigned)circuit->base_.n_circ_id);
-    tor_free(err_msg);
-  }
-  stage_descr = NULL;
-
-  /* Increment INTRODUCE2 counter */
-  ++(intro_point->accepted_introduce2_count);
-
-  /* Find the rendezvous point */
-  rp = find_rp_for_intro(parsed_req, &err_msg);
-  if (!rp) {
-    err_msg_severity = LOG_PROTOCOL_WARN;
-    goto log_error;
-  }
-
-  /* Check if we'd refuse to talk to this router */
-  if (options->StrictNodes &&
-      routerset_contains_extendinfo(options->ExcludeNodes, rp)) {
-    log_warn(LD_REND, "Client asked to rendezvous at a relay that we "
-             "exclude, and StrictNodes is set. Refusing service.");
-    reason = END_CIRC_REASON_INTERNAL; /* XXX might leak why we refused */
-    goto err;
-  }
-
-  base16_encode(hexcookie, 9, (const char *)(parsed_req->rc), 4);
-
-  /* Check whether there is a past request with the same Diffie-Hellman,
-   * part 1. */
-  replay = replaycache_add_test_and_elapsed(
-      service->accepted_intro_dh_parts,
-      parsed_req->dh, DH1024_KEY_LEN,
-      &elapsed);
-
-  if (replay) {
-    /* A Tor client will send a new INTRODUCE1 cell with the same rend
-     * cookie and DH public key as its previous one if its intro circ
-     * times out while in state CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT .
-     * If we received the first INTRODUCE1 cell (the intro-point relay
-     * converts it into an INTRODUCE2 cell), we are already trying to
-     * connect to that rend point (and may have already succeeded);
-     * drop this cell. */
-    log_info(LD_REND, "We received an "
-             "INTRODUCE2 cell with same first part of "
-             "Diffie-Hellman handshake %d seconds ago. Dropping "
-             "cell.",
-             (int) elapsed);
-    goto err;
-  }
-
-  /* If the service performs client authorization, check included auth data. */
-  if (service->clients) {
-    if (parsed_req->version == 3 && parsed_req->u.v3.auth_len > 0) {
-      if (rend_check_authorization(service,
-                                   (const char*)parsed_req->u.v3.auth_data,
-                                   parsed_req->u.v3.auth_len)) {
-        log_info(LD_REND, "Authorization data in INTRODUCE2 cell are valid.");
-      } else {
-        log_info(LD_REND, "The authorization data that are contained in "
-                 "the INTRODUCE2 cell are invalid. Dropping cell.");
-        reason = END_CIRC_REASON_CONNECTFAILED;
-        goto err;
-      }
-    } else {
-      log_info(LD_REND, "INTRODUCE2 cell does not contain authentication "
-               "data, but we require client authorization. Dropping cell.");
-      reason = END_CIRC_REASON_CONNECTFAILED;
-      goto err;
-    }
-  }
-
-  /* Try DH handshake... */
-  dh = crypto_dh_new(DH_TYPE_REND);
-  if (!dh || crypto_dh_generate_public(dh)<0) {
-    log_warn(LD_BUG,"Internal error: couldn't build DH state "
-             "or generate public key.");
-    reason = END_CIRC_REASON_INTERNAL;
-    goto err;
-  }
-  if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh,
-                               (char *)(parsed_req->dh),
-                               DH1024_KEY_LEN, keys,
-                               DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
-    log_warn(LD_BUG, "Internal error: couldn't complete DH handshake");
-    reason = END_CIRC_REASON_INTERNAL;
-    goto err;
-  }
-
-  circ_needs_uptime = hs_service_requires_uptime_circ(service->ports);
-
-  /* help predict this next time */
-  rep_hist_note_used_internal(now, circ_needs_uptime, 1);
-
-  /* Launch a circuit to the client's chosen rendezvous point.
-   */
-  int max_rend_failures=hs_get_service_max_rend_failures();
-  for (i=0;i<max_rend_failures;i++) {
-    int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
-    if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
-    /* A Single Onion Service only uses a direct connection if its
-     * firewall rules permit direct connections to the address.
-     *
-     * We only use a one-hop path on the first attempt. If the first attempt
-     * fails, we use a 3-hop path for reachability / reliability.
-     * See the comment in rend_service_relaunch_rendezvous() for details. */
-    if (rend_service_use_direct_connection(options, rp) && i == 0) {
-      flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL;
-    }
-    launched = circuit_launch_by_extend_info(
-                        CIRCUIT_PURPOSE_S_CONNECT_REND, rp, flags);
-
-    if (launched)
-      break;
-  }
-  if (!launched) { /* give up */
-    log_warn(LD_REND, "Giving up launching first hop of circuit to rendezvous "
-             "point %s for service %s.",
-             safe_str_client(extend_info_describe(rp)),
-             serviceid);
-    reason = END_CIRC_REASON_CONNECTFAILED;
-    goto err;
-  }
-  log_info(LD_REND,
-           "Accepted intro; launching circuit to %s "
-           "(cookie %s) for service %s.",
-           safe_str_client(extend_info_describe(rp)),
-           hexcookie, serviceid);
-  tor_assert(launched->build_state);
-  /* Fill in the circuit's state. */
-
-  launched->rend_data =
-    rend_data_service_create(service->service_id, rend_pk_digest,
-                             parsed_req->rc, service->auth_type);
-
-  launched->build_state->service_pending_final_cpath_ref =
-    tor_malloc_zero(sizeof(crypt_path_reference_t));
-  launched->build_state->service_pending_final_cpath_ref->refcount = 1;
-
-  launched->build_state->service_pending_final_cpath_ref->cpath = cpath =
-    tor_malloc_zero(sizeof(crypt_path_t));
-  cpath->magic = CRYPT_PATH_MAGIC;
-  launched->build_state->expiry_time = now + MAX_REND_TIMEOUT;
-
-  cpath->rend_dh_handshake_state = dh;
-  dh = NULL;
-  if (cpath_init_circuit_crypto(cpath,
-                                keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
-                                1, 0)<0)
-    goto err;
-  memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN);
-
-  goto done;
-
- log_error:
-  if (!err_msg) {
-    if (stage_descr) {
-      tor_asprintf(&err_msg,
-                   "unknown %s error for INTRODUCE2", stage_descr);
-    } else {
-      err_msg = tor_strdup("unknown error for INTRODUCE2");
-    }
-  }
-
-  log_fn(err_msg_severity, LD_REND, "%s on circ %u", err_msg,
-           (unsigned)circuit->base_.n_circ_id);
- err:
-  status = -1;
-  if (dh) crypto_dh_free(dh);
-  if (launched) {
-    circuit_mark_for_close(TO_CIRCUIT(launched), reason);
-  }
-  tor_free(err_msg);
-
- done:
-  memwipe(keys, 0, sizeof(keys));
-  memwipe(buf, 0, sizeof(buf));
-  memwipe(serviceid, 0, sizeof(serviceid));
-  memwipe(hexcookie, 0, sizeof(hexcookie));
-
-  /* Free the parsed cell */
-  rend_service_free_intro(parsed_req);
-
-  /* Free rp */
-  extend_info_free(rp);
-
-  return status;
-}
-
-/** Given a parsed and decrypted INTRODUCE2, find the rendezvous point or
- * return NULL and an error string if we can't. Return a newly allocated
- * extend_info_t* for the rendezvous point. */
-static extend_info_t *
-find_rp_for_intro(const rend_intro_cell_t *intro,
-                  char **err_msg_out)
-{
-  extend_info_t *rp = NULL;
-  char *err_msg = NULL;
-  const char *rp_nickname = NULL;
-  const node_t *node = NULL;
-
-  if (!intro) {
-    if (err_msg_out)
-      err_msg = tor_strdup("Bad parameters to find_rp_for_intro()");
-
-    goto err;
-  }
-
-  if (intro->version == 0 || intro->version == 1) {
-    rp_nickname = (const char *)(intro->u.v0_v1.rp);
-
-    node = node_get_by_nickname(rp_nickname, NNF_NO_WARN_UNNAMED);
-    if (!node) {
-      if (err_msg_out) {
-        tor_asprintf(&err_msg,
-                     "Couldn't find router %s named in INTRODUCE2 cell",
-                     escaped_safe_str_client(rp_nickname));
-      }
-
-      goto err;
-    }
-
-    /* Are we in single onion mode? */
-    const int allow_direct = rend_service_allow_non_anonymous_connection(
-                                                                get_options());
-    rp = extend_info_from_node(node, allow_direct);
-    if (!rp) {
-      if (err_msg_out) {
-        tor_asprintf(&err_msg,
-                     "Couldn't build extend_info_t for router %s named "
-                     "in INTRODUCE2 cell",
-                     escaped_safe_str_client(rp_nickname));
-      }
-
-      goto err;
-    }
-  } else if (intro->version == 2) {
-    rp = extend_info_dup(intro->u.v2.extend_info);
-  } else if (intro->version == 3) {
-    rp = extend_info_dup(intro->u.v3.extend_info);
-  } else {
-    if (err_msg_out) {
-      tor_asprintf(&err_msg,
-                   "Unknown version %d in INTRODUCE2 cell",
-                   (int)(intro->version));
-    }
-
-    goto err;
-  }
-
-  /* rp is always set here: extend_info_dup guarantees a non-NULL result, and
-   * the other cases goto err. */
-  tor_assert(rp);
-
-  /* Make sure the RP we are being asked to connect to is _not_ a private
-   * address unless it's allowed. Let's avoid to build a circuit to our
-   * second middle node and fail right after when extending to the RP. */
-  const tor_addr_port_t *orport = extend_info_get_orport(rp, AF_INET);
-  if (! orport || !extend_info_addr_is_allowed(&orport->addr)) {
-    if (err_msg_out) {
-      tor_asprintf(&err_msg,
-                   "Relay IP in INTRODUCE2 cell is private address.");
-    }
-    extend_info_free(rp);
-    rp = NULL;
-    goto err;
-  }
-  goto done;
-
- err:
-  if (err_msg_out)
-    *err_msg_out = err_msg;
-  else
-    tor_free(err_msg);
-
- done:
-  return rp;
-}
-
-/** Free a parsed INTRODUCE1 or INTRODUCE2 cell that was allocated by
- * rend_service_parse_intro().
- */
-void
-rend_service_free_intro_(rend_intro_cell_t *request)
-{
-  if (!request) {
-    return;
-  }
-
-  /* Free ciphertext */
-  tor_free(request->ciphertext);
-  request->ciphertext_len = 0;
-
-  /* Have plaintext? */
-  if (request->plaintext) {
-    /* Zero it out just to be safe */
-    memwipe(request->plaintext, 0, request->plaintext_len);
-    tor_free(request->plaintext);
-    request->plaintext_len = 0;
-  }
-
-  /* Have parsed plaintext? */
-  if (request->parsed) {
-    switch (request->version) {
-      case 0:
-      case 1:
-        /*
-         * Nothing more to do; these formats have no further pointers
-         * in them.
-         */
-        break;
-      case 2:
-        extend_info_free(request->u.v2.extend_info);
-        request->u.v2.extend_info = NULL;
-        break;
-      case 3:
-        if (request->u.v3.auth_data) {
-          memwipe(request->u.v3.auth_data, 0, request->u.v3.auth_len);
-          tor_free(request->u.v3.auth_data);
-        }
-
-        extend_info_free(request->u.v3.extend_info);
-        request->u.v3.extend_info = NULL;
-        break;
-      default:
-        log_info(LD_BUG,
-                 "rend_service_free_intro() saw unknown protocol "
-                 "version %d.",
-                 request->version);
-    }
-  }
-
-  /* Zero it out to make sure sensitive stuff doesn't hang around in memory */
-  memwipe(request, 0, sizeof(*request));
-
-  tor_free(request);
-}
-
-/** Parse an INTRODUCE1 or INTRODUCE2 cell into a newly allocated
- * rend_intro_cell_t structure.  Free it with rend_service_free_intro()
- * when finished.  The type parameter should be 1 or 2 to indicate whether
- * this is INTRODUCE1 or INTRODUCE2.  This parses only the non-encrypted
- * parts; after this, call rend_service_decrypt_intro() with a key, then
- * rend_service_parse_intro_plaintext() to finish parsing.  The optional
- * err_msg_out parameter is set to a string suitable for log output
- * if parsing fails.  This function does some validation, but only
- * that which depends solely on the contents of the cell and the
- * key; it can be unit-tested.  Further validation is done in
- * rend_service_validate_intro().
- */
-
-rend_intro_cell_t *
-rend_service_begin_parse_intro(const uint8_t *request,
-                               size_t request_len,
-                               uint8_t type,
-                               char **err_msg_out)
-{
-  rend_intro_cell_t *rv = NULL;
-  char *err_msg = NULL;
-
-  if (!request || request_len <= 0) goto err;
-  if (!(type == 1 || type == 2)) goto err;
-
-  /* First, check that the cell is long enough to be a sensible INTRODUCE */
-
-  /* min key length plus digest length plus nickname length */
-  if (request_len <
-        (DIGEST_LEN + REND_COOKIE_LEN + (MAX_NICKNAME_LEN + 1) +
-         DH1024_KEY_LEN + 42)) {
-    if (err_msg_out) {
-      tor_asprintf(&err_msg,
-                   "got a truncated INTRODUCE%d cell",
-                   (int)type);
-    }
-    goto err;
-  }
-
-  /* Allocate a new parsed cell structure */
-  rv = tor_malloc_zero(sizeof(*rv));
-
-  /* Set the type */
-  rv->type = type;
-
-  /* Copy in the ID */
-  memcpy(rv->pk, request, DIGEST_LEN);
-
-  /* Copy in the ciphertext */
-  rv->ciphertext = tor_malloc(request_len - DIGEST_LEN);
-  memcpy(rv->ciphertext, request + DIGEST_LEN, request_len - DIGEST_LEN);
-  rv->ciphertext_len = request_len - DIGEST_LEN;
-
-  goto done;
-
- err:
-  rend_service_free_intro(rv);
-  rv = NULL;
-
-  if (err_msg_out && !err_msg) {
-    tor_asprintf(&err_msg,
-                 "unknown INTRODUCE%d error",
-                 (int)type);
-  }
-
- done:
-  if (err_msg_out) *err_msg_out = err_msg;
-  else tor_free(err_msg);
-
-  return rv;
-}
-
-/** Parse the version-specific parts of a v0 or v1 INTRODUCE1 or INTRODUCE2
- * cell
- */
-
-static ssize_t
-rend_service_parse_intro_for_v0_or_v1(
-    rend_intro_cell_t *intro,
-    const uint8_t *buf,
-    size_t plaintext_len,
-    char **err_msg_out)
-{
-  const char *rp_nickname, *endptr;
-  size_t nickname_field_len, ver_specific_len;
-
-  if (intro->version == 1) {
-    ver_specific_len = MAX_HEX_NICKNAME_LEN + 2;
-    rp_nickname = ((const char *)buf) + 1;
-    nickname_field_len = MAX_HEX_NICKNAME_LEN + 1;
-  } else if (intro->version == 0) {
-    ver_specific_len = MAX_NICKNAME_LEN + 1;
-    rp_nickname = (const char *)buf;
-    nickname_field_len = MAX_NICKNAME_LEN + 1;
-  } else {
-    if (err_msg_out)
-      tor_asprintf(err_msg_out,
-                   "rend_service_parse_intro_for_v0_or_v1() called with "
-                   "bad version %d on INTRODUCE%d cell (this is a bug)",
-                   intro->version,
-                   (int)(intro->type));
-    goto err;
-  }
-
-  if (plaintext_len < ver_specific_len) {
-    if (err_msg_out)
-      tor_asprintf(err_msg_out,
-                   "short plaintext of encrypted part in v1 INTRODUCE%d "
-                   "cell (%lu bytes, needed %lu)",
-                   (int)(intro->type),
-                   (unsigned long)plaintext_len,
-                   (unsigned long)ver_specific_len);
-    goto err;
-  }
-
-  endptr = memchr(rp_nickname, 0, nickname_field_len);
-  if (!endptr || endptr == rp_nickname) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "couldn't find a nul-padded nickname in "
-                   "INTRODUCE%d cell",
-                   (int)(intro->type));
-    }
-    goto err;
-  }
-
-  if ((intro->version == 0 &&
-       !is_legal_nickname(rp_nickname)) ||
-      (intro->version == 1 &&
-       !is_legal_nickname_or_hexdigest(rp_nickname))) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "bad nickname in INTRODUCE%d cell",
-                   (int)(intro->type));
-    }
-    goto err;
-  }
-
-  memcpy(intro->u.v0_v1.rp, rp_nickname, endptr - rp_nickname + 1);
-
-  return ver_specific_len;
-
- err:
-  return -1;
-}
-
-/** Parse the version-specific parts of a v2 INTRODUCE1 or INTRODUCE2 cell
- */
-
-static ssize_t
-rend_service_parse_intro_for_v2(
-    rend_intro_cell_t *intro,
-    const uint8_t *buf,
-    size_t plaintext_len,
-    char **err_msg_out)
-{
-  unsigned int klen;
-  extend_info_t *extend_info = NULL;
-  ssize_t ver_specific_len;
-
-  /*
-   * We accept version 3 too so that the v3 parser can call this with
-   * an adjusted buffer for the latter part of a v3 cell, which is
-   * identical to a v2 cell.
-   */
-  if (!(intro->version == 2 ||
-        intro->version == 3)) {
-    if (err_msg_out)
-      tor_asprintf(err_msg_out,
-                   "rend_service_parse_intro_for_v2() called with "
-                   "bad version %d on INTRODUCE%d cell (this is a bug)",
-                   intro->version,
-                   (int)(intro->type));
-    goto err;
-  }
-
-  /* 7 == version, IP and port, DIGEST_LEN == id, 2 == key length */
-  if (plaintext_len < 7 + DIGEST_LEN + 2) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "truncated plaintext of encrypted parted of "
-                   "version %d INTRODUCE%d cell",
-                   intro->version,
-                   (int)(intro->type));
-    }
-
-    goto err;
-  }
-
-  extend_info = extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0);
-  tor_addr_t addr;
-  tor_addr_from_ipv4n(&addr, get_uint32(buf + 1));
-  uint16_t port = ntohs(get_uint16(buf + 5));
-  extend_info_add_orport(extend_info, &addr, port);
-  memcpy(extend_info->identity_digest, buf + 7, DIGEST_LEN);
-  extend_info->nickname[0] = '$';
-  base16_encode(extend_info->nickname + 1, sizeof(extend_info->nickname) - 1,
-                extend_info->identity_digest, DIGEST_LEN);
-  klen = ntohs(get_uint16(buf + 7 + DIGEST_LEN));
-
-  /* 7 == version, IP and port, DIGEST_LEN == id, 2 == key length */
-  if (plaintext_len < 7 + DIGEST_LEN + 2 + klen) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "truncated plaintext of encrypted parted of "
-                   "version %d INTRODUCE%d cell",
-                   intro->version,
-                   (int)(intro->type));
-    }
-
-    goto err;
-  }
-
-  extend_info->onion_key =
-    crypto_pk_asn1_decode((const char *)(buf + 7 + DIGEST_LEN + 2), klen);
-  if (!extend_info->onion_key) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "error decoding onion key in version %d "
-                   "INTRODUCE%d cell",
-                   intro->version,
-                   (intro->type));
-    }
-
-    goto err;
-  }
-  if (128 != crypto_pk_keysize(extend_info->onion_key)) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "invalid onion key size in version %d INTRODUCE%d cell",
-                   intro->version,
-                   (intro->type));
-    }
-
-    goto err;
-  }
-
-  ver_specific_len = 7+DIGEST_LEN+2+klen;
-
-  if (intro->version == 2) intro->u.v2.extend_info = extend_info;
-  else intro->u.v3.extend_info = extend_info;
-
-  return ver_specific_len;
-
- err:
-  extend_info_free(extend_info);
-
-  return -1;
-}
-
-/** Parse the version-specific parts of a v3 INTRODUCE1 or INTRODUCE2 cell
- */
-
-static ssize_t
-rend_service_parse_intro_for_v3(
-    rend_intro_cell_t *intro,
-    const uint8_t *buf,
-    size_t plaintext_len,
-    char **err_msg_out)
-{
-  ssize_t adjust, v2_ver_specific_len, ts_offset;
-
-  /* This should only be called on v3 cells */
-  if (intro->version != 3) {
-    if (err_msg_out)
-      tor_asprintf(err_msg_out,
-                   "rend_service_parse_intro_for_v3() called with "
-                   "bad version %d on INTRODUCE%d cell (this is a bug)",
-                   intro->version,
-                   (int)(intro->type));
-    goto err;
-  }
-
-  /*
-   * Check that we have at least enough to get auth_len:
-   *
-   * 1 octet for version, 1 for auth_type, 2 for auth_len
-   */
-  if (plaintext_len < 4) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "truncated plaintext of encrypted parted of "
-                   "version %d INTRODUCE%d cell",
-                   intro->version,
-                   (int)(intro->type));
-    }
-
-    goto err;
-  }
-
-  /*
-   * The rend_client_send_introduction() function over in rendclient.c is
-   * broken (i.e., fails to match the spec) in such a way that we can't
-   * change it without breaking the protocol.  Specifically, it doesn't
-   * emit auth_len when auth-type is REND_NO_AUTH, so everything is off
-   * by two bytes after that.  Calculate ts_offset and do everything from
-   * the timestamp on relative to that to handle this dain bramage.
-   */
-
-  intro->u.v3.auth_type = buf[1];
-  if (intro->u.v3.auth_type != REND_NO_AUTH) {
-    intro->u.v3.auth_len = ntohs(get_uint16(buf + 2));
-    ts_offset = 4 + intro->u.v3.auth_len;
-  } else {
-    intro->u.v3.auth_len = 0;
-    ts_offset = 2;
-  }
-
-  /* Check that auth len makes sense for this auth type */
-  if (intro->u.v3.auth_type == REND_BASIC_AUTH ||
-      intro->u.v3.auth_type == REND_STEALTH_AUTH) {
-      if (intro->u.v3.auth_len != REND_DESC_COOKIE_LEN) {
-        if (err_msg_out) {
-          tor_asprintf(err_msg_out,
-                       "wrong auth data size %d for INTRODUCE%d cell, "
-                       "should be %d",
-                       (int)(intro->u.v3.auth_len),
-                       (int)(intro->type),
-                       REND_DESC_COOKIE_LEN);
-        }
-
-        goto err;
-      }
-  }
-
-  /* Check that we actually have everything up through the timestamp */
-  if (plaintext_len < (size_t)(ts_offset)+4) {
-    if (err_msg_out) {
-      tor_asprintf(err_msg_out,
-                   "truncated plaintext of encrypted parted of "
-                   "version %d INTRODUCE%d cell",
-                   intro->version,
-                   (int)(intro->type));
-    }
-
-    goto err;
-  }
-
-  if (intro->u.v3.auth_type != REND_NO_AUTH &&
-      intro->u.v3.auth_len > 0) {
-    /* Okay, we can go ahead and copy auth_data */
-    intro->u.v3.auth_data = tor_malloc(intro->u.v3.auth_len);
-    /*
-     * We know we had an auth_len field in this case, so 4 is
-     * always right.
-     */
-    memcpy(intro->u.v3.auth_data, buf + 4, intro->u.v3.auth_len);
-  }
-
-  /*
-   * From here on, the format is as in v2, so we call the v2 parser with
-   * adjusted buffer and length.  We are 4 + ts_offset octets in, but the
-   * v2 parser expects to skip over a version byte at the start, so we
-   * adjust by 3 + ts_offset.
-   */
-  adjust = 3 + ts_offset;
-
-  v2_ver_specific_len =
-    rend_service_parse_intro_for_v2(intro,
-                                    buf + adjust, plaintext_len - adjust,
-                                    err_msg_out);
-
-  /* Success in v2 parser */
-  if (v2_ver_specific_len >= 0) return v2_ver_specific_len + adjust;
-  /* Failure in v2 parser; it will have provided an err_msg */
-  else return v2_ver_specific_len;
-
- err:
-  return -1;
-}
-
-/** Table of parser functions for version-specific parts of an INTRODUCE2
- * cell.
- */
-
-static ssize_t
-  (*intro_version_handlers[])(
-    rend_intro_cell_t *,
-    const uint8_t *,
-    size_t,
-    char **) =
-{ rend_service_parse_intro_for_v0_or_v1,
-  rend_service_parse_intro_for_v0_or_v1,
-  rend_service_parse_intro_for_v2,
-  rend_service_parse_intro_for_v3 };
-
-/** Decrypt the encrypted part of an INTRODUCE1 or INTRODUCE2 cell,
- * return 0 if successful, or < 0 and write an error message to
- * *err_msg_out if provided.
- */
-
-int
-rend_service_decrypt_intro(
-    rend_intro_cell_t *intro,
-    crypto_pk_t *key,
-    char **err_msg_out)
-{
-  char *err_msg = NULL;
-  uint8_t key_digest[DIGEST_LEN];
-  char service_id[REND_SERVICE_ID_LEN_BASE32+1];
-  ssize_t key_len;
-  uint8_t buf[RELAY_PAYLOAD_SIZE];
-  int result, status = -1;
-
-  if (!intro || !key) {
-    if (err_msg_out) {
-      err_msg =
-        tor_strdup("rend_service_decrypt_intro() called with bad "
-                   "parameters");
-    }
-
-    status = -2;
-    goto err;
-  }
-
-  /* Make sure we have ciphertext */
-  if (!(intro->ciphertext) || intro->ciphertext_len <= 0) {
-    if (err_msg_out) {
-      tor_asprintf(&err_msg,
-                   "rend_intro_cell_t was missing ciphertext for "
-                   "INTRODUCE%d cell",
-                   (int)(intro->type));
-    }
-    status = -3;
-    goto err;
-  }
-
-  /* Check that this cell actually matches this service key */
-
-  /* first DIGEST_LEN bytes of request is intro or service pk digest */
-  if (crypto_pk_get_digest(key, (char *)key_digest) < 0) {
-    if (err_msg_out)
-      *err_msg_out = tor_strdup("Couldn't compute RSA digest.");
-    log_warn(LD_BUG, "Couldn't compute key digest.");
-    status = -7;
-    goto err;
-  }
-
-  if (tor_memneq(key_digest, intro->pk, DIGEST_LEN)) {
-    if (err_msg_out) {
-      base32_encode(service_id, REND_SERVICE_ID_LEN_BASE32 + 1,
-                    (char*)(intro->pk), REND_SERVICE_ID_LEN);
-      tor_asprintf(&err_msg,
-                   "got an INTRODUCE%d cell for the wrong service (%s)",
-                   (int)(intro->type),
-                   escaped(service_id));
-    }
-
-    status = -4;
-    goto err;
-  }
-
-  /* Make sure the encrypted part is long enough to decrypt */
-
-  key_len = crypto_pk_keysize(key);
-  if (intro->ciphertext_len < key_len) {
-    if (err_msg_out) {
-      tor_asprintf(&err_msg,
-                   "got an INTRODUCE%d cell with a truncated PK-encrypted "
-                   "part",
-                   (int)(intro->type));
-    }
-
-    status = -5;
-    goto err;
-  }
-
-  /* Decrypt the encrypted part */
-  result =
-    crypto_pk_obsolete_private_hybrid_decrypt(
-       key, (char *)buf, sizeof(buf),
-       (const char *)(intro->ciphertext), intro->ciphertext_len,
-       PK_PKCS1_OAEP_PADDING, 1);
-  if (result < 0) {
-    if (err_msg_out) {
-      tor_asprintf(&err_msg,
-                   "couldn't decrypt INTRODUCE%d cell",
-                   (int)(intro->type));
-    }
-    status = -6;
-    goto err;
-  }
-  intro->plaintext_len = result;
-  intro->plaintext = tor_malloc(intro->plaintext_len);
-  memcpy(intro->plaintext, buf, intro->plaintext_len);
-
-  status = 0;
-
-  goto done;
-
- err:
-  if (err_msg_out && !err_msg) {
-    tor_asprintf(&err_msg,
-                 "unknown INTRODUCE%d error decrypting encrypted part",
-                 intro ? (int)(intro->type) : -1);
-  }
-
- done:
-  if (err_msg_out) *err_msg_out = err_msg;
-  else tor_free(err_msg);
-
-  /* clean up potentially sensitive material */
-  memwipe(buf, 0, sizeof(buf));
-  memwipe(key_digest, 0, sizeof(key_digest));
-  memwipe(service_id, 0, sizeof(service_id));
-
-  return status;
-}
-
-/** Parse the plaintext of the encrypted part of an INTRODUCE1 or
- * INTRODUCE2 cell, return 0 if successful, or < 0 and write an error
- * message to *err_msg_out if provided.
- */
-
-int
-rend_service_parse_intro_plaintext(
-    rend_intro_cell_t *intro,
-    char **err_msg_out)
-{
-  char *err_msg = NULL;
-  ssize_t ver_specific_len, ver_invariant_len;
-  uint8_t version;
-  int status = -1;
-
-  if (!intro) {
-    if (err_msg_out) {
-      err_msg =
-        tor_strdup("rend_service_parse_intro_plaintext() called with NULL "
-                   "rend_intro_cell_t");
-    }
-
-    status = -2;
-    goto err;
-  }
-
-  /* Check that we have plaintext */
-  if (!(intro->plaintext) || intro->plaintext_len <= 0) {
-    if (err_msg_out) {
-      err_msg = tor_strdup("rend_intro_cell_t was missing plaintext");
-    }
-    status = -3;
-    goto err;
-  }
-
-  /* In all formats except v0, the first byte is a version number */
-  version = intro->plaintext[0];
-
-  /* v0 has no version byte (stupid...), so handle it as a fallback */
-  if (version > 3) version = 0;
-
-  /* Copy the version into the parsed cell structure */
-  intro->version = version;
-
-  /* Call the version-specific parser from the table */
-  ver_specific_len =
-    intro_version_handlers[version](intro,
-                                    intro->plaintext, intro->plaintext_len,
-                                    &err_msg);
-  if (ver_specific_len < 0) {
-    status = -4;
-    goto err;
-  }
-
-  /** The rendezvous cookie and Diffie-Hellman stuff are version-invariant
-   * and at the end of the plaintext of the encrypted part of the cell.
-   */
-
-  ver_invariant_len = intro->plaintext_len - ver_specific_len;
-  if (ver_invariant_len < REND_COOKIE_LEN + DH1024_KEY_LEN) {
-    tor_asprintf(&err_msg,
-        "decrypted plaintext of INTRODUCE%d cell was truncated (%ld bytes)",
-        (int)(intro->type),
-        (long)(intro->plaintext_len));
-    status = -5;
-    goto err;
-  } else if (ver_invariant_len > REND_COOKIE_LEN + DH1024_KEY_LEN) {
-    tor_asprintf(&err_msg,
-        "decrypted plaintext of INTRODUCE%d cell was too long (%ld bytes)",
-        (int)(intro->type),
-        (long)(intro->plaintext_len));
-    status = -6;
-    goto err;
-  } else {
-    memcpy(intro->rc,
-           intro->plaintext + ver_specific_len,
-           REND_COOKIE_LEN);
-    memcpy(intro->dh,
-           intro->plaintext + ver_specific_len + REND_COOKIE_LEN,
-           DH1024_KEY_LEN);
-  }
-
-  /* Flag it as being fully parsed */
-  intro->parsed = 1;
-
-  status = 0;
-  goto done;
-
- err:
-  if (err_msg_out && !err_msg) {
-    tor_asprintf(&err_msg,
-                 "unknown INTRODUCE%d error parsing encrypted part",
-                 intro ? (int)(intro->type) : -1);
-  }
-
- done:
-  if (err_msg_out) *err_msg_out = err_msg;
-  else tor_free(err_msg);
-
-  return status;
-}
-
-/** Do validity checks on a parsed intro cell after decryption; some of
- * these are not done in rend_service_parse_intro_plaintext() itself because
- * they depend on a lot of other state and would make it hard to unit test.
- * Returns >= 0 if successful or < 0 if the intro cell is invalid, and
- * optionally writes out an error message for logging.  If an err_msg
- * pointer is provided, it is the caller's responsibility to free any
- * provided message.
- */
-
-int
-rend_service_validate_intro_late(const rend_intro_cell_t *intro,
-                                 char **err_msg_out)
-{
-  int status = 0;
-
-  if (!intro) {
-    if (err_msg_out)
-      *err_msg_out =
-        tor_strdup("NULL intro cell passed to "
-                   "rend_service_validate_intro_late()");
-
-    status = -1;
-    goto err;
-  }
-
-  if (intro->version == 3 && intro->parsed) {
-    if (!(intro->u.v3.auth_type == REND_NO_AUTH ||
-          intro->u.v3.auth_type == REND_BASIC_AUTH ||
-          intro->u.v3.auth_type == REND_STEALTH_AUTH)) {
-      /* This is an informative message, not an error, as in the old code */
-      if (err_msg_out)
-        tor_asprintf(err_msg_out,
-                     "unknown authorization type %d",
-                     intro->u.v3.auth_type);
-    }
-  }
-
- err:
-  return status;
-}
-
-/** Called when we fail building a rendezvous circuit at some point other
- * than the last hop: launches a new circuit to the same rendezvous point.
- */
-void
-rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
-{
-  origin_circuit_t *newcirc;
-  cpath_build_state_t *newstate, *oldstate;
-  const char *rend_pk_digest;
-  rend_service_t *service = NULL;
-
-  int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
-
-  tor_assert(oldcirc->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
-  oldstate = oldcirc->build_state;
-  tor_assert(oldstate);
-
-  if (oldstate->service_pending_final_cpath_ref == NULL) {
-    log_info(LD_REND,"Skipping relaunch of circ that failed on its first hop. "
-             "Initiator will retry.");
-    return;
-  }
-
-  log_info(LD_REND,"Reattempting rendezvous circuit to '%s'",
-           safe_str(extend_info_describe(oldstate->chosen_exit)));
-
-  /* Look up the service. */
-  rend_pk_digest = (char *) rend_data_get_pk_digest(oldcirc->rend_data, NULL);
-  service = rend_service_get_by_pk_digest(rend_pk_digest);
-
-  if (!service) {
-    char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-    base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
-                  rend_pk_digest, REND_SERVICE_ID_LEN);
-
-    log_warn(LD_BUG, "Internal error: Trying to relaunch a rendezvous circ "
-                     "for an unrecognized service %s.",
-                     safe_str_client(serviceid));
-    return;
-  }
-
-  if (hs_service_requires_uptime_circ(service->ports)) {
-    flags |= CIRCLAUNCH_NEED_UPTIME;
-  }
-
-  /* You'd think Single Onion Services would want to retry the rendezvous
-   * using a direct connection. But if it's blocked by a firewall, or the
-   * service is IPv6-only, or the rend point avoiding becoming a one-hop
-   * proxy, we need a 3-hop connection. */
-  newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
-                            oldstate->chosen_exit, flags);
-
-  if (!newcirc) {
-    log_warn(LD_REND,"Couldn't relaunch rendezvous circuit to '%s'.",
-             safe_str(extend_info_describe(oldstate->chosen_exit)));
-    return;
-  }
-  newstate = newcirc->build_state;
-  tor_assert(newstate);
-  newstate->failure_count = oldstate->failure_count+1;
-  newstate->expiry_time = oldstate->expiry_time;
-  newstate->service_pending_final_cpath_ref =
-    oldstate->service_pending_final_cpath_ref;
-  ++(newstate->service_pending_final_cpath_ref->refcount);
-
-  newcirc->rend_data = rend_data_dup(oldcirc->rend_data);
-}
-
-/** Launch a circuit to serve as an introduction point for the service
- * <b>service</b> at the introduction point <b>nickname</b>
- */
-static int
-rend_service_launch_establish_intro(rend_service_t *service,
-                                    rend_intro_point_t *intro)
-{
-  origin_circuit_t *launched;
-  int flags = CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL;
-  const or_options_t *options = get_options();
-  extend_info_t *launch_ei = intro->extend_info;
-  extend_info_t *direct_ei = NULL;
-
-  /* Are we in single onion mode?
-   *
-   * We only use a one-hop path on the first attempt. If the first attempt
-   * fails, we use a 3-hop path for reachability / reliability.
-   * (Unlike v3, retries is incremented by the caller after it calls this
-   * function.)
-   */
-  if (rend_service_allow_non_anonymous_connection(options) &&
-      intro->circuit_retries == 0) {
-    /* Do we have a descriptor for the node?
-     * We've either just chosen it from the consensus, or we've just reviewed
-     * our intro points to see which ones are still valid, and deleted the ones
-     * that aren't in the consensus any more. */
-    const node_t *node = node_get_by_id(launch_ei->identity_digest);
-    if (BUG(!node)) {
-      /* The service has kept an intro point after it went missing from the
-       * consensus. If we did anything else here, it would be a consensus
-       * distinguisher. Which are less of an issue for single onion services,
-       * but still a bug. */
-      return -1;
-    }
-    /* Can we connect to the node directly? If so, replace launch_ei
-     * (a multi-hop extend_info) with one suitable for direct connection. */
-    if (rend_service_use_direct_connection_node(options, node)) {
-      direct_ei = extend_info_from_node(node, 1);
-      if (BUG(!direct_ei)) {
-        /* rend_service_use_direct_connection_node and extend_info_from_node
-         * disagree about which addresses on this node are permitted. This
-         * should never happen. Avoiding the connection is a safe response. */
-        return -1;
-      }
-      flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL;
-      launch_ei = direct_ei;
-    }
-  }
-  /* launch_ei is either intro->extend_info, or has been replaced with a valid
-   * extend_info for single onion service direct connection. */
-  tor_assert(launch_ei);
-  /* We must have the same intro when making a direct connection. */
-  tor_assert(tor_memeq(intro->extend_info->identity_digest,
-                       launch_ei->identity_digest,
-                       DIGEST_LEN));
-
-  log_info(LD_REND,
-           "Launching circuit to introduction point %s%s%s for service %s",
-           safe_str_client(extend_info_describe(intro->extend_info)),
-           direct_ei ? " via direct address " : "",
-           direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : "",
-           service->service_id);
-
-  rep_hist_note_used_internal(time(NULL), 1, 0);
-
-  ++service->n_intro_circuits_launched;
-  launched = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
-                             launch_ei, flags);
-
-  if (!launched) {
-    log_info(LD_REND,
-             "Can't launch circuit to establish introduction at %s%s%s.",
-             safe_str_client(extend_info_describe(intro->extend_info)),
-             direct_ei ? " via direct address " : "",
-             direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : ""
-             );
-    extend_info_free(direct_ei);
-    return -1;
-  }
-  /* We must have the same exit node even if cannibalized or direct connection.
-   */
-  tor_assert(tor_memeq(intro->extend_info->identity_digest,
-                       launched->build_state->chosen_exit->identity_digest,
-                       DIGEST_LEN));
-
-  launched->rend_data = rend_data_service_create(service->service_id,
-                                                 service->pk_digest, NULL,
-                                                 service->auth_type);
-  launched->intro_key = crypto_pk_dup_key(intro->intro_key);
-  if (launched->base_.state == CIRCUIT_STATE_OPEN)
-    rend_service_intro_has_opened(launched);
-  extend_info_free(direct_ei);
-  return 0;
-}
-
-/** Return the number of introduction points that are established for the
- * given service. */
-static unsigned int
-count_established_intro_points(const rend_service_t *service)
-{
-  unsigned int num = 0;
-
-  SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro,
-    num += intro->circuit_established
-  );
-  return num;
-}
-
-/** Return the number of introduction points that are or are being
- * established for the given service. This function iterates over all
- * circuit and count those that are linked to the service and are waiting
- * for the intro point to respond. */
-static unsigned int
-count_intro_point_circuits(const rend_service_t *service)
-{
-  unsigned int num_ipos = 0;
-  SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
-    if (!circ->marked_for_close &&
-        circ->state == CIRCUIT_STATE_OPEN &&
-        (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
-         circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
-      origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
-      if (oc->rend_data &&
-          rend_circuit_pk_digest_eq(oc, (uint8_t *) service->pk_digest)) {
-        num_ipos++;
-      }
-    }
-  }
-  SMARTLIST_FOREACH_END(circ);
-  return num_ipos;
-}
-
-/* Given a buffer of at least RELAY_PAYLOAD_SIZE bytes in <b>cell_body_out</b>,
-   write the body of a legacy ESTABLISH_INTRO cell in it. Use <b>intro_key</b>
-   as the intro point auth key, and <b>rend_circ_nonce</b> as the circuit
-   crypto material. On success, fill <b>cell_body_out</b> and return the number
-   of bytes written. On fail, return -1.
- */
-ssize_t
-rend_service_encode_establish_intro_cell(char *cell_body_out,
-                                         size_t cell_body_out_len,
-                                         crypto_pk_t *intro_key,
-                                         const char *rend_circ_nonce)
-{
-  int retval = -1;
-  int r;
-  int len = 0;
-  char auth[DIGEST_LEN + 9];
-
-  tor_assert(intro_key);
-  tor_assert(rend_circ_nonce);
-
-  /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
-  r = crypto_pk_asn1_encode(intro_key, cell_body_out+2,
-                            RELAY_PAYLOAD_SIZE-2);
-  if (r < 0) {
-    log_warn(LD_BUG, "Internal error; failed to establish intro point.");
-    goto err;
-  }
-  len = r;
-  set_uint16(cell_body_out, htons((uint16_t)len));
-  len += 2;
-  memcpy(auth, rend_circ_nonce, DIGEST_LEN);
-  memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
-  if (crypto_digest(cell_body_out+len, auth, DIGEST_LEN+9))
-    goto err;
-  len += 20;
-  r = crypto_pk_private_sign_digest(intro_key, cell_body_out+len,
-                                    cell_body_out_len - len,
-                                    cell_body_out, len);
-  if (r<0) {
-    log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
-    goto err;
-  }
-  len += r;
-
-  retval = len;
-
- err:
-  memwipe(auth, 0, sizeof(auth));
-
-  return retval;
-}
-
-/** Called when we're done building a circuit to an introduction point:
- *  sends a RELAY_ESTABLISH_INTRO cell.
- */
-void
-rend_service_intro_has_opened(origin_circuit_t *circuit)
-{
-  rend_service_t *service;
-  char buf[RELAY_PAYLOAD_SIZE];
-  char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  unsigned int expiring_nodes_len, num_ip_circuits, valid_ip_circuits = 0;
-  int reason = END_CIRC_REASON_TORPROTOCOL;
-  const char *rend_pk_digest;
-
-  tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
-  assert_circ_anonymity_ok(circuit, get_options());
-  tor_assert(circuit->cpath);
-  tor_assert(circuit->rend_data);
-  /* XXX: This is version 2 specific (only on supported). */
-  rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
-
-  base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
-                rend_pk_digest, REND_SERVICE_ID_LEN);
-
-  service = rend_service_get_by_pk_digest(rend_pk_digest);
-  if (!service) {
-    log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %u.",
-             safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id);
-    reason = END_CIRC_REASON_NOSUCHSERVICE;
-    goto err;
-  }
-
-  /* Take the current amount of expiring nodes and the current amount of IP
-   * circuits and compute how many valid IP circuits we have. */
-  expiring_nodes_len = (unsigned int) smartlist_len(service->expiring_nodes);
-  num_ip_circuits = count_intro_point_circuits(service);
-  /* Let's avoid an underflow. The valid_ip_circuits is initialized to 0 in
-   * case this condition turns out false because it means that all circuits
-   * are expiring so we need to keep this circuit. */
-  if (num_ip_circuits > expiring_nodes_len) {
-    valid_ip_circuits = num_ip_circuits - expiring_nodes_len;
-  }
-
-  /* If we already have enough introduction circuits for this service,
-   * redefine this one as a general circuit or close it, depending.
-   * Subtract the amount of expiring nodes here because the circuits are
-   * still opened. */
-  if (valid_ip_circuits > service->n_intro_points_wanted) {
-    const or_options_t *options = get_options();
-    /* Remove the intro point associated with this circuit, it's being
-     * repurposed or closed thus cleanup memory. */
-    rend_intro_point_t *intro = find_intro_point(circuit);
-    if (intro != NULL) {
-      smartlist_remove(service->intro_nodes, intro);
-      rend_intro_point_free(intro);
-    }
-
-    if (options->ExcludeNodes) {
-      /* XXXX in some future version, we can test whether the transition is
-         allowed or not given the actual nodes in the circuit.  But for now,
-         this case, we might as well close the thing. */
-      log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
-               "circuit, but we already have enough.  Closing it.");
-      reason = END_CIRC_REASON_NONE;
-      goto err;
-    } else {
-      tor_assert(circuit->build_state->is_internal);
-      log_info(LD_CIRC|LD_REND, "We have just finished an introduction "
-               "circuit, but we already have enough. Redefining purpose to "
-               "general; leaving as internal.");
-
-      if (circuit_should_use_vanguards(TO_CIRCUIT(circuit)->purpose)) {
-        circuit_change_purpose(TO_CIRCUIT(circuit),
-                CIRCUIT_PURPOSE_HS_VANGUARDS);
-      } else {
-        circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_C_GENERAL);
-      }
-
-      {
-        rend_data_free(circuit->rend_data);
-        circuit->rend_data = NULL;
-      }
-      {
-        crypto_pk_t *intro_key = circuit->intro_key;
-        circuit->intro_key = NULL;
-        crypto_pk_free(intro_key);
-      }
-
-      circuit_has_opened(circuit);
-      goto done;
-    }
-  }
-
-  log_info(LD_REND,
-           "Established circuit %u as introduction point for service %s",
-           (unsigned)circuit->base_.n_circ_id, serviceid);
-  circuit_log_path(LOG_INFO, LD_REND, circuit);
-
-  /* Send the ESTABLISH_INTRO cell */
-  {
-    ssize_t len;
-    len = rend_service_encode_establish_intro_cell(buf, sizeof(buf),
-                                      circuit->intro_key,
-                                      circuit->cpath->prev->rend_circ_nonce);
-    if (len < 0) {
-      reason = END_CIRC_REASON_INTERNAL;
-      goto err;
-    }
-
-    if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
-                                     RELAY_COMMAND_ESTABLISH_INTRO,
-                                     buf, len, circuit->cpath->prev)<0) {
-      log_info(LD_GENERAL,
-             "Couldn't send introduction request for service %s on circuit %u",
-             serviceid, (unsigned)circuit->base_.n_circ_id);
-      goto done;
-    }
-  }
-
-  /* We've attempted to use this circuit */
-  pathbias_count_use_attempt(circuit);
-
-  goto done;
-
- err:
-  circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
- done:
-  memwipe(buf, 0, sizeof(buf));
-  memwipe(serviceid, 0, sizeof(serviceid));
-
-  return;
-}
-
-/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
- * live introduction point, and note that the service descriptor is
- * now out-of-date. */
-int
-rend_service_intro_established(origin_circuit_t *circuit,
-                               const uint8_t *request,
-                               size_t request_len)
-{
-  rend_service_t *service;
-  rend_intro_point_t *intro;
-  char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  (void) request;
-  (void) request_len;
-  tor_assert(circuit->rend_data);
-  /* XXX: This is version 2 specific (only supported one for now). */
-  const char *rend_pk_digest =
-    (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
-
-  if (circuit->base_.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
-    log_warn(LD_PROTOCOL,
-             "received INTRO_ESTABLISHED cell on non-intro circuit.");
-    goto err;
-  }
-  service = rend_service_get_by_pk_digest(rend_pk_digest);
-  if (!service) {
-    log_warn(LD_REND, "Unknown service on introduction circuit %u.",
-             (unsigned)circuit->base_.n_circ_id);
-    goto err;
-  }
-  base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
-                rend_pk_digest, REND_SERVICE_ID_LEN);
-  /* We've just successfully established a intro circuit to one of our
-   * introduction point, account for it. */
-  intro = find_intro_point(circuit);
-  if (intro == NULL) {
-    log_warn(LD_REND,
-             "Introduction circuit established without a rend_intro_point_t "
-             "object for service %s on circuit %u",
-             safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id);
-    goto err;
-  }
-  intro->circuit_established = 1;
-  /* We might not have every introduction point ready but at this point we
-   * know that the descriptor needs to be uploaded. */
-  service->desc_is_dirty = time(NULL);
-  circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_INTRO);
-
-  log_info(LD_REND,
-           "Received INTRO_ESTABLISHED cell on circuit %u for service %s",
-           (unsigned)circuit->base_.n_circ_id, serviceid);
-
-  /* Getting a valid INTRODUCE_ESTABLISHED means we've successfully
-   * used the circ */
-  pathbias_mark_use_success(circuit);
-
-  return 0;
- err:
-  circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL);
-  return -1;
-}
-
-/** Called once a circuit to a rendezvous point is established: sends a
- *  RELAY_COMMAND_RENDEZVOUS1 cell.
- */
-void
-rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
-{
-  rend_service_t *service;
-  char buf[RELAY_PAYLOAD_SIZE];
-  crypt_path_t *hop;
-  char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  char hexcookie[9];
-  int reason;
-  const char *rend_cookie, *rend_pk_digest;
-
-  tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
-  tor_assert(circuit->cpath);
-  tor_assert(circuit->build_state);
-  assert_circ_anonymity_ok(circuit, get_options());
-  tor_assert(circuit->rend_data);
-
-  /* XXX: This is version 2 specific (only one supported). */
-  rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data,
-                                                    NULL);
-  rend_cookie = circuit->rend_data->rend_cookie;
-
-  /* Declare the circuit dirty to avoid reuse, and for path-bias. We set the
-   * timestamp regardless of its content because that circuit could have been
-   * cannibalized so in any cases, we are about to use that circuit more. */
-  circuit->base_.timestamp_dirty = time(NULL);
-
-  /* This may be redundant */
-  pathbias_count_use_attempt(circuit);
-
-  hop = circuit->build_state->service_pending_final_cpath_ref->cpath;
-
-  base16_encode(hexcookie,9, rend_cookie,4);
-  base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
-                rend_pk_digest, REND_SERVICE_ID_LEN);
-
-  log_info(LD_REND,
-           "Done building circuit %u to rendezvous with "
-           "cookie %s for service %s",
-           (unsigned)circuit->base_.n_circ_id, hexcookie, serviceid);
-  circuit_log_path(LOG_INFO, LD_REND, circuit);
-
-  /* Clear the 'in-progress HS circ has timed out' flag for
-   * consistency with what happens on the client side; this line has
-   * no effect on Tor's behaviour. */
-  circuit->hs_circ_has_timed_out = 0;
-
-  /* If hop is NULL, another rend circ has already connected to this
-   * rend point.  Close this circ. */
-  if (hop == NULL) {
-    log_info(LD_REND, "Another rend circ has already reached this rend point; "
-             "closing this rend circ.");
-    reason = END_CIRC_REASON_NONE;
-    goto err;
-  }
-
-  /* Remove our final cpath element from the reference, so that no
-   * other circuit will try to use it.  Store it in
-   * pending_final_cpath for now to ensure that it will be freed if
-   * our rendezvous attempt fails. */
-  circuit->build_state->pending_final_cpath = hop;
-  circuit->build_state->service_pending_final_cpath_ref->cpath = NULL;
-
-  service = rend_service_get_by_pk_digest(rend_pk_digest);
-  if (!service) {
-    log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
-             "rendezvous circuit.");
-    reason = END_CIRC_REASON_INTERNAL;
-    goto err;
-  }
-
-  /* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
-  memcpy(buf, rend_cookie, REND_COOKIE_LEN);
-  if (crypto_dh_get_public(hop->rend_dh_handshake_state,
-                           buf+REND_COOKIE_LEN, DH1024_KEY_LEN)<0) {
-    log_warn(LD_GENERAL,"Couldn't get DH public key.");
-    reason = END_CIRC_REASON_INTERNAL;
-    goto err;
-  }
-  memcpy(buf+REND_COOKIE_LEN+DH1024_KEY_LEN, hop->rend_circ_nonce,
-         DIGEST_LEN);
-
-  /* Send the cell */
-  if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
-                                   RELAY_COMMAND_RENDEZVOUS1,
-                                   buf, HS_LEGACY_RENDEZVOUS_CELL_SIZE,
-                                   circuit->cpath->prev)<0) {
-    log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell.");
-    goto done;
-  }
-
-  crypto_dh_free(hop->rend_dh_handshake_state);
-  hop->rend_dh_handshake_state = NULL;
-
-  /* Append the cpath entry. */
-  hop->state = CPATH_STATE_OPEN;
-  /* set the windows to default. these are the windows
-   * that the service thinks the client has.
-   */
-  hop->package_window = circuit_initial_package_window();
-  hop->deliver_window = CIRCWINDOW_START;
-
-  cpath_extend_linked_list(&circuit->cpath, hop);
-  circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
-
-  /* Change the circuit purpose. */
-  circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_REND_JOINED);
-
-  goto done;
-
- err:
-  circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
- done:
-  memwipe(buf, 0, sizeof(buf));
-  memwipe(serviceid, 0, sizeof(serviceid));
-  memwipe(hexcookie, 0, sizeof(hexcookie));
-
-  return;
-}
-
-/*
- * Manage introduction points
- */
-
-/** Return the (possibly non-open) introduction circuit ending at
- * <b>intro</b> for the service whose public key is <b>pk_digest</b>.
- * (<b>desc_version</b> is ignored). Return NULL if no such service is
- * found.
- */
-static origin_circuit_t *
-find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
-{
-  origin_circuit_t *circ = NULL;
-
-  tor_assert(intro);
-  while ((circ = circuit_get_next_by_pk_and_purpose(circ,
-                         (uint8_t *) pk_digest, CIRCUIT_PURPOSE_S_INTRO))) {
-    if (tor_memeq(circ->build_state->chosen_exit->identity_digest,
-                intro->extend_info->identity_digest, DIGEST_LEN) &&
-        circ->rend_data) {
-      return circ;
-    }
-  }
-
-  circ = NULL;
-  while ((circ = circuit_get_next_by_pk_and_purpose(circ,
-                         (uint8_t *) pk_digest,
-                         CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
-    if (tor_memeq(circ->build_state->chosen_exit->identity_digest,
-                intro->extend_info->identity_digest, DIGEST_LEN) &&
-        circ->rend_data) {
-      return circ;
-    }
-  }
-  return NULL;
-}
-
-/** Return the corresponding introdution point using the circuit <b>circ</b>
- * found in the <b>service</b>. NULL is returned if not found. */
-static rend_intro_point_t *
-find_expiring_intro_point(rend_service_t *service, origin_circuit_t *circ)
-{
-  tor_assert(service);
-  tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
-             TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
-
-  SMARTLIST_FOREACH(service->expiring_nodes, rend_intro_point_t *,
-                    intro_point,
-    if (crypto_pk_eq_keys(intro_point->intro_key, circ->intro_key)) {
-      return intro_point;
-  });
-
-  return NULL;
-}
-
-/** Return a pointer to the rend_intro_point_t corresponding to the
- * service-side introduction circuit <b>circ</b>. */
-static rend_intro_point_t *
-find_intro_point(origin_circuit_t *circ)
-{
-  const char *serviceid;
-  rend_service_t *service = NULL;
-
-  tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
-             TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
-  tor_assert(circ->rend_data);
-  serviceid = rend_data_get_address(circ->rend_data);
-
-  SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s,
-    if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) {
-      service = s;
-      break;
-    });
-
-  if (service == NULL) return NULL;
-
-  SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro_point,
-    if (crypto_pk_eq_keys(intro_point->intro_key, circ->intro_key)) {
-      return intro_point;
-    });
-
-  return NULL;
-}
-
-/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b>
- * associated with the rend_service_descriptor_t <b>renddesc</b> to
- * the responsible hidden service directories OR the hidden service
- * directories specified by <b>hs_dirs</b>; <b>service_id</b> and
- * <b>seconds_valid</b> are only passed for logging purposes.
- */
-void
-directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
-                         smartlist_t *descs, smartlist_t *hs_dirs,
-                         const char *service_id, int seconds_valid)
-{
-  int i, j, failed_upload = 0;
-  smartlist_t *responsible_dirs = smartlist_new();
-  smartlist_t *successful_uploads = smartlist_new();
-  routerstatus_t *hs_dir;
-  for (i = 0; i < smartlist_len(descs); i++) {
-    rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
-    /** If any HSDirs are specified, they should be used instead of
-     *  the responsible directories */
-    if (hs_dirs && smartlist_len(hs_dirs) > 0) {
-      smartlist_add_all(responsible_dirs, hs_dirs);
-    } else {
-      /* Determine responsible dirs. */
-      if (hid_serv_get_responsible_directories(responsible_dirs,
-                                               desc->desc_id) < 0) {
-        log_warn(LD_REND, "Could not determine the responsible hidden service "
-                          "directories to post descriptors to.");
-        control_event_hs_descriptor_upload(service_id,
-                                           "UNKNOWN",
-                                           "UNKNOWN", NULL);
-        goto done;
-      }
-    }
-    for (j = 0; j < smartlist_len(responsible_dirs); j++) {
-      char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-      char *hs_dir_ip;
-      const node_t *node;
-      rend_data_t *rend_data;
-      hs_dir = smartlist_get(responsible_dirs, j);
-      if (smartlist_contains_digest(renddesc->successful_uploads,
-                                hs_dir->identity_digest))
-        /* Don't upload descriptor if we succeeded in doing so last time. */
-        continue;
-      node = node_get_by_id(hs_dir->identity_digest);
-      if (!node || !node_has_preferred_descriptor(node,0)) {
-        log_info(LD_REND, "Not launching upload for for v2 descriptor to "
-                          "hidden service directory %s; we don't have its "
-                          "router descriptor. Queuing for later upload.",
-                 safe_str_client(routerstatus_describe(hs_dir)));
-        failed_upload = -1;
-        continue;
-      }
-      /* Send publish request. */
-
-      /* We need the service ID to identify which service did the upload
-       * request. Lookup is made in rend_service_desc_has_uploaded(). */
-      rend_data = rend_data_client_create(service_id, desc->desc_id, NULL,
-                                          REND_NO_AUTH);
-      directory_request_t *req =
-        directory_request_new(DIR_PURPOSE_UPLOAD_RENDDESC_V2);
-      directory_request_set_routerstatus(req, hs_dir);
-      directory_request_set_indirection(req, DIRIND_ANONYMOUS);
-      directory_request_set_payload(req,
-                                    desc->desc_str, strlen(desc->desc_str));
-      directory_request_set_rend_query(req, rend_data);
-      directory_initiate_request(req);
-      directory_request_free(req);
-
-      rend_data_free(rend_data);
-      base32_encode(desc_id_base32, sizeof(desc_id_base32),
-                    desc->desc_id, DIGEST_LEN);
-      hs_dir_ip = tor_addr_to_str_dup(&hs_dir->ipv4_addr);
-      if (hs_dir_ip) {
-        log_info(LD_REND, "Launching upload for v2 descriptor for "
-                          "service '%s' with descriptor ID '%s' with validity "
-                          "of %d seconds to hidden service directory '%s' on "
-                          "%s:%d.",
-                 safe_str_client(service_id),
-                 safe_str_client(desc_id_base32),
-                 seconds_valid,
-                 hs_dir->nickname,
-                 hs_dir_ip,
-                 hs_dir->ipv4_orport);
-        tor_free(hs_dir_ip);
-      }
-
-      control_event_hs_descriptor_upload(service_id,
-                                         hs_dir->identity_digest,
-                                         desc_id_base32, NULL);
-      /* Remember successful upload to this router for next time. */
-      if (!smartlist_contains_digest(successful_uploads,
-                                     hs_dir->identity_digest))
-        smartlist_add(successful_uploads, hs_dir->identity_digest);
-    }
-    smartlist_clear(responsible_dirs);
-  }
-  if (!failed_upload) {
-    if (renddesc->successful_uploads) {
-      SMARTLIST_FOREACH(renddesc->successful_uploads, char *, c, tor_free(c););
-      smartlist_free(renddesc->successful_uploads);
-      renddesc->successful_uploads = NULL;
-    }
-    renddesc->all_uploads_performed = 1;
-  } else {
-    /* Remember which routers worked this time, so that we don't upload the
-     * descriptor to them again. */
-    if (!renddesc->successful_uploads)
-      renddesc->successful_uploads = smartlist_new();
-    SMARTLIST_FOREACH(successful_uploads, const char *, c, {
-      if (!smartlist_contains_digest(renddesc->successful_uploads, c)) {
-        char *hsdir_id = tor_memdup(c, DIGEST_LEN);
-        smartlist_add(renddesc->successful_uploads, hsdir_id);
-      }
-    });
-  }
- done:
-  smartlist_free(responsible_dirs);
-  smartlist_free(successful_uploads);
-}
-
-/** Encode and sign an up-to-date service descriptor for <b>service</b>,
- * and upload it/them to the responsible hidden service directories.
- */
-static void
-upload_service_descriptor(rend_service_t *service)
-{
-  time_t now = time(NULL);
-  int rendpostperiod;
-  char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  int uploaded = 0;
-
-  rendpostperiod = get_options()->RendPostPeriod;
-
-  networkstatus_t *c = networkstatus_get_latest_consensus();
-  if (c && smartlist_len(c->routerstatus_list) > 0) {
-    int seconds_valid, i, j, num_descs;
-    smartlist_t *descs = smartlist_new();
-    smartlist_t *client_cookies = smartlist_new();
-    /* Either upload a single descriptor (including replicas) or one
-     * descriptor for each authorized client in case of authorization
-     * type 'stealth'. */
-    num_descs = service->auth_type == REND_STEALTH_AUTH ?
-                    smartlist_len(service->clients) : 1;
-    for (j = 0; j < num_descs; j++) {
-      crypto_pk_t *client_key = NULL;
-      rend_authorized_client_t *client = NULL;
-      smartlist_clear(client_cookies);
-      switch (service->auth_type) {
-        case REND_NO_AUTH:
-        case REND_V3_AUTH:
-          /* Do nothing here. */
-          break;
-        case REND_BASIC_AUTH:
-          SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
-              cl, smartlist_add(client_cookies, cl->descriptor_cookie));
-          break;
-        case REND_STEALTH_AUTH:
-          client = smartlist_get(service->clients, j);
-          client_key = client->client_key;
-          smartlist_add(client_cookies, client->descriptor_cookie);
-          break;
-      }
-      /* Encode the current descriptor. */
-      seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
-                                                 now, 0,
-                                                 service->auth_type,
-                                                 client_key,
-                                                 client_cookies);
-      if (seconds_valid < 0) {
-        log_warn(LD_BUG, "Internal error: couldn't encode service "
-                 "descriptor; not uploading.");
-        smartlist_free(descs);
-        smartlist_free(client_cookies);
-        return;
-      }
-      rend_get_service_id(service->desc->pk, serviceid);
-      if (get_options()->PublishHidServDescriptors) {
-        /* Post the current descriptors to the hidden service directories. */
-        /* This log message is used by Chutney as part of its bootstrap
-         * detection mechanism. Please don't change without first checking
-         * Chutney. */
-        log_info(LD_REND, "Launching upload for hidden service %s",
-                     serviceid);
-        directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
-                                 seconds_valid);
-      }
-      /* Free memory for descriptors. */
-      for (i = 0; i < smartlist_len(descs); i++)
-        rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i));
-      smartlist_clear(descs);
-      /* Update next upload time. */
-      if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
-          > rendpostperiod)
-        service->next_upload_time = now + rendpostperiod;
-      else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
-        service->next_upload_time = now + seconds_valid + 1;
-      else
-        service->next_upload_time = now + seconds_valid -
-            REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
-      /* Post also the next descriptors, if necessary. */
-      if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
-        seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
-                                                   now, 1,
-                                                   service->auth_type,
-                                                   client_key,
-                                                   client_cookies);
-        if (seconds_valid < 0) {
-          log_warn(LD_BUG, "Internal error: couldn't encode service "
-                   "descriptor; not uploading.");
-          smartlist_free(descs);
-          smartlist_free(client_cookies);
-          return;
-        }
-        if (get_options()->PublishHidServDescriptors) {
-          directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
-                                   seconds_valid);
-        }
-        /* Free memory for descriptors. */
-        for (i = 0; i < smartlist_len(descs); i++)
-          rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i));
-        smartlist_clear(descs);
-      }
-    }
-    smartlist_free(descs);
-    smartlist_free(client_cookies);
-    uploaded = 1;
-    if (get_options()->PublishHidServDescriptors) {
-      log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
-    } else {
-      log_info(LD_REND, "Successfully stored created v2 rend descriptors!");
-    }
-  }
-
-  /* If not uploaded, try again in one minute. */
-  if (!uploaded)
-    service->next_upload_time = now + 60;
-
-  /* Unmark dirty flag of this service. */
-  service->desc_is_dirty = 0;
-}
-
-/** Return the number of INTRODUCE2 cells this hidden service has received
- * from this intro point. */
-static int
-intro_point_accepted_intro_count(rend_intro_point_t *intro)
-{
-  return intro->accepted_introduce2_count;
-}
-
-/** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we
- * should stop publishing it in new descriptors and eventually close
- * it). */
-static int
-intro_point_should_expire_now(rend_intro_point_t *intro,
-                              time_t now)
-{
-  tor_assert(intro != NULL);
-
-  if (intro->time_published == -1) {
-    /* Don't expire an intro point if we haven't even published it yet. */
-    return 0;
-  }
-
-  if (intro_point_accepted_intro_count(intro) >=
-      intro->max_introductions) {
-    /* This intro point has been used too many times.  Expire it now. */
-    return 1;
-  }
-
-  if (intro->time_to_expire == -1) {
-    /* This intro point has been published, but we haven't picked an
-     * expiration time for it.  Pick one now. */
-    int intro_point_lifetime_seconds =
-      crypto_rand_int_range(INTRO_POINT_LIFETIME_MIN_SECONDS,
-                            INTRO_POINT_LIFETIME_MAX_SECONDS);
-
-    /* Start the expiration timer now, rather than when the intro
-     * point was first published.  There shouldn't be much of a time
-     * difference. */
-    intro->time_to_expire = now + intro_point_lifetime_seconds;
-
-    return 0;
-  }
-
-  /* This intro point has a time to expire set already.  Use it. */
-  return (now >= intro->time_to_expire);
-}
-
-/** Iterate over intro points in the given service and remove the invalid
- * ones. For an intro point object to be considered invalid, the circuit
- * _and_ node need to have disappeared.
- *
- * If the intro point should expire, it's placed into the expiring_nodes
- * list of the service and removed from the active intro nodes list.
- *
- * If <b>exclude_nodes</b> is not NULL, add the valid nodes to it.
- *
- * If <b>retry_nodes</b> is not NULL, add the valid node to it if the
- * circuit disappeared but the node is still in the consensus. */
-static void
-remove_invalid_intro_points(rend_service_t *service,
-                            smartlist_t *exclude_nodes,
-                            smartlist_t *retry_nodes, time_t now)
-{
-  tor_assert(service);
-
-  /* Remove any expired nodes that doesn't have a circuit. */
-  SMARTLIST_FOREACH_BEGIN(service->expiring_nodes, rend_intro_point_t *,
-                          intro) {
-    origin_circuit_t *intro_circ =
-      find_intro_circuit(intro, service->pk_digest);
-    if (intro_circ) {
-      continue;
-    }
-    /* No more circuit, cleanup the into point object. */
-    SMARTLIST_DEL_CURRENT(service->expiring_nodes, intro);
-    rend_intro_point_free(intro);
-  } SMARTLIST_FOREACH_END(intro);
-
-  SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *,
-                          intro) {
-    /* Find the introduction point node object. */
-    const node_t *node =
-      node_get_by_id(intro->extend_info->identity_digest);
-    /* Find the intro circuit, this might be NULL. */
-    origin_circuit_t *intro_circ =
-      find_intro_circuit(intro, service->pk_digest);
-
-    /* Add the valid node to the exclusion list so we don't try to establish
-     * an introduction point to it again. */
-    if (node && exclude_nodes) {
-      smartlist_add(exclude_nodes, (void*) node);
-    }
-
-    /* First, make sure we still have a valid circuit for this intro point.
-     * If we dont, we'll give up on it and make a new one. */
-    if (intro_circ == NULL) {
-      log_info(LD_REND, "Attempting to retry on %s as intro point for %s"
-               " (circuit disappeared).",
-               safe_str_client(extend_info_describe(intro->extend_info)),
-               safe_str_client(service->service_id));
-      /* We've lost the circuit for this intro point, flag it so it can be
-       * accounted for when considiring uploading a descriptor. */
-      intro->circuit_established = 0;
-
-      /* Node is gone or we've reached our maximum circuit creation retry
-       * count, clean up everything, we'll find a new one. */
-      if (node == NULL ||
-          intro->circuit_retries >= MAX_INTRO_POINT_CIRCUIT_RETRIES) {
-        rend_intro_point_free(intro);
-        SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
-        /* We've just killed the intro point, nothing left to do. */
-        continue;
-      }
-
-      /* The intro point is still alive so let's try to use it again because
-       * we have a published descriptor containing it. Keep the intro point
-       * in the intro_nodes list because it's still valid, we are rebuilding
-       * a circuit to it. */
-      if (retry_nodes) {
-        smartlist_add(retry_nodes, intro);
-      }
-    }
-    /* else, the circuit is valid so in both cases, node being alive or not,
-     * we leave the circuit and intro point object as is. Closing the
-     * circuit here would leak new consensus timing and freeing the intro
-     * point object would make the intro circuit unusable. */
-
-    /* Now, check if intro point should expire. If it does, queue it so
-     * it can be cleaned up once it has been replaced properly. */
-    if (intro_point_should_expire_now(intro, now)) {
-      log_info(LD_REND, "Expiring %s as intro point for %s.",
-               safe_str_client(extend_info_describe(intro->extend_info)),
-               safe_str_client(service->service_id));
-      /* We might have put it in the retry list if so, undo. */
-      if (retry_nodes) {
-        smartlist_remove(retry_nodes, intro);
-      }
-      smartlist_add(service->expiring_nodes, intro);
-      SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
-      /* Intro point is expired, we need a new one thus don't consider it
-       * anymore has a valid established intro point. */
-      intro->circuit_established = 0;
-    }
-  } SMARTLIST_FOREACH_END(intro);
-}
-
-/** A new descriptor has been successfully uploaded for the given
- * <b>rend_data</b>. Remove and free the expiring nodes from the associated
- * service. */
-void
-rend_service_desc_has_uploaded(const rend_data_t *rend_data)
-{
-  rend_service_t *service;
-  const char *onion_address;
-
-  tor_assert(rend_data);
-
-  onion_address = rend_data_get_address(rend_data);
-
-  service = rend_service_get_by_service_id(onion_address);
-  if (service == NULL) {
-    return;
-  }
-
-  SMARTLIST_FOREACH_BEGIN(service->expiring_nodes, rend_intro_point_t *,
-                          intro) {
-    origin_circuit_t *intro_circ =
-      find_intro_circuit(intro, service->pk_digest);
-    if (intro_circ != NULL) {
-      circuit_mark_for_close(TO_CIRCUIT(intro_circ),
-                             END_CIRC_REASON_FINISHED);
-    }
-    SMARTLIST_DEL_CURRENT(service->expiring_nodes, intro);
-    rend_intro_point_free(intro);
-  } SMARTLIST_FOREACH_END(intro);
-}
-
-/** Don't try to build more than this many circuits before giving up
- * for a while. Dynamically calculated based on the configured number of
- * introduction points for the service, n_intro_points_wanted. */
-static int
-rend_max_intro_circs_per_period(unsigned int n_intro_points_wanted)
-{
-  /* Allow all but one of the initial connections to fail and be
-   * retried. (If all fail, we *want* to wait, because something is broken.) */
-  tor_assert(n_intro_points_wanted <= NUM_INTRO_POINTS_MAX);
-
-  /* For the normal use case, 3 intro points plus 2 extra for performance and
-   * allow that twice because once every 24h or so, we can do it twice for two
-   * descriptors that is the current one and the next one. So (3 + 2) * 2 ==
-   * 12 allowed attempts for one period. */
-  return ((n_intro_points_wanted + NUM_INTRO_POINTS_EXTRA) * 2);
-}
-
-/** For every service, check how many intro points it currently has, and:
- *  - Invalidate introdution points based on specific criteria, see
- *  remove_invalid_intro_points comments.
- *  - Pick new intro points as necessary.
- *  - Launch circuits to any new intro points.
- *
- * This is called once a second by the main loop.
- */
-void
-rend_consider_services_intro_points(time_t now)
-{
-  int i;
-  const or_options_t *options = get_options();
-  /* Are we in single onion mode? */
-  const int allow_direct = rend_service_allow_non_anonymous_connection(
-                                                                get_options());
-  /* List of nodes we need to _exclude_ when choosing a new node to
-   * establish an intro point to. */
-  smartlist_t *exclude_nodes;
-  /* List of nodes we need to retry to build a circuit on them because the
-   * node is valid but circuit died. */
-  smartlist_t *retry_nodes;
-
-  if (!have_completed_a_circuit())
-    return;
-
-  exclude_nodes = smartlist_new();
-  retry_nodes = smartlist_new();
-
-  SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, service) {
-    int r;
-    /* Number of intro points we want to open and add to the intro nodes
-     * list of the service. */
-    unsigned int n_intro_points_to_open;
-    /* Have an unsigned len so we can use it to compare values else gcc is
-     * not happy with unmatching signed comparison. */
-    unsigned int intro_nodes_len;
-    /* Different service are allowed to have the same introduction point as
-     * long as they are on different circuit thus why we clear this list. */
-    smartlist_clear(exclude_nodes);
-    smartlist_clear(retry_nodes);
-
-    /* Cleanup the invalid intro points and save the node objects, if any,
-     * in the exclude_nodes and retry_nodes lists. */
-    remove_invalid_intro_points(service, exclude_nodes, retry_nodes, now);
-
-    /* This retry period is important here so we don't stress circuit
-     * creation. */
-
-    if (now > service->intro_period_started + INTRO_CIRC_RETRY_PERIOD) {
-      /* One period has elapsed:
-       *  - if we stopped, we can try building circuits again,
-       *  - if we haven't, we reset the circuit creation counts. */
-      rend_log_intro_limit(service, LOG_INFO);
-      service->intro_period_started = now;
-      service->n_intro_circuits_launched = 0;
-    } else if (service->n_intro_circuits_launched >=
-               rend_max_intro_circs_per_period(
-                                      service->n_intro_points_wanted)) {
-      /* We have failed too many times in this period; wait for the next
-       * one before we try to initiate any more connections. */
-      rend_log_intro_limit(service, LOG_WARN);
-      continue;
-    }
-
-    /* Let's try to rebuild circuit on the nodes we want to retry on. */
-    SMARTLIST_FOREACH_BEGIN(retry_nodes, rend_intro_point_t *, intro) {
-      r = rend_service_launch_establish_intro(service, intro);
-      if (r < 0) {
-        log_warn(LD_REND, "Error launching circuit to node %s for service %s.",
-                 safe_str_client(extend_info_describe(intro->extend_info)),
-                 safe_str_client(service->service_id));
-        /* Unable to launch a circuit to that intro point, remove it from
-         * the valid list so we can create a new one. */
-        smartlist_remove(service->intro_nodes, intro);
-        rend_intro_point_free(intro);
-        continue;
-      }
-      intro->circuit_retries++;
-    } SMARTLIST_FOREACH_END(intro);
-
-    /* Avoid mismatched signed comparison below. */
-    intro_nodes_len = (unsigned int) smartlist_len(service->intro_nodes);
-
-    /* Quiescent state, we have more or the equal amount of wanted node for
-     * this service. Proceed to the next service. We can have more nodes
-     * because we launch extra preemptive circuits if our intro nodes list was
-     * originally empty for performance reasons. */
-    if (intro_nodes_len >= service->n_intro_points_wanted) {
-      continue;
-    }
-
-    /* Number of intro points we want to open which is the wanted amount minus
-     * the current amount of valid nodes. We know that this won't underflow
-     * because of the check above. */
-    n_intro_points_to_open = service->n_intro_points_wanted - intro_nodes_len;
-    if (intro_nodes_len == 0) {
-      /* We want to end up with n_intro_points_wanted intro points, but if
-       * we have no intro points at all (chances are they all cycled or we
-       * are starting up), we launch NUM_INTRO_POINTS_EXTRA extra circuits
-       * and use the first n_intro_points_wanted that complete. See proposal
-       * #155, section 4 for the rationale of this which is purely for
-       * performance.
-       *
-       * The ones after the first n_intro_points_to_open will be converted
-       * to 'general' internal circuits in rend_service_intro_has_opened(),
-       * and then we'll drop them from the list of intro points. */
-      n_intro_points_to_open += NUM_INTRO_POINTS_EXTRA;
-    }
-
-    for (i = 0; i < (int) n_intro_points_to_open; i++) {
-      const node_t *node;
-      rend_intro_point_t *intro;
-      router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
-      router_crn_flags_t direct_flags = flags;
-      direct_flags |= CRN_PREF_ADDR;
-      direct_flags |= CRN_DIRECT_CONN;
-
-      node = router_choose_random_node(exclude_nodes,
-                                       options->ExcludeNodes,
-                                       allow_direct ? direct_flags : flags);
-      /* If we are in single onion mode, retry node selection for a 3-hop
-       * path */
-      if (allow_direct && !node) {
-        log_info(LD_REND,
-                 "Unable to find an intro point that we can connect to "
-                 "directly for %s, falling back to a 3-hop path.",
-                 safe_str_client(service->service_id));
-        node = router_choose_random_node(exclude_nodes,
-                                         options->ExcludeNodes, flags);
-      }
-
-      if (!node) {
-        log_warn(LD_REND,
-                 "We only have %d introduction points established for %s; "
-                 "wanted %u.",
-                 smartlist_len(service->intro_nodes),
-                 safe_str_client(service->service_id),
-                 n_intro_points_to_open);
-        break;
-      }
-      /* Add the chosen node to the exclusion list in order to avoid picking
-       * it again in the next iteration. */
-      smartlist_add(exclude_nodes, (void*)node);
-      intro = tor_malloc_zero(sizeof(rend_intro_point_t));
-      /* extend_info is for clients, so we want the multi-hop primary ORPort,
-       * even if we are a single onion service and intend to connect to it
-       * directly ourselves. */
-      intro->extend_info = extend_info_from_node(node, 0);
-      if (BUG(intro->extend_info == NULL)) {
-        tor_free(intro);
-        break;
-      }
-      intro->intro_key = crypto_pk_new();
-      const int fail = crypto_pk_generate_key(intro->intro_key);
-      tor_assert(!fail);
-      intro->time_published = -1;
-      intro->time_to_expire = -1;
-      intro->max_introductions =
-        crypto_rand_int_range(INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS,
-                              INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
-      smartlist_add(service->intro_nodes, intro);
-      log_info(LD_REND, "Picked router %s as an intro point for %s.",
-               safe_str_client(node_describe(node)),
-               safe_str_client(service->service_id));
-      /* Establish new introduction circuit to our chosen intro point. */
-      r = rend_service_launch_establish_intro(service, intro);
-      if (r < 0) {
-        log_warn(LD_REND, "Error launching circuit to node %s for service %s.",
-                 safe_str_client(extend_info_describe(intro->extend_info)),
-                 safe_str_client(service->service_id));
-        /* This function will be called again by the main loop so this intro
-         * point without a intro circuit will be retried on or removed after
-         * a maximum number of attempts. */
-      }
-    }
-  } SMARTLIST_FOREACH_END(service);
-  smartlist_free(exclude_nodes);
-  smartlist_free(retry_nodes);
-}
-
-#define MIN_REND_INITIAL_POST_DELAY (30)
-#define MIN_REND_INITIAL_POST_DELAY_TESTING (5)
-
-/** Regenerate and upload rendezvous service descriptors for all
- * services, if necessary. If the descriptor has been dirty enough
- * for long enough, definitely upload; else only upload when the
- * periodic timeout has expired.
- *
- * For the first upload, pick a random time between now and two periods
- * from now, and pick it independently for each service.
- */
-void
-rend_consider_services_upload(time_t now)
-{
-  int i;
-  rend_service_t *service;
-  const or_options_t *options = get_options();
-  int rendpostperiod = options->RendPostPeriod;
-  int rendinitialpostdelay = (options->TestingTorNetwork ?
-                              MIN_REND_INITIAL_POST_DELAY_TESTING :
-                              MIN_REND_INITIAL_POST_DELAY);
-
-  for (i=0; i < smartlist_len(rend_service_list); ++i) {
-    service = smartlist_get(rend_service_list, i);
-    if (!service->next_upload_time) { /* never been uploaded yet */
-      /* The fixed lower bound of rendinitialpostdelay seconds ensures that
-       * the descriptor is stable before being published. See comment below. */
-      service->next_upload_time =
-        now + rendinitialpostdelay + crypto_rand_int(2*rendpostperiod);
-      /* Single Onion Services prioritise availability over hiding their
-       * startup time, as their IP address is publicly discoverable anyway.
-       */
-      if (rend_service_reveal_startup_time(options)) {
-        service->next_upload_time = now + rendinitialpostdelay;
-      }
-    }
-    /* Does every introduction points have been established? */
-    unsigned int intro_points_ready =
-      count_established_intro_points(service) >=
-        service->n_intro_points_wanted;
-    if (intro_points_ready &&
-        (service->next_upload_time < now ||
-        (service->desc_is_dirty &&
-         service->desc_is_dirty < now-rendinitialpostdelay))) {
-      /* if it's time, or if the directory servers have a wrong service
-       * descriptor and ours has been stable for rendinitialpostdelay seconds,
-       * upload a new one of each format. */
-      rend_service_update_descriptor(service);
-      upload_service_descriptor(service);
-    }
-  }
-}
-
-/** True if the list of available router descriptors might have changed so
- * that we should have a look whether we can republish previously failed
- * rendezvous service descriptors. */
-static int consider_republishing_rend_descriptors = 1;
-
-/** Called when our internal view of the directory has changed, so that we
- * might have router descriptors of hidden service directories available that
- * we did not have before. */
-void
-rend_hsdir_routers_changed(void)
-{
-  consider_republishing_rend_descriptors = 1;
-}
-
-/** Consider republication of v2 rendezvous service descriptors that failed
- * previously, but without regenerating descriptor contents.
- */
-void
-rend_consider_descriptor_republication(void)
-{
-  int i;
-  rend_service_t *service;
-
-  if (!consider_republishing_rend_descriptors)
-    return;
-  consider_republishing_rend_descriptors = 0;
-
-  if (!get_options()->PublishHidServDescriptors)
-    return;
-
-  for (i=0; i < smartlist_len(rend_service_list); ++i) {
-    service = smartlist_get(rend_service_list, i);
-    if (service->desc && !service->desc->all_uploads_performed) {
-      /* If we failed in uploading a descriptor last time, try again *without*
-       * updating the descriptor's contents. */
-      upload_service_descriptor(service);
-    }
-  }
-}
-
-/** Log the status of introduction points for all rendezvous services
- * at log severity <b>severity</b>.
- */
-void
-rend_service_dump_stats(int severity)
-{
-  rend_service_t *service;
-  rend_intro_point_t *intro;
-  const char *safe_name;
-  origin_circuit_t *circ;
-
-  for (int i = 0; i < smartlist_len(rend_service_list); ++i) {
-    service = smartlist_get(rend_service_list, i);
-    tor_log(severity, LD_GENERAL, "Service configured in %s:",
-            rend_service_escaped_dir(service));
-    for (int j = 0; j < smartlist_len(service->intro_nodes); ++j) {
-      intro = smartlist_get(service->intro_nodes, j);
-      safe_name = safe_str_client(intro->extend_info->nickname);
-
-      circ = find_intro_circuit(intro, service->pk_digest);
-      if (!circ) {
-        tor_log(severity, LD_GENERAL, "  Intro point %d at %s: no circuit",
-            j, safe_name);
-        continue;
-      }
-      tor_log(severity, LD_GENERAL, "  Intro point %d at %s: circuit is %s",
-          j, safe_name, circuit_state_to_string(circ->base_.state));
-    }
-  }
-}
-
-/** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
- * <b>circ</b>, and look up the port and address based on conn-\>port.
- * Assign the actual conn-\>addr and conn-\>port. Return -2 on failure
- * for which the circuit should be closed, -1 on other failure,
- * or 0 for success.
- */
-int
-rend_service_set_connection_addr_port(edge_connection_t *conn,
-                                      origin_circuit_t *circ)
-{
-  rend_service_t *service;
-  char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
-  const char *rend_pk_digest;
-
-  tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
-  tor_assert(circ->rend_data);
-  log_debug(LD_REND,"beginning to hunt for addr/port");
-  rend_pk_digest = (char *) rend_data_get_pk_digest(circ->rend_data, NULL);
-  base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
-                rend_pk_digest, REND_SERVICE_ID_LEN);
-  service = rend_service_get_by_pk_digest(rend_pk_digest);
-  if (!service) {
-    log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
-             "rendezvous circuit %u; closing.",
-             serviceid, (unsigned)circ->base_.n_circ_id);
-    return -2;
-  }
-  if (service->max_streams_per_circuit > 0) {
-    /* Enforce the streams-per-circuit limit, and refuse to provide a
-     * mapping if this circuit will exceed the limit. */
-#define MAX_STREAM_WARN_INTERVAL 600
-    static struct ratelim_t stream_ratelim =
-        RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
-    if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) {
-      log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
-                     "Maximum streams per circuit limit reached on rendezvous "
-                     "circuit %u; %s.  Circuit has %d out of %d streams.",
-                     (unsigned)circ->base_.n_circ_id,
-                     service->max_streams_close_circuit ?
-                       "closing circuit" :
-                       "ignoring open stream request",
-                     circ->rend_data->nr_streams,
-                     service->max_streams_per_circuit);
-      return service->max_streams_close_circuit ? -2 : -1;
-    }
-  }
-
-  if (hs_set_conn_addr_port(service->ports, conn) == 0) {
-    /* Successfully set the port to the connection. We are done. */
-    return 0;
-  }
-
-  log_info(LD_REND,
-           "No virtual port mapping exists for port %d on service %s",
-           conn->base_.port, serviceid);
-
-  if (service->allow_unknown_ports)
-    return -1;
-  else
-    return -2;
-}
-
-/* Are HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode consistent?
- */
-static int
-rend_service_non_anonymous_mode_consistent(const or_options_t *options)
-{
-  /* !! is used to make these options boolean */
-  return (!! options->HiddenServiceSingleHopMode ==
-          !! options->HiddenServiceNonAnonymousMode);
-}
-
-/* Do the options allow onion services to make direct (non-anonymous)
- * connections to introduction or rendezvous points?
- * Must only be called after options_validate_single_onion() has successfully
- * checked onion service option consistency.
- * Returns true if tor is in HiddenServiceSingleHopMode. */
-int
-rend_service_allow_non_anonymous_connection(const or_options_t *options)
-{
-  tor_assert(rend_service_non_anonymous_mode_consistent(options));
-  return options->HiddenServiceSingleHopMode ? 1 : 0;
-}
-
-/* Do the options allow us to reveal the exact startup time of the onion
- * service?
- * Single Onion Services prioritise availability over hiding their
- * startup time, as their IP address is publicly discoverable anyway.
- * Must only be called after options_validate_single_onion() has successfully
- * checked onion service option consistency.
- * Returns true if tor is in non-anonymous hidden service mode. */
-int
-rend_service_reveal_startup_time(const or_options_t *options)
-{
-  tor_assert(rend_service_non_anonymous_mode_consistent(options));
-  return rend_service_non_anonymous_mode_enabled(options);
-}
-
-/* Is non-anonymous mode enabled using the HiddenServiceNonAnonymousMode
- * config option?
- * Must only be called after options_validate_single_onion() has successfully
- * checked onion service option consistency.
- */
-int
-rend_service_non_anonymous_mode_enabled(const or_options_t *options)
-{
-  tor_assert(rend_service_non_anonymous_mode_consistent(options));
-  return options->HiddenServiceNonAnonymousMode ? 1 : 0;
-}
-
-#ifdef TOR_UNIT_TESTS
-
-STATIC void
-set_rend_service_list(smartlist_t *new_list)
-{
-  rend_service_list = new_list;
-}
-
-STATIC void
-set_rend_rend_service_staging_list(smartlist_t *new_list)
-{
-  rend_service_staging_list = new_list;
-}
-
-#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/feature/rend/rendservice.h b/src/feature/rend/rendservice.h
deleted file mode 100644
index cd44787ce2..0000000000
--- a/src/feature/rend/rendservice.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file rendservice.h
- * \brief Header file for rendservice.c.
- **/
-
-#ifndef TOR_RENDSERVICE_H
-#define TOR_RENDSERVICE_H
-
-#include "core/or/or.h"
-#include "feature/hs/hs_service.h"
-
-typedef struct rend_intro_cell_t rend_intro_cell_t;
-struct config_line_t;
-
-/* This can be used for both INTRODUCE1 and INTRODUCE2 */
-
-struct rend_intro_cell_t {
-  /* 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 or hex-encoded key digest */
-      uint8_t rp[42];
-    } v0_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;
-      /* 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[DH1024_KEY_LEN];
-};
-
-#ifdef RENDSERVICE_PRIVATE
-
-/** Represents a single hidden service running at this OP. */
-typedef struct rend_service_t {
-  /* Fields specified in config file */
-  char *directory; /**< where in the filesystem it stores it. Will be NULL if
-                    * this service is ephemeral. */
-  int dir_group_readable; /**< if 1, allow group read
-                             permissions on directory */
-  smartlist_t *ports; /**< List of hs_port_config_t */
-  rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
-                               * authorization is performed. */
-  smartlist_t *clients; /**< List of rend_authorized_client_t's of
-                         * clients that may access our service. Can be NULL
-                         * if no client authorization is performed. */
-  /* Other fields */
-  crypto_pk_t *private_key; /**< Permanent hidden-service key. */
-  char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
-                                                  * '.onion' */
-  char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */
-  smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
-                             * or are trying to establish. */
-  /** List of rend_intro_point_t that are expiring. They are removed once
-   * the new descriptor is successfully uploaded. A node in this list CAN
-   * NOT appear in the intro_nodes list. */
-  smartlist_t *expiring_nodes;
-  time_t intro_period_started; /**< Start of the current period to build
-                                * introduction points. */
-  int n_intro_circuits_launched; /**< Count of intro circuits we have
-                                  * established in this period. */
-  unsigned int n_intro_points_wanted; /**< Number of intro points this
-                                       * service wants to have open. */
-  rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
-  time_t desc_is_dirty; /**< Time at which changes to the hidden service
-                         * descriptor content occurred, or 0 if it's
-                         * up-to-date. */
-  time_t next_upload_time; /**< Scheduled next hidden service descriptor
-                            * upload time. */
-  /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to
-   * detect repeats.  Clients may send INTRODUCE1 cells for the same
-   * rendezvous point through two or more different introduction points;
-   * when they do, this keeps us from launching multiple simultaneous attempts
-   * to connect to the same rend point. */
-  replaycache_t *accepted_intro_dh_parts;
-  /** If true, we don't close circuits for making requests to unsupported
-   * ports. */
-  int allow_unknown_ports;
-  /** The maximum number of simultaneous streams-per-circuit that are allowed
-   * to be established, or 0 if no limit is set.
-   */
-  int max_streams_per_circuit;
-  /** If true, we close circuits that exceed the max_streams_per_circuit
-   * limit.  */
-  int max_streams_close_circuit;
-} rend_service_t;
-
-STATIC void rend_service_free_(rend_service_t *service);
-#define rend_service_free(s) \
-  FREE_AND_NULL(rend_service_t, rend_service_free_, (s))
-STATIC char *rend_service_sos_poison_path(const rend_service_t *service);
-STATIC int rend_service_verify_single_onion_poison(
-                                                  const rend_service_t *s,
-                                                  const or_options_t *options);
-STATIC int rend_service_poison_new_single_onion_dir(
-                                                  const rend_service_t *s,
-                                                  const or_options_t* options);
-#ifdef TOR_UNIT_TESTS
-
-STATIC void set_rend_service_list(smartlist_t *new_list);
-STATIC void set_rend_rend_service_staging_list(smartlist_t *new_list);
-STATIC void rend_service_prune_list_impl_(void);
-
-#endif /* defined(TOR_UNIT_TESTS) */
-
-#endif /* defined(RENDSERVICE_PRIVATE) */
-
-int rend_num_services(void);
-struct hs_opts_t;
-int rend_config_service(const struct hs_opts_t *hs_opts,
-                        const or_options_t *options,
-                        hs_service_config_t *config);
-void rend_service_prune_list(void);
-void rend_service_free_staging_list(void);
-int rend_service_load_all_keys(const smartlist_t *service_list);
-int rend_service_key_on_disk(const char *directory_path);
-void rend_services_add_filenames_to_lists(smartlist_t *open_lst,
-                                          smartlist_t *stat_lst);
-void rend_consider_services_intro_points(time_t now);
-void rend_consider_services_upload(time_t now);
-void rend_hsdir_routers_changed(void);
-void rend_consider_descriptor_republication(void);
-
-void rend_service_intro_has_opened(origin_circuit_t *circuit);
-int rend_service_intro_established(origin_circuit_t *circuit,
-                                   const uint8_t *request,
-                                   size_t request_len);
-void rend_service_rendezvous_has_opened(origin_circuit_t *circuit);
-int rend_service_receive_introduction(origin_circuit_t *circuit,
-                                      const uint8_t *request,
-                                      size_t request_len);
-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);
-#define rend_service_free_intro(req) do {       \
-    rend_service_free_intro_(req);              \
-    (req) = NULL;                               \
-  } while (0)
-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);
-ssize_t rend_service_encode_establish_intro_cell(char *cell_body_out,
-                                                 size_t cell_body_out_len,
-                                                 crypto_pk_t *intro_key,
-                                                 const char *rend_circ_nonce);
-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);
-void rend_service_dump_stats(int severity);
-void rend_service_free_all(void);
-void rend_service_init(void);
-
-void rend_authorized_client_free_(rend_authorized_client_t *client);
-#define rend_authorized_client_free(client) \
-  FREE_AND_NULL(rend_authorized_client_t, rend_authorized_client_free_, \
-                (client))
-
-hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
-                               smartlist_t *ports,
-                               int max_streams_per_circuit,
-                               int max_streams_close_circuit,
-                               rend_auth_type_t auth_type,
-                               smartlist_t *auth_clients,
-                               char **service_id_out);
-int rend_service_del_ephemeral(const char *service_id);
-
-void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
-                              smartlist_t *descs, smartlist_t *hs_dirs,
-                              const char *service_id, int seconds_valid);
-void rend_service_desc_has_uploaded(const rend_data_t *rend_data);
-
-int rend_service_allow_non_anonymous_connection(const or_options_t *options);
-int rend_service_reveal_startup_time(const or_options_t *options);
-int rend_service_non_anonymous_mode_enabled(const or_options_t *options);
-
-#endif /* !defined(TOR_RENDSERVICE_H) */
diff --git a/src/test/fuzz/fuzz_hsdescv2.c b/src/test/fuzz/fuzz_hsdescv2.c
deleted file mode 100644
index 81d9e5f00e..0000000000
--- a/src/test/fuzz/fuzz_hsdescv2.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2016-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-#include "core/or/or.h"
-#include "feature/dirparse/unparseable.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendparse.h"
-#include "lib/crypt_ops/crypto_ed25519.h"
-#include "test/fuzz/fuzzing.h"
-
-static void
-mock_dump_desc__nodump(const char *desc, const char *type)
-{
-  (void)desc;
-  (void)type;
-}
-
-int
-fuzz_init(void)
-{
-  disable_signature_checking();
-  MOCK(dump_desc, mock_dump_desc__nodump);
-  ed25519_init();
-  return 0;
-}
-
-int
-fuzz_cleanup(void)
-{
-  return 0;
-}
-
-int
-fuzz_main(const uint8_t *data, size_t sz)
-{
-  rend_service_descriptor_t *desc = NULL;
-  char desc_id[64];
-  char *ipts = NULL;
-  size_t ipts_size, esize;
-  const char *next;
-  char *str = tor_memdup_nulterm(data, sz);
-  (void) rend_parse_v2_service_descriptor(&desc, desc_id, &ipts, &ipts_size,
-                                          &esize, &next, str, 1);
-  if (desc) {
-    log_debug(LD_GENERAL, "Parsing okay");
-    rend_service_descriptor_free(desc);
-  } else {
-    log_debug(LD_GENERAL, "Parsing failed");
-  }
-  tor_free(ipts);
-  tor_free(str);
-  return 0;
-}
diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am
index ef952c3812..9bdced9e6f 100644
--- a/src/test/fuzz/include.am
+++ b/src/test/fuzz/include.am
@@ -83,16 +83,6 @@ src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG)
 src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS)
 endif
 
-if UNITTESTS_ENABLED
-src_test_fuzz_fuzz_hsdescv2_SOURCES = \
-	src/test/fuzz/fuzzing_common.c \
-	src/test/fuzz/fuzz_hsdescv2.c
-src_test_fuzz_fuzz_hsdescv2_CPPFLAGS = $(FUZZING_CPPFLAGS)
-src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS)
-src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG)
-src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS)
-endif
-
 if UNITTESTS_ENABLED
 src_test_fuzz_fuzz_hsdescv3_SOURCES = \
 	src/test/fuzz/fuzzing_common.c \
@@ -123,16 +113,6 @@ src_test_fuzz_fuzz_http_connect_LDFLAGS = $(FUZZING_LDFLAG)
 src_test_fuzz_fuzz_http_connect_LDADD = $(FUZZING_LIBS)
 endif
 
-if UNITTESTS_ENABLED
-src_test_fuzz_fuzz_iptsv2_SOURCES = \
-	src/test/fuzz/fuzzing_common.c \
-	src/test/fuzz/fuzz_iptsv2.c
-src_test_fuzz_fuzz_iptsv2_CPPFLAGS = $(FUZZING_CPPFLAGS)
-src_test_fuzz_fuzz_iptsv2_CFLAGS = $(FUZZING_CFLAGS)
-src_test_fuzz_fuzz_iptsv2_LDFLAGS = $(FUZZING_LDFLAG)
-src_test_fuzz_fuzz_iptsv2_LDADD = $(FUZZING_LIBS)
-endif
-
 if UNITTESTS_ENABLED
 src_test_fuzz_fuzz_microdesc_SOURCES = \
 	src/test/fuzz/fuzzing_common.c \
@@ -180,11 +160,9 @@ FUZZERS = \
 	src/test/fuzz/fuzz-diff \
 	src/test/fuzz/fuzz-diff-apply \
 	src/test/fuzz/fuzz-extrainfo \
-	src/test/fuzz/fuzz-hsdescv2 \
 	src/test/fuzz/fuzz-hsdescv3 \
 	src/test/fuzz/fuzz-http \
 	src/test/fuzz/fuzz-http-connect \
-	src/test/fuzz/fuzz-iptsv2 \
 	src/test/fuzz/fuzz-microdesc \
 	src/test/fuzz/fuzz-socks \
 	src/test/fuzz/fuzz-strops \
@@ -239,15 +217,6 @@ src_test_fuzz_lf_fuzz_extrainfo_LDFLAGS = $(LIBFUZZER_LDFLAG)
 src_test_fuzz_lf_fuzz_extrainfo_LDADD = $(LIBFUZZER_LIBS)
 endif
 
-if UNITTESTS_ENABLED
-src_test_fuzz_lf_fuzz_hsdescv2_SOURCES = \
-	$(src_test_fuzz_fuzz_hsdescv2_SOURCES)
-src_test_fuzz_lf_fuzz_hsdescv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
-src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS)
-src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG)
-src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS)
-endif
-
 if UNITTESTS_ENABLED
 src_test_fuzz_lf_fuzz_hsdescv3_SOURCES = \
 	$(src_test_fuzz_fuzz_hsdescv3_SOURCES)
@@ -275,15 +244,6 @@ src_test_fuzz_lf_fuzz_http_connect_LDFLAGS = $(LIBFUZZER_LDFLAG)
 src_test_fuzz_lf_fuzz_http_connect_LDADD = $(LIBFUZZER_LIBS)
 endif
 
-if UNITTESTS_ENABLED
-src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \
-	$(src_test_fuzz_fuzz_iptsv2_SOURCES)
-src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
-src_test_fuzz_lf_fuzz_iptsv2_CFLAGS = $(LIBFUZZER_CFLAGS)
-src_test_fuzz_lf_fuzz_iptsv2_LDFLAGS = $(LIBFUZZER_LDFLAG)
-src_test_fuzz_lf_fuzz_iptsv2_LDADD = $(LIBFUZZER_LIBS)
-endif
-
 if UNITTESTS_ENABLED
 src_test_fuzz_lf_fuzz_microdesc_SOURCES = \
 	$(src_test_fuzz_fuzz_microdesc_SOURCES)
@@ -326,11 +286,9 @@ LIBFUZZER_FUZZERS = \
 	src/test/fuzz/lf-fuzz-diff \
 	src/test/fuzz/lf-fuzz-diff-apply \
 	src/test/fuzz/lf-fuzz-extrainfo \
-	src/test/fuzz/lf-fuzz-hsdescv2 \
 	src/test/fuzz/lf-fuzz-hsdescv3 \
 	src/test/fuzz/lf-fuzz-http \
 	src/test/fuzz/lf-fuzz-http-connect \
-	src/test/fuzz/lf-fuzz-iptsv2 \
 	src/test/fuzz/lf-fuzz-microdesc \
 	src/test/fuzz/lf-fuzz-socks \
 	src/test/fuzz/lf-fuzz-strops \
@@ -378,13 +336,6 @@ src_test_fuzz_liboss_fuzz_extrainfo_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
 src_test_fuzz_liboss_fuzz_extrainfo_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
 endif
 
-if UNITTESTS_ENABLED
-src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \
-	$(src_test_fuzz_fuzz_hsdescv2_SOURCES)
-src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
-src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
-endif
-
 if UNITTESTS_ENABLED
 src_test_fuzz_liboss_fuzz_hsdescv3_a_SOURCES = \
 	$(src_test_fuzz_fuzz_hsdescv3_SOURCES)
@@ -406,13 +357,6 @@ src_test_fuzz_liboss_fuzz_http_connect_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
 src_test_fuzz_liboss_fuzz_http_connect_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
 endif
 
-if UNITTESTS_ENABLED
-src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \
-	$(src_test_fuzz_fuzz_iptsv2_SOURCES)
-src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
-src_test_fuzz_liboss_fuzz_iptsv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
-endif
-
 if UNITTESTS_ENABLED
 src_test_fuzz_liboss_fuzz_microdesc_a_SOURCES = \
 	$(src_test_fuzz_fuzz_microdesc_SOURCES)
@@ -447,11 +391,9 @@ OSS_FUZZ_FUZZERS = \
 	src/test/fuzz/liboss-fuzz-diff.a \
 	src/test/fuzz/liboss-fuzz-diff-apply.a \
 	src/test/fuzz/liboss-fuzz-extrainfo.a \
-	src/test/fuzz/liboss-fuzz-hsdescv2.a \
 	src/test/fuzz/liboss-fuzz-hsdescv3.a \
 	src/test/fuzz/liboss-fuzz-http.a \
 	src/test/fuzz/liboss-fuzz-http-connect.a \
-	src/test/fuzz/liboss-fuzz-iptsv2.a \
 	src/test/fuzz/liboss-fuzz-microdesc.a \
 	src/test/fuzz/liboss-fuzz-socks.a \
 	src/test/fuzz/liboss-fuzz-strops.a \
diff --git a/src/test/include.am b/src/test/include.am
index cdf3b20c48..9372b796f8 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -130,7 +130,6 @@ src_test_test_SOURCES += \
 	src/test/log_test_helpers.c \
 	src/test/hs_test_helpers.c \
 	src/test/opts_test_helpers.c \
-	src/test/rend_test_helpers.c \
 	src/test/resolve_test_helpers.c \
 	src/test/rng_test_helpers.c \
 	src/test/test.c \
@@ -181,7 +180,6 @@ src_test_test_SOURCES += \
 	src/test/test_geoip.c \
 	src/test/test_guardfraction.c \
 	src/test/test_extorport.c \
-	src/test/test_hs.c \
 	src/test/test_hs_common.c \
 	src/test/test_hs_config.c \
 	src/test/test_hs_cell.c \
@@ -196,7 +194,6 @@ src_test_test_SOURCES += \
 	src/test/test_hs_descriptor.c \
 	src/test/test_hs_dos.c \
 	src/test/test_hs_metrics.c \
-	src/test/test_introduce.c \
 	src/test/test_keypin.c \
 	src/test/test_link_handshake.c \
 	src/test/test_logging.c \
@@ -227,7 +224,6 @@ src_test_test_SOURCES += \
 	src/test/test_relay.c \
 	src/test/test_relaycell.c \
 	src/test/test_relaycrypt.c \
-	src/test/test_rendcache.c \
 	src/test/test_replay.c \
 	src/test/test_router.c \
 	src/test/test_routerkeys.c \
@@ -380,7 +376,6 @@ noinst_HEADERS+= \
 	src/test/hs_test_helpers.h \
 	src/test/log_test_helpers.h \
 	src/test/opts_test_helpers.h \
-	src/test/rend_test_helpers.h \
 	src/test/resolve_test_helpers.h \
 	src/test/rng_test_helpers.h \
 	src/test/test.h \
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
deleted file mode 100644
index 8e40167aeb..0000000000
--- a/src/test/rend_test_helpers.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright (c) 2014-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "core/or/or.h"
-#include "core/or/extendinfo.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "test/test.h"
-#include "feature/rend/rendcommon.h"
-#include "test/rend_test_helpers.h"
-
-#include "core/or/extend_info_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-
-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);
-  tor_assert(smartlist_len(descs) > 1);
-  *desc = smartlist_get(descs, 0);
-  smartlist_set(descs, 0, NULL);
-
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  smartlist_free(descs);
-  rend_service_descriptor_free(generated);
-}
-
-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 =
-      extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0);
-    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_t addr;
-    uint16_t port;
-    /* Does not cover all IP addresses. */
-    tor_addr_from_ipv4h(&addr, crypto_rand_int(65536) + 1);
-    port = 1 + crypto_rand_int(65535);
-    extend_info_add_orport(intro->extend_info, &addr, port);
-    intro->intro_key = crypto_pk_dup_key(pk2);
-    smartlist_add((*generated)->intro_nodes, intro);
-  }
-
-  crypto_pk_free(pk1);
-  crypto_pk_free(pk2);
-}
-
-rend_data_t *
-mock_rend_data(const char *onion_address)
-{
-  rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data));
-  rend_data_t *rend_query = &v2_data->base_;
-  rend_query->version = 2;
-
-  strlcpy(v2_data->onion_address, onion_address,
-          sizeof(v2_data->onion_address));
-  v2_data->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;
-}
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
deleted file mode 100644
index b1078ce866..0000000000
--- a/src/test/rend_test_helpers.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Copyright (c) 2014-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "core/or/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);
-rend_data_t *mock_rend_data(const char *onion_address);
-
-#endif /* !defined(TOR_REND_TEST_HELPERS_H) */
-
diff --git a/src/test/test.c b/src/test/test.c
index 0d6c0a0d4a..fd9ce230ea 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -45,9 +45,6 @@
 #include "app/config/config.h"
 #include "core/or/connection_edge.h"
 #include "core/or/extendinfo.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendcache.h"
-#include "feature/rend/rendparse.h"
 #include "test/test.h"
 #include "core/mainloop/mainloop.h"
 #include "lib/memarea/memarea.h"
@@ -62,9 +59,6 @@
 
 #include "core/or/extend_info_st.h"
 #include "core/or/or_circuit_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
 #include "feature/relay/onion_queue.h"
 
 /** Run unit tests for the onion handshake code. */
@@ -619,127 +613,6 @@ test_circuit_timeout(void *arg)
   testing_disable_deterministic_rng();
 }
 
-/** Test encoding and parsing of rendezvous service descriptors. */
-static void
-test_rend_fns(void *arg)
-{
-  rend_service_descriptor_t *generated = NULL, *parsed = NULL;
-  char service_id[DIGEST_LEN];
-  char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
-  const char *next_desc;
-  smartlist_t *descs = smartlist_new();
-  char computed_desc_id[DIGEST_LEN];
-  char parsed_desc_id[DIGEST_LEN];
-  crypto_pk_t *pk1 = NULL, *pk2 = NULL;
-  time_t now;
-  char *intro_points_encrypted = NULL;
-  size_t intro_points_size;
-  size_t encoded_size;
-  int i;
-
-  (void)arg;
-
-  /* Initialize the service cache. */
-  rend_cache_init();
-
-  pk1 = pk_generate(0);
-  pk2 = pk_generate(1);
-  generated = tor_malloc_zero(sizeof(rend_service_descriptor_t));
-  generated->pk = crypto_pk_dup_key(pk1);
-  crypto_pk_get_digest(generated->pk, service_id);
-  base32_encode(service_id_base32, REND_SERVICE_ID_LEN_BASE32+1,
-                service_id, REND_SERVICE_ID_LEN);
-  now = time(NULL);
-  generated->timestamp = now;
-  generated->version = 2;
-  generated->protocols = 42;
-  generated->intro_nodes = smartlist_new();
-
-  for (i = 0; i < 3; 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 =
-      extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0);
-    intro->extend_info->onion_key = okey;
-    crypto_pk_get_digest(intro->extend_info->onion_key,
-                         intro->extend_info->identity_digest);
-    //crypto_rand(info->identity_digest, DIGEST_LEN); /* Would this work? */
-    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_t addr;
-    uint16_t port;
-    /* Does not cover all IP addresses. */
-    tor_addr_from_ipv4h(&addr, crypto_rand_int(65536) + 1);
-    port = 1 + crypto_rand_int(65535);
-    extend_info_add_orport(intro->extend_info, &addr, port);
-    intro->intro_key = crypto_pk_dup_key(pk2);
-    smartlist_add(generated->intro_nodes, intro);
-  }
-  int rv = rend_encode_v2_descriptors(descs, generated, now, 0,
-                                      REND_NO_AUTH, NULL, NULL);
-  tt_int_op(rv, OP_GT, 0);
-  rv = rend_compute_v2_desc_id(computed_desc_id, service_id_base32, NULL,
-                               now, 0);
-  tt_int_op(rv, OP_EQ, 0);
-  tt_mem_op(((rend_encoded_v2_service_descriptor_t *)
-             smartlist_get(descs, 0))->desc_id, OP_EQ,
-            computed_desc_id, DIGEST_LEN);
-  rv = rend_parse_v2_service_descriptor(&parsed, parsed_desc_id,
-               &intro_points_encrypted, &intro_points_size, &encoded_size,
-               &next_desc,
-          ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0))
-                                        ->desc_str, 1);
-  tt_int_op(rv, OP_EQ, 0);
-  tt_assert(parsed);
-  tt_mem_op(((rend_encoded_v2_service_descriptor_t *)
-         smartlist_get(descs, 0))->desc_id,OP_EQ, parsed_desc_id, DIGEST_LEN);
-  tt_int_op(rend_parse_introduction_points(parsed, intro_points_encrypted,
-                                         intro_points_size),OP_EQ, 3);
-  tt_assert(!crypto_pk_cmp_keys(generated->pk, parsed->pk));
-  tt_int_op(parsed->timestamp,OP_EQ, now);
-  tt_int_op(parsed->version,OP_EQ, 2);
-  tt_int_op(parsed->protocols,OP_EQ, 42);
-  tt_int_op(smartlist_len(parsed->intro_nodes),OP_EQ, 3);
-  for (i = 0; i < smartlist_len(parsed->intro_nodes); i++) {
-    rend_intro_point_t *par_intro = smartlist_get(parsed->intro_nodes, i),
-      *gen_intro = smartlist_get(generated->intro_nodes, i);
-    extend_info_t *par_info = par_intro->extend_info;
-    extend_info_t *gen_info = gen_intro->extend_info;
-    tt_assert(!crypto_pk_cmp_keys(gen_info->onion_key, par_info->onion_key));
-    tt_mem_op(gen_info->identity_digest,OP_EQ, par_info->identity_digest,
-               DIGEST_LEN);
-    tt_str_op(gen_info->nickname,OP_EQ, par_info->nickname);
-    const tor_addr_port_t *a1, *a2;
-    a1 = extend_info_get_orport(gen_info, AF_INET);
-    a2 = extend_info_get_orport(par_info, AF_INET);
-    tt_assert(a1 && a2);
-    tt_assert(tor_addr_eq(&a1->addr, &a2->addr));
-    tt_int_op(a2->port,OP_EQ, a2->port);
-  }
-
-  rend_service_descriptor_free(parsed);
-  rend_service_descriptor_free(generated);
-  parsed = generated = NULL;
-
- done:
-  if (descs) {
-    for (i = 0; i < smartlist_len(descs); i++)
-      rend_encoded_v2_service_descriptor_free_(smartlist_get(descs, i));
-    smartlist_free(descs);
-  }
-  if (parsed)
-    rend_service_descriptor_free(parsed);
-  if (generated)
-    rend_service_descriptor_free(generated);
-  if (pk1)
-    crypto_pk_free(pk1);
-  if (pk2)
-    crypto_pk_free(pk2);
-  tor_free(intro_points_encrypted);
-}
-
 #define ENT(name)                                                       \
   { #name, test_ ## name , 0, NULL, NULL }
 #define FORK(name)                                                      \
@@ -753,7 +626,6 @@ static struct testcase_t test_array[] = {
   { "fast_handshake", test_fast_handshake, 0, NULL, NULL },
   FORK(circuit_timeout),
   FORK(circuit_timeout_xm_alpha),
-  FORK(rend_fns),
 
   END_OF_TESTCASES
 };
@@ -828,9 +700,7 @@ struct testgroup_t testgroups[] = {
   { "hs_ntor/", hs_ntor_tests },
   { "hs_ob/", hs_ob_tests },
   { "hs_service/", hs_service_tests },
-  { "introduce/", introduce_tests },
   { "keypin/", keypin_tests },
-  { "legacy_hs/", hs_tests },
   { "link-handshake/", link_handshake_tests },
   { "mainloop/", mainloop_tests },
   { "metrics/", metrics_tests },
@@ -856,7 +726,6 @@ struct testgroup_t testgroups[] = {
   { "relay/" , relay_tests },
   { "relaycell/", relaycell_tests },
   { "relaycrypt/", relaycrypt_tests },
-  { "rend_cache/", rend_cache_tests },
   { "replaycache/", replaycache_tests },
   { "router/", router_tests },
   { "routerkeys/", routerkeys_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 56037648d3..bdbbe25cb9 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -145,8 +145,6 @@ extern struct testcase_t hs_metrics_tests[];
 extern struct testcase_t hs_ntor_tests[];
 extern struct testcase_t hs_ob_tests[];
 extern struct testcase_t hs_service_tests[];
-extern struct testcase_t hs_tests[];
-extern struct testcase_t introduce_tests[];
 extern struct testcase_t keypin_tests[];
 extern struct testcase_t link_handshake_tests[];
 extern struct testcase_t logging_tests[];
@@ -179,7 +177,6 @@ extern struct testcase_t pubsub_msg_tests[];
 extern struct testcase_t relay_tests[];
 extern struct testcase_t relaycell_tests[];
 extern struct testcase_t relaycrypt_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[];
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 5bca3e04fc..710336cb28 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -41,7 +41,6 @@
 #include "feature/nodelist/networkstatus.h"
 #include "feature/nodelist/nodelist.h"
 #include "core/or/policies.h"
-#include "feature/rend/rendservice.h"
 #include "feature/relay/relay_find_addr.h"
 #include "feature/relay/router.h"
 #include "feature/relay/routermode.h"
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index cf5626ead7..2ebe9afbe2 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -19,7 +19,6 @@
 #include "feature/nodelist/microdesc.h"
 #include "feature/nodelist/nodelist.h"
 #include "feature/nodelist/networkstatus.h"
-#include "feature/rend/rendcache.h"
 #include "feature/dircommon/directory.h"
 #include "core/or/connection_or.h"
 #include "lib/net/resolve.h"
@@ -38,10 +37,6 @@ static void * test_conn_get_basic_setup(const struct testcase_t *tc);
 static int test_conn_get_basic_teardown(const struct testcase_t *tc,
                                         void *arg);
 
-static void * test_conn_get_rend_setup(const struct testcase_t *tc);
-static int test_conn_get_rend_teardown(const struct testcase_t *tc,
-                                        void *arg);
-
 static void * test_conn_get_rsrc_setup(const struct testcase_t *tc);
 static int test_conn_get_rsrc_teardown(const struct testcase_t *tc,
                                         void *arg);
@@ -179,52 +174,6 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
   return 0;
 }
 
-static void *
-test_conn_get_rend_setup(const struct testcase_t *tc)
-{
-  dir_connection_t *conn = DOWNCAST(dir_connection_t,
-                                    test_conn_get_connection(
-                                                    TEST_CONN_STATE,
-                                                    TEST_CONN_TYPE,
-                                                    TEST_CONN_REND_PURPOSE));
-  tt_assert(conn);
-  assert_connection_ok(&conn->base_, time(NULL));
-
-  rend_cache_init();
-
-  /* TODO: use directory_initiate_request() to do this - maybe? */
-  tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32);
-  conn->rend_data = rend_data_client_create(TEST_CONN_REND_ADDR, NULL, NULL,
-                                            REND_NO_AUTH);
-  assert_connection_ok(&conn->base_, time(NULL));
-  return conn;
-
-  /* On failure */
- done:
-  test_conn_get_rend_teardown(tc, conn);
-  /* Returning NULL causes the unit test to fail */
-  return NULL;
-}
-
-static int
-test_conn_get_rend_teardown(const struct testcase_t *tc, void *arg)
-{
-  dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
-  int rv = 0;
-
-  tt_assert(conn);
-  assert_connection_ok(&conn->base_, time(NULL));
-
-  /* avoid a last-ditch attempt to refetch the descriptor */
-  conn->base_.purpose = TEST_CONN_REND_PURPOSE_SUCCESSFUL;
-
-  /* connection_free_() cleans up rend_data */
-  rv = test_conn_get_basic_teardown(tc, arg);
- done:
-  rend_cache_free_all();
-  return rv;
-}
-
 static dir_connection_t *
 test_conn_download_status_add_a_connection(const char *resource)
 {
@@ -369,10 +318,6 @@ static struct testcase_setup_t test_conn_get_basic_st = {
   test_conn_get_basic_setup, test_conn_get_basic_teardown
 };
 
-static struct testcase_setup_t test_conn_get_rend_st = {
-  test_conn_get_rend_setup, test_conn_get_rend_teardown
-};
-
 static struct testcase_setup_t test_conn_get_rsrc_st = {
   test_conn_get_rsrc_setup, test_conn_get_rsrc_teardown
 };
@@ -489,37 +434,6 @@ test_conn_get_basic(void *arg)
   ;
 }
 
-static void
-test_conn_get_rend(void *arg)
-{
-  dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
-  tt_assert(conn);
-  assert_connection_ok(&conn->base_, time(NULL));
-
-  tt_assert(connection_get_by_type_state_rendquery(
-                                            conn->base_.type,
-                                            conn->base_.state,
-                                            rend_data_get_address(
-                                                      conn->rend_data))
-            == TO_CONN(conn));
-  tt_assert(connection_get_by_type_state_rendquery(
-                                            TEST_CONN_TYPE,
-                                            TEST_CONN_STATE,
-                                            TEST_CONN_REND_ADDR)
-            == TO_CONN(conn));
-  tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2,
-                                                   !conn->base_.state,
-                                                   "")
-            == NULL);
-  tt_assert(connection_get_by_type_state_rendquery(TEST_CONN_REND_TYPE_2,
-                                                   !TEST_CONN_STATE,
-                                                   TEST_CONN_REND_ADDR_2)
-            == NULL);
-
- done:
-  ;
-}
-
 #define sl_is_conn_assert(sl_input, conn) \
   do {                                               \
     the_sl = (sl_input);                             \
@@ -1091,7 +1005,6 @@ static const unsigned int PROXY_HAPROXY_ARG = PROXY_HAPROXY;
 
 struct testcase_t connection_tests[] = {
   CONNECTION_TESTCASE(get_basic, TT_FORK, test_conn_get_basic_st),
-  CONNECTION_TESTCASE(get_rend,  TT_FORK, test_conn_get_rend_st),
   CONNECTION_TESTCASE(get_rsrc,  TT_FORK, test_conn_get_rsrc_st),
 
   CONNECTION_TESTCASE_ARG(download_status,  TT_FORK,
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 0745651aca..4737a35939 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -16,7 +16,6 @@
 #include "feature/dircache/dirserv.h"
 #include "feature/hs/hs_common.h"
 #include "feature/nodelist/networkstatus.h"
-#include "feature/rend/rendservice.h"
 #include "feature/nodelist/authcert.h"
 #include "feature/nodelist/nodelist.h"
 #include "feature/stats/rephist.h"
@@ -316,110 +315,6 @@ test_add_onion_helper_keyarg_v3(void *arg)
   UNMOCK(control_write_reply);
 }
 
-static void
-test_add_onion_helper_keyarg_v2(void *arg)
-{
-  int ret, hs_version;
-  add_onion_secret_key_t pk;
-  crypto_pk_t *pk1 = NULL;
-  const char *key_new_alg = NULL;
-  char *key_new_blob = NULL;
-  char *encoded = NULL;
-  char *arg_str = NULL;
-
-  (void) arg;
-  MOCK(control_write_reply, mock_control_write_reply);
-
-  memset(&pk, 0, sizeof(pk));
-
-  /* Test explicit RSA1024 key generation. */
-  tor_free(reply_str);
-  ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
-                                &pk, &hs_version, NULL);
-  tt_int_op(ret, OP_EQ, 0);
-  tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
-  tt_assert(pk.v2);
-  tt_str_op(key_new_alg, OP_EQ, "RSA1024");
-  tt_assert(key_new_blob);
-  tt_ptr_op(reply_str, OP_EQ, NULL);
-
-  /* Test discarding the private key. */
-  crypto_pk_free(pk.v2); pk.v2 = NULL;
-  tor_free(key_new_blob);
-  ret = add_onion_helper_keyarg("NEW:RSA1024", 1, &key_new_alg, &key_new_blob,
-                               &pk, &hs_version, NULL);
-  tt_int_op(ret, OP_EQ, 0);
-  tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
-  tt_assert(pk.v2);
-  tt_ptr_op(key_new_alg, OP_EQ, NULL);
-  tt_ptr_op(key_new_blob, OP_EQ, NULL);
-  tt_ptr_op(reply_str, OP_EQ, NULL);
-
-  /* Test generating a invalid key type. */
-  crypto_pk_free(pk.v2); pk.v2 = NULL;
-  ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
-                               &pk, &hs_version, NULL);
-  tt_int_op(ret, OP_EQ, -1);
-  tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
-  tt_assert(!pk.v2);
-  tt_ptr_op(key_new_alg, OP_EQ, NULL);
-  tt_ptr_op(key_new_blob, OP_EQ, NULL);
-  tt_assert(reply_str);
-
-  /* Test loading a RSA1024 key. */
-  tor_free(reply_str);
-  pk1 = pk_generate(0);
-  tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded));
-  tor_asprintf(&arg_str, "RSA1024:%s", encoded);
-  ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
-                                &pk, &hs_version, NULL);
-  tt_int_op(ret, OP_EQ, 0);
-  tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
-  tt_assert(pk.v2);
-  tt_ptr_op(key_new_alg, OP_EQ, NULL);
-  tt_ptr_op(key_new_blob, OP_EQ, NULL);
-  tt_ptr_op(reply_str, OP_EQ, NULL);
-  tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0);
-
-  /* Test loading a invalid key type. */
-  tor_free(arg_str);
-  crypto_pk_free(pk1); pk1 = NULL;
-  crypto_pk_free(pk.v2); pk.v2 = NULL;
-  tor_asprintf(&arg_str, "RSA512:%s", encoded);
-  ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
-                                &pk, &hs_version, NULL);
-  tt_int_op(ret, OP_EQ, -1);
-  tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
-  tt_assert(!pk.v2);
-  tt_ptr_op(key_new_alg, OP_EQ, NULL);
-  tt_ptr_op(key_new_blob, OP_EQ, NULL);
-  tt_assert(reply_str);
-
-  /* Test loading a invalid key. */
-  tor_free(arg_str);
-  crypto_pk_free(pk.v2); pk.v2 = NULL;
-  tor_free(reply_str);
-  encoded[strlen(encoded)/2] = '\0';
-  tor_asprintf(&arg_str, "RSA1024:%s", encoded);
-  ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
-                               &pk, &hs_version, NULL);
-  tt_int_op(ret, OP_EQ, -1);
-  tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
-  tt_assert(!pk.v2);
-  tt_ptr_op(key_new_alg, OP_EQ, NULL);
-  tt_ptr_op(key_new_blob, OP_EQ, NULL);
-  tt_assert(reply_str);
-
- done:
-  crypto_pk_free(pk1);
-  crypto_pk_free(pk.v2);
-  tor_free(key_new_blob);
-  tor_free(reply_str);
-  tor_free(encoded);
-  tor_free(arg_str);
-  UNMOCK(control_write_reply);
-}
-
 static void
 test_getinfo_helper_onion(void *arg)
 {
@@ -567,58 +462,6 @@ test_hs_parse_port_config(void *arg)
   tor_free(err_msg);
 }
 
-static void
-test_add_onion_helper_clientauth(void *arg)
-{
-  rend_authorized_client_t *client = NULL;
-  int created = 0;
-
-  (void)arg;
-
-  MOCK(control_write_reply, mock_control_write_reply);
-  /* Test "ClientName" only. */
-  tor_free(reply_str);
-  client = add_onion_helper_clientauth("alice", &created, NULL);
-  tt_assert(client);
-  tt_assert(created);
-  tt_ptr_op(reply_str, OP_EQ, NULL);
-  rend_authorized_client_free(client);
-
-  /* Test "ClientName:Blob" */
-  tor_free(reply_str);
-  client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B",
-                                       &created, NULL);
-  tt_assert(client);
-  tt_assert(!created);
-  tt_ptr_op(reply_str, OP_EQ, NULL);
-  rend_authorized_client_free(client);
-
-  /* Test invalid client names */
-  tor_free(reply_str);
-  client = add_onion_helper_clientauth("no*asterisks*allowed", &created,
-                                       NULL);
-  tt_ptr_op(client, OP_EQ, NULL);
-  tt_assert(reply_str);
-
-  /* Test invalid auth cookie */
-  tor_free(reply_str);
-  client = add_onion_helper_clientauth("alice:12345", &created, NULL);
-  tt_ptr_op(client, OP_EQ, NULL);
-  tt_assert(reply_str);
-
-  /* Test invalid syntax */
-  tor_free(reply_str);
-  client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created,
-                                       NULL);
-  tt_ptr_op(client, OP_EQ, NULL);
-  tt_assert(reply_str);
-
- done:
-  rend_authorized_client_free(client);
-  tor_free(reply_str);
-  UNMOCK(control_write_reply);
-}
-
 /* Mocks and data/variables used for GETINFO download status tests */
 
 static const download_status_t dl_status_default =
@@ -2209,15 +2052,11 @@ struct testcase_t controller_tests[] = {
   PARSER_TEST(no_args_one_obj),
   PARSER_TEST(no_args_kwargs),
   PARSER_TEST(one_arg_kwargs),
-  { "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
-    NULL, NULL },
   { "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
     NULL, NULL },
   { "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL },
   { "hs_parse_port_config", test_hs_parse_port_config, 0,
     NULL, NULL },
-  { "add_onion_helper_clientauth", test_add_onion_helper_clientauth, 0, NULL,
-    NULL },
   { "download_status_consensus", test_download_status_consensus, 0, NULL,
     NULL },
   {"getinfo_helper_current_consensus_from_cache",
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index d62dd3fb9e..bb2bc6ad21 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -4848,9 +4848,6 @@ test_dir_purpose_needs_anonymity_returns_true_for_bridges(void *arg)
   tt_int_op(1, OP_EQ, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, NULL));
   tt_int_op(1, OP_EQ, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE,
                                            "foobar"));
-  tt_int_op(1, OP_EQ,
-            purpose_needs_anonymity(DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2,
-                                    ROUTER_PURPOSE_BRIDGE, NULL));
  done: ;
 }
 
@@ -4864,21 +4861,6 @@ test_dir_purpose_needs_anonymity_returns_false_for_own_bridge_desc(void *arg)
  done: ;
 }
 
-static void
-test_dir_purpose_needs_anonymity_returns_true_for_sensitive_purpose(void *arg)
-{
-  (void)arg;
-
-  tt_int_op(1, OP_EQ, purpose_needs_anonymity(
-                    DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2,
-                    ROUTER_PURPOSE_GENERAL, NULL));
-  tt_int_op(1, OP_EQ, purpose_needs_anonymity(
-                    DIR_PURPOSE_UPLOAD_RENDDESC_V2, 0,  NULL));
-  tt_int_op(1, OP_EQ, purpose_needs_anonymity(
-                    DIR_PURPOSE_FETCH_RENDDESC_V2, 0, NULL));
- done: ;
-}
-
 static void
 test_dir_purpose_needs_anonymity_ret_false_for_non_sensitive_conn(void *arg)
 {
@@ -4937,12 +4919,6 @@ test_dir_fetch_type(void *arg)
   tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL,
                            NULL), OP_EQ, MICRODESC_DIRINFO);
 
-  /* This will give a warning, because this function isn't supposed to be
-   * used for HS descriptors. */
-  setup_full_capture_of_logs(LOG_WARN);
-  tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_RENDDESC_V2,
-                           ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, NO_DIRINFO);
-  expect_single_log_msg_containing("Unexpected purpose");
  done:
   teardown_capture_of_logs();
 }
@@ -5300,10 +5276,6 @@ test_dir_conn_purpose_to_string(void *data)
   EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_STATUS_VOTE, "status vote fetch");
   EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES,
                       "consensus signature fetch");
-  EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_RENDDESC_V2,
-                      "hidden-service v2 descriptor fetch");
-  EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_RENDDESC_V2,
-                      "hidden-service v2 descriptor upload");
   EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_MICRODESC, "microdescriptor fetch");
 
   /* This will give a warning, because there is no purpose 1024. */
@@ -7311,7 +7283,6 @@ struct testcase_t dir_tests[] = {
   DIR(purpose_needs_anonymity_returns_true_for_bridges, 0),
   DIR(purpose_needs_anonymity_returns_false_for_own_bridge_desc, 0),
   DIR(purpose_needs_anonymity_returns_true_by_default, 0),
-  DIR(purpose_needs_anonymity_returns_true_for_sensitive_purpose, 0),
   DIR(purpose_needs_anonymity_ret_false_for_non_sensitive_conn, 0),
   DIR(post_parsing, 0),
   DIR(fetch_type, 0),
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 28f07efbe8..06ab309362 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -18,14 +18,11 @@
 #include "feature/dircache/dircache.h"
 #include "test/test.h"
 #include "lib/compress/compress.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendcache.h"
 #include "feature/relay/relay_config.h"
 #include "feature/relay/router.h"
 #include "feature/nodelist/authcert.h"
 #include "feature/nodelist/dirlist.h"
 #include "feature/nodelist/routerlist.h"
-#include "test/rend_test_helpers.h"
 #include "feature/nodelist/microdesc.h"
 #include "test/test_helpers.h"
 #include "feature/nodelist/nodelist.h"
@@ -44,7 +41,6 @@
 #include "feature/dircommon/dir_connection_st.h"
 #include "feature/dirclient/dir_server_st.h"
 #include "feature/nodelist/networkstatus_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
 #include "feature/nodelist/routerinfo_st.h"
 #include "feature/nodelist/routerlist_st.h"
 
@@ -261,125 +257,6 @@ test_dir_handle_get_robots_txt(void *data)
     tor_free(body);
 }
 
-#define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid)
-static void
-test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
-{
-  dir_connection_t *conn = NULL;
-  char *header = NULL;
-  (void) data;
-
-  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
-
-  conn = new_dir_conn();
-
-  // connection is not encrypted
-  tt_assert(!connection_dir_is_encrypted(conn));
-
-  tt_int_op(directory_handle_command_get(conn, RENDEZVOUS2_GET(), NULL, 0),
-            OP_EQ, 0);
-  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
-                      NULL, NULL, 1, 0);
-
-  tt_str_op(NOT_FOUND, OP_EQ, header);
-
-  done:
-    UNMOCK(connection_write_to_buf_impl_);
-    connection_free_minimal(TO_CONN(conn));
-    tor_free(header);
-}
-
-static void
-test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
-  void *data)
-{
-  dir_connection_t *conn = NULL;
-  char *header = NULL;
-  (void) data;
-
-  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
-  conn = new_dir_conn();
-
-  // connection is encrypted
-  TO_CONN(conn)->linked = 1;
-  tt_assert(connection_dir_is_encrypted(conn));
-
-  tt_int_op(directory_handle_command_get(conn,
-            RENDEZVOUS2_GET("invalid-desc-id"), NULL, 0), OP_EQ, 0);
-  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
-                      NULL, NULL, 1, 0);
-
-  tt_str_op(header, OP_EQ, BAD_REQUEST);
-
-  done:
-    UNMOCK(connection_write_to_buf_impl_);
-    connection_free_minimal(TO_CONN(conn));
-    tor_free(header);
-}
-
-static void
-test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
-{
-  dir_connection_t *conn = NULL;
-  char *header = NULL;
-  (void) data;
-
-  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
-  conn = new_dir_conn();
-
-  // connection is encrypted
-  TO_CONN(conn)->linked = 1;
-  tt_assert(connection_dir_is_encrypted(conn));
-
-  //TODO: this can't be reached because rend_valid_descriptor_id() prevents
-  //this case to happen. This test is the same as
-  //test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id We
-  //should refactor to remove the case from the switch.
-
-  const char *req = RENDEZVOUS2_GET("1bababababababababababababababab");
-  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
-
-  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
-                      NULL, NULL, 1, 0);
-
-  tt_str_op(header, OP_EQ, BAD_REQUEST);
-
-  done:
-    UNMOCK(connection_write_to_buf_impl_);
-    connection_free_minimal(TO_CONN(conn));
-    tor_free(header);
-}
-
-static void
-test_dir_handle_get_rendezvous2_not_found(void *data)
-{
-  dir_connection_t *conn = NULL;
-  char *header = NULL;
-  (void) data;
-
-  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
-  conn = new_dir_conn();
-
-  rend_cache_init();
-
-  // connection is encrypted
-  TO_CONN(conn)->linked = 1;
-  tt_assert(connection_dir_is_encrypted(conn));
-
-  const char *req = RENDEZVOUS2_GET("3xqunszqnaolrrfmtzgaki7mxelgvkje");
-  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
-  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
-                      NULL, NULL, 1, 0);
-
-  tt_str_op(NOT_FOUND, OP_EQ, header);
-
-  done:
-    UNMOCK(connection_write_to_buf_impl_);
-    connection_free_minimal(TO_CONN(conn));
-    tor_free(header);
-    rend_cache_free_all();
-}
-
 static const routerinfo_t * dhg_tests_router_get_my_routerinfo(void);
 ATTR_UNUSED static int dhg_tests_router_get_my_routerinfo_called = 0;
 
@@ -395,76 +272,6 @@ dhg_tests_router_get_my_routerinfo(void)
   return mock_routerinfo;
 }
 
-static void
-test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
-{
-  dir_connection_t *conn = NULL;
-  char *header = NULL;
-  char *body = NULL;
-  size_t body_used = 0;
-  char buff[30];
-  char req[70];
-  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
-  char *service_id = NULL;
-  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-  size_t body_len = 0;
-  (void) data;
-
-  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
-  MOCK(router_get_my_routerinfo,
-       dhg_tests_router_get_my_routerinfo);
-
-  rend_cache_init();
-
-  /* create a valid rend service descriptor */
-  #define RECENT_TIME -10
-  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
-
-  tt_int_op(rend_cache_store_v2_desc_as_dir(desc_holder->desc_str),
-            OP_EQ, 0);
-
-  base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
-                DIGEST_LEN);
-
-  conn = new_dir_conn();
-
-  // connection is encrypted
-  TO_CONN(conn)->linked = 1;
-  tt_assert(connection_dir_is_encrypted(conn));
-
-  tor_snprintf(req, sizeof(req), RENDEZVOUS2_GET("%s"), desc_id_base32);
-
-  tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
-
-  body_len = strlen(desc_holder->desc_str);
-  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
-                      &body, &body_used, body_len+1, 0);
-
-  tt_assert(header);
-  tt_assert(body);
-
-  tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
-  tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
-  tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
-  tt_assert(strstr(header, "Pragma: no-cache\r\n"));
-  tor_snprintf(buff, sizeof(buff), "Content-Length: %ld\r\n", (long) body_len);
-  tt_assert(strstr(header, buff));
-
-  tt_int_op(body_used, OP_EQ, strlen(body));
-  tt_str_op(body, OP_EQ, desc_holder->desc_str);
-
-  done:
-    UNMOCK(connection_write_to_buf_impl_);
-    UNMOCK(router_get_my_routerinfo);
-
-    connection_free_minimal(TO_CONN(conn));
-    tor_free(header);
-    tor_free(body);
-    rend_encoded_v2_service_descriptor_free(desc_holder);
-    tor_free(service_id);
-    rend_cache_free_all();
-}
-
 #define MICRODESC_GET(digest) GET("/tor/micro/d/" digest)
 static void
 test_dir_handle_get_micro_d_not_found(void *data)
@@ -2934,11 +2741,6 @@ struct testcase_t dir_handle_get_tests[] = {
   DIR_HANDLE_CMD(v1_command_not_found, 0),
   DIR_HANDLE_CMD(v1_command, 0),
   DIR_HANDLE_CMD(robots_txt, 0),
-  DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0),
-  DIR_HANDLE_CMD(rendezvous2_not_found, 0),
-  DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0),
-  DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_not_well_formed, 0),
-  DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_success, 0),
   DIR_HANDLE_CMD(micro_d_not_found, 0),
   DIR_HANDLE_CMD(micro_d_server_busy, 0),
   DIR_HANDLE_CMD(micro_d, 0),
diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c
index 11840b2c4f..d426934882 100644
--- a/src/test/test_entryconn.c
+++ b/src/test/test_entryconn.c
@@ -17,7 +17,6 @@
 #include "feature/nodelist/nodelist.h"
 
 #include "feature/hs/hs_cache.h"
-#include "feature/rend/rendcache.h"
 
 #include "core/or/entry_connection_st.h"
 #include "core/or/socks_request_st.h"
@@ -748,7 +747,6 @@ test_entryconn_rewrite_onion_v3(void *arg)
   /* Make an onion connection using the SOCKS request */
   conn->entry_cfg.onion_traffic = 1;
   ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_SOCKS_WAIT;
-  tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
   tt_assert(!ENTRY_TO_EDGE_CONN(conn)->hs_ident);
 
   /* Handle SOCKS and rewrite! */
@@ -763,7 +761,6 @@ test_entryconn_rewrite_onion_v3(void *arg)
             "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid");
   /* check that HS information got attached to the connection */
   tt_assert(ENTRY_TO_EDGE_CONN(conn)->hs_ident);
-  tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
 
  done:
   hs_free_all();
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
deleted file mode 100644
index 42e663330a..0000000000
--- a/src/test/test_hs.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/* Copyright (c) 2007-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file test_hs.c
- * \brief Unit tests for hidden service.
- **/
-
-#define CONTROL_EVENTS_PRIVATE
-#define CIRCUITBUILD_PRIVATE
-#define RENDCOMMON_PRIVATE
-#define RENDSERVICE_PRIVATE
-#define HS_SERVICE_PRIVATE
-
-#include "core/or/or.h"
-#include "test/test.h"
-#include "feature/control/control.h"
-#include "feature/control/control_events.h"
-#include "feature/control/control_fmt.h"
-#include "app/config/config.h"
-#include "feature/hs/hs_common.h"
-#include "feature/rend/rendcommon.h"
-#include "feature/rend/rendservice.h"
-#include "feature/nodelist/routerlist.h"
-#include "feature/nodelist/routerset.h"
-#include "core/or/circuitbuild.h"
-
-#include "feature/nodelist/node_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/nodelist/routerinfo_st.h"
-
-#include "test/test_helpers.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-/* mock ID digest and longname for node that's in nodelist */
-#define HSDIR_EXIST_ID "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \
-                       "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
-#define STR_HSDIR_EXIST_LONGNAME \
-                       "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir"
-/* mock ID digest and longname for node that's not in nodelist */
-#define HSDIR_NONE_EXIST_ID "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB" \
-                            "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB"
-#define STR_HSDIR_NONE_EXIST_LONGNAME \
-                       "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
-
-/* DuckDuckGo descriptor as an example. This one has extra "\r" at the end so
- * the control port is happy. */
-static const char *hs_desc_content_control = "\
-rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\r\n\
-version 2\r\n\
-permanent-key\r\n\
------BEGIN RSA PUBLIC KEY-----\r\n\
-MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE\r\n\
-aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg\r\n\
-I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=\r\n\
------END RSA PUBLIC KEY-----\r\n\
-secret-id-part anmjoxxwiupreyajjt5yasimfmwcnxlf\r\n\
-publication-time 2015-03-11 19:00:00\r\n\
-protocol-versions 2,3\r\n\
-introduction-points\r\n\
------BEGIN MESSAGE-----\r\n\
-aW50cm9kdWN0aW9uLXBvaW50IDd1bnd4cmg2dG5kNGh6eWt1Z3EzaGZzdHduc2ll\r\n\
-cmhyCmlwLWFkZHJlc3MgMTg4LjEzOC4xMjEuMTE4Cm9uaW9uLXBvcnQgOTAwMQpv\r\n\
-bmlvbi1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dC\r\n\
-QUxGRVVyeVpDbk9ROEhURmV5cDVjMTRObWVqL1BhekFLTTBxRENTNElKUWh0Y3g1\r\n\
-NXpRSFdOVWIKQ2hHZ0JqR1RjV3ZGRnA0N3FkdGF6WUZhVXE2c0lQKzVqeWZ5b0Q4\r\n\
-UmJ1bzBwQmFWclJjMmNhYUptWWM0RDh6Vgpuby9sZnhzOVVaQnZ1cWY4eHIrMDB2\r\n\
-S0JJNmFSMlA2OE1WeDhrMExqcUpUU2RKOE9idm9yQWdNQkFBRT0KLS0tLS1FTkQg\r\n\
-UlNBIFBVQkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQ\r\n\
-VUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTnJHb0ozeTlHNXQzN2F2ekI1cTlwN1hG\r\n\
-VUplRUVYMUNOaExnWmJXWGJhVk5OcXpoZFhyL0xTUQppM1Z6dW5OaUs3cndUVnE2\r\n\
-K2QyZ1lRckhMMmIvMXBBY3ZKWjJiNSs0bTRRc0NibFpjRENXTktRbHJnRWN5WXRJ\r\n\
-CkdscXJTbFFEaXA0ZnNrUFMvNDVkWTI0QmJsQ3NGU1k3RzVLVkxJck4zZFpGbmJr\r\n\
-NEZIS1hBZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJv\r\n\
-ZHVjdGlvbi1wb2ludCBiNGM3enlxNXNheGZzN2prNXFibG1wN3I1b3pwdHRvagpp\r\n\
-cC1hZGRyZXNzIDEwOS4xNjkuNDUuMjI2Cm9uaW9uLXBvcnQgOTAwMQpvbmlvbi1r\r\n\
-ZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQU8xSXpw\r\n\
-WFFUTUY3RXZUb1NEUXpzVnZiRVFRQUQrcGZ6NzczMVRXZzVaUEJZY1EyUkRaeVp4\r\n\
-OEQKNUVQSU1FeUE1RE83cGd0ak5LaXJvYXJGMC8yempjMkRXTUlSaXZyU29YUWVZ\r\n\
-ZXlMM1pzKzFIajJhMDlCdkYxZAp6MEswblRFdVhoNVR5V3lyMHdsbGI1SFBnTlI0\r\n\
-MS9oYkprZzkwZitPVCtIeGhKL1duUml2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBV\r\n\
-QkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMg\r\n\
-S0VZLS0tLS0KTUlHSkFvR0JBSzNWZEJ2ajFtQllLL3JrcHNwcm9Ub0llNUtHVmth\r\n\
-QkxvMW1tK1I2YUVJek1VZFE1SjkwNGtyRwpCd3k5NC8rV0lGNFpGYXh5Z2phejl1\r\n\
-N2pKY1k3ZGJhd1pFeG1hYXFCRlRwL2h2ZG9rcHQ4a1ByRVk4OTJPRHJ1CmJORUox\r\n\
-N1FPSmVMTVZZZk5Kcjl4TWZCQ3JQai8zOGh2RUdrbWVRNmRVWElvbVFNaUJGOVRB\r\n\
-Z01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlv\r\n\
-bi1wb2ludCBhdjVtcWl0Y2Q3cjJkandsYmN0c2Jlc2R3eGt0ZWtvegppcC1hZGRy\r\n\
-ZXNzIDE0NC43Ni44LjczCm9uaW9uLXBvcnQgNDQzCm9uaW9uLWtleQotLS0tLUJF\r\n\
-R0lOIFJTQSBQVUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTzVweVZzQmpZQmNmMXBE\r\n\
-dklHUlpmWXUzQ05nNldka0ZLMGlvdTBXTGZtejZRVDN0NWhzd3cyVwpjejlHMXhx\r\n\
-MmN0Nkd6VWkrNnVkTDlITTRVOUdHTi9BbW8wRG9GV1hKWHpBQkFXd2YyMVdsd1lW\r\n\
-eFJQMHRydi9WCkN6UDkzcHc5OG5vSmdGUGRUZ05iMjdKYmVUZENLVFBrTEtscXFt\r\n\
-b3NveUN2RitRa25vUS9BZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0t\r\n\
-LS0tCnNlcnZpY2Uta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpN\r\n\
-SUdKQW9HQkFMVjNKSmtWN3lTNU9jc1lHMHNFYzFQOTVRclFRR3ZzbGJ6Wi9zRGxl\r\n\
-RlpKYXFSOUYvYjRUVERNClNGcFMxcU1GbldkZDgxVmRGMEdYRmN2WVpLamRJdHU2\r\n\
-SndBaTRJeEhxeXZtdTRKdUxrcXNaTEFLaXRLVkx4eGsKeERlMjlDNzRWMmJrOTRJ\r\n\
-MEgybTNKS2tzTHVwc3VxWWRVUmhOVXN0SElKZmgyZmNIalF0bEFnTUJBQUU9Ci0t\r\n\
-LS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KCg==\r\n\
------END MESSAGE-----\r\n\
-signature\r\n\
------BEGIN SIGNATURE-----\r\n\
-d4OuCE5OLAOnRB6cQN6WyMEmg/BHem144Vec+eYgeWoKwx3MxXFplUjFxgnMlmwN\r\n\
-PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\r\n\
-myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\r\n\
------END SIGNATURE-----";
-
-/* DuckDuckGo descriptor as an example. */
-static const char *hs_desc_content = "\
-rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\n\
-version 2\n\
-permanent-key\n\
------BEGIN RSA PUBLIC KEY-----\n\
-MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE\n\
-aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg\n\
-I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=\n\
------END RSA PUBLIC KEY-----\n\
-secret-id-part anmjoxxwiupreyajjt5yasimfmwcnxlf\n\
-publication-time 2015-03-11 19:00:00\n\
-protocol-versions 2,3\n\
-introduction-points\n\
------BEGIN MESSAGE-----\n\
-aW50cm9kdWN0aW9uLXBvaW50IDd1bnd4cmg2dG5kNGh6eWt1Z3EzaGZzdHduc2ll\n\
-cmhyCmlwLWFkZHJlc3MgMTg4LjEzOC4xMjEuMTE4Cm9uaW9uLXBvcnQgOTAwMQpv\n\
-bmlvbi1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dC\n\
-QUxGRVVyeVpDbk9ROEhURmV5cDVjMTRObWVqL1BhekFLTTBxRENTNElKUWh0Y3g1\n\
-NXpRSFdOVWIKQ2hHZ0JqR1RjV3ZGRnA0N3FkdGF6WUZhVXE2c0lQKzVqeWZ5b0Q4\n\
-UmJ1bzBwQmFWclJjMmNhYUptWWM0RDh6Vgpuby9sZnhzOVVaQnZ1cWY4eHIrMDB2\n\
-S0JJNmFSMlA2OE1WeDhrMExqcUpUU2RKOE9idm9yQWdNQkFBRT0KLS0tLS1FTkQg\n\
-UlNBIFBVQkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQ\n\
-VUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTnJHb0ozeTlHNXQzN2F2ekI1cTlwN1hG\n\
-VUplRUVYMUNOaExnWmJXWGJhVk5OcXpoZFhyL0xTUQppM1Z6dW5OaUs3cndUVnE2\n\
-K2QyZ1lRckhMMmIvMXBBY3ZKWjJiNSs0bTRRc0NibFpjRENXTktRbHJnRWN5WXRJ\n\
-CkdscXJTbFFEaXA0ZnNrUFMvNDVkWTI0QmJsQ3NGU1k3RzVLVkxJck4zZFpGbmJr\n\
-NEZIS1hBZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJv\n\
-ZHVjdGlvbi1wb2ludCBiNGM3enlxNXNheGZzN2prNXFibG1wN3I1b3pwdHRvagpp\n\
-cC1hZGRyZXNzIDEwOS4xNjkuNDUuMjI2Cm9uaW9uLXBvcnQgOTAwMQpvbmlvbi1r\n\
-ZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQU8xSXpw\n\
-WFFUTUY3RXZUb1NEUXpzVnZiRVFRQUQrcGZ6NzczMVRXZzVaUEJZY1EyUkRaeVp4\n\
-OEQKNUVQSU1FeUE1RE83cGd0ak5LaXJvYXJGMC8yempjMkRXTUlSaXZyU29YUWVZ\n\
-ZXlMM1pzKzFIajJhMDlCdkYxZAp6MEswblRFdVhoNVR5V3lyMHdsbGI1SFBnTlI0\n\
-MS9oYkprZzkwZitPVCtIeGhKL1duUml2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBV\n\
-QkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMg\n\
-S0VZLS0tLS0KTUlHSkFvR0JBSzNWZEJ2ajFtQllLL3JrcHNwcm9Ub0llNUtHVmth\n\
-QkxvMW1tK1I2YUVJek1VZFE1SjkwNGtyRwpCd3k5NC8rV0lGNFpGYXh5Z2phejl1\n\
-N2pKY1k3ZGJhd1pFeG1hYXFCRlRwL2h2ZG9rcHQ4a1ByRVk4OTJPRHJ1CmJORUox\n\
-N1FPSmVMTVZZZk5Kcjl4TWZCQ3JQai8zOGh2RUdrbWVRNmRVWElvbVFNaUJGOVRB\n\
-Z01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlv\n\
-bi1wb2ludCBhdjVtcWl0Y2Q3cjJkandsYmN0c2Jlc2R3eGt0ZWtvegppcC1hZGRy\n\
-ZXNzIDE0NC43Ni44LjczCm9uaW9uLXBvcnQgNDQzCm9uaW9uLWtleQotLS0tLUJF\n\
-R0lOIFJTQSBQVUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTzVweVZzQmpZQmNmMXBE\n\
-dklHUlpmWXUzQ05nNldka0ZLMGlvdTBXTGZtejZRVDN0NWhzd3cyVwpjejlHMXhx\n\
-MmN0Nkd6VWkrNnVkTDlITTRVOUdHTi9BbW8wRG9GV1hKWHpBQkFXd2YyMVdsd1lW\n\
-eFJQMHRydi9WCkN6UDkzcHc5OG5vSmdGUGRUZ05iMjdKYmVUZENLVFBrTEtscXFt\n\
-b3NveUN2RitRa25vUS9BZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0t\n\
-LS0tCnNlcnZpY2Uta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpN\n\
-SUdKQW9HQkFMVjNKSmtWN3lTNU9jc1lHMHNFYzFQOTVRclFRR3ZzbGJ6Wi9zRGxl\n\
-RlpKYXFSOUYvYjRUVERNClNGcFMxcU1GbldkZDgxVmRGMEdYRmN2WVpLamRJdHU2\n\
-SndBaTRJeEhxeXZtdTRKdUxrcXNaTEFLaXRLVkx4eGsKeERlMjlDNzRWMmJrOTRJ\n\
-MEgybTNKS2tzTHVwc3VxWWRVUmhOVXN0SElKZmgyZmNIalF0bEFnTUJBQUU9Ci0t\n\
-LS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KCg==\n\
------END MESSAGE-----\n\
-signature\n\
------BEGIN SIGNATURE-----\n\
-d4OuCE5OLAOnRB6cQN6WyMEmg/BHem144Vec+eYgeWoKwx3MxXFplUjFxgnMlmwN\n\
-PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\n\
-myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\n\
------END SIGNATURE-----";
-
-/* Helper global variable for hidden service descriptor event test.
- * It's used as a pointer to dynamically created message buffer in
- * send_control_event_string_replacement function, which mocks
- * send_control_event_string function.
- *
- * Always free it after use! */
-static char *received_msg = NULL;
-
-/** Mock function for send_control_event_string
- */
-static void
-queue_control_event_string_replacement(uint16_t event, char *msg)
-{
-  (void) event;
-  tor_free(received_msg);
-  received_msg = msg;
-}
-
-/** Mock function for node_describe_longname_by_id, it returns either
- * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
- */
-static const char *
-node_describe_longname_by_id_replacement(const char *id_digest)
-{
-  if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
-    return STR_HSDIR_EXIST_LONGNAME;
-  } else {
-    return STR_HSDIR_NONE_EXIST_LONGNAME;
-  }
-}
-
-/** Test that we can parse a hardcoded v2 HS desc. */
-static void
-test_hs_parse_static_v2_desc(void *arg)
-{
-  int ret;
-  rend_encoded_v2_service_descriptor_t desc;
-
-  (void) arg;
-
-  /* Test an obviously not parseable string */
-  desc.desc_str = tor_strdup("ceci n'est pas un HS descriptor");
-  ret = rend_desc_v2_is_parsable(&desc);
-  tor_free(desc.desc_str);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Test an actual descriptor */
-  desc.desc_str = tor_strdup(hs_desc_content);
-  ret = rend_desc_v2_is_parsable(&desc);
-  tor_free(desc.desc_str);
-  tt_int_op(ret, OP_EQ, 1);
-
- done: ;
-}
-
-/** Make sure each hidden service descriptor async event generation
- *
- * function generates the message in expected format.
- */
-static void
-test_hs_desc_event(void *arg)
-{
-  #define STR_HS_ADDR "ajhb7kljbiru65qo"
-  #define STR_HS_CONTENT_DESC_ID "g5ojobzupf275beh5ra72uyhb3dkpxwg"
-  #define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3"
-
-  int ret;
-  rend_data_v2_t rend_query;
-  const char *expected_msg;
-  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-
-  (void) arg;
-  MOCK(queue_control_event_string,
-       queue_control_event_string_replacement);
-  MOCK(node_describe_longname_by_id,
-       node_describe_longname_by_id_replacement);
-
-  /* setup rend_query struct */
-  memset(&rend_query, 0, sizeof(rend_query));
-  rend_query.base_.version = 2;
-  strncpy(rend_query.onion_address, STR_HS_ADDR,
-          REND_SERVICE_ID_LEN_BASE32+1);
-  rend_query.auth_type = REND_NO_AUTH;
-  rend_query.base_.hsdirs_fp = smartlist_new();
-  smartlist_add(rend_query.base_.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID,
-                                                       DIGEST_LEN));
-
-  /* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */
-  ret = rend_compute_v2_desc_id(rend_query.descriptor_id[0],
-                                rend_query.onion_address,
-                                NULL, 0, 0);
-  tt_int_op(ret, OP_EQ, 0);
-  base32_encode(desc_id_base32, sizeof(desc_id_base32),
-                rend_query.descriptor_id[0], DIGEST_LEN);
-  /* Make sure rend_compute_v2_desc_id works properly. */
-  tt_mem_op(desc_id_base32, OP_EQ, STR_DESC_ID_BASE32,
-            sizeof(desc_id_base32));
-
-  /* test request event */
-  control_event_hs_descriptor_requested(rend_query.onion_address,
-                                        rend_query.auth_type, HSDIR_EXIST_ID,
-                                        STR_DESC_ID_BASE32, NULL);
-  expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\
-                  STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n";
-  tt_assert(received_msg);
-  tt_str_op(received_msg,OP_EQ, expected_msg);
-  tor_free(received_msg);
-
-  /* test received event */
-  rend_query.auth_type = REND_BASIC_AUTH;
-  control_event_hsv2_descriptor_received(rend_query.onion_address,
-                                         &rend_query.base_, HSDIR_EXIST_ID);
-  expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
-                  STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n";
-  tt_assert(received_msg);
-  tt_str_op(received_msg,OP_EQ, expected_msg);
-  tor_free(received_msg);
-
-  /* test failed event */
-  rend_query.auth_type = REND_STEALTH_AUTH;
-  control_event_hsv2_descriptor_failed(&rend_query.base_,
-                                     HSDIR_NONE_EXIST_ID,
-                                     "QUERY_REJECTED");
-  expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
-                  STR_HSDIR_NONE_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n";
-  tt_assert(received_msg);
-  tt_str_op(received_msg,OP_EQ, expected_msg);
-  tor_free(received_msg);
-
-  /* test invalid auth type */
-  rend_query.auth_type = 999;
-  control_event_hsv2_descriptor_failed(&rend_query.base_,
-                                     HSDIR_EXIST_ID,
-                                     "QUERY_REJECTED");
-  expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
-                  STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32\
-                  " REASON=QUERY_REJECTED\r\n";
-  tt_assert(received_msg);
-  tt_str_op(received_msg,OP_EQ, expected_msg);
-  tor_free(received_msg);
-
-  /* test no HSDir fingerprint type */
-  rend_query.auth_type = REND_NO_AUTH;
-  control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
-                                     "QUERY_NO_HSDIR");
-  expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
-                 "UNKNOWN REASON=QUERY_NO_HSDIR\r\n";
-  tt_assert(received_msg);
-  tt_str_op(received_msg,OP_EQ, expected_msg);
-  tor_free(received_msg);
-
-  /* test HSDir rate limited */
-  rend_query.auth_type = REND_NO_AUTH;
-  control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
-                                     "QUERY_RATE_LIMITED");
-  expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
-                 "UNKNOWN REASON=QUERY_RATE_LIMITED\r\n";
-  tt_assert(received_msg);
-  tt_str_op(received_msg,OP_EQ, expected_msg);
-  tor_free(received_msg);
-
-  /* Test invalid content with no HSDir fingerprint. */
-  char *exp_msg;
-  control_event_hs_descriptor_content(rend_query.onion_address,
-                                      STR_HS_CONTENT_DESC_ID, NULL, NULL);
-  tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\
-               STR_HS_CONTENT_DESC_ID " UNKNOWN" \
-               "\r\n\r\n.\r\n650 OK\r\n");
-  tt_assert(received_msg);
-  tt_str_op(received_msg, OP_EQ, exp_msg);
-  tor_free(received_msg);
-  tor_free(exp_msg);
-
-  /* test valid content. */
-  control_event_hs_descriptor_content(rend_query.onion_address,
-                                      STR_HS_CONTENT_DESC_ID, HSDIR_EXIST_ID,
-                                      hs_desc_content_control);
-  tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\
-               STR_HS_CONTENT_DESC_ID " " STR_HSDIR_EXIST_LONGNAME\
-               "\r\n%s\r\n.\r\n650 OK\r\n", hs_desc_content_control);
-
-  tt_assert(received_msg);
-  tt_str_op(received_msg, OP_EQ, exp_msg);
-  tor_free(received_msg);
-  tor_free(exp_msg);
-  SMARTLIST_FOREACH(rend_query.base_.hsdirs_fp, char *, d, tor_free(d));
-  smartlist_free(rend_query.base_.hsdirs_fp);
-
- done:
-  UNMOCK(queue_control_event_string);
-  UNMOCK(node_describe_longname_by_id);
-  tor_free(received_msg);
-}
-
-/* Make sure rend_data_t is valid at creation, destruction and when
- * duplicated. */
-static void
-test_hs_rend_data(void *arg)
-{
-  int rep;
-  rend_data_t *client = NULL, *client_dup = NULL;
-  /* Binary format of a descriptor ID. */
-  char desc_id[DIGEST_LEN];
-  char client_cookie[REND_DESC_COOKIE_LEN];
-  time_t now = time(NULL);
-  rend_data_t *service_dup = NULL;
-  rend_data_t *service = NULL;
-
-  (void)arg;
-
-  base32_decode(desc_id, sizeof(desc_id), STR_DESC_ID_BASE32,
-                REND_DESC_ID_V2_LEN_BASE32);
-  memset(client_cookie, 'e', sizeof(client_cookie));
-
-  client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie,
-                                   REND_NO_AUTH);
-  tt_assert(client);
-  rend_data_v2_t *client_v2 = TO_REND_DATA_V2(client);
-  tt_int_op(client_v2->auth_type, OP_EQ, REND_NO_AUTH);
-  tt_str_op(client_v2->onion_address, OP_EQ, STR_HS_ADDR);
-  tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
-  tt_mem_op(client_v2->descriptor_cookie, OP_EQ, client_cookie,
-            sizeof(client_cookie));
-  tt_assert(client->hsdirs_fp);
-  tt_int_op(smartlist_len(client->hsdirs_fp), OP_EQ, 0);
-  for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
-    int ret = rend_compute_v2_desc_id(desc_id, client_v2->onion_address,
-                                      client_v2->descriptor_cookie, now, rep);
-    /* That shouldn't never fail. */
-    tt_int_op(ret, OP_EQ, 0);
-    tt_mem_op(client_v2->descriptor_id[rep], OP_EQ, desc_id,
-              sizeof(desc_id));
-  }
-  /* The rest should be zeroed because this is a client request. */
-  tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), OP_EQ, 1);
-  tt_int_op(tor_digest_is_zero(client->rend_cookie), OP_EQ, 1);
-
-  /* Test dup(). */
-  client_dup = rend_data_dup(client);
-  tt_assert(client_dup);
-  rend_data_v2_t *client_dup_v2 = TO_REND_DATA_V2(client_dup);
-  tt_int_op(client_dup_v2->auth_type, OP_EQ, client_v2->auth_type);
-  tt_str_op(client_dup_v2->onion_address, OP_EQ, client_v2->onion_address);
-  tt_mem_op(client_dup_v2->desc_id_fetch, OP_EQ, client_v2->desc_id_fetch,
-            sizeof(client_dup_v2->desc_id_fetch));
-  tt_mem_op(client_dup_v2->descriptor_cookie, OP_EQ,
-            client_v2->descriptor_cookie,
-            sizeof(client_dup_v2->descriptor_cookie));
-
-  tt_assert(client_dup->hsdirs_fp);
-  tt_int_op(smartlist_len(client_dup->hsdirs_fp), OP_EQ, 0);
-  for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
-    tt_mem_op(client_dup_v2->descriptor_id[rep], OP_EQ,
-              client_v2->descriptor_id[rep], DIGEST_LEN);
-  }
-  /* The rest should be zeroed because this is a client request. */
-  tt_int_op(tor_digest_is_zero(client_dup_v2->rend_pk_digest), OP_EQ, 1);
-  tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), OP_EQ, 1);
-  rend_data_free(client);
-  client = NULL;
-  rend_data_free(client_dup);
-  client_dup = NULL;
-
-  /* Reset state. */
-  base32_decode(desc_id, sizeof(desc_id), STR_DESC_ID_BASE32,
-                REND_DESC_ID_V2_LEN_BASE32);
-  memset(client_cookie, 'e', sizeof(client_cookie));
-
-  /* Try with different parameters here for which some content should be
-   * zeroed out. */
-  client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH);
-  tt_assert(client);
-  client_v2 = TO_REND_DATA_V2(client);
-  tt_int_op(client_v2->auth_type, OP_EQ, REND_BASIC_AUTH);
-  tt_int_op(strlen(client_v2->onion_address), OP_EQ, 0);
-  tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
-  tt_int_op(fast_mem_is_zero(client_v2->descriptor_cookie,
-                            sizeof(client_v2->descriptor_cookie)), OP_EQ, 1);
-  tt_assert(client->hsdirs_fp);
-  tt_int_op(smartlist_len(client->hsdirs_fp), OP_EQ, 0);
-  for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
-    tt_int_op(tor_digest_is_zero(client_v2->descriptor_id[rep]), OP_EQ, 1);
-  }
-  /* The rest should be zeroed because this is a client request. */
-  tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), OP_EQ, 1);
-  tt_int_op(tor_digest_is_zero(client->rend_cookie), OP_EQ, 1);
-  rend_data_free(client);
-  client = NULL;
-
-  /* Let's test the service object now. */
-  char rend_pk_digest[DIGEST_LEN];
-  uint8_t rend_cookie[DIGEST_LEN];
-  memset(rend_pk_digest, 'f', sizeof(rend_pk_digest));
-  memset(rend_cookie, 'g', sizeof(rend_cookie));
-
-  service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest,
-                                     rend_cookie, REND_NO_AUTH);
-  tt_assert(service);
-  rend_data_v2_t *service_v2 = TO_REND_DATA_V2(service);
-  tt_int_op(service_v2->auth_type, OP_EQ, REND_NO_AUTH);
-  tt_str_op(service_v2->onion_address, OP_EQ, STR_HS_ADDR);
-  tt_mem_op(service_v2->rend_pk_digest, OP_EQ, rend_pk_digest,
-            sizeof(rend_pk_digest));
-  tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie));
-  tt_assert(service->hsdirs_fp);
-  tt_int_op(smartlist_len(service->hsdirs_fp), OP_EQ, 0);
-  for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
-    tt_int_op(tor_digest_is_zero(service_v2->descriptor_id[rep]), OP_EQ, 1);
-  }
-  /* The rest should be zeroed because this is a service request. */
-  tt_int_op(tor_digest_is_zero(service_v2->descriptor_cookie), OP_EQ, 1);
-  tt_int_op(tor_digest_is_zero(service_v2->desc_id_fetch), OP_EQ, 1);
-
-  /* Test dup(). */
-  service_dup = rend_data_dup(service);
-  rend_data_v2_t *service_dup_v2 = TO_REND_DATA_V2(service_dup);
-  tt_assert(service_dup);
-  tt_int_op(service_dup_v2->auth_type, OP_EQ, service_v2->auth_type);
-  tt_str_op(service_dup_v2->onion_address, OP_EQ, service_v2->onion_address);
-  tt_mem_op(service_dup_v2->rend_pk_digest, OP_EQ, service_v2->rend_pk_digest,
-            sizeof(service_dup_v2->rend_pk_digest));
-  tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie,
-            sizeof(service_dup->rend_cookie));
-  tt_assert(service_dup->hsdirs_fp);
-  tt_int_op(smartlist_len(service_dup->hsdirs_fp), OP_EQ, 0);
-  for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
-    tt_assert(tor_digest_is_zero(service_dup_v2->descriptor_id[rep]));
-  }
-  /* The rest should be zeroed because this is a service request. */
-  tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_cookie), OP_EQ, 1);
-  tt_int_op(tor_digest_is_zero(service_dup_v2->desc_id_fetch), OP_EQ, 1);
-
- done:
-  rend_data_free(service);
-  rend_data_free(service_dup);
-  rend_data_free(client);
-  rend_data_free(client_dup);
-}
-
-/* Test encoding and decoding service authorization cookies */
-static void
-test_hs_auth_cookies(void *arg)
-{
-#define TEST_COOKIE_RAW ((const uint8_t *) "abcdefghijklmnop")
-#define TEST_COOKIE_ENCODED "YWJjZGVmZ2hpamtsbW5vcA"
-#define TEST_COOKIE_ENCODED_STEALTH "YWJjZGVmZ2hpamtsbW5vcB"
-#define TEST_COOKIE_ENCODED_INVALID "YWJjZGVmZ2hpamtsbW5vcD"
-
-  char *encoded_cookie = NULL;
-  uint8_t raw_cookie[REND_DESC_COOKIE_LEN];
-  rend_auth_type_t auth_type;
-  char *err_msg = NULL;
-  int re;
-
-  (void)arg;
-
-  /* Test that encoding gives the expected result */
-  encoded_cookie = rend_auth_encode_cookie(TEST_COOKIE_RAW, REND_BASIC_AUTH);
-  tt_str_op(encoded_cookie, OP_EQ, TEST_COOKIE_ENCODED);
-  tor_free(encoded_cookie);
-
-  encoded_cookie = rend_auth_encode_cookie(TEST_COOKIE_RAW, REND_STEALTH_AUTH);
-  tt_str_op(encoded_cookie, OP_EQ, TEST_COOKIE_ENCODED_STEALTH);
-  tor_free(encoded_cookie);
-
-  /* Decoding should give the original value */
-  re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED, raw_cookie, &auth_type,
-                               &err_msg);
-  tt_assert(!re);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
-  tt_int_op(auth_type, OP_EQ, REND_BASIC_AUTH);
-  memset(raw_cookie, 0, sizeof(raw_cookie));
-
-  re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_STEALTH, raw_cookie,
-                               &auth_type, &err_msg);
-  tt_assert(!re);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
-  tt_int_op(auth_type, OP_EQ, REND_STEALTH_AUTH);
-  memset(raw_cookie, 0, sizeof(raw_cookie));
-
-  /* Decoding with padding characters should also work */
-  re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED "==", raw_cookie, NULL,
-                               &err_msg);
-  tt_assert(!re);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
-
-  /* Decoding with an unknown type should fail */
-  re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_INVALID, raw_cookie,
-                               &auth_type, &err_msg);
-  tt_int_op(re, OP_LT, 0);
-  tt_assert(err_msg);
-  tor_free(err_msg);
-
- done:
-  tor_free(encoded_cookie);
-  tor_free(err_msg);
-
-  return;
-}
-
-static int mock_get_options_calls = 0;
-static or_options_t *mock_options = NULL;
-
-static void
-reset_options(or_options_t *options, int *get_options_calls)
-{
-  memset(options, 0, sizeof(or_options_t));
-  options->TestingTorNetwork = 1;
-
-  *get_options_calls = 0;
-}
-
-static const or_options_t *
-mock_get_options(void)
-{
-  ++mock_get_options_calls;
-  tor_assert(mock_options);
-  return mock_options;
-}
-
-/* arg can't be 0 (the test fails) or 2 (the test is skipped) */
-#define CREATE_HS_DIR_NONE ((intptr_t)0x04)
-#define CREATE_HS_DIR1     ((intptr_t)0x08)
-#define CREATE_HS_DIR2     ((intptr_t)0x10)
-
-/* Test that single onion poisoning works. */
-static void
-test_single_onion_poisoning(void *arg)
-{
-  or_options_t opt;
-  mock_options = &opt;
-  reset_options(mock_options, &mock_get_options_calls);
-  MOCK(get_options, mock_get_options);
-
-  int ret = -1;
-  intptr_t create_dir_mask = (intptr_t)arg;
-  /* Get directories with a random suffix so we can repeat the tests */
-  mock_options->DataDirectory = tor_strdup(get_fname_rnd("test_data_dir"));
-  rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t));
-  char *dir1 = tor_strdup(get_fname_rnd("test_hs_dir1"));
-  rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t));
-  char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2"));
-  smartlist_t *services = smartlist_new();
-  char *poison_path = NULL;
-  char *err_msg = NULL;
-
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-
-  /* Create the data directory, and, if the correct bit in arg is set,
-   * create a directory for that service.
-   * The data directory is required for the lockfile, which is used when
-   * loading keys. */
-  ret = check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL);
-  tt_int_op(ret, OP_EQ, 0);
-  if (create_dir_mask & CREATE_HS_DIR1) {
-    ret = check_private_dir(dir1, CPD_CREATE, NULL);
-    tt_int_op(ret, OP_EQ, 0);
-  }
-  if (create_dir_mask & CREATE_HS_DIR2) {
-    ret = check_private_dir(dir2, CPD_CREATE, NULL);
-    tt_int_op(ret, OP_EQ, 0);
-  }
-
-  service_1->directory = dir1;
-  service_2->directory = dir2;
-  /* The services own the directory pointers now */
-  dir1 = dir2 = NULL;
-  /* Add port to service 1 */
-  service_1->ports = smartlist_new();
-  service_2->ports = smartlist_new();
-  hs_port_config_t *port1 = hs_parse_port_config("80", " ", &err_msg);
-  tt_assert(port1);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  smartlist_add(service_1->ports, port1);
-
-  hs_port_config_t *port2 = hs_parse_port_config("90", " ", &err_msg);
-  /* Add port to service 2 */
-  tt_assert(port2);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  smartlist_add(service_2->ports, port2);
-
-  /* No services, a service to verify, no problem! */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Either way, no problem. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Add the first service */
-  ret = hs_check_service_private_dir(mock_options->User, service_1->directory,
-                                     service_1->dir_group_readable, 1);
-  tt_int_op(ret, OP_EQ, 0);
-  smartlist_add(services, service_1);
-  /* But don't add the second service yet. */
-
-  /* Service directories, but no previous keys, no problem! */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Either way, no problem. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Poison! Poison! Poison!
-   * This can only be done in HiddenServiceSingleHopMode. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  /* Poisoning twice is a no-op. */
-  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Poisoned service directories, but no previous keys, no problem! */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Either way, no problem. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Now add some keys, and we'll have a problem. */
-  ret = rend_service_load_all_keys(services);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Poisoned service directories with previous keys are not allowed. */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_LT, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* But they are allowed if we're in non-anonymous mode. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Re-poisoning directories with existing keys is a no-op, because
-   * directories with existing keys are ignored. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  /* And it keeps the poison. */
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Now add the second service: it has no key and no poison file */
-  ret = hs_check_service_private_dir(mock_options->User, service_2->directory,
-                                     service_2->dir_group_readable, 1);
-  tt_int_op(ret, OP_EQ, 0);
-  smartlist_add(services, service_2);
-
-  /* A new service, and an existing poisoned service. Not ok. */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_LT, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* But ok to add in non-anonymous mode. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Now remove the poisoning from the first service, and we have the opposite
-   * problem. */
-  poison_path = rend_service_sos_poison_path(service_1);
-  tt_assert(poison_path);
-  ret = unlink(poison_path);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Unpoisoned service directories with previous keys are ok, as are empty
-   * directories. */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* But the existing unpoisoned key is not ok in non-anonymous mode, even if
-   * there is an empty service. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_LT, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Poisoning directories with existing keys is a no-op, because directories
-   * with existing keys are ignored. But the new directory should poison. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  /* And the old directory remains unpoisoned. */
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_LT, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* And the new directory should be ignored, because it has no key. */
-  mock_options->HiddenServiceSingleHopMode = 0;
-  mock_options->HiddenServiceNonAnonymousMode = 0;
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
-  /* Re-poisoning directories without existing keys is a no-op. */
-  mock_options->HiddenServiceSingleHopMode = 1;
-  mock_options->HiddenServiceNonAnonymousMode = 1;
-  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-  /* And the old directory remains unpoisoned. */
-  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
-  tt_int_op(ret, OP_LT, 0);
-  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
-  tt_int_op(ret, OP_EQ, 0);
-
- done:
-  /* The test harness deletes the directories at exit */
-  tor_free(poison_path);
-  tor_free(dir1);
-  tor_free(dir2);
-  smartlist_free(services);
-  rend_service_free(service_1);
-  rend_service_free(service_2);
-  UNMOCK(get_options);
-  tor_free(mock_options->DataDirectory);
-  tor_free(err_msg);
-}
-
-static rend_service_t *
-helper_create_rend_service(const char *path)
-{
-  rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t));
-  s->ports = smartlist_new();
-  s->intro_nodes = smartlist_new();
-  s->expiring_nodes = smartlist_new();
-  if (path) {
-    s->directory = tor_strdup(path);
-  }
-  return s;
-}
-
-static void
-test_prune_services_on_reload(void *arg)
-{
-  smartlist_t *new = smartlist_new(), *old = smartlist_new();
-  /* Non ephemeral service. */
-  rend_service_t *s1 = helper_create_rend_service("SomePath");
-  /* Create a non ephemeral service with the _same_ path as so we can test the
-   * transfer of introduction point between the same services on reload. */
-  rend_service_t *s2 = helper_create_rend_service(s1->directory);
-  /* Ephemeral service (directory is NULL). */
-  rend_service_t *e1 = helper_create_rend_service(NULL);
-  rend_service_t *e2 = helper_create_rend_service(NULL);
-
-  (void) arg;
-
-  {
-    /* Add both services to the old list. */
-    smartlist_add(old, s1);
-    smartlist_add(old, e1);
-    /* Only put the non ephemeral in the new list. */
-    smartlist_add(new, s1);
-    set_rend_service_list(old);
-    set_rend_rend_service_staging_list(new);
-    rend_service_prune_list_impl_();
-    /* We expect that the ephemeral one is in the new list but removed from
-     * the old one. */
-    tt_int_op(smartlist_len(old), OP_EQ, 1);
-    tt_assert(smartlist_get(old, 0) == s1);
-    tt_int_op(smartlist_len(new), OP_EQ, 2);
-    tt_assert(smartlist_get(new, 0) == s1);
-    tt_assert(smartlist_get(new, 1) == e1);
-    /* Cleanup for next test. */
-    smartlist_clear(new);
-    smartlist_clear(old);
-  }
-
-  {
-    /* This test will make sure that only the ephemeral service is kept if the
-     * new list is empty. The old list should contain only the non ephemeral
-     * one. */
-    smartlist_add(old, s1);
-    smartlist_add(old, e1);
-    set_rend_service_list(old);
-    set_rend_rend_service_staging_list(new);
-    rend_service_prune_list_impl_();
-    tt_int_op(smartlist_len(old), OP_EQ, 1);
-    tt_assert(smartlist_get(old, 0) == s1);
-    tt_int_op(smartlist_len(new), OP_EQ, 1);
-    tt_assert(smartlist_get(new, 0) == e1);
-    /* Cleanup for next test. */
-    smartlist_clear(new);
-    smartlist_clear(old);
-  }
-
-  {
-    /* This test makes sure that the new list stays the same even from the old
-     * list being completely different. */
-    smartlist_add(new, s1);
-    smartlist_add(new, e1);
-    set_rend_service_list(old);
-    set_rend_rend_service_staging_list(new);
-    rend_service_prune_list_impl_();
-    tt_int_op(smartlist_len(old), OP_EQ, 0);
-    tt_int_op(smartlist_len(new), OP_EQ, 2);
-    tt_assert(smartlist_get(new, 0) == s1);
-    tt_assert(smartlist_get(new, 1) == e1);
-    /* Cleanup for next test. */
-    smartlist_clear(new);
-  }
-
-  {
-    rend_intro_point_t ip1;
-    /* This IP should be found in the s2 service after pruning. */
-    smartlist_add(s1->intro_nodes, &ip1);
-    /* Setup our list. */
-    smartlist_add(old, s1);
-    smartlist_add(new, s2);
-    set_rend_service_list(old);
-    set_rend_rend_service_staging_list(new);
-    rend_service_prune_list_impl_();
-    tt_int_op(smartlist_len(old), OP_EQ, 1);
-    /* Intro nodes have been moved to the s2 in theory so it must be empty. */
-    tt_int_op(smartlist_len(s1->intro_nodes), OP_EQ, 0);
-    tt_int_op(smartlist_len(new), OP_EQ, 1);
-    rend_service_t *elem = smartlist_get(new, 0);
-    tt_assert(elem);
-    tt_assert(elem == s2);
-    tt_int_op(smartlist_len(elem->intro_nodes), OP_EQ, 1);
-    tt_assert(smartlist_get(elem->intro_nodes, 0) == &ip1);
-    smartlist_clear(s1->intro_nodes);
-    smartlist_clear(s2->intro_nodes);
-    /* Cleanup for next test. */
-    smartlist_clear(new);
-    smartlist_clear(old);
-  }
-
-  {
-    /* Test two ephemeral services. */
-    smartlist_add(old, e1);
-    smartlist_add(old, e2);
-    set_rend_service_list(old);
-    set_rend_rend_service_staging_list(new);
-    rend_service_prune_list_impl_();
-    /* Check if they've all been transferred. */
-    tt_int_op(smartlist_len(old), OP_EQ, 0);
-    tt_int_op(smartlist_len(new), OP_EQ, 2);
-  }
-
- done:
-  rend_service_free(s1);
-  rend_service_free(s2);
-  rend_service_free(e1);
-  rend_service_free(e2);
-  smartlist_free(new);
-  smartlist_free(old);
-}
-
-struct testcase_t hs_tests[] = {
-  { "hs_rend_data", test_hs_rend_data, TT_FORK,
-    NULL, NULL },
-  { "hs_parse_static_v2_desc", test_hs_parse_static_v2_desc, TT_FORK,
-    NULL, NULL },
-  { "hs_desc_event", test_hs_desc_event, TT_FORK,
-    NULL, NULL },
-  { "hs_auth_cookies", test_hs_auth_cookies, TT_FORK,
-    NULL, NULL },
-  { "single_onion_poisoning_create_dir_none", test_single_onion_poisoning,
-    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR_NONE) },
-  { "single_onion_poisoning_create_dir1", test_single_onion_poisoning,
-    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1) },
-  { "single_onion_poisoning_create_dir2", test_single_onion_poisoning,
-    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR2) },
-  { "single_onion_poisoning_create_dir_both", test_single_onion_poisoning,
-    TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1 | CREATE_HS_DIR2) },
-  { "prune_services_on_reload", test_prune_services_on_reload, TT_FORK,
-    NULL, NULL },
-
-  END_OF_TESTCASES
-};
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index df96b2c791..c3e0eee691 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -14,7 +14,6 @@
 
 #include "trunnel/ed25519_cert.h"
 #include "feature/hs/hs_cache.h"
-#include "feature/rend/rendcache.h"
 #include "feature/dircache/dircache.h"
 #include "feature/dirclient/dirclient.h"
 #include "feature/nodelist/networkstatus.h"
@@ -51,8 +50,6 @@ init_test(void)
 {
   /* Always needed. Initialize the subsystem. */
   hs_cache_init();
-  /* We need the v2 cache since our OOM and cache cleanup does poke at it. */
-  rend_cache_init();
 }
 
 static void
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index f59b3a59cd..7df9fc5d31 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -19,7 +19,6 @@
 #include "test/test.h"
 #include "test/test_helpers.h"
 #include "test/log_test_helpers.h"
-#include "test/rend_test_helpers.h"
 #include "test/hs_test_helpers.h"
 
 #include "app/config/config.h"
@@ -38,7 +37,6 @@
 #include "feature/hs/hs_config.h"
 #include "feature/hs/hs_ident.h"
 #include "feature/hs/hs_cache.h"
-#include "feature/rend/rendcache.h"
 #include "core/or/circuitlist.h"
 #include "core/or/circuitbuild.h"
 #include "core/or/extendinfo.h"
@@ -137,12 +135,9 @@ helper_add_random_client_auth(const ed25519_public_key_t *service_pk)
  * hidden service. */
 static int
 helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
-                                    connection_t **conn_out,
-                                    int is_legacy)
+                                    connection_t **conn_out)
 {
-  int retval;
   channel_tls_t *n_chan=NULL;
-  rend_data_t *conn_rend_data = NULL;
   origin_circuit_t *or_circ = NULL;
   connection_t *conn = NULL;
   ed25519_public_key_t service_pk;
@@ -151,20 +146,13 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
   conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT,
                                   CONN_TYPE_AP /* ??? */,
                                   0);
-  if (is_legacy) {
-    /* Legacy: Setup rend_data of stream */
-    char service_id[REND_SERVICE_ID_LEN_BASE32+1] = {0};
-    TO_EDGE_CONN(conn)->rend_data = mock_rend_data(service_id);
-    conn_rend_data = TO_EDGE_CONN(conn)->rend_data;
-  } else {
-    /* prop224: Setup hs conn identifier on the stream */
-    ed25519_secret_key_t sk;
-    tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
-    tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
-
-    /* Setup hs_conn_identifier of stream */
-    TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
-  }
+  /* prop224: Setup hs conn identifier on the stream */
+  ed25519_secret_key_t sk;
+  tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
+  tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
+
+  /* Setup hs_conn_identifier of stream */
+  TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
 
   /* Make it wait for circuit */
   connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn));
@@ -184,23 +172,8 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
   or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
   or_circ->build_state->is_internal = 1;
 
-  if (is_legacy) {
-    /* Legacy: Setup rend data and final cpath */
-    or_circ->build_state->pending_final_cpath =
-      tor_malloc_zero(sizeof(crypt_path_t));
-    or_circ->build_state->pending_final_cpath->magic = CRYPT_PATH_MAGIC;
-    or_circ->build_state->pending_final_cpath->rend_dh_handshake_state =
-      crypto_dh_new(DH_TYPE_REND);
-    tt_assert(
-         or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
-    retval = crypto_dh_generate_public(
-           or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
-    tt_int_op(retval, OP_EQ, 0);
-    or_circ->rend_data = rend_data_dup(conn_rend_data);
-  } else {
-    /* prop224: Setup hs ident on the circuit */
-    or_circ->hs_ident = hs_ident_circuit_new(&service_pk);
-  }
+  /* prop224: Setup hs ident on the circuit */
+  or_circ->hs_ident = hs_ident_circuit_new(&service_pk);
 
   TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
 
@@ -219,91 +192,6 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
   return -1;
 }
 
-/* Test: Ensure that setting up legacy e2e rendezvous circuits works
- * correctly. */
-static void
-test_e2e_rend_circuit_setup_legacy(void *arg)
-{
-  ssize_t retval;
-  origin_circuit_t *or_circ = NULL;
-  connection_t *conn = NULL;
-
-  (void) arg;
-
-  /** In this test we create a v2 legacy HS stream and a circuit with the same
-   *  hidden service destination. We make the stream wait for circuits to be
-   *  established to the hidden service, and then we complete the circuit using
-   *  the hs_circuit_setup_e2e_rend_circ_legacy_client() function. We then
-   *  check that the end-to-end cpath was setup correctly and that the stream
-   *  was attached to the circuit as expected. */
-
-  MOCK(connection_ap_handshake_send_begin,
-       mock_connection_ap_handshake_send_begin);
-
-  /* Setup */
-  retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 1);
-  tt_int_op(retval, OP_EQ, 0);
-  tt_assert(or_circ);
-  tt_assert(conn);
-
-  /* Check number of hops */
-  retval = cpath_get_n_hops(&or_circ->cpath);
-  tt_int_op(retval, OP_EQ, 0);
-
-  /* Check that our stream is not attached on any circuits */
-  tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, NULL);
-
-  /********************************************** */
-
-  /* Make a good RENDEZVOUS1 cell body because it needs to pass key exchange
-   * digest verification... */
-  uint8_t rend_cell_body[DH1024_KEY_LEN+DIGEST_LEN] = {2};
-  {
-    char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
-    crypto_dh_t *dh_state =
-      or_circ->build_state->pending_final_cpath->rend_dh_handshake_state;
-    /* compute and overwrite digest of cell body with the right value */
-    retval = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh_state,
-                                      (char*)rend_cell_body, DH1024_KEY_LEN,
-                                      keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN);
-    tt_int_op(retval, OP_GT, 0);
-    memcpy(rend_cell_body+DH1024_KEY_LEN, keys, DIGEST_LEN);
-  }
-
-  /* Setup the circuit */
-  retval = hs_circuit_setup_e2e_rend_circ_legacy_client(or_circ,
-                                                        rend_cell_body);
-  tt_int_op(retval, OP_EQ, 0);
-
-  /**********************************************/
-
-  /* See that a hop was added to the circuit's cpath */
-  retval = cpath_get_n_hops(&or_circ->cpath);
-  tt_int_op(retval, OP_EQ, 1);
-
-  /* Check the digest algo */
-  tt_int_op(
-         crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
-            OP_EQ, DIGEST_SHA1);
-  tt_int_op(
-         crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
-            OP_EQ, DIGEST_SHA1);
-  tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
-  tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
-
-  /* Ensure that circ purpose was changed */
-  tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
-
-  /* Test that stream got attached */
-  tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
-
- done:
-  connection_free_minimal(conn);
-  if (or_circ)
-    tor_free(TO_CIRCUIT(or_circ)->n_chan);
-  circuit_free_(TO_CIRCUIT(or_circ));
-}
-
 /* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
 static void
 test_e2e_rend_circuit_setup(void *arg)
@@ -326,7 +214,7 @@ test_e2e_rend_circuit_setup(void *arg)
        mock_connection_ap_handshake_send_begin);
 
   /* Setup */
-  retval = helper_get_circ_and_stream_for_test(&or_circ, &conn, 0);
+  retval = helper_get_circ_and_stream_for_test(&or_circ, &conn);
   tt_int_op(retval, OP_EQ, 0);
   tt_assert(or_circ);
   tt_assert(conn);
@@ -974,7 +862,6 @@ test_close_intro_circuits_new_desc(void *arg)
   (void) arg;
 
   hs_init();
-  rend_cache_init();
 
   /* This is needed because of the client cache expiration timestamp is based
    * on having a consensus. See cached_client_descriptor_has_expired(). */
@@ -1120,7 +1007,6 @@ test_close_intro_circuits_cache_clean(void *arg)
   (void) arg;
 
   hs_init();
-  rend_cache_init();
 
   /* This is needed because of the client cache expiration timestamp is based
    * on having a consensus. See cached_client_descriptor_has_expired(). */
@@ -1189,7 +1075,6 @@ test_close_intro_circuits_cache_clean(void *arg)
   circuit_free(circ);
   hs_descriptor_free(desc1);
   hs_free_all();
-  rend_cache_free_all();
   UNMOCK(networkstatus_get_reasonably_live_consensus);
 }
 
@@ -1554,8 +1439,6 @@ test_purge_ephemeral_client_auth(void *arg)
 }
 
 struct testcase_t hs_client_tests[] = {
-  { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
-    TT_FORK, NULL, NULL },
   { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup,
     TT_FORK, NULL, NULL },
   { "client_pick_intro", test_client_pick_intro,
diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c
index dc3b598c34..104e5effbb 100644
--- a/src/test/test_hs_config.c
+++ b/src/test/test_hs_config.c
@@ -18,7 +18,6 @@
 #include "feature/hs/hs_common.h"
 #include "feature/hs/hs_config.h"
 #include "feature/hs/hs_service.h"
-#include "feature/rend/rendservice.h"
 
 static int
 helper_config_service(const char *conf, int validate_only)
@@ -49,7 +48,7 @@ test_invalid_service(void *arg)
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
     tt_int_op(ret, OP_EQ, -1);
-    expect_log_msg_containing("HiddenServiceVersion must be between 2 and 3");
+    expect_log_msg_containing("HiddenServiceVersion must be between 3 and 3");
     teardown_capture_of_logs();
   }
 
@@ -57,7 +56,7 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServiceAllowUnknownPorts 2\n"; /* Should be 0 or 1. */
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
@@ -72,7 +71,7 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServiceDirGroupReadable 2\n"; /* Should be 0 or 1. */
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
@@ -87,7 +86,7 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServiceMaxStreamsCloseCircuit 2\n"; /* Should be 0 or 1. */
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
@@ -102,7 +101,7 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServicePort 80\n"
       "HiddenServiceMaxStreams 65536\n"; /* One too many. */
     setup_full_capture_of_logs(LOG_WARN);
@@ -117,10 +116,10 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServicePort 80\n"
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServicePort 81\n";
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
@@ -134,7 +133,7 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServicePort 65536\n";
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
@@ -147,7 +146,7 @@ test_invalid_service(void *arg)
   {
     const char *conf =
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServicePort 80 127.0.0.1 8000\n";
     setup_full_capture_of_logs(LOG_WARN);
     ret = helper_config_service(conf, 1);
@@ -160,7 +159,7 @@ test_invalid_service(void *arg)
   /* Out of order directives. */
   {
     const char *conf =
-      "HiddenServiceVersion 2\n"
+      "HiddenServiceVersion 3\n"
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
       "HiddenServicePort 80\n";
     setup_full_capture_of_logs(LOG_WARN);
@@ -182,18 +181,11 @@ test_valid_service(void *arg)
 
   (void) arg;
 
-  /* Mix of v2 and v3. Still valid. */
   {
     const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 80\n"
       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
       "HiddenServiceVersion 3\n"
-      "HiddenServicePort 81\n"
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 82\n";
+      "HiddenServicePort 81\n";
     ret = helper_config_service(conf, 1);
     tt_int_op(ret, OP_EQ, 0);
   }
@@ -202,127 +194,6 @@ test_valid_service(void *arg)
   ;
 }
 
-static void
-test_invalid_service_v2(void *arg)
-{
-  int validate_only = 1, ret;
-
-  (void) arg;
-
-  /* Try with a missing port configuration. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n";
-    setup_full_capture_of_logs(LOG_WARN);
-    ret = helper_config_service(conf, validate_only);
-    tt_int_op(ret, OP_EQ, -1);
-    expect_log_msg_containing("with no ports configured.");
-    teardown_capture_of_logs();
-  }
-
-  /* Too many introduction points. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 80\n"
-      "HiddenServiceNumIntroductionPoints 11\n"; /* One too many. */
-    setup_full_capture_of_logs(LOG_WARN);
-    ret = helper_config_service(conf, validate_only);
-    tt_int_op(ret, OP_EQ, -1);
-    expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
-                              "be between 0 and 10, not 11.");
-    teardown_capture_of_logs();
-  }
-
-  /* Too little introduction points. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 80\n"
-      "HiddenServiceNumIntroductionPoints -1\n";
-    setup_full_capture_of_logs(LOG_WARN);
-    ret = helper_config_service(conf, validate_only);
-    tt_int_op(ret, OP_EQ, -1);
-    expect_log_msg_containing("Could not parse "
-                              "HiddenServiceNumIntroductionPoints: "
-                              "Integer -1 is malformed or out of bounds.");
-    teardown_capture_of_logs();
-  }
-
-  /* Bad authorized client type. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 80\n"
-      "HiddenServiceAuthorizeClient blah alice,bob\n"; /* blah is no good. */
-    setup_full_capture_of_logs(LOG_WARN);
-    ret = helper_config_service(conf, validate_only);
-    tt_int_op(ret, OP_EQ, -1);
-    expect_log_msg_containing("HiddenServiceAuthorizeClient contains "
-                              "unrecognized auth-type");
-    teardown_capture_of_logs();
-  }
-
- done:
-  ;
-}
-
-static void
-test_valid_service_v2(void *arg)
-{
-  int ret;
-
-  (void) arg;
-  mock_hostname_resolver();
-
-  /* Valid complex configuration. Basic client authorization. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 80\n"
-      "HiddenServicePort 22 localhost:22\n"
-#ifdef HAVE_SYS_UN_H
-      "HiddenServicePort 42 unix:/path/to/socket\n"
-#endif
-      "HiddenServiceAuthorizeClient basic alice,bob,eve\n"
-      "HiddenServiceAllowUnknownPorts 1\n"
-      "HiddenServiceMaxStreams 42\n"
-      "HiddenServiceMaxStreamsCloseCircuit 0\n"
-      "HiddenServiceDirGroupReadable 1\n"
-      "HiddenServiceNumIntroductionPoints 7\n";
-    ret = helper_config_service(conf, 1);
-    tt_int_op(ret, OP_EQ, 0);
-  }
-
-  /* Valid complex configuration. Stealth client authorization. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 65535\n"
-      "HiddenServicePort 22 1.1.1.1:22\n"
-#ifdef HAVE_SYS_UN_H
-      "HiddenServicePort 9000 unix:/path/to/socket\n"
-#endif
-      "HiddenServiceAuthorizeClient stealth charlie,romeo\n"
-      "HiddenServiceAllowUnknownPorts 0\n"
-      "HiddenServiceMaxStreams 42\n"
-      "HiddenServiceMaxStreamsCloseCircuit 0\n"
-      "HiddenServiceDirGroupReadable 1\n"
-      "HiddenServiceNumIntroductionPoints 8\n";
-    ret = helper_config_service(conf, 1);
-    tt_int_op(ret, OP_EQ, 0);
-  }
-
- done:
-  unmock_hostname_resolver();
-}
-
 static void
 test_invalid_service_v3(void *arg)
 {
@@ -438,22 +309,6 @@ test_valid_service_v3(void *arg)
     tt_int_op(ret, OP_EQ, 0);
   }
 
-  /* Mix of v2 and v3. Still valid. */
-  {
-    const char *conf =
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 80\n"
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
-      "HiddenServiceVersion 3\n"
-      "HiddenServicePort 81\n"
-      "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
-      "HiddenServiceVersion 2\n"
-      "HiddenServicePort 82\n";
-    ret = helper_config_service(conf, 1);
-    tt_int_op(ret, OP_EQ, 0);
-  }
-
  done:
   unmock_hostname_resolver();
 }
@@ -489,8 +344,6 @@ test_staging_service_v3(void *arg)
   tt_int_op(ret, OP_EQ, 0);
   /* Ok, we have a service in our map! Registration went well. */
   tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
-  /* Make sure we don't have a magic v2 service out of this. */
-  tt_int_op(rend_num_services(), OP_EQ, 0);
 
  done:
   hs_free_all();
@@ -611,12 +464,6 @@ struct testcase_t hs_config_tests[] = {
   { "valid_service", test_valid_service, TT_FORK,
     NULL, NULL },
 
-  /* Test case only for version 2. */
-  { "invalid_service_v2", test_invalid_service_v2, TT_FORK,
-    NULL, NULL },
-  { "valid_service_v2", test_valid_service_v2, TT_FORK,
-    NULL, NULL },
-
   /* Test case only for version 3. */
   { "invalid_service_v3", test_invalid_service_v3, TT_FORK,
     NULL, NULL },
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
index dfc1e5445e..e4999a4ed5 100644
--- a/src/test/test_hs_control.c
+++ b/src/test/test_hs_control.c
@@ -25,7 +25,6 @@
 #include "feature/hs/hs_client.h"
 #include "feature/hs/hs_control.h"
 #include "feature/nodelist/nodelist.h"
-#include "feature/rend/rendservice.h"
 
 #include "feature/nodelist/node_st.h"
 #include "feature/nodelist/routerstatus_st.h"
@@ -797,7 +796,7 @@ test_hs_control_add_onion_helper_add_service(void *arg)
   hs_service_authorized_client_t *client_good, *client_bad;
   smartlist_t *list_good, *list_bad;
   hs_service_ht *global_map;
-  rend_service_port_config_t *portcfg;
+  hs_port_config_t *portcfg;
   smartlist_t *portcfgs;
   char *address_out_good, *address_out_bad;
   hs_service_t *service_good = NULL;
@@ -808,7 +807,7 @@ test_hs_control_add_onion_helper_add_service(void *arg)
   hs_init();
   global_map = get_hs_service_map();
 
-  portcfg = rend_service_parse_port_config("8080", ",", NULL);
+  portcfg = hs_parse_port_config("8080", ",", NULL);
   portcfgs = smartlist_new();
   smartlist_add(portcfgs, portcfg);
 
@@ -831,7 +830,7 @@ test_hs_control_add_onion_helper_add_service(void *arg)
   smartlist_add(list_good, client_good);
 
   add_onion_helper_add_service(HS_VERSION_THREE, &sk_good, portcfgs, 1, 1,
-                          REND_V3_AUTH, NULL, list_good, &address_out_good);
+                               list_good, &address_out_good);
 
   service_good = find_service(global_map, &pk_good);
   tt_int_op(smartlist_len(service_good->config.clients), OP_EQ, 1);
@@ -841,12 +840,12 @@ test_hs_control_add_onion_helper_add_service(void *arg)
   list_bad = smartlist_new();
   smartlist_add(list_bad, client_bad);
 
-  portcfg = rend_service_parse_port_config("8080", ",", NULL);
+  portcfg = hs_parse_port_config("8080", ",", NULL);
   portcfgs = smartlist_new();
   smartlist_add(portcfgs, portcfg);
 
   add_onion_helper_add_service(HS_VERSION_THREE, &sk_bad, portcfgs, 1, 1,
-                          REND_V3_AUTH, NULL, list_bad, &address_out_bad);
+                               list_bad, &address_out_bad);
 
   service_bad = find_service(global_map, &pk_bad);
 
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index e6b27d7a50..d18de775ae 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -21,7 +21,6 @@
 #include "core/or/circuituse.h"
 #include "ht.h"
 #include "core/or/relay.h"
-#include "feature/rend/rendservice.h"
 
 #include "feature/hs/hs_cell.h"
 #include "feature/hs/hs_circuitmap.h"
@@ -517,42 +516,6 @@ helper_establish_intro_v3(or_circuit_t *intro_circ)
   return cell;
 }
 
-/* Helper function: Send a well-formed v2 ESTABLISH_INTRO cell to
- * <b>intro_circ</b>. Return the public key advertised in the cell. */
-static crypto_pk_t *
-helper_establish_intro_v2(or_circuit_t *intro_circ)
-{
-  crypto_pk_t *key1 = NULL;
-  int retval;
-  uint8_t cell_body[RELAY_PAYLOAD_SIZE];
-  ssize_t cell_len = 0;
-  char circ_nonce[DIGEST_LEN] = {0};
-
-  tt_assert(intro_circ);
-
-  /* Prepare the circuit for the incoming ESTABLISH_INTRO */
-  crypto_rand(circ_nonce, sizeof(circ_nonce));
-  helper_prepare_circ_for_intro(intro_circ, circ_nonce);
-
-  /* Send legacy establish_intro */
-  key1 = pk_generate(0);
-
-  /* Use old circ_nonce why not */
-  cell_len = rend_service_encode_establish_intro_cell(
-                                           (char*)cell_body,
-                                           sizeof(cell_body), key1,
-                                           circ_nonce);
-  tt_int_op(cell_len, OP_GT, 0);
-
-  /* Receive legacy establish_intro */
-  retval = hs_intro_received_establish_intro(intro_circ,
-                                             cell_body, (size_t) cell_len);
-  tt_int_op(retval, OP_EQ, 0);
-
- done:
-  return key1;
-}
-
 /* Helper function: test circuitmap free_all function outside of
  * test_intro_point_registration to prevent Coverity from seeing a
  * double free if the assertion hypothetically fails.
@@ -571,21 +534,17 @@ test_circuitmap_free_all(void)
   ;
 }
 
-/** Successfully register a v2 intro point and a v3 intro point. Ensure that HS
+/** Successfully register a v3 intro point. Ensure that HS
  *  circuitmap is maintained properly. */
 static void
 test_intro_point_registration(void *arg)
 {
-  int retval;
   hs_circuitmap_ht *the_hs_circuitmap = NULL;
 
   or_circuit_t *intro_circ = NULL;
   trn_cell_establish_intro_t *establish_intro_cell = NULL;
   ed25519_public_key_t auth_key;
 
-  crypto_pk_t *legacy_auth_key = NULL;
-  or_circuit_t *legacy_intro_circ = NULL;
-
   or_circuit_t *returned_intro_circ = NULL;
 
   (void) arg;
@@ -621,35 +580,11 @@ test_intro_point_registration(void *arg)
     tt_ptr_op(intro_circ, OP_EQ, returned_intro_circ);
   }
 
-  /* Create a v2 intro point */
-  {
-    char key_digest[DIGEST_LEN];
-
-    legacy_intro_circ = or_circuit_new(1, NULL);
-    tt_assert(legacy_intro_circ);
-    legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ);
-    tt_assert(legacy_auth_key);
-
-    /* Check that the circuitmap now has two elements */
-    the_hs_circuitmap = get_hs_circuitmap();
-    tt_assert(the_hs_circuitmap);
-    tt_int_op(2, OP_EQ, HT_SIZE(the_hs_circuitmap));
-
-    /* Check that the new element is our legacy intro circuit. */
-    retval = crypto_pk_get_digest(legacy_auth_key, key_digest);
-    tt_int_op(retval, OP_EQ, 0);
-    returned_intro_circ =
-      hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest);
-    tt_ptr_op(legacy_intro_circ, OP_EQ, returned_intro_circ);
-  }
-
   /* XXX Continue test and try to register a second v3 intro point with the
    * same auth key. Make sure that old intro circuit gets closed. */
 
  done:
-  crypto_pk_free(legacy_auth_key);
   circuit_free_(TO_CIRCUIT(intro_circ));
-  circuit_free_(TO_CIRCUIT(legacy_intro_circ));
   trn_cell_establish_intro_free(establish_intro_cell);
   test_circuitmap_free_all();
 
@@ -719,31 +654,6 @@ test_introduce1_suitable_circuit(void *arg)
   ;
 }
 
-static void
-test_introduce1_is_legacy(void *arg)
-{
-  int ret;
-  uint8_t request[256];
-
-  (void) arg;
-
-  /* For a cell to be considered legacy, according to the specification, the
-   * first 20 bytes MUST BE non-zero else it's a v3 cell. */
-  memset(request, 'a', DIGEST_LEN);
-  memset(request + DIGEST_LEN, 0, sizeof(request) - DIGEST_LEN);
-  ret = introduce1_cell_is_legacy(request);
-  tt_int_op(ret, OP_EQ, 1);
-
-  /* This is a NON legacy cell. */
-  memset(request, 0, DIGEST_LEN);
-  memset(request + DIGEST_LEN, 'a', sizeof(request) - DIGEST_LEN);
-  ret = introduce1_cell_is_legacy(request);
-  tt_int_op(ret, OP_EQ, 0);
-
- done:
-  ;
-}
-
 static void
 test_introduce1_validation(void *arg)
 {
@@ -757,20 +667,6 @@ test_introduce1_validation(void *arg)
   cell = helper_create_introduce1_cell();
   tt_assert(cell);
 
-#ifndef ALL_BUGS_ARE_FATAL
-  /* It should NOT be a legacy cell which will trigger a BUG(). */
-  memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id));
-  tor_capture_bugs_(1);
-  ret = validate_introduce1_parsed_cell(cell);
-  tor_end_capture_bugs_();
-  tt_int_op(ret, OP_EQ, -1);
-#endif /* !defined(ALL_BUGS_ARE_FATAL) */
-
-  /* Reset legacy ID and make sure it's correct. */
-  memset(cell->legacy_key_id, 0, sizeof(cell->legacy_key_id));
-  ret = validate_introduce1_parsed_cell(cell);
-  tt_int_op(ret, OP_EQ, 0);
-
   /* Non existing auth key type. */
   cell->auth_key_type = 42;
   ret = validate_introduce1_parsed_cell(cell);
@@ -877,35 +773,6 @@ test_received_introduce1_handling(void *arg)
     tt_int_op(ret, OP_EQ, 0);
   }
 
-  /* Valid legacy cell. */
-  {
-    tor_free(request);
-    trn_cell_introduce1_free(cell);
-    cell = helper_create_introduce1_cell();
-    uint8_t *legacy_key_id = trn_cell_introduce1_getarray_legacy_key_id(cell);
-    memset(legacy_key_id, 'a', DIGEST_LEN);
-    /* Add an arbitrary amount of data for the payload of a v2 cell. */
-    size_t request_len = trn_cell_introduce1_encoded_len(cell) + 256;
-    tt_size_op(request_len, OP_GT, 0);
-    request = tor_malloc_zero(request_len + 256);
-    ssize_t encoded_len =
-      trn_cell_introduce1_encode(request, request_len, cell);
-    tt_int_op((int)encoded_len, OP_GT, 0);
-
-    circ = helper_create_intro_circuit();
-    or_circuit_t *service_circ = helper_create_intro_circuit();
-    circuit_change_purpose(TO_CIRCUIT(service_circ),
-                           CIRCUIT_PURPOSE_INTRO_POINT);
-    /* Register the circuit in the map for the auth key of the cell. */
-    uint8_t token[REND_TOKEN_LEN];
-    memcpy(token, legacy_key_id, sizeof(token));
-    hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token);
-    ret = hs_intro_received_introduce1(circ, request, request_len);
-    circuit_free_(TO_CIRCUIT(circ));
-    circuit_free_(TO_CIRCUIT(service_circ));
-    tt_int_op(ret, OP_EQ, 0);
-  }
-
  done:
   trn_cell_introduce1_free(cell);
   tor_free(request);
@@ -1109,9 +976,6 @@ struct testcase_t hs_intropoint_tests[] = {
   { "introduce1_suitable_circuit",
     test_introduce1_suitable_circuit, TT_FORK, NULL, &test_setup},
 
-  { "introduce1_is_legacy",
-    test_introduce1_is_legacy, TT_FORK, NULL, &test_setup},
-
   { "introduce1_validation",
     test_introduce1_validation, TT_FORK, NULL, &test_setup},
 
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 66e8e2f473..91d4689848 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -26,7 +26,6 @@
 #include "test/test.h"
 #include "test/test_helpers.h"
 #include "test/log_test_helpers.h"
-#include "test/rend_test_helpers.h"
 #include "test/hs_test_helpers.h"
 
 #include "core/or/or.h"
@@ -58,7 +57,6 @@
 #include "feature/hs/hs_service.h"
 #include "feature/nodelist/networkstatus.h"
 #include "feature/nodelist/nodelist.h"
-#include "feature/rend/rendservice.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/fs/dir.h"
 
@@ -383,14 +381,13 @@ test_load_keys(void *arg)
 {
   int ret;
   char *conf = NULL;
-  char *hsdir_v2 = tor_strdup(get_fname("hs2"));
   char *hsdir_v3 = tor_strdup(get_fname("hs3"));
   char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
 
   (void) arg;
 
-  /* We'll register two services, a v2 and a v3, then we'll load keys and
-   * validate that both are in a correct state. */
+  /* We'll register one service then we'll load keys and validate that both
+   * are in a correct state. */
 
   hs_init();
 
@@ -399,15 +396,6 @@ test_load_keys(void *arg)
   "HiddenServiceVersion %d\n" \
   "HiddenServicePort 65535\n"
 
-  /* v2 service. */
-  tor_asprintf(&conf, conf_fmt, hsdir_v2, HS_VERSION_TWO);
-  ret = helper_config_service(conf);
-  tor_free(conf);
-  tt_int_op(ret, OP_EQ, 0);
-  /* This one should now be registered into the v2 list. */
-  tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 0);
-  tt_int_op(rend_num_services(), OP_EQ, 1);
-
   /* v3 service. */
   tor_asprintf(&conf, conf_fmt, hsdir_v3, HS_VERSION_THREE);
   ret = helper_config_service(conf);
@@ -441,7 +429,6 @@ test_load_keys(void *arg)
   tt_assert(!s->config.is_client_auth_enabled);
 
  done:
-  tor_free(hsdir_v2);
   tor_free(hsdir_v3);
   hs_free_all();
 }
@@ -634,8 +621,8 @@ test_access_service(void *arg)
 
   (void) arg;
 
-  /* We'll register two services, a v2 and a v3, then we'll load keys and
-   * validate that both are in a correct state. */
+  /* We'll register one service then we'll load keys and validate that both
+   * are in a correct state. */
 
   hs_init();
 
diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c
deleted file mode 100644
index 0ae78496b2..0000000000
--- a/src/test/test_introduce.c
+++ /dev/null
@@ -1,539 +0,0 @@
-/* Copyright (c) 2012-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "orconfig.h"
-#include "lib/crypt_ops/crypto_cipher.h"
-#include "core/or/or.h"
-#include "test/test.h"
-
-#define RENDSERVICE_PRIVATE
-#include "feature/rend/rendservice.h"
-
-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;
-  ssize_t 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();
-  tt_assert(k);
-  r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_1, -1);
-  tt_assert(!r);
-
-  /* Get digest for future comparison */
-  r = crypto_pk_get_digest(k, digest);
-  tt_assert(r >= 0);
-
-  /* Make a cell out of it */
-  r = make_intro_from_plaintext(
-      plaintext, plaintext_len,
-      k, (void **)(&cell));
-  tt_assert(r > 0);
-  tt_assert(cell);
-  cell_len = r;
-
-  /* Do early parsing */
-  parsed_req = rend_service_begin_parse_intro(cell, cell_len, 2, &err_msg);
-  tt_assert(parsed_req);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  tt_mem_op(parsed_req->pk,OP_EQ, digest, DIGEST_LEN);
-  tt_assert(parsed_req->ciphertext);
-  tt_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);
-  tt_assert(!r);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  tt_assert(parsed_req->plaintext);
-  tt_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);
-  tt_assert(!r);
-  tt_ptr_op(err_msg, OP_EQ, NULL);
-  tt_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_obsolete_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);
-  tt_assert(r >= 0);
-
-  /* Do encryption */
-  r = crypto_pk_obsolete_public_hybrid_encrypt(
-      key, cell + DIGEST_LEN, ciphertext_size,
-      buf, len,
-      PK_PKCS1_OAEP_PADDING, 0);
-  tt_assert(r >= 0);
-
-  /* Figure out cell length */
-  cell_len = DIGEST_LEN + r;
-
-  /* Output the cell */
-  *cell_out = cell;
-  cell = NULL;
-
- done:
-  tor_free(cell);
-  return cell_len;
-}
-
-/** Test v0 INTRODUCE2 parsing through decryption only
- */
-
-static void
-test_introduce_decrypt_v0(void *arg)
-{
-  (void)arg;
-  do_decrypt_test(v0_test_plaintext, sizeof(v0_test_plaintext));
-}
-
-/** Test v1 INTRODUCE2 parsing through decryption only
- */
-
-static void
-test_introduce_decrypt_v1(void *arg)
-{
-  (void)arg;
-  do_decrypt_test(v1_test_plaintext, sizeof(v1_test_plaintext));
-}
-
-/** Test v2 INTRODUCE2 parsing through decryption only
- */
-
-static void
-test_introduce_decrypt_v2(void *arg)
-{
-  (void)arg;
-  do_decrypt_test(v2_test_plaintext, sizeof(v2_test_plaintext));
-}
-
-/** Test v3 INTRODUCE2 parsing through decryption only
- */
-
-static void
-test_introduce_decrypt_v3(void *arg)
-{
-  (void)arg;
-  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 *arg)
-{
-  (void)arg;
-  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 *arg)
-{
-  (void)arg;
-  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 *arg)
-{
-  (void)arg;
-  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 *arg)
-{
-  (void)arg;
-  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 *arg)
-{
-  (void)arg;
-  do_late_parse_test(v0_test_plaintext, sizeof(v0_test_plaintext));
-}
-
-/** Test v1 INTRODUCE2 parsing
- */
-
-static void
-test_introduce_late_parse_v1(void *arg)
-{
-  (void)arg;
-  do_late_parse_test(v1_test_plaintext, sizeof(v1_test_plaintext));
-}
-
-/** Test v2 INTRODUCE2 parsing
- */
-
-static void
-test_introduce_late_parse_v2(void *arg)
-{
-  (void)arg;
-  do_late_parse_test(v2_test_plaintext, sizeof(v2_test_plaintext));
-}
-
-/** Test v3 INTRODUCE2 parsing
- */
-
-static void
-test_introduce_late_parse_v3(void *arg)
-{
-  (void)arg;
-  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, test_introduce_ ## name , 0, NULL, NULL }
-
-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
-};
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
deleted file mode 100644
index 06167635c1..0000000000
--- a/src/test/test_rendcache.c
+++ /dev/null
@@ -1,1248 +0,0 @@
-/* Copyright (c) 2010-2020, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#include "orconfig.h"
-#include "core/or/or.h"
-
-#include "test/test.h"
-#define RENDCACHE_PRIVATE
-#include "feature/rend/rendcache.h"
-#include "feature/relay/router.h"
-#include "feature/nodelist/routerlist.h"
-#include "app/config/config.h"
-#include "feature/hs/hs_common.h"
-
-#include "core/or/extend_info_st.h"
-#include "feature/rend/rend_encoded_v2_service_descriptor_st.h"
-#include "feature/rend/rend_intro_point_st.h"
-#include "feature/rend/rend_service_descriptor_st.h"
-#include "feature/nodelist/routerinfo_st.h"
-
-#include "test/rend_test_helpers.h"
-#include "test/log_test_helpers.h"
-
-static const int RECENT_TIME = -10;
-static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
-                                      REND_CACHE_MAX_SKEW + 60);
-static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60;
-
-static void
-test_rend_cache_lookup_entry(void *data)
-{
-  int ret;
-  rend_data_t *mock_rend_query = NULL;
-  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_int_op(ret, OP_EQ, 0);
-  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:
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_cache_free_all();
-  rend_data_free(mock_rend_query);
-}
-
-static void
-test_rend_cache_store_v2_desc_as_client(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;
-  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, 0);
-  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, -1); */
-
-  // Test bad base32 failure
-  // This causes an assertion failure if we're running with assertions.
-  // But when building without asserts, we can test it.
-#ifdef DISABLE_ASSERTS_IN_UNIT_TESTS
-  ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str,
-                   "!xqunszqnaolrrfmtzgaki7mxelgvkj", mock_rend_query, NULL);
-  tt_int_op(ret, OP_EQ, -1);
-#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, -1);
-
-  // 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)
-
-  rend_cache_free_all();
-
-  // Test mismatch between service ID and onion address
-  rend_cache_init();
-  strncpy(TO_REND_DATA_V2(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, -1);
-  rend_cache_free_all();
-  rend_data_free(mock_rend_query);
-
-  // Test incorrect descriptor ID
-  rend_cache_init();
-  mock_rend_query = mock_rend_data(service_id);
-  char orig = desc_id_base32[0];
-  if (desc_id_base32[0] == 'a')
-    desc_id_base32[0] = 'b';
-  else
-    desc_id_base32[0] = 'a';
-  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, -1);
-  desc_id_base32[0] = orig;
-  rend_cache_free_all();
-
-  // Test too old descriptor
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  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, -1);
-  rend_cache_free_all();
-
-  // Test too new descriptor (in the future)
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  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, -1);
-  rend_cache_free_all();
-
-  // Test when a descriptor is already in the cache
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  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, 0);
-
-  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, 0);
-  tt_assert(entry);
-  rend_cache_free_all();
-
-  // Test unsuccessful decrypting of introduction points
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
-  mock_rend_query = mock_rend_data(service_id);
-  TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH;
-  client_cookie[0] = 'A';
-  memcpy(TO_REND_DATA_V2(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, 0);
-  rend_cache_free_all();
-
-  // Test successful run when we have REND_BASIC_AUTH but not cookie
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  generate_desc(RECENT_TIME, &desc_holder, &service_id, 3);
-  mock_rend_query = mock_rend_data(service_id);
-  TO_REND_DATA_V2(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, 0);
-
-  rend_cache_free_all();
-
-  // Test when we have no introduction points
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  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, -1);
-  rend_cache_free_all();
-
-  // Test when we have too many intro points
-  rend_cache_init();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_data_free(mock_rend_query);
-
-  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, -1);
-
- done:
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_cache_free_all();
-  rend_data_free(mock_rend_query);
-}
-
-static void
-test_rend_cache_store_v2_desc_as_client_with_different_time(void *data)
-{
-  int 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);
-  rend_cache_init();
-
-  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_set(descs, 0, NULL);
-
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  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));
-  smartlist_set(descs, 0, NULL);
-  (void)data;
-
-  // 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, 0);
-
-  rend_cache_free_all();
-
-  // 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, 0);
-
- done:
-  rend_encoded_v2_service_descriptor_free(desc_holder_newer);
-  rend_encoded_v2_service_descriptor_free(desc_holder_older);
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  smartlist_free(descs);
-  rend_service_descriptor_free(generated);
-  tor_free(service_id);
-  rend_cache_free_all();
-  rend_data_free(mock_rend_query);
-}
-
-static const routerinfo_t *rcache_lookup_v2_as_dir_get_my_routerinfo(void);
-
-static routerinfo_t *mock_routerinfo;
-
-static const routerinfo_t *
-rcache_lookup_v2_as_dir_get_my_routerinfo(void)
-{
-  if (!mock_routerinfo) {
-    mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
-  }
-
-  return mock_routerinfo;
-}
-
-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;
-
-  MOCK(router_get_my_routerinfo,
-       rcache_lookup_v2_as_dir_get_my_routerinfo);
-
-  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
-  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:
-  UNMOCK(router_get_my_routerinfo);
-  tor_free(mock_routerinfo);
-  rend_cache_free_all();
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-}
-
-static const routerinfo_t *rcache_store_v2_as_dir_get_my_routerinfo(void);
-
-static const routerinfo_t *
-rcache_store_v2_as_dir_get_my_routerinfo(void)
-{
-  return mock_routerinfo;
-}
-
-static void
-test_rend_cache_store_v2_desc_as_dir(void *data)
-{
-  (void)data;
-  int ret;
-  rend_encoded_v2_service_descriptor_t *desc_holder = NULL;
-  char *service_id = NULL;
-
-  MOCK(router_get_my_routerinfo,
-       rcache_store_v2_as_dir_get_my_routerinfo);
-
-  rend_cache_init();
-
-  // Test when we can't parse the descriptor
-  mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
-  ret = rend_cache_store_v2_desc_as_dir("unparseable");
-  tt_int_op(ret, OP_EQ, -1);
-
-  // Test when we have an old descriptor
-  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, 0);
-
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-
-  // 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, 0);
-
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-
-  // 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, 0);
-
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-
-  // 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, 0);
-
- done:
-  UNMOCK(router_get_my_routerinfo);
-  rend_encoded_v2_service_descriptor_free(desc_holder);
-  tor_free(service_id);
-  rend_cache_free_all();
-  tor_free(mock_routerinfo);
-}
-
-static void
-test_rend_cache_store_v2_desc_as_dir_with_different_time(void *data)
-{
-  (void)data;
-
-  int 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;
-
-  MOCK(router_get_my_routerinfo,
-       rcache_store_v2_as_dir_get_my_routerinfo);
-
-  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_set(descs, 0, NULL);
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  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));
-  smartlist_set(descs, 0, NULL);
-
-  // Test when we have a newer descriptor stored
-  mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
-  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, 0);
-
-  // 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, 0);
-
- done:
-  UNMOCK(router_get_my_routerinfo);
-  rend_cache_free_all();
-  rend_service_descriptor_free(generated);
-  tor_free(service_id);
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  smartlist_free(descs);
-  rend_encoded_v2_service_descriptor_free(desc_holder_newer);
-  rend_encoded_v2_service_descriptor_free(desc_holder_older);
-  tor_free(mock_routerinfo);
-}
-
-static void
-test_rend_cache_store_v2_desc_as_dir_with_different_content(void *data)
-{
-  (void)data;
-
-  int 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 = NULL;
-  rend_encoded_v2_service_descriptor_t *desc_holder_two = NULL;
-
-  MOCK(router_get_my_routerinfo,
-       rcache_store_v2_as_dir_get_my_routerinfo);
-
-  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_set(descs, 0, NULL);
-
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  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));
-  smartlist_set(descs, 0, NULL);
-
-  // Test when we have another descriptor stored, with a different descriptor
-  mock_routerinfo = tor_malloc(sizeof(routerinfo_t));
-  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, 0);
-
- done:
-  UNMOCK(router_get_my_routerinfo);
-  rend_cache_free_all();
-  rend_service_descriptor_free(generated);
-  tor_free(service_id);
-  SMARTLIST_FOREACH(descs, rend_encoded_v2_service_descriptor_t *, d,
-                    rend_encoded_v2_service_descriptor_free(d));
-  smartlist_free(descs);
-  rend_encoded_v2_service_descriptor_free(desc_holder_one);
-  rend_encoded_v2_service_descriptor_free(desc_holder_two);
-}
-
-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:
-  rend_cache_free_all();
-}
-
-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;
-  setup_full_capture_of_logs(LOG_WARN);
-  rend_cache_decrement_allocation(2);
-  tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
-  expect_single_log_msg_containing(
-                    "Underflow in rend_cache_decrement_allocation");
-  teardown_capture_of_logs();
-
-  // And again
-  rend_cache_decrement_allocation(2);
-  tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
-
- done:
-  teardown_capture_of_logs();
-}
-
-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;
-  setup_full_capture_of_logs(LOG_WARN);
-  rend_cache_increment_allocation(2);
-  tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
-  expect_single_log_msg_containing(
-                    "Overflow in rend_cache_increment_allocation");
-  teardown_capture_of_logs();
-
-  // And again
-  rend_cache_increment_allocation(2);
-  tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
-
- done:
-  teardown_capture_of_logs();
-}
-
-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;
-  const char key_ip_one[DIGEST_LEN] = "ip1";
-  const char key_ip_two[DIGEST_LEN] = "ip2";
-  const char key_foo[DIGEST_LEN] = "foo1";
-
-  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, key_ip_one, ip);
-  strmap_set_lc(rend_cache_failure, "foo1", failure);
-
-  // Test not found
-  ret = cache_failure_intro_lookup((const uint8_t *) key_foo, "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 *) key_ip_two, "foo1", NULL);
-  tt_int_op(ret, OP_EQ, 0);
-
-  // Test found
-  ret = cache_failure_intro_lookup((const uint8_t *) key_ip_one, "foo1", NULL);
-  tt_int_op(ret, OP_EQ, 1);
-
-  // Test found and asking for entry
-  cache_failure_intro_lookup((const uint8_t *) key_ip_one, "foo1", &entry);
-  tt_assert(entry);
-  tt_assert(entry == ip);
-
- done:
-  rend_cache_free_all();
-}
-
-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), REND_CACHE_TYPE_CLIENT);
-  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);
-  rend_cache_increment_allocation(rend_cache_entry_allocation(one));
-  strmap_set_lc(rend_cache, "foo2", two);
-  rend_cache_increment_allocation(rend_cache_entry_allocation(two));
-
-  rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
-  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);
-
-  rend_cache_increment_allocation(rend_cache_entry_allocation(one));
-  strmap_set_lc(rend_cache, "foo1", one);
-  rend_cache_increment_allocation(rend_cache_entry_allocation(two));
-  strmap_set_lc(rend_cache, "foo2", two);
-
-  rend_cache_clean(time(NULL), REND_CACHE_TYPE_CLIENT);
-  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:
-  rend_cache_free_all();
-}
-
-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:
-  rend_cache_failure_entry_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;
-
-  const char key_one[DIGEST_LEN] = "ip1";
-  const char key_two[DIGEST_LEN] = "ip2";
-
-  (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, key_one, 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, key_one, 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, key_one, 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, key_two, 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:
-  rend_cache_free_all();
-}
-
-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
-  rend_cache_free_all();
-
-  rend_service_descriptor_free(desc);
- /* 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);
-  rend_cache_increment_allocation(rend_cache_entry_allocation(one));
-  strmap_set_lc(rend_cache, "foo1", one);
-
-  rend_cache_free_all();
-
-  tt_ptr_op(rend_cache, OP_EQ, NULL);
-  tt_ptr_op(rend_cache_v2_dir, OP_EQ, NULL);
-  tt_ptr_op(rend_cache_failure, OP_EQ, NULL);
-  tt_assert(!rend_cache_total_allocation);
-
- done:
-  rend_cache_free_all();
-}
-
-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_increment_allocation(rend_cache_entry_allocation(e));
-  rend_cache_entry_free(e);
-
-  // Handles non-NULL descriptor correctly
-  e = tor_malloc_zero(sizeof(rend_cache_entry_t));
-  e->desc = tor_malloc(10);
-  rend_cache_increment_allocation(rend_cache_entry_allocation(e));
-  rend_cache_entry_free(e);
-
- /* done: */
- /*  (void)0; */
-}
-
-static void
-test_rend_cache_purge(void *data)
-{
-  (void)data;
-
-  // Deals with a NULL rend_cache
-  rend_cache_purge();
-  tt_assert(rend_cache);
-  tt_assert(strmap_size(rend_cache) == 0);
-
-  // Deals with existing rend_cache
-  rend_cache_free_all();
-  rend_cache_init();
-  tt_assert(rend_cache);
-  tt_assert(strmap_size(rend_cache) == 0);
-
-  rend_cache_purge();
-  tt_assert(rend_cache);
-  tt_assert(strmap_size(rend_cache) == 0);
-
- done:
-  rend_cache_free_all();
-}
-
-static void
-test_rend_cache_failure_intro_add(void *data)
-{
-  (void)data;
-  rend_cache_failure_t *fail_entry;
-  rend_cache_failure_intro_t *entry;
-  const char identity[DIGEST_LEN] = "foo1";
-
-  rend_cache_init();
-
-  // Adds non-existing entry
-  cache_failure_intro_add((const uint8_t *) identity, "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, identity);
-  tt_assert(entry);
-
-  // Adds existing entry
-  cache_failure_intro_add((const uint8_t *) identity, "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, identity);
-  tt_assert(entry);
-
- done:
-  rend_cache_free_all();
-}
-
-static void
-test_rend_cache_intro_failure_note(void *data)
-{
-  (void)data;
-  rend_cache_failure_t *fail_entry;
-  rend_cache_failure_intro_t *entry;
-  const char key[DIGEST_LEN] = "foo1";
-
-  rend_cache_init();
-
-  // Test not found
-  rend_cache_intro_failure_note(INTRO_POINT_FAILURE_TIMEOUT,
-                                (const uint8_t *) key, "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, key);
-  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 *) key, "foo2");
-  tt_int_op(entry->failure_type, OP_EQ, INTRO_POINT_FAILURE_UNREACHABLE);
-
- done:
-  rend_cache_free_all();
-}
-
-static void
-test_rend_cache_clean_v2_descs_as_dir(void *data)
-{
-  rend_cache_entry_t *e;
-  time_t now, cutoff;
-  rend_service_descriptor_t *desc;
-  now = time(NULL);
-  cutoff = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW);
-  const char key[DIGEST_LEN] = "abcde";
-
-  (void)data;
-
-  rend_cache_init();
-
-  // Test running with an empty cache
-  rend_cache_clean_v2_descs_as_dir(cutoff);
-  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;
-  rend_cache_increment_allocation(rend_cache_entry_allocation(e));
-  digestmap_set(rend_cache_v2_dir, key, e);
-
-  /* Set the cutoff to minus 10 seconds. */
-  rend_cache_clean_v2_descs_as_dir(cutoff - 10);
-  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1);
-
-  // Test with one old entry
-  desc->timestamp = cutoff - 1000;
-  rend_cache_clean_v2_descs_as_dir(cutoff);
-  tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0);
-
- done:
-  rend_cache_free_all();
-}
-
-static void
-test_rend_cache_entry_allocation(void *data)
-{
-  (void)data;
-
-  size_t ret;
-  rend_cache_entry_t *e = NULL;
-
-  // 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_GT, sizeof(rend_cache_entry_t));
-
- done:
-  tor_free(e);
-}
-
-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
-  strmap_free(rend_cache_failure, rend_cache_failure_entry_free_void);
-  rend_cache_failure = NULL;
-
-  rend_cache_failure_purge();
-
-  tt_ptr_op(rend_cache_failure, OP_NE, NULL);
-  tt_int_op(strmap_size(rend_cache_failure), OP_EQ, 0);
-
- done:
-  rend_cache_free_all();
-}
-
-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 char *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 = 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, 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_free_all();
-  rend_service_descriptor_free(desc);
-  tor_free(service_id);
-}
-
-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