diff --git a/changes/bug14784 b/changes/bug14784 new file mode 100644 index 0000000000..1f5014235e --- /dev/null +++ b/changes/bug14784 @@ -0,0 +1,4 @@ + o Minor features: + - Implement a new controller command "status/fresh-relay-descs" to + fetch a descriptor/extrainfo pair that was generated on demand + just for the controller's use. Implements ticket 14784. diff --git a/src/or/control.c b/src/or/control.c index 5ec97bd9af..a34eb0d4c2 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2100,6 +2100,47 @@ getinfo_helper_events(control_connection_t *control_conn, return -1; } *answer = bridge_stats; + } else if (!strcmp(question, "status/fresh-relay-descs")) { + if (!server_mode(get_options())) { + *errmsg = "Only relays have descriptors"; + return -1; + } + routerinfo_t *r; + extrainfo_t *e; + if (router_build_fresh_descriptor(&r, &e) < 0) { + *errmsg = "Error generating descriptor"; + return -1; + } + size_t size = r->cache_info.signed_descriptor_len + 1; + if (e) { + size += e->cache_info.signed_descriptor_len + 1; + } + tor_assert(r->cache_info.signed_descriptor_len); + char *descs = tor_malloc(size); + char *cp = descs; + memcpy(cp, signed_descriptor_get_body(&r->cache_info), + r->cache_info.signed_descriptor_len); + cp += r->cache_info.signed_descriptor_len - 1; + if (e) { + if (cp[0] == '\0') { + cp[0] = '\n'; + } else if (cp[0] != '\n') { + cp[1] = '\n'; + cp++; + } + memcpy(cp, signed_descriptor_get_body(&e->cache_info), + e->cache_info.signed_descriptor_len); + cp += e->cache_info.signed_descriptor_len - 1; + } + if (cp[0] == '\n') { + cp[0] = '\0'; + } else if (cp[0] != '\0') { + cp[1] = '\0'; + } + log_warn(LD_CONFIG, "%s", descs); + *answer = descs; + routerinfo_free(r); + extrainfo_free(e); } else { return 0; } @@ -2210,6 +2251,8 @@ static const getinfo_item_t getinfo_items[] = { "The last bootstrap phase status event that Tor sent."), DOC("status/clients-seen", "Breakdown of client countries seen by a bridge."), + DOC("status/fresh-relay-descs", + "A fresh relay/ei descriptor pair for Tor's current state. Not stored."), DOC("status/version/recommended", "List of currently recommended versions."), DOC("status/version/current", "Status of the current version."), DOC("status/version/num-versioning", "Number of versioning authorities."), diff --git a/src/or/router.c b/src/or/router.c index 2ddaa895fc..b8bfd3cf6f 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1802,12 +1802,15 @@ router_pick_published_address(const or_options_t *options, uint32_t *addr) return 0; } -/** If force is true, or our descriptor is out-of-date, rebuild a fresh - * routerinfo, signed server descriptor, and extra-info document for this OR. - * Return 0 on success, -1 on temporary error. +/** Build a fresh routerinfo, signed server descriptor, and extra-info document + * for this OR. Set r to the generated routerinfo, e to the generated + * extra-info document. Return 0 on success, -1 on temporary error. Failure to + * generate an extra-info document is not an error and is indicated by setting + * e to NULL. Caller is responsible for freeing generated documents if 0 is + * returned. */ int -router_rebuild_descriptor(int force) +router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) { routerinfo_t *ri; extrainfo_t *ei; @@ -1816,20 +1819,11 @@ router_rebuild_descriptor(int force) int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); - if (desc_clean_since && !force) - return 0; - - if (router_pick_published_address(options, &addr) < 0 || - router_get_advertised_or_port(options) == 0) { - /* Stop trying to rebuild our descriptor every second. We'll - * learn that it's time to try again when ip_address_changed() - * marks it dirty. */ - desc_clean_since = time(NULL); + if (router_pick_published_address(options, &addr) < 0) { + log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); return -1; } - log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); - ri = tor_malloc_zero(sizeof(routerinfo_t)); ri->cache_info.routerlist_index = -1; ri->nickname = tor_strdup(options->Nickname); @@ -2024,6 +2018,41 @@ router_rebuild_descriptor(int force) tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL)); } + *r = ri; + *e = ei; + return 0; +} + +/** If force is true, or our descriptor is out-of-date, rebuild a fresh + * routerinfo, signed server descriptor, and extra-info document for this OR. + * Return 0 on success, -1 on temporary error. + */ +int +router_rebuild_descriptor(int force) +{ + routerinfo_t *ri; + extrainfo_t *ei; + uint32_t addr; + const or_options_t *options = get_options(); + + if (desc_clean_since && !force) + return 0; + + if (router_pick_published_address(options, &addr) < 0 || + router_get_advertised_or_port(options) == 0) { + /* Stop trying to rebuild our descriptor every second. We'll + * learn that it's time to try again when ip_address_changed() + * marks it dirty. */ + desc_clean_since = time(NULL); + return -1; + } + + log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); + + if (router_build_fresh_descriptor(&ri, &ei) < 0) { + return -1; + } + routerinfo_free(desc_routerinfo); desc_routerinfo = ri; extrainfo_free(desc_extrainfo); diff --git a/src/or/router.h b/src/or/router.h index 8108ffb22f..73b43bc751 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -89,6 +89,7 @@ const uint8_t *router_get_my_id_digest(void); int router_extrainfo_digest_is_me(const char *digest); int router_is_me(const routerinfo_t *router); int router_pick_published_address(const or_options_t *options, uint32_t *addr); +int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, crypto_pk_t *ident_key);