123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * \file nodefamily.c
- * \brief Code to manipulate encoded, reference-counted node families. We
- * use these tricks to save space, since these families would otherwise
- * require a large number of tiny allocations.
- **/
- #include "core/or/or.h"
- #include "feature/nodelist/nickname.h"
- #include "feature/nodelist/nodefamily.h"
- #include "feature/nodelist/nodefamily_st.h"
- #include "feature/nodelist/nodelist.h"
- #include "feature/relay/router.h"
- #include "feature/nodelist/routerlist.h"
- #include "ht.h"
- #include "siphash.h"
- #include "lib/container/smartlist.h"
- #include "lib/ctime/di_ops.h"
- #include "lib/defs/digest_sizes.h"
- #include "lib/log/util_bug.h"
- #include <stdlib.h>
- #include <string.h>
- /**
- * Allocate and return a blank node family with space to hold <b>n_members</b>
- * members.
- */
- static nodefamily_t *
- nodefamily_alloc(int n_members)
- {
- size_t alloc_len = offsetof(nodefamily_t, family_members) +
- NODEFAMILY_ARRAY_SIZE(n_members);
- nodefamily_t *nf = tor_malloc_zero(alloc_len);
- nf->n_members = n_members;
- return nf;
- }
- /**
- * Hashtable hash implementation.
- */
- static inline unsigned int
- nodefamily_hash(const nodefamily_t *nf)
- {
- return (unsigned) siphash24g(nf->family_members,
- NODEFAMILY_ARRAY_SIZE(nf->n_members));
- }
- /**
- * Hashtable equality implementation.
- */
- static inline unsigned int
- nodefamily_eq(const nodefamily_t *a, const nodefamily_t *b)
- {
- return (a->n_members == b->n_members) &&
- fast_memeq(a->family_members, b->family_members,
- NODEFAMILY_ARRAY_SIZE(a->n_members));
- }
- static HT_HEAD(nodefamily_map, nodefamily_t) the_node_families
- = HT_INITIALIZER();
- HT_PROTOTYPE(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash,
- nodefamily_eq)
- HT_GENERATE2(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash,
- node_family_eq, 0.6, tor_reallocarray_, tor_free_)
- /**
- * Parse the family declaration in <b>s</b>, returning the canonical
- * <b>nodefamily_t</b> for its members. Return NULL on error.
- *
- * If <b>rsa_id_self</b> is provided, it is a DIGEST_LEN-byte digest
- * for the router that declared this family: insert it into the
- * family declaration if it is not there already.
- *
- * If NF_WARN_MALFORMED is set in <b>flags</b>, warn about any
- * elements that we can't parse. (By default, we log at info.)
- *
- * If NF_REJECT_MALFORMED is set in <b>flags</b>, treat any unparseable
- * elements as an error. (By default, we simply omit them.)
- **/
- nodefamily_t *
- nodefamily_parse(const char *s, const uint8_t *rsa_id_self,
- unsigned flags)
- {
- smartlist_t *sl = smartlist_new();
- smartlist_split_string(sl, s, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- nodefamily_t *result = nodefamily_from_members(sl, rsa_id_self, flags, NULL);
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
- smartlist_free(sl);
- return result;
- }
- /**
- * Canonicalize the family list <b>s</b>, returning a newly allocated string.
- *
- * The canonicalization rules are fully specified in dir-spec.txt, but,
- * briefly: $hexid entries are put in caps, $hexid[=~]foo entries are
- * truncated, nicknames are put into lowercase, unrecognized entries are left
- * alone, and everything is sorted.
- **/
- char *
- nodefamily_canonicalize(const char *s, const uint8_t *rsa_id_self,
- unsigned flags)
- {
- smartlist_t *sl = smartlist_new();
- smartlist_t *result_members = smartlist_new();
- smartlist_split_string(sl, s, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- nodefamily_t *nf = nodefamily_from_members(sl, rsa_id_self, flags,
- result_members);
- char *formatted = nodefamily_format(nf);
- smartlist_split_string(result_members, formatted, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- smartlist_sort_strings(result_members);
- char *combined = smartlist_join_strings(result_members, " ", 0, NULL);
- nodefamily_free(nf);
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
- smartlist_free(sl);
- SMARTLIST_FOREACH(result_members, char *, cp, tor_free(cp));
- smartlist_free(result_members);
- tor_free(formatted);
- return combined;
- }
- /**
- * qsort helper for encoded nodefamily elements.
- **/
- static int
- compare_members(const void *a, const void *b)
- {
- return fast_memcmp(a, b, NODEFAMILY_MEMBER_LEN);
- }
- /**
- * Parse the member strings in <b>members</b>, returning their canonical
- * <b>nodefamily_t</b>. Return NULL on error.
- *
- * If <b>rsa_id_self</b> is provided, it is a DIGEST_LEN-byte digest
- * for the router that declared this family: insert it into the
- * family declaration if it is not there already.
- *
- * The <b>flags</b> element is interpreted as in nodefamily_parse().
- *
- * If <b>unrecognized</b> is provided, fill it copies of any unrecognized
- * members. (Note that malformed $hexids are not considered unrecognized.)
- **/
- nodefamily_t *
- nodefamily_from_members(const smartlist_t *members,
- const uint8_t *rsa_id_self,
- unsigned flags,
- smartlist_t *unrecognized_out)
- {
- const int n_self = rsa_id_self ? 1 : 0;
- int n_bad_elements = 0;
- int n_members = smartlist_len(members) + n_self;
- nodefamily_t *tmp = nodefamily_alloc(n_members);
- uint8_t *ptr = NODEFAMILY_MEMBER_PTR(tmp, 0);
- SMARTLIST_FOREACH_BEGIN(members, const char *, cp) {
- bool bad_element = true;
- if (is_legal_nickname(cp)) {
- ptr[0] = NODEFAMILY_BY_NICKNAME;
- tor_assert(strlen(cp) < DIGEST_LEN); // guaranteed by is_legal_nickname
- memcpy(ptr+1, cp, strlen(cp));
- tor_strlower((char*) ptr+1);
- bad_element = false;
- } else if (is_legal_hexdigest(cp)) {
- char digest_buf[DIGEST_LEN];
- char nn_buf[MAX_NICKNAME_LEN+1];
- char nn_char=0;
- if (hex_digest_nickname_decode(cp, digest_buf, &nn_char, nn_buf)==0) {
- bad_element = false;
- ptr[0] = NODEFAMILY_BY_RSA_ID;
- memcpy(ptr+1, digest_buf, DIGEST_LEN);
- }
- } else {
- if (unrecognized_out)
- smartlist_add_strdup(unrecognized_out, cp);
- }
- if (bad_element) {
- const int severity = (flags & NF_WARN_MALFORMED) ? LOG_WARN : LOG_INFO;
- log_fn(severity, LD_GENERAL,
- "Bad element %s while parsing a node family.",
- escaped(cp));
- ++n_bad_elements;
- } else {
- ptr += NODEFAMILY_MEMBER_LEN;
- }
- } SMARTLIST_FOREACH_END(cp);
- if (n_bad_elements && (flags & NF_REJECT_MALFORMED))
- goto err;
- if (rsa_id_self) {
- /* Add self. */
- ptr[0] = NODEFAMILY_BY_RSA_ID;
- memcpy(ptr+1, rsa_id_self, DIGEST_LEN);
- }
- n_members -= n_bad_elements;
- /* Sort tmp into canonical order. */
- qsort(tmp->family_members, n_members, NODEFAMILY_MEMBER_LEN,
- compare_members);
- /* Remove duplicates. */
- int i;
- for (i = 0; i < n_members-1; ++i) {
- uint8_t *thisptr = NODEFAMILY_MEMBER_PTR(tmp, i);
- uint8_t *nextptr = NODEFAMILY_MEMBER_PTR(tmp, i+1);
- if (fast_memeq(thisptr, nextptr, NODEFAMILY_MEMBER_LEN)) {
- memmove(thisptr, nextptr, (n_members-i-1)*NODEFAMILY_MEMBER_LEN);
- --n_members;
- --i;
- }
- }
- int n_members_alloc = tmp->n_members;
- tmp->n_members = n_members;
- /* See if we already allocated this family. */
- nodefamily_t *found = HT_FIND(nodefamily_map, &the_node_families, tmp);
- if (found) {
- /* If we did, great: incref it and return it. */
- ++found->refcnt;
- tor_free(tmp);
- return found;
- } else {
- /* If not, insert it into the hashtable. */
- if (n_members_alloc != n_members) {
- /* Compact the family if needed */
- nodefamily_t *tmp2 = nodefamily_alloc(n_members);
- memcpy(tmp2->family_members, tmp->family_members,
- n_members * NODEFAMILY_MEMBER_LEN);
- tor_free(tmp);
- tmp = tmp2;
- }
- tmp->refcnt = 1;
- HT_INSERT(nodefamily_map, &the_node_families, tmp);
- return tmp;
- }
- err:
- tor_free(tmp);
- return NULL;
- }
- /**
- * Drop our reference to <b>family</b>, freeing it if there are no more
- * references.
- */
- void
- nodefamily_free_(nodefamily_t *family)
- {
- if (family == NULL)
- return;
- --family->refcnt;
- if (family->refcnt == 0) {
- HT_REMOVE(nodefamily_map, &the_node_families, family);
- tor_free(family);
- }
- }
- /**
- * Return true iff <b>family</b> contains the SHA1 RSA1024 identity
- * <b>rsa_id</b>.
- */
- bool
- nodefamily_contains_rsa_id(const nodefamily_t *family,
- const uint8_t *rsa_id)
- {
- if (family == NULL)
- return false;
- unsigned i;
- for (i = 0; i < family->n_members; ++i) {
- const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
- if (ptr[0] == NODEFAMILY_BY_RSA_ID &&
- fast_memeq(ptr+1, rsa_id, DIGEST_LEN)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Return true iff <b>family</b> contains the nickname <b>name</b>.
- */
- bool
- nodefamily_contains_nickname(const nodefamily_t *family,
- const char *name)
- {
- if (family == NULL)
- return false;
- unsigned i;
- for (i = 0; i < family->n_members; ++i) {
- const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
- // note that the strcasecmp() is safe because there is always at least one
- // NUL in the encoded nickname, because all legal nicknames are less than
- // DIGEST_LEN bytes long.
- if (ptr[0] == NODEFAMILY_BY_NICKNAME && !strcasecmp((char*)ptr+1, name)) {
- return true;
- }
- }
- return false;
- }
- /**
- * Return true if <b>family</b> contains the nickname or the RSA ID for
- * <b>node</b>
- **/
- bool
- nodefamily_contains_node(const nodefamily_t *family,
- const node_t *node)
- {
- return
- nodefamily_contains_nickname(family, node_get_nickname(node))
- ||
- nodefamily_contains_rsa_id(family, node_get_rsa_id_digest(node));
- }
- /**
- * Look up every entry in <b>family</b>, and add add the corresponding
- * node_t to <b>out</b>.
- **/
- void
- nodefamily_add_nodes_to_smartlist(const nodefamily_t *family,
- smartlist_t *out)
- {
- if (!family)
- return;
- unsigned i;
- for (i = 0; i < family->n_members; ++i) {
- const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
- const node_t *node = NULL;
- switch (ptr[0]) {
- case NODEFAMILY_BY_NICKNAME:
- node = node_get_by_nickname((char*)ptr+1, NNF_NO_WARN_UNNAMED);
- break;
- case NODEFAMILY_BY_RSA_ID:
- node = node_get_by_id((char*)ptr+1);
- break;
- default:
- /* LCOV_EXCL_START */
- tor_assert_nonfatal_unreached();
- break;
- /* LCOV_EXCL_STOP */
- }
- if (node)
- smartlist_add(out, (void *)node);
- }
- }
- /**
- * Encode <b>family</b> as a space-separated string.
- */
- char *
- nodefamily_format(const nodefamily_t *family)
- {
- if (!family)
- return tor_strdup("");
- unsigned i;
- smartlist_t *sl = smartlist_new();
- for (i = 0; i < family->n_members; ++i) {
- const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i);
- switch (ptr[0]) {
- case NODEFAMILY_BY_NICKNAME:
- smartlist_add_strdup(sl, (char*)ptr+1);
- break;
- case NODEFAMILY_BY_RSA_ID: {
- char buf[HEX_DIGEST_LEN+2];
- buf[0]='$';
- base16_encode(buf+1, sizeof(buf)-1, (char*)ptr+1, DIGEST_LEN);
- tor_strupper(buf);
- smartlist_add_strdup(sl, buf);
- break;
- }
- default:
- /* LCOV_EXCL_START */
- tor_assert_nonfatal_unreached();
- break;
- /* LCOV_EXCL_STOP */
- }
- }
- char *result = smartlist_join_strings(sl, " ", 0, NULL);
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
- smartlist_free(sl);
- return result;
- }
- /**
- * Free all storage held in the nodefamily map.
- **/
- void
- nodefamily_free_all(void)
- {
- HT_CLEAR(nodefamily_map, &the_node_families);
- }
|