2007-12-12 22:09:01 +01:00
|
|
|
/* Copyright (c) 2001-2004, Roger Dingledine.
|
|
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
2018-06-20 14:13:28 +02:00
|
|
|
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
2003-09-27 23:30:10 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
2007-06-13 20:16:05 +02:00
|
|
|
#define DIRSERV_PRIVATE
|
2018-07-05 22:34:59 +02:00
|
|
|
#include "core/or/or.h"
|
2018-09-20 14:09:35 +02:00
|
|
|
|
2018-07-05 22:34:59 +02:00
|
|
|
#include "app/config/config.h"
|
|
|
|
#include "core/mainloop/connection.h"
|
|
|
|
#include "feature/dircache/conscache.h"
|
|
|
|
#include "feature/dircache/consdiffmgr.h"
|
|
|
|
#include "feature/dircache/directory.h"
|
|
|
|
#include "feature/dircache/dirserv.h"
|
|
|
|
#include "feature/nodelist/microdesc.h"
|
|
|
|
#include "feature/nodelist/routerlist.h"
|
2018-09-20 14:09:35 +02:00
|
|
|
#include "feature/relay/router.h"
|
|
|
|
#include "feature/stats/rephist.h"
|
2018-04-05 20:27:30 +02:00
|
|
|
|
2018-07-05 22:34:59 +02:00
|
|
|
#include "feature/dircache/cached_dir_st.h"
|
|
|
|
#include "feature/dircommon/dir_connection_st.h"
|
|
|
|
#include "feature/nodelist/extrainfo_st.h"
|
|
|
|
#include "feature/nodelist/microdesc_st.h"
|
|
|
|
#include "feature/nodelist/routerinfo_st.h"
|
|
|
|
#include "feature/nodelist/routerlist_st.h"
|
2018-06-15 16:31:21 +02:00
|
|
|
|
2018-07-01 17:32:11 +02:00
|
|
|
#include "lib/compress/compress.h"
|
2018-06-21 22:48:54 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/**
|
|
|
|
* \file dirserv.c
|
2005-06-11 07:31:17 +02:00
|
|
|
* \brief Directory server core implementation. Manages directory
|
2018-06-25 00:08:24 +02:00
|
|
|
* contents and generates directory documents.
|
2016-10-15 02:08:51 +02:00
|
|
|
*
|
|
|
|
* This module implements most of directory cache functionality, and some of
|
|
|
|
* the directory authority functionality. The directory.c module delegates
|
|
|
|
* here in order to handle incoming requests from clients, via
|
|
|
|
* connection_dirserv_flushed_some() and its kin. In order to save RAM, this
|
2018-01-24 09:55:15 +01:00
|
|
|
* module is responsible for spooling directory objects (in whole or in part)
|
2016-10-15 02:08:51 +02:00
|
|
|
* onto buf_t instances, and then closing the dir_connection_t once the
|
|
|
|
* objects are totally flushed.
|
|
|
|
*
|
|
|
|
* The directory.c module also delegates here for handling descriptor uploads
|
|
|
|
* via dirserv_add_multiple_descriptors().
|
|
|
|
*
|
|
|
|
* Additionally, this module handles some aspects of voting, including:
|
|
|
|
* deciding how to vote on individual flags (based on decisions reached in
|
|
|
|
* rephist.c), of formatting routerstatus lines, and deciding what relays to
|
|
|
|
* include in an authority's vote. (TODO: Those functions could profitably be
|
|
|
|
* split off. They only live in this file because historically they were
|
|
|
|
* shared among the v1, v2, and v3 directory code.)
|
2007-07-26 00:56:40 +02:00
|
|
|
*/
|
2004-05-05 02:30:43 +02:00
|
|
|
|
2006-06-18 09:38:55 +02:00
|
|
|
static void clear_cached_dir(cached_dir_t *d);
|
2010-09-29 06:38:32 +02:00
|
|
|
static const signed_descriptor_t *get_signed_descriptor_by_fp(
|
2017-03-13 20:38:20 +01:00
|
|
|
const uint8_t *fp,
|
|
|
|
int extrainfo);
|
2007-05-15 09:13:56 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
static int spooled_resource_lookup_body(const spooled_resource_t *spooled,
|
|
|
|
int conn_is_encrypted,
|
|
|
|
const uint8_t **body_out,
|
|
|
|
size_t *size_out,
|
|
|
|
time_t *published_out);
|
|
|
|
static cached_dir_t *spooled_resource_lookup_cached_dir(
|
|
|
|
const spooled_resource_t *spooled,
|
|
|
|
time_t *published_out);
|
|
|
|
static cached_dir_t *lookup_cached_dir_by_fp(const uint8_t *fp);
|
|
|
|
|
2007-12-01 05:58:53 +01:00
|
|
|
/********************************************************************/
|
|
|
|
|
|
|
|
/* A set of functions to answer questions about how we'd like to behave
|
2007-12-04 19:35:03 +01:00
|
|
|
* as a directory mirror/client. */
|
2007-12-01 05:58:53 +01:00
|
|
|
|
2007-12-04 19:35:03 +01:00
|
|
|
/** Return 1 if we fetch our directory material directly from the
|
|
|
|
* authorities, rather than from a mirror. */
|
|
|
|
int
|
2011-06-14 19:01:38 +02:00
|
|
|
directory_fetches_from_authorities(const or_options_t *options)
|
2007-12-04 19:35:03 +01:00
|
|
|
{
|
2010-09-29 06:38:32 +02:00
|
|
|
const routerinfo_t *me;
|
2008-02-24 00:39:16 +01:00
|
|
|
uint32_t addr;
|
2010-09-21 07:03:29 +02:00
|
|
|
int refuseunknown;
|
2007-12-21 07:08:00 +01:00
|
|
|
if (options->FetchDirInfoEarly)
|
|
|
|
return 1;
|
2007-12-06 18:01:16 +01:00
|
|
|
if (options->BridgeRelay == 1)
|
|
|
|
return 0;
|
2016-11-16 22:13:03 +01:00
|
|
|
if (server_mode(options) &&
|
2016-11-16 22:22:11 +01:00
|
|
|
router_pick_published_address(options, &addr, 1) < 0)
|
2008-02-24 00:39:16 +01:00
|
|
|
return 1; /* we don't know our IP address; ask an authority. */
|
2010-12-06 17:36:01 +01:00
|
|
|
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
|
2010-09-21 07:03:29 +02:00
|
|
|
should_refuse_unknown_exits(options);
|
2014-10-28 23:01:06 +01:00
|
|
|
if (!dir_server_mode(options) && !refuseunknown)
|
2008-02-24 00:39:16 +01:00
|
|
|
return 0;
|
2008-01-18 05:42:22 +01:00
|
|
|
if (!server_mode(options) || !advertised_server_mode())
|
2007-12-04 19:35:03 +01:00
|
|
|
return 0;
|
2018-09-20 14:09:35 +02:00
|
|
|
me = router_get_my_routerinfo();
|
|
|
|
if (!me || (!me->supports_tunnelled_dir_requests && !refuseunknown))
|
|
|
|
return 0; /* if we don't service directory requests, return 0 too */
|
|
|
|
return 1;
|
2015-01-29 15:54:47 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Return 1 if we should fetch new networkstatuses, descriptors, etc
|
|
|
|
* on the "mirror" schedule rather than the "client" schedule.
|
|
|
|
*/
|
2015-01-29 15:54:47 +01:00
|
|
|
int
|
2018-09-20 14:09:35 +02:00
|
|
|
directory_fetches_dir_info_early(const or_options_t *options)
|
2015-01-29 15:54:47 +01:00
|
|
|
{
|
2018-09-20 14:09:35 +02:00
|
|
|
return directory_fetches_from_authorities(options);
|
2015-01-29 15:54:47 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Return 1 if we should fetch new networkstatuses, descriptors, etc
|
|
|
|
* on a very passive schedule -- waiting long enough for ordinary clients
|
|
|
|
* to probably have the info we want. These would include bridge users,
|
|
|
|
* and maybe others in the future e.g. if a Tor client uses another Tor
|
|
|
|
* client as a directory guard.
|
2009-07-31 06:33:53 +02:00
|
|
|
*/
|
2018-09-20 14:09:35 +02:00
|
|
|
int
|
|
|
|
directory_fetches_dir_info_later(const or_options_t *options)
|
2009-07-31 06:33:53 +02:00
|
|
|
{
|
2018-09-20 14:09:35 +02:00
|
|
|
return options->UseBridges != 0;
|
|
|
|
}
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Return true iff we want to serve certificates for authorities
|
|
|
|
* that we don't acknowledge as authorities ourself.
|
|
|
|
* Use we_want_to_fetch_unknown_auth_certs to check if we want to fetch
|
|
|
|
* and keep these certificates.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
directory_caches_unknown_auth_certs(const or_options_t *options)
|
|
|
|
{
|
|
|
|
return dir_server_mode(options) || options->BridgeRelay;
|
2009-07-31 06:33:53 +02:00
|
|
|
}
|
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Return 1 if we want to fetch and serve descriptors, networkstatuses, etc
|
|
|
|
* Else return 0.
|
|
|
|
* Check options->DirPort_set and directory_permits_begindir_requests()
|
|
|
|
* to see if we are willing to serve these directory documents to others via
|
|
|
|
* the DirPort and begindir-over-ORPort, respectively.
|
|
|
|
*
|
|
|
|
* To check if we should fetch documents, use we_want_to_fetch_flavor and
|
|
|
|
* we_want_to_fetch_unknown_auth_certs instead of this function.
|
2009-07-31 06:33:53 +02:00
|
|
|
*/
|
2018-09-20 14:09:35 +02:00
|
|
|
int
|
|
|
|
directory_caches_dir_info(const or_options_t *options)
|
2009-07-31 06:33:53 +02:00
|
|
|
{
|
2018-09-20 14:09:35 +02:00
|
|
|
if (options->BridgeRelay || dir_server_mode(options))
|
|
|
|
return 1;
|
|
|
|
if (!server_mode(options) || !advertised_server_mode())
|
2009-07-31 06:33:53 +02:00
|
|
|
return 0;
|
2018-09-20 14:09:35 +02:00
|
|
|
/* We need an up-to-date view of network info if we're going to try to
|
|
|
|
* block exit attempts from unknown relays. */
|
|
|
|
return ! router_my_exit_policy_is_reject_star() &&
|
|
|
|
should_refuse_unknown_exits(options);
|
|
|
|
}
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Return 1 if we want to allow remote clients to ask us directory
|
|
|
|
* requests via the "begin_dir" interface, which doesn't require
|
|
|
|
* having any separate port open. */
|
|
|
|
int
|
|
|
|
directory_permits_begindir_requests(const or_options_t *options)
|
|
|
|
{
|
|
|
|
return options->BridgeRelay != 0 || dir_server_mode(options);
|
2009-07-31 06:33:53 +02:00
|
|
|
}
|
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Return 1 if we have no need to fetch new descriptors. This generally
|
|
|
|
* happens when we're not a dir cache and we haven't built any circuits
|
|
|
|
* lately.
|
2009-07-31 06:33:53 +02:00
|
|
|
*/
|
|
|
|
int
|
2018-09-20 14:09:35 +02:00
|
|
|
directory_too_idle_to_fetch_descriptors(const or_options_t *options,
|
|
|
|
time_t now)
|
2009-07-31 06:33:53 +02:00
|
|
|
{
|
2018-09-20 14:09:35 +02:00
|
|
|
return !directory_caches_dir_info(options) &&
|
|
|
|
!options->FetchUselessDescriptors &&
|
|
|
|
rep_hist_circbuilding_dormant(now);
|
|
|
|
}
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/********************************************************************/
|
2018-05-02 14:33:21 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Map from flavor name to the cached_dir_t for the v3 consensuses that we're
|
|
|
|
* currently serving. */
|
|
|
|
static strmap_t *cached_consensuses = NULL;
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Decrement the reference count on <b>d</b>, and free it if it no longer has
|
|
|
|
* any references. */
|
|
|
|
void
|
|
|
|
cached_dir_decref(cached_dir_t *d)
|
|
|
|
{
|
|
|
|
if (!d || --d->refcnt > 0)
|
|
|
|
return;
|
|
|
|
clear_cached_dir(d);
|
|
|
|
tor_free(d);
|
|
|
|
}
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Allocate and return a new cached_dir_t containing the string <b>s</b>,
|
|
|
|
* published at <b>published</b>. */
|
|
|
|
cached_dir_t *
|
|
|
|
new_cached_dir(char *s, time_t published)
|
|
|
|
{
|
|
|
|
cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t));
|
|
|
|
d->refcnt = 1;
|
|
|
|
d->dir = s;
|
|
|
|
d->dir_len = strlen(s);
|
|
|
|
d->published = published;
|
|
|
|
if (tor_compress(&(d->dir_compressed), &(d->dir_compressed_len),
|
|
|
|
d->dir, d->dir_len, ZLIB_METHOD)) {
|
|
|
|
log_warn(LD_BUG, "Error compressing directory");
|
2009-07-31 06:33:53 +02:00
|
|
|
}
|
2018-09-20 14:09:35 +02:00
|
|
|
return d;
|
|
|
|
}
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Remove all storage held in <b>d</b>, but do not free <b>d</b> itself. */
|
|
|
|
static void
|
|
|
|
clear_cached_dir(cached_dir_t *d)
|
|
|
|
{
|
|
|
|
tor_free(d->dir);
|
|
|
|
tor_free(d->dir_compressed);
|
|
|
|
memset(d, 0, sizeof(cached_dir_t));
|
|
|
|
}
|
2009-07-31 06:33:53 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Free all storage held by the cached_dir_t in <b>d</b>. */
|
|
|
|
static void
|
|
|
|
free_cached_dir_(void *_d)
|
|
|
|
{
|
|
|
|
cached_dir_t *d;
|
|
|
|
if (!_d)
|
|
|
|
return;
|
2013-02-28 04:43:50 +01:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
d = (cached_dir_t *)_d;
|
|
|
|
cached_dir_decref(d);
|
|
|
|
}
|
2018-07-10 16:26:22 +02:00
|
|
|
|
2018-09-20 14:09:35 +02:00
|
|
|
/** Replace the v3 consensus networkstatus of type <b>flavor_name</b> that
|
|
|
|
* we're serving with <b>networkstatus</b>, published at <b>published</b>. No
|
|
|
|
* validation is performed. */
|
|
|
|
void
|
|
|
|
dirserv_set_cached_consensus_networkstatus(const char *networkstatus,
|
|
|
|
const char *flavor_name,
|
|
|
|
const common_digests_t *digests,
|
|
|
|
const uint8_t *sha3_as_signed,
|
|
|
|
time_t published)
|
|
|
|
{
|
|
|
|
cached_dir_t *new_networkstatus;
|
|
|
|
cached_dir_t *old_networkstatus;
|
|
|
|
if (!cached_consensuses)
|
|
|
|
cached_consensuses = strmap_new();
|
|
|
|
|
|
|
|
new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published);
|
|
|
|
memcpy(&new_networkstatus->digests, digests, sizeof(common_digests_t));
|
|
|
|
memcpy(&new_networkstatus->digest_sha3_as_signed, sha3_as_signed,
|
|
|
|
DIGEST256_LEN);
|
|
|
|
old_networkstatus = strmap_set(cached_consensuses, flavor_name,
|
|
|
|
new_networkstatus);
|
|
|
|
if (old_networkstatus)
|
|
|
|
cached_dir_decref(old_networkstatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the latest downloaded consensus networkstatus in encoded, signed,
|
|
|
|
* optionally compressed format, suitable for sending to clients. */
|
|
|
|
cached_dir_t *
|
|
|
|
dirserv_get_consensus(const char *flavor_name)
|
|
|
|
{
|
|
|
|
if (!cached_consensuses)
|
|
|
|
return NULL;
|
|
|
|
return strmap_get(cached_consensuses, flavor_name);
|
2009-07-31 06:33:53 +02:00
|
|
|
}
|
|
|
|
|
2006-06-18 09:38:55 +02:00
|
|
|
/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t
|
2007-04-16 20:39:39 +02:00
|
|
|
* pointers, adds copies of digests to fps_out, and doesn't use the
|
|
|
|
* /tor/server/ prefix. For a /d/ request, adds descriptor digests; for other
|
|
|
|
* requests, adds identity digests.
|
2006-06-18 09:38:55 +02:00
|
|
|
*/
|
|
|
|
int
|
2017-03-13 20:38:20 +01:00
|
|
|
dirserv_get_routerdesc_spool(smartlist_t *spool_out,
|
|
|
|
const char *key,
|
|
|
|
dir_spool_source_t source,
|
|
|
|
int conn_is_encrypted,
|
|
|
|
const char **msg_out)
|
2006-06-18 09:38:55 +02:00
|
|
|
{
|
2017-03-13 20:38:20 +01:00
|
|
|
*msg_out = NULL;
|
2006-06-18 09:38:55 +02:00
|
|
|
|
2007-04-16 20:39:39 +02:00
|
|
|
if (!strcmp(key, "all")) {
|
2017-03-13 20:38:20 +01:00
|
|
|
const routerlist_t *rl = router_get_routerlist();
|
|
|
|
SMARTLIST_FOREACH_BEGIN(rl->routers, const routerinfo_t *, r) {
|
|
|
|
spooled_resource_t *spooled;
|
|
|
|
spooled = spooled_resource_new(source,
|
|
|
|
(const uint8_t *)r->cache_info.identity_digest,
|
|
|
|
DIGEST_LEN);
|
|
|
|
/* Treat "all" requests as if they were unencrypted */
|
|
|
|
conn_is_encrypted = 0;
|
|
|
|
smartlist_add(spool_out, spooled);
|
|
|
|
} SMARTLIST_FOREACH_END(r);
|
2007-04-16 20:39:39 +02:00
|
|
|
} else if (!strcmp(key, "authority")) {
|
2010-09-29 06:38:32 +02:00
|
|
|
const routerinfo_t *ri = router_get_my_routerinfo();
|
2006-06-18 09:38:55 +02:00
|
|
|
if (ri)
|
2017-03-13 20:38:20 +01:00
|
|
|
smartlist_add(spool_out,
|
|
|
|
spooled_resource_new(source,
|
|
|
|
(const uint8_t *)ri->cache_info.identity_digest,
|
|
|
|
DIGEST_LEN));
|
2007-04-16 20:39:39 +02:00
|
|
|
} else if (!strcmpstart(key, "d/")) {
|
|
|
|
key += strlen("d/");
|
2017-03-13 20:38:20 +01:00
|
|
|
dir_split_resource_into_spoolable(key, source, spool_out, NULL,
|
|
|
|
DSR_HEX|DSR_SORT_UNIQ);
|
2007-04-16 20:39:39 +02:00
|
|
|
} else if (!strcmpstart(key, "fp/")) {
|
|
|
|
key += strlen("fp/");
|
2017-03-13 20:38:20 +01:00
|
|
|
dir_split_resource_into_spoolable(key, source, spool_out, NULL,
|
|
|
|
DSR_HEX|DSR_SORT_UNIQ);
|
2006-06-18 09:38:55 +02:00
|
|
|
} else {
|
2017-03-13 20:38:20 +01:00
|
|
|
*msg_out = "Not found";
|
2006-06-18 09:38:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
if (! conn_is_encrypted) {
|
2009-01-06 16:37:51 +01:00
|
|
|
/* Remove anything that insists it not be sent unencrypted. */
|
2017-03-13 20:38:20 +01:00
|
|
|
SMARTLIST_FOREACH_BEGIN(spool_out, spooled_resource_t *, spooled) {
|
|
|
|
const uint8_t *body = NULL;
|
|
|
|
size_t bodylen = 0;
|
|
|
|
int r = spooled_resource_lookup_body(spooled, conn_is_encrypted,
|
|
|
|
&body, &bodylen, NULL);
|
|
|
|
if (r < 0 || body == NULL || bodylen == 0) {
|
|
|
|
SMARTLIST_DEL_CURRENT(spool_out, spooled);
|
|
|
|
spooled_resource_free(spooled);
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(spooled);
|
2008-01-10 18:48:40 +01:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
if (!smartlist_len(spool_out)) {
|
|
|
|
*msg_out = "Servers unavailable";
|
2006-06-18 09:38:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-05 21:15:27 +01:00
|
|
|
/** Add a signed_descriptor_t to <b>descs_out</b> for each router matching
|
2005-10-18 16:33:19 +02:00
|
|
|
* <b>key</b>. The key should be either
|
|
|
|
* - "/tor/server/authority" for our own routerinfo;
|
|
|
|
* - "/tor/server/all" for all the routerinfos we have, concatenated;
|
|
|
|
* - "/tor/server/fp/FP" where FP is a plus-separated sequence of
|
|
|
|
* hex identity digests; or
|
2005-10-18 16:34:49 +02:00
|
|
|
* - "/tor/server/d/D" where D is a plus-separated sequence
|
2005-10-18 16:33:19 +02:00
|
|
|
* of server descriptor digests, in hex.
|
2005-10-18 19:09:57 +02:00
|
|
|
*
|
2005-11-01 04:48:51 +01:00
|
|
|
* Return 0 if we found some matching descriptors, or -1 if we do not
|
|
|
|
* have any descriptors, no matching descriptors, or if we did not
|
|
|
|
* recognize the key (URL).
|
|
|
|
* If -1 is returned *<b>msg</b> will be set to an appropriate error
|
|
|
|
* message.
|
2006-03-15 06:06:56 +01:00
|
|
|
*
|
2008-12-18 17:11:24 +01:00
|
|
|
* XXXX rename this function. It's only called from the controller.
|
2009-05-27 23:55:51 +02:00
|
|
|
* XXXX in fact, refactor this function, merging as much as possible.
|
2005-08-26 20:02:49 +02:00
|
|
|
*/
|
2005-10-18 19:09:57 +02:00
|
|
|
int
|
|
|
|
dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
|
2008-01-10 18:48:40 +01:00
|
|
|
const char **msg)
|
2005-08-25 22:33:17 +02:00
|
|
|
{
|
2005-10-18 19:09:57 +02:00
|
|
|
*msg = NULL;
|
|
|
|
|
2005-08-25 22:33:17 +02:00
|
|
|
if (!strcmp(key, "/tor/server/all")) {
|
2005-10-18 19:43:54 +02:00
|
|
|
routerlist_t *rl = router_get_routerlist();
|
2005-11-05 21:15:27 +01:00
|
|
|
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
|
|
|
|
smartlist_add(descs_out, &(r->cache_info)));
|
2005-08-25 22:33:17 +02:00
|
|
|
} else if (!strcmp(key, "/tor/server/authority")) {
|
2010-09-29 06:38:32 +02:00
|
|
|
const routerinfo_t *ri = router_get_my_routerinfo();
|
2005-08-25 22:33:17 +02:00
|
|
|
if (ri)
|
2010-09-29 06:38:32 +02:00
|
|
|
smartlist_add(descs_out, (void*) &(ri->cache_info));
|
2005-10-14 06:56:20 +02:00
|
|
|
} else if (!strcmpstart(key, "/tor/server/d/")) {
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *digests = smartlist_new();
|
2005-10-14 06:56:20 +02:00
|
|
|
key += strlen("/tor/server/d/");
|
2009-10-18 21:45:57 +02:00
|
|
|
dir_split_resource_into_fingerprints(key, digests, NULL,
|
|
|
|
DSR_HEX|DSR_SORT_UNIQ);
|
2005-10-14 06:56:20 +02:00
|
|
|
SMARTLIST_FOREACH(digests, const char *, d,
|
|
|
|
{
|
2005-11-05 21:15:27 +01:00
|
|
|
signed_descriptor_t *sd = router_get_by_descriptor_digest(d);
|
|
|
|
if (sd)
|
|
|
|
smartlist_add(descs_out,sd);
|
2005-10-14 06:56:20 +02:00
|
|
|
});
|
|
|
|
SMARTLIST_FOREACH(digests, char *, d, tor_free(d));
|
|
|
|
smartlist_free(digests);
|
2005-08-25 22:33:17 +02:00
|
|
|
} else if (!strcmpstart(key, "/tor/server/fp/")) {
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *digests = smartlist_new();
|
2006-03-08 07:29:52 +01:00
|
|
|
time_t cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH;
|
2005-08-25 22:33:17 +02:00
|
|
|
key += strlen("/tor/server/fp/");
|
2009-10-18 21:45:57 +02:00
|
|
|
dir_split_resource_into_fingerprints(key, digests, NULL,
|
|
|
|
DSR_HEX|DSR_SORT_UNIQ);
|
2012-07-17 15:33:38 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(digests, const char *, d) {
|
2005-09-08 23:01:24 +02:00
|
|
|
if (router_digest_is_me(d)) {
|
2016-02-08 14:31:31 +01:00
|
|
|
/* calling router_get_my_routerinfo() to make sure it exists */
|
2010-09-29 06:38:32 +02:00
|
|
|
const routerinfo_t *ri = router_get_my_routerinfo();
|
2008-02-24 23:11:18 +01:00
|
|
|
if (ri)
|
2010-09-29 06:38:32 +02:00
|
|
|
smartlist_add(descs_out, (void*) &(ri->cache_info));
|
2005-09-08 23:01:24 +02:00
|
|
|
} else {
|
2010-10-14 17:49:51 +02:00
|
|
|
const routerinfo_t *ri = router_get_by_id_digest(d);
|
2006-01-10 05:57:12 +01:00
|
|
|
/* Don't actually serve a descriptor that everyone will think is
|
|
|
|
* expired. This is an (ugly) workaround to keep buggy 0.1.1.10
|
|
|
|
* Tors from downloading descriptors that they will throw away.
|
|
|
|
*/
|
|
|
|
if (ri && ri->cache_info.published_on > cutoff)
|
2010-09-29 06:38:32 +02:00
|
|
|
smartlist_add(descs_out, (void*) &(ri->cache_info));
|
2005-09-08 23:01:24 +02:00
|
|
|
}
|
2012-07-17 15:33:38 +02:00
|
|
|
} SMARTLIST_FOREACH_END(d);
|
2005-08-25 22:33:17 +02:00
|
|
|
SMARTLIST_FOREACH(digests, char *, d, tor_free(d));
|
|
|
|
smartlist_free(digests);
|
2005-10-18 19:09:57 +02:00
|
|
|
} else {
|
|
|
|
*msg = "Key not recognized";
|
|
|
|
return -1;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
2005-10-18 19:09:57 +02:00
|
|
|
|
|
|
|
if (!smartlist_len(descs_out)) {
|
|
|
|
*msg = "Servers unavailable";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2004-06-16 23:08:29 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/* ==========
|
|
|
|
* Spooling code.
|
|
|
|
* ========== */
|
|
|
|
|
|
|
|
spooled_resource_t *
|
|
|
|
spooled_resource_new(dir_spool_source_t source,
|
|
|
|
const uint8_t *digest, size_t digestlen)
|
2007-12-24 12:25:45 +01:00
|
|
|
{
|
2017-03-13 20:38:20 +01:00
|
|
|
spooled_resource_t *spooled = tor_malloc_zero(sizeof(spooled_resource_t));
|
|
|
|
spooled->spool_source = source;
|
|
|
|
switch (source) {
|
|
|
|
case DIR_SPOOL_NETWORKSTATUS:
|
|
|
|
spooled->spool_eagerly = 0;
|
|
|
|
break;
|
|
|
|
case DIR_SPOOL_SERVER_BY_DIGEST:
|
|
|
|
case DIR_SPOOL_SERVER_BY_FP:
|
|
|
|
case DIR_SPOOL_EXTRA_BY_DIGEST:
|
|
|
|
case DIR_SPOOL_EXTRA_BY_FP:
|
|
|
|
case DIR_SPOOL_MICRODESC:
|
|
|
|
default:
|
|
|
|
spooled->spool_eagerly = 1;
|
|
|
|
break;
|
2017-04-16 23:47:28 +02:00
|
|
|
case DIR_SPOOL_CONSENSUS_CACHE_ENTRY:
|
|
|
|
tor_assert_unreached();
|
|
|
|
break;
|
2014-01-29 21:17:05 +01:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
tor_assert(digestlen <= sizeof(spooled->digest));
|
|
|
|
if (digest)
|
|
|
|
memcpy(spooled->digest, digest, digestlen);
|
|
|
|
return spooled;
|
2007-12-24 12:25:45 +01:00
|
|
|
}
|
|
|
|
|
2017-04-16 23:47:28 +02:00
|
|
|
/**
|
|
|
|
* Create a new spooled_resource_t to spool the contents of <b>entry</b> to
|
|
|
|
* the user. Return the spooled object on success, or NULL on failure (which
|
|
|
|
* is probably caused by a failure to map the body of the item from disk).
|
|
|
|
*
|
|
|
|
* Adds a reference to entry's reference counter.
|
|
|
|
*/
|
|
|
|
spooled_resource_t *
|
|
|
|
spooled_resource_new_from_cache_entry(consensus_cache_entry_t *entry)
|
|
|
|
{
|
|
|
|
spooled_resource_t *spooled = tor_malloc_zero(sizeof(spooled_resource_t));
|
|
|
|
spooled->spool_source = DIR_SPOOL_CONSENSUS_CACHE_ENTRY;
|
|
|
|
spooled->spool_eagerly = 0;
|
|
|
|
consensus_cache_entry_incref(entry);
|
|
|
|
spooled->consensus_cache_entry = entry;
|
|
|
|
|
|
|
|
int r = consensus_cache_entry_get_body(entry,
|
|
|
|
&spooled->cce_body,
|
|
|
|
&spooled->cce_len);
|
|
|
|
if (r == 0) {
|
|
|
|
return spooled;
|
|
|
|
} else {
|
|
|
|
spooled_resource_free(spooled);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Release all storage held by <b>spooled</b>. */
|
|
|
|
void
|
2017-11-21 15:37:47 +01:00
|
|
|
spooled_resource_free_(spooled_resource_t *spooled)
|
2007-06-02 17:26:57 +02:00
|
|
|
{
|
2017-03-13 20:38:20 +01:00
|
|
|
if (spooled == NULL)
|
|
|
|
return;
|
2007-08-14 22:19:58 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
if (spooled->cached_dir_ref) {
|
|
|
|
cached_dir_decref(spooled->cached_dir_ref);
|
2007-09-21 00:08:40 +02:00
|
|
|
}
|
|
|
|
|
2017-04-16 23:47:28 +02:00
|
|
|
if (spooled->consensus_cache_entry) {
|
|
|
|
consensus_cache_entry_decref(spooled->consensus_cache_entry);
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
tor_free(spooled);
|
2007-06-02 17:26:57 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** When spooling data from a cached_dir_t object, we always add
|
|
|
|
* at least this much. */
|
|
|
|
#define DIRSERV_CACHED_DIR_CHUNK_SIZE 8192
|
|
|
|
|
|
|
|
/** Return an compression ratio for compressing objects from <b>source</b>.
|
|
|
|
*/
|
|
|
|
static double
|
|
|
|
estimate_compression_ratio(dir_spool_source_t source)
|
2009-10-18 21:45:57 +02:00
|
|
|
{
|
2017-03-13 20:38:20 +01:00
|
|
|
/* We should put in better estimates here, depending on the number of
|
|
|
|
objects and their type */
|
|
|
|
(void) source;
|
|
|
|
return 0.5;
|
2009-10-18 21:45:57 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Return an estimated number of bytes needed for transmitting the
|
|
|
|
* resource in <b>spooled</b> on <b>conn</b>
|
2007-01-03 20:58:00 +01:00
|
|
|
*
|
2017-03-13 20:38:20 +01:00
|
|
|
* As a convenient side-effect, set *<b>published_out</b> to the resource's
|
|
|
|
* publication time.
|
|
|
|
*/
|
|
|
|
static size_t
|
|
|
|
spooled_resource_estimate_size(const spooled_resource_t *spooled,
|
|
|
|
dir_connection_t *conn,
|
|
|
|
int compressed,
|
|
|
|
time_t *published_out)
|
|
|
|
{
|
|
|
|
if (spooled->spool_eagerly) {
|
|
|
|
const uint8_t *body = NULL;
|
|
|
|
size_t bodylen = 0;
|
|
|
|
int r = spooled_resource_lookup_body(spooled,
|
|
|
|
connection_dir_is_encrypted(conn),
|
|
|
|
&body, &bodylen,
|
|
|
|
published_out);
|
|
|
|
if (r == -1 || body == NULL || bodylen == 0)
|
|
|
|
return 0;
|
|
|
|
if (compressed) {
|
|
|
|
double ratio = estimate_compression_ratio(spooled->spool_source);
|
|
|
|
bodylen = (size_t)(bodylen * ratio);
|
|
|
|
}
|
|
|
|
return bodylen;
|
2007-01-03 20:58:00 +01:00
|
|
|
} else {
|
2017-03-13 20:38:20 +01:00
|
|
|
cached_dir_t *cached;
|
2017-04-16 23:47:28 +02:00
|
|
|
if (spooled->consensus_cache_entry) {
|
2017-06-24 01:55:54 +02:00
|
|
|
if (published_out) {
|
|
|
|
consensus_cache_entry_get_valid_after(
|
|
|
|
spooled->consensus_cache_entry, published_out);
|
|
|
|
}
|
|
|
|
|
2017-04-16 23:47:28 +02:00
|
|
|
return spooled->cce_len;
|
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
if (spooled->cached_dir_ref) {
|
|
|
|
cached = spooled->cached_dir_ref;
|
|
|
|
} else {
|
|
|
|
cached = spooled_resource_lookup_cached_dir(spooled,
|
|
|
|
published_out);
|
|
|
|
}
|
|
|
|
if (cached == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
2017-05-04 16:21:05 +02:00
|
|
|
size_t result = compressed ? cached->dir_compressed_len : cached->dir_len;
|
2017-03-13 20:38:20 +01:00
|
|
|
return result;
|
2007-01-03 20:58:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Return code for spooled_resource_flush_some */
|
|
|
|
typedef enum {
|
|
|
|
SRFS_ERR = -1,
|
|
|
|
SRFS_MORE = 0,
|
|
|
|
SRFS_DONE
|
|
|
|
} spooled_resource_flush_status_t;
|
2009-10-18 21:45:57 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Flush some or all of the bytes from <b>spooled</b> onto <b>conn</b>.
|
|
|
|
* Return SRFS_ERR on error, SRFS_MORE if there are more bytes to flush from
|
|
|
|
* this spooled resource, or SRFS_DONE if we are done flushing this spooled
|
|
|
|
* resource.
|
|
|
|
*/
|
|
|
|
static spooled_resource_flush_status_t
|
|
|
|
spooled_resource_flush_some(spooled_resource_t *spooled,
|
|
|
|
dir_connection_t *conn)
|
|
|
|
{
|
|
|
|
if (spooled->spool_eagerly) {
|
|
|
|
/* Spool_eagerly resources are sent all-at-once. */
|
|
|
|
const uint8_t *body = NULL;
|
|
|
|
size_t bodylen = 0;
|
|
|
|
int r = spooled_resource_lookup_body(spooled,
|
|
|
|
connection_dir_is_encrypted(conn),
|
|
|
|
&body, &bodylen, NULL);
|
|
|
|
if (r == -1 || body == NULL || bodylen == 0) {
|
|
|
|
/* Absent objects count as "done". */
|
|
|
|
return SRFS_DONE;
|
|
|
|
}
|
2017-04-18 01:10:25 +02:00
|
|
|
if (conn->compress_state) {
|
2017-08-08 21:16:39 +02:00
|
|
|
connection_buf_add_compress((const char*)body, bodylen, conn, 0);
|
2017-03-13 20:38:20 +01:00
|
|
|
} else {
|
2017-08-08 21:16:39 +02:00
|
|
|
connection_buf_add((const char*)body, bodylen, TO_CONN(conn));
|
2017-03-13 20:38:20 +01:00
|
|
|
}
|
|
|
|
return SRFS_DONE;
|
|
|
|
} else {
|
|
|
|
cached_dir_t *cached = spooled->cached_dir_ref;
|
2017-04-16 23:47:28 +02:00
|
|
|
consensus_cache_entry_t *cce = spooled->consensus_cache_entry;
|
|
|
|
if (cached == NULL && cce == NULL) {
|
2017-03-13 20:38:20 +01:00
|
|
|
/* The cached_dir_t hasn't been materialized yet. So let's look it up. */
|
|
|
|
cached = spooled->cached_dir_ref =
|
|
|
|
spooled_resource_lookup_cached_dir(spooled, NULL);
|
|
|
|
if (!cached) {
|
|
|
|
/* Absent objects count as done. */
|
|
|
|
return SRFS_DONE;
|
|
|
|
}
|
|
|
|
++cached->refcnt;
|
|
|
|
tor_assert_nonfatal(spooled->cached_dir_offset == 0);
|
|
|
|
}
|
2006-06-18 09:38:55 +02:00
|
|
|
|
2017-04-16 23:47:28 +02:00
|
|
|
if (BUG(!cached && !cce))
|
|
|
|
return SRFS_DONE;
|
|
|
|
|
|
|
|
int64_t total_len;
|
|
|
|
const char *ptr;
|
|
|
|
if (cached) {
|
2017-05-04 16:21:05 +02:00
|
|
|
total_len = cached->dir_compressed_len;
|
|
|
|
ptr = cached->dir_compressed;
|
2017-04-16 23:47:28 +02:00
|
|
|
} else {
|
|
|
|
total_len = spooled->cce_len;
|
|
|
|
ptr = (const char *)spooled->cce_body;
|
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
/* How many bytes left to flush? */
|
2017-04-16 23:47:28 +02:00
|
|
|
int64_t remaining;
|
|
|
|
remaining = total_len - spooled->cached_dir_offset;
|
2017-03-13 20:38:20 +01:00
|
|
|
if (BUG(remaining < 0))
|
|
|
|
return SRFS_ERR;
|
2017-03-29 09:07:51 +02:00
|
|
|
ssize_t bytes = (ssize_t) MIN(DIRSERV_CACHED_DIR_CHUNK_SIZE, remaining);
|
2017-04-18 01:10:25 +02:00
|
|
|
if (conn->compress_state) {
|
2017-08-08 21:16:39 +02:00
|
|
|
connection_buf_add_compress(
|
2017-04-16 23:47:28 +02:00
|
|
|
ptr + spooled->cached_dir_offset,
|
2017-04-18 01:23:39 +02:00
|
|
|
bytes, conn, 0);
|
2017-03-13 20:38:20 +01:00
|
|
|
} else {
|
2017-08-08 21:16:39 +02:00
|
|
|
connection_buf_add(ptr + spooled->cached_dir_offset,
|
2017-03-13 20:38:20 +01:00
|
|
|
bytes, TO_CONN(conn));
|
|
|
|
}
|
|
|
|
spooled->cached_dir_offset += bytes;
|
2017-04-16 23:47:28 +02:00
|
|
|
if (spooled->cached_dir_offset >= (off_t)total_len) {
|
2017-03-13 20:38:20 +01:00
|
|
|
return SRFS_DONE;
|
|
|
|
} else {
|
|
|
|
return SRFS_MORE;
|
|
|
|
}
|
2006-06-20 02:48:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Helper: find the cached_dir_t for a spooled_resource_t, for
|
|
|
|
* sending it to <b>conn</b>. Set *<b>published_out</b>, if provided,
|
|
|
|
* to the published time of the cached_dir_t.
|
|
|
|
*
|
|
|
|
* DOES NOT increase the reference count on the result. Callers must do that
|
|
|
|
* themselves if they mean to hang on to it.
|
2007-01-23 20:22:52 +01:00
|
|
|
*/
|
2017-03-13 20:38:20 +01:00
|
|
|
static cached_dir_t *
|
|
|
|
spooled_resource_lookup_cached_dir(const spooled_resource_t *spooled,
|
|
|
|
time_t *published_out)
|
|
|
|
{
|
|
|
|
tor_assert(spooled->spool_eagerly == 0);
|
|
|
|
cached_dir_t *d = lookup_cached_dir_by_fp(spooled->digest);
|
|
|
|
if (d != NULL) {
|
|
|
|
if (published_out)
|
|
|
|
*published_out = d->published;
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Helper: Look up the body for an eagerly-served spooled_resource. If
|
|
|
|
* <b>conn_is_encrypted</b> is false, don't look up any resource that
|
|
|
|
* shouldn't be sent over an unencrypted connection. On success, set
|
|
|
|
* <b>body_out</b>, <b>size_out</b>, and <b>published_out</b> to refer
|
|
|
|
* to the resource's body, size, and publication date, and return 0.
|
|
|
|
* On failure return -1. */
|
2006-06-18 09:38:55 +02:00
|
|
|
static int
|
2017-03-13 20:38:20 +01:00
|
|
|
spooled_resource_lookup_body(const spooled_resource_t *spooled,
|
|
|
|
int conn_is_encrypted,
|
|
|
|
const uint8_t **body_out,
|
|
|
|
size_t *size_out,
|
|
|
|
time_t *published_out)
|
2006-06-18 09:38:55 +02:00
|
|
|
{
|
2017-03-13 20:38:20 +01:00
|
|
|
tor_assert(spooled->spool_eagerly == 1);
|
2006-06-18 09:38:55 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
const signed_descriptor_t *sd = NULL;
|
2011-10-25 14:11:13 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
switch (spooled->spool_source) {
|
|
|
|
case DIR_SPOOL_EXTRA_BY_FP: {
|
|
|
|
sd = get_signed_descriptor_by_fp(spooled->digest, 1);
|
|
|
|
break;
|
2007-04-16 20:39:39 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
case DIR_SPOOL_SERVER_BY_FP: {
|
|
|
|
sd = get_signed_descriptor_by_fp(spooled->digest, 0);
|
|
|
|
break;
|
2008-01-10 18:48:40 +01:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
case DIR_SPOOL_SERVER_BY_DIGEST: {
|
|
|
|
sd = router_get_by_descriptor_digest((const char *)spooled->digest);
|
|
|
|
break;
|
2011-10-25 14:11:13 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
case DIR_SPOOL_EXTRA_BY_DIGEST: {
|
|
|
|
sd = extrainfo_get_by_descriptor_digest((const char *)spooled->digest);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIR_SPOOL_MICRODESC: {
|
|
|
|
microdesc_t *md = microdesc_cache_lookup_by_digest256(
|
|
|
|
get_microdesc_cache(),
|
|
|
|
(const char *)spooled->digest);
|
|
|
|
if (! md || ! md->body) {
|
|
|
|
return -1;
|
2006-06-29 13:19:52 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
*body_out = (const uint8_t *)md->body;
|
|
|
|
*size_out = md->bodylen;
|
|
|
|
if (published_out)
|
|
|
|
*published_out = TIME_MAX;
|
|
|
|
return 0;
|
2006-06-18 09:38:55 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
case DIR_SPOOL_NETWORKSTATUS:
|
2017-04-16 23:47:28 +02:00
|
|
|
case DIR_SPOOL_CONSENSUS_CACHE_ENTRY:
|
2017-03-13 20:38:20 +01:00
|
|
|
default:
|
|
|
|
/* LCOV_EXCL_START */
|
|
|
|
tor_assert_nonfatal_unreached();
|
|
|
|
return -1;
|
|
|
|
/* LCOV_EXCL_STOP */
|
2006-06-18 09:38:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/* If we get here, then we tried to set "sd" to a signed_descriptor_t. */
|
2006-06-18 09:38:55 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
if (sd == NULL) {
|
|
|
|
return -1;
|
2009-10-18 21:45:57 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
if (sd->send_unencrypted == 0 && ! conn_is_encrypted) {
|
|
|
|
/* we did this check once before (so we could have an accurate size
|
|
|
|
* estimate and maybe send a 404 if somebody asked for only bridges on
|
|
|
|
* a connection), but we need to do it again in case a previously
|
|
|
|
* unknown bridge descriptor has shown up between then and now. */
|
|
|
|
return -1;
|
2009-10-18 21:45:57 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
*body_out = (const uint8_t *) signed_descriptor_get_body(sd);
|
|
|
|
*size_out = sd->signed_descriptor_len;
|
|
|
|
if (published_out)
|
|
|
|
*published_out = sd->published_on;
|
2009-10-18 21:45:57 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Given a fingerprint <b>fp</b> which is either set if we're looking for a
|
|
|
|
* v2 status, or zeroes if we're looking for a v3 status, or a NUL-padded
|
|
|
|
* flavor name if we want a flavored v3 status, return a pointer to the
|
|
|
|
* appropriate cached dir object, or NULL if there isn't one available. */
|
|
|
|
static cached_dir_t *
|
|
|
|
lookup_cached_dir_by_fp(const uint8_t *fp)
|
|
|
|
{
|
|
|
|
cached_dir_t *d = NULL;
|
|
|
|
if (tor_digest_is_zero((const char *)fp) && cached_consensuses) {
|
|
|
|
d = strmap_get(cached_consensuses, "ns");
|
|
|
|
} else if (memchr(fp, '\0', DIGEST_LEN) && cached_consensuses) {
|
|
|
|
/* this here interface is a nasty hack: we're shoving a flavor into
|
|
|
|
* a digest field. */
|
|
|
|
d = strmap_get(cached_consensuses, (const char *)fp);
|
2017-02-27 15:12:51 +01:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
return d;
|
|
|
|
}
|
2006-06-18 09:38:55 +02:00
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Try to guess the number of bytes that will be needed to send the
|
|
|
|
* spooled objects for <b>conn</b>'s outgoing spool. In the process,
|
|
|
|
* remove every element of the spool that refers to an absent object, or
|
|
|
|
* which was published earlier than <b>cutoff</b>. Set *<b>size_out</b>
|
|
|
|
* to the number of bytes, and *<b>n_expired_out</b> to the number of
|
|
|
|
* objects removed for being too old. */
|
|
|
|
void
|
|
|
|
dirserv_spool_remove_missing_and_guess_size(dir_connection_t *conn,
|
|
|
|
time_t cutoff,
|
|
|
|
int compression,
|
2017-03-28 21:41:59 +02:00
|
|
|
size_t *size_out,
|
2017-03-13 20:38:20 +01:00
|
|
|
int *n_expired_out)
|
|
|
|
{
|
|
|
|
if (BUG(!conn))
|
|
|
|
return;
|
|
|
|
|
|
|
|
smartlist_t *spool = conn->spool;
|
|
|
|
if (!spool) {
|
|
|
|
if (size_out)
|
|
|
|
*size_out = 0;
|
|
|
|
if (n_expired_out)
|
|
|
|
*n_expired_out = 0;
|
|
|
|
return;
|
2006-06-20 02:48:23 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
int n_expired = 0;
|
|
|
|
uint64_t total = 0;
|
|
|
|
SMARTLIST_FOREACH_BEGIN(spool, spooled_resource_t *, spooled) {
|
|
|
|
time_t published = TIME_MAX;
|
|
|
|
size_t sz = spooled_resource_estimate_size(spooled, conn,
|
|
|
|
compression, &published);
|
|
|
|
if (published < cutoff) {
|
|
|
|
++n_expired;
|
|
|
|
SMARTLIST_DEL_CURRENT(spool, spooled);
|
|
|
|
spooled_resource_free(spooled);
|
|
|
|
} else if (sz == 0) {
|
|
|
|
SMARTLIST_DEL_CURRENT(spool, spooled);
|
|
|
|
spooled_resource_free(spooled);
|
|
|
|
} else {
|
|
|
|
total += sz;
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(spooled);
|
|
|
|
|
2017-03-28 21:41:59 +02:00
|
|
|
if (size_out) {
|
|
|
|
*size_out = (total > SIZE_MAX) ? SIZE_MAX : (size_t)total;
|
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
if (n_expired_out)
|
|
|
|
*n_expired_out = n_expired;
|
2006-06-20 02:48:23 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** Helper: used to sort a connection's spool. */
|
2006-06-20 02:48:23 +02:00
|
|
|
static int
|
2017-03-13 20:38:20 +01:00
|
|
|
dirserv_spool_sort_comparison_(const void **a_, const void **b_)
|
|
|
|
{
|
|
|
|
const spooled_resource_t *a = *a_;
|
|
|
|
const spooled_resource_t *b = *b_;
|
|
|
|
return fast_memcmp(a->digest, b->digest, sizeof(a->digest));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sort all the entries in <b>conn</b> by digest. */
|
|
|
|
void
|
|
|
|
dirserv_spool_sort(dir_connection_t *conn)
|
|
|
|
{
|
|
|
|
if (conn->spool == NULL)
|
|
|
|
return;
|
|
|
|
smartlist_sort(conn->spool, dirserv_spool_sort_comparison_);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the cache-info for identity fingerprint <b>fp</b>, or
|
|
|
|
* its extra-info document if <b>extrainfo</b> is true. Return
|
|
|
|
* NULL if not found or if the descriptor is older than
|
|
|
|
* <b>publish_cutoff</b>. */
|
|
|
|
static const signed_descriptor_t *
|
|
|
|
get_signed_descriptor_by_fp(const uint8_t *fp, int extrainfo)
|
|
|
|
{
|
|
|
|
if (router_digest_is_me((const char *)fp)) {
|
|
|
|
if (extrainfo)
|
|
|
|
return &(router_get_my_extrainfo()->cache_info);
|
|
|
|
else
|
|
|
|
return &(router_get_my_routerinfo()->cache_info);
|
|
|
|
} else {
|
|
|
|
const routerinfo_t *ri = router_get_by_id_digest((const char *)fp);
|
|
|
|
if (ri) {
|
|
|
|
if (extrainfo)
|
|
|
|
return extrainfo_get_by_descriptor_digest(
|
|
|
|
ri->cache_info.extra_info_digest);
|
|
|
|
else
|
|
|
|
return &ri->cache_info;
|
2006-06-20 02:48:23 +02:00
|
|
|
}
|
2006-06-18 09:38:55 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
return NULL;
|
2006-06-18 09:38:55 +02:00
|
|
|
}
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
/** When we're spooling data onto our outbuf, add more whenever we dip
|
|
|
|
* below this threshold. */
|
|
|
|
#define DIRSERV_BUFFER_MIN 16384
|
|
|
|
|
2017-03-17 16:31:21 +01:00
|
|
|
/**
|
|
|
|
* Called whenever we have flushed some directory data in state
|
|
|
|
* SERVER_WRITING, or whenever we want to fill the buffer with initial
|
|
|
|
* directory data (so that subsequent writes will occur, and trigger this
|
|
|
|
* function again.)
|
|
|
|
*
|
|
|
|
* Return 0 on success, and -1 on failure.
|
|
|
|
*/
|
2006-06-18 09:38:55 +02:00
|
|
|
int
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_dirserv_flushed_some(dir_connection_t *conn)
|
2006-06-18 09:38:55 +02:00
|
|
|
{
|
2012-10-12 18:22:13 +02:00
|
|
|
tor_assert(conn->base_.state == DIR_CONN_STATE_SERVER_WRITING);
|
2017-03-13 20:38:20 +01:00
|
|
|
if (conn->spool == NULL)
|
2006-06-18 09:38:55 +02:00
|
|
|
return 0;
|
|
|
|
|
2017-03-13 20:38:20 +01:00
|
|
|
while (connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN &&
|
|
|
|
smartlist_len(conn->spool)) {
|
|
|
|
spooled_resource_t *spooled =
|
|
|
|
smartlist_get(conn->spool, smartlist_len(conn->spool)-1);
|
|
|
|
spooled_resource_flush_status_t status;
|
|
|
|
status = spooled_resource_flush_some(spooled, conn);
|
|
|
|
if (status == SRFS_ERR) {
|
|
|
|
return -1;
|
|
|
|
} else if (status == SRFS_MORE) {
|
2006-06-18 09:57:47 +02:00
|
|
|
return 0;
|
2017-03-13 20:38:20 +01:00
|
|
|
}
|
|
|
|
tor_assert(status == SRFS_DONE);
|
|
|
|
|
|
|
|
/* If we're here, we're done flushing this resource. */
|
|
|
|
tor_assert(smartlist_pop_last(conn->spool) == spooled);
|
|
|
|
spooled_resource_free(spooled);
|
2006-06-18 09:38:55 +02:00
|
|
|
}
|
2017-03-13 20:38:20 +01:00
|
|
|
|
|
|
|
if (smartlist_len(conn->spool) > 0) {
|
|
|
|
/* We're still spooling something. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we get here, we're done. */
|
|
|
|
smartlist_free(conn->spool);
|
|
|
|
conn->spool = NULL;
|
2017-04-18 01:10:25 +02:00
|
|
|
if (conn->compress_state) {
|
2017-04-18 01:23:39 +02:00
|
|
|
/* Flush the compression state: there could be more bytes pending in there,
|
|
|
|
* and we don't want to omit bytes. */
|
2017-08-08 21:16:39 +02:00
|
|
|
connection_buf_add_compress("", 0, conn, 1);
|
2017-04-18 01:10:25 +02:00
|
|
|
tor_compress_free(conn->compress_state);
|
|
|
|
conn->compress_state = NULL;
|
2017-03-13 20:38:20 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove every element from <b>conn</b>'s outgoing spool, and delete
|
|
|
|
* the spool. */
|
|
|
|
void
|
|
|
|
dir_conn_clear_spool(dir_connection_t *conn)
|
|
|
|
{
|
|
|
|
if (!conn || ! conn->spool)
|
|
|
|
return;
|
|
|
|
SMARTLIST_FOREACH(conn->spool, spooled_resource_t *, s,
|
|
|
|
spooled_resource_free(s));
|
|
|
|
smartlist_free(conn->spool);
|
|
|
|
conn->spool = NULL;
|
2006-06-18 09:38:55 +02:00
|
|
|
}
|
|
|
|
|
2005-06-11 20:52:12 +02:00
|
|
|
/** Release all storage used by the directory server. */
|
2005-02-11 00:18:39 +01:00
|
|
|
void
|
|
|
|
dirserv_free_all(void)
|
|
|
|
{
|
2012-10-12 18:22:13 +02:00
|
|
|
strmap_free(cached_consensuses, free_cached_dir_);
|
2009-12-12 08:07:59 +01:00
|
|
|
cached_consensuses = NULL;
|
2005-02-11 00:18:39 +01:00
|
|
|
}
|