|
@@ -26,6 +26,10 @@
|
|
|
|
|
|
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 int intro_point_should_expire_now(rend_intro_point_t *intro,
|
|
|
+ time_t now);
|
|
|
|
|
|
/** Represents the mapping from a virtual port of a rendezvous service to
|
|
|
* a real port on some IP.
|
|
@@ -36,8 +40,10 @@ typedef struct rend_service_port_config_t {
|
|
|
tor_addr_t real_addr;
|
|
|
} rend_service_port_config_t;
|
|
|
|
|
|
-/** Try to maintain this many intro points per service if possible. */
|
|
|
-#define NUM_INTRO_POINTS 3
|
|
|
+/** Try to maintain this many intro points per service by default. */
|
|
|
+#define NUM_INTRO_POINTS_DEFAULT 3
|
|
|
+/** Maintain no more than this many intro points per hidden service. */
|
|
|
+#define NUM_INTRO_POINTS_MAX 10
|
|
|
|
|
|
/** If we can't build our intro circuits, don't retry for this long. */
|
|
|
#define INTRO_CIRC_RETRY_PERIOD (60*5)
|
|
@@ -51,6 +57,10 @@ typedef struct rend_service_port_config_t {
|
|
|
* rendezvous point before giving up? */
|
|
|
#define MAX_REND_TIMEOUT 30
|
|
|
|
|
|
+/** How many seconds should we wait for new HS descriptors to reach
|
|
|
+ * our clients before we close an expiring intro point? */
|
|
|
+#define INTRO_POINT_EXPIRATION_GRACE_PERIOD 5*60
|
|
|
+
|
|
|
/** Represents a single hidden service running at this OP. */
|
|
|
typedef struct rend_service_t {
|
|
|
/* Fields specified in config file */
|
|
@@ -72,17 +82,24 @@ typedef struct rend_service_t {
|
|
|
* 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. */
|
|
|
- /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t of when
|
|
|
- * they were received; used to prevent replays. */
|
|
|
- digestmap_t *accepted_intros;
|
|
|
- /** Time at which we last removed expired values from accepted_intros. */
|
|
|
- time_t last_cleaned_accepted_intros;
|
|
|
+ /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t
|
|
|
+ * of when they were received. Clients may send INTRODUCE1 cells
|
|
|
+ * for the same rendezvous point through two or more different
|
|
|
+ * introduction points; when they do, this digestmap keeps us from
|
|
|
+ * launching multiple simultaneous attempts to connect to the same
|
|
|
+ * rend point. */
|
|
|
+ digestmap_t *accepted_intro_dh_parts;
|
|
|
+ /** Time at which we last removed expired values from
|
|
|
+ * accepted_intro_dh_parts. */
|
|
|
+ time_t last_cleaned_accepted_intro_dh_parts;
|
|
|
} rend_service_t;
|
|
|
|
|
|
/** A list of rend_service_t's for services run on this OP.
|
|
@@ -142,7 +159,7 @@ rend_service_free(rend_service_t *service)
|
|
|
rend_authorized_client_free(c););
|
|
|
smartlist_free(service->clients);
|
|
|
}
|
|
|
- digestmap_free(service->accepted_intros, _tor_free);
|
|
|
+ digestmap_free(service->accepted_intro_dh_parts, _tor_free);
|
|
|
tor_free(service);
|
|
|
}
|
|
|
|
|
@@ -319,6 +336,7 @@ rend_config_services(const or_options_t *options, int validate_only)
|
|
|
service->directory = tor_strdup(line->value);
|
|
|
service->ports = smartlist_create();
|
|
|
service->intro_period_started = time(NULL);
|
|
|
+ service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
|
|
|
continue;
|
|
|
}
|
|
|
if (!service) {
|
|
@@ -542,16 +560,38 @@ rend_service_update_descriptor(rend_service_t *service)
|
|
|
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;
|
|
|
+
|
|
|
+ if (intro_svc->time_expiring != -1) {
|
|
|
+ /* This intro point is expiring. Don't list it. */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
circ = find_intro_circuit(intro_svc, service->pk_digest);
|
|
|
- if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO)
|
|
|
+ if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
|
|
|
+ /* This intro point's circuit isn't finished yet. Don't list it. */
|
|
|
continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ...unless this intro point is listed in the descriptor. */
|
|
|
+ intro_svc->listed_in_last_desc = 1;
|
|
|
|
|
|
- /* We have an entirely established intro circuit. */
|
|
|
+ /* 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);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -857,15 +897,16 @@ rend_check_authorization(rend_service_t *service,
|
|
|
/** Remove elements from <b>service</b>'s replay cache that are old enough to
|
|
|
* be noticed by timestamp checking. */
|
|
|
static void
|
|
|
-clean_accepted_intros(rend_service_t *service, time_t now)
|
|
|
+clean_accepted_intro_dh_parts(rend_service_t *service, time_t now)
|
|
|
{
|
|
|
const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL;
|
|
|
|
|
|
- service->last_cleaned_accepted_intros = now;
|
|
|
- if (!service->accepted_intros)
|
|
|
+ service->last_cleaned_accepted_intro_dh_parts = now;
|
|
|
+ if (!service->accepted_intro_dh_parts)
|
|
|
return;
|
|
|
|
|
|
- DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) {
|
|
|
+ DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest,
|
|
|
+ time_t *, t) {
|
|
|
if (*t < cutoff) {
|
|
|
tor_free(t);
|
|
|
MAP_DEL_CURRENT(digest);
|
|
@@ -890,6 +931,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
|
|
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
|
|
|
rend_service_t *service;
|
|
|
+ rend_intro_point_t *intro_point;
|
|
|
int r, i, v3_shift = 0;
|
|
|
size_t len, keylen;
|
|
|
crypto_dh_env_t *dh = NULL;
|
|
@@ -937,7 +979,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
service = rend_service_get_by_pk_digest(
|
|
|
circuit->rend_data->rend_pk_digest);
|
|
|
if (!service) {
|
|
|
- log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
|
|
|
+ log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro "
|
|
|
+ "circ for an unrecognized service %s.",
|
|
|
escaped(serviceid));
|
|
|
return -1;
|
|
|
}
|
|
@@ -962,17 +1005,26 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- if (!service->accepted_intros)
|
|
|
- service->accepted_intros = digestmap_new();
|
|
|
+ intro_point = find_intro_point(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));
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!service->accepted_intro_dh_parts)
|
|
|
+ service->accepted_intro_dh_parts = digestmap_new();
|
|
|
+
|
|
|
+ if (!intro_point->accepted_intro_rsa_parts)
|
|
|
+ intro_point->accepted_intro_rsa_parts = digestmap_new();
|
|
|
|
|
|
{
|
|
|
char pkpart_digest[DIGEST_LEN];
|
|
|
- /* Check for replay of PK-encrypted portion. It is slightly naughty to
|
|
|
- use the same digestmap to check for this and for g^x replays, but
|
|
|
- collisions are tremendously unlikely.
|
|
|
- */
|
|
|
+ /* Check for replay of PK-encrypted portion. */
|
|
|
crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen);
|
|
|
- access_time = digestmap_get(service->accepted_intros, pkpart_digest);
|
|
|
+ access_time = digestmap_get(intro_point->accepted_intro_rsa_parts,
|
|
|
+ pkpart_digest);
|
|
|
if (access_time != NULL) {
|
|
|
log_warn(LD_REND, "Possible replay detected! We received an "
|
|
|
"INTRODUCE2 cell with same PK-encrypted part %d seconds ago. "
|
|
@@ -981,7 +1033,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
}
|
|
|
access_time = tor_malloc(sizeof(time_t));
|
|
|
*access_time = now;
|
|
|
- digestmap_set(service->accepted_intros, pkpart_digest, access_time);
|
|
|
+ digestmap_set(intro_point->accepted_intro_rsa_parts,
|
|
|
+ pkpart_digest, access_time);
|
|
|
}
|
|
|
|
|
|
/* Next N bytes is encrypted with service key */
|
|
@@ -997,7 +1050,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
len = r;
|
|
|
if (*buf == 3) {
|
|
|
/* Version 3 INTRODUCE2 cell. */
|
|
|
- time_t ts = 0;
|
|
|
v3_shift = 1;
|
|
|
auth_type = buf[1];
|
|
|
switch (auth_type) {
|
|
@@ -1019,17 +1071,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
log_info(LD_REND, "Unknown authorization type '%d'", auth_type);
|
|
|
}
|
|
|
|
|
|
- /* Check timestamp. */
|
|
|
- ts = ntohl(get_uint32(buf+1+v3_shift));
|
|
|
+ /* Skip the timestamp field. We no longer use it. */
|
|
|
v3_shift += 4;
|
|
|
- if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 ||
|
|
|
- (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) {
|
|
|
- /* This is far more likely to mean that a client's clock is
|
|
|
- * skewed than that a replay attack is in progress. */
|
|
|
- log_info(LD_REND, "INTRODUCE2 cell is too %s. Discarding.",
|
|
|
- (now - ts) < 0 ? "old" : "new");
|
|
|
- return -1;
|
|
|
- }
|
|
|
}
|
|
|
if (*buf == 2 || *buf == 3) {
|
|
|
/* Version 2 INTRODUCE2 cell. */
|
|
@@ -1128,7 +1171,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
|
|
|
/* Check whether there is a past request with the same Diffie-Hellman,
|
|
|
* part 1. */
|
|
|
- access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash);
|
|
|
+ access_time = digestmap_get(service->accepted_intro_dh_parts,
|
|
|
+ diffie_hellman_hash);
|
|
|
if (access_time != NULL) {
|
|
|
/* 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
|
|
@@ -1150,9 +1194,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
|
|
|
* one hour. */
|
|
|
access_time = tor_malloc(sizeof(time_t));
|
|
|
*access_time = now;
|
|
|
- digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time);
|
|
|
- if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now)
|
|
|
- clean_accepted_intros(service, now);
|
|
|
+ digestmap_set(service->accepted_intro_dh_parts,
|
|
|
+ diffie_hellman_hash, access_time);
|
|
|
+ if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL
|
|
|
+ < now)
|
|
|
+ clean_accepted_intro_dh_parts(service, now);
|
|
|
|
|
|
/* If the service performs client authorization, check included auth data. */
|
|
|
if (service->clients) {
|
|
@@ -1412,7 +1458,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
|
|
|
|
|
|
/* If we already have enough introduction circuits for this service,
|
|
|
* redefine this one as a general circuit or close it, depending. */
|
|
|
- if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) {
|
|
|
+ if (count_established_intro_points(serviceid) >
|
|
|
+ (int)service->n_intro_points_wanted) { /* XXX023 remove cast */
|
|
|
const or_options_t *options = get_options();
|
|
|
if (options->ExcludeNodes) {
|
|
|
/* XXXX in some future version, we can test whether the transition is
|
|
@@ -1652,6 +1699,35 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest)
|
|
|
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 = circ->rend_data->onion_address;
|
|
|
+
|
|
|
+ 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_cmp_keys(intro_point->intro_key, circ->intro_key) == 0) {
|
|
|
+ return intro_point;
|
|
|
+ });
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
/** Determine the responsible hidden service directories for the
|
|
|
* rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
|
|
|
* <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
|
|
@@ -1855,6 +1931,52 @@ upload_service_descriptor(rend_service_t *service)
|
|
|
service->desc_is_dirty = 0;
|
|
|
}
|
|
|
|
|
|
+/** 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->time_expiring != -1) {
|
|
|
+ /* We've already started expiring this intro point. *Don't* let
|
|
|
+ * this function's result 'flap'. */
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (digestmap_size(intro->accepted_intro_rsa_parts) >=
|
|
|
+ INTRO_POINT_LIFETIME_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 =
|
|
|
+ INTRO_POINT_LIFETIME_MIN_SECONDS +
|
|
|
+ crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS -
|
|
|
+ INTRO_POINT_LIFETIME_MIN_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);
|
|
|
+}
|
|
|
+
|
|
|
/** For every service, check how many intro points it currently has, and:
|
|
|
* - Pick new intro points as necessary.
|
|
|
* - Launch circuits to any new intro points.
|
|
@@ -1866,7 +1988,9 @@ rend_services_introduce(void)
|
|
|
const node_t *node;
|
|
|
rend_service_t *service;
|
|
|
rend_intro_point_t *intro;
|
|
|
- int changed, prev_intro_nodes;
|
|
|
+ int intro_point_set_changed, prev_intro_nodes;
|
|
|
+ unsigned int n_intro_points_unexpired;
|
|
|
+ unsigned int n_intro_points_to_open;
|
|
|
smartlist_t *intro_nodes;
|
|
|
time_t now;
|
|
|
const or_options_t *options = get_options();
|
|
@@ -1879,7 +2003,16 @@ rend_services_introduce(void)
|
|
|
service = smartlist_get(rend_service_list, i);
|
|
|
|
|
|
tor_assert(service);
|
|
|
- changed = 0;
|
|
|
+
|
|
|
+ /* intro_point_set_changed becomes non-zero iff the set of intro
|
|
|
+ * points to be published in service's descriptor has changed. */
|
|
|
+ intro_point_set_changed = 0;
|
|
|
+
|
|
|
+ /* n_intro_points_unexpired collects the number of non-expiring
|
|
|
+ * intro points we have, so that we know how many new intro
|
|
|
+ * circuits we need to launch for this service. */
|
|
|
+ n_intro_points_unexpired = 0;
|
|
|
+
|
|
|
if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) {
|
|
|
/* One period has elapsed; we can try building circuits again. */
|
|
|
service->intro_period_started = now;
|
|
@@ -1893,59 +2026,115 @@ rend_services_introduce(void)
|
|
|
|
|
|
/* Find out which introduction points we have in progress for this
|
|
|
service. */
|
|
|
- for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
|
|
|
- intro = smartlist_get(service->intro_nodes, j);
|
|
|
+ SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *, intro){
|
|
|
+ origin_circuit_t *intro_circ =
|
|
|
+ find_intro_circuit(intro, service->pk_digest);
|
|
|
+
|
|
|
+ if (intro->time_expiring + INTRO_POINT_EXPIRATION_GRACE_PERIOD > now) {
|
|
|
+ /* This intro point has completely expired. Remove it, and
|
|
|
+ * mark the circuit for close if it's still alive. */
|
|
|
+ if (intro_circ != NULL) {
|
|
|
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ),
|
|
|
+ END_CIRC_REASON_FINISHED);
|
|
|
+ }
|
|
|
+ rend_intro_point_free(intro);
|
|
|
+ intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */
|
|
|
+ SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
|
|
|
+ /* We don't need to set intro_point_set_changed here, because
|
|
|
+ * this intro point wouldn't have been published in a current
|
|
|
+ * descriptor anyway. */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
node = node_get_by_id(intro->extend_info->identity_digest);
|
|
|
- if (!node || !find_intro_circuit(intro, service->pk_digest)) {
|
|
|
- log_info(LD_REND,"Giving up on %s as intro point for %s.",
|
|
|
+ if (!node || !intro_circ) {
|
|
|
+ int removing_this_intro_point_changes_the_intro_point_set = 1;
|
|
|
+ log_info(LD_REND, "Giving up on %s as intro point for %s"
|
|
|
+ " (circuit disappeared).",
|
|
|
safe_str_client(extend_info_describe(intro->extend_info)),
|
|
|
safe_str_client(service->service_id));
|
|
|
- if (service->desc) {
|
|
|
- SMARTLIST_FOREACH(service->desc->intro_nodes, rend_intro_point_t *,
|
|
|
- dintro, {
|
|
|
- if (tor_memeq(dintro->extend_info->identity_digest,
|
|
|
- intro->extend_info->identity_digest, DIGEST_LEN)) {
|
|
|
- log_info(LD_REND, "The intro point we are giving up on was "
|
|
|
- "included in the last published descriptor. "
|
|
|
- "Marking current descriptor as dirty.");
|
|
|
- service->desc_is_dirty = now;
|
|
|
- }
|
|
|
- });
|
|
|
+ if (intro->time_expiring != -1) {
|
|
|
+ log_info(LD_REND, "We were already expiring the intro point; "
|
|
|
+ "no need to mark the HS descriptor as dirty over this.");
|
|
|
+ removing_this_intro_point_changes_the_intro_point_set = 0;
|
|
|
+ } else if (intro->listed_in_last_desc) {
|
|
|
+ log_info(LD_REND, "The intro point we are giving up on was "
|
|
|
+ "included in the last published descriptor. "
|
|
|
+ "Marking current descriptor as dirty.");
|
|
|
+ service->desc_is_dirty = now;
|
|
|
}
|
|
|
rend_intro_point_free(intro);
|
|
|
- smartlist_del(service->intro_nodes,j--);
|
|
|
- changed = 1;
|
|
|
+ intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */
|
|
|
+ SMARTLIST_DEL_CURRENT(service->intro_nodes, intro);
|
|
|
+ if (removing_this_intro_point_changes_the_intro_point_set)
|
|
|
+ intro_point_set_changed = 1;
|
|
|
}
|
|
|
+
|
|
|
+ if (intro != NULL && 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));
|
|
|
+
|
|
|
+ /* The polite (and generally Right) way to expire an intro
|
|
|
+ * point is to establish a new one to replace it, publish a
|
|
|
+ * new descriptor that doesn't list any expiring intro points,
|
|
|
+ * and *then*, once our upload attempts for the new descriptor
|
|
|
+ * have ended (whether in success or failure), close the
|
|
|
+ * expiring intro points.
|
|
|
+ *
|
|
|
+ * Unfortunately, we can't find out when the new descriptor
|
|
|
+ * has actually been uploaded, so we'll have to settle for a
|
|
|
+ * five-minute timer. Start it. XXX023 This sucks. */
|
|
|
+ intro->time_expiring = now;
|
|
|
+
|
|
|
+ intro_point_set_changed = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (intro != NULL && intro->time_expiring == -1)
|
|
|
+ ++n_intro_points_unexpired;
|
|
|
+
|
|
|
if (node)
|
|
|
smartlist_add(intro_nodes, (void*)node);
|
|
|
- }
|
|
|
-
|
|
|
- /* We have enough intro points, and the intro points we thought we had were
|
|
|
- * all connected.
|
|
|
- */
|
|
|
- if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) {
|
|
|
- /* We have all our intro points! Start a fresh period and reset the
|
|
|
- * circuit count. */
|
|
|
+ } SMARTLIST_FOREACH_END(intro);
|
|
|
+
|
|
|
+ if (!intro_point_set_changed &&
|
|
|
+ (n_intro_points_unexpired >= service->n_intro_points_wanted)) {
|
|
|
+ /* We have enough intro circuits in progress, and none of our
|
|
|
+ * intro circuits have died since the last call to
|
|
|
+ * rend_services_introduce! Start a fresh period and reset the
|
|
|
+ * circuit count.
|
|
|
+ *
|
|
|
+ * XXXX WTF? */
|
|
|
service->intro_period_started = now;
|
|
|
service->n_intro_circuits_launched = 0;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- /* Remember how many introduction circuits we started with. */
|
|
|
+ /* Remember how many introduction circuits we started with.
|
|
|
+ *
|
|
|
+ * prev_intro_nodes serves a different purpose than
|
|
|
+ * n_intro_points_unexpired -- this variable tells us where our
|
|
|
+ * previously-created intro points end and our new ones begin in
|
|
|
+ * the intro-point list, so we don't have to launch the circuits
|
|
|
+ * at the same time as we create the intro points they correspond
|
|
|
+ * to. XXXX This is daft. */
|
|
|
prev_intro_nodes = smartlist_len(service->intro_nodes);
|
|
|
+
|
|
|
/* We have enough directory information to start establishing our
|
|
|
- * intro points. We want to end up with three intro points, but if
|
|
|
- * we're just starting, we launch five and pick the first three that
|
|
|
- * complete.
|
|
|
+ * intro points. We want to end up with n_intro_points_wanted
|
|
|
+ * intro points, but if we're just starting, we launch two extra
|
|
|
+ * circuits and use the first n_intro_points_wanted that complete.
|
|
|
*
|
|
|
* The ones after the first three 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 next time we
|
|
|
* go through the above "find out which introduction points we have
|
|
|
* in progress" loop. */
|
|
|
-#define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2)
|
|
|
- for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ?
|
|
|
- NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) {
|
|
|
+ n_intro_points_to_open = (service->n_intro_points_wanted +
|
|
|
+ (prev_intro_nodes == 0 ? 2 : 0));
|
|
|
+ for (j = (int)n_intro_points_unexpired;
|
|
|
+ j < (int)n_intro_points_to_open;
|
|
|
+ ++j) { /* XXXX remove casts */
|
|
|
router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
|
|
|
if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
|
|
|
flags |= CRN_ALLOW_INVALID;
|
|
@@ -1953,16 +2142,21 @@ rend_services_introduce(void)
|
|
|
options->ExcludeNodes, flags);
|
|
|
if (!node) {
|
|
|
log_warn(LD_REND,
|
|
|
- "Could only establish %d introduction points for %s.",
|
|
|
- smartlist_len(service->intro_nodes), service->service_id);
|
|
|
+ "Could only establish %d introduction points for %s; "
|
|
|
+ "wanted %u.",
|
|
|
+ smartlist_len(service->intro_nodes), service->service_id,
|
|
|
+ n_intro_points_to_open);
|
|
|
break;
|
|
|
}
|
|
|
- changed = 1;
|
|
|
+ intro_point_set_changed = 1;
|
|
|
smartlist_add(intro_nodes, (void*)node);
|
|
|
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
|
|
|
intro->extend_info = extend_info_from_node(node);
|
|
|
intro->intro_key = crypto_new_pk_env();
|
|
|
tor_assert(!crypto_pk_generate_key(intro->intro_key));
|
|
|
+ intro->time_published = -1;
|
|
|
+ intro->time_to_expire = -1;
|
|
|
+ intro->time_expiring = -1;
|
|
|
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)),
|
|
@@ -1970,7 +2164,7 @@ rend_services_introduce(void)
|
|
|
}
|
|
|
|
|
|
/* If there's no need to launch new circuits, stop here. */
|
|
|
- if (!changed)
|
|
|
+ if (!intro_point_set_changed)
|
|
|
continue;
|
|
|
|
|
|
/* Establish new introduction points. */
|