2007-12-12 22:09:01 +01:00
|
|
|
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
|
|
* Copyright (c) 2007, The Tor Project, Inc. */
|
2004-03-31 23:54:56 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
/* $Id$ */
|
2005-12-14 21:40:40 +01:00
|
|
|
const char rendservice_c_id[] =
|
|
|
|
"$Id$";
|
2004-03-31 23:54:56 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/**
|
|
|
|
* \file rendservice.c
|
|
|
|
* \brief The hidden-service side of rendezvous functionality.
|
|
|
|
**/
|
2004-03-31 23:54:56 +02:00
|
|
|
|
|
|
|
#include "or.h"
|
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro,
|
|
|
|
const char *pk_digest,
|
|
|
|
int desc_version);
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Represents the mapping from a virtual port of a rendezvous service to
|
2004-03-31 23:54:56 +02:00
|
|
|
* a real port on some IP.
|
|
|
|
*/
|
|
|
|
typedef struct rend_service_port_config_t {
|
|
|
|
uint16_t virtual_port;
|
|
|
|
uint16_t real_port;
|
2005-10-05 04:06:36 +02:00
|
|
|
uint32_t real_addr;
|
2004-03-31 23:54:56 +02:00
|
|
|
} rend_service_port_config_t;
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Try to maintain this many intro points per service if possible. */
|
2004-04-03 01:01:00 +02:00
|
|
|
#define NUM_INTRO_POINTS 3
|
|
|
|
|
2004-05-18 03:53:53 +02:00
|
|
|
/** If we can't build our intro circuits, don't retry for this long. */
|
2006-03-13 00:31:16 +01:00
|
|
|
#define INTRO_CIRC_RETRY_PERIOD (60*5)
|
2004-05-18 03:53:53 +02:00
|
|
|
/** Don't try to build more than this many circuits before giving up
|
|
|
|
* for a while.*/
|
2004-06-02 21:44:41 +02:00
|
|
|
#define MAX_INTRO_CIRCS_PER_PERIOD 10
|
2004-10-30 07:04:52 +02:00
|
|
|
/** How many times will a hidden service operator attempt to connect to
|
|
|
|
* a requested rendezvous point before giving up? */
|
2005-03-19 05:38:59 +01:00
|
|
|
#define MAX_REND_FAILURES 30
|
|
|
|
/** How many seconds should we spend trying to connect to a requested
|
|
|
|
* rendezvous point before giving up? */
|
|
|
|
#define MAX_REND_TIMEOUT 30
|
2004-05-18 03:53:53 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Represents a single hidden service running at this OP. */
|
2004-03-31 23:54:56 +02:00
|
|
|
typedef struct rend_service_t {
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Fields specified in config file */
|
|
|
|
char *directory; /**< where in the filesystem it stores it */
|
|
|
|
smartlist_t *ports; /**< List of rend_service_port_config_t */
|
|
|
|
char *intro_prefer_nodes; /**< comma-separated list of nicknames */
|
|
|
|
char *intro_exclude_nodes; /**< comma-separated list of nicknames */
|
2004-03-31 23:54:56 +02:00
|
|
|
/* Other fields */
|
|
|
|
crypto_pk_env_t *private_key;
|
2007-11-29 16:25:04 +01:00
|
|
|
char service_id[REND_SERVICE_ID_LEN_BASE32+1];
|
2004-04-03 05:07:25 +02:00
|
|
|
char pk_digest[DIGEST_LEN];
|
2007-12-21 10:28:22 +01:00
|
|
|
smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
|
2004-05-05 23:32:43 +02:00
|
|
|
* or are trying to establish. */
|
2004-05-18 03:53:53 +02:00
|
|
|
time_t intro_period_started;
|
|
|
|
int n_intro_circuits_launched; /**< count of intro circuits we have
|
|
|
|
* established in this period. */
|
2007-10-31 21:48:08 +01:00
|
|
|
/* DOCDOC undocumented versions */
|
2004-04-03 01:01:00 +02:00
|
|
|
rend_service_descriptor_t *desc;
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
time_t desc_is_dirty;
|
2004-11-15 10:05:54 +01:00
|
|
|
time_t next_upload_time;
|
2007-11-29 16:30:32 +01:00
|
|
|
int descriptor_version; /**< Rendezvous descriptor version that will be
|
2007-11-29 16:25:04 +01:00
|
|
|
* published. */
|
2004-03-31 23:54:56 +02:00
|
|
|
} rend_service_t;
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** A list of rend_service_t's for services run on this OP.
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
|
|
|
static smartlist_t *rend_service_list = NULL;
|
|
|
|
|
2005-08-13 02:22:07 +02:00
|
|
|
/** Return the number of rendezvous services we have configured. */
|
2005-09-30 03:09:52 +02:00
|
|
|
int
|
|
|
|
num_rend_services(void)
|
|
|
|
{
|
2005-08-13 02:22:07 +02:00
|
|
|
if (!rend_service_list)
|
|
|
|
return 0;
|
|
|
|
return smartlist_len(rend_service_list);
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Release the storage held by <b>service</b>.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
rend_service_free(rend_service_t *service)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2004-04-06 05:44:36 +02:00
|
|
|
if (!service) return;
|
|
|
|
tor_free(service->directory);
|
|
|
|
SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p));
|
|
|
|
smartlist_free(service->ports);
|
|
|
|
if (service->private_key)
|
|
|
|
crypto_free_pk_env(service->private_key);
|
2007-12-21 10:28:22 +01:00
|
|
|
if (service->intro_nodes) {
|
|
|
|
SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro,
|
|
|
|
rend_intro_point_free(intro););
|
|
|
|
smartlist_free(service->intro_nodes);
|
|
|
|
}
|
2004-04-06 05:44:36 +02:00
|
|
|
tor_free(service->intro_prefer_nodes);
|
|
|
|
tor_free(service->intro_exclude_nodes);
|
|
|
|
if (service->desc)
|
|
|
|
rend_service_descriptor_free(service->desc);
|
|
|
|
tor_free(service);
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
|
2005-02-27 23:08:01 +01:00
|
|
|
/** Release all the storage held in rend_service_list.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rend_service_free_all(void)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
|
|
|
if (!rend_service_list) {
|
|
|
|
return;
|
|
|
|
}
|
2004-04-03 02:58:54 +02:00
|
|
|
SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
|
|
|
|
rend_service_free(ptr));
|
2004-03-31 23:54:56 +02:00
|
|
|
smartlist_free(rend_service_list);
|
2005-02-11 00:18:39 +01:00
|
|
|
rend_service_list = NULL;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 09:27:29 +02:00
|
|
|
/** Validate <b>service</b> and add it to rend_service_list if possible.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(rend_service_t *service)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
rend_service_port_config_t *p;
|
|
|
|
struct in_addr addr;
|
|
|
|
|
2004-04-03 01:01:00 +02:00
|
|
|
if (!service->intro_prefer_nodes)
|
|
|
|
service->intro_prefer_nodes = tor_strdup("");
|
|
|
|
if (!service->intro_exclude_nodes)
|
|
|
|
service->intro_exclude_nodes = tor_strdup("");
|
2007-11-29 16:25:04 +01:00
|
|
|
service->intro_nodes = smartlist_create();
|
2004-04-03 01:01:00 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/* If the service is configured to publish unversioned (v0) and versioned
|
|
|
|
* descriptors (v2 or higher), split it up into two separate services. */
|
2007-11-29 16:25:04 +01:00
|
|
|
if (service->descriptor_version == -1) {
|
2007-10-31 21:48:06 +01:00
|
|
|
rend_service_t *v0_service = tor_malloc_zero(sizeof(rend_service_t));
|
|
|
|
v0_service->directory = tor_strdup(service->directory);
|
|
|
|
v0_service->ports = smartlist_create();
|
|
|
|
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p, {
|
|
|
|
rend_service_port_config_t *copy =
|
|
|
|
tor_malloc_zero(sizeof(rend_service_port_config_t));
|
|
|
|
memcpy(copy, p, sizeof(rend_service_port_config_t));
|
|
|
|
smartlist_add(v0_service->ports, copy);
|
|
|
|
});
|
|
|
|
v0_service->intro_prefer_nodes = tor_strdup(service->intro_prefer_nodes);
|
|
|
|
v0_service->intro_exclude_nodes = tor_strdup(service->intro_exclude_nodes);
|
|
|
|
v0_service->intro_period_started = service->intro_period_started;
|
2007-11-29 16:25:04 +01:00
|
|
|
v0_service->descriptor_version = 0; /* Unversioned descriptor. */
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(v0_service);
|
2007-10-31 21:48:06 +01:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
service->descriptor_version = 2; /* Versioned descriptor. */
|
2007-10-31 21:48:06 +01:00
|
|
|
}
|
|
|
|
|
2004-04-03 02:58:54 +02:00
|
|
|
if (!smartlist_len(service->ports)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "Hidden service with no ports configured; ignoring.");
|
2004-03-31 23:54:56 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
} else {
|
2004-04-03 02:58:54 +02:00
|
|
|
smartlist_set_capacity(service->ports, -1);
|
2004-03-31 23:54:56 +02:00
|
|
|
smartlist_add(rend_service_list, service);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_REND,"Configuring service with directory \"%s\"",
|
|
|
|
service->directory);
|
2004-04-03 02:58:54 +02:00
|
|
|
for (i = 0; i < smartlist_len(service->ports); ++i) {
|
2005-02-22 09:18:36 +01:00
|
|
|
char addrbuf[INET_NTOA_BUF_LEN];
|
2004-04-03 02:58:54 +02:00
|
|
|
p = smartlist_get(service->ports, i);
|
2005-10-05 04:06:36 +02:00
|
|
|
addr.s_addr = htonl(p->real_addr);
|
2005-02-22 09:18:36 +01:00
|
|
|
tor_inet_ntoa(&addr, addrbuf, sizeof(addrbuf));
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_REND,"Service maps port %d to %s:%d",
|
|
|
|
p->virtual_port, addrbuf, p->real_port);
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Parses a real-port to virtual-port mapping and returns a new
|
2004-04-06 05:44:36 +02:00
|
|
|
* rend_service_port_config_t.
|
|
|
|
*
|
|
|
|
* The format is: VirtualPort (IP|RealPort|IP:RealPort)?
|
2004-05-10 06:34:48 +02:00
|
|
|
*
|
|
|
|
* IP defaults to 127.0.0.1; RealPort defaults to VirtualPort.
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static rend_service_port_config_t *
|
|
|
|
parse_port_config(const char *string)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2005-08-06 23:35:04 +02:00
|
|
|
smartlist_t *sl;
|
2004-10-12 17:52:09 +02:00
|
|
|
int virtport;
|
2004-10-13 21:53:34 +02:00
|
|
|
int realport;
|
|
|
|
uint16_t p;
|
2004-10-12 17:52:09 +02:00
|
|
|
uint32_t addr;
|
2005-08-06 23:35:04 +02:00
|
|
|
const char *addrport;
|
|
|
|
rend_service_port_config_t *result = NULL;
|
|
|
|
|
|
|
|
sl = smartlist_create();
|
2005-12-14 21:40:40 +01:00
|
|
|
smartlist_split_string(sl, string, " ",
|
|
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
2005-08-06 23:35:04 +02:00
|
|
|
if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "Bad syntax in hidden service port configuration.");
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
2005-08-06 23:35:04 +02:00
|
|
|
|
|
|
|
virtport = atoi(smartlist_get(sl,0));
|
2004-03-31 23:54:56 +02:00
|
|
|
if (virtport < 1 || virtport > 65535) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "Missing or invalid port in hidden service port "
|
|
|
|
"configuration.");
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
2005-08-06 23:35:04 +02:00
|
|
|
|
|
|
|
if (smartlist_len(sl) == 1) {
|
2004-03-31 23:54:56 +02:00
|
|
|
/* No addr:port part; use default. */
|
|
|
|
realport = virtport;
|
2004-10-12 17:52:09 +02:00
|
|
|
addr = 0x7F000001u; /* 127.0.0.1 */
|
|
|
|
} else {
|
2005-08-06 23:35:04 +02:00
|
|
|
addrport = smartlist_get(sl,1);
|
|
|
|
if (strchr(addrport, ':') || strchr(addrport, '.')) {
|
2006-07-06 04:44:07 +02:00
|
|
|
if (parse_addr_port(LOG_WARN, addrport, NULL, &addr, &p)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG,"Unparseable address in hidden service port "
|
|
|
|
"configuration.");
|
2005-08-06 23:35:04 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
realport = p?p:virtport;
|
|
|
|
} else {
|
|
|
|
/* No addr:port, no addr -- must be port. */
|
|
|
|
realport = atoi(addrport);
|
|
|
|
if (realport < 1 || realport > 65535)
|
|
|
|
goto err;
|
|
|
|
addr = 0x7F000001u; /* Default to 127.0.0.1 */
|
2004-10-12 21:01:53 +02:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
result = tor_malloc(sizeof(rend_service_port_config_t));
|
|
|
|
result->virtual_port = virtport;
|
|
|
|
result->real_port = realport;
|
2005-10-05 04:06:36 +02:00
|
|
|
result->real_addr = addr;
|
2005-08-06 23:35:04 +02:00
|
|
|
err:
|
|
|
|
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
|
|
|
|
smartlist_free(sl);
|
2004-03-31 23:54:56 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Set up rend_service_list, based on the values of HiddenServiceDir and
|
2004-05-10 06:34:48 +02:00
|
|
|
* HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
|
2004-11-09 00:12:40 +01:00
|
|
|
* failure. (If <b>validate_only</b> is set, parse, warn and return as
|
|
|
|
* normal, but don't actually change the configured services.)
|
2004-03-31 23:54:56 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
rend_config_services(or_options_t *options, int validate_only)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
2005-07-22 23:12:10 +02:00
|
|
|
config_line_t *line;
|
2004-03-31 23:54:56 +02:00
|
|
|
rend_service_t *service = NULL;
|
|
|
|
rend_service_port_config_t *portcfg;
|
2004-11-09 00:12:40 +01:00
|
|
|
|
2005-02-11 00:18:39 +01:00
|
|
|
if (!validate_only) {
|
2004-11-09 00:12:40 +01:00
|
|
|
rend_service_free_all();
|
2005-02-11 00:18:39 +01:00
|
|
|
rend_service_list = smartlist_create();
|
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
|
|
|
|
for (line = options->RendConfigLines; line; line = line->next) {
|
|
|
|
if (!strcasecmp(line->key, "HiddenServiceDir")) {
|
2004-11-09 00:12:40 +01:00
|
|
|
if (service) {
|
|
|
|
if (validate_only)
|
|
|
|
rend_service_free(service);
|
|
|
|
else
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(service);
|
2004-11-09 00:12:40 +01:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
service = tor_malloc_zero(sizeof(rend_service_t));
|
|
|
|
service->directory = tor_strdup(line->value);
|
|
|
|
service->ports = smartlist_create();
|
2004-05-18 03:53:53 +02:00
|
|
|
service->intro_period_started = time(NULL);
|
2007-11-29 16:25:04 +01:00
|
|
|
service->descriptor_version = -1; /**< All descriptor versions. */
|
2004-04-03 00:06:46 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
|
|
|
|
line->key);
|
2004-04-03 00:06:46 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(line->key, "HiddenServicePort")) {
|
2004-03-31 23:54:56 +02:00
|
|
|
portcfg = parse_port_config(line->value);
|
|
|
|
if (!portcfg) {
|
2004-04-03 04:14:20 +02:00
|
|
|
rend_service_free(service);
|
|
|
|
return -1;
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
smartlist_add(service->ports, portcfg);
|
2004-04-03 00:06:46 +02:00
|
|
|
} else if (!strcasecmp(line->key, "HiddenServiceNodes")) {
|
2004-04-03 01:01:00 +02:00
|
|
|
if (service->intro_prefer_nodes) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG,
|
|
|
|
"Got multiple HiddenServiceNodes lines for a single "
|
|
|
|
"service.");
|
2008-01-16 06:27:19 +01:00
|
|
|
rend_service_free(service);
|
2004-04-03 00:06:46 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-03 01:01:00 +02:00
|
|
|
service->intro_prefer_nodes = tor_strdup(line->value);
|
2007-10-31 21:48:06 +01:00
|
|
|
} else if (!strcasecmp(line->key, "HiddenServiceExcludeNodes")) {
|
2004-04-03 00:06:46 +02:00
|
|
|
if (service->intro_exclude_nodes) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CONFIG,
|
|
|
|
"Got multiple HiddenServiceExcludedNodes lines for "
|
|
|
|
"a single service.");
|
2008-01-16 06:27:19 +01:00
|
|
|
rend_service_free(service);
|
2004-04-03 00:06:46 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
service->intro_exclude_nodes = tor_strdup(line->value);
|
2007-10-31 21:48:06 +01:00
|
|
|
} else {
|
|
|
|
smartlist_t *versions;
|
|
|
|
char *version_str;
|
2007-11-29 16:25:04 +01:00
|
|
|
int i, version, versions_bitmask = 0;
|
2007-10-31 21:48:06 +01:00
|
|
|
tor_assert(!strcasecmp(line->key, "HiddenServiceVersion"));
|
|
|
|
versions = smartlist_create();
|
|
|
|
smartlist_split_string(versions, line->value, ",",
|
|
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
|
|
|
for (i = 0; i < smartlist_len(versions); i++) {
|
|
|
|
version_str = smartlist_get(versions, i);
|
|
|
|
if (strlen(version_str) != 1 || strspn(version_str, "02") != 1) {
|
|
|
|
log_warn(LD_CONFIG,
|
|
|
|
"HiddenServiceVersion can only be 0 and/or 2.");
|
2008-01-16 06:27:19 +01:00
|
|
|
SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(versions);
|
|
|
|
rend_service_free(service);
|
2007-10-31 21:48:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
version = atoi(version_str);
|
2007-11-29 16:25:04 +01:00
|
|
|
versions_bitmask |= 1 << version;
|
2007-10-31 21:48:06 +01:00
|
|
|
}
|
2007-12-15 21:50:56 +01:00
|
|
|
/* If exactly one version is set, change descriptor_version to that
|
2007-12-15 21:28:09 +01:00
|
|
|
* value; otherwise leave it at -1. */
|
2007-11-29 16:25:04 +01:00
|
|
|
if (versions_bitmask == 1 << 0) service->descriptor_version = 0;
|
|
|
|
if (versions_bitmask == 1 << 2) service->descriptor_version = 2;
|
2008-01-16 06:27:19 +01:00
|
|
|
SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(versions);
|
2004-03-31 23:54:56 +02:00
|
|
|
}
|
|
|
|
}
|
2004-11-09 00:12:40 +01:00
|
|
|
if (service) {
|
|
|
|
if (validate_only)
|
|
|
|
rend_service_free(service);
|
|
|
|
else
|
2007-11-29 16:30:32 +01:00
|
|
|
rend_add_service(service);
|
2004-11-09 00:12:40 +01:00
|
|
|
}
|
2004-03-31 23:54:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Replace the old value of <b>service</b>-\>desc with one that reflects
|
2004-04-03 02:58:54 +02:00
|
|
|
* the other fields in service.
|
2004-04-03 01:01:00 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
|
|
|
rend_service_update_descriptor(rend_service_t *service)
|
2004-04-03 01:01:00 +02:00
|
|
|
{
|
|
|
|
rend_service_descriptor_t *d;
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *circ;
|
2007-12-21 10:28:22 +01:00
|
|
|
int i;
|
2004-04-03 01:01:00 +02:00
|
|
|
if (service->desc) {
|
|
|
|
rend_service_descriptor_free(service->desc);
|
|
|
|
service->desc = NULL;
|
|
|
|
}
|
2007-10-28 20:48:16 +01:00
|
|
|
d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
2004-04-03 01:01:00 +02:00
|
|
|
d->pk = crypto_pk_dup_key(service->private_key);
|
|
|
|
d->timestamp = time(NULL);
|
2007-11-29 16:25:04 +01:00
|
|
|
d->version = service->descriptor_version;
|
2007-12-21 10:28:22 +01:00
|
|
|
d->intro_nodes = smartlist_create();
|
2007-12-21 10:33:11 +01:00
|
|
|
/* Whoever understands descriptor version 2 also understands intro
|
|
|
|
* protocol 2. So we only support 2. */
|
|
|
|
d->protocols = 1 << 2;
|
2007-10-28 20:48:16 +01:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
for (i = 0; i < smartlist_len(service->intro_nodes); ++i) {
|
|
|
|
rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i);
|
|
|
|
rend_intro_point_t *intro_desc;
|
|
|
|
circ = find_intro_circuit(intro_svc, service->pk_digest, d->version);
|
|
|
|
if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO)
|
2004-09-20 05:07:11 +02:00
|
|
|
continue;
|
2007-12-21 10:28:22 +01:00
|
|
|
|
|
|
|
/* We have an entirely established intro circuit. */
|
|
|
|
intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t));
|
|
|
|
intro_desc->extend_info = extend_info_dup(intro_svc->extend_info);
|
|
|
|
if (intro_svc->intro_key)
|
|
|
|
intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key);
|
|
|
|
smartlist_add(d->intro_nodes, intro_desc);
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Load and/or generate private keys for all hidden services. Return 0 on
|
2004-03-31 23:54:56 +02:00
|
|
|
* success, -1 on failure.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
rend_service_load_keys(void)
|
2004-03-31 23:54:56 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
rend_service_t *s;
|
|
|
|
char fname[512];
|
|
|
|
char buf[128];
|
|
|
|
|
2004-04-03 02:58:54 +02:00
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
|
|
|
s = smartlist_get(rend_service_list,i);
|
2004-03-31 23:54:56 +02:00
|
|
|
if (s->private_key)
|
|
|
|
continue;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
|
|
|
|
s->directory);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2004-03-31 23:54:56 +02:00
|
|
|
/* Check/create directory */
|
2004-11-09 08:12:31 +01:00
|
|
|
if (check_private_dir(s->directory, CPD_CREATE) < 0)
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Load key */
|
2004-04-06 05:44:36 +02:00
|
|
|
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
|
2007-03-09 22:39:30 +01:00
|
|
|
strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
|
|
|
|
>= sizeof(fname)) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
|
|
|
|
s->directory);
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-05-29 19:31:13 +02:00
|
|
|
s->private_key = init_key_from_file(fname, 1, LOG_ERR);
|
2004-03-31 23:54:56 +02:00
|
|
|
if (!s->private_key)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Create service file */
|
|
|
|
if (rend_get_service_id(s->private_key, s->service_id)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-01 05:34:05 +02:00
|
|
|
if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
|
2007-03-04 21:11:46 +01:00
|
|
|
log_warn(LD_BUG, "Couldn't compute hash of public key.");
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-06 05:44:36 +02:00
|
|
|
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
|
2007-03-09 22:39:30 +01:00
|
|
|
strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
|
|
|
|
>= sizeof(fname)) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
|
|
|
|
" \"%s\".", s->directory);
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-27 08:37:34 +02:00
|
|
|
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
|
2004-09-08 09:16:34 +02:00
|
|
|
if (write_str_to_file(fname,buf,0)<0)
|
2004-03-31 23:54:56 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2007-10-31 21:48:08 +01:00
|
|
|
/** Return the service whose public key has a digest of <b>digest</b> and
|
2007-11-29 16:25:04 +01:00
|
|
|
* which publishes the given descriptor <b>version</b>. Return NULL if no
|
|
|
|
* such service exists.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2004-04-02 23:56:52 +02:00
|
|
|
static rend_service_t *
|
2007-10-31 21:48:06 +01:00
|
|
|
rend_service_get_by_pk_digest_and_version(const char* digest,
|
2007-11-29 16:25:04 +01:00
|
|
|
uint8_t version)
|
2004-04-01 05:34:05 +02:00
|
|
|
{
|
2004-04-03 02:58:54 +02:00
|
|
|
SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
|
2007-10-31 21:48:06 +01:00
|
|
|
if (!memcmp(s->pk_digest,digest,DIGEST_LEN) &&
|
2007-11-29 16:25:04 +01:00
|
|
|
s->descriptor_version == version) return s);
|
2004-04-01 05:34:05 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-01-14 05:35:53 +01:00
|
|
|
/** Return 1 if any virtual port in <b>service</b> wants a circuit
|
|
|
|
* to have good uptime. Else return 0.
|
|
|
|
*/
|
|
|
|
static int
|
2005-09-30 03:09:52 +02:00
|
|
|
rend_service_requires_uptime(rend_service_t *service)
|
|
|
|
{
|
2005-01-14 05:35:53 +01:00
|
|
|
int i;
|
|
|
|
rend_service_port_config_t *p;
|
|
|
|
|
|
|
|
for (i=0; i < smartlist_len(service->ports); ++i) {
|
|
|
|
p = smartlist_get(service->ports, i);
|
2005-12-14 21:40:40 +01:00
|
|
|
if (smartlist_string_num_isin(get_options()->LongLivedPorts,
|
|
|
|
p->virtual_port))
|
2005-01-14 05:35:53 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-04-01 05:34:05 +02:00
|
|
|
/******
|
|
|
|
* Handle cells
|
|
|
|
******/
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Respond to an INTRODUCE2 cell by launching a circuit to the chosen
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
* rendezvous point.
|
2004-04-02 00:21:01 +02:00
|
|
|
*/
|
2004-04-01 05:34:05 +02:00
|
|
|
int
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_introduce(origin_circuit_t *circuit, const char *request,
|
2005-12-14 21:40:40 +01:00
|
|
|
size_t request_len)
|
2004-04-01 05:34:05 +02:00
|
|
|
{
|
2005-06-29 23:46:55 +02:00
|
|
|
char *ptr, *r_cookie;
|
|
|
|
extend_info_t *extend_info = NULL;
|
2004-04-01 22:05:57 +02:00
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
2004-04-05 23:15:14 +02:00
|
|
|
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
|
2004-04-01 05:34:05 +02:00
|
|
|
rend_service_t *service;
|
2004-10-30 07:04:52 +02:00
|
|
|
int r, i;
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t len, keylen;
|
2004-04-01 22:05:57 +02:00
|
|
|
crypto_dh_env_t *dh = NULL;
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *launched = NULL;
|
2004-04-02 00:21:01 +02:00
|
|
|
crypt_path_t *cpath = NULL;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2004-04-03 02:58:54 +02:00
|
|
|
char hexcookie[9];
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
int circ_needs_uptime;
|
2006-10-09 17:47:50 +02:00
|
|
|
int reason = END_CIRC_REASON_TORPROTOCOL;
|
2007-10-31 21:48:06 +01:00
|
|
|
crypto_pk_env_t *intro_key;
|
|
|
|
char intro_key_digest[DIGEST_LEN];
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
|
|
|
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
escaped(serviceid), circuit->_base.n_circ_id);
|
2004-04-01 05:34:05 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"Got an INTRODUCE2 over a non-introduction circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id);
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-04-05 19:28:48 +02:00
|
|
|
/* min key length plus digest length plus nickname length */
|
2004-08-18 07:05:58 +02:00
|
|
|
if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+
|
2004-11-23 00:28:26 +01:00
|
|
|
DH_KEY_LEN+42) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id);
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/* look up service depending on circuit. */
|
|
|
|
service = rend_service_get_by_pk_digest_and_version(
|
|
|
|
circuit->rend_pk_digest, circuit->rend_desc_version);
|
2004-04-01 05:34:05 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
|
2006-03-06 01:25:39 +01:00
|
|
|
escaped(serviceid));
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-10-31 21:48:06 +01:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
/* if descriptor version is 2, use intro key instead of service key. */
|
|
|
|
if (circuit->rend_desc_version == 0) {
|
2007-10-31 21:48:06 +01:00
|
|
|
intro_key = service->private_key;
|
|
|
|
} else {
|
|
|
|
intro_key = circuit->intro_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* first DIGEST_LEN bytes of request is intro or service pk digest */
|
|
|
|
crypto_pk_get_digest(intro_key, intro_key_digest);
|
|
|
|
if (memcmp(intro_key_digest, request, DIGEST_LEN)) {
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
|
|
|
request, REND_SERVICE_ID_LEN);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
|
2006-03-06 01:25:39 +01:00
|
|
|
escaped(serviceid));
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
keylen = crypto_pk_keysize(intro_key);
|
2004-04-05 23:15:14 +02:00
|
|
|
if (request_len < keylen+DIGEST_LEN) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"PK-encrypted portion of INTRODUCE2 cell was truncated.");
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Next N bytes is encrypted with service key */
|
2006-10-31 20:17:07 +01:00
|
|
|
note_crypto_pk_op(REND_SERVER);
|
2004-10-14 04:47:09 +02:00
|
|
|
r = crypto_pk_private_hybrid_decrypt(
|
2007-10-31 21:48:06 +01:00
|
|
|
intro_key,buf,request+DIGEST_LEN,request_len-DIGEST_LEN,
|
2004-05-12 21:30:28 +02:00
|
|
|
PK_PKCS1_OAEP_PADDING,1);
|
2004-10-14 04:47:09 +02:00
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
|
2004-04-01 05:34:05 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-14 04:47:09 +02:00
|
|
|
len = r;
|
2005-06-29 23:46:55 +02:00
|
|
|
if (*buf == 2) {
|
|
|
|
/* Version 2 INTRODUCE2 cell. */
|
|
|
|
int klen;
|
|
|
|
extend_info = tor_malloc_zero(sizeof(extend_info_t));
|
|
|
|
extend_info->addr = ntohl(get_uint32(buf+1));
|
|
|
|
extend_info->port = ntohs(get_uint16(buf+5));
|
|
|
|
memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
|
|
|
|
extend_info->nickname[0] = '$';
|
|
|
|
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
|
|
|
|
extend_info->identity_digest, DIGEST_LEN);
|
|
|
|
|
|
|
|
klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
|
2005-09-08 08:37:50 +02:00
|
|
|
if ((int)len != 7+DIGEST_LEN+2+klen+20+128) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Bad length %u for version 2 INTRODUCE2 cell.",
|
|
|
|
(int)len);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2005-06-29 23:46:55 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
|
|
|
|
if (!extend_info->onion_key) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"Error decoding onion key in version 2 INTRODUCE2 cell.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2005-06-29 23:46:55 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
ptr = buf+7+DIGEST_LEN+2+klen;
|
|
|
|
len -= 7+DIGEST_LEN+2+klen;
|
2004-08-18 06:48:59 +02:00
|
|
|
} else {
|
2005-06-29 23:46:55 +02:00
|
|
|
char *rp_nickname;
|
|
|
|
size_t nickname_field_len;
|
|
|
|
routerinfo_t *router;
|
|
|
|
int version;
|
|
|
|
if (*buf == 1) {
|
|
|
|
rp_nickname = buf+1;
|
|
|
|
nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
|
|
|
|
version = 1;
|
|
|
|
} else {
|
|
|
|
nickname_field_len = MAX_NICKNAME_LEN+1;
|
|
|
|
rp_nickname = buf;
|
|
|
|
version = 0;
|
|
|
|
}
|
|
|
|
ptr=memchr(rp_nickname,0,nickname_field_len);
|
|
|
|
if (!ptr || ptr == rp_nickname) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
2006-07-15 21:21:30 +02:00
|
|
|
"Couldn't find a nul-padded nickname in INTRODUCE2 cell.");
|
2005-06-29 23:46:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
|
|
|
|
(version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Bad nickname in INTRODUCE2 cell.");
|
2005-06-29 23:46:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Okay, now we know that a nickname is at the start of the buffer. */
|
|
|
|
ptr = rp_nickname+nickname_field_len;
|
|
|
|
len -= nickname_field_len;
|
2005-12-14 21:40:40 +01:00
|
|
|
len -= rp_nickname - buf; /* also remove header space used by version, if
|
|
|
|
* any */
|
2005-10-05 00:23:31 +02:00
|
|
|
router = router_get_by_nickname(rp_nickname, 0);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (!router) {
|
2007-09-19 01:34:27 +02:00
|
|
|
log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
|
2006-12-07 18:04:44 +01:00
|
|
|
escaped_safe_str(rp_nickname));
|
2006-10-09 17:47:50 +02:00
|
|
|
/* XXXX Add a no-such-router reason? */
|
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2005-06-29 23:46:55 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extend_info = extend_info_from_router(router);
|
2004-04-01 05:34:05 +02:00
|
|
|
}
|
2005-06-29 23:46:55 +02:00
|
|
|
|
2004-04-06 05:44:36 +02:00
|
|
|
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL, "Bad length %u for INTRODUCE2 cell.", (int)len);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_TORPROTOCOL;
|
2008-01-16 06:27:19 +01:00
|
|
|
goto err;
|
2004-04-01 05:34:05 +02:00
|
|
|
}
|
2005-06-29 23:46:55 +02:00
|
|
|
|
2004-04-01 22:05:57 +02:00
|
|
|
r_cookie = ptr;
|
2004-08-07 03:03:33 +02:00
|
|
|
base16_encode(hexcookie,9,r_cookie,4);
|
2004-04-01 05:34:05 +02:00
|
|
|
|
2004-04-01 22:05:57 +02:00
|
|
|
/* Try DH handshake... */
|
|
|
|
dh = crypto_dh_new();
|
|
|
|
if (!dh || crypto_dh_generate_public(dh)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"Internal error: couldn't build DH state "
|
|
|
|
"or generate public key.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-01 22:05:57 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2004-04-05 23:15:14 +02:00
|
|
|
if (crypto_dh_compute_secret(dh, ptr+REND_COOKIE_LEN, DH_KEY_LEN, keys,
|
|
|
|
DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't complete DH handshake");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-01 22:05:57 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
circ_needs_uptime = rend_service_requires_uptime(service);
|
|
|
|
|
|
|
|
/* help predict this next time */
|
2005-11-25 09:08:56 +01:00
|
|
|
rep_hist_note_used_internal(time(NULL), circ_needs_uptime, 1);
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
|
2004-04-01 22:05:57 +02:00
|
|
|
/* Launch a circuit to alice's chosen rendezvous point.
|
2004-04-01 05:34:05 +02:00
|
|
|
*/
|
2004-11-28 10:05:49 +01:00
|
|
|
for (i=0;i<MAX_REND_FAILURES;i++) {
|
2007-12-23 20:15:22 +01:00
|
|
|
int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
|
|
|
|
if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
|
2005-06-29 23:46:55 +02:00
|
|
|
launched = circuit_launch_by_extend_info(
|
2007-12-23 20:15:22 +01:00
|
|
|
CIRCUIT_PURPOSE_S_CONNECT_REND, extend_info, flags);
|
2005-06-29 23:46:55 +02:00
|
|
|
|
2004-10-30 07:04:52 +02:00
|
|
|
if (launched)
|
|
|
|
break;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!launched) { /* give up */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Giving up launching first hop of circuit to rendezvous "
|
2006-12-07 21:14:29 +01:00
|
|
|
"point %s for service %s.",
|
2006-12-07 18:04:44 +01:00
|
|
|
escaped_safe_str(extend_info->nickname), serviceid);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_CONNECTFAILED;
|
2004-10-25 01:09:48 +02:00
|
|
|
goto err;
|
2004-04-01 22:05:57 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
2006-12-07 21:14:29 +01:00
|
|
|
"Accepted intro; launching circuit to %s "
|
2006-02-13 11:33:00 +01:00
|
|
|
"(cookie %s) for service %s.",
|
2006-12-07 21:14:29 +01:00
|
|
|
escaped_safe_str(extend_info->nickname), hexcookie, serviceid);
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(launched->build_state);
|
2004-04-01 22:05:57 +02:00
|
|
|
/* Fill in the circuit's state. */
|
2006-07-26 21:05:41 +02:00
|
|
|
memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
|
2004-04-03 04:40:30 +02:00
|
|
|
DIGEST_LEN);
|
2006-07-26 21:05:41 +02:00
|
|
|
memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
|
2006-07-26 21:05:34 +02:00
|
|
|
strlcpy(launched->rend_query, service->service_id,
|
|
|
|
sizeof(launched->rend_query));
|
2007-11-29 16:25:04 +01:00
|
|
|
launched->rend_desc_version = service->descriptor_version;
|
2004-04-02 00:21:01 +02:00
|
|
|
launched->build_state->pending_final_cpath = cpath =
|
|
|
|
tor_malloc_zero(sizeof(crypt_path_t));
|
2005-03-23 07:21:48 +01:00
|
|
|
cpath->magic = CRYPT_PATH_MAGIC;
|
2005-03-19 05:38:59 +01:00
|
|
|
launched->build_state->expiry_time = time(NULL) + MAX_REND_TIMEOUT;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2005-05-03 00:35:18 +02:00
|
|
|
cpath->dh_handshake_state = dh;
|
2004-04-01 22:05:57 +02:00
|
|
|
dh = NULL;
|
2004-04-06 05:44:36 +02:00
|
|
|
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
2004-04-06 05:44:36 +02:00
|
|
|
memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (extend_info) extend_info_free(extend_info);
|
2004-04-01 05:34:05 +02:00
|
|
|
|
|
|
|
return 0;
|
2004-04-01 22:05:57 +02:00
|
|
|
err:
|
|
|
|
if (dh) crypto_dh_free(dh);
|
2006-07-23 09:37:35 +02:00
|
|
|
if (launched)
|
2006-10-09 17:47:50 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(launched), reason);
|
2005-06-29 23:46:55 +02:00
|
|
|
if (extend_info) extend_info_free(extend_info);
|
2004-04-01 22:05:57 +02:00
|
|
|
return -1;
|
2004-04-01 05:34:05 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we fail building a rendezvous circuit at some point other
|
2004-05-05 23:32:43 +02:00
|
|
|
* than the last hop: launches a new circuit to the same rendezvous point.
|
|
|
|
*/
|
2004-04-14 23:40:50 +02:00
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
|
2004-04-14 23:40:50 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *newcirc;
|
2004-04-14 23:40:50 +02:00
|
|
|
cpath_build_state_t *newstate, *oldstate;
|
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(oldcirc->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
|
2004-04-14 23:40:50 +02:00
|
|
|
|
|
|
|
if (!oldcirc->build_state ||
|
2005-03-19 05:38:59 +01:00
|
|
|
oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
|
|
|
|
oldcirc->build_state->expiry_time < time(NULL)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Attempt to build circuit to %s for rendezvous has failed "
|
|
|
|
"too many times or expired; giving up.",
|
|
|
|
oldcirc->build_state ?
|
|
|
|
oldcirc->build_state->chosen_exit->nickname : "*unknown*");
|
2004-04-14 23:40:50 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-10-30 07:04:52 +02:00
|
|
|
oldstate = oldcirc->build_state;
|
|
|
|
tor_assert(oldstate);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (oldstate->pending_final_cpath == NULL) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Skipping relaunch of circ that failed on its first hop. "
|
|
|
|
"Initiator will retry.");
|
2004-10-30 07:04:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-03-06 01:25:39 +01:00
|
|
|
log_info(LD_REND,"Reattempting rendezvous circuit to '%s'",
|
2006-02-13 11:33:00 +01:00
|
|
|
oldstate->chosen_exit->nickname);
|
2004-04-14 23:40:50 +02:00
|
|
|
|
2007-12-23 20:15:22 +01:00
|
|
|
newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
|
|
|
|
oldstate->chosen_exit,
|
|
|
|
CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
|
|
|
|
|
2004-04-14 23:40:50 +02:00
|
|
|
if (!newcirc) {
|
2006-03-06 01:25:39 +01:00
|
|
|
log_warn(LD_REND,"Couldn't relaunch rendezvous circuit to '%s'.",
|
2006-02-13 11:33:00 +01:00
|
|
|
oldstate->chosen_exit->nickname);
|
2004-04-14 23:40:50 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
newstate = newcirc->build_state;
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(newstate);
|
2004-04-14 23:40:50 +02:00
|
|
|
newstate->failure_count = oldstate->failure_count+1;
|
2005-03-19 05:38:59 +01:00
|
|
|
newstate->expiry_time = oldstate->expiry_time;
|
2004-04-14 23:40:50 +02:00
|
|
|
newstate->pending_final_cpath = oldstate->pending_final_cpath;
|
|
|
|
oldstate->pending_final_cpath = NULL;
|
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
memcpy(newcirc->rend_query, oldcirc->rend_query,
|
|
|
|
REND_SERVICE_ID_LEN_BASE32+1);
|
2006-07-26 21:05:41 +02:00
|
|
|
memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest,
|
2006-07-23 09:37:35 +02:00
|
|
|
DIGEST_LEN);
|
2006-07-26 21:05:41 +02:00
|
|
|
memcpy(newcirc->rend_cookie, oldcirc->rend_cookie,
|
2006-07-23 09:37:35 +02:00
|
|
|
REND_COOKIE_LEN);
|
2007-10-31 21:48:06 +01:00
|
|
|
newcirc->rend_desc_version = oldcirc->rend_desc_version;
|
2004-04-14 23:40:50 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Launch a circuit to serve as an introduction point for the service
|
2004-05-10 06:34:48 +02:00
|
|
|
* <b>service</b> at the introduction point <b>nickname</b>
|
2004-04-02 00:21:01 +02:00
|
|
|
*/
|
|
|
|
static int
|
2005-12-14 21:40:40 +01:00
|
|
|
rend_service_launch_establish_intro(rend_service_t *service,
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro)
|
2004-04-02 00:21:01 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *launched;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Launching circuit to introduction point %s for service %s",
|
2007-12-21 10:28:22 +01:00
|
|
|
escaped_safe_str(intro->extend_info->nickname),
|
|
|
|
service->service_id);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2005-11-25 09:08:56 +01:00
|
|
|
rep_hist_note_used_internal(time(NULL), 1, 0);
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
|
2004-05-18 03:53:53 +02:00
|
|
|
++service->n_intro_circuits_launched;
|
2007-12-21 10:28:22 +01:00
|
|
|
launched = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
|
2007-12-23 20:15:22 +01:00
|
|
|
intro->extend_info,
|
|
|
|
CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL);
|
|
|
|
|
2004-04-02 00:21:01 +02:00
|
|
|
if (!launched) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
2006-12-07 21:14:29 +01:00
|
|
|
"Can't launch circuit to establish introduction at %s.",
|
2007-12-21 10:28:22 +01:00
|
|
|
escaped_safe_str(intro->extend_info->nickname));
|
2004-04-02 00:21:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2006-07-26 21:05:34 +02:00
|
|
|
strlcpy(launched->rend_query, service->service_id,
|
|
|
|
sizeof(launched->rend_query));
|
2006-07-26 21:05:41 +02:00
|
|
|
memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN);
|
2007-11-29 16:25:04 +01:00
|
|
|
launched->rend_desc_version = service->descriptor_version;
|
2007-12-21 10:28:22 +01:00
|
|
|
if (service->descriptor_version == 2)
|
|
|
|
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
|
2006-07-23 09:37:35 +02:00
|
|
|
if (launched->_base.state == CIRCUIT_STATE_OPEN)
|
2005-08-12 03:26:21 +02:00
|
|
|
rend_service_intro_has_opened(launched);
|
2004-04-02 00:21:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we're done building a circuit to an introduction point:
|
2004-04-02 00:21:01 +02:00
|
|
|
* sends a RELAY_ESTABLISH_INTRO cell.
|
|
|
|
*/
|
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_intro_has_opened(origin_circuit_t *circuit)
|
2004-04-02 00:21:01 +02:00
|
|
|
{
|
|
|
|
rend_service_t *service;
|
2004-10-14 04:47:09 +02:00
|
|
|
size_t len;
|
|
|
|
int r;
|
2004-04-02 00:21:01 +02:00
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
2004-04-06 05:44:36 +02:00
|
|
|
char auth[DIGEST_LEN + 9];
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2006-10-09 17:47:50 +02:00
|
|
|
int reason = END_CIRC_REASON_TORPROTOCOL;
|
2007-10-31 21:48:06 +01:00
|
|
|
crypto_pk_env_t *intro_key;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(circuit->cpath);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
|
|
|
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2004-04-12 07:12:50 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
service = rend_service_get_by_pk_digest_and_version(
|
|
|
|
circuit->rend_pk_digest, circuit->rend_desc_version);
|
2004-04-02 00:21:01 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
serviceid, circuit->_base.n_circ_id);
|
2006-10-17 17:20:00 +02:00
|
|
|
reason = END_CIRC_REASON_NOSUCHSERVICE;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Established circuit %d as introduction point for service %s",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id, serviceid);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/* If the introduction point will not be used in an unversioned
|
|
|
|
* descriptor, use the intro key instead of the service key in
|
|
|
|
* ESTABLISH_INTRO. */
|
2007-11-29 16:25:04 +01:00
|
|
|
if (service->descriptor_version == 0)
|
2007-10-31 21:48:06 +01:00
|
|
|
intro_key = service->private_key;
|
|
|
|
else
|
|
|
|
intro_key = circuit->intro_key;
|
2004-04-02 00:21:01 +02:00
|
|
|
/* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
|
2007-10-31 21:48:06 +01:00
|
|
|
len = crypto_pk_asn1_encode(intro_key, buf+2,
|
2004-04-02 00:21:01 +02:00
|
|
|
RELAY_PAYLOAD_SIZE-2);
|
2004-04-28 22:13:21 +02:00
|
|
|
set_uint16(buf, htons((uint16_t)len));
|
2004-04-02 00:21:01 +02:00
|
|
|
len += 2;
|
2004-04-03 04:40:30 +02:00
|
|
|
memcpy(auth, circuit->cpath->prev->handshake_digest, DIGEST_LEN);
|
|
|
|
memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
|
2004-11-02 03:28:51 +01:00
|
|
|
if (crypto_digest(buf+len, auth, DIGEST_LEN+9))
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
len += 20;
|
2006-10-31 20:17:07 +01:00
|
|
|
note_crypto_pk_op(REND_SERVER);
|
2007-10-31 21:48:06 +01:00
|
|
|
r = crypto_pk_private_sign_digest(intro_key, buf+len, buf, len);
|
2004-04-02 00:21:01 +02:00
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
len += r;
|
|
|
|
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
|
2006-07-23 09:37:35 +02:00
|
|
|
RELAY_COMMAND_ESTABLISH_INTRO,
|
2004-04-02 00:21:01 +02:00
|
|
|
buf, len, circuit->cpath->prev)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_GENERAL,
|
|
|
|
"Couldn't send introduction request for service %s on circuit %d",
|
2006-07-23 09:37:35 +02:00
|
|
|
serviceid, circuit->_base.n_circ_id);
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
err:
|
2006-10-09 17:47:50 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
|
2004-04-02 00:21:01 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
|
2004-05-05 23:32:43 +02:00
|
|
|
* live introduction point, and note that the service descriptor is
|
|
|
|
* now out-of-date.*/
|
2004-04-03 06:55:22 +02:00
|
|
|
int
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_intro_established(origin_circuit_t *circuit, const char *request,
|
2005-12-14 21:40:40 +01:00
|
|
|
size_t request_len)
|
2004-04-03 06:55:22 +02:00
|
|
|
{
|
2004-04-13 19:16:47 +02:00
|
|
|
rend_service_t *service;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2006-06-05 00:42:13 +02:00
|
|
|
(void) request;
|
|
|
|
(void) request_len;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"received INTRO_ESTABLISHED cell on non-intro circuit.");
|
2004-04-03 06:55:22 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2007-10-31 21:48:06 +01:00
|
|
|
service = rend_service_get_by_pk_digest_and_version(
|
|
|
|
circuit->rend_pk_digest, circuit->rend_desc_version);
|
2004-04-13 19:16:47 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Unknown service on introduction circuit %d.",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id);
|
2004-04-13 19:16:47 +02:00
|
|
|
goto err;
|
|
|
|
}
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
service->desc_is_dirty = time(NULL);
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.purpose = CIRCUIT_PURPOSE_S_INTRO;
|
2004-04-03 06:55:22 +02:00
|
|
|
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32 + 1,
|
|
|
|
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2007-08-11 16:13:25 +02:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Received INTRO_ESTABLISHED cell on circuit %d for service %s",
|
|
|
|
circuit->_base.n_circ_id, serviceid);
|
|
|
|
|
2004-04-03 06:55:22 +02:00
|
|
|
return 0;
|
|
|
|
err:
|
2006-10-09 17:47:27 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_TORPROTOCOL);
|
2004-04-03 06:55:22 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Called once a circuit to a rendezvous point is established: sends a
|
2004-04-02 00:21:01 +02:00
|
|
|
* RELAY_COMMAND_RENDEZVOUS1 cell.
|
|
|
|
*/
|
|
|
|
void
|
2006-07-23 09:37:35 +02:00
|
|
|
rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
|
2004-04-02 00:21:01 +02:00
|
|
|
{
|
|
|
|
rend_service_t *service;
|
|
|
|
char buf[RELAY_PAYLOAD_SIZE];
|
|
|
|
crypt_path_t *hop;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2004-04-03 02:58:54 +02:00
|
|
|
char hexcookie[9];
|
2006-10-09 17:47:50 +02:00
|
|
|
int reason;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(circuit->cpath);
|
|
|
|
tor_assert(circuit->build_state);
|
2004-04-02 00:21:01 +02:00
|
|
|
hop = circuit->build_state->pending_final_cpath;
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(hop);
|
2004-04-02 00:21:01 +02:00
|
|
|
|
2006-07-26 21:05:41 +02:00
|
|
|
base16_encode(hexcookie,9,circuit->rend_cookie,4);
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
|
|
|
circuit->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,
|
|
|
|
"Done building circuit %d to rendezvous with "
|
|
|
|
"cookie %s for service %s",
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.n_circ_id, hexcookie, serviceid);
|
2004-04-03 02:58:54 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
service = rend_service_get_by_pk_digest_and_version(
|
|
|
|
circuit->rend_pk_digest, circuit->rend_desc_version);
|
2004-04-02 00:21:01 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
|
|
|
|
"introduction circuit.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* All we need to do is send a RELAY_RENDEZVOUS1 cell... */
|
2006-07-26 21:05:41 +02:00
|
|
|
memcpy(buf, circuit->rend_cookie, REND_COOKIE_LEN);
|
2005-05-03 00:35:18 +02:00
|
|
|
if (crypto_dh_get_public(hop->dh_handshake_state,
|
2004-04-02 00:21:01 +02:00
|
|
|
buf+REND_COOKIE_LEN, DH_KEY_LEN)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL,"Couldn't get DH public key.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
memcpy(buf+REND_COOKIE_LEN+DH_KEY_LEN, hop->handshake_digest,
|
2004-04-03 04:40:30 +02:00
|
|
|
DIGEST_LEN);
|
2004-04-02 00:21:01 +02:00
|
|
|
|
|
|
|
/* Send the cell */
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
|
2006-07-23 09:37:35 +02:00
|
|
|
RELAY_COMMAND_RENDEZVOUS1,
|
2004-04-05 23:39:47 +02:00
|
|
|
buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN,
|
2004-04-02 00:21:01 +02:00
|
|
|
circuit->cpath->prev)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell.");
|
2006-10-09 17:47:50 +02:00
|
|
|
reason = END_CIRC_REASON_INTERNAL;
|
2004-04-02 00:21:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2005-05-03 00:35:18 +02:00
|
|
|
crypto_dh_free(hop->dh_handshake_state);
|
|
|
|
hop->dh_handshake_state = NULL;
|
2004-04-06 22:23:58 +02:00
|
|
|
|
2004-04-02 00:21:01 +02:00
|
|
|
/* Append the cpath entry. */
|
2004-04-06 01:40:59 +02:00
|
|
|
hop->state = CPATH_STATE_OPEN;
|
2004-04-06 23:25:11 +02:00
|
|
|
/* set the windows to default. these are the windows
|
|
|
|
* that bob thinks alice has.
|
|
|
|
*/
|
|
|
|
hop->package_window = CIRCWINDOW_START;
|
|
|
|
hop->deliver_window = CIRCWINDOW_START;
|
|
|
|
|
2004-04-02 00:21:01 +02:00
|
|
|
onion_append_to_cpath(&circuit->cpath, hop);
|
|
|
|
circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
|
|
|
|
|
|
|
|
/* Change the circuit purpose. */
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit->_base.purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
|
2004-04-02 00:21:01 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
err:
|
2006-10-09 17:47:50 +02:00
|
|
|
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
|
2004-04-02 00:21:01 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/*
|
2004-04-01 05:34:05 +02:00
|
|
|
* Manage introduction points
|
2004-05-10 06:34:48 +02:00
|
|
|
*/
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Return the (possibly non-open) introduction circuit ending at
|
2007-12-21 10:28:22 +01:00
|
|
|
* <b>intro</b> for the service whose public key is <b>pk_digest</b> and
|
|
|
|
* which publishes descriptor of version <b>desc_version</b>. Return
|
2004-04-13 03:15:06 +02:00
|
|
|
* NULL if no such service is found.
|
2004-04-06 05:44:36 +02:00
|
|
|
*/
|
2006-07-23 09:37:35 +02:00
|
|
|
static origin_circuit_t *
|
2007-12-21 10:28:22 +01:00
|
|
|
find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest,
|
|
|
|
int desc_version)
|
2004-04-03 06:55:22 +02:00
|
|
|
{
|
2006-07-26 21:05:41 +02:00
|
|
|
origin_circuit_t *circ = NULL;
|
2004-04-03 06:55:22 +02:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
tor_assert(intro);
|
2004-04-03 06:55:22 +02:00
|
|
|
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
|
|
|
CIRCUIT_PURPOSE_S_INTRO))) {
|
2007-12-21 10:28:22 +01:00
|
|
|
if (!strcasecmp(circ->build_state->chosen_exit->identity_digest,
|
|
|
|
intro->extend_info->identity_digest) &&
|
|
|
|
circ->rend_desc_version == desc_version) {
|
2006-07-26 21:05:41 +02:00
|
|
|
return circ;
|
2004-04-03 06:55:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
circ = NULL;
|
|
|
|
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
|
|
|
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
|
2007-12-21 10:28:22 +01:00
|
|
|
if (!strcasecmp(circ->build_state->chosen_exit->identity_digest,
|
|
|
|
intro->extend_info->identity_digest) &&
|
|
|
|
circ->rend_desc_version == desc_version) {
|
2006-07-26 21:05:41 +02:00
|
|
|
return circ;
|
2004-04-03 06:55:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-01-26 01:42:13 +01:00
|
|
|
/** Determine the responsible hidden service directories for the
|
|
|
|
* rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
|
|
|
|
* <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
|
|
|
|
* purposes. */
|
2008-01-27 02:03:30 +01:00
|
|
|
static void
|
2008-01-26 01:42:13 +01:00
|
|
|
directory_post_to_hs_dir(smartlist_t *descs, const char *service_id,
|
|
|
|
int seconds_valid)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
smartlist_t *responsible_dirs = smartlist_create();
|
|
|
|
routerstatus_t *hs_dir;
|
|
|
|
for (i = 0; i < smartlist_len(descs); i++) {
|
|
|
|
rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
|
|
|
|
/* Determine responsible dirs. */
|
|
|
|
if (hid_serv_get_responsible_directories(responsible_dirs,
|
|
|
|
desc->desc_id) < 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 < smartlist_len(responsible_dirs); j++) {
|
|
|
|
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
|
|
|
hs_dir = smartlist_get(responsible_dirs, j);
|
|
|
|
/* Send publish request. */
|
|
|
|
directory_initiate_command_routerstatus(hs_dir,
|
|
|
|
DIR_PURPOSE_UPLOAD_RENDDESC_V2,
|
|
|
|
ROUTER_PURPOSE_GENERAL,
|
|
|
|
1, NULL, desc->desc_str,
|
|
|
|
strlen(desc->desc_str), 0);
|
|
|
|
base32_encode(desc_id_base32, sizeof(desc_id_base32),
|
|
|
|
desc->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);
|
|
|
|
}
|
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/** Encode and sign up-to-date v0 and/or v2 service descriptors for
|
|
|
|
* <b>service</b>, and upload it/them to all the dirservers/to the
|
|
|
|
* responsible hidden service directories.
|
2004-05-05 23:32:43 +02:00
|
|
|
*/
|
2004-04-13 19:16:47 +02:00
|
|
|
static void
|
2007-09-18 23:17:45 +02:00
|
|
|
upload_service_descriptor(rend_service_t *service)
|
2004-04-13 19:16:47 +02:00
|
|
|
{
|
2007-10-31 21:48:06 +01:00
|
|
|
time_t now = time(NULL);
|
|
|
|
int rendpostperiod;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2007-10-31 21:48:06 +01:00
|
|
|
int uploaded = 0;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
|
|
|
/* Update the descriptor. */
|
|
|
|
rend_service_update_descriptor(service);
|
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
rendpostperiod = get_options()->RendPostPeriod;
|
|
|
|
|
|
|
|
/* Upload unversioned (v0) descriptor? */
|
2007-11-29 16:25:04 +01:00
|
|
|
if (service->descriptor_version == 0 &&
|
2007-10-31 21:48:06 +01:00
|
|
|
get_options()->PublishHidServDescriptors) {
|
|
|
|
char *desc;
|
|
|
|
size_t desc_len;
|
|
|
|
/* Encode the descriptor. */
|
|
|
|
if (rend_encode_service_descriptor(service->desc,
|
|
|
|
service->private_key,
|
|
|
|
&desc, &desc_len)<0) {
|
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
|
|
|
|
"not uploading.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Post it to the dirservers */
|
|
|
|
rend_get_service_id(service->desc->pk, serviceid);
|
|
|
|
log_info(LD_REND, "Sending publish request for hidden service %s",
|
2007-08-11 16:13:25 +02:00
|
|
|
serviceid);
|
2007-10-31 21:48:06 +01:00
|
|
|
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_RENDDESC,
|
|
|
|
ROUTER_PURPOSE_GENERAL,
|
|
|
|
HIDSERV_AUTHORITY, desc, desc_len, 0);
|
|
|
|
tor_free(desc);
|
|
|
|
service->next_upload_time = now + rendpostperiod;
|
|
|
|
uploaded = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Upload v2 descriptor? */
|
2007-11-29 16:25:04 +01:00
|
|
|
if (service->descriptor_version == 2 &&
|
2007-10-31 21:48:06 +01:00
|
|
|
get_options()->PublishHidServDescriptors) {
|
2008-02-05 22:39:29 +01:00
|
|
|
networkstatus_t *c = networkstatus_get_latest_consensus();
|
2008-01-24 23:46:29 +01:00
|
|
|
if (c && smartlist_len(c->routerstatus_list) > 0) {
|
2007-10-31 21:48:06 +01:00
|
|
|
int seconds_valid;
|
2007-12-15 21:28:09 +01:00
|
|
|
smartlist_t *descs = smartlist_create();
|
2007-10-31 21:48:06 +01:00
|
|
|
int i;
|
|
|
|
/* Encode the current descriptor. */
|
2007-12-15 21:28:09 +01:00
|
|
|
seconds_valid = rend_encode_v2_descriptors(descs, service->desc, now,
|
|
|
|
NULL, 0);
|
2007-10-31 21:48:06 +01:00
|
|
|
if (seconds_valid < 0) {
|
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
|
|
|
|
"not uploading.");
|
2007-12-15 21:50:56 +01:00
|
|
|
smartlist_free(descs);
|
2007-10-31 21:48:06 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Post the current descriptors to the hidden service directories. */
|
|
|
|
rend_get_service_id(service->desc->pk, serviceid);
|
|
|
|
log_info(LD_REND, "Sending publish request for hidden service %s",
|
|
|
|
serviceid);
|
2007-12-15 21:28:09 +01:00
|
|
|
directory_post_to_hs_dir(descs, serviceid, seconds_valid);
|
2007-10-31 21:48:06 +01:00
|
|
|
/* Free memory for descriptors. */
|
2007-12-15 21:50:56 +01:00
|
|
|
for (i = 0; i < smartlist_len(descs); i++)
|
2007-12-15 21:28:09 +01:00
|
|
|
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
|
|
|
smartlist_clear(descs);
|
2007-10-31 21:48:06 +01:00
|
|
|
/* Update next upload time. */
|
|
|
|
if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
|
|
|
|
> rendpostperiod)
|
|
|
|
service->next_upload_time = now + rendpostperiod;
|
|
|
|
else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
|
|
|
|
service->next_upload_time = now + seconds_valid + 1;
|
|
|
|
else
|
|
|
|
service->next_upload_time = now + seconds_valid -
|
|
|
|
REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
|
|
|
|
/* Post also the next descriptors, if necessary. */
|
|
|
|
if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
|
2007-12-15 21:28:09 +01:00
|
|
|
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
|
|
|
|
now, NULL, 1);
|
2007-10-31 21:48:06 +01:00
|
|
|
if (seconds_valid < 0) {
|
|
|
|
log_warn(LD_BUG, "Internal error: couldn't encode service "
|
|
|
|
"descriptor; not uploading.");
|
2007-12-15 21:50:56 +01:00
|
|
|
smartlist_free(descs);
|
2007-10-31 21:48:06 +01:00
|
|
|
return;
|
|
|
|
}
|
2007-12-15 21:28:09 +01:00
|
|
|
directory_post_to_hs_dir(descs, serviceid, seconds_valid);
|
2007-10-31 21:48:06 +01:00
|
|
|
/* Free memory for descriptors. */
|
2007-12-15 21:50:56 +01:00
|
|
|
for (i = 0; i < smartlist_len(descs); i++)
|
2007-12-15 21:28:09 +01:00
|
|
|
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
|
2007-10-31 21:48:06 +01:00
|
|
|
}
|
2007-12-15 21:50:56 +01:00
|
|
|
smartlist_free(descs);
|
2007-10-31 21:48:06 +01:00
|
|
|
uploaded = 1;
|
|
|
|
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If not uploaded, try again in one minute. */
|
|
|
|
if (!uploaded)
|
|
|
|
service->next_upload_time = now + 60;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2007-10-31 21:48:06 +01:00
|
|
|
/* Unmark dirty flag of this service. */
|
2004-04-13 19:16:47 +02:00
|
|
|
service->desc_is_dirty = 0;
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** For every service, check how many intro points it currently has, and:
|
2004-04-03 01:01:00 +02:00
|
|
|
* - Pick new intro points as necessary.
|
|
|
|
* - Launch circuits to any new intro points.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
|
|
|
rend_services_introduce(void)
|
|
|
|
{
|
2004-04-02 00:21:01 +02:00
|
|
|
int i,j,r;
|
2004-04-01 05:23:28 +02:00
|
|
|
routerinfo_t *router;
|
2004-04-02 00:21:01 +02:00
|
|
|
rend_service_t *service;
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro;
|
2004-04-13 19:16:47 +02:00
|
|
|
int changed, prev_intro_nodes;
|
2004-04-07 23:36:03 +02:00
|
|
|
smartlist_t *intro_routers, *exclude_routers;
|
2004-05-18 03:53:53 +02:00
|
|
|
time_t now;
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-04-06 23:19:59 +02:00
|
|
|
intro_routers = smartlist_create();
|
2004-04-07 23:36:03 +02:00
|
|
|
exclude_routers = smartlist_create();
|
2004-05-18 03:53:53 +02:00
|
|
|
now = time(NULL);
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2005-01-14 05:35:53 +01:00
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_clear(intro_routers);
|
2004-04-03 02:58:54 +02:00
|
|
|
service = smartlist_get(rend_service_list, i);
|
|
|
|
|
2004-04-25 22:37:37 +02:00
|
|
|
tor_assert(service);
|
2004-04-03 01:01:00 +02:00
|
|
|
changed = 0;
|
2004-05-18 03:53:53 +02:00
|
|
|
if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) {
|
|
|
|
/* One period has elapsed; we can try building circuits again. */
|
|
|
|
service->intro_period_started = now;
|
|
|
|
service->n_intro_circuits_launched = 0;
|
2004-11-23 00:28:26 +01:00
|
|
|
} else if (service->n_intro_circuits_launched >=
|
|
|
|
MAX_INTRO_CIRCS_PER_PERIOD) {
|
2004-05-18 03:53:53 +02:00
|
|
|
/* We have failed too many times in this period; wait for the next
|
|
|
|
* one before we try again. */
|
|
|
|
continue;
|
|
|
|
}
|
2004-04-03 01:01:00 +02:00
|
|
|
|
2005-12-14 21:40:40 +01:00
|
|
|
/* Find out which introduction points we have in progress for this
|
|
|
|
service. */
|
2005-01-14 05:35:53 +01:00
|
|
|
for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
|
2004-04-16 13:48:03 +02:00
|
|
|
intro = smartlist_get(service->intro_nodes, j);
|
2007-12-21 10:28:22 +01:00
|
|
|
router = router_get_by_digest(intro->extend_info->identity_digest);
|
|
|
|
if (!router || !find_intro_circuit(intro, service->pk_digest,
|
|
|
|
service->descriptor_version)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND,"Giving up on %s as intro point for %s.",
|
2007-12-21 10:28:22 +01:00
|
|
|
intro->extend_info->nickname, service->service_id);
|
|
|
|
rend_intro_point_free(intro);
|
2004-04-03 06:55:22 +02:00
|
|
|
smartlist_del(service->intro_nodes,j--);
|
Introduce a notion of 'internal' circs, which are chosen without regard
to the exit policy of the last hop. Intro and rendezvous circs must
be internal circs, to avoid leaking information. Resolve and connect
streams can use internal circs if they want.
New circuit pooling algorithm: make sure to have enough circs around
to satisfy any predicted ports, and also make sure to have 2 internal
circs around if we've required internal circs lately (with high uptime
if we've seen that lately).
Split NewCircuitPeriod config option into NewCircuitPeriod (30 secs),
which describes how often we retry making new circuits if current ones
are dirty, and MaxCircuitDirtiness (10 mins), which describes how long
we're willing to make use of an already-dirty circuit.
Once rendezvous circuits are established, keep using the same circuit as
long as you attach a new stream to it at least every 10 minutes. (So web
browsing doesn't require you to build new rend circs every 30 seconds.)
Cannibalize GENERAL circs to be C_REND, C_INTRO, S_INTRO, and S_REND
circ as necessary, if there are any completed ones lying around when
we try to launch one.
Re-instate the ifdef's to use version-0 style introduce cells, since
there was yet another bug in handling version-1 style. We'll try switching
over again after 0.0.9 is obsolete.
Bugfix: when choosing an exit node for a new non-internal circ, don't take
into account whether it'll be useful for any pending x.onion addresses --
it won't.
Bugfix: we weren't actually publishing the hidden service descriptor when
it became dirty. So we only published it every 20 minutes or so, which
means when you first start your Tor, the hidden service will seem broken.
svn:r3360
2005-01-17 19:13:09 +01:00
|
|
|
changed = 1;
|
|
|
|
service->desc_is_dirty = now;
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_add(intro_routers, router);
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We have enough intro points, and the intro points we thought we had were
|
|
|
|
* all connected.
|
|
|
|
*/
|
2004-05-18 03:53:53 +02:00
|
|
|
if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) {
|
|
|
|
/* We have all our intro points! Start a fresh period and reset the
|
|
|
|
* circuit count. */
|
|
|
|
service->intro_period_started = now;
|
|
|
|
service->n_intro_circuits_launched = 0;
|
2004-04-03 01:01:00 +02:00
|
|
|
continue;
|
2004-05-18 03:53:53 +02:00
|
|
|
}
|
2004-04-03 01:01:00 +02:00
|
|
|
|
|
|
|
/* Remember how many introduction circuits we started with. */
|
2004-04-03 02:58:54 +02:00
|
|
|
prev_intro_nodes = smartlist_len(service->intro_nodes);
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-04-07 23:36:03 +02:00
|
|
|
smartlist_add_all(exclude_routers, intro_routers);
|
2004-04-01 05:23:28 +02:00
|
|
|
/* The directory is now here. Pick three ORs as intro points. */
|
2004-04-03 01:01:00 +02:00
|
|
|
for (j=prev_intro_nodes; j < NUM_INTRO_POINTS; ++j) {
|
2004-05-20 21:12:28 +02:00
|
|
|
router = router_choose_random_node(service->intro_prefer_nodes,
|
2006-02-12 04:43:39 +01:00
|
|
|
service->intro_exclude_nodes, exclude_routers, 1, 0, 0,
|
2006-03-19 02:21:59 +01:00
|
|
|
get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION,
|
2006-10-01 23:59:09 +02:00
|
|
|
0, 0);
|
2004-04-03 01:01:00 +02:00
|
|
|
if (!router) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND,
|
|
|
|
"Could only establish %d introduction points for %s.",
|
|
|
|
smartlist_len(service->intro_nodes), service->service_id);
|
2004-04-03 01:01:00 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
changed = 1;
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_add(intro_routers, router);
|
2004-04-07 23:36:03 +02:00
|
|
|
smartlist_add(exclude_routers, router);
|
2007-12-21 10:28:22 +01:00
|
|
|
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
|
|
|
|
intro->extend_info = extend_info_from_router(router);
|
|
|
|
if (service->descriptor_version == 2) {
|
|
|
|
intro->intro_key = crypto_new_pk_env();
|
|
|
|
tor_assert(!crypto_pk_generate_key(intro->intro_key));
|
|
|
|
}
|
|
|
|
smartlist_add(service->intro_nodes, intro);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "Picked router %s as an intro point for %s.",
|
|
|
|
router->nickname, service->service_id);
|
2004-04-01 05:23:28 +02:00
|
|
|
}
|
|
|
|
|
2004-04-25 23:32:04 +02:00
|
|
|
/* Reset exclude_routers, for the next time around the loop. */
|
|
|
|
smartlist_clear(exclude_routers);
|
2004-04-07 23:36:03 +02:00
|
|
|
|
2004-04-13 19:16:47 +02:00
|
|
|
/* If there's no need to launch new circuits, stop here. */
|
|
|
|
if (!changed)
|
2004-04-03 01:01:00 +02:00
|
|
|
continue;
|
2004-04-01 05:23:28 +02:00
|
|
|
|
2004-04-03 01:01:00 +02:00
|
|
|
/* Establish new introduction points. */
|
2004-04-03 02:58:54 +02:00
|
|
|
for (j=prev_intro_nodes; j < smartlist_len(service->intro_nodes); ++j) {
|
|
|
|
intro = smartlist_get(service->intro_nodes, j);
|
2004-04-03 01:01:00 +02:00
|
|
|
r = rend_service_launch_establish_intro(service, intro);
|
|
|
|
if (r<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Error launching circuit to node %s for service %s.",
|
2007-12-21 10:28:22 +01:00
|
|
|
intro->extend_info->nickname, service->service_id);
|
2004-04-03 01:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
2004-04-02 00:21:01 +02:00
|
|
|
}
|
2004-04-06 23:19:59 +02:00
|
|
|
smartlist_free(intro_routers);
|
2004-04-07 23:36:03 +02:00
|
|
|
smartlist_free(exclude_routers);
|
2004-04-01 05:23:28 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Regenerate and upload rendezvous service descriptors for all
|
2004-11-15 10:05:54 +01:00
|
|
|
* services, if necessary. If the descriptor has been dirty enough
|
|
|
|
* for long enough, definitely upload; else only upload when the
|
|
|
|
* periodic timeout has expired.
|
|
|
|
*
|
|
|
|
* For the first upload, pick a random time between now and two periods
|
|
|
|
* from now, and pick it independently for each service.
|
2004-05-05 23:32:43 +02:00
|
|
|
*/
|
2004-04-13 19:16:47 +02:00
|
|
|
void
|
2005-06-11 20:52:12 +02:00
|
|
|
rend_consider_services_upload(time_t now)
|
|
|
|
{
|
2004-04-13 19:16:47 +02:00
|
|
|
int i;
|
|
|
|
rend_service_t *service;
|
2004-11-15 10:05:54 +01:00
|
|
|
int rendpostperiod = get_options()->RendPostPeriod;
|
2004-04-13 19:16:47 +02:00
|
|
|
|
2006-02-19 23:02:02 +01:00
|
|
|
if (!get_options()->PublishHidServDescriptors)
|
|
|
|
return;
|
|
|
|
|
2005-01-14 05:35:53 +01:00
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
2004-04-13 19:16:47 +02:00
|
|
|
service = smartlist_get(rend_service_list, i);
|
2004-11-15 10:05:54 +01:00
|
|
|
if (!service->next_upload_time) { /* never been uploaded yet */
|
|
|
|
service->next_upload_time =
|
2005-10-07 00:18:01 +02:00
|
|
|
now + crypto_rand_int(2*rendpostperiod);
|
2004-11-15 10:05:54 +01:00
|
|
|
}
|
|
|
|
if (service->next_upload_time < now ||
|
|
|
|
(service->desc_is_dirty &&
|
2007-03-01 01:41:28 +01:00
|
|
|
service->desc_is_dirty < now-30)) {
|
2004-11-15 10:05:54 +01:00
|
|
|
/* if it's time, or if the directory servers have a wrong service
|
2007-03-01 01:41:28 +01:00
|
|
|
* descriptor and ours has been stable for 30 seconds, upload a
|
2005-08-12 17:05:05 +02:00
|
|
|
* new one of each format. */
|
2007-09-18 23:17:45 +02:00
|
|
|
upload_service_descriptor(service);
|
2004-11-15 10:05:54 +01:00
|
|
|
}
|
2004-04-13 19:16:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Log the status of introduction points for all rendezvous services
|
2004-12-01 04:48:14 +01:00
|
|
|
* at log severity <b>severity</b>.
|
2004-05-05 23:32:43 +02:00
|
|
|
*/
|
2004-04-09 22:02:16 +02:00
|
|
|
void
|
|
|
|
rend_service_dump_stats(int severity)
|
|
|
|
{
|
|
|
|
int i,j;
|
|
|
|
rend_service_t *service;
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro;
|
|
|
|
const char *safe_name;
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *circ;
|
2004-04-09 22:02:16 +02:00
|
|
|
|
|
|
|
for (i=0; i < smartlist_len(rend_service_list); ++i) {
|
|
|
|
service = smartlist_get(rend_service_list, i);
|
2005-12-14 21:40:40 +01:00
|
|
|
log(severity, LD_GENERAL, "Service configured in \"%s\":",
|
|
|
|
service->directory);
|
2004-04-09 22:02:16 +02:00
|
|
|
for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
|
2007-12-21 10:28:22 +01:00
|
|
|
intro = smartlist_get(service->intro_nodes, j);
|
|
|
|
safe_name = safe_str(intro->extend_info->nickname);
|
2006-12-07 18:04:44 +01:00
|
|
|
|
2007-12-21 10:28:22 +01:00
|
|
|
circ = find_intro_circuit(intro, service->pk_digest,
|
|
|
|
service->descriptor_version);
|
2004-04-09 22:02:16 +02:00
|
|
|
if (!circ) {
|
2006-12-07 18:04:44 +01:00
|
|
|
log(severity, LD_GENERAL, " Intro point %d at %s: no circuit",
|
|
|
|
j, safe_name);
|
2004-04-09 22:02:16 +02:00
|
|
|
continue;
|
|
|
|
}
|
2006-12-07 18:04:44 +01:00
|
|
|
log(severity, LD_GENERAL, " Intro point %d at %s: circuit is %s",
|
|
|
|
j, safe_name, circuit_state_to_string(circ->_base.state));
|
2004-04-09 22:02:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-10 09:27:29 +02:00
|
|
|
/** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
|
2004-05-10 06:34:48 +02:00
|
|
|
* 'circ', and look up the port and address based on conn-\>port.
|
|
|
|
* Assign the actual conn-\>addr and conn-\>port. Return -1 if failure,
|
2004-05-05 23:32:43 +02:00
|
|
|
* or 0 for success.
|
2004-04-07 00:05:49 +02:00
|
|
|
*/
|
|
|
|
int
|
2006-07-26 21:07:26 +02:00
|
|
|
rend_service_set_connection_addr_port(edge_connection_t *conn,
|
2006-07-23 09:37:35 +02:00
|
|
|
origin_circuit_t *circ)
|
2004-04-07 00:05:49 +02:00
|
|
|
{
|
|
|
|
rend_service_t *service;
|
2007-11-29 16:25:04 +01:00
|
|
|
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
|
2007-05-29 20:58:16 +02:00
|
|
|
smartlist_t *matching_ports;
|
|
|
|
rend_service_port_config_t *chosen_port;
|
2004-04-07 00:05:49 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_REND,"beginning to hunt for addr/port");
|
2007-11-29 16:25:04 +01:00
|
|
|
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
|
|
|
|
circ->rend_pk_digest, REND_SERVICE_ID_LEN);
|
2007-10-31 21:48:06 +01:00
|
|
|
service = rend_service_get_by_pk_digest_and_version(circ->rend_pk_digest,
|
|
|
|
circ->rend_desc_version);
|
2004-04-07 00:05:49 +02:00
|
|
|
if (!service) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
|
|
|
|
"rendezvous circuit %d; closing.",
|
2006-07-23 09:37:35 +02:00
|
|
|
serviceid, circ->_base.n_circ_id);
|
2004-04-15 03:23:43 +02:00
|
|
|
return -1;
|
2004-04-07 00:05:49 +02:00
|
|
|
}
|
2007-05-29 20:58:16 +02:00
|
|
|
matching_ports = smartlist_create();
|
|
|
|
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
|
|
|
|
{
|
2006-07-26 21:07:26 +02:00
|
|
|
if (conn->_base.port == p->virtual_port) {
|
2007-05-29 20:58:16 +02:00
|
|
|
smartlist_add(matching_ports, p);
|
2004-04-07 00:05:49 +02:00
|
|
|
}
|
2007-05-29 20:58:16 +02:00
|
|
|
});
|
|
|
|
chosen_port = smartlist_choose(matching_ports);
|
|
|
|
smartlist_free(matching_ports);
|
|
|
|
if (chosen_port) {
|
|
|
|
conn->_base.addr = chosen_port->real_addr;
|
|
|
|
conn->_base.port = chosen_port->real_port;
|
|
|
|
return 0;
|
2004-04-07 00:05:49 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_REND, "No virtual port mapping exists for port %d on service %s",
|
2006-07-26 21:07:26 +02:00
|
|
|
conn->_base.port,serviceid);
|
2004-04-07 00:05:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2005-06-09 21:03:31 +02:00
|
|
|
|