aboutsummaryrefslogtreecommitdiff
path: root/src/or/nodelist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/nodelist.c')
-rw-r--r--src/or/nodelist.c418
1 files changed, 397 insertions, 21 deletions
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index b620b408ea..f2923a4eba 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -5,14 +5,17 @@
/* See LICENSE for licensing information */
#include "or.h"
-#include "nodelist.h"
+#include "config.h"
+#include "dirserv.h"
#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
+#include "router.h"
#include "routerlist.h"
#include <string.h>
-static void nodelist_drop_node(node_t *node);
+static void nodelist_drop_node(node_t *node, int remove_from_ht);
static void node_free(node_t *node);
/** A nodelist_t holds a node_t object for every router we're "willing to use
@@ -24,6 +27,7 @@ typedef struct nodelist_t {
smartlist_t *nodes;
/* Hash table to map from node ID digest to node. */
HT_HEAD(nodelist_map, node_t) nodes_by_id;
+
} nodelist_t;
static INLINE unsigned int
@@ -63,10 +67,9 @@ init_nodelist(void)
}
}
-/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
- * if no such node exists. */
+/** As node_get_by_id, but returns a non-const pointer */
node_t *
-node_get_by_id(const char *identity_digest)
+node_get_mutable_by_id(const char *identity_digest)
{
node_t search, *node;
if (PREDICT_UNLIKELY(the_nodelist == NULL))
@@ -77,6 +80,14 @@ node_get_by_id(const char *identity_digest)
return node;
}
+/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
+ * if no such node exists. */
+const node_t *
+node_get_by_id(const char *identity_digest)
+{
+ return node_get_mutable_by_id(identity_digest);
+}
+
/** Internal: return the node_t whose identity_digest is
* <b>identity_digest</b>. If none exists, create a new one, add it to the
* nodelist, and return it.
@@ -88,7 +99,7 @@ node_get_or_create(const char *identity_digest)
{
node_t *node;
- if ((node = node_get_by_id(identity_digest)))
+ if ((node = node_get_mutable_by_id(identity_digest)))
return node;
node = tor_malloc_zero(sizeof(node_t));
@@ -98,6 +109,8 @@ node_get_or_create(const char *identity_digest)
smartlist_add(the_nodelist->nodes, node);
node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
+ node->country = -1;
+
return node;
}
@@ -109,6 +122,16 @@ nodelist_add_routerinfo(routerinfo_t *ri)
init_nodelist();
node = node_get_or_create(ri->cache_info.identity_digest);
node->ri = ri;
+
+ if (node->country == -1)
+ node_set_country(node);
+
+ if (authdir_mode(get_options())) {
+ const char *discard=NULL;
+ uint32_t status = dirserv_router_get_status(ri, &discard);
+ dirserv_set_node_flags_from_authoritative_status(node, status);
+ }
+
return node;
}
@@ -133,7 +156,7 @@ nodelist_add_microdesc(microdesc_t *md)
rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest);
if (rs == NULL)
return NULL;
- node = node_get_by_id(rs->identity_digest);
+ node = node_get_mutable_by_id(rs->identity_digest);
if (node)
node->md = md;
return node;
@@ -147,6 +170,8 @@ nodelist_add_microdesc(microdesc_t *md)
void
nodelist_set_consensus(networkstatus_t *ns)
{
+ or_options_t *options = get_options();
+ int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
init_nodelist();
SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node,
@@ -162,8 +187,43 @@ nodelist_set_consensus(networkstatus_t *ns)
rs->descriptor_digest);
}
}
+
+ node_set_country(node);
+
+ /* If we're not an authdir, believe others. */
+ if (!authdir) {
+ node->is_valid = rs->is_valid;
+ node->is_running = rs->is_running;
+ node->is_fast = rs->is_fast;
+ node->is_stable = rs->is_stable;
+ node->is_possible_guard = rs->is_possible_guard;
+ node->is_exit = rs->is_exit;
+ node->is_bad_directory = rs->is_bad_directory;
+ node->is_bad_exit = rs->is_bad_exit;
+ node->is_hs_dir = rs->is_hs_dir;
+ }
+
} SMARTLIST_FOREACH_END(rs);
+
nodelist_purge();
+
+ if (! authdir) {
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ /* We have no routerstatus for this router. Clear flags so we can skip
+ * it, maybe.*/
+ if (!node->rs) {
+ tor_assert(node->ri); /* if it had only an md, or nothing, purge
+ * would have removed it. */
+ if (node->ri->purpose == ROUTER_PURPOSE_GENERAL) {
+ /* Clear all flags. */
+ node->is_valid = node->is_running = node->is_hs_dir =
+ node->is_fast = node->is_stable =
+ node->is_possible_guard = node->is_exit =
+ node->is_bad_exit = node->is_bad_directory = 0;
+ }
+ }
+ } SMARTLIST_FOREACH_END(node);
+ }
}
/** Helper: return true iff a node has a usable amount of information*/
@@ -178,7 +238,7 @@ node_is_usable(const node_t *node)
void
nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
{
- node_t *node = node_get_by_id(identity_digest);
+ node_t *node = node_get_mutable_by_id(identity_digest);
if (node && node->md == md)
node->md = NULL;
}
@@ -187,11 +247,11 @@ nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
void
nodelist_remove_routerinfo(routerinfo_t *ri)
{
- node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
if (node && node->ri == ri) {
node->ri = NULL;
if (! node_is_usable(node)) {
- nodelist_drop_node(node);
+ nodelist_drop_node(node, 1);
node_free(node);
}
}
@@ -200,12 +260,14 @@ nodelist_remove_routerinfo(routerinfo_t *ri)
/** Remove <b>node</b> from the nodelist. (Asserts that it was there to begin
* with.) */
static void
-nodelist_drop_node(node_t *node)
+nodelist_drop_node(node_t *node, int remove_from_ht)
{
node_t *tmp;
int idx;
- tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
- tor_assert(tmp == node);
+ if (remove_from_ht) {
+ tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
+ tor_assert(tmp == node);
+ }
idx = node->nodelist_idx;
tor_assert(idx >= 0);
@@ -238,6 +300,7 @@ nodelist_purge(void)
if (PREDICT_UNLIKELY(the_nodelist == NULL))
return;
+ /* Remove the non-usable nodes. */
for (iter = HT_START(nodelist_map, &the_nodelist->nodes_by_id); iter; ) {
node_t *node = *iter;
@@ -245,11 +308,11 @@ nodelist_purge(void)
iter = HT_NEXT(nodelist_map, &the_nodelist->nodes_by_id, iter);
} else {
iter = HT_NEXT_RMV(nodelist_map, &the_nodelist->nodes_by_id, iter);
- nodelist_drop_node(node);
+ nodelist_drop_node(node, 0);
node_free(node);
}
}
-
+ nodelist_assert_ok();
}
/** Release all storage held by the nodelist. */
@@ -286,25 +349,25 @@ nodelist_assert_ok(void)
/* every routerinfo in rl->routers should be in the nodelist. */
if (rl) {
SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
- node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+ const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
tor_assert(node && node->ri == ri);
tor_assert(0 == memcmp(ri->cache_info.identity_digest,
node->identity, DIGEST_LEN));
tor_assert(! digestmap_get(dm, node->identity));
- digestmap_set(dm, node->identity, node);
+ digestmap_set(dm, node->identity, (void*)node);
} SMARTLIST_FOREACH_END(ri);
}
/* every routerstatus in ns should be in the nodelist */
if (ns) {
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
- node_t *node = node_get_by_id(rs->identity_digest);
+ const node_t *node = node_get_by_id(rs->identity_digest);
tor_assert(node && node->rs == rs);
tor_assert(0 == memcmp(rs->identity_digest, node->identity, DIGEST_LEN));
- digestmap_set(dm, node->identity, node);
+ digestmap_set(dm, node->identity, (void*)node);
if (ns->flavor == FLAV_MICRODESC) {
- /* If it's a microdesc consensus, every entry that has a microdescriptor
- * should be in the nodelist.
+ /* If it's a microdesc consensus, every entry that has a
+ * microdescriptor should be in the nodelist.
*/
microdesc_t *md =
microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest);
@@ -326,3 +389,316 @@ nodelist_assert_ok(void)
digestmap_free(dm, NULL);
}
+/** Return a list of a node_t * for every node we know about. The caller
+ * MUST NOT modify the list. (You can set and clear flags in the nodes if
+ * you must, but you must not add or remove nodes.) */
+smartlist_t *
+nodelist_get_list(void)
+{
+ init_nodelist();
+ return the_nodelist->nodes;
+}
+
+/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
+ * the corresponding node_t, or NULL if none exists. Warn the user if
+ * <b>warn_if_unnamed</b> is set, and they have specified a router by
+ * nickname, but the Named flag isn't set for that router. */
+const node_t *
+node_get_by_nickname(const char *nickname, int warn_if_unnamed)
+{
+ char digest_buf[DIGEST_LEN];
+ char nn_buf[MAX_NICKNAME_LEN+1];
+ char nn_char='\0';
+
+ if (!the_nodelist)
+ return NULL;
+
+ /* ???? NM Naming authorities had an additional weird behavior here where
+ they would treat their own namings as slightly authoritative in a
+ strange and inconsistent way. I think that this way is better, but we
+ could get the old behavior back if we wanted to by adding a function
+ to look in the fp_by_name table in fingerprint_list, and using this
+ function to override the name-to-digest lookup below if we are a
+ naming server. -NM
+ */
+
+ /* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
+ if (hex_digest_nickname_decode(nickname, digest_buf, &nn_char, nn_buf)==0) {
+ const node_t *node = node_get_by_id(digest_buf);
+ if (!node)
+ return NULL;
+ if (nn_char) {
+ const char *real_name = node_get_nickname(node);
+ if (!real_name || strcasecmp(real_name, nn_buf))
+ return NULL;
+ if (nn_char == '=') {
+ const char *named_id =
+ networkstatus_get_router_digest_by_nickname(nn_buf);
+ if (!named_id || memcmp(named_id, digest_buf, DIGEST_LEN))
+ return NULL;
+ }
+ }
+ return node;
+ }
+
+ if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
+ return NULL;
+
+ /* Okay, so if we get here, the nickname is just a nickname. Is there
+ * a binding for it in the consensus? */
+ {
+ const char *named_id =
+ networkstatus_get_router_digest_by_nickname(nickname);
+ if (named_id)
+ return node_get_by_id(named_id);
+ }
+
+ /* Is it marked as owned-by-someone-else? */
+ if (networkstatus_nickname_is_unnamed(nickname)) {
+ log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
+ "router that holds it, but not one listed in the current "
+ "consensus.", escaped(nickname));
+ return NULL;
+ }
+
+ /* Okay, so the name is not canonical for anybody. */
+ {
+ smartlist_t *matches = smartlist_create();
+ const node_t *choice = NULL;
+
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ if (!strcasecmp(node_get_nickname(node), nickname))
+ smartlist_add(matches, node);
+ } SMARTLIST_FOREACH_END(node);
+
+ if (smartlist_len(matches)>1 && warn_if_unnamed) {
+ int any_unwarned = 0;
+ SMARTLIST_FOREACH_BEGIN(matches, node_t *, node) {
+ if (!node->name_lookup_warned) {
+ node->name_lookup_warned = 1;
+ any_unwarned = 1;
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ if (any_unwarned) {
+ log_warn(LD_CONFIG, "There are multiple matches for the name %s, "
+ "but none is listed as Named in the directory consensus. "
+ "Choosing one arbitrarily.", nickname);
+ }
+ } else if (smartlist_len(matches)>1 && warn_if_unnamed) {
+ char fp[HEX_DIGEST_LEN+1];
+ node_t *node = smartlist_get(matches, 0);
+ if (node->name_lookup_warned) {
+ base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN);
+ log_warn(LD_CONFIG,
+ "You specified a server \"%s\" by name, but the directory "
+ "authorities do not have any key registered for this "
+ "nickname -- so it could be used by any server, not just "
+ "the one you meant. "
+ "To make sure you get the same server in the future, refer "
+ "to it by key, as \"$%s\".", nickname, fp);
+ node->name_lookup_warned = 1;
+ }
+ }
+
+ if (smartlist_len(matches))
+ choice = smartlist_get(matches, 0);
+
+ smartlist_free(matches);
+ return choice;
+ }
+}
+
+/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
+const char *
+node_get_nickname(const node_t *node)
+{
+ if (node->rs)
+ return node->rs->nickname;
+ else if (node->ri)
+ return node->ri->nickname;
+ else
+ return NULL;
+}
+
+/** Return true iff the nickname of <b>node</b> is canonical, based on the
+ * latest consensus. */
+int
+node_is_named(const node_t *node)
+{
+ const char *named_id;
+ const char *nickname = node_get_nickname(node);
+ if (!nickname)
+ return 0;
+ named_id = networkstatus_get_router_digest_by_nickname(nickname);
+ if (!named_id)
+ return 0;
+ return !memcmp(named_id, node->identity, DIGEST_LEN);
+}
+
+/** Return true iff <b>node</b> appears to be a directory authority or
+ * directory cache */
+int
+node_is_dir(const node_t *node)
+{
+ if (node->rs)
+ return node->rs->dir_port != 0;
+ else if (node->ri)
+ return node->ri->dir_port != 0;
+ else
+ return 0;
+}
+
+
+/** Return true iff <b>node</b> has either kind of usable descriptor -- that
+ * is, a routerdecriptor or a microdescriptor. */
+int
+node_has_descriptor(const node_t *node)
+{
+ return (node->ri ||
+ (node->rs && node->md));
+}
+
+/** Return the router_purpose of <b>node</b>. */
+int
+node_get_purpose(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->purpose;
+ else
+ return ROUTER_PURPOSE_GENERAL;
+}
+
+/** Compute the verbose ("extended") nickname of <b>node</b> and store it
+ * into the MAX_VERBOSE_NICKNAME_LEN+1 character buffer at
+ * <b>verbose_nickname_out</b> */
+void
+node_get_verbose_nickname(const node_t *node,
+ char *verbose_name_out)
+{
+ const char *nickname = node_get_nickname(node);
+ int is_named = node_is_named(node);
+ verbose_name_out[0] = '$';
+ base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
+ DIGEST_LEN);
+ if (!nickname)
+ return;
+ verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
+}
+
+/** Return true iff it seems that <b>node</b> allows circuits to exit
+ * through it directlry from the client. */
+int
+node_allows_single_hop_exits(const node_t *node)
+{
+ (void)node;
+ UNIMPLEMENTED_NODELIST();
+ return 0;
+}
+
+/** Return true iff it seems that <b>node</b> has an exit policy that
+ * doesn't actually permit anything to exit. */
+int
+node_exit_policy_rejects_all(const node_t *node)
+{
+ (void)node;
+ UNIMPLEMENTED_NODELIST();
+ return 0;
+}
+
+/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
+int
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+ if (node->ri) {
+ tor_addr_from_ipv4h(addr_out, node->ri->addr);
+ return 0;
+ } else if (node->rs) {
+ tor_addr_from_ipv4h(addr_out, node->rs->addr);
+ return 0;
+ }
+ return -1;
+}
+
+/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
+ * seem to have one. */
+uint32_t
+node_get_addr_ipv4h(const node_t *node)
+{
+ if (node->ri) {
+ return node->ri->addr;
+ } else if (node->rs) {
+ return node->rs->addr;
+ }
+ return 0;
+}
+
+/** Copy a string representation of the IP address for <b>node</b> into the
+ * <b>len</b>-byte buffer at <b>buf</b>.
+ */
+void
+node_get_address_string(const node_t *node, char *buf, size_t len)
+{
+ if (node->ri) {
+ strlcpy(buf, node->ri->address, len);
+ } else if (node->rs) {
+ tor_addr_t addr;
+ tor_addr_from_ipv4h(&addr, node->rs->addr);
+ tor_addr_to_str(buf, &addr, len, 0);
+ } else {
+ buf[0] = '\0';
+ }
+}
+
+/** Return <b>node</b>'s declared uptime, or -1 if it doesn't seem to have
+ * one. */
+long
+node_get_declared_uptime(const node_t *node)
+{
+ (void)node;
+ UNIMPLEMENTED_NODELIST();
+ return 0;
+}
+
+/** Return <b>node</b>'s declared or_port */
+uint16_t
+node_get_orport(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->or_port;
+ else if (node->rs)
+ return node->rs->or_port;
+ else
+ return 0;
+}
+
+/** Return <b>node</b>'s platform string */
+const char *
+node_get_platform(const node_t *node)
+{
+ (void)node;
+ UNIMPLEMENTED_NODELIST();
+ return NULL;
+}
+
+/** Return <b>node</b>'s time of publication. */
+time_t
+node_get_published_on(const node_t *node)
+{
+ (void)node;
+ UNIMPLEMENTED_NODELIST();
+ return 0;
+}
+
+/** Return true iff <b>node</b> is one representing this router. */
+int
+node_is_me(const node_t *node)
+{
+ return router_digest_is_me(node->identity);
+}
+
+/* KILLTHIS XXXX NM -- it's a dummy to keep UNIMPLEMENTED_NODELIST()
+ * working */
+int unimplemented_nodelist_truth = 1;
+