2004-11-07 02:33:06 +01:00
|
|
|
/* Copyright 2001-2004 Roger Dingledine.
|
|
|
|
* Copyright 2004 Roger Dingledine, Nick Mathewson. */
|
2003-09-27 23:30:10 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
/* $Id$ */
|
2004-11-29 23:25:31 +01:00
|
|
|
const char dirserv_c_id[] = "$Id$";
|
2003-09-27 23:30:10 +02:00
|
|
|
|
|
|
|
#include "or.h"
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/**
|
|
|
|
* \file dirserv.c
|
|
|
|
* \brief Directory server core implementation.
|
2004-05-10 06:34:48 +02:00
|
|
|
**/
|
2004-05-05 02:30:43 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** How far in the future do we allow a router to get? (seconds) */
|
2003-10-22 18:41:35 +02:00
|
|
|
#define ROUTER_ALLOW_SKEW (30*60)
|
2004-09-23 21:58:44 +02:00
|
|
|
/** How many seconds do we wait before regenerating the directory? */
|
|
|
|
#define DIR_REGEN_SLACK_TIME 10
|
2003-10-22 18:41:35 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Do we need to regenerate the directory when someone asks for it? */
|
2003-09-27 23:30:10 +02:00
|
|
|
static int the_directory_is_dirty = 1;
|
2004-06-25 02:29:31 +02:00
|
|
|
static int runningrouters_is_dirty = 1;
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-10-27 02:48:51 +02:00
|
|
|
static int list_server_status(char **running_routers_out,
|
|
|
|
char **router_status_out);
|
2004-04-13 22:06:08 +02:00
|
|
|
static void directory_remove_unrecognized(void);
|
2004-09-02 20:57:09 +02:00
|
|
|
static int dirserv_regenerate_directory(void);
|
2004-11-02 04:02:17 +01:00
|
|
|
/* Should be static; exposed for testing */
|
|
|
|
void add_fingerprint_to_dir(const char *nickname, const char *fp);
|
2003-10-01 02:43:34 +02:00
|
|
|
|
2003-09-29 09:50:08 +02:00
|
|
|
/************** Fingerprint handling code ************/
|
|
|
|
|
2003-09-27 23:30:10 +02:00
|
|
|
typedef struct fingerprint_entry_t {
|
|
|
|
char *nickname;
|
2004-10-06 15:31:48 +02:00
|
|
|
char *fingerprint; /**< Stored as HEX_DIGEST_LEN characters, followed by a NUL */
|
2003-09-27 23:30:10 +02:00
|
|
|
} fingerprint_entry_t;
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** List of nickname-\>identity fingerprint mappings for all the routers
|
2004-05-05 02:30:43 +02:00
|
|
|
* that we recognize. Used to prevent Sybil attacks. */
|
2004-05-18 19:41:40 +02:00
|
|
|
static smartlist_t *fingerprint_list = NULL;
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Add the fingerprint <b>fp</b> for the nickname <b>nickname</b> to
|
|
|
|
* the global list of recognized identity key fingerprints.
|
2004-05-05 02:30:43 +02:00
|
|
|
*/
|
2004-03-29 21:50:59 +02:00
|
|
|
void /* Should be static; exposed for testing */
|
2003-09-30 21:27:54 +02:00
|
|
|
add_fingerprint_to_dir(const char *nickname, const char *fp)
|
|
|
|
{
|
|
|
|
int i;
|
2004-05-18 19:41:40 +02:00
|
|
|
fingerprint_entry_t *ent;
|
|
|
|
if (!fingerprint_list)
|
|
|
|
fingerprint_list = smartlist_create();
|
|
|
|
|
|
|
|
for (i = 0; i < smartlist_len(fingerprint_list); ++i) {
|
|
|
|
ent = smartlist_get(fingerprint_list, i);
|
|
|
|
if (!strcasecmp(ent->nickname,nickname)) {
|
|
|
|
tor_free(ent->fingerprint);
|
|
|
|
ent->fingerprint = tor_strdup(fp);
|
2003-09-30 21:27:54 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2004-05-18 19:41:40 +02:00
|
|
|
ent = tor_malloc(sizeof(fingerprint_entry_t));
|
|
|
|
ent->nickname = tor_strdup(nickname);
|
|
|
|
ent->fingerprint = tor_strdup(fp);
|
2004-10-06 15:31:48 +02:00
|
|
|
tor_strstrip(ent->fingerprint, " ");
|
2004-05-18 19:41:40 +02:00
|
|
|
smartlist_add(fingerprint_list, ent);
|
2003-09-30 21:27:54 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Add the nickname and fingerprint for this OR to the recognized list.
|
2004-05-05 02:30:43 +02:00
|
|
|
*/
|
2003-09-30 21:27:54 +02:00
|
|
|
int
|
|
|
|
dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
|
|
|
|
{
|
|
|
|
char fp[FINGERPRINT_LEN+1];
|
2004-10-06 15:31:48 +02:00
|
|
|
if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
|
2003-09-30 21:27:54 +02:00
|
|
|
log_fn(LOG_ERR, "Error computing fingerprint");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
add_fingerprint_to_dir(nickname, fp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Parse the nickname-\>fingerprint mappings stored in the file named
|
|
|
|
* <b>fname</b>. The file format is line-based, with each non-blank
|
2004-05-05 02:30:43 +02:00
|
|
|
* holding one nickname, some space, and a fingerprint for that
|
|
|
|
* nickname. On success, replace the current fingerprint list with
|
2004-05-10 06:34:48 +02:00
|
|
|
* the contents of <b>fname</b> and return 0. On failure, leave the
|
2004-05-05 02:30:43 +02:00
|
|
|
* current fingerprint list untouched, and return -1. */
|
2003-12-14 07:03:46 +01:00
|
|
|
int
|
2003-09-27 23:30:10 +02:00
|
|
|
dirserv_parse_fingerprint_file(const char *fname)
|
|
|
|
{
|
2004-11-06 06:18:11 +01:00
|
|
|
char *cf;
|
2003-09-29 09:50:08 +02:00
|
|
|
char *nickname, *fingerprint;
|
2004-05-18 19:41:40 +02:00
|
|
|
smartlist_t *fingerprint_list_new;
|
2003-09-29 09:50:08 +02:00
|
|
|
int i, result;
|
2004-05-18 19:41:40 +02:00
|
|
|
fingerprint_entry_t *ent;
|
2004-11-06 06:18:11 +01:00
|
|
|
struct config_line_t *front=NULL, *list;
|
2003-09-29 09:50:08 +02:00
|
|
|
|
2004-11-06 06:18:11 +01:00
|
|
|
cf = read_file_to_str(fname, 0);
|
|
|
|
if (!cf) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN, "Cannot open fingerprint file %s", fname);
|
2003-09-29 09:50:08 +02:00
|
|
|
return -1;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2004-11-06 06:18:11 +01:00
|
|
|
result = config_get_lines(cf, &front);
|
|
|
|
tor_free(cf);
|
|
|
|
if (result < 0) {
|
|
|
|
log_fn(LOG_WARN, "Error reading from fingerprint file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
fingerprint_list_new = smartlist_create();
|
2004-11-06 06:18:11 +01:00
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
for (list=front; list; list=list->next) {
|
2004-11-06 06:18:11 +01:00
|
|
|
nickname = list->key; fingerprint = list->value;
|
2003-09-27 23:30:10 +02:00
|
|
|
if (strlen(nickname) > MAX_NICKNAME_LEN) {
|
2004-12-13 01:44:39 +01:00
|
|
|
log(LOG_NOTICE, "Nickname '%s' too long in fingerprint file. Skipping.", nickname);
|
2003-09-29 09:50:08 +02:00
|
|
|
continue;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (strlen(fingerprint) != FINGERPRINT_LEN ||
|
2004-11-28 12:39:53 +01:00
|
|
|
!crypto_pk_check_fingerprint_syntax(fingerprint)) {
|
2004-12-13 01:44:39 +01:00
|
|
|
log_fn(LOG_NOTICE, "Invalid fingerprint (nickname '%s', fingerprint %s). Skipping.",
|
2003-09-29 09:50:08 +02:00
|
|
|
nickname, fingerprint);
|
|
|
|
continue;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2004-11-09 21:04:00 +01:00
|
|
|
if (0==strcasecmp(nickname, DEFAULT_CLIENT_NICKNAME)) {
|
2004-11-09 19:22:17 +01:00
|
|
|
/* If you approved an OR called "client", then clients who use
|
|
|
|
* the default nickname could all be rejected. That's no good. */
|
2004-12-13 01:44:39 +01:00
|
|
|
log(LOG_NOTICE,
|
2004-11-09 19:22:17 +01:00
|
|
|
"Authorizing a nickname '%s' would break many clients; skipping.",
|
|
|
|
DEFAULT_CLIENT_NICKNAME);
|
|
|
|
continue;
|
|
|
|
}
|
2004-05-18 19:41:40 +02:00
|
|
|
for (i = 0; i < smartlist_len(fingerprint_list_new); ++i) {
|
|
|
|
ent = smartlist_get(fingerprint_list_new, i);
|
|
|
|
if (0==strcasecmp(ent->nickname, nickname)) {
|
2004-12-13 01:44:39 +01:00
|
|
|
log(LOG_NOTICE, "Duplicate nickname '%s'. Skipping.",nickname);
|
2003-09-29 09:50:08 +02:00
|
|
|
break; /* out of the for. the 'if' below means skip to the next line. */
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (i == smartlist_len(fingerprint_list_new)) { /* not a duplicate */
|
2004-05-18 19:41:40 +02:00
|
|
|
ent = tor_malloc(sizeof(fingerprint_entry_t));
|
|
|
|
ent->nickname = tor_strdup(nickname);
|
|
|
|
ent->fingerprint = tor_strdup(fingerprint);
|
2004-10-06 15:31:48 +02:00
|
|
|
tor_strstrip(ent->fingerprint, " ");
|
2004-05-18 19:41:40 +02:00
|
|
|
smartlist_add(fingerprint_list_new, ent);
|
2003-09-29 09:50:08 +02:00
|
|
|
}
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2004-11-06 06:18:11 +01:00
|
|
|
|
|
|
|
config_free_lines(front);
|
|
|
|
dirserv_free_fingerprint_list();
|
|
|
|
fingerprint_list = fingerprint_list_new;
|
|
|
|
/* Delete any routers whose fingerprints we no longer recognize */
|
|
|
|
directory_remove_unrecognized();
|
|
|
|
return 0;
|
2003-12-14 06:25:23 +01:00
|
|
|
}
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Check whether <b>router</b> has a nickname/identity key combination that
|
2004-05-05 02:30:43 +02:00
|
|
|
* we recognize from the fingerprint list. Return 1 if router's
|
|
|
|
* identity and nickname match, -1 if we recognize the nickname but
|
|
|
|
* the identity key is wrong, and 0 if the nickname is not known. */
|
2003-09-27 23:30:10 +02:00
|
|
|
int
|
|
|
|
dirserv_router_fingerprint_is_known(const routerinfo_t *router)
|
|
|
|
{
|
2004-05-18 19:41:40 +02:00
|
|
|
int i, found=0;
|
2003-09-27 23:30:10 +02:00
|
|
|
fingerprint_entry_t *ent =NULL;
|
|
|
|
char fp[FINGERPRINT_LEN+1];
|
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
if (!fingerprint_list)
|
|
|
|
fingerprint_list = smartlist_create();
|
|
|
|
|
|
|
|
log_fn(LOG_DEBUG, "%d fingerprints known.", smartlist_len(fingerprint_list));
|
|
|
|
for (i=0;i<smartlist_len(fingerprint_list);++i) {
|
|
|
|
ent = smartlist_get(fingerprint_list, i);
|
|
|
|
log_fn(LOG_DEBUG,"%s vs %s", router->nickname, ent->nickname);
|
|
|
|
if (!strcasecmp(router->nickname,ent->nickname)) {
|
|
|
|
found = 1;
|
2003-09-27 23:30:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-12-13 23:53:17 +01:00
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
if (!found) { /* No such server known */
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_INFO,"no fingerprint found for '%s'",router->nickname);
|
2003-09-27 23:30:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2004-10-06 15:31:48 +02:00
|
|
|
if (crypto_pk_get_fingerprint(router->identity_pkey, fp, 0)) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"error computing fingerprint");
|
2003-11-12 06:12:51 +01:00
|
|
|
return -1;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
if (0==strcasecmp(ent->fingerprint, fp)) {
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_DEBUG,"good fingerprint for '%s'",router->nickname);
|
2003-09-29 09:50:08 +02:00
|
|
|
return 1; /* Right fingerprint. */
|
2003-09-27 23:30:10 +02:00
|
|
|
} else {
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_WARN,"mismatched fingerprint for '%s'",router->nickname);
|
2003-11-12 06:12:51 +01:00
|
|
|
return -1; /* Wrong fingerprint. */
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-29 01:27:41 +02:00
|
|
|
/** If we are an authoritative dirserver, and the list of approved
|
2004-09-29 00:24:56 +02:00
|
|
|
* servers contains one whose identity key digest is <b>digest</b>,
|
|
|
|
* return that router's nickname. Otherwise return NULL. */
|
|
|
|
const char *dirserv_get_nickname_by_digest(const char *digest)
|
2004-05-05 06:55:00 +02:00
|
|
|
{
|
2004-10-13 21:17:27 +02:00
|
|
|
char hexdigest[HEX_DIGEST_LEN+1];
|
2004-05-18 19:41:40 +02:00
|
|
|
if (!fingerprint_list)
|
2004-09-29 00:24:56 +02:00
|
|
|
return NULL;
|
|
|
|
tor_assert(digest);
|
|
|
|
|
2004-10-13 21:17:27 +02:00
|
|
|
base16_encode(hexdigest, HEX_DIGEST_LEN+1, digest, DIGEST_LEN);
|
2004-09-29 00:24:56 +02:00
|
|
|
SMARTLIST_FOREACH(fingerprint_list, fingerprint_entry_t*, ent,
|
2004-10-13 21:17:27 +02:00
|
|
|
{ if (!strcasecmp(hexdigest, ent->fingerprint))
|
2004-09-29 00:24:56 +02:00
|
|
|
return ent->nickname; } );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-10-13 22:17:45 +02:00
|
|
|
#if 0
|
2004-09-29 00:24:56 +02:00
|
|
|
/** Return true iff any router named <b>nickname</b> with <b>digest</b>
|
|
|
|
* is in the verified fingerprint list. */
|
|
|
|
static int
|
|
|
|
router_nickname_is_approved(const char *nickname, const char *digest)
|
|
|
|
{
|
|
|
|
const char *n;
|
|
|
|
|
|
|
|
n = dirserv_get_nickname_by_digest(digest);
|
|
|
|
if (n && !strcasecmp(n,nickname))
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
2004-05-05 06:55:00 +02:00
|
|
|
}
|
2004-10-13 22:17:45 +02:00
|
|
|
#endif
|
2004-05-05 06:55:00 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Clear the current fingerprint list. */
|
2003-12-17 22:09:31 +01:00
|
|
|
void
|
2003-09-27 23:30:10 +02:00
|
|
|
dirserv_free_fingerprint_list()
|
|
|
|
{
|
|
|
|
int i;
|
2004-05-18 19:41:40 +02:00
|
|
|
fingerprint_entry_t *ent;
|
|
|
|
if (!fingerprint_list)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < smartlist_len(fingerprint_list); ++i) {
|
|
|
|
ent = smartlist_get(fingerprint_list, i);
|
|
|
|
tor_free(ent->nickname);
|
|
|
|
tor_free(ent->fingerprint);
|
|
|
|
tor_free(ent);
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2004-05-18 19:41:40 +02:00
|
|
|
smartlist_free(fingerprint_list);
|
|
|
|
fingerprint_list = NULL;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Descriptor list
|
|
|
|
*/
|
2004-05-05 02:30:43 +02:00
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** A directory server's view of a server descriptor. Contains both
|
2004-05-05 02:30:43 +02:00
|
|
|
* parsed and unparsed versions. */
|
2003-09-27 23:30:10 +02:00
|
|
|
typedef struct descriptor_entry_t {
|
|
|
|
char *nickname;
|
|
|
|
time_t published;
|
|
|
|
size_t desc_len;
|
|
|
|
char *descriptor;
|
2004-07-13 20:23:40 +02:00
|
|
|
int verified;
|
2004-04-13 22:06:08 +02:00
|
|
|
routerinfo_t *router;
|
2003-09-27 23:30:10 +02:00
|
|
|
} descriptor_entry_t;
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** List of all server descriptors that this dirserv is holding. */
|
2004-05-18 19:41:40 +02:00
|
|
|
static smartlist_t *descriptor_list = NULL;
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Release the storage held by <b>desc</b> */
|
2003-09-27 23:30:10 +02:00
|
|
|
static void free_descriptor_entry(descriptor_entry_t *desc)
|
|
|
|
{
|
2003-10-21 11:48:17 +02:00
|
|
|
tor_free(desc->descriptor);
|
|
|
|
tor_free(desc->nickname);
|
2004-04-13 22:06:08 +02:00
|
|
|
routerinfo_free(desc->router);
|
2004-09-29 08:52:36 +02:00
|
|
|
tor_free(desc);
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Release all storage that the dirserv is holding for server
|
2004-05-05 02:30:43 +02:00
|
|
|
* descriptors. */
|
2003-12-14 07:03:46 +01:00
|
|
|
void
|
2003-09-27 23:30:10 +02:00
|
|
|
dirserv_free_descriptors()
|
|
|
|
{
|
2004-05-18 19:41:40 +02:00
|
|
|
if (!descriptor_list)
|
|
|
|
return;
|
|
|
|
SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d,
|
|
|
|
free_descriptor_entry(d));
|
|
|
|
smartlist_clear(descriptor_list);
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Parse the server descriptor at *desc and maybe insert it into the
|
2004-08-08 13:15:38 +02:00
|
|
|
* list of server descriptors, and (if the descriptor is well-formed)
|
2004-05-05 02:30:43 +02:00
|
|
|
* advance *desc immediately past the descriptor's end.
|
2004-03-12 13:43:13 +01:00
|
|
|
*
|
2004-05-05 02:30:43 +02:00
|
|
|
* Return 1 if descriptor is well-formed and accepted;
|
|
|
|
* 0 if well-formed and server is unapproved;
|
|
|
|
* -1 if not well-formed or other error.
|
2003-11-12 06:12:51 +01:00
|
|
|
*/
|
2003-09-27 23:30:10 +02:00
|
|
|
int
|
|
|
|
dirserv_add_descriptor(const char **desc)
|
|
|
|
{
|
2004-05-18 19:41:40 +02:00
|
|
|
descriptor_entry_t *ent = NULL;
|
2003-09-27 23:30:10 +02:00
|
|
|
routerinfo_t *ri = NULL;
|
2004-05-18 19:41:40 +02:00
|
|
|
int i, r, found=-1;
|
2003-09-27 23:30:10 +02:00
|
|
|
char *start, *end;
|
2003-12-09 00:45:37 +01:00
|
|
|
char *desc_tmp = NULL;
|
|
|
|
const char *cp;
|
2003-09-27 23:30:10 +02:00
|
|
|
size_t desc_len;
|
2004-03-29 21:28:16 +02:00
|
|
|
time_t now;
|
2004-07-21 11:13:12 +02:00
|
|
|
int verified=1; /* whether we knew its fingerprint already */
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
if (!descriptor_list)
|
|
|
|
descriptor_list = smartlist_create();
|
|
|
|
|
2003-09-27 23:30:10 +02:00
|
|
|
start = strstr(*desc, "router ");
|
|
|
|
if (!start) {
|
2004-04-05 02:47:48 +02:00
|
|
|
log_fn(LOG_WARN, "no 'router' line found. This is not a descriptor.");
|
2004-03-12 13:43:13 +01:00
|
|
|
return -1;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2003-09-30 22:04:40 +02:00
|
|
|
if ((end = strstr(start+6, "\nrouter "))) {
|
2003-09-27 23:30:10 +02:00
|
|
|
++end; /* Include NL. */
|
2003-09-30 22:04:40 +02:00
|
|
|
} else if ((end = strstr(start+6, "\ndirectory-signature"))) {
|
|
|
|
++end;
|
2003-09-27 23:30:10 +02:00
|
|
|
} else {
|
|
|
|
end = start+strlen(start);
|
|
|
|
}
|
|
|
|
desc_len = end-start;
|
2003-12-09 00:45:37 +01:00
|
|
|
cp = desc_tmp = tor_strndup(start, desc_len);
|
2003-09-27 23:30:10 +02:00
|
|
|
|
|
|
|
/* Check: is the descriptor syntactically valid? */
|
2004-05-10 19:30:51 +02:00
|
|
|
ri = router_parse_entry_from_string(cp, NULL);
|
2004-03-12 13:43:13 +01:00
|
|
|
tor_free(desc_tmp);
|
2003-09-27 23:30:10 +02:00
|
|
|
if (!ri) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log(LOG_WARN, "Couldn't parse descriptor");
|
2004-03-12 13:43:13 +01:00
|
|
|
return -1;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
/* Okay. Now check whether the fingerprint is recognized. */
|
2003-11-12 06:12:51 +01:00
|
|
|
r = dirserv_router_fingerprint_is_known(ri);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (r==-1) {
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_WARN, "Known nickname '%s', wrong fingerprint. Not adding.", ri->nickname);
|
2003-11-12 06:12:51 +01:00
|
|
|
routerinfo_free(ri);
|
|
|
|
*desc = end;
|
|
|
|
return 0;
|
2003-10-22 18:41:35 +02:00
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (r==0) {
|
2004-07-21 11:13:12 +02:00
|
|
|
char fp[FINGERPRINT_LEN+1];
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_INFO, "Unknown nickname '%s' (%s:%d). Adding.",
|
2004-07-21 11:13:12 +02:00
|
|
|
ri->nickname, ri->address, ri->or_port);
|
2004-10-06 15:31:48 +02:00
|
|
|
if (crypto_pk_get_fingerprint(ri->identity_pkey, fp, 1) < 0) {
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_WARN, "Error computing fingerprint for '%s'", ri->nickname);
|
2004-07-21 11:13:12 +02:00
|
|
|
} else {
|
2004-07-22 08:04:13 +02:00
|
|
|
log_fn(LOG_INFO, "Fingerprint line: %s %s", ri->nickname, fp);
|
2004-07-21 11:13:12 +02:00
|
|
|
}
|
|
|
|
verified = 0;
|
|
|
|
}
|
2003-10-22 18:41:35 +02:00
|
|
|
/* Is there too much clock skew? */
|
2004-03-29 21:28:16 +02:00
|
|
|
now = time(NULL);
|
|
|
|
if (ri->published_on > now+ROUTER_ALLOW_SKEW) {
|
2004-12-13 01:44:39 +01:00
|
|
|
log_fn(LOG_NOTICE, "Publication time for nickname '%s' is too far in the future; possible clock skew. Not adding.", ri->nickname);
|
2003-11-12 06:12:51 +01:00
|
|
|
routerinfo_free(ri);
|
|
|
|
*desc = end;
|
2004-03-30 01:23:01 +02:00
|
|
|
return 0;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2004-03-29 21:28:16 +02:00
|
|
|
if (ri->published_on < now-ROUTER_MAX_AGE) {
|
2004-12-13 01:44:39 +01:00
|
|
|
log_fn(LOG_NOTICE, "Publication time for router with nickname '%s' is too far in the past. Not adding.", ri->nickname);
|
2004-03-29 21:28:16 +02:00
|
|
|
routerinfo_free(ri);
|
|
|
|
*desc = end;
|
2004-03-30 01:23:01 +02:00
|
|
|
return 0;
|
2004-03-29 21:28:16 +02:00
|
|
|
}
|
|
|
|
|
2003-09-27 23:30:10 +02:00
|
|
|
/* Do we already have an entry for this router? */
|
2004-05-18 19:41:40 +02:00
|
|
|
for (i = 0; i < smartlist_len(descriptor_list); ++i) {
|
|
|
|
ent = smartlist_get(descriptor_list, i);
|
|
|
|
if (!strcasecmp(ri->nickname, ent->nickname)) {
|
|
|
|
found = i;
|
2003-09-27 23:30:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-05-18 19:41:40 +02:00
|
|
|
if (found >= 0) {
|
2003-09-27 23:30:10 +02:00
|
|
|
/* if so, decide whether to update it. */
|
2004-08-08 13:15:38 +02:00
|
|
|
if (ent->published >= ri->published_on) {
|
|
|
|
/* We already have a newer or equal-time descriptor */
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_INFO,"We already have a new enough desc for nickname '%s'. Not adding.",ri->nickname);
|
2004-03-12 13:43:13 +01:00
|
|
|
/* This isn't really an error; return success. */
|
2003-11-12 06:12:51 +01:00
|
|
|
routerinfo_free(ri);
|
2003-09-30 21:27:54 +02:00
|
|
|
*desc = end;
|
2005-01-03 18:47:32 +01:00
|
|
|
return verified;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
/* We don't have a newer one; we'll update this one. */
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_INFO,"Dirserv updating desc for nickname '%s'",ri->nickname);
|
2004-05-18 19:41:40 +02:00
|
|
|
free_descriptor_entry(ent);
|
|
|
|
smartlist_del_keeporder(descriptor_list, found);
|
2003-09-27 23:30:10 +02:00
|
|
|
} else {
|
2004-05-18 19:41:40 +02:00
|
|
|
/* Add at the end. */
|
2004-11-25 05:20:10 +01:00
|
|
|
log_fn(LOG_INFO,"Dirserv adding desc for nickname '%s'",ri->nickname);
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2003-12-14 06:25:23 +01:00
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
ent = tor_malloc(sizeof(descriptor_entry_t));
|
|
|
|
ent->nickname = tor_strdup(ri->nickname);
|
|
|
|
ent->published = ri->published_on;
|
|
|
|
ent->desc_len = desc_len;
|
2004-10-27 08:03:28 +02:00
|
|
|
ent->descriptor = tor_strndup(start,desc_len);
|
2004-05-18 19:41:40 +02:00
|
|
|
ent->router = ri;
|
2004-11-09 19:22:17 +01:00
|
|
|
ent->verified = verified;
|
2004-05-18 19:41:40 +02:00
|
|
|
smartlist_add(descriptor_list, ent);
|
|
|
|
|
2003-09-27 23:30:10 +02:00
|
|
|
*desc = end;
|
2003-12-14 06:25:23 +01:00
|
|
|
directory_set_dirty();
|
|
|
|
|
2005-01-03 18:47:32 +01:00
|
|
|
return verified;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
|
2004-05-09 18:47:25 +02:00
|
|
|
/** Remove all descriptors whose nicknames or fingerprints we don't
|
2004-05-05 02:30:43 +02:00
|
|
|
* recognize. (Descriptors that used to be good can become
|
|
|
|
* unrecognized when we reload the fingerprint list.)
|
|
|
|
*/
|
2004-04-13 22:06:08 +02:00
|
|
|
static void
|
|
|
|
directory_remove_unrecognized(void)
|
|
|
|
{
|
|
|
|
int i;
|
2004-05-18 19:41:40 +02:00
|
|
|
descriptor_entry_t *ent;
|
|
|
|
if (!descriptor_list)
|
|
|
|
descriptor_list = smartlist_create();
|
|
|
|
|
|
|
|
for (i = 0; i < smartlist_len(descriptor_list); ++i) {
|
|
|
|
ent = smartlist_get(descriptor_list, i);
|
|
|
|
if (dirserv_router_fingerprint_is_known(ent->router)<=0) {
|
2004-11-25 05:20:10 +01:00
|
|
|
log(LOG_INFO, "Router '%s' is no longer recognized",
|
2004-05-18 19:41:40 +02:00
|
|
|
ent->nickname);
|
|
|
|
free_descriptor_entry(ent);
|
|
|
|
smartlist_del(descriptor_list, i--);
|
2004-04-13 22:06:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Mark the directory as <b>dirty</b> -- when we're next asked for a
|
2004-05-05 02:30:43 +02:00
|
|
|
* directory, we will rebuild it instead of reusing the most recently
|
|
|
|
* generated one.
|
|
|
|
*/
|
2003-12-17 22:09:31 +01:00
|
|
|
void
|
2003-09-27 23:30:10 +02:00
|
|
|
directory_set_dirty()
|
|
|
|
{
|
2004-09-23 21:58:44 +02:00
|
|
|
time_t now = time(NULL);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!the_directory_is_dirty)
|
2004-09-23 21:58:44 +02:00
|
|
|
the_directory_is_dirty = now;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!runningrouters_is_dirty)
|
2004-09-23 21:58:44 +02:00
|
|
|
runningrouters_is_dirty = now;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
|
2004-06-25 02:29:31 +02:00
|
|
|
/** Load all descriptors from a directory stored in the string
|
2004-05-10 06:34:48 +02:00
|
|
|
* <b>dir</b>.
|
2004-05-05 02:30:43 +02:00
|
|
|
*/
|
2003-12-17 22:09:31 +01:00
|
|
|
int
|
2004-06-25 02:29:31 +02:00
|
|
|
dirserv_load_from_directory_string(const char *dir)
|
2003-09-27 23:30:10 +02:00
|
|
|
{
|
|
|
|
const char *cp = dir;
|
2004-11-28 10:05:49 +01:00
|
|
|
while (1) {
|
2003-09-27 23:30:10 +02:00
|
|
|
cp = strstr(cp, "\nrouter ");
|
|
|
|
if (!cp) break;
|
|
|
|
++cp;
|
2004-03-12 13:43:13 +01:00
|
|
|
if (dirserv_add_descriptor(&cp) < 0) {
|
2003-09-27 23:30:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
--cp; /*Back up to newline.*/
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-10-27 02:48:51 +02:00
|
|
|
/**
|
|
|
|
* Allocate and return a description of the status of the server <b>desc</b>,
|
|
|
|
* for use in a running-routers line (if <b>rr_format</b> is true), or in a
|
2004-11-09 21:04:00 +01:00
|
|
|
* router-status line (if <b>rr_format</b> is false. The server is listed
|
2004-10-27 02:48:51 +02:00
|
|
|
* as running iff <b>is_live</b> is true.
|
2004-05-05 02:30:43 +02:00
|
|
|
*/
|
2004-10-27 02:48:51 +02:00
|
|
|
static char *
|
|
|
|
list_single_server_status(descriptor_entry_t *desc, int is_live,
|
|
|
|
int rr_format)
|
2003-10-01 02:43:34 +02:00
|
|
|
{
|
2004-10-27 02:48:51 +02:00
|
|
|
char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
|
2003-10-01 02:43:34 +02:00
|
|
|
char *cp;
|
2004-07-13 20:23:40 +02:00
|
|
|
|
2004-10-27 02:48:51 +02:00
|
|
|
tor_assert(desc);
|
|
|
|
tor_assert(desc->router);
|
|
|
|
|
|
|
|
cp = buf;
|
|
|
|
if (!is_live) {
|
|
|
|
*cp++ = '!';
|
|
|
|
}
|
|
|
|
if (desc->verified) {
|
2004-10-27 08:48:16 +02:00
|
|
|
strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
|
2004-10-27 02:48:51 +02:00
|
|
|
cp += strlen(cp);
|
|
|
|
if (!rr_format)
|
|
|
|
*cp++ = '=';
|
|
|
|
}
|
|
|
|
if (!desc->verified || !rr_format) {
|
|
|
|
*cp++ = '$';
|
|
|
|
base16_encode(cp, HEX_DIGEST_LEN+1, desc->router->identity_digest,
|
|
|
|
DIGEST_LEN);
|
|
|
|
}
|
|
|
|
return tor_strdup(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Allocate the contents of a running-routers line and a router-status line,
|
|
|
|
* and store them in *<b>running_routers_out</b> and *<b>router_status_out</b>
|
|
|
|
* respectively. Return 0 on success, -1 on failure.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
list_server_status(char **running_routers_out, char **router_status_out)
|
|
|
|
{
|
|
|
|
/* List of entries in running-routers style: An optional !, then either
|
|
|
|
* a nickname or a dollar-prefixed hexdigest. */
|
2004-11-09 18:12:56 +01:00
|
|
|
smartlist_t *rr_entries;
|
2004-10-27 02:48:51 +02:00
|
|
|
/* List of entries in a router-status style: An optional !, then an optional
|
|
|
|
* equals-suffixed nickname, then a dollar-prefixed hexdigest. */
|
|
|
|
smartlist_t *rs_entries;
|
|
|
|
|
|
|
|
tor_assert(running_routers_out || router_status_out);
|
|
|
|
|
|
|
|
rr_entries = smartlist_create();
|
|
|
|
rs_entries = smartlist_create();
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d,
|
|
|
|
{
|
|
|
|
int is_live;
|
2004-11-09 18:12:56 +01:00
|
|
|
connection_t *conn;
|
2004-10-27 02:48:51 +02:00
|
|
|
tor_assert(d->router);
|
2004-10-27 20:14:38 +02:00
|
|
|
conn = connection_get_by_identity_digest(
|
2004-10-27 02:48:51 +02:00
|
|
|
d->router->identity_digest, CONN_TYPE_OR);
|
2004-11-09 18:12:56 +01:00
|
|
|
/* Treat a router as alive if
|
|
|
|
* - It's me, and I'm not hibernating.
|
|
|
|
* or - we're connected to it. */
|
|
|
|
is_live = (router_is_me(d->router) && !we_are_hibernating()) ||
|
|
|
|
(conn && conn->state == OR_CONN_STATE_OPEN);
|
2004-10-27 02:48:51 +02:00
|
|
|
smartlist_add(rr_entries, list_single_server_status(d, is_live, 1));
|
|
|
|
smartlist_add(rs_entries, list_single_server_status(d, is_live, 0));
|
|
|
|
});
|
|
|
|
|
|
|
|
if (running_routers_out)
|
2004-11-03 19:33:07 +01:00
|
|
|
*running_routers_out = smartlist_join_strings(rr_entries, " ", 0,NULL);
|
2004-10-27 02:48:51 +02:00
|
|
|
if (router_status_out)
|
2004-11-03 19:33:07 +01:00
|
|
|
*router_status_out = smartlist_join_strings(rs_entries, " ", 0,NULL);
|
2004-10-27 02:48:51 +02:00
|
|
|
|
|
|
|
SMARTLIST_FOREACH(rr_entries, char *, cp, tor_free(cp));
|
|
|
|
SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(rr_entries);
|
|
|
|
smartlist_free(rs_entries);
|
|
|
|
|
2003-10-01 02:43:34 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-08-07 00:15:25 +02:00
|
|
|
/** Remove any descriptors from the directory that are more than <b>age</b>
|
2004-03-29 21:28:16 +02:00
|
|
|
* seconds old.
|
|
|
|
*/
|
|
|
|
void
|
2004-08-07 00:15:25 +02:00
|
|
|
dirserv_remove_old_servers(int age)
|
2004-03-29 21:28:16 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
time_t cutoff;
|
2004-05-18 19:41:40 +02:00
|
|
|
descriptor_entry_t *ent;
|
|
|
|
if (!descriptor_list)
|
|
|
|
descriptor_list = smartlist_create();
|
|
|
|
|
2004-08-07 00:15:25 +02:00
|
|
|
cutoff = time(NULL) - age;
|
2004-05-18 19:41:40 +02:00
|
|
|
for (i = 0; i < smartlist_len(descriptor_list); ++i) {
|
|
|
|
ent = smartlist_get(descriptor_list, i);
|
2004-08-07 00:15:25 +02:00
|
|
|
if (ent->published <= cutoff) {
|
2004-03-29 21:28:16 +02:00
|
|
|
/* descriptor_list[i] is too old. Remove it. */
|
2004-05-18 19:41:40 +02:00
|
|
|
free_descriptor_entry(ent);
|
|
|
|
smartlist_del(descriptor_list, i--);
|
2004-03-29 21:28:16 +02:00
|
|
|
directory_set_dirty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
/** Generate a new directory and write it into a newly allocated string.
|
|
|
|
* Point *<b>dir_out</b> to the allocated string. Sign the
|
2004-05-10 06:34:48 +02:00
|
|
|
* directory with <b>private_key</b>. Return 0 on success, -1 on
|
|
|
|
* failure.
|
2004-03-29 21:28:16 +02:00
|
|
|
*/
|
2003-09-27 23:30:10 +02:00
|
|
|
int
|
2004-12-24 03:17:32 +01:00
|
|
|
dirserv_dump_directory_to_string(char **dir_out,
|
2003-09-27 23:30:10 +02:00
|
|
|
crypto_pk_env_t *private_key)
|
|
|
|
{
|
2004-12-24 03:17:32 +01:00
|
|
|
char *cp;
|
2004-10-27 02:48:51 +02:00
|
|
|
char *running_routers, *router_status;
|
2004-10-07 23:10:40 +02:00
|
|
|
char *identity_pkey; /* Identity key, DER64-encoded. */
|
2004-10-15 07:09:48 +02:00
|
|
|
char *recommended_versions;
|
2003-09-27 23:30:10 +02:00
|
|
|
char digest[20];
|
|
|
|
char signature[128];
|
|
|
|
char published[33];
|
|
|
|
time_t published_on;
|
2004-12-24 03:17:32 +01:00
|
|
|
char *buf = NULL;
|
|
|
|
size_t buf_len;
|
2004-10-07 05:11:42 +02:00
|
|
|
int i;
|
2004-12-24 03:17:32 +01:00
|
|
|
|
|
|
|
tor_assert(dir_out);
|
|
|
|
*dir_out = NULL;
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
if (!descriptor_list)
|
|
|
|
descriptor_list = smartlist_create();
|
|
|
|
|
2004-10-27 02:48:51 +02:00
|
|
|
if (list_server_status(&running_routers, &router_status))
|
2003-09-27 23:30:10 +02:00
|
|
|
return -1;
|
2004-10-07 05:11:42 +02:00
|
|
|
|
|
|
|
/* ASN.1-encode the public key. This is a temporary measure; once
|
|
|
|
* everyone is running 0.0.9pre3 or later, we can shift to using a
|
|
|
|
* PEM-encoded key instead.
|
|
|
|
*/
|
2004-10-12 17:52:57 +02:00
|
|
|
#if 1
|
2004-11-28 10:05:49 +01:00
|
|
|
if (crypto_pk_DER64_encode_public_key(private_key, &identity_pkey)<0) {
|
2004-10-06 15:31:48 +02:00
|
|
|
log_fn(LOG_WARN,"write identity_pkey to string failed!");
|
|
|
|
return -1;
|
|
|
|
}
|
2004-10-12 17:52:57 +02:00
|
|
|
#else
|
2004-11-23 00:28:26 +01:00
|
|
|
{
|
|
|
|
int l;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,&l)<0) {
|
2004-10-12 17:52:57 +02:00
|
|
|
log_fn(LOG_WARN,"write identity_pkey to string failed!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2004-10-15 07:09:48 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
smartlist_t *versions;
|
|
|
|
struct config_line_t *ln;
|
|
|
|
versions = smartlist_create();
|
2004-11-06 06:18:11 +01:00
|
|
|
for (ln = get_options()->RecommendedVersions; ln; ln = ln->next) {
|
2004-11-09 21:04:00 +01:00
|
|
|
smartlist_split_string(versions, ln->value, ",",
|
2004-10-15 07:09:48 +02:00
|
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
|
|
|
}
|
2004-11-03 19:33:07 +01:00
|
|
|
recommended_versions = smartlist_join_strings(versions,",",0,NULL);
|
2004-10-15 07:09:48 +02:00
|
|
|
SMARTLIST_FOREACH(versions,char *,s,tor_free(s));
|
|
|
|
smartlist_free(versions);
|
|
|
|
}
|
2004-11-09 21:04:00 +01:00
|
|
|
|
2004-08-07 00:15:25 +02:00
|
|
|
dirserv_remove_old_servers(ROUTER_MAX_AGE);
|
2003-09-27 23:30:10 +02:00
|
|
|
published_on = time(NULL);
|
2004-08-07 04:46:16 +02:00
|
|
|
format_iso_time(published, published_on);
|
2004-12-24 03:17:32 +01:00
|
|
|
|
|
|
|
buf_len = 2048+strlen(recommended_versions)+strlen(running_routers)+
|
|
|
|
strlen(router_status);
|
|
|
|
SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d,
|
|
|
|
buf_len += strlen(d->descriptor));
|
|
|
|
buf = tor_malloc(buf_len);
|
|
|
|
/* We'll be comparing against buf_len throughout the rest of the
|
|
|
|
function, though strictly speaking we shouldn't be able to exceed
|
|
|
|
it. This is C, after all, so we may as well check for buffer
|
|
|
|
overruns.*/
|
|
|
|
|
|
|
|
tor_snprintf(buf, buf_len,
|
2003-09-27 23:30:10 +02:00
|
|
|
"signed-directory\n"
|
|
|
|
"published %s\n"
|
2003-11-13 07:49:25 +01:00
|
|
|
"recommended-software %s\n"
|
2004-10-07 05:11:42 +02:00
|
|
|
"running-routers %s\n"
|
2004-10-27 02:48:51 +02:00
|
|
|
"opt router-status %s\n"
|
2004-10-07 05:11:42 +02:00
|
|
|
"opt dir-signing-key %s\n\n",
|
2004-10-27 02:48:51 +02:00
|
|
|
published, recommended_versions, running_routers, router_status,
|
|
|
|
identity_pkey);
|
2004-06-25 02:29:31 +02:00
|
|
|
|
2004-10-27 02:48:51 +02:00
|
|
|
tor_free(running_routers);
|
|
|
|
tor_free(router_status);
|
2004-10-06 15:31:48 +02:00
|
|
|
tor_free(identity_pkey);
|
2004-12-24 03:17:32 +01:00
|
|
|
i = strlen(buf);
|
|
|
|
cp = buf+i;
|
2003-12-14 06:25:23 +01:00
|
|
|
|
2004-05-18 19:41:40 +02:00
|
|
|
SMARTLIST_FOREACH(descriptor_list, descriptor_entry_t *, d,
|
2004-12-24 03:17:32 +01:00
|
|
|
if (strlcat(buf, d->descriptor, buf_len) >= buf_len)
|
2004-05-18 19:41:40 +02:00
|
|
|
goto truncated);
|
|
|
|
|
2004-03-29 21:28:16 +02:00
|
|
|
/* These multiple strlcat calls are inefficient, but dwarfed by the RSA
|
2003-09-27 23:30:10 +02:00
|
|
|
signature.
|
|
|
|
*/
|
2004-12-24 03:17:32 +01:00
|
|
|
if (strlcat(buf, "directory-signature ", buf_len) >= buf_len)
|
2004-03-29 21:28:16 +02:00
|
|
|
goto truncated;
|
2004-12-24 03:17:32 +01:00
|
|
|
if (strlcat(buf, get_options()->Nickname, buf_len) >= buf_len)
|
2004-04-25 00:17:50 +02:00
|
|
|
goto truncated;
|
2004-12-24 03:17:32 +01:00
|
|
|
if (strlcat(buf, "\n", buf_len) >= buf_len)
|
2004-04-25 00:17:50 +02:00
|
|
|
goto truncated;
|
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
if (router_get_dir_hash(buf,digest)) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"couldn't compute digest");
|
2004-12-24 03:17:32 +01:00
|
|
|
tor_free(buf);
|
2003-09-27 23:30:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-11-02 03:28:51 +01:00
|
|
|
if (crypto_pk_private_sign(private_key, signature, digest, 20) < 0) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"couldn't sign digest");
|
2004-12-24 03:17:32 +01:00
|
|
|
tor_free(buf);
|
2003-09-27 23:30:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-04-06 22:16:12 +02:00
|
|
|
log(LOG_DEBUG,"generated directory digest begins with %s",hex_str(digest,4));
|
2003-12-14 06:25:23 +01:00
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
if (strlcat(cp, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len)
|
2004-03-29 21:28:16 +02:00
|
|
|
goto truncated;
|
2003-12-14 06:25:23 +01:00
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
i = strlen(buf);
|
|
|
|
cp = buf+i;
|
|
|
|
if (base64_encode(cp, buf_len-i, signature, 128) < 0) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"couldn't base64-encode signature");
|
2004-12-24 03:17:32 +01:00
|
|
|
tor_free(buf);
|
2003-09-27 23:30:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-07-23 01:21:12 +02:00
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
if (strlcat(buf, "-----END SIGNATURE-----\n", buf_len) >= buf_len)
|
2004-03-29 21:28:16 +02:00
|
|
|
goto truncated;
|
2003-09-27 23:30:10 +02:00
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
*dir_out = buf;
|
2003-09-27 23:30:10 +02:00
|
|
|
return 0;
|
2004-03-29 21:28:16 +02:00
|
|
|
truncated:
|
|
|
|
log_fn(LOG_WARN,"tried to exceed string length.");
|
2004-12-24 03:17:32 +01:00
|
|
|
tor_free(buf);
|
2004-03-29 21:28:16 +02:00
|
|
|
return -1;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Most recently generated encoded signed directory. */
|
2003-12-14 06:25:23 +01:00
|
|
|
static char *the_directory = NULL;
|
2004-10-01 23:02:12 +02:00
|
|
|
static size_t the_directory_len = 0;
|
2004-09-02 20:57:09 +02:00
|
|
|
static char *the_directory_z = NULL;
|
2004-10-01 23:02:12 +02:00
|
|
|
static size_t the_directory_z_len = 0;
|
2004-09-02 20:57:09 +02:00
|
|
|
|
2004-11-15 05:04:20 +01:00
|
|
|
typedef struct cached_dir_t {
|
|
|
|
char *dir;
|
|
|
|
char *dir_z;
|
|
|
|
size_t dir_len;
|
|
|
|
size_t dir_z_len;
|
|
|
|
time_t published;
|
|
|
|
} cached_dir_t;
|
|
|
|
|
|
|
|
/* used only by non-auth dirservers */
|
|
|
|
static cached_dir_t cached_directory = { NULL, NULL, 0, 0, 0 };
|
|
|
|
static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0 };
|
2004-06-21 06:37:27 +02:00
|
|
|
|
2004-11-09 19:22:17 +01:00
|
|
|
/** If we have no cached directory, or it is older than <b>when</b>, then
|
|
|
|
* replace it with <b>directory</b>, published at <b>when</b>.
|
|
|
|
*/
|
2004-11-15 05:04:20 +01:00
|
|
|
void dirserv_set_cached_directory(const char *directory, time_t when,
|
|
|
|
int is_running_routers)
|
2004-06-21 06:37:27 +02:00
|
|
|
{
|
|
|
|
time_t now;
|
2004-11-15 05:04:20 +01:00
|
|
|
cached_dir_t *d;
|
2004-06-21 06:37:27 +02:00
|
|
|
now = time(NULL);
|
2004-11-15 05:04:20 +01:00
|
|
|
d = is_running_routers ? &cached_runningrouters : &cached_directory;
|
|
|
|
if (when<=d->published) {
|
2004-09-08 08:52:33 +02:00
|
|
|
log_fn(LOG_INFO, "Ignoring old directory; not caching.");
|
|
|
|
} else if (when>=now+ROUTER_ALLOW_SKEW) {
|
|
|
|
log_fn(LOG_INFO, "Ignoring future directory; not caching.");
|
2004-11-15 05:04:20 +01:00
|
|
|
} else if (when>d->published &&
|
2004-09-08 08:52:33 +02:00
|
|
|
when<now+ROUTER_ALLOW_SKEW) {
|
|
|
|
log_fn(LOG_DEBUG, "Caching directory.");
|
2004-11-15 05:04:20 +01:00
|
|
|
tor_free(d->dir);
|
|
|
|
d->dir = tor_strdup(directory);
|
|
|
|
d->dir_len = strlen(directory);
|
|
|
|
tor_free(d->dir_z);
|
|
|
|
if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len,
|
2004-09-02 20:57:09 +02:00
|
|
|
ZLIB_METHOD)) {
|
|
|
|
log_fn(LOG_WARN,"Error compressing cached directory");
|
|
|
|
}
|
2004-11-15 05:04:20 +01:00
|
|
|
d->published = when;
|
|
|
|
if (!is_running_routers) {
|
|
|
|
char filename[512];
|
|
|
|
tor_snprintf(filename,sizeof(filename),"%s/cached-directory", get_options()->DataDirectory);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (write_str_to_file(filename,cached_directory.dir,0) < 0) {
|
2004-12-13 01:44:39 +01:00
|
|
|
log_fn(LOG_NOTICE, "Couldn't write cached directory to disk. Ignoring.");
|
2004-11-15 05:04:20 +01:00
|
|
|
}
|
2004-09-08 08:52:33 +02:00
|
|
|
}
|
2004-06-21 06:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
2003-12-14 06:25:23 +01:00
|
|
|
|
2004-05-10 06:34:48 +02:00
|
|
|
/** Set *<b>directory</b> to the most recently generated encoded signed
|
2004-11-15 05:04:20 +01:00
|
|
|
* directory, generating a new one as necessary. If not an authoritative
|
|
|
|
* directory may return 0 if no directory is yet cached.*/
|
2004-09-27 08:57:16 +02:00
|
|
|
size_t dirserv_get_directory(const char **directory, int compress)
|
2003-09-27 23:30:10 +02:00
|
|
|
{
|
2004-11-06 06:18:11 +01:00
|
|
|
if (!get_options()->AuthoritativeDir) {
|
2004-11-15 05:04:20 +01:00
|
|
|
cached_dir_t *d = &cached_directory;
|
|
|
|
*directory = compress ? d->dir_z : d->dir;
|
|
|
|
if (*directory) {
|
|
|
|
return compress ? d->dir_z_len : d->dir_len;
|
2004-06-21 06:37:27 +02:00
|
|
|
} else {
|
|
|
|
/* no directory yet retrieved */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2004-09-23 21:58:44 +02:00
|
|
|
if (the_directory_is_dirty &&
|
|
|
|
the_directory_is_dirty + DIR_REGEN_SLACK_TIME < time(NULL)) {
|
2004-09-02 20:57:09 +02:00
|
|
|
if (dirserv_regenerate_directory())
|
2003-09-27 23:30:10 +02:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
log(LOG_INFO,"Directory still clean, reusing.");
|
|
|
|
}
|
2004-09-27 08:57:16 +02:00
|
|
|
*directory = compress ? the_directory_z : the_directory;
|
|
|
|
return compress ? the_directory_z_len : the_directory_len;
|
2004-09-02 20:57:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a fresh directory (authdirservers only.)
|
|
|
|
*/
|
|
|
|
static int dirserv_regenerate_directory(void)
|
|
|
|
{
|
2004-12-24 03:17:32 +01:00
|
|
|
char *new_directory=NULL;
|
2004-09-02 20:57:09 +02:00
|
|
|
|
2004-12-24 03:17:32 +01:00
|
|
|
if (dirserv_dump_directory_to_string(&new_directory,
|
2004-09-02 20:57:09 +02:00
|
|
|
get_identity_key())) {
|
|
|
|
log(LOG_WARN, "Error creating directory.");
|
|
|
|
tor_free(new_directory);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
tor_free(the_directory);
|
|
|
|
the_directory = new_directory;
|
|
|
|
the_directory_len = strlen(the_directory);
|
2004-10-01 23:02:12 +02:00
|
|
|
log_fn(LOG_INFO,"New directory (size %d):\n%s",(int)the_directory_len,
|
2004-09-02 20:57:09 +02:00
|
|
|
the_directory);
|
|
|
|
tor_free(the_directory_z);
|
2004-10-01 23:02:12 +02:00
|
|
|
if (tor_gzip_compress(&the_directory_z, &the_directory_z_len,
|
2004-09-02 20:57:09 +02:00
|
|
|
the_directory, the_directory_len,
|
|
|
|
ZLIB_METHOD)) {
|
|
|
|
log_fn(LOG_WARN, "Error gzipping directory.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now read the directory we just made in order to update our own
|
|
|
|
* router lists. This does more signature checking than is strictly
|
|
|
|
* necessary, but safe is better than sorry. */
|
|
|
|
new_directory = tor_strdup(the_directory);
|
|
|
|
/* use a new copy of the dir, since get_dir_from_string scribbles on it */
|
2005-01-06 21:11:52 +01:00
|
|
|
if (router_load_routerlist_from_directory(new_directory,
|
|
|
|
get_identity_key(), 1, 0)) {
|
2004-09-02 20:57:09 +02:00
|
|
|
log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying.");
|
|
|
|
tor_cleanup();
|
|
|
|
exit(0);
|
|
|
|
}
|
2004-09-29 08:52:36 +02:00
|
|
|
tor_free(new_directory);
|
2004-09-02 20:57:09 +02:00
|
|
|
the_directory_is_dirty = 0;
|
|
|
|
|
2004-11-15 17:17:59 +01:00
|
|
|
/* Save the directory to disk so we re-load it quickly on startup.
|
|
|
|
*/
|
|
|
|
dirserv_set_cached_directory(the_directory, time(NULL), 0);
|
|
|
|
|
2004-09-02 20:57:09 +02:00
|
|
|
return 0;
|
2003-09-27 23:30:10 +02:00
|
|
|
}
|
2003-10-18 05:23:26 +02:00
|
|
|
|
2004-11-15 05:04:20 +01:00
|
|
|
static char *the_runningrouters=NULL;
|
|
|
|
static size_t the_runningrouters_len=0;
|
|
|
|
static char *the_runningrouters_z=NULL;
|
|
|
|
static size_t the_runningrouters_z_len=0;
|
2004-06-25 02:29:31 +02:00
|
|
|
|
|
|
|
/** Replace the current running-routers list with a newly generated one. */
|
|
|
|
static int generate_runningrouters(crypto_pk_env_t *private_key)
|
|
|
|
{
|
2004-10-27 14:34:02 +02:00
|
|
|
char *s=NULL, *cp;
|
|
|
|
char *router_status=NULL;
|
2004-06-25 02:29:31 +02:00
|
|
|
char digest[DIGEST_LEN];
|
|
|
|
char signature[PK_BYTES];
|
2004-08-04 01:57:05 +02:00
|
|
|
int i;
|
2004-06-25 02:29:31 +02:00
|
|
|
char published[33];
|
2004-08-04 01:57:05 +02:00
|
|
|
size_t len;
|
2004-06-25 02:29:31 +02:00
|
|
|
time_t published_on;
|
2004-10-07 23:10:40 +02:00
|
|
|
char *identity_pkey; /* Identity key, DER64-encoded. */
|
2004-06-25 02:29:31 +02:00
|
|
|
|
2004-10-27 14:34:02 +02:00
|
|
|
if (list_server_status(NULL, &router_status)) {
|
|
|
|
goto err;
|
|
|
|
}
|
2004-10-07 23:10:40 +02:00
|
|
|
/* ASN.1-encode the public key. This is a temporary measure; once
|
|
|
|
* everyone is running 0.0.9pre3 or later, we can shift to using a
|
|
|
|
* PEM-encoded key instead.
|
|
|
|
*/
|
2004-10-12 17:52:57 +02:00
|
|
|
#if 1
|
2004-11-28 10:05:49 +01:00
|
|
|
if (crypto_pk_DER64_encode_public_key(private_key, &identity_pkey)<0) {
|
2004-10-07 23:10:40 +02:00
|
|
|
log_fn(LOG_WARN,"write identity_pkey to string failed!");
|
2004-10-27 14:34:02 +02:00
|
|
|
goto err;
|
2004-10-07 23:10:40 +02:00
|
|
|
}
|
2004-10-12 17:52:57 +02:00
|
|
|
#else
|
2004-11-23 00:28:26 +01:00
|
|
|
{
|
|
|
|
int l;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,&l)<0) {
|
2004-10-12 17:52:57 +02:00
|
|
|
log_fn(LOG_WARN,"write identity_pkey to string failed!");
|
2004-10-27 14:34:02 +02:00
|
|
|
goto err;
|
2004-10-12 17:52:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2004-06-25 02:29:31 +02:00
|
|
|
published_on = time(NULL);
|
2004-08-07 04:46:16 +02:00
|
|
|
format_iso_time(published, published_on);
|
2004-12-24 02:41:52 +01:00
|
|
|
|
|
|
|
len = 2048+strlen(router_status);
|
|
|
|
s = tor_malloc_zero(len);
|
2004-10-27 08:37:34 +02:00
|
|
|
tor_snprintf(s, len, "network-status\n"
|
2004-06-25 02:29:31 +02:00
|
|
|
"published %s\n"
|
2004-10-27 02:48:51 +02:00
|
|
|
"router-status %s\n"
|
2004-10-07 23:10:40 +02:00
|
|
|
"opt dir-signing-key %s\n"
|
2004-06-25 02:29:31 +02:00
|
|
|
"directory-signature %s\n"
|
|
|
|
"-----BEGIN SIGNATURE-----\n",
|
2004-11-06 06:18:11 +01:00
|
|
|
published, router_status, identity_pkey, get_options()->Nickname);
|
2004-10-27 02:48:51 +02:00
|
|
|
tor_free(router_status);
|
2004-10-07 23:10:40 +02:00
|
|
|
tor_free(identity_pkey);
|
2004-06-25 02:29:31 +02:00
|
|
|
if (router_get_runningrouters_hash(s,digest)) {
|
|
|
|
log_fn(LOG_WARN,"couldn't compute digest");
|
2004-10-27 14:34:02 +02:00
|
|
|
goto err;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
2004-11-02 03:28:51 +01:00
|
|
|
if (crypto_pk_private_sign(private_key, signature, digest, 20) < 0) {
|
2004-06-25 02:29:31 +02:00
|
|
|
log_fn(LOG_WARN,"couldn't sign digest");
|
2004-10-27 14:34:02 +02:00
|
|
|
goto err;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
i = strlen(s);
|
|
|
|
cp = s+i;
|
|
|
|
if (base64_encode(cp, len-i, signature, 128) < 0) {
|
|
|
|
log_fn(LOG_WARN,"couldn't base64-encode signature");
|
2004-10-27 14:34:02 +02:00
|
|
|
goto err;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
|
|
|
if (strlcat(s, "-----END SIGNATURE-----\n", len) >= len) {
|
2004-10-27 14:34:02 +02:00
|
|
|
goto err;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
|
|
|
|
2004-11-15 05:04:20 +01:00
|
|
|
tor_free(the_runningrouters);
|
|
|
|
the_runningrouters = s;
|
|
|
|
the_runningrouters_len = strlen(s);
|
|
|
|
tor_free(the_runningrouters_z);
|
|
|
|
if (tor_gzip_compress(&the_runningrouters_z, &the_runningrouters_z_len,
|
|
|
|
the_runningrouters, the_runningrouters_len,
|
|
|
|
ZLIB_METHOD)) {
|
|
|
|
log_fn(LOG_WARN, "Error gzipping runningrouters");
|
|
|
|
return -1;
|
|
|
|
}
|
2004-06-25 02:29:31 +02:00
|
|
|
runningrouters_is_dirty = 0;
|
2004-11-15 17:17:59 +01:00
|
|
|
|
2004-12-01 04:48:14 +01:00
|
|
|
/* We don't cache running-routers to disk, so there's no point in
|
2004-11-15 17:17:59 +01:00
|
|
|
* authdirservers caching it. */
|
|
|
|
/* dirserv_set_cached_directory(the_runningrouters, time(NULL), 1); */
|
|
|
|
|
2004-06-25 02:29:31 +02:00
|
|
|
return 0;
|
2004-10-27 14:34:02 +02:00
|
|
|
err:
|
|
|
|
tor_free(s);
|
|
|
|
tor_free(router_status);
|
|
|
|
return -1;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
|
|
|
|
2004-06-16 23:08:29 +02:00
|
|
|
/** Set *<b>rr</b> to the most recently generated encoded signed
|
2004-08-06 22:00:16 +02:00
|
|
|
* running-routers list, generating a new one as necessary. Return the
|
|
|
|
* size of the directory on success, and 0 on failure. */
|
2004-11-15 05:04:20 +01:00
|
|
|
size_t dirserv_get_runningrouters(const char **rr, int compress)
|
2004-06-16 23:08:29 +02:00
|
|
|
{
|
2004-11-15 05:04:20 +01:00
|
|
|
if (!get_options()->AuthoritativeDir) {
|
|
|
|
cached_dir_t *d = &cached_runningrouters;
|
|
|
|
*rr = compress ? d->dir_z : d->dir;
|
|
|
|
if (*rr) {
|
|
|
|
return compress ? d->dir_z_len : d->dir_len;
|
|
|
|
} else {
|
|
|
|
/* no directory yet retrieved */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2004-09-23 21:58:44 +02:00
|
|
|
if (runningrouters_is_dirty &&
|
|
|
|
runningrouters_is_dirty + DIR_REGEN_SLACK_TIME < time(NULL)) {
|
2004-11-28 10:05:49 +01:00
|
|
|
if (generate_runningrouters(get_identity_key())) {
|
2004-06-25 02:29:31 +02:00
|
|
|
log_fn(LOG_ERR, "Couldn't generate running-routers list?");
|
2004-08-06 22:00:16 +02:00
|
|
|
return 0;
|
2004-06-25 02:29:31 +02:00
|
|
|
}
|
|
|
|
}
|
2004-11-15 05:04:20 +01:00
|
|
|
*rr = compress ? the_runningrouters_z : the_runningrouters;
|
|
|
|
return compress ? the_runningrouters_z_len : the_runningrouters_len;
|
2004-06-16 23:08:29 +02:00
|
|
|
}
|
|
|
|
|