mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 15:43:32 +01:00
r12981@Kushana: nickm | 2007-05-18 14:12:19 -0400
First cut at code to download extra-info docs. Also note a bad bug in directory.c (look for the string BUG BUG BUG). svn:r10209
This commit is contained in:
parent
a187704872
commit
ec55cf526d
@ -87,8 +87,7 @@ Changes in version 0.2.0.1-alpha - 2007-??-??
|
|||||||
routers. These documents contain fields from router descriptors
|
routers. These documents contain fields from router descriptors
|
||||||
that aren't usually needed, and that use a lot of excess
|
that aren't usually needed, and that use a lot of excess
|
||||||
bandwidth. Once these fields are removed from router descriptors,
|
bandwidth. Once these fields are removed from router descriptors,
|
||||||
the bandwidth savings should be about 60%. (Limitation: servers
|
the bandwidth savings should be about 60%. [Partially implements
|
||||||
do not yet upload extra-info documents.) [Partially implements
|
|
||||||
proposal 104.]
|
proposal 104.]
|
||||||
- Directory authorities allow multiple router descriptors and/or extra
|
- Directory authorities allow multiple router descriptors and/or extra
|
||||||
info documents to be uploaded in a single go. This will make
|
info documents to be uploaded in a single go. This will make
|
||||||
@ -96,6 +95,10 @@ Changes in version 0.2.0.1-alpha - 2007-??-??
|
|||||||
- New config option V2AuthoritativeDirectory that all directory
|
- New config option V2AuthoritativeDirectory that all directory
|
||||||
authorities should set. This will let future authorities choose
|
authorities should set. This will let future authorities choose
|
||||||
not to serve V2 directory information.
|
not to serve V2 directory information.
|
||||||
|
- Servers upload extra-info documents to any authority that accepts
|
||||||
|
them. Authorities (and caches that have been configured to download
|
||||||
|
extra-info documents) download them as needed. [Partially implements
|
||||||
|
proposal 104.]
|
||||||
|
|
||||||
o Minor features (controller):
|
o Minor features (controller):
|
||||||
- Add a new config option __DisablePredictedCircuits designed for
|
- Add a new config option __DisablePredictedCircuits designed for
|
||||||
|
3
doc/TODO
3
doc/TODO
@ -92,7 +92,8 @@ Things we'd like to do in 0.2.0.x:
|
|||||||
version 0.2.0.0-alpha-dev (r10070) or later.
|
version 0.2.0.0-alpha-dev (r10070) or later.
|
||||||
o Implement, but make it option-controlled.
|
o Implement, but make it option-controlled.
|
||||||
o Make it always-on once it seems to work.
|
o Make it always-on once it seems to work.
|
||||||
- Implement option to download and cache extra-info documents.
|
o Implement option to download and cache extra-info documents.
|
||||||
|
- Improve the 'retry' logic on extra-info documents.
|
||||||
- Drop bandwidth history from router-descriptors
|
- Drop bandwidth history from router-descriptors
|
||||||
- 105: Version negotiation for the Tor protocol (finalize by Jun 1)
|
- 105: Version negotiation for the Tor protocol (finalize by Jun 1)
|
||||||
- 108: Base "Stable" Flag on Mean Time Between Failures
|
- 108: Base "Stable" Flag on Mean Time Between Failures
|
||||||
|
@ -155,6 +155,7 @@ static config_var_t _option_vars[] = {
|
|||||||
VAR("DirPort", UINT, DirPort, "0"),
|
VAR("DirPort", UINT, DirPort, "0"),
|
||||||
OBSOLETE("DirPostPeriod"),
|
OBSOLETE("DirPostPeriod"),
|
||||||
VAR("DirServer", LINELIST, DirServers, NULL),
|
VAR("DirServer", LINELIST, DirServers, NULL),
|
||||||
|
VAR("DownloadExtraInfo", BOOL, DownloadExtraInfo, "0"),
|
||||||
VAR("EnforceDistinctSubnets", BOOL, EnforceDistinctSubnets,"1"),
|
VAR("EnforceDistinctSubnets", BOOL, EnforceDistinctSubnets,"1"),
|
||||||
VAR("EntryNodes", STRING, EntryNodes, NULL),
|
VAR("EntryNodes", STRING, EntryNodes, NULL),
|
||||||
VAR("ExcludeNodes", STRING, ExcludeNodes, NULL),
|
VAR("ExcludeNodes", STRING, ExcludeNodes, NULL),
|
||||||
@ -2555,6 +2556,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
|||||||
"UseEntryGuards. Disabling.");
|
"UseEntryGuards. Disabling.");
|
||||||
options->UseEntryGuards = 0;
|
options->UseEntryGuards = 0;
|
||||||
}
|
}
|
||||||
|
if (!options->DownloadExtraInfo) {
|
||||||
|
log_info(LD_CONFIG, "Authoritative directories always try to download "
|
||||||
|
"extra-info documents. Setting DownloadExtraInfo.");
|
||||||
|
options->DownloadExtraInfo = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->AuthoritativeDir && !options->DirPort)
|
if (options->AuthoritativeDir && !options->DirPort)
|
||||||
|
@ -45,7 +45,8 @@ static void connection_dir_download_routerdesc_failed(dir_connection_t *conn);
|
|||||||
static void dir_networkstatus_download_failed(smartlist_t *failed,
|
static void dir_networkstatus_download_failed(smartlist_t *failed,
|
||||||
int status_code);
|
int status_code);
|
||||||
static void dir_routerdesc_download_failed(smartlist_t *failed,
|
static void dir_routerdesc_download_failed(smartlist_t *failed,
|
||||||
int status_code);
|
int status_code,
|
||||||
|
int was_extrainfo);
|
||||||
static void note_request(const char *key, size_t bytes);
|
static void note_request(const char *key, size_t bytes);
|
||||||
|
|
||||||
/********* START VARIABLES **********/
|
/********* START VARIABLES **********/
|
||||||
@ -78,7 +79,8 @@ purpose_is_private(uint8_t purpose)
|
|||||||
purpose == DIR_PURPOSE_UPLOAD_DIR ||
|
purpose == DIR_PURPOSE_UPLOAD_DIR ||
|
||||||
purpose == DIR_PURPOSE_FETCH_RUNNING_LIST ||
|
purpose == DIR_PURPOSE_FETCH_RUNNING_LIST ||
|
||||||
purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS ||
|
purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS ||
|
||||||
purpose == DIR_PURPOSE_FETCH_SERVERDESC)
|
purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
|
||||||
|
purpose == DIR_PURPOSE_FETCH_EXTRAINFO)
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -106,6 +108,28 @@ authority_type_to_string(authority_type_t auth)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* DOCDOC */
|
||||||
|
int
|
||||||
|
router_supports_extrainfo(const char *identity_digest, int is_authority)
|
||||||
|
{
|
||||||
|
routerinfo_t *ri = router_get_by_digest(identity_digest);
|
||||||
|
local_routerstatus_t *lrs;
|
||||||
|
|
||||||
|
if (ri) {
|
||||||
|
if (ri->caches_extra_info)
|
||||||
|
return 1;
|
||||||
|
if (is_authority && ri->platform &&
|
||||||
|
tor_version_as_new_as(ri->platform, "Tor 0.2.0.0-alpha-dev (r10070)"))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (is_authority) {
|
||||||
|
lrs = router_get_combined_status_by_digest(identity_digest);
|
||||||
|
if (lrs && lrs->status.version_supports_extrainfo_upload)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Start a connection to every suitable directory server, using
|
/** Start a connection to every suitable directory server, using
|
||||||
* connection purpose 'purpose' and uploading the payload 'payload'
|
* connection purpose 'purpose' and uploading the payload 'payload'
|
||||||
* (length 'payload_len'). The purpose should be one of
|
* (length 'payload_len'). The purpose should be one of
|
||||||
@ -131,9 +155,6 @@ directory_post_to_dirservers(uint8_t purpose, authority_type_t type,
|
|||||||
SMARTLIST_FOREACH(dirservers, trusted_dir_server_t *, ds,
|
SMARTLIST_FOREACH(dirservers, trusted_dir_server_t *, ds,
|
||||||
{
|
{
|
||||||
routerstatus_t *rs = &(ds->fake_status.status);
|
routerstatus_t *rs = &(ds->fake_status.status);
|
||||||
local_routerstatus_t *lrs = router_get_combined_status_by_digest(
|
|
||||||
ds->digest);
|
|
||||||
int new_enough;
|
|
||||||
size_t upload_len = payload_len;
|
size_t upload_len = payload_len;
|
||||||
|
|
||||||
if ((type & ds->type) == 0)
|
if ((type & ds->type) == 0)
|
||||||
@ -143,10 +164,7 @@ directory_post_to_dirservers(uint8_t purpose, authority_type_t type,
|
|||||||
if (purpose == DIR_PURPOSE_UPLOAD_DIR)
|
if (purpose == DIR_PURPOSE_UPLOAD_DIR)
|
||||||
ds->has_accepted_serverdesc = 0;
|
ds->has_accepted_serverdesc = 0;
|
||||||
|
|
||||||
new_enough = (lrs && lrs->status.version_supports_extrainfo_upload) ||
|
if (extrainfo_len && router_supports_extrainfo(ds->digest, 1)) {
|
||||||
(router_digest_version_as_new_as(ds->digest,
|
|
||||||
"Tor 0.2.0.0-alpha-dev (r10070)"));
|
|
||||||
if (extrainfo_len && new_enough) {
|
|
||||||
upload_len += extrainfo_len;
|
upload_len += extrainfo_len;
|
||||||
log_info(LD_DIR, "Uploading an extrainfo (length %d)",
|
log_info(LD_DIR, "Uploading an extrainfo (length %d)",
|
||||||
(int) extrainfo_len);
|
(int) extrainfo_len);
|
||||||
@ -182,6 +200,9 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource,
|
|||||||
/* FFFF we could break this switch into its own function, and call
|
/* FFFF we could break this switch into its own function, and call
|
||||||
* it elsewhere in directory.c. -RD */
|
* it elsewhere in directory.c. -RD */
|
||||||
switch (purpose) {
|
switch (purpose) {
|
||||||
|
case DIR_PURPOSE_FETCH_EXTRAINFO:
|
||||||
|
type = EXTRAINFO_CACHE | V2_AUTHORITY;
|
||||||
|
break;
|
||||||
case DIR_PURPOSE_FETCH_NETWORKSTATUS:
|
case DIR_PURPOSE_FETCH_NETWORKSTATUS:
|
||||||
case DIR_PURPOSE_FETCH_SERVERDESC:
|
case DIR_PURPOSE_FETCH_SERVERDESC:
|
||||||
type = V2_AUTHORITY;
|
type = V2_AUTHORITY;
|
||||||
@ -344,7 +365,8 @@ connection_dir_request_failed(dir_connection_t *conn)
|
|||||||
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
|
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
|
||||||
conn->_base.address);
|
conn->_base.address);
|
||||||
connection_dir_download_networkstatus_failed(conn, -1);
|
connection_dir_download_networkstatus_failed(conn, -1);
|
||||||
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC) {
|
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
|
||||||
|
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
|
||||||
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
|
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
|
||||||
conn->_base.address);
|
conn->_base.address);
|
||||||
connection_dir_download_routerdesc_failed(conn);
|
connection_dir_download_routerdesc_failed(conn);
|
||||||
@ -389,7 +411,7 @@ connection_dir_download_networkstatus_failed(dir_connection_t *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Called when an attempt to download one or more router descriptors
|
/** Called when an attempt to download one or more router descriptors
|
||||||
* on connection <b>conn</b> failed.
|
* or extra-info documents on connection <b>conn</b> failed.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
connection_dir_download_routerdesc_failed(dir_connection_t *conn)
|
connection_dir_download_routerdesc_failed(dir_connection_t *conn)
|
||||||
@ -399,6 +421,8 @@ connection_dir_download_routerdesc_failed(dir_connection_t *conn)
|
|||||||
|
|
||||||
/* No need to relaunch descriptor downloads here: we already do it
|
/* No need to relaunch descriptor downloads here: we already do it
|
||||||
* every 10 seconds (DESCRIPTOR_RETRY_INTERVAL) in main.c. */
|
* every 10 seconds (DESCRIPTOR_RETRY_INTERVAL) in main.c. */
|
||||||
|
tor_assert(conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
|
||||||
|
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO);
|
||||||
|
|
||||||
(void) conn;
|
(void) conn;
|
||||||
}
|
}
|
||||||
@ -452,6 +476,9 @@ directory_initiate_command(const char *address, uint32_t addr,
|
|||||||
case DIR_PURPOSE_FETCH_SERVERDESC:
|
case DIR_PURPOSE_FETCH_SERVERDESC:
|
||||||
log_debug(LD_DIR,"initiating server descriptor fetch");
|
log_debug(LD_DIR,"initiating server descriptor fetch");
|
||||||
break;
|
break;
|
||||||
|
case DIR_PURPOSE_FETCH_EXTRAINFO:
|
||||||
|
log_debug(LD_DIR,"initiating extra-info fetch");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
log_err(LD_BUG, "Unrecognized directory connection purpose.");
|
log_err(LD_BUG, "Unrecognized directory connection purpose.");
|
||||||
tor_assert(0);
|
tor_assert(0);
|
||||||
@ -611,6 +638,12 @@ directory_send_command(dir_connection_t *conn,
|
|||||||
url = tor_malloc(len);
|
url = tor_malloc(len);
|
||||||
tor_snprintf(url, len, "/tor/server/%s", resource);
|
tor_snprintf(url, len, "/tor/server/%s", resource);
|
||||||
break;
|
break;
|
||||||
|
case DIR_PURPOSE_FETCH_EXTRAINFO:
|
||||||
|
httpcommand = "GET";
|
||||||
|
len = strlen(resource)+32;
|
||||||
|
url = tor_malloc(len);
|
||||||
|
tor_snprintf(url, len, "/tor/extra/%s", resource);
|
||||||
|
break;
|
||||||
case DIR_PURPOSE_UPLOAD_DIR:
|
case DIR_PURPOSE_UPLOAD_DIR:
|
||||||
tor_assert(!resource);
|
tor_assert(!resource);
|
||||||
tor_assert(payload);
|
tor_assert(payload);
|
||||||
@ -912,7 +945,8 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
|||||||
compress_method_t compression;
|
compress_method_t compression;
|
||||||
int plausible;
|
int plausible;
|
||||||
int skewed=0;
|
int skewed=0;
|
||||||
int allow_partial = conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC;
|
int allow_partial = (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
|
||||||
|
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO);
|
||||||
int was_compressed=0;
|
int was_compressed=0;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
|
|
||||||
@ -1165,11 +1199,17 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC) {
|
if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
|
||||||
|
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
|
||||||
|
int was_ei = conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO;
|
||||||
smartlist_t *which = NULL;
|
smartlist_t *which = NULL;
|
||||||
int n_asked_for = 0;
|
int n_asked_for = 0;
|
||||||
log_info(LD_DIR,"Received server info (size %d) from server '%s:%d'",
|
log_info(LD_DIR,"Received %s (size %d) from server '%s:%d'",
|
||||||
|
was_ei ? "server info" : "extra server info",
|
||||||
(int)body_len, conn->_base.address, conn->_base.port);
|
(int)body_len, conn->_base.address, conn->_base.port);
|
||||||
|
if (was_ei)
|
||||||
|
note_request(was_compressed?"dl/extra.z":"dl/extra", orig_len);
|
||||||
|
else
|
||||||
note_request(was_compressed?"dl/server.z":"dl/server", orig_len);
|
note_request(was_compressed?"dl/server.z":"dl/server", orig_len);
|
||||||
if (conn->requested_resource &&
|
if (conn->requested_resource &&
|
||||||
!strcmpstart(conn->requested_resource,"d/")) {
|
!strcmpstart(conn->requested_resource,"d/")) {
|
||||||
@ -1192,7 +1232,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
|||||||
if (!which) {
|
if (!which) {
|
||||||
connection_dir_download_routerdesc_failed(conn);
|
connection_dir_download_routerdesc_failed(conn);
|
||||||
} else {
|
} else {
|
||||||
dir_routerdesc_download_failed(which, status_code);
|
dir_routerdesc_download_failed(which, status_code, was_ei);
|
||||||
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
|
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
|
||||||
smartlist_free(which);
|
smartlist_free(which);
|
||||||
}
|
}
|
||||||
@ -1207,15 +1247,19 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
|||||||
if (which || (conn->requested_resource &&
|
if (which || (conn->requested_resource &&
|
||||||
!strcmpstart(conn->requested_resource, "all"))) {
|
!strcmpstart(conn->requested_resource, "all"))) {
|
||||||
/* as we learn from them, we remove them from 'which' */
|
/* as we learn from them, we remove them from 'which' */
|
||||||
|
if (was_ei) {
|
||||||
|
router_load_extrainfo_from_string(body, SAVED_NOWHERE, which);
|
||||||
|
} else {
|
||||||
router_load_routers_from_string(body, SAVED_NOWHERE, which);
|
router_load_routers_from_string(body, SAVED_NOWHERE, which);
|
||||||
directory_info_has_arrived(now, 0);
|
directory_info_has_arrived(now, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (which) { /* mark remaining ones as failed */
|
if (which) { /* mark remaining ones as failed */
|
||||||
log_info(LD_DIR, "Received %d/%d routers requested from %s:%d",
|
log_info(LD_DIR, "Received %d/%d routers requested from %s:%d",
|
||||||
n_asked_for-smartlist_len(which), n_asked_for,
|
n_asked_for-smartlist_len(which), n_asked_for,
|
||||||
conn->_base.address, (int)conn->_base.port);
|
conn->_base.address, (int)conn->_base.port);
|
||||||
if (smartlist_len(which)) {
|
if (smartlist_len(which)) {
|
||||||
dir_routerdesc_download_failed(which, status_code);
|
dir_routerdesc_download_failed(which, status_code, was_ei);
|
||||||
}
|
}
|
||||||
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
|
SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
|
||||||
smartlist_free(which);
|
smartlist_free(which);
|
||||||
@ -2078,17 +2122,23 @@ dir_networkstatus_download_failed(smartlist_t *failed, int status_code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Called when one or more routerdesc fetches have failed (with uppercase
|
/** Called when one or more routerdesc fetches have failed (with uppercase
|
||||||
* fingerprints listed in <b>failed</b>). */
|
* fingerprints listed in <b>failed</b>).
|
||||||
|
*
|
||||||
|
* DOCDOC was_extrainfo */
|
||||||
static void
|
static void
|
||||||
dir_routerdesc_download_failed(smartlist_t *failed, int status_code)
|
dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
|
||||||
|
int was_extrainfo)
|
||||||
{
|
{
|
||||||
char digest[DIGEST_LEN];
|
char digest[DIGEST_LEN];
|
||||||
local_routerstatus_t *rs;
|
local_routerstatus_t *rs;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
int server = server_mode(get_options()) && get_options()->DirPort;
|
int server = server_mode(get_options()) && get_options()->DirPort;
|
||||||
|
(void) was_extrainfo;
|
||||||
SMARTLIST_FOREACH(failed, const char *, cp,
|
SMARTLIST_FOREACH(failed, const char *, cp,
|
||||||
{
|
{
|
||||||
base16_decode(digest, DIGEST_LEN, cp, strlen(cp));
|
base16_decode(digest, DIGEST_LEN, cp, strlen(cp));
|
||||||
|
/* XXXX020 BUG BUG BUG. Fails miserably when requesting by desc digest
|
||||||
|
* rather than by identity digest. */
|
||||||
rs = router_get_combined_status_by_digest(digest);
|
rs = router_get_combined_status_by_digest(digest);
|
||||||
if (!rs || rs->n_download_failures >= MAX_ROUTERDESC_DOWNLOAD_FAILURES)
|
if (!rs || rs->n_download_failures >= MAX_ROUTERDESC_DOWNLOAD_FAILURES)
|
||||||
continue;
|
continue;
|
||||||
|
@ -668,6 +668,11 @@ directory_info_has_arrived(time_t now, int from_cache)
|
|||||||
"build a circuit.");
|
"build a circuit.");
|
||||||
update_router_descriptor_downloads(now);
|
update_router_descriptor_downloads(now);
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
/* Don't even bother trying to get extrainfo until the rest of our
|
||||||
|
* directory info is up-to-date */
|
||||||
|
if (options->DownloadExtraInfo)
|
||||||
|
update_extrainfo_downloads(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_mode(options) && !we_are_hibernating() && !from_cache &&
|
if (server_mode(options) && !we_are_hibernating() && !from_cache &&
|
||||||
@ -862,6 +867,7 @@ run_scheduled_events(time_t now)
|
|||||||
/* XXXX Maybe we should do this every 10sec when not enough info,
|
/* XXXX Maybe we should do this every 10sec when not enough info,
|
||||||
* and every 60sec when we have enough info -NM */
|
* and every 60sec when we have enough info -NM */
|
||||||
update_router_descriptor_downloads(now);
|
update_router_descriptor_downloads(now);
|
||||||
|
update_extrainfo_downloads(now);
|
||||||
time_to_try_getting_descriptors = now + DESCRIPTOR_RETRY_INTERVAL;
|
time_to_try_getting_descriptors = now + DESCRIPTOR_RETRY_INTERVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
src/or/or.h
24
src/or/or.h
@ -360,14 +360,17 @@ typedef enum {
|
|||||||
/** A connection to a directory server: download one or more server
|
/** A connection to a directory server: download one or more server
|
||||||
* descriptors. */
|
* descriptors. */
|
||||||
#define DIR_PURPOSE_FETCH_SERVERDESC 6
|
#define DIR_PURPOSE_FETCH_SERVERDESC 6
|
||||||
|
/** A connection to a directory server: download one or more extra-info
|
||||||
|
* documents. */
|
||||||
|
#define DIR_PURPOSE_FETCH_EXTRAINFO 7
|
||||||
/** A connection to a directory server: upload a server descriptor. */
|
/** A connection to a directory server: upload a server descriptor. */
|
||||||
#define DIR_PURPOSE_UPLOAD_DIR 7
|
#define DIR_PURPOSE_UPLOAD_DIR 8
|
||||||
/** A connection to a directory server: upload a rendezvous
|
/** A connection to a directory server: upload a rendezvous
|
||||||
* descriptor. */
|
* descriptor. */
|
||||||
#define DIR_PURPOSE_UPLOAD_RENDDESC 8
|
#define DIR_PURPOSE_UPLOAD_RENDDESC 9
|
||||||
/** Purpose for connection at a directory server. */
|
/** Purpose for connection at a directory server. */
|
||||||
#define DIR_PURPOSE_SERVER 9
|
#define DIR_PURPOSE_SERVER 10
|
||||||
#define _DIR_PURPOSE_MAX 9
|
#define _DIR_PURPOSE_MAX 10
|
||||||
|
|
||||||
#define _EXIT_PURPOSE_MIN 1
|
#define _EXIT_PURPOSE_MIN 1
|
||||||
/** This exit stream wants to do an ordinary connect. */
|
/** This exit stream wants to do an ordinary connect. */
|
||||||
@ -1068,6 +1071,8 @@ typedef struct signed_descriptor_t {
|
|||||||
off_t saved_offset;
|
off_t saved_offset;
|
||||||
/* DOCDOC */
|
/* DOCDOC */
|
||||||
unsigned int do_not_cache : 1;
|
unsigned int do_not_cache : 1;
|
||||||
|
/* DOCDOC; XXXX020 replace with something smarter. */
|
||||||
|
unsigned int tried_downloading_extrainfo : 1;
|
||||||
} signed_descriptor_t;
|
} signed_descriptor_t;
|
||||||
|
|
||||||
/** Information about another onion router in the network. */
|
/** Information about another onion router in the network. */
|
||||||
@ -1322,6 +1327,7 @@ typedef enum {
|
|||||||
V2_AUTHORITY = 1 << 1,
|
V2_AUTHORITY = 1 << 1,
|
||||||
HIDSERV_AUTHORITY = 1 << 2,
|
HIDSERV_AUTHORITY = 1 << 2,
|
||||||
BRIDGE_AUTHORITY = 1 << 3,
|
BRIDGE_AUTHORITY = 1 << 3,
|
||||||
|
EXTRAINFO_CACHE = 1 << 4, /* not precisely an authority type. */
|
||||||
} authority_type_t;
|
} authority_type_t;
|
||||||
|
|
||||||
#define CRYPT_PATH_MAGIC 0x70127012u
|
#define CRYPT_PATH_MAGIC 0x70127012u
|
||||||
@ -1920,6 +1926,10 @@ typedef struct {
|
|||||||
/** If true, we try resolving hostnames with weird characters. */
|
/** If true, we try resolving hostnames with weird characters. */
|
||||||
int ServerDNSAllowNonRFC953Hostnames;
|
int ServerDNSAllowNonRFC953Hostnames;
|
||||||
|
|
||||||
|
/** If true, we try to download extra-info documents (and we serve them,
|
||||||
|
* if we are a cache). For authorities, this is always true. */
|
||||||
|
int DownloadExtraInfo;
|
||||||
|
|
||||||
} or_options_t;
|
} or_options_t;
|
||||||
|
|
||||||
/** Persistent state for an onion router, as saved to disk. */
|
/** Persistent state for an onion router, as saved to disk. */
|
||||||
@ -2560,6 +2570,7 @@ int dir_split_resource_into_fingerprints(const char *resource,
|
|||||||
smartlist_t *fp_out, int *compresseed_out,
|
smartlist_t *fp_out, int *compresseed_out,
|
||||||
int decode_hex, int sort_uniq);
|
int decode_hex, int sort_uniq);
|
||||||
char *directory_dump_request_log(void);
|
char *directory_dump_request_log(void);
|
||||||
|
int router_supports_extrainfo(const char *identity_digest, int is_authority);
|
||||||
|
|
||||||
/********************************* dirserv.c ***************************/
|
/********************************* dirserv.c ***************************/
|
||||||
|
|
||||||
@ -3125,6 +3136,10 @@ int router_load_single_router(const char *s, uint8_t purpose,
|
|||||||
void router_load_routers_from_string(const char *s,
|
void router_load_routers_from_string(const char *s,
|
||||||
saved_location_t saved_location,
|
saved_location_t saved_location,
|
||||||
smartlist_t *requested_fingerprints);
|
smartlist_t *requested_fingerprints);
|
||||||
|
void router_load_extrainfo_from_string(const char *s,
|
||||||
|
saved_location_t saved_location,
|
||||||
|
smartlist_t *requested_fps);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NS_FROM_CACHE, NS_FROM_DIR_BY_FP, NS_FROM_DIR_ALL, NS_GENERATED
|
NS_FROM_CACHE, NS_FROM_DIR_BY_FP, NS_FROM_DIR_ALL, NS_GENERATED
|
||||||
} networkstatus_source_t;
|
} networkstatus_source_t;
|
||||||
@ -3147,6 +3162,7 @@ local_routerstatus_t *router_get_combined_status_by_digest(const char *digest);
|
|||||||
routerstatus_t *routerstatus_get_by_hexdigest(const char *hexdigest);
|
routerstatus_t *routerstatus_get_by_hexdigest(const char *hexdigest);
|
||||||
void update_networkstatus_downloads(time_t now);
|
void update_networkstatus_downloads(time_t now);
|
||||||
void update_router_descriptor_downloads(time_t now);
|
void update_router_descriptor_downloads(time_t now);
|
||||||
|
void update_extrainfo_downloads(time_t now);
|
||||||
void routers_update_all_from_networkstatus(time_t now);
|
void routers_update_all_from_networkstatus(time_t now);
|
||||||
void routers_update_status_from_networkstatus(smartlist_t *routers,
|
void routers_update_status_from_networkstatus(smartlist_t *routers,
|
||||||
int reset_failures);
|
int reset_failures);
|
||||||
|
@ -1341,7 +1341,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
|
|||||||
"opt fingerprint %s\n"
|
"opt fingerprint %s\n"
|
||||||
"uptime %ld\n"
|
"uptime %ld\n"
|
||||||
"bandwidth %d %d %d\n"
|
"bandwidth %d %d %d\n"
|
||||||
"opt extra-info-digest %s\n"
|
"opt extra-info-digest %s\n%s"
|
||||||
"onion-key\n%s"
|
"onion-key\n%s"
|
||||||
"signing-key\n%s"
|
"signing-key\n%s"
|
||||||
"%s%s%s",
|
"%s%s%s",
|
||||||
@ -1357,6 +1357,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
|
|||||||
(int) router->bandwidthburst,
|
(int) router->bandwidthburst,
|
||||||
(int) router->bandwidthcapacity,
|
(int) router->bandwidthcapacity,
|
||||||
extra_info_digest,
|
extra_info_digest,
|
||||||
|
options->DownloadExtraInfo ? "opt caches-extra-info 1\n" : "",
|
||||||
onion_pkey, identity_pkey,
|
onion_pkey, identity_pkey,
|
||||||
family_line, bandwidth_usage,
|
family_line, bandwidth_usage,
|
||||||
we_are_hibernating() ? "opt hibernating 1\n" : "");
|
we_are_hibernating() ? "opt hibernating 1\n" : "");
|
||||||
|
@ -41,8 +41,6 @@ static local_routerstatus_t *router_get_combined_status_by_nickname(
|
|||||||
int warn_if_unnamed);
|
int warn_if_unnamed);
|
||||||
static void update_router_have_minimum_dir_info(void);
|
static void update_router_have_minimum_dir_info(void);
|
||||||
static void router_dir_info_changed(void);
|
static void router_dir_info_changed(void);
|
||||||
static void router_load_extrainfo_from_string(const char *s,
|
|
||||||
saved_location_t saved_location);
|
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
@ -438,7 +436,7 @@ router_reload_router_list_impl(int extrainfo)
|
|||||||
stats->store_len = (*mmap_ptr)->size;
|
stats->store_len = (*mmap_ptr)->size;
|
||||||
if (extrainfo)
|
if (extrainfo)
|
||||||
router_load_extrainfo_from_string((*mmap_ptr)->data,
|
router_load_extrainfo_from_string((*mmap_ptr)->data,
|
||||||
SAVED_IN_CACHE);
|
SAVED_IN_CACHE, NULL);
|
||||||
else
|
else
|
||||||
router_load_routers_from_string((*mmap_ptr)->data,
|
router_load_routers_from_string((*mmap_ptr)->data,
|
||||||
SAVED_IN_CACHE, NULL);
|
SAVED_IN_CACHE, NULL);
|
||||||
@ -450,7 +448,7 @@ router_reload_router_list_impl(int extrainfo)
|
|||||||
contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, NULL);
|
contents = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, NULL);
|
||||||
if (contents) {
|
if (contents) {
|
||||||
if (extrainfo)
|
if (extrainfo)
|
||||||
router_load_extrainfo_from_string(contents, SAVED_IN_JOURNAL);
|
router_load_extrainfo_from_string(contents, SAVED_IN_JOURNAL, NULL);
|
||||||
else
|
else
|
||||||
router_load_routers_from_string(contents, SAVED_IN_JOURNAL, NULL);
|
router_load_routers_from_string(contents, SAVED_IN_JOURNAL, NULL);
|
||||||
tor_free(contents);
|
tor_free(contents);
|
||||||
@ -644,6 +642,9 @@ router_pick_directory_server_impl(int requireother, int fascistfirewall,
|
|||||||
is_trusted = router_digest_is_trusted_dir(status->identity_digest);
|
is_trusted = router_digest_is_trusted_dir(status->identity_digest);
|
||||||
if ((type & V2_AUTHORITY) && !(status->is_v2_dir || is_trusted))
|
if ((type & V2_AUTHORITY) && !(status->is_v2_dir || is_trusted))
|
||||||
continue;
|
continue;
|
||||||
|
if ((type & EXTRAINFO_CACHE) &&
|
||||||
|
!router_supports_extrainfo(status->identity_digest, 0))
|
||||||
|
continue;
|
||||||
if (prefer_tunnel &&
|
if (prefer_tunnel &&
|
||||||
status->version_supports_begindir &&
|
status->version_supports_begindir &&
|
||||||
(!fascistfirewall ||
|
(!fascistfirewall ||
|
||||||
@ -714,6 +715,9 @@ router_pick_trusteddirserver_impl(authority_type_t type,
|
|||||||
if (!d->is_running) continue;
|
if (!d->is_running) continue;
|
||||||
if ((type & d->type) == 0)
|
if ((type & d->type) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
if ((type & EXTRAINFO_CACHE) &&
|
||||||
|
!router_supports_extrainfo(d->digest, 1))
|
||||||
|
continue;
|
||||||
if (requireother && me && router_digest_is_me(d->digest))
|
if (requireother && me && router_digest_is_me(d->digest))
|
||||||
continue;
|
continue;
|
||||||
if (prefer_tunnel &&
|
if (prefer_tunnel &&
|
||||||
@ -2594,9 +2598,10 @@ router_load_routers_from_string(const char *s, saved_location_t saved_location,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** DOCDOC */
|
/** DOCDOC */
|
||||||
static void
|
void
|
||||||
router_load_extrainfo_from_string(const char *s,
|
router_load_extrainfo_from_string(const char *s,
|
||||||
saved_location_t saved_location)
|
saved_location_t saved_location,
|
||||||
|
smartlist_t *requested_fingerprints)
|
||||||
{
|
{
|
||||||
smartlist_t *extrainfo_list = smartlist_create();
|
smartlist_t *extrainfo_list = smartlist_create();
|
||||||
const char *msg;
|
const char *msg;
|
||||||
@ -2606,8 +2611,15 @@ router_load_extrainfo_from_string(const char *s,
|
|||||||
|
|
||||||
log_info(LD_DIR, "%d elements to add", smartlist_len(extrainfo_list));
|
log_info(LD_DIR, "%d elements to add", smartlist_len(extrainfo_list));
|
||||||
|
|
||||||
SMARTLIST_FOREACH(extrainfo_list, extrainfo_t *, ei,
|
SMARTLIST_FOREACH(extrainfo_list, extrainfo_t *, ei, {
|
||||||
router_add_extrainfo_to_routerlist(ei, &msg, from_cache, !from_cache));
|
if (requested_fingerprints) {
|
||||||
|
char fp[HEX_DIGEST_LEN+1];
|
||||||
|
base16_encode(fp, sizeof(fp), ei->cache_info.signed_descriptor_digest,
|
||||||
|
DIGEST_LEN);
|
||||||
|
smartlist_string_remove(requested_fingerprints, fp);
|
||||||
|
}
|
||||||
|
router_add_extrainfo_to_routerlist(ei, &msg, from_cache, !from_cache);
|
||||||
|
});
|
||||||
|
|
||||||
routerlist_assert_ok(routerlist);
|
routerlist_assert_ok(routerlist);
|
||||||
router_rebuild_store(0, 1);
|
router_rebuild_store(0, 1);
|
||||||
@ -4015,15 +4027,17 @@ routers_update_status_from_networkstatus(smartlist_t *routers,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** For every router descriptor we are currently downloading by descriptor
|
/** For every router descriptor we are currently downloading by descriptor
|
||||||
* digest, set result[d] to 1. */
|
* digest, set result[d] to 1. DOCDOC extrainfo */
|
||||||
static void
|
static void
|
||||||
list_pending_descriptor_downloads(digestmap_t *result)
|
list_pending_descriptor_downloads(digestmap_t *result, int extrainfo)
|
||||||
{
|
{
|
||||||
const char *prefix = "d/";
|
const char *prefix = "d/";
|
||||||
size_t p_len = strlen(prefix);
|
size_t p_len = strlen(prefix);
|
||||||
int i, n_conns;
|
int i, n_conns;
|
||||||
connection_t **carray;
|
connection_t **carray;
|
||||||
smartlist_t *tmp = smartlist_create();
|
smartlist_t *tmp = smartlist_create();
|
||||||
|
int purpose =
|
||||||
|
extrainfo ? DIR_PURPOSE_FETCH_EXTRAINFO : DIR_PURPOSE_FETCH_SERVERDESC;
|
||||||
|
|
||||||
tor_assert(result);
|
tor_assert(result);
|
||||||
get_connection_array(&carray, &n_conns);
|
get_connection_array(&carray, &n_conns);
|
||||||
@ -4031,7 +4045,7 @@ list_pending_descriptor_downloads(digestmap_t *result)
|
|||||||
for (i = 0; i < n_conns; ++i) {
|
for (i = 0; i < n_conns; ++i) {
|
||||||
connection_t *conn = carray[i];
|
connection_t *conn = carray[i];
|
||||||
if (conn->type == CONN_TYPE_DIR &&
|
if (conn->type == CONN_TYPE_DIR &&
|
||||||
conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC &&
|
conn->purpose == purpose &&
|
||||||
!conn->marked_for_close) {
|
!conn->marked_for_close) {
|
||||||
const char *resource = TO_DIR_CONN(conn)->requested_resource;
|
const char *resource = TO_DIR_CONN(conn)->requested_resource;
|
||||||
if (!strcmpstart(resource, prefix))
|
if (!strcmpstart(resource, prefix))
|
||||||
@ -4054,6 +4068,7 @@ list_pending_descriptor_downloads(digestmap_t *result)
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
initiate_descriptor_downloads(routerstatus_t *source,
|
initiate_descriptor_downloads(routerstatus_t *source,
|
||||||
|
int purpose,
|
||||||
smartlist_t *digests,
|
smartlist_t *digests,
|
||||||
int lo, int hi)
|
int lo, int hi)
|
||||||
{
|
{
|
||||||
@ -4081,14 +4096,11 @@ initiate_descriptor_downloads(routerstatus_t *source,
|
|||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
/* We know which authority we want. */
|
/* We know which authority we want. */
|
||||||
directory_initiate_command_routerstatus(source,
|
directory_initiate_command_routerstatus(source, purpose,
|
||||||
DIR_PURPOSE_FETCH_SERVERDESC,
|
|
||||||
0, /* not private */
|
0, /* not private */
|
||||||
resource, NULL, 0);
|
resource, NULL, 0);
|
||||||
} else {
|
} else {
|
||||||
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
|
directory_get_from_dirserver(purpose, resource, 1);
|
||||||
resource,
|
|
||||||
1);
|
|
||||||
}
|
}
|
||||||
tor_free(resource);
|
tor_free(resource);
|
||||||
}
|
}
|
||||||
@ -4133,7 +4145,7 @@ router_list_client_downloadable(void)
|
|||||||
return downloadable;
|
return downloadable;
|
||||||
|
|
||||||
downloading = digestmap_new();
|
downloading = digestmap_new();
|
||||||
list_pending_descriptor_downloads(downloading);
|
list_pending_descriptor_downloads(downloading, 0);
|
||||||
|
|
||||||
routerstatus_list_update_from_networkstatus(now);
|
routerstatus_list_update_from_networkstatus(now);
|
||||||
SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, rs,
|
SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, rs,
|
||||||
@ -4277,7 +4289,8 @@ update_router_descriptor_client_downloads(time_t now)
|
|||||||
req_plural, n_downloadable, rtr_plural, n_per_request);
|
req_plural, n_downloadable, rtr_plural, n_per_request);
|
||||||
smartlist_sort_digests(downloadable);
|
smartlist_sort_digests(downloadable);
|
||||||
for (i=0; i < n_downloadable; i += n_per_request) {
|
for (i=0; i < n_downloadable; i += n_per_request) {
|
||||||
initiate_descriptor_downloads(NULL, downloadable, i, i+n_per_request);
|
initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_SERVERDESC,
|
||||||
|
downloadable, i, i+n_per_request);
|
||||||
}
|
}
|
||||||
last_routerdesc_download_attempted = now;
|
last_routerdesc_download_attempted = now;
|
||||||
}
|
}
|
||||||
@ -4313,7 +4326,7 @@ update_router_descriptor_cache_downloads(time_t now)
|
|||||||
|
|
||||||
/* Set map[d]=1 for the digest of every descriptor that we are currently
|
/* Set map[d]=1 for the digest of every descriptor that we are currently
|
||||||
* downloading. */
|
* downloading. */
|
||||||
list_pending_descriptor_downloads(map);
|
list_pending_descriptor_downloads(map, 0);
|
||||||
|
|
||||||
/* For the digest of every descriptor that we don't have, and that we aren't
|
/* For the digest of every descriptor that we don't have, and that we aren't
|
||||||
* downloading, add d to downloadable[i] if the i'th networkstatus knows
|
* downloading, add d to downloadable[i] if the i'th networkstatus knows
|
||||||
@ -4411,7 +4424,8 @@ update_router_descriptor_cache_downloads(time_t now)
|
|||||||
log_info(LD_DIR, "Requesting %d descriptors from authority \"%s\"",
|
log_info(LD_DIR, "Requesting %d descriptors from authority \"%s\"",
|
||||||
smartlist_len(dl), ds->nickname);
|
smartlist_len(dl), ds->nickname);
|
||||||
for (j=0; j < smartlist_len(dl); j += MAX_DL_PER_REQUEST) {
|
for (j=0; j < smartlist_len(dl); j += MAX_DL_PER_REQUEST) {
|
||||||
initiate_descriptor_downloads(&(ds->fake_status.status), dl, j,
|
initiate_descriptor_downloads(&(ds->fake_status.status),
|
||||||
|
DIR_PURPOSE_FETCH_SERVERDESC, dl, j,
|
||||||
j+MAX_DL_PER_REQUEST);
|
j+MAX_DL_PER_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4437,6 +4451,63 @@ update_router_descriptor_downloads(time_t now)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** DOCDOC */
|
||||||
|
static INLINE int
|
||||||
|
should_download_extrainfo(signed_descriptor_t *sd,
|
||||||
|
const routerlist_t *rl,
|
||||||
|
const digestmap_t *pending)
|
||||||
|
{
|
||||||
|
const char *d = sd->extra_info_digest;
|
||||||
|
/* XXXX020 Check for failures; keep a failure count. Don't just
|
||||||
|
* do this dumb "try once and give up" thing. */
|
||||||
|
return (!sd->tried_downloading_extrainfo &&
|
||||||
|
!tor_digest_is_zero(d) &&
|
||||||
|
!digestmap_get(rl->extra_info_map, d) &&
|
||||||
|
!digestmap_get(pending, d));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** DOCDOC */
|
||||||
|
void
|
||||||
|
update_extrainfo_downloads(time_t now)
|
||||||
|
{
|
||||||
|
or_options_t *options = get_options();
|
||||||
|
routerlist_t *rl;
|
||||||
|
smartlist_t *wanted;
|
||||||
|
digestmap_t *pending;
|
||||||
|
int i;
|
||||||
|
(void) now;
|
||||||
|
if (! options->DownloadExtraInfo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pending = digestmap_new();
|
||||||
|
list_pending_descriptor_downloads(pending, 1);
|
||||||
|
rl = router_get_routerlist();
|
||||||
|
wanted = smartlist_create();
|
||||||
|
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
|
||||||
|
if (should_download_extrainfo(&ri->cache_info, rl, pending)) {
|
||||||
|
smartlist_add(wanted, ri->cache_info.extra_info_digest);
|
||||||
|
ri->cache_info.tried_downloading_extrainfo = 1; /*XXXX020 be smarter.*/
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (options->DirPort) {
|
||||||
|
SMARTLIST_FOREACH(rl->old_routers, signed_descriptor_t *, sd, {
|
||||||
|
if (should_download_extrainfo(sd, rl, pending)) {
|
||||||
|
smartlist_add(wanted, sd->extra_info_digest);
|
||||||
|
sd->tried_downloading_extrainfo = 1; /*XXXX020 be smarter. */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
digestmap_free(pending, NULL);
|
||||||
|
|
||||||
|
smartlist_shuffle(wanted);
|
||||||
|
for (i = 0; i < smartlist_len(wanted); i += MAX_DL_PER_REQUEST) {
|
||||||
|
initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_EXTRAINFO,
|
||||||
|
wanted, i, i + MAX_DL_PER_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
smartlist_free(wanted);
|
||||||
|
}
|
||||||
|
|
||||||
/** Return the number of routerstatus_t in <b>entries</b> that we'd actually
|
/** Return the number of routerstatus_t in <b>entries</b> that we'd actually
|
||||||
* use. */
|
* use. */
|
||||||
static int
|
static int
|
||||||
|
Loading…
Reference in New Issue
Block a user