mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-20 10:12:15 +01:00
r16262@catbus: nickm | 2007-10-29 13:21:35 -0400
Patch from Karsten: Code to act as (and use) v2 hidden service directories. svn:r12272
This commit is contained in:
parent
6ad71ec37f
commit
e136f00ca8
@ -188,6 +188,7 @@ static config_var_t _option_vars[] = {
|
||||
V(Group, STRING, NULL),
|
||||
V(HardwareAccel, BOOL, "0"),
|
||||
V(HashedControlPassword, STRING, NULL),
|
||||
V(HidServDirectoryV2, BOOL, "0"),
|
||||
VAR("HiddenServiceDir", LINELIST_S, RendConfigLines, NULL),
|
||||
VAR("HiddenServiceExcludeNodes", LINELIST_S, RendConfigLines, NULL),
|
||||
VAR("HiddenServiceNodes", LINELIST_S, RendConfigLines, NULL),
|
||||
@ -286,8 +287,12 @@ static config_var_t _option_vars[] = {
|
||||
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
|
||||
V(VirtualAddrNetwork, STRING, "127.192.0.0/10"),
|
||||
VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"),
|
||||
VAR("__ConsiderAllRoutersAsHidServDirectories", BOOL,
|
||||
__ConsiderAllRoutersAsHidServDirectories, "0"),
|
||||
VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
|
||||
VAR("__LeaveStreamsUnattached",BOOL, LeaveStreamsUnattached, "0"),
|
||||
VAR("__MinUptimeHidServDirectoryV2", INTERVAL,
|
||||
__MinUptimeHidServDirectoryV2, "24 hours"),
|
||||
|
||||
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
|
||||
};
|
||||
@ -2688,6 +2693,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
REJECT("HSAuthorityRecordStats is set but we're not running as "
|
||||
"a hidden service authority.");
|
||||
|
||||
if (options->HidServDirectoryV2 && !options->DirPort)
|
||||
REJECT("Running as hidden service directory, but no DirPort set.");
|
||||
|
||||
if (options->ConnLimit <= 0) {
|
||||
r = tor_snprintf(buf, sizeof(buf),
|
||||
"ConnLimit must be greater than 0, but was set to %d",
|
||||
@ -2820,6 +2828,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (options->__MinUptimeHidServDirectoryV2 < 0) {
|
||||
log_warn(LD_CONFIG, "__MinUptimeHidServDirectoryV2 option must be at "
|
||||
"least 0 seconds. Changing to 0.");
|
||||
options->__MinUptimeHidServDirectoryV2 = 0;
|
||||
}
|
||||
|
||||
if (options->RendPostPeriod < MIN_REND_POST_PERIOD) {
|
||||
log(LOG_WARN,LD_CONFIG,"RendPostPeriod option must be at least %d seconds."
|
||||
" Clipping.", MIN_REND_POST_PERIOD);
|
||||
|
@ -157,6 +157,10 @@ dir_conn_purpose_to_string(int purpose)
|
||||
return "status vote fetch";
|
||||
case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
|
||||
return "consensus signature fetch";
|
||||
case DIR_PURPOSE_FETCH_RENDDESC_V2:
|
||||
return "hidden-service v2 descriptor fetch";
|
||||
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
|
||||
return "hidden-service v2 descriptor upload";
|
||||
}
|
||||
|
||||
log_warn(LD_BUG, "Called with unknown purpose %d", purpose);
|
||||
@ -422,7 +426,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
|
||||
* upload or download a server or rendezvous
|
||||
* descriptor. <b>dir_purpose</b> determines what
|
||||
* kind of directory connection we're launching, and must be one of
|
||||
* DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}. <b>router_purpose</b>
|
||||
* DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2}. <b>router_purpose</b>
|
||||
* specifies the descriptor purposes we have in mind (currently only
|
||||
* used for FETCH_DIR).
|
||||
*
|
||||
@ -867,12 +871,27 @@ directory_send_command(dir_connection_t *conn,
|
||||
url = tor_malloc(len);
|
||||
tor_snprintf(url, len, "/tor/rendezvous/%s", resource);
|
||||
break;
|
||||
case DIR_PURPOSE_FETCH_RENDDESC_V2:
|
||||
tor_assert(resource);
|
||||
tor_assert(!payload);
|
||||
tor_assert(strlen(resource) <= REND_DESC_ID_V2_BASE32);
|
||||
httpcommand = "GET";
|
||||
len = strlen(resource) + 32;
|
||||
url = tor_malloc(len);
|
||||
tor_snprintf(url, len, "/tor/rendezvous2/%s", resource);
|
||||
break;
|
||||
case DIR_PURPOSE_UPLOAD_RENDDESC:
|
||||
tor_assert(!resource);
|
||||
tor_assert(payload);
|
||||
httpcommand = "POST";
|
||||
url = tor_strdup("/tor/rendezvous/publish");
|
||||
break;
|
||||
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
|
||||
tor_assert(!resource);
|
||||
tor_assert(payload);
|
||||
httpcommand = "POST";
|
||||
url = tor_strdup("/tor/rendezvous2/publish");
|
||||
break;
|
||||
default:
|
||||
tor_assert(0);
|
||||
return;
|
||||
@ -1721,8 +1740,46 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
|
||||
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d (%s))",
|
||||
if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) {
|
||||
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
|
||||
"(%s))",
|
||||
(int)body_len, status_code, escaped(reason));
|
||||
switch (status_code) {
|
||||
case 200:
|
||||
if (rend_cache_store_v2_client(body, NULL) < 0) {
|
||||
log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed.");
|
||||
/* alice's ap_stream will notice when connection_mark_for_close
|
||||
* cleans it up */
|
||||
} else {
|
||||
/* success. notify pending connections about this. */
|
||||
log_info(LD_REND, "Successfully fetched rendezvous descriptor.");
|
||||
conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
|
||||
rend_client_desc_here(conn->rend_query);
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
/* not there. pending connections will be notified when
|
||||
* connection_mark_for_close cleans it up. */
|
||||
break;
|
||||
case 400:
|
||||
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
|
||||
"http status 400 (%s). Dirserver didn't like our "
|
||||
"v2 rendezvous query?", escaped(reason));
|
||||
break;
|
||||
default:
|
||||
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
|
||||
"http status %d (%s) response unexpected while "
|
||||
"fetching v2 hidden service descriptor (server '%s:%d').",
|
||||
status_code, escaped(reason), conn->_base.address,
|
||||
conn->_base.port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC ||
|
||||
conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
|
||||
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
|
||||
"(%s))",
|
||||
status_code, escaped(reason));
|
||||
switch (status_code) {
|
||||
case 200:
|
||||
@ -2428,6 +2485,32 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (options->HidServDirectoryV2 &&
|
||||
!strcmpstart(url,"/tor/rendezvous2/")) {
|
||||
/* Handle v2 rendezvous descriptor fetch request. */
|
||||
char *descp;
|
||||
const char *query = url + strlen("/tor/rendezvous2/");
|
||||
if (strlen(query) == REND_DESC_ID_V2_BASE32) {
|
||||
log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
|
||||
query);
|
||||
switch (rend_cache_lookup_v2_dir(query, &descp)) {
|
||||
case 1: /* valid */
|
||||
write_http_response_header(conn, strlen(descp), 0, 0);
|
||||
connection_write_to_buf(descp, strlen(descp), TO_CONN(conn));
|
||||
break;
|
||||
case 0: /* well-formed but not present */
|
||||
write_http_status_line(conn, 404, "Not found");
|
||||
break;
|
||||
case -1: /* not well-formed */
|
||||
write_http_status_line(conn, 400, "Bad request");
|
||||
break;
|
||||
}
|
||||
} else { /* not well-formed */
|
||||
write_http_status_line(conn, 400, "Bad request");
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (options->HSAuthoritativeDir && !strcmpstart(url,"/tor/rendezvous/")) {
|
||||
/* rendezvous descriptor fetch */
|
||||
const char *descp;
|
||||
@ -2546,6 +2629,27 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers,
|
||||
|
||||
conn->_base.state = DIR_CONN_STATE_SERVER_WRITING;
|
||||
|
||||
if (parse_http_url(headers, &url) < 0) {
|
||||
write_http_status_line(conn, 400, "Bad request");
|
||||
return 0;
|
||||
}
|
||||
log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
|
||||
|
||||
/* Handle v2 rendezvous service publish request. */
|
||||
if (options->HidServDirectoryV2 &&
|
||||
!strcmpstart(url,"/tor/rendezvous2/publish")) {
|
||||
if (rend_cache_store_v2_dir(body) < 0) {
|
||||
log_warn(LD_REND, "Rejected rend descriptor (length %d) from %s.",
|
||||
(int)body_len, conn->_base.address);
|
||||
write_http_status_line(conn, 400, "Invalid service descriptor rejected");
|
||||
log_info(LD_REND, "Handled v2 rendezvous descriptor post: rejected");
|
||||
} else {
|
||||
write_http_status_line(conn, 200, "Service descriptor stored");
|
||||
log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!authdir_mode(options)) {
|
||||
/* we just provide cached directories; we don't want to
|
||||
* receive anything. */
|
||||
@ -2554,12 +2658,6 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parse_http_url(headers, &url) < 0) {
|
||||
write_http_status_line(conn, 400, "Bad request");
|
||||
return 0;
|
||||
}
|
||||
log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
|
||||
|
||||
if (authdir_mode_handles_descs(options) &&
|
||||
!strcmp(url,"/tor/")) { /* server descriptor post */
|
||||
const char *msg = NULL;
|
||||
@ -2948,3 +3046,105 @@ dir_split_resource_into_fingerprints(const char *resource,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Determine the responsible hidden service directories for
|
||||
* <b>desc_ids</b> and upload the appropriate descriptor from
|
||||
* <b>desc_strs</b> to them; each smartlist must contain
|
||||
* REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS entries; <b>service_id</b> and
|
||||
* <b>seconds_valid</b> are only passed for logging purposes.*/
|
||||
/* XXXX020 enable tunneling when available!! */
|
||||
void
|
||||
directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *desc_strs,
|
||||
const char *service_id, int seconds_valid,
|
||||
smartlist_t *hs_dirs_)
|
||||
{
|
||||
int i, j;
|
||||
smartlist_t *responsible_dirs;
|
||||
routerinfo_t *hs_dir;
|
||||
if (smartlist_len(desc_ids) != REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS ||
|
||||
smartlist_len(desc_strs) != REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) {
|
||||
log_warn(LD_REND, "Could not post descriptors to hidden service "
|
||||
"directories: Illegal number of descriptor "
|
||||
"IDs/strings");
|
||||
return;
|
||||
}
|
||||
responsible_dirs = smartlist_create();
|
||||
for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
|
||||
const char *desc_id = smartlist_get(desc_ids, i);
|
||||
const char *desc_str = smartlist_get(desc_strs, i);
|
||||
/* Determine responsible dirs. */
|
||||
if (hid_serv_get_responsible_directories(responsible_dirs, desc_id,
|
||||
hs_dirs_) < 0) {
|
||||
log_warn(LD_REND, "Could not determine the responsible hidden service "
|
||||
"directories to post descriptors to.");
|
||||
smartlist_free(responsible_dirs);
|
||||
return;
|
||||
}
|
||||
for (j = 0; j < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; j++) {
|
||||
char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
hs_dir = smartlist_get(responsible_dirs, j);
|
||||
/* Send publish request. */
|
||||
directory_initiate_command(hs_dir->address, hs_dir->addr,
|
||||
hs_dir->or_port, hs_dir->dir_port, 0,
|
||||
hs_dir->cache_info.identity_digest,
|
||||
DIR_PURPOSE_UPLOAD_RENDDESC_V2,
|
||||
ROUTER_PURPOSE_GENERAL,
|
||||
1, NULL, desc_str, strlen(desc_str), 0);
|
||||
base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
|
||||
desc_id, DIGEST_LEN);
|
||||
log_info(LD_REND, "Sending publish request for v2 descriptor for "
|
||||
"service '%s' with descriptor ID '%s' with validity "
|
||||
"of %d seconds to hidden service directory '%s' on "
|
||||
"port %d.",
|
||||
service_id,
|
||||
desc_id_base32,
|
||||
seconds_valid,
|
||||
hs_dir->nickname,
|
||||
hs_dir->dir_port);
|
||||
}
|
||||
smartlist_clear(responsible_dirs);
|
||||
}
|
||||
smartlist_free(responsible_dirs);
|
||||
}
|
||||
|
||||
/** Determine the responsible hidden service directories for <b>desc_id</b>
|
||||
* and fetch the descriptor belonging to this ID from one of them;
|
||||
* <b>query</b> is only passed for pretty log statements.
|
||||
* XXXX020 enable tunneling when available!! */
|
||||
void
|
||||
directory_get_from_hs_dir(const char *desc_id, const char *query,
|
||||
smartlist_t *hs_dirs_)
|
||||
{
|
||||
smartlist_t *responsible_dirs = smartlist_create();
|
||||
routerinfo_t *hs_dir;
|
||||
char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
int replica;
|
||||
tor_assert(desc_id);
|
||||
tor_assert(query);
|
||||
tor_assert(strlen(query) == REND_SERVICE_ID_LEN);
|
||||
/* Determine responsible dirs. */
|
||||
if (hid_serv_get_responsible_directories(responsible_dirs, desc_id,
|
||||
hs_dirs_) < 0) {
|
||||
log_warn(LD_REND, "Could not determine the responsible hidden service "
|
||||
"directories to fetch descriptors.");
|
||||
smartlist_free(responsible_dirs);
|
||||
return;
|
||||
}
|
||||
replica = crypto_rand_int(REND_NUMBER_OF_CONSECUTIVE_REPLICAS);
|
||||
hs_dir = smartlist_get(responsible_dirs, replica);
|
||||
/* XXXX020 if hsdir fails, use another one... */
|
||||
base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
|
||||
desc_id, DIGEST_LEN);
|
||||
/* Send fetch request. */
|
||||
directory_initiate_command(hs_dir->address, hs_dir->addr,
|
||||
hs_dir->or_port, hs_dir->dir_port, 0,
|
||||
hs_dir->cache_info.identity_digest,
|
||||
DIR_PURPOSE_FETCH_RENDDESC_V2,
|
||||
ROUTER_PURPOSE_GENERAL,
|
||||
1, desc_id_base32, NULL, 0, 0);
|
||||
log_info(LD_REND, "Sending fetch request for v2 descriptor for "
|
||||
"service '%s' with descriptor ID '%s' to hidden "
|
||||
"service directory '%s' on port %d.",
|
||||
query, desc_id_base32, hs_dir->nickname, hs_dir->dir_port);
|
||||
smartlist_free(responsible_dirs);
|
||||
}
|
||||
|
||||
|
@ -1526,6 +1526,21 @@ dirserv_thinks_router_is_unreliable(time_t now,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return true if <b>router</b> has an uptime of at least
|
||||
* <b>__MinUptimeHidServDirectoryV2</b> and is reachable in the last
|
||||
* REND_HS_DIR_REACHABLE_TIMEOUT seconds, else false.
|
||||
*/
|
||||
static int
|
||||
dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
|
||||
{
|
||||
int uptime = real_uptime(router, now);
|
||||
|
||||
return (router->wants_to_be_hs_dir &&
|
||||
uptime > get_options()->__MinUptimeHidServDirectoryV2 &&
|
||||
((router_is_me(router) && !we_are_hibernating()) ||
|
||||
(now < router->last_reachable + REND_HS_DIR_REACHABLE_TIMEOUT)));
|
||||
}
|
||||
|
||||
/** Look through the routerlist, and assign the median uptime of running valid
|
||||
* servers to stable_uptime, and the relative bandwidth capacities to
|
||||
* fast_bandwidth and guard_bandwidth. Set total_bandwidth to the total
|
||||
@ -1674,13 +1689,14 @@ routerstatus_format_entry(char *buf, size_t buf_len,
|
||||
return 0;
|
||||
cp = buf + strlen(buf);
|
||||
r = tor_snprintf(cp, buf_len - (cp-buf),
|
||||
"s%s%s%s%s%s%s%s%s%s%s%s\n",
|
||||
"s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
||||
/* These must stay in alphabetical order. */
|
||||
rs->is_authority?" Authority":"",
|
||||
rs->is_bad_exit?" BadExit":"",
|
||||
rs->is_exit?" Exit":"",
|
||||
rs->is_fast?" Fast":"",
|
||||
rs->is_possible_guard?" Guard":"",
|
||||
rs->is_hs_dir?" HSDir":"",
|
||||
rs->is_named?" Named":"",
|
||||
rs->is_running?" Running":"",
|
||||
rs->is_stable?" Stable":"",
|
||||
@ -1843,6 +1859,10 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
|
||||
rs->is_possible_guard = 0;
|
||||
}
|
||||
rs->is_bad_exit = listbadexits && ri->is_bad_exit;
|
||||
ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
|
||||
if (get_options()->__ConsiderAllRoutersAsHidServDirectories)
|
||||
ri->is_hs_dir = 1; /* Override real value. */
|
||||
rs->is_hs_dir = ri->is_hs_dir;
|
||||
/* 0.1.1.9-alpha is the first version to support fetch by descriptor
|
||||
* hash. */
|
||||
rs->is_v2_dir = ri->dir_port &&
|
||||
@ -1992,7 +2012,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
|
||||
v3_out->server_versions = server_versions;
|
||||
v3_out->known_flags = smartlist_create();
|
||||
smartlist_split_string(v3_out->known_flags,
|
||||
"Authority Exit Fast Guard Running Stable V2Dir Valid",
|
||||
"Authority Exit Fast Guard HSDir Running Stable V2Dir Valid",
|
||||
0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||
if (listbadexits)
|
||||
smartlist_add(v3_out->known_flags, tor_strdup("BadExit"));
|
||||
|
@ -1576,6 +1576,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
|
||||
router->is_possible_guard = rs->is_possible_guard;
|
||||
router->is_exit = rs->is_exit;
|
||||
router->is_bad_exit = rs->is_bad_exit;
|
||||
router->is_hs_dir = rs->is_hs_dir;
|
||||
}
|
||||
if (router->is_running && ds) {
|
||||
download_status_reset(&ds->v2_ns_dl_status);
|
||||
|
51
src/or/or.h
51
src/or/or.h
@ -351,7 +351,13 @@ typedef enum {
|
||||
|
||||
/** Purpose for connection at a directory server. */
|
||||
#define DIR_PURPOSE_SERVER 16
|
||||
#define _DIR_PURPOSE_MAX 16
|
||||
/** A connection to a hidden service directory server: upload a v2 rendezvous
|
||||
* descriptor. */
|
||||
#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 17
|
||||
/** A connection to a hidden service directory server: download a v2 rendezvous
|
||||
* descriptor. */
|
||||
#define DIR_PURPOSE_FETCH_RENDDESC_V2 18
|
||||
#define _DIR_PURPOSE_MAX 18
|
||||
|
||||
#define _EXIT_PURPOSE_MIN 1
|
||||
/** This exit stream wants to do an ordinary connect. */
|
||||
@ -608,6 +614,14 @@ typedef enum {
|
||||
/** Length of v2 descriptor ID (32 base32 chars = 160 bits). */
|
||||
#define REND_DESC_ID_V2_BASE32 32
|
||||
|
||||
/** Length of the base32-encoded secret ID part of versioned hidden service
|
||||
* descriptors. */
|
||||
#define REND_SECRET_ID_PART_LEN_BASE32 32
|
||||
|
||||
/** Length of the base32-encoded hash of an introduction point's
|
||||
* identity key. */
|
||||
#define REND_INTRO_POINT_ID_LEN_BASE32 32
|
||||
|
||||
#define CELL_DIRECTION_IN 1
|
||||
#define CELL_DIRECTION_OUT 2
|
||||
|
||||
@ -1197,6 +1211,11 @@ typedef struct {
|
||||
unsigned int is_exit:1; /**< Do we think this is an OK exit? */
|
||||
unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
|
||||
* or otherwise nasty? */
|
||||
unsigned int wants_to_be_hs_dir:1; /**< True iff this router has set a flag
|
||||
to possibly act as hidden service
|
||||
directory. */
|
||||
unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
|
||||
* directory. */
|
||||
|
||||
/** Tor can use this router for general positions in circuits. */
|
||||
#define ROUTER_PURPOSE_GENERAL 0
|
||||
@ -1269,6 +1288,8 @@ typedef struct routerstatus_t {
|
||||
* an exit node. */
|
||||
unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
|
||||
* underpowered, or otherwise useless? */
|
||||
unsigned int is_hs_dir:1; /** True iff this router is a hidden service
|
||||
* directory. */
|
||||
/** True iff we know version info for this router. (i.e., a "v" entry was
|
||||
* included.) We'll replace all these with a big tor_version_t or a char[]
|
||||
* if the number of traits we care about ever becomes incredibly big. */
|
||||
@ -1998,6 +2019,10 @@ typedef struct {
|
||||
int PublishHidServDescriptors;
|
||||
int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */
|
||||
int FetchHidServDescriptors; /** and hidden service descriptors? */
|
||||
int HidServDirectoryV2; /**< Do we act as hs dir? */
|
||||
int __MinUptimeHidServDirectoryV2; /**< Accept hs dirs after what time? */
|
||||
int __ConsiderAllRoutersAsHidServDirectories; /**< Consider all routers as
|
||||
* hidden service dirs? */
|
||||
int FetchUselessDescriptors; /**< Do we fetch non-running descriptors too? */
|
||||
int AllDirActionsPrivate; /**< Should every directory action be sent
|
||||
* through a Tor circuit? */
|
||||
@ -2849,6 +2874,12 @@ int dir_split_resource_into_fingerprints(const char *resource,
|
||||
char *directory_dump_request_log(void);
|
||||
int router_supports_extrainfo(const char *identity_digest, int is_authority);
|
||||
|
||||
void directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *descs,
|
||||
const char *service_id, int seconds_valid,
|
||||
smartlist_t *hs_dirs);
|
||||
void directory_get_from_hs_dir(const char *desc_id, const char *query,
|
||||
smartlist_t *hs_dirs);
|
||||
|
||||
time_t download_status_increment_failure(download_status_t *dls,
|
||||
int status_code, const char *item,
|
||||
int server, time_t now);
|
||||
@ -3422,13 +3453,19 @@ typedef struct rend_cache_entry_t {
|
||||
|
||||
void rend_cache_init(void);
|
||||
void rend_cache_clean(void);
|
||||
void rend_cache_clean_up(void);
|
||||
void rend_cache_clean_v2_dir(void);
|
||||
void rend_cache_free_all(void);
|
||||
int rend_valid_service_id(const char *query);
|
||||
int rend_cache_lookup_desc(const char *query, int version, const char **desc,
|
||||
size_t *desc_len);
|
||||
int rend_cache_lookup_entry(const char *query, int version,
|
||||
rend_cache_entry_t **entry_out);
|
||||
int rend_cache_lookup_v2_dir(const char *query, char **desc);
|
||||
int rend_cache_store(const char *desc, size_t desc_len, int published);
|
||||
int rend_cache_store_v2_client(const char *desc,
|
||||
const char *descriptor_cookie);
|
||||
int rend_cache_store_v2_dir(const char *desc);
|
||||
int rend_cache_size(void);
|
||||
int rend_encode_v2_descriptors(smartlist_t *desc_strs_out,
|
||||
smartlist_t *desc_ids_out,
|
||||
@ -3702,6 +3739,18 @@ void routerlist_assert_ok(routerlist_t *rl);
|
||||
const char *esc_router_info(routerinfo_t *router);
|
||||
void routers_sort_by_identity(smartlist_t *routers);
|
||||
|
||||
smartlist_t *hid_serv_create_routing_table(void);
|
||||
int hid_serv_have_enough_directories(smartlist_t *hs_dirs);
|
||||
int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
|
||||
const char *id,
|
||||
smartlist_t *hs_dirs);
|
||||
routerinfo_t *hid_serv_next_directory(const char *id,
|
||||
smartlist_t *hs_dirs);
|
||||
routerinfo_t *hid_serv_previous_directory(const char *id,
|
||||
smartlist_t *hs_dirs);
|
||||
int hid_serv_acting_as_directory(smartlist_t *hs_dirs);
|
||||
int hid_serv_responsible_for_desc_id(const char *id, smartlist_t *hs_dirs);
|
||||
|
||||
/********************************* routerparse.c ************************/
|
||||
|
||||
#define MAX_STATUS_TAG_LEN 32
|
||||
|
@ -60,25 +60,13 @@ rend_service_descriptor_free(rend_service_descriptor_t *desc)
|
||||
/*XXXX020 Rename to include "len" and maybe not "binary" */
|
||||
#define REND_SERVICE_ID_BINARY 10
|
||||
|
||||
/** Length of the time period that is used to encode the secret ID part of
|
||||
* versioned hidden service descriptors. */
|
||||
/*XXXX020 Rename to include "len" and maybe not "binary" */
|
||||
#define REND_TIME_PERIOD_BINARY 4
|
||||
|
||||
/** Length of the descriptor cookie that is used for versioned hidden
|
||||
* service descriptors. */
|
||||
/* XXXX020 rename to REND_DESC_COOKIE_(BINARY_)LEN */
|
||||
#define REND_DESC_COOKIE_BINARY 16
|
||||
#define REND_DESC_COOKIE_LEN 16
|
||||
|
||||
/** Length of the replica number that is used to determine the secret ID
|
||||
* part of versioned hidden service descriptors. */
|
||||
/* XXXX020 rename to REND_REPLICA_(BINARY_)LEN */
|
||||
#define REND_REPLICA_BINARY 1
|
||||
|
||||
/** Length of the base32-encoded secret ID part of versioned hidden service
|
||||
* descriptors. */
|
||||
/*XXXX020 Rename to include "len" */
|
||||
#define REND_SECRET_ID_PART_BASE32 32
|
||||
#define REND_REPLICA_LEN 1
|
||||
|
||||
/** Compute the descriptor ID for <b>service_id</b> of length
|
||||
* <b>REND_SERVICE_ID_BINARY</b> and <b>secret_id_part</b> of length
|
||||
@ -98,7 +86,7 @@ rend_get_descriptor_id_bytes(char *descriptor_id_out,
|
||||
|
||||
/** Compute the secret ID part for time_period,
|
||||
* a <b>descriptor_cookie</b> of length
|
||||
* <b>REND_DESC_COOKIE_BINARY</b> which may also be <b>NULL</b> if no
|
||||
* <b>REND_DESC_COOKIE_LEN</b> which may also be <b>NULL</b> if no
|
||||
* descriptor_cookie shall be used, and <b>replica</b>, and write it to
|
||||
* <b>secret_id_part</b> of length DIGEST_LEN. */
|
||||
static void
|
||||
@ -110,9 +98,9 @@ get_secret_id_part_bytes(char *secret_id_part, uint32_t time_period,
|
||||
crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t));
|
||||
if (descriptor_cookie) {
|
||||
crypto_digest_add_bytes(digest, descriptor_cookie,
|
||||
REND_DESC_COOKIE_BINARY);
|
||||
REND_DESC_COOKIE_LEN);
|
||||
}
|
||||
crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_BINARY);
|
||||
crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN);
|
||||
crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN);
|
||||
crypto_free_digest_env(digest);
|
||||
}
|
||||
@ -146,7 +134,7 @@ get_seconds_valid(time_t now, const char *service_id)
|
||||
|
||||
/** Compute the binary <b>desc_id_out</b> (DIGEST_LEN bytes long) for a given
|
||||
* base32-encoded <b>service_id</b> and optional unencoded
|
||||
* <b>descriptor_cookie</b> of length REND_DESC_COOKIE_BINARY,
|
||||
* <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
|
||||
* at time <b>now</b> for replica number
|
||||
* <b>replica</b>. <b>desc_id</b> needs to have <b>DIGEST_LEN</b> bytes
|
||||
* free. Return 0 for success, -1 otherwise. */
|
||||
@ -188,7 +176,7 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
|
||||
}
|
||||
|
||||
/* Encode the introduction points in <b>desc</b>, optionally encrypt them with
|
||||
* an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_BINARY,
|
||||
* an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
|
||||
* encode it in base64, and write it to a newly allocated string, and write a
|
||||
* pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1
|
||||
* otherwise. */
|
||||
@ -207,7 +195,7 @@ rend_encode_v2_intro_points(char **ipos_base64,
|
||||
unenc_len = desc->n_intro_points * 1000; /* too long, but ok. */
|
||||
unenc = tor_malloc_zero(unenc_len);
|
||||
for (i = 0; i < desc->n_intro_points; i++) {
|
||||
char id_base32[32 + 1]; /*XXXX020 should be a macro */
|
||||
char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1];
|
||||
char *onion_key = NULL;
|
||||
size_t onion_key_len;
|
||||
crypto_pk_env_t *intro_key;
|
||||
@ -370,7 +358,7 @@ rend_encode_v2_descriptors(smartlist_t *desc_strs_out,
|
||||
/* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
|
||||
for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
|
||||
char secret_id_part[DIGEST_LEN];
|
||||
char secret_id_part_base32[REND_SECRET_ID_PART_BASE32 + 1];
|
||||
char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1];
|
||||
char *desc_id;
|
||||
char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
char *permanent_key = NULL;
|
||||
@ -387,7 +375,7 @@ rend_encode_v2_descriptors(smartlist_t *desc_strs_out,
|
||||
/* Calculate secret-id-part = h(time-period + cookie + replica). */
|
||||
get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
|
||||
k);
|
||||
base32_encode(secret_id_part_base32, REND_SECRET_ID_PART_BASE32 + 1,
|
||||
base32_encode(secret_id_part_base32, REND_SECRET_ID_PART_LEN_BASE32 + 1,
|
||||
secret_id_part, DIGEST_LEN);
|
||||
/* Calculate descriptor ID. */
|
||||
desc_id = tor_malloc_zero(DIGEST_LEN);
|
||||
@ -628,12 +616,17 @@ rend_get_service_id(crypto_pk_env_t *pk, char *out)
|
||||
* rend_cache_entry_t. */
|
||||
static strmap_t *rend_cache = NULL;
|
||||
|
||||
/** Map from descriptor id to rend_cache_entry_t; only for hidden service
|
||||
* directories. */
|
||||
static digestmap_t *rend_cache_v2_dir = NULL;
|
||||
|
||||
/** Initializes the service descriptor cache.
|
||||
*/
|
||||
void
|
||||
rend_cache_init(void)
|
||||
{
|
||||
rend_cache = strmap_new();
|
||||
rend_cache_v2_dir = digestmap_new();
|
||||
}
|
||||
|
||||
/** Helper: free storage held by a single service descriptor cache entry. */
|
||||
@ -651,7 +644,9 @@ void
|
||||
rend_cache_free_all(void)
|
||||
{
|
||||
strmap_free(rend_cache, _rend_cache_entry_free);
|
||||
digestmap_free(rend_cache_v2_dir, _rend_cache_entry_free);
|
||||
rend_cache = NULL;
|
||||
rend_cache_v2_dir = NULL;
|
||||
}
|
||||
|
||||
/** Removes all old entries from the service descriptor cache.
|
||||
@ -677,6 +672,88 @@ rend_cache_clean(void)
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove all old entries on v2 hidden service directories. */
|
||||
void
|
||||
rend_cache_clean_v2_dir(void)
|
||||
{
|
||||
digestmap_iter_t *iter;
|
||||
const char *key;
|
||||
void *val;
|
||||
rend_cache_entry_t *ent;
|
||||
time_t cutoff;
|
||||
cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
|
||||
for (iter = digestmap_iter_init(rend_cache_v2_dir);
|
||||
!digestmap_iter_done(iter); ) {
|
||||
digestmap_iter_get(iter, &key, &val);
|
||||
ent = (rend_cache_entry_t*)val;
|
||||
if (ent->parsed->timestamp < cutoff) {
|
||||
char key_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
base32_encode(key_base32, REND_DESC_ID_V2_BASE32 + 1, key, DIGEST_LEN);
|
||||
log_info(LD_REND, "Removing descriptor with ID '%s' from cache, "
|
||||
"because it is too old!",
|
||||
key_base32);
|
||||
iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
|
||||
_rend_cache_entry_free(ent);
|
||||
} else {
|
||||
iter = digestmap_iter_next(rend_cache_v2_dir, iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and
|
||||
* <b>c</b> (included) in a circular digest ring; returns 1 if this is the
|
||||
* case, and 0 otherwise.
|
||||
*/
|
||||
int
|
||||
rend_id_is_in_interval(const char *a, const char *b, const char *c)
|
||||
{
|
||||
tor_assert(a);
|
||||
tor_assert(b);
|
||||
tor_assert(c);
|
||||
/* There are five cases in which a is outside the interval ]b,c]: */
|
||||
if ((memcmp(a, b, DIGEST_LEN) == 0) || /* 1. a == b (b is excluded) */
|
||||
/* 2. b == c (interval is empty) */
|
||||
(memcmp(b, c, DIGEST_LEN) == 0) ||
|
||||
/* 3. a b c */
|
||||
(memcmp(a, b, DIGEST_LEN) <= 0 && memcmp(b, c, DIGEST_LEN) < 0) ||
|
||||
/* 4. c a b */
|
||||
(memcmp(c, a, DIGEST_LEN) < 0 && memcmp(a, b, DIGEST_LEN) <= 0) ||
|
||||
/* 5. b c a */
|
||||
(memcmp(b, c, DIGEST_LEN) < 0 && memcmp(c, a, DIGEST_LEN) < 0))
|
||||
return 0;
|
||||
/* In the other cases, a is inside the interval. */
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Clean up all values for which this node as hidden service directory is
|
||||
* not responsible */
|
||||
void
|
||||
rend_cache_clean_up(void)
|
||||
{
|
||||
digestmap_iter_t *iter;
|
||||
const char *key;
|
||||
void *val;
|
||||
rend_cache_entry_t *ent;
|
||||
smartlist_t *hs_dirs = hid_serv_create_routing_table();
|
||||
for (iter = digestmap_iter_init(rend_cache_v2_dir);
|
||||
!digestmap_iter_done(iter); ) {
|
||||
digestmap_iter_get(iter, &key, &val);
|
||||
ent = (rend_cache_entry_t*)val;
|
||||
if (!hid_serv_responsible_for_desc_id(key, hs_dirs)) {
|
||||
char key_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
base32_encode(key_base32, REND_DESC_ID_V2_BASE32 + 1, key, DIGEST_LEN);
|
||||
log_info(LD_REND, "Removing descriptor with ID '%s' from cache, "
|
||||
"because we are not reponsible for it!", key_base32);
|
||||
iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
|
||||
_rend_cache_entry_free(ent);
|
||||
} else {
|
||||
iter = digestmap_iter_next(rend_cache_v2_dir, iter);
|
||||
}
|
||||
}
|
||||
smartlist_free(hs_dirs);
|
||||
}
|
||||
|
||||
/** Return true iff <b>query</b> is a syntactically valid service ID (as
|
||||
* generated by rend_get_service_id). */
|
||||
int
|
||||
@ -731,6 +808,41 @@ rend_cache_lookup_desc(const char *query, int version, const char **desc,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
|
||||
* copy the pointer to it to <b>desc</b>.
|
||||
*/
|
||||
int
|
||||
rend_cache_lookup_v2_dir(const char *desc_id, char **desc)
|
||||
{
|
||||
rend_cache_entry_t *e;
|
||||
char desc_id_digest[DIGEST_LEN];
|
||||
smartlist_t *hs_dirs;
|
||||
tor_assert(rend_cache_v2_dir);
|
||||
if (base32_decode(desc_id_digest, DIGEST_LEN,
|
||||
desc_id, REND_DESC_ID_V2_BASE32) < 0) {
|
||||
log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
|
||||
desc_id);
|
||||
return -1;
|
||||
}
|
||||
/* Determine if we are responsible. */
|
||||
hs_dirs = hid_serv_create_routing_table();
|
||||
if (hid_serv_responsible_for_desc_id(desc_id_digest, hs_dirs) < 0) {
|
||||
log_info(LD_REND, "Could not answer fetch request for v2 descriptor; "
|
||||
"either we are no hidden service directory, or we are "
|
||||
"not responsible for the requested ID.");
|
||||
smartlist_free(hs_dirs);
|
||||
return -1;
|
||||
}
|
||||
smartlist_free(hs_dirs);
|
||||
/* Lookup descriptor and return. */
|
||||
e = (rend_cache_entry_t*) digestmap_get(rend_cache_v2_dir, desc_id_digest);
|
||||
if (e) {
|
||||
*desc = e->desc;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Parse *desc, calculate its service id, and store it in the cache.
|
||||
* If we have a newer descriptor with the same ID, ignore this one.
|
||||
* If we have an older descriptor with the same ID, replace it.
|
||||
@ -813,6 +925,218 @@ rend_cache_store(const char *desc, size_t desc_len, int published)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the
|
||||
* local rend cache. Don't attempt to decrypt the included list of introduction
|
||||
* points (as we don't have a descriptor cookie for it).
|
||||
*
|
||||
* If we have a newer descriptor with the same ID, ignore this one.
|
||||
* If we have an older descriptor with the same ID, replace it.
|
||||
* Return -1 if it's malformed or otherwise rejected; return 0 if
|
||||
* it's the same or older than one we've already got; return 1 if
|
||||
* it's novel.
|
||||
*/
|
||||
int
|
||||
rend_cache_store_v2_dir(const char *desc)
|
||||
{
|
||||
rend_service_descriptor_t *parsed;
|
||||
char desc_id[DIGEST_LEN];
|
||||
char *intro_content;
|
||||
size_t intro_size;
|
||||
size_t encoded_size;
|
||||
char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
int number_stored = 0;
|
||||
const char *current_desc = desc;
|
||||
const char *next_desc;
|
||||
rend_cache_entry_t *e;
|
||||
time_t now = time(NULL);
|
||||
smartlist_t *hs_dirs = hid_serv_create_routing_table();
|
||||
tor_assert(rend_cache_v2_dir);
|
||||
tor_assert(desc);
|
||||
if (!hid_serv_acting_as_directory(hs_dirs)) {
|
||||
/* Cannot store descs, because we are (currently) not acting as
|
||||
* hidden service directory. */
|
||||
log_info(LD_REND, "Cannot store descs: Not acting as hs dir");
|
||||
smartlist_free(hs_dirs);
|
||||
return -1;
|
||||
}
|
||||
while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
|
||||
&intro_size, &encoded_size,
|
||||
&next_desc, current_desc) >= 0) {
|
||||
/* We don't care about the introduction points. */
|
||||
tor_free(intro_content);
|
||||
/* For pretty log statements. */
|
||||
base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
|
||||
desc_id, DIGEST_LEN);
|
||||
/* Is desc ID in the range that we are (directly or indirectly) responsible
|
||||
* for? */
|
||||
if (!hid_serv_responsible_for_desc_id(desc_id, hs_dirs)) {
|
||||
log_info(LD_REND, "Service descriptor with desc ID %s is not in "
|
||||
"interval that we are responsible for.",
|
||||
desc_id_base32);
|
||||
rend_service_descriptor_free(parsed);
|
||||
goto skip;
|
||||
}
|
||||
/* Is descriptor too old? */
|
||||
if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
|
||||
log_info(LD_REND, "Service descriptor with desc ID %s is too old.",
|
||||
desc_id_base32);
|
||||
rend_service_descriptor_free(parsed);
|
||||
goto skip;
|
||||
}
|
||||
/* Is descriptor too far in the future? */
|
||||
if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
|
||||
log_info(LD_REND, "Service descriptor with desc ID %s is too far in the "
|
||||
"future.",
|
||||
desc_id_base32);
|
||||
rend_service_descriptor_free(parsed);
|
||||
goto skip;
|
||||
}
|
||||
/* Do we already have a newer descriptor? */
|
||||
e = (rend_cache_entry_t *)digestmap_get(rend_cache_v2_dir, desc_id);
|
||||
if (e && e->parsed->timestamp > parsed->timestamp) {
|
||||
log_info(LD_REND, "We already have a newer service descriptor with the "
|
||||
"same desc ID %s and version.", desc_id_base32);
|
||||
rend_service_descriptor_free(parsed);
|
||||
goto skip;
|
||||
}
|
||||
/* Do we already have this descriptor? */
|
||||
if (e && !strcmp(desc, e->desc)) {
|
||||
log_info(LD_REND, "We already have this service descriptor with desc "
|
||||
"ID %s.", desc_id_base32);
|
||||
e->received = time(NULL);
|
||||
rend_service_descriptor_free(parsed);
|
||||
goto skip;
|
||||
}
|
||||
/* Store received descriptor. */
|
||||
if (!e) {
|
||||
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
|
||||
digestmap_set(rend_cache_v2_dir, desc_id, e);
|
||||
} else {
|
||||
rend_service_descriptor_free(e->parsed);
|
||||
tor_free(e->desc);
|
||||
}
|
||||
e->received = time(NULL);
|
||||
e->parsed = parsed;
|
||||
e->desc = tor_malloc(encoded_size + 1);
|
||||
strlcpy(e->desc, current_desc, encoded_size + 1);
|
||||
e->len = encoded_size;
|
||||
log_info(LD_REND, "Successfully stored service descriptor with desc ID "
|
||||
"'%s' and len %d.", desc_id_base32, encoded_size);
|
||||
number_stored++;
|
||||
skip:
|
||||
/* advance to next descriptor, if available. */
|
||||
current_desc = next_desc;
|
||||
/* check if there is a next descriptor. */
|
||||
if (strncmp(current_desc, "rendezvous-service-descriptor ",
|
||||
strlen("rendezvous-service-descriptor ")))
|
||||
break;
|
||||
}
|
||||
log_info(LD_REND, "Parsed and added %d descriptor%s.",
|
||||
number_stored, number_stored != 1 ? "s" : "");
|
||||
return number_stored;
|
||||
}
|
||||
|
||||
/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
|
||||
* of introduction points with <b>descriptor_cookie</b> (which may also be
|
||||
* <b>NULL</b> if decryption is not necessary), and store the descriptor to
|
||||
* the local cache under its version and service id.
|
||||
*
|
||||
* If we have a newer descriptor with the same ID, ignore this one.
|
||||
* If we have an older descriptor with the same ID, replace it.
|
||||
* Return -1 if it's malformed or otherwise rejected; return 0 if
|
||||
* it's the same or older than one we've already got; return 1 if
|
||||
* it's novel.
|
||||
*/
|
||||
int
|
||||
rend_cache_store_v2_client(const char *desc, const char *descriptor_cookie)
|
||||
{
|
||||
rend_service_descriptor_t *parsed = NULL;
|
||||
char desc_id[DIGEST_LEN];
|
||||
char *intro_content = NULL;
|
||||
size_t intro_size;
|
||||
size_t encoded_size;
|
||||
const char *next_desc;
|
||||
time_t now = time(NULL);
|
||||
char key[REND_SERVICE_ID_LEN+2];
|
||||
char service_id[REND_SERVICE_ID_LEN+1];
|
||||
rend_cache_entry_t *e;
|
||||
tor_assert(rend_cache);
|
||||
tor_assert(desc);
|
||||
/* Parse the descriptor. */
|
||||
if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
|
||||
&intro_size, &encoded_size,
|
||||
&next_desc, desc) < 0) {
|
||||
if (parsed) rend_service_descriptor_free(parsed);
|
||||
if (intro_content) tor_free(intro_content);
|
||||
log_warn(LD_REND, "Could not parse descriptor.");
|
||||
return -1;
|
||||
}
|
||||
/* Compute service ID from public key. */
|
||||
if (rend_get_service_id(parsed->pk, service_id)<0) {
|
||||
log_warn(LD_REND, "Couldn't compute service ID.");
|
||||
rend_service_descriptor_free(parsed);
|
||||
tor_free(intro_content);
|
||||
return -1;
|
||||
}
|
||||
/* Decode/decrypt introduction points. */
|
||||
if (rend_decrypt_introduction_points(parsed, descriptor_cookie,
|
||||
intro_content, intro_size) < 0) {
|
||||
log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points.");
|
||||
rend_service_descriptor_free(parsed);
|
||||
tor_free(intro_content);
|
||||
return -1;
|
||||
}
|
||||
/* We don't need the encoded/encrypted introduction points any longer. */
|
||||
tor_free(intro_content);
|
||||
/* Is descriptor too old? */
|
||||
if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
|
||||
log_warn(LD_REND, "Service descriptor with service ID %s is too old.",
|
||||
service_id);
|
||||
rend_service_descriptor_free(parsed);
|
||||
return -1;
|
||||
}
|
||||
/* Is descriptor too far in the future? */
|
||||
if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
|
||||
log_warn(LD_REND, "Service descriptor with service ID %s is too far in "
|
||||
"the future.", service_id);
|
||||
rend_service_descriptor_free(parsed);
|
||||
return -1;
|
||||
}
|
||||
/* Do we already have a newer descriptor? */
|
||||
tor_snprintf(key, sizeof(key), "2%s", service_id);
|
||||
e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
|
||||
if (e && e->parsed->timestamp > parsed->timestamp) {
|
||||
log_info(LD_REND, "We already have a newer service descriptor for "
|
||||
"service ID %s with the same desc ID and version.",
|
||||
service_id);
|
||||
rend_service_descriptor_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
/* Do we already have this descriptor? */
|
||||
if (e && !strcmp(desc, e->desc)) {
|
||||
log_info(LD_REND,"We already have this service descriptor %s.",
|
||||
service_id);
|
||||
e->received = time(NULL);
|
||||
rend_service_descriptor_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
if (!e) {
|
||||
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
|
||||
strmap_set_lc(rend_cache, key, e);
|
||||
} else {
|
||||
rend_service_descriptor_free(e->parsed);
|
||||
tor_free(e->desc);
|
||||
}
|
||||
e->received = time(NULL);
|
||||
e->parsed = parsed;
|
||||
e->desc = tor_malloc_zero(encoded_size + 1);
|
||||
strlcpy(e->desc, desc, encoded_size + 1);
|
||||
e->len = encoded_size;
|
||||
log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
|
||||
service_id, encoded_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Called when we get a rendezvous-related relay cell on circuit
|
||||
* <b>circ</b>. Dispatch on rendezvous relay command. */
|
||||
void
|
||||
|
@ -309,7 +309,8 @@ rend_service_update_descriptor(rend_service_t *service)
|
||||
d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
||||
d->pk = crypto_pk_dup_key(service->private_key);
|
||||
d->timestamp = time(NULL);
|
||||
d->version = 1;
|
||||
d->version = 1; /*< XXXX020 this value is ignored by the
|
||||
* encode functions; do we need to set it at all? */
|
||||
n = smartlist_len(service->intro_nodes);
|
||||
d->n_intro_points = 0;
|
||||
d->intro_points = tor_malloc_zero(sizeof(char*)*n);
|
||||
|
@ -1577,7 +1577,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
|
||||
"opt extra-info-digest %s\n%s"
|
||||
"onion-key\n%s"
|
||||
"signing-key\n%s"
|
||||
"%s%s",
|
||||
"%s%s%s",
|
||||
router->nickname,
|
||||
router->address,
|
||||
router->or_port,
|
||||
@ -1593,7 +1593,9 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
|
||||
options->DownloadExtraInfo ? "opt caches-extra-info\n" : "",
|
||||
onion_pkey, identity_pkey,
|
||||
family_line,
|
||||
we_are_hibernating() ? "opt hibernating 1\n" : "");
|
||||
we_are_hibernating() ? "opt hibernating 1\n" : "",
|
||||
options->HidServDirectoryV2 ? "opt hidden-service-dir\n" : "");
|
||||
|
||||
tor_free(family_line);
|
||||
tor_free(onion_pkey);
|
||||
tor_free(identity_pkey);
|
||||
|
@ -4243,3 +4243,174 @@ routers_sort_by_identity(smartlist_t *routers)
|
||||
smartlist_sort(routers, _compare_routerinfo_by_id_digest);
|
||||
}
|
||||
|
||||
/** Return the first router that is acting as hidden service directory and that
|
||||
* has a greater ID than <b>id</b>; if all routers have smaller IDs than
|
||||
* <b>id</b>, return the router with the smallest ID; if the router list is
|
||||
* NULL, or has no elements, return NULL.
|
||||
*/
|
||||
routerinfo_t *
|
||||
hid_serv_next_directory(const char *id, smartlist_t *hs_dirs)
|
||||
{
|
||||
int i;
|
||||
if (!hs_dirs) return NULL;
|
||||
if (smartlist_len(hs_dirs) == 0) return NULL;
|
||||
for (i = 0; i < smartlist_len(hs_dirs); i++) {
|
||||
routerinfo_t *router = (routerinfo_t *) smartlist_get(hs_dirs, i);
|
||||
if (memcmp(router->cache_info.identity_digest, id, DIGEST_LEN) > 0) {
|
||||
return router;
|
||||
}
|
||||
}
|
||||
return (routerinfo_t *) smartlist_get(hs_dirs, 0);
|
||||
}
|
||||
|
||||
/** Return the first router that is acting as hidden service directory and that
|
||||
* has a smaller ID than <b>id</b>; if all routers have greater IDs than
|
||||
* <b>id</b>, return the router with the highest ID; if the router list is
|
||||
* NULL, or has no elements, return NULL.
|
||||
*/
|
||||
routerinfo_t *
|
||||
hid_serv_previous_directory(const char *id, smartlist_t *hs_dirs)
|
||||
{
|
||||
int i;
|
||||
if (!hs_dirs) return NULL;
|
||||
if (smartlist_len(hs_dirs) == 0) return NULL;
|
||||
for (i = smartlist_len(hs_dirs) - 1; i >= 0; i--) {
|
||||
routerinfo_t *router = (routerinfo_t *) smartlist_get(hs_dirs, i);
|
||||
if (memcmp(router->cache_info.identity_digest, id, DIGEST_LEN) < 0) {
|
||||
return router;
|
||||
}
|
||||
}
|
||||
return (routerinfo_t *)
|
||||
smartlist_get(hs_dirs, smartlist_len(hs_dirs) - 1);
|
||||
}
|
||||
|
||||
/** Returns true, if we are aware of enough hidden service directory to
|
||||
* usefully perform v2 rend operations on them (publish, fetch, replicate),
|
||||
* or false otherwise. */
|
||||
int
|
||||
hid_serv_have_enough_directories(smartlist_t *hs_dirs)
|
||||
{
|
||||
return (smartlist_len(hs_dirs) > REND_NUMBER_OF_CONSECUTIVE_REPLICAS);
|
||||
}
|
||||
|
||||
/** Determine the REND_NUMBER_OF_CONSECUTIVE_REPLICAS routers that are
|
||||
* responsible for <b>id</b> (binary) and add pointers to those routers'
|
||||
* routerstatus_t to <b>responsible_dirs</b>. If we don't have enough
|
||||
* hidden service directories, return -1, else 0. */
|
||||
int
|
||||
hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
|
||||
const char *id,
|
||||
smartlist_t *hs_dirs)
|
||||
{
|
||||
const char *digest;
|
||||
int i;
|
||||
routerinfo_t *router;
|
||||
char id_base32[32+1];
|
||||
base32_encode(id_base32, REND_DESC_ID_V2_BASE32 + 1, id, DIGEST_LEN);
|
||||
tor_assert(id);
|
||||
if (!hid_serv_have_enough_directories(hs_dirs)) {
|
||||
log_warn(LD_REND, "We don't have enough hidden service directories to "
|
||||
"perform v2 rendezvous operations!");
|
||||
return -1;
|
||||
}
|
||||
digest = id;
|
||||
for (i = 0; i < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; i++) {
|
||||
router = hid_serv_next_directory(digest, hs_dirs);
|
||||
digest = router->cache_info.identity_digest;
|
||||
if (!router) {
|
||||
log_warn(LD_REND, "Could not determine next router in "
|
||||
"hidden service routing table.");
|
||||
return -1;
|
||||
}
|
||||
smartlist_add(responsible_dirs, router);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Create a list of routerinfo_t in ascending order of identity digests
|
||||
* containing all routers that have been assigned as hidden service
|
||||
* directories by the directory authorities; this list can be used as
|
||||
* hidden service routing table. */
|
||||
smartlist_t *
|
||||
hid_serv_create_routing_table(void)
|
||||
{
|
||||
smartlist_t *hs_dirs = smartlist_create();
|
||||
tor_assert(routerlist);
|
||||
/* Copy the routerinfo_t's of all hidden service directories to a new
|
||||
* smartlist. */
|
||||
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r,
|
||||
{
|
||||
if (r->is_hs_dir)
|
||||
smartlist_add(hs_dirs, r);
|
||||
});
|
||||
routers_sort_by_identity(hs_dirs);
|
||||
return hs_dirs;
|
||||
}
|
||||
|
||||
/** Return true if this node is currently acting as hidden service
|
||||
* directory, false otherwise. */
|
||||
int
|
||||
hid_serv_acting_as_directory(smartlist_t *hs_dirs)
|
||||
{
|
||||
routerinfo_t *me = routerlist_find_my_routerinfo();
|
||||
int found_me = 0;
|
||||
if (!me) {
|
||||
return 0;
|
||||
}
|
||||
if (!get_options()->HidServDirectoryV2) {
|
||||
log_info(LD_REND, "We are not acting as hidden service directory, "
|
||||
"because we have not been configured as such.");
|
||||
return 0;
|
||||
}
|
||||
if (!hs_dirs) {
|
||||
/* routing table is NULL */
|
||||
log_info(LD_REND, "We are not acting as hidden service directory, "
|
||||
"because our own routing table is NULL.");
|
||||
return 0;
|
||||
}
|
||||
SMARTLIST_FOREACH(hs_dirs, routerinfo_t *, router,
|
||||
{
|
||||
if (router_is_me(router))
|
||||
found_me = 1;
|
||||
});
|
||||
if (!found_me) {
|
||||
/* not acting as HS Dir */
|
||||
char me_base32[REND_DESC_ID_V2_BASE32 + 1];
|
||||
base32_encode(me_base32, REND_DESC_ID_V2_BASE32 + 1,
|
||||
me->cache_info.identity_digest, DIGEST_LEN);
|
||||
log_info(LD_REND, "We are not acting as hidden service directory, "
|
||||
"because we are not listed as such in our own "
|
||||
"routing table. me=%s, num entries in RT=%d",
|
||||
me_base32, smartlist_len(hs_dirs));
|
||||
return 0;
|
||||
}
|
||||
if (smartlist_len(hs_dirs) <= REND_NUMBER_OF_CONSECUTIVE_REPLICAS) {
|
||||
/* too few HS Dirs -- that won't work */
|
||||
log_info(LD_REND, "We are not acting as hidden service directory, "
|
||||
"because there are too few hidden service "
|
||||
"directories in the routing table.");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Return true if this node is responsible for storing the descriptor ID
|
||||
* in <b>query</b> and false otherwise. */
|
||||
int
|
||||
hid_serv_responsible_for_desc_id(const char *query, smartlist_t *hs_dirs)
|
||||
{
|
||||
const char *me;
|
||||
const char *predecessor;
|
||||
routerinfo_t *router;
|
||||
int i;
|
||||
if (!hid_serv_acting_as_directory(hs_dirs))
|
||||
return 0;
|
||||
me = router_get_my_routerinfo()->cache_info.identity_digest;
|
||||
predecessor = me;
|
||||
for (i = 0; i < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; i++) {
|
||||
router = hid_serv_previous_directory(predecessor, hs_dirs);
|
||||
predecessor = router->cache_info.identity_digest;
|
||||
}
|
||||
return rend_id_is_in_interval(query, predecessor, me);
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ typedef enum {
|
||||
K_EXTRA_INFO,
|
||||
K_EXTRA_INFO_DIGEST,
|
||||
K_CACHES_EXTRA_INFO,
|
||||
K_HIDDEN_SERVICE_DIR,
|
||||
|
||||
K_DIR_KEY_CERTIFICATE_VERSION,
|
||||
K_DIR_IDENTITY_KEY,
|
||||
@ -218,6 +219,7 @@ static token_rule_t routerdesc_token_table[] = {
|
||||
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
|
||||
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
|
||||
T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ),
|
||||
T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ),
|
||||
|
||||
T01("family", K_FAMILY, ARGS, NO_OBJ ),
|
||||
T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ),
|
||||
@ -1255,6 +1257,10 @@ router_parse_entry_from_string(const char *s, const char *end,
|
||||
}
|
||||
}
|
||||
|
||||
if ((tok = find_first_by_keyword(tokens, K_HIDDEN_SERVICE_DIR))) {
|
||||
router->wants_to_be_hs_dir = 1;
|
||||
}
|
||||
|
||||
tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE);
|
||||
tor_assert(tok);
|
||||
note_crypto_pk_op(VERIFY_RTR);
|
||||
@ -1698,6 +1704,8 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens,
|
||||
consensus_method >= 2) {
|
||||
/* Unnamed is computed right by consensus method 2 and later. */
|
||||
rs->is_unnamed = 1;
|
||||
} else if (!strcmp(tok->args[i], "HSDir")) {
|
||||
rs->is_hs_dir = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3172,7 +3180,7 @@ sort_version_list(smartlist_t *versions, int remove_duplicates)
|
||||
* *<b>intro_points_encrypted_out</b>, their encrypted size to
|
||||
* *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor
|
||||
* to *<b>encoded_size_out</b>, and a pointer to the possibly next
|
||||
* descriptor to *<b>next_now</b>; return 0 for success (including validation)
|
||||
* descriptor to *<b>next_out</b>; return 0 for success (including validation)
|
||||
* and -1 for failure.
|
||||
*/
|
||||
int
|
||||
@ -3228,15 +3236,13 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
||||
log_warn(LD_REND, "Impossibly short descriptor.");
|
||||
goto err;
|
||||
}
|
||||
/* Check whether descriptor starts correctly. */
|
||||
/* Parse base32-encoded descriptor ID. */
|
||||
tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
|
||||
tor_assert(tok);
|
||||
tor_assert(tok == smartlist_get(tokens, 0));
|
||||
tor_assert(tok->n_args == 1);
|
||||
/*XXXX020 magic 32. */
|
||||
if (strlen(tok->args[0]) != 32 ||
|
||||
strspn(tok->args[0], BASE32_CHARS) != 32) {
|
||||
if (strlen(tok->args[0]) != REND_DESC_ID_V2_BASE32 ||
|
||||
strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_BASE32) {
|
||||
log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
|
||||
goto err;
|
||||
}
|
||||
@ -3252,6 +3258,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
||||
tor_assert(tok->n_args == 1);
|
||||
result->version = atoi(tok->args[0]);
|
||||
if (result->version < 2) { /*XXXX020 what if > 2? */
|
||||
/* Good question: should higher versions
|
||||
* be rejected by directories? -KL */
|
||||
log_warn(LD_REND, "Wrong descriptor version: %d", result->version);
|
||||
goto err;
|
||||
}
|
||||
@ -3264,9 +3272,8 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
||||
tok = find_first_by_keyword(tokens, R_SECRET_ID_PART);
|
||||
tor_assert(tok);
|
||||
tor_assert(tok->n_args == 1);
|
||||
/* XXXX020 magic 32. */
|
||||
if (strlen(tok->args[0]) != 32 ||
|
||||
strspn(tok->args[0], BASE32_CHARS) != 32) {
|
||||
if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 ||
|
||||
strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) {
|
||||
log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
|
||||
goto err;
|
||||
}
|
||||
@ -3418,9 +3425,8 @@ rend_decrypt_introduction_points(rend_service_descriptor_t *parsed,
|
||||
/* Parse identifier. */
|
||||
tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER);
|
||||
tor_assert(tok);
|
||||
/* XXXX020 magic 32. */
|
||||
if (base32_decode(info->identity_digest, DIGEST_LEN,
|
||||
tok->args[0], 32) < 0) {
|
||||
tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) {
|
||||
log_warn(LD_REND, "Identity digest contains illegal characters: %s",
|
||||
tok->args[0]);
|
||||
tor_free(info);
|
||||
|
Loading…
Reference in New Issue
Block a user