#define PROCESS_DESCS_PRIVATE
#include "core/or/or.h"
#include "feature/dirauth/process_descs.h"
#include "app/config/config.h"
#include "core/or/policies.h"
#include "core/or/versions.h"
#include "feature/dirauth/dirauth_sys.h"
#include "feature/dirauth/keypin.h"
#include "feature/dirauth/reachability.h"
#include "feature/dirclient/dlstatus.h"
#include "feature/dircommon/directory.h"
#include "feature/nodelist/describe.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerinfo.h"
#include "feature/nodelist/routerlist.h"
#include "feature/dirparse/routerparse.h"
#include "feature/nodelist/torcert.h"
#include "feature/relay/router.h"
#include "core/or/tor_version_st.h"
#include "feature/dirauth/dirauth_options_st.h"
#include "feature/nodelist/extrainfo_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/microdesc_st.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/routerstatus_st.h"
#include "feature/nodelist/vote_routerstatus_st.h"
#include "lib/encoding/confline.h"
#include "lib/crypt_ops/crypto_format.h"
#define ROUTER_ALLOW_SKEW (60*60*12)
static void directory_remove_invalid(void);
static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei,
const char **msg);
static uint32_t
dirserv_get_status_impl(const char *id_digest,
const ed25519_public_key_t *ed25519_public_key,
const char *nickname, uint32_t addr, uint16_t or_port,
const char *platform, const char **msg, int severity);
static authdir_config_t *fingerprint_list = NULL;
static authdir_config_t *
authdir_config_new(void)
{
authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t));
list->fp_by_name = strmap_new();
list->status_by_digest = digestmap_new();
list->status_by_digest256 = digest256map_new();
return list;
}
#ifdef TOR_UNIT_TESTS
void
authdir_init_fingerprint_list(void)
{
fingerprint_list = authdir_config_new();
}
authdir_config_t *
authdir_return_fingerprint_list(void)
{
return fingerprint_list;
}
#endif
int
add_rsa_fingerprint_to_dir(const char *fp, authdir_config_t *list,
rtr_flags_t add_status)
{
char *fingerprint;
char d[DIGEST_LEN];
rtr_flags_t *status;
tor_assert(fp);
tor_assert(list);
fingerprint = tor_strdup(fp);
tor_strstrip(fingerprint, " ");
if (base16_decode(d, DIGEST_LEN,
fingerprint, strlen(fingerprint)) != DIGEST_LEN) {
log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
escaped(fp));
tor_free(fingerprint);
return -1;
}
status = digestmap_get(list->status_by_digest, d);
if (!status) {
status = tor_malloc_zero(sizeof(rtr_flags_t));
digestmap_set(list->status_by_digest, d, status);
}
tor_free(fingerprint);
*status |= add_status;
return 0;
}
int
add_ed25519_to_dir(const ed25519_public_key_t *edkey, authdir_config_t *list,
rtr_flags_t add_status)
{
rtr_flags_t *status;
tor_assert(edkey);
tor_assert(list);
if (ed25519_validate_pubkey(edkey) < 0) {
log_warn(LD_DIRSERV, "Invalid ed25519 key \"%s\"", ed25519_fmt(edkey));
return -1;
}
status = digest256map_get(list->status_by_digest256, edkey->pubkey);
if (!status) {
status = tor_malloc_zero(sizeof(rtr_flags_t));
digest256map_set(list->status_by_digest256, edkey->pubkey, status);
}
*status |= add_status;
return 0;
}
int
dirserv_add_own_fingerprint(crypto_pk_t *pk, const ed25519_public_key_t *edkey)
{
char fp[FINGERPRINT_LEN+1];
if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
log_err(LD_BUG, "Error computing fingerprint");
return -1;
}
if (!fingerprint_list)
fingerprint_list = authdir_config_new();
if (add_rsa_fingerprint_to_dir(fp, fingerprint_list, 0) < 0) {
log_err(LD_BUG, "Error adding RSA fingerprint");
return -1;
}
if (add_ed25519_to_dir(edkey, fingerprint_list, 0) < 0) {
log_err(LD_BUG, "Error adding ed25519 key");
return -1;
}
return 0;
}
int
dirserv_load_fingerprint_file(void)
{
char *fname;
char *cf;
char *nickname, *fingerprint;
authdir_config_t *fingerprint_list_new;
int result;
config_line_t *front=NULL, *list;
fname = get_datadir_fname("approved-routers");
log_info(LD_GENERAL,
"Reloading approved fingerprints from \"%s\"...", fname);
cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
if (!cf) {
log_warn(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname);
tor_free(fname);
return 0;
}
tor_free(fname);
result = config_get_lines(cf, &front, 0);
tor_free(cf);
if (result < 0) {
log_warn(LD_CONFIG, "Error reading from fingerprint file");
return -1;
}
fingerprint_list_new = authdir_config_new();
for (list=front; list; list=list->next) {
rtr_flags_t add_status = 0;
nickname = list->key; fingerprint = list->value;
tor_strstrip(fingerprint, " ");
if (!strcasecmp(nickname, "!reject")) {
add_status = RTR_REJECT;
} else if (!strcasecmp(nickname, "!badexit")) {
add_status = RTR_BADEXIT;
} else if (!strcasecmp(nickname, "!invalid")) {
add_status = RTR_INVALID;
}
int ed25519_not_ok = -1, rsa_not_ok = -1;
if (strlen(fingerprint) == HEX_DIGEST_LEN) {
rsa_not_ok = add_rsa_fingerprint_to_dir(fingerprint,
fingerprint_list_new,
add_status);
}
ed25519_public_key_t ed25519_pubkey_tmp;
if (strlen(fingerprint) == BASE64_DIGEST256_LEN) {
if (!digest256_from_base64((char *) ed25519_pubkey_tmp.pubkey,
fingerprint)) {
ed25519_not_ok = add_ed25519_to_dir(&ed25519_pubkey_tmp,
fingerprint_list_new, add_status);
}
}
if (ed25519_not_ok && rsa_not_ok) {
log_warn(LD_CONFIG, "Invalid fingerprint (nickname '%s', "
"fingerprint %s). Skipping.", nickname, fingerprint);
continue;
}
}
config_free_lines(front);
dirserv_free_fingerprint_list();
fingerprint_list = fingerprint_list_new;
directory_remove_invalid();
return 0;
}
#define DISABLE_DISABLING_ED25519
uint32_t
dirserv_router_get_status(const routerinfo_t *router, const char **msg,
int severity)
{
char d[DIGEST_LEN];
const int key_pinning = dirauth_get_options()->AuthDirPinKeys;
uint32_t r;
ed25519_public_key_t *signing_key = NULL;
if (crypto_pk_get_digest(router->identity_pkey, d)) {
log_warn(LD_BUG,"Error computing fingerprint");
if (msg)
*msg = "Bug: Error computing fingerprint";
return RTR_REJECT;
}
if (router->cache_info.signing_key_cert) {
signing_key = &router->cache_info.signing_key_cert->signing_key;
}
r = dirserv_get_status_impl(d, signing_key, router->nickname, router->addr,
router->or_port, router->platform, msg,
severity);
if (r)
return r;
if (!routerinfo_has_curve25519_onion_key(router)) {
log_fn(severity, LD_DIR,
"Descriptor from router %s is missing an ntor curve25519 onion "
"key.", router_describe(router));
if (msg)
*msg = "Missing ntor curve25519 onion key. Please upgrade!";
return RTR_REJECT;
}
if (router->cache_info.signing_key_cert) {
if (KEYPIN_MISMATCH ==
keypin_check((const uint8_t*)router->cache_info.identity_digest,
router->cache_info.signing_key_cert->signing_key.pubkey)) {
log_fn(severity, LD_DIR,
"Descriptor from router %s has an Ed25519 key, "
"but the <rsa,ed25519> keys don't match what they were before.",
router_describe(router));
if (key_pinning) {
if (msg) {
*msg = "Ed25519 identity key or RSA identity key has changed.";
}
return RTR_REJECT;
}
}
} else {
if (KEYPIN_MISMATCH == keypin_check_lone_rsa(
(const uint8_t*)router->cache_info.identity_digest)) {
log_fn(severity, LD_DIR,
"Descriptor from router %s has no Ed25519 key, "
"when we previously knew an Ed25519 for it. Ignoring for now, "
"since Ed25519 keys are fairly new.",
router_describe(router));
#ifdef DISABLE_DISABLING_ED25519
if (key_pinning) {
if (msg) {
*msg = "Ed25519 identity key has disappeared.";
}
return RTR_REJECT;
}
#endif
}
}
return 0;
}
int
dirserv_would_reject_router(const routerstatus_t *rs,
const vote_routerstatus_t *vrs)
{
uint32_t res;
struct ed25519_public_key_t pk;
memcpy(&pk.pubkey, vrs->ed25519_id, ED25519_PUBKEY_LEN);
res = dirserv_get_status_impl(rs->identity_digest, &pk, rs->nickname,
rs->addr, rs->or_port, NULL, NULL, LOG_DEBUG);
return (res & RTR_REJECT) != 0;
}
STATIC bool
dirserv_rejects_tor_version(const char *platform,
const char **msg)
{
if (!platform)
return false;
static const char please_upgrade_string[] =
"Tor version is insecure or unsupported. Please upgrade!";
if (!tor_version_as_new_as(platform,"0.3.5.7")) {
if (msg)
*msg = please_upgrade_string;
return true;
}
if (tor_version_as_new_as(platform,"0.3.6.0-alpha-dev") &&
!tor_version_as_new_as(platform,"0.4.1.5")) {
if (msg) {
*msg = please_upgrade_string;
}
return true;
}
return false;
}
static uint32_t
dirserv_get_status_impl(const char *id_digest,
const ed25519_public_key_t *ed25519_public_key,
const char *nickname, uint32_t addr, uint16_t or_port,
const char *platform, const char **msg, int severity)
{
uint32_t result = 0;
rtr_flags_t *status_by_digest;
if (!fingerprint_list)
fingerprint_list = authdir_config_new();
log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.",
strmap_size(fingerprint_list->fp_by_name),
digestmap_size(fingerprint_list->status_by_digest));
if (platform) {
tor_version_t ver_tmp;
if (tor_version_parse_platform(platform, &ver_tmp, 1) < 0) {
if (msg) {
*msg = "Malformed platform string.";
}
return RTR_REJECT;
}
}
if (platform && dirserv_rejects_tor_version(platform, msg)) {
return RTR_REJECT;
}
status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
id_digest);
if (status_by_digest)
result |= *status_by_digest;
if (ed25519_public_key) {
status_by_digest = digest256map_get(fingerprint_list->status_by_digest256,
ed25519_public_key->pubkey);
if (status_by_digest)
result |= *status_by_digest;
}
if (result & RTR_REJECT) {
if (msg)
*msg = "Fingerprint and/or ed25519 identity is marked rejected -- if "
"you think this is a mistake please set a valid email address "
"in ContactInfo and send an email to "
"bad-relays@lists.torproject.org mentioning your fingerprint(s)?";
return RTR_REJECT;
} else if (result & RTR_INVALID) {
if (msg)
*msg = "Fingerprint and/or ed25519 identity is marked invalid";
}
if (authdir_policy_badexit_address(addr, or_port)) {
log_fn(severity, LD_DIRSERV,
"Marking '%s' as bad exit because of address '%s'",
nickname, fmt_addr32(addr));
result |= RTR_BADEXIT;
}
if (!authdir_policy_permits_address(addr, or_port)) {
log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'",
nickname, fmt_addr32(addr));
if (msg)
*msg = "Suspicious relay address range -- if you think this is a "
"mistake please set a valid email address in ContactInfo and "
"send an email to bad-relays@lists.torproject.org mentioning "
"your address(es) and fingerprint(s)?";
return RTR_REJECT;
}
if (!authdir_policy_valid_address(addr, or_port)) {
log_fn(severity, LD_DIRSERV,
"Not marking '%s' valid because of address '%s'",
nickname, fmt_addr32(addr));
result |= RTR_INVALID;
}
return result;
}
void
dirserv_free_fingerprint_list(void)
{
if (!fingerprint_list)
return;
strmap_free(fingerprint_list->fp_by_name, tor_free_);
digestmap_free(fingerprint_list->status_by_digest, tor_free_);
digest256map_free(fingerprint_list->status_by_digest256, tor_free_);
tor_free(fingerprint_list);
}
STATIC int
dirserv_router_has_valid_address(routerinfo_t *ri)
{
tor_addr_t addr;
if (get_options()->DirAllowPrivateAddresses)
return 0;
tor_addr_from_ipv4h(&addr, ri->addr);
if (tor_addr_is_null(&addr) || tor_addr_is_internal(&addr, 0)) {
log_info(LD_DIRSERV,
"Router %s published internal IPv4 address. Refusing.",
router_describe(ri));
return -1;
}
if (!tor_addr_is_null(&ri->ipv6_addr) &&
tor_addr_is_internal(&ri->ipv6_addr, 0)) {
log_info(LD_DIRSERV,
"Router %s published internal IPv6 address. Refusing.",
router_describe(ri));
return -1;
}
return 0;
}
int
authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
int complain, int *valid_out)
{
time_t now;
int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO;
uint32_t status = dirserv_router_get_status(ri, msg, severity);
tor_assert(msg);
if (status & RTR_REJECT)
return -1;
now = time(NULL);
if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) {
log_fn(severity, LD_DIRSERV, "Publication time for %s is too "
"far (%d minutes) in the future; possible clock skew. Not adding "
"(%s)",
router_describe(ri),
(int)((ri->cache_info.published_on-now)/60),
esc_router_info(ri));
*msg = "Rejected: Your clock is set too far in the future, or your "
"timezone is not correct.";
return -1;
}
if (ri->cache_info.published_on < now-ROUTER_MAX_AGE_TO_PUBLISH) {
log_fn(severity, LD_DIRSERV,
"Publication time for %s is too far "
"(%d minutes) in the past. Not adding (%s)",
router_describe(ri),
(int)((now-ri->cache_info.published_on)/60),
esc_router_info(ri));
*msg = "Rejected: Server is expired, or your clock is too far in the past,"
" or your timezone is not correct.";
return -1;
}
if (dirserv_router_has_valid_address(ri) < 0) {
log_fn(severity, LD_DIRSERV,
"Router %s has invalid address. Not adding (%s).",
router_describe(ri),
esc_router_info(ri));
*msg = "Rejected: Address is a private address.";
return -1;
}
*valid_out = ! (status & RTR_INVALID);
return 0;
}
void
dirserv_set_node_flags_from_authoritative_status(node_t *node,
uint32_t authstatus)
{
node->is_valid = (authstatus & RTR_INVALID) ? 0 : 1;
node->is_bad_exit = (authstatus & RTR_BADEXIT) ? 1 : 0;
}
static int
WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
{
return a < b;
}
was_router_added_t
dirserv_add_multiple_descriptors(const char *desc, size_t desclen,
uint8_t purpose,
const char *source,
const char **msg)
{
was_router_added_t r, r_tmp;
const char *msg_out;
smartlist_t *list;
const char *s;
int n_parsed = 0;
time_t now = time(NULL);
char annotation_buf[ROUTER_ANNOTATION_BUF_LEN];
char time_buf[ISO_TIME_LEN+1];
int general = purpose == ROUTER_PURPOSE_GENERAL;
tor_assert(msg);
r=ROUTER_ADDED_SUCCESSFULLY;
if (!string_is_utf8_no_bom(desc, desclen)) {
*msg = "descriptor(s) or extrainfo(s) not valid UTF-8 or had BOM.";
return ROUTER_AUTHDIR_REJECTS;
}
format_iso_time(time_buf, now);
if (tor_snprintf(annotation_buf, sizeof(annotation_buf),
"@uploaded-at %s\n"
"@source %s\n"
"%s%s%s", time_buf, escaped(source),
!general ? "@purpose " : "",
!general ? router_purpose_to_string(purpose) : "",
!general ? "\n" : "")<0) {
*msg = "Couldn't format annotations";
return ROUTER_AUTHDIR_BUG_ANNOTATIONS;
}
s = desc;
list = smartlist_new();
if (!router_parse_list_from_string(&s, s+desclen, list, SAVED_NOWHERE, 0, 0,
annotation_buf, NULL)) {
SMARTLIST_FOREACH(list, routerinfo_t *, ri, {
msg_out = NULL;
tor_assert(ri->purpose == purpose);
r_tmp = dirserv_add_descriptor(ri, &msg_out, source);
if (WRA_MORE_SEVERE(r_tmp, r)) {
r = r_tmp;
*msg = msg_out;
}
});
}
n_parsed += smartlist_len(list);
smartlist_clear(list);
s = desc;
if (!router_parse_list_from_string(&s, s+desclen, list, SAVED_NOWHERE, 1, 0,
NULL, NULL)) {
SMARTLIST_FOREACH(list, extrainfo_t *, ei, {
msg_out = NULL;
r_tmp = dirserv_add_extrainfo(ei, &msg_out);
if (WRA_MORE_SEVERE(r_tmp, r)) {
r = r_tmp;
*msg = msg_out;
}
});
}
n_parsed += smartlist_len(list);
smartlist_free(list);
if (! *msg) {
if (!n_parsed) {
*msg = "No descriptors found in your POST.";
if (WRA_WAS_ADDED(r))
r = ROUTER_IS_ALREADY_KNOWN;
} else {
*msg = "(no message)";
}
}
return r;
}
was_router_added_t
dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
{
was_router_added_t r;
routerinfo_t *ri_old;
char *desc, *nickname;
const size_t desclen = ri->cache_info.signed_descriptor_len +
ri->cache_info.annotations_len;
const int key_pinning = dirauth_get_options()->AuthDirPinKeys;
*msg = NULL;
if (ri->cache_info.signed_descriptor_len > MAX_DESCRIPTOR_UPLOAD_SIZE) {
log_notice(LD_DIR, "Somebody attempted to publish a router descriptor '%s'"
" (source: %s) with size %d. Either this is an attack, or the "
"MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.",
ri->nickname, source, (int)ri->cache_info.signed_descriptor_len,
MAX_DESCRIPTOR_UPLOAD_SIZE);
*msg = "Router descriptor was too large.";
r = ROUTER_AUTHDIR_REJECTS;
goto fail;
}
ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest);
if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
&& router_differences_are_cosmetic(ri_old, ri)
&& !router_is_me(ri)) {
log_info(LD_DIRSERV,
"Not replacing descriptor from %s (source: %s); "
"differences are cosmetic.",
router_describe(ri), source);
*msg = "Not replacing router descriptor; no information has changed since "
"the last one with this identity.";
r = ROUTER_IS_ALREADY_KNOWN;
goto fail;
}
int keypin_status;
if (ri->cache_info.signing_key_cert) {
ed25519_public_key_t *pkey = &ri->cache_info.signing_key_cert->signing_key;
if (ed25519_validate_pubkey(pkey) < 0) {
log_warn(LD_DIRSERV, "Received bad key from %s (source %s)",
router_describe(ri), source);
routerinfo_free(ri);
return ROUTER_AUTHDIR_REJECTS;
}
keypin_status = keypin_check_and_add(
(const uint8_t*)ri->cache_info.identity_digest,
pkey->pubkey, ! key_pinning);
} else {
keypin_status = keypin_check_lone_rsa(
(const uint8_t*)ri->cache_info.identity_digest);
#ifndef DISABLE_DISABLING_ED25519
if (keypin_status == KEYPIN_MISMATCH)
keypin_status = KEYPIN_NOT_FOUND;
#endif
}
if (keypin_status == KEYPIN_MISMATCH && key_pinning) {
log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because "
"its key did not match an older RSA/Ed25519 keypair",
router_describe(ri), source);
*msg = "Looks like your keypair has changed? This authority previously "
"recorded a different RSA identity for this Ed25519 identity (or vice "
"versa.) Did you replace or copy some of your key files, but not "
"the others? You should either restore the expected keypair, or "
"delete your keys and restart Tor to start your relay with a new "
"identity.";
r = ROUTER_AUTHDIR_REJECTS;
goto fail;
}
desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen);
nickname = tor_strdup(ri->nickname);
ri->needs_retest_if_added =
dirserv_should_launch_reachability_test(ri, ri_old);
r = router_add_to_routerlist(ri, msg, 0, 0);
if (!WRA_WAS_ADDED(r)) {
log_info(LD_DIRSERV,
"Did not add descriptor from '%s' (source: %s): %s.",
nickname, source, *msg ? *msg : "(no message)");
} else {
smartlist_t *changed;
changed = smartlist_new();
smartlist_add(changed, ri);
routerlist_descriptors_added(changed, 0);
smartlist_free(changed);
if (!*msg) {
*msg = "Descriptor accepted";
}
log_info(LD_DIRSERV,
"Added descriptor from '%s' (source: %s): %s.",
nickname, source, *msg);
}
tor_free(desc);
tor_free(nickname);
return r;
fail:
{
const char *desc_digest = ri->cache_info.signed_descriptor_digest;
download_status_t *dls =
router_get_dl_status_by_descriptor_digest(desc_digest);
if (dls) {
log_info(LD_GENERAL, "Marking router with descriptor %s as rejected, "
"and therefore undownloadable",
hex_str(desc_digest, DIGEST_LEN));
download_status_mark_impossible(dls);
}
routerinfo_free(ri);
}
return r;
}
static was_router_added_t
dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{
routerinfo_t *ri;
int r;
was_router_added_t rv;
tor_assert(msg);
*msg = NULL;
ri = router_get_mutable_by_digest(ei->cache_info.identity_digest);
if (!ri) {
*msg = "No corresponding router descriptor for extra-info descriptor";
rv = ROUTER_BAD_EI;
goto fail;
}
if (ei->cache_info.signed_descriptor_len > MAX_EXTRAINFO_UPLOAD_SIZE) {
log_notice(LD_DIR, "Somebody attempted to publish an extrainfo "
"with size %d. Either this is an attack, or the "
"MAX_EXTRAINFO_UPLOAD_SIZE (%d) constant is too low.",
(int)ei->cache_info.signed_descriptor_len,
MAX_EXTRAINFO_UPLOAD_SIZE);
*msg = "Extrainfo document was too large";
rv = ROUTER_BAD_EI;
goto fail;
}
if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
&ri->cache_info, msg))) {
if (r<0) {
extrainfo_free(ei);
return ROUTER_IS_ALREADY_KNOWN;
}
rv = ROUTER_BAD_EI;
goto fail;
}
router_add_extrainfo_to_routerlist(ei, msg, 0, 0);
return ROUTER_ADDED_SUCCESSFULLY;
fail:
{
const char *d = ei->cache_info.signed_descriptor_digest;
signed_descriptor_t *sd = router_get_by_extrainfo_digest((char*)d);
if (sd) {
log_info(LD_GENERAL, "Marking extrainfo with descriptor %s as "
"rejected, and therefore undownloadable",
hex_str((char*)d,DIGEST_LEN));
download_status_mark_impossible(&sd->ei_dl_status);
}
extrainfo_free(ei);
}
return rv;
}
static void
directory_remove_invalid(void)
{
routerlist_t *rl = router_get_routerlist();
smartlist_t *nodes = smartlist_new();
smartlist_add_all(nodes, nodelist_get_list());
SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
const char *msg = NULL;
const char *description;
routerinfo_t *ent = node->ri;
uint32_t r;
if (!ent)
continue;
r = dirserv_router_get_status(ent, &msg, LOG_INFO);
description = router_describe(ent);
if (r & RTR_REJECT) {
log_info(LD_DIRSERV, "Router %s is now rejected: %s",
description, msg?msg:"");
routerlist_remove(rl, ent, 0, time(NULL));
continue;
}
if (bool_neq((r & RTR_INVALID), !node->is_valid)) {
log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description,
(r&RTR_INVALID) ? "in" : "");
node->is_valid = (r&RTR_INVALID)?0:1;
}
if (bool_neq((r & RTR_BADEXIT), node->is_bad_exit)) {
log_info(LD_DIRSERV, "Router '%s' is now a %s exit", description,
(r & RTR_BADEXIT) ? "bad" : "good");
node->is_bad_exit = (r&RTR_BADEXIT) ? 1: 0;
}
} SMARTLIST_FOREACH_END(node);
routerlist_assert_ok(rl);
smartlist_free(nodes);
}