summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/rendservice.c122
-rw-r--r--src/or/rendservice.h12
2 files changed, 92 insertions, 42 deletions
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 9d4dc5bbdc..3c4b6775c0 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -106,9 +106,12 @@ 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.
- */
+/** 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;
/* Like rend_get_service_list_mutable, but returns a read-only list. */
static const smartlist_t*
@@ -520,18 +523,34 @@ rend_service_check_dir_and_add(smartlist_t *service_list,
return rend_add_service(s_list, service);
}
-/* If this is a reload and there were hidden services configured before,
- * keep the introduction points that are still needed and close the
- * other ones. */
+/* 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
-prune_services_on_reload(smartlist_t *old_service_list,
- smartlist_t *new_service_list)
+rend_service_prune_list_impl_(void)
{
origin_circuit_t *ocirc = NULL;
- smartlist_t *surviving_services = 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);
- tor_assert(old_service_list);
- tor_assert(new_service_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
@@ -609,6 +628,27 @@ prune_services_on_reload(smartlist_t *old_service_list,
smartlist_free(surviving_services);
}
+/* 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;
+ /* Don't try to prune anything if we have no staging list. */
+ if (!rend_service_staging_list) {
+ return;
+ }
+ 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);
+ }
+}
+
/** Set up rend_service_list, based on the values of HiddenServiceDir and
* HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
* failure. (If <b>validate_only</b> is set, parse, warn and return as
@@ -620,22 +660,22 @@ rend_config_services(const or_options_t *options, int validate_only)
config_line_t *line;
rend_service_t *service = NULL;
rend_service_port_config_t *portcfg;
- smartlist_t *old_service_list = NULL;
- smartlist_t *temp_service_list = NULL;
int ok = 0;
int rv = -1;
- /* Use a temporary service list, so that we can check the new services'
- * consistency with each other */
- temp_service_list = smartlist_new();
+ /* 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();
+ }
for (line = options->RendConfigLines; line; line = line->next) {
if (!strcasecmp(line->key, "HiddenServiceDir")) {
/* register the service we just finished parsing
* this code registers every service except the last one parsed,
* which is registered below the loop */
- if (rend_service_check_dir_and_add(temp_service_list, options, service,
- validate_only) < 0) {
+ if (rend_service_check_dir_and_add(rend_service_staging_list, options,
+ service, validate_only) < 0) {
service = NULL;
goto free_and_return;
}
@@ -835,8 +875,8 @@ rend_config_services(const or_options_t *options, int validate_only)
/* register the final service after we have finished parsing all services
* this code only registers the last service, other services are registered
* within the loop. It is ok for this service to be NULL, it is ignored. */
- if (rend_service_check_dir_and_add(temp_service_list, options, service,
- validate_only) < 0) {
+ if (rend_service_check_dir_and_add(rend_service_staging_list, options,
+ service, validate_only) < 0) {
service = NULL;
goto free_and_return;
}
@@ -848,31 +888,19 @@ rend_config_services(const or_options_t *options, int validate_only)
goto free_and_return;
}
- /* Otherwise, use the newly added services as the new service list
- * Since we have now replaced the global service list, from this point on we
- * must succeed, or die trying. */
- old_service_list = rend_service_list;
- rend_service_list = temp_service_list;
- temp_service_list = NULL;
-
- /* If this is a reload and there were hidden services configured before,
- * keep the introduction points that are still needed and close the
- * other ones. */
- if (old_service_list && !validate_only) {
- prune_services_on_reload(old_service_list, rend_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);
- }
+ /* This could be a reload of configuration so try to prune the main list
+ * using the staging one. And we know we are not in validate mode here.
+ * After this, the main and staging list will point to the right place and
+ * be in a quiescent usable state. */
+ rend_service_prune_list();
return 0;
free_and_return:
rend_service_free(service);
- SMARTLIST_FOREACH(temp_service_list, rend_service_t *, ptr,
+ SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t *, ptr,
rend_service_free(ptr));
- smartlist_free(temp_service_list);
+ smartlist_free(rend_service_staging_list);
+ rend_service_staging_list = NULL;
return rv;
}
@@ -4551,3 +4579,19 @@ rend_service_non_anonymous_mode_enabled(const or_options_t *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 /* TOR_UNIT_TESTS */
+
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 6f45f473a5..1583a6010b 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -133,13 +133,19 @@ STATIC ssize_t encode_establish_intro_cell_legacy(char *cell_body_out,
size_t cell_body_out_len,
crypto_pk_t *intro_key,
char *rend_circ_nonce);
-STATIC void prune_services_on_reload(smartlist_t *old_service_list,
- smartlist_t *new_service_list);
+#ifdef TOR_UNIT_TESTS
-#endif
+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 /* TOR_UNIT_TESTS */
+
+#endif /* RENDSERVICE_PRIVATE */
int num_rend_services(void);
int rend_config_services(const or_options_t *options, int validate_only);
+void rend_service_prune_list(void);
int rend_service_load_all_keys(const smartlist_t *service_list);
void rend_services_add_filenames_to_lists(smartlist_t *open_lst,
smartlist_t *stat_lst);