diff --git a/src/or/or.h b/src/or/or.h
index dee1881034..d9be1be37d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -793,6 +793,11 @@ typedef struct rend_data_t {
/** 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];
@@ -800,8 +805,9 @@ typedef struct rend_data_t {
rend_auth_type_t auth_type;
/** Descriptor ID for a client request. The control port command HSFETCH
- * can use this. */
- char descriptor_id[DIGEST_LEN];
+ * 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];
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 13781c212e..8a8d8f7243 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -860,8 +860,8 @@ rend_client_fetch_v2_desc(const rend_data_t *query,
if (query->onion_address[0] != '\0') {
ret = fetch_v2_desc_by_addr(query, hsdirs);
- } else if (query->descriptor_id[0] != '\0') {
- ret = fetch_v2_desc_by_descid(query->descriptor_id, query, hsdirs);
+ } else if (!tor_digest_is_zero(query->desc_id_fetch)) {
+ ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs);
} else {
/* Query data is invalid. */
ret = -1;
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 03dd757921..1b042f9fe9 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -1404,3 +1404,100 @@ rend_data_dup(const rend_data_t *data)
return tor_memdup(data, sizeof(rend_data_t));
}
+/** Compute descriptor ID for each replicas and save them. A valid onion
+ * address must be present in the rend_data.
+ *
+ * Return 0 on success else -1. */
+static int
+compute_desc_id(rend_data_t *rend_data)
+{
+ int ret, replica;
+ time_t now = time(NULL);
+
+ tor_assert(rend_data);
+
+ /* Compute descriptor ID for each replicas. */
+ for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id);
+ replica++) {
+ ret = rend_compute_v2_desc_id(rend_data->descriptor_id[replica],
+ rend_data->onion_address,
+ rend_data->descriptor_cookie,
+ now, replica);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+/** Allocate and initialize a rend_data_t object for a service using the
+ * given arguments. Only the onion_address is not optional.
+ *
+ * Return a valid rend_data_t pointer. */
+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)
+{
+ rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
+
+ /* We need at least one else the call is wrong. */
+ tor_assert(onion_address != NULL);
+
+ if (pk_digest) {
+ memcpy(rend_data->rend_pk_digest, pk_digest,
+ sizeof(rend_data->rend_pk_digest));
+ }
+ if (cookie) {
+ memcpy(rend_data->rend_cookie, cookie,
+ sizeof(rend_data->rend_cookie));
+ }
+
+ strlcpy(rend_data->onion_address, onion_address,
+ sizeof(rend_data->onion_address));
+ rend_data->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 only the onion address will be used to make
+ * the descriptor fetch.
+ *
+ * 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)
+{
+ rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
+
+ /* We need at least one else the call is wrong. */
+ tor_assert(onion_address != NULL || desc_id != NULL);
+
+ if (cookie) {
+ memcpy(rend_data->descriptor_cookie, cookie,
+ sizeof(rend_data->descriptor_cookie));
+ }
+ if (desc_id) {
+ memcpy(rend_data->desc_id_fetch, desc_id,
+ sizeof(rend_data->desc_id_fetch));
+ }
+ if (onion_address) {
+ strlcpy(rend_data->onion_address, onion_address,
+ sizeof(rend_data->onion_address));
+ if (compute_desc_id(rend_data) < 0) {
+ goto error;
+ }
+ }
+
+ rend_data->auth_type = auth_type;
+
+ return rend_data;
+
+error:
+ rend_data_free(rend_data);
+ return NULL;
+}
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index b86cdb7fa6..a4d55d21d0 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -67,5 +67,13 @@ void rend_get_descriptor_id_bytes(char *descriptor_id_out,
const char *secret_id_part);
size_t rend_cache_get_total_allocation(void);
+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);
#endif