|  | @@ -6,15 +6,19 @@
 | 
	
		
			
				|  |  |   * \brief Unit tests for nodelist related functions.
 | 
	
		
			
				|  |  |   **/
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#define NODELIST_PRIVATE
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #include "core/or/or.h"
 | 
	
		
			
				|  |  |  #include "lib/crypt_ops/crypto_rand.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/networkstatus.h"
 | 
	
		
			
				|  |  | +#include "feature/nodelist/nodefamily.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/nodelist.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/torcert.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "feature/nodelist/microdesc_st.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/networkstatus_st.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/node_st.h"
 | 
	
		
			
				|  |  | +#include "feature/nodelist/nodefamily_st.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/routerinfo_st.h"
 | 
	
		
			
				|  |  |  #include "feature/nodelist/routerstatus_st.h"
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -231,6 +235,381 @@ test_nodelist_ed_id(void *arg)
 | 
	
		
			
				|  |  |  #undef N_NODES
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +test_nodelist_nodefamily(void *arg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)arg;
 | 
	
		
			
				|  |  | +  /* hex ID digests */
 | 
	
		
			
				|  |  | +  const char h1[] = "5B435D6869206861206C65207363617270652070";
 | 
	
		
			
				|  |  | +  const char h2[] = "75C3B220616E6461726520696E206769726F2061";
 | 
	
		
			
				|  |  | +  const char h3[] = "2074726F766172206461206D616E67696172652C";
 | 
	
		
			
				|  |  | +  const char h4[] = "206D656E747265206E6F6E2076616C65206C2769";
 | 
	
		
			
				|  |  | +  const char h5[] = "6E766572736F2E202D2D5072696D6F204C657669";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* binary ID digests */
 | 
	
		
			
				|  |  | +  uint8_t d1[DIGEST_LEN], d2[DIGEST_LEN], d3[DIGEST_LEN], d4[DIGEST_LEN],
 | 
	
		
			
				|  |  | +    d5[DIGEST_LEN];
 | 
	
		
			
				|  |  | +  base16_decode((char*)d1, sizeof(d1), h1, strlen(h1));
 | 
	
		
			
				|  |  | +  base16_decode((char*)d2, sizeof(d2), h2, strlen(h2));
 | 
	
		
			
				|  |  | +  base16_decode((char*)d3, sizeof(d3), h3, strlen(h3));
 | 
	
		
			
				|  |  | +  base16_decode((char*)d4, sizeof(d4), h4, strlen(h4));
 | 
	
		
			
				|  |  | +  base16_decode((char*)d5, sizeof(d5), h5, strlen(h5));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  char *enc=NULL, *enc2=NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  nodefamily_t *nf1 = NULL;
 | 
	
		
			
				|  |  | +  nodefamily_t *nf2 = NULL;
 | 
	
		
			
				|  |  | +  nodefamily_t *nf3 = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  enc = nodefamily_format(NULL);
 | 
	
		
			
				|  |  | +  tt_str_op(enc, OP_EQ, "");
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Make sure that sorting and de-duplication work. */
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "$%s hello", h1);
 | 
	
		
			
				|  |  | +  nf1 = nodefamily_parse(enc, NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf1);
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "hello hello $%s hello", h1);
 | 
	
		
			
				|  |  | +  nf2 = nodefamily_parse(enc, NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf2);
 | 
	
		
			
				|  |  | +  tt_ptr_op(nf1, OP_EQ, nf2);
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "%s $%s hello", h1, h1);
 | 
	
		
			
				|  |  | +  nf3 = nodefamily_parse(enc, NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf3);
 | 
	
		
			
				|  |  | +  tt_ptr_op(nf1, OP_EQ, nf3);
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf1, d1));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(nf1, d2));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_nickname(nf1, "hello"));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_nickname(nf1, "HELLO"));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_nickname(nf1, "goodbye"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tt_int_op(nf1->refcnt, OP_EQ, 3);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf3);
 | 
	
		
			
				|  |  | +  tt_int_op(nf1->refcnt, OP_EQ, 2);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Try parsing with a provided self RSA digest. */
 | 
	
		
			
				|  |  | +  nf3 = nodefamily_parse("hello ", d1, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf3);
 | 
	
		
			
				|  |  | +  tt_ptr_op(nf1, OP_EQ, nf3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Do we get the expected result when we re-encode? */
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "hello $%s", h1);
 | 
	
		
			
				|  |  | +  enc2 = nodefamily_format(nf1);
 | 
	
		
			
				|  |  | +  tt_str_op(enc2, OP_EQ, enc);
 | 
	
		
			
				|  |  | +  tor_free(enc2);
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Make sure that we get a different result if we give a different digest. */
 | 
	
		
			
				|  |  | +  nodefamily_free(nf3);
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "hello $%s hello", h3);
 | 
	
		
			
				|  |  | +  nf3 = nodefamily_parse(enc, NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf3);
 | 
	
		
			
				|  |  | +  tt_ptr_op(nf1, OP_NE, nf3);
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf3, d3));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(nf3, d2));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(nf3, d1));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_nickname(nf3, "hello"));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_nickname(nf3, "goodbye"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  nodefamily_free(nf1);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf2);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Try one with several digests, all with nicknames appended, in different
 | 
	
		
			
				|  |  | +     formats. */
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "%s $%s $%s=res $%s~ist", h1, h2, h3, h4);
 | 
	
		
			
				|  |  | +  nf1 = nodefamily_parse(enc, d5, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf1);
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf1, d1));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf1, d2));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf1, d3));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf1, d4));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf1, d5));
 | 
	
		
			
				|  |  | +  /* Nicknames aren't preserved when ids are present, since node naming is
 | 
	
		
			
				|  |  | +   * deprecated */
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_nickname(nf3, "res"));
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +  tor_asprintf(&enc, "$%s $%s $%s $%s $%s", h4, h3, h1, h5, h2);
 | 
	
		
			
				|  |  | +  enc2 = nodefamily_format(nf1);
 | 
	
		
			
				|  |  | +  tt_str_op(enc, OP_EQ, enc2);
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +  tor_free(enc2);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Try ones where we parse the empty string. */
 | 
	
		
			
				|  |  | +  nf2 = nodefamily_parse("", NULL, 0);
 | 
	
		
			
				|  |  | +  nf3 = nodefamily_parse("", d4, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf2);
 | 
	
		
			
				|  |  | +  tt_assert(nf3);
 | 
	
		
			
				|  |  | +  tt_ptr_op(nf2, OP_NE, nf3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(nf2, d4));
 | 
	
		
			
				|  |  | +  tt_assert(nodefamily_contains_rsa_id(nf3, d4));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(nf2, d5));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(nf3, d5));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_nickname(nf2, "fred"));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_nickname(nf3, "bosco"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* The NULL family should contain nothing. */
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(NULL, d4));
 | 
	
		
			
				|  |  | +  tt_assert(! nodefamily_contains_rsa_id(NULL, d5));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + done:
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +  tor_free(enc2);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf1);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf2);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf3);
 | 
	
		
			
				|  |  | +  nodefamily_free_all();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +test_nodelist_nodefamily_parse_err(void *arg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)arg;
 | 
	
		
			
				|  |  | +  nodefamily_t *nf1 = NULL;
 | 
	
		
			
				|  |  | +  char *enc = NULL;
 | 
	
		
			
				|  |  | +  const char *semibogus =
 | 
	
		
			
				|  |  | +    "sdakljfdslkfjdsaklfjdkl9sdf " // too long for nickname
 | 
	
		
			
				|  |  | +    "$jkASDFLkjsadfjhkl " // not hex
 | 
	
		
			
				|  |  | +    "$7468696e67732d696e2d7468656d73656c766573 " // ok
 | 
	
		
			
				|  |  | +    "reticulatogranulate "// ok
 | 
	
		
			
				|  |  | +    "$73656d69616e7468726f706f6c6f676963616c6c79 " // too long for hex
 | 
	
		
			
				|  |  | +    "$616273656e746d696e6465646e6573736573" // too short for hex
 | 
	
		
			
				|  |  | +    ;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  setup_capture_of_logs(LOG_WARN);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // We only get two items when we parse this.
 | 
	
		
			
				|  |  | +  for (int reject = 0; reject <= 1; ++reject) {
 | 
	
		
			
				|  |  | +    for (int log_at_warn = 0; log_at_warn <= 1; ++log_at_warn) {
 | 
	
		
			
				|  |  | +      unsigned flags = log_at_warn ? NF_WARN_MALFORMED : 0;
 | 
	
		
			
				|  |  | +      flags |= reject ? NF_REJECT_MALFORMED : 0;
 | 
	
		
			
				|  |  | +      nf1 = nodefamily_parse(semibogus, NULL, flags);
 | 
	
		
			
				|  |  | +      if (reject) {
 | 
	
		
			
				|  |  | +        tt_assert(nf1 == NULL);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        tt_assert(nf1);
 | 
	
		
			
				|  |  | +        enc = nodefamily_format(nf1);
 | 
	
		
			
				|  |  | +        tt_str_op(enc, OP_EQ,
 | 
	
		
			
				|  |  | +                  "reticulatogranulate "
 | 
	
		
			
				|  |  | +                  "$7468696E67732D696E2D7468656D73656C766573");
 | 
	
		
			
				|  |  | +        tor_free(enc);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (log_at_warn) {
 | 
	
		
			
				|  |  | +        expect_log_msg_containing("$616273656e746d696e6465646e6573736573");
 | 
	
		
			
				|  |  | +        expect_log_msg_containing("sdakljfdslkfjdsaklfjdkl9sdf");
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        tt_int_op(mock_saved_log_n_entries(), OP_EQ, 0);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      mock_clean_saved_logs();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + done:
 | 
	
		
			
				|  |  | +  tor_free(enc);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf1);
 | 
	
		
			
				|  |  | +  teardown_capture_of_logs();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const node_t *
 | 
	
		
			
				|  |  | +mock_node_get_by_id(const char *id)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  if (fast_memeq(id, "!!!!!!!!!!!!!!!!!!!!", DIGEST_LEN))
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // use tor_free, not node_free.
 | 
	
		
			
				|  |  | +  node_t *fake_node = tor_malloc_zero(sizeof(node_t));
 | 
	
		
			
				|  |  | +  memcpy(fake_node->identity, id, DIGEST_LEN);
 | 
	
		
			
				|  |  | +  return fake_node;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const node_t *
 | 
	
		
			
				|  |  | +mock_node_get_by_nickname(const char *nn, unsigned flags)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)flags;
 | 
	
		
			
				|  |  | +  if (!strcmp(nn, "nonesuch"))
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // use tor_free, not node_free.
 | 
	
		
			
				|  |  | +  node_t *fake_node = tor_malloc_zero(sizeof(node_t));
 | 
	
		
			
				|  |  | +  strlcpy(fake_node->identity, nn, DIGEST_LEN);
 | 
	
		
			
				|  |  | +  return fake_node;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +test_nodelist_nodefamily_lookup(void *arg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)arg;
 | 
	
		
			
				|  |  | +  MOCK(node_get_by_nickname, mock_node_get_by_nickname);
 | 
	
		
			
				|  |  | +  MOCK(node_get_by_id, mock_node_get_by_id);
 | 
	
		
			
				|  |  | +  smartlist_t *sl = smartlist_new();
 | 
	
		
			
				|  |  | +  nodefamily_t *nf1 = NULL;
 | 
	
		
			
				|  |  | +  char *mem_op_hex_tmp = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 'null' is allowed.
 | 
	
		
			
				|  |  | +  nodefamily_add_nodes_to_smartlist(NULL, sl);
 | 
	
		
			
				|  |  | +  tt_int_op(smartlist_len(sl), OP_EQ, 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Try a real family
 | 
	
		
			
				|  |  | +  nf1 = nodefamily_parse("$EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE "
 | 
	
		
			
				|  |  | +                         "$2121212121212121212121212121212121212121 "
 | 
	
		
			
				|  |  | +                         "$3333333333333333333333333333333333333333 "
 | 
	
		
			
				|  |  | +                         "erewhon nonesuch", NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(nf1);
 | 
	
		
			
				|  |  | +  nodefamily_add_nodes_to_smartlist(nf1, sl);
 | 
	
		
			
				|  |  | +  // There were 5 elements; 2 were dropped because the mocked lookup failed.
 | 
	
		
			
				|  |  | +  tt_int_op(smartlist_len(sl), OP_EQ, 3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const node_t *n = smartlist_get(sl, 0);
 | 
	
		
			
				|  |  | +  tt_str_op(n->identity, OP_EQ, "erewhon");
 | 
	
		
			
				|  |  | +  n = smartlist_get(sl, 1);
 | 
	
		
			
				|  |  | +  test_memeq_hex(n->identity, "3333333333333333333333333333333333333333");
 | 
	
		
			
				|  |  | +  n = smartlist_get(sl, 2);
 | 
	
		
			
				|  |  | +  test_memeq_hex(n->identity, "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + done:
 | 
	
		
			
				|  |  | +  UNMOCK(node_get_by_nickname);
 | 
	
		
			
				|  |  | +  UNMOCK(node_get_by_id);
 | 
	
		
			
				|  |  | +  SMARTLIST_FOREACH(sl, node_t *, fake_node, tor_free(fake_node));
 | 
	
		
			
				|  |  | +  smartlist_free(sl);
 | 
	
		
			
				|  |  | +  nodefamily_free(nf1);
 | 
	
		
			
				|  |  | +  tor_free(mem_op_hex_tmp);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +test_nodelist_nickname_matches(void *arg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)arg;
 | 
	
		
			
				|  |  | +  node_t mock_node;
 | 
	
		
			
				|  |  | +  routerstatus_t mock_rs;
 | 
	
		
			
				|  |  | +  memset(&mock_node, 0, sizeof(mock_node));
 | 
	
		
			
				|  |  | +  memset(&mock_rs, 0, sizeof(mock_rs));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  strlcpy(mock_rs.nickname, "evilgeniuses", sizeof(mock_rs.nickname));
 | 
	
		
			
				|  |  | +  mock_node.rs = &mock_rs;
 | 
	
		
			
				|  |  | +  memcpy(mock_node.identity, ".forabettertomorrow.", DIGEST_LEN);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define match(x) tt_assert(node_nickname_matches(&mock_node, (x)))
 | 
	
		
			
				|  |  | +#define no_match(x) tt_assert(! node_nickname_matches(&mock_node, (x)))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  match("evilgeniuses");
 | 
	
		
			
				|  |  | +  match("EvilGeniuses");
 | 
	
		
			
				|  |  | +  match("EvilGeniuses");
 | 
	
		
			
				|  |  | +  match("2e666f7261626574746572746f6d6f72726f772e");
 | 
	
		
			
				|  |  | +  match("2E666F7261626574746572746F6D6F72726F772E");
 | 
	
		
			
				|  |  | +  match("$2e666f7261626574746572746f6d6f72726f772e");
 | 
	
		
			
				|  |  | +  match("$2E666F7261626574746572746F6D6F72726F772E");
 | 
	
		
			
				|  |  | +  match("$2E666F7261626574746572746F6D6F72726F772E~evilgeniuses");
 | 
	
		
			
				|  |  | +  match("$2E666F7261626574746572746F6D6F72726F772E~EVILGENIUSES");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  no_match("evilgenius");
 | 
	
		
			
				|  |  | +  no_match("evilgeniuseses");
 | 
	
		
			
				|  |  | +  no_match("evil.genius");
 | 
	
		
			
				|  |  | +  no_match("$2E666F7261626574746572746F6D6F72726FFFFF");
 | 
	
		
			
				|  |  | +  no_match("2E666F7261626574746572746F6D6F72726FFFFF");
 | 
	
		
			
				|  |  | +  no_match("$2E666F7261626574746572746F6D6F72726F772E~fred");
 | 
	
		
			
				|  |  | +  no_match("$2E666F7261626574746572746F6D6F72726F772E=EVILGENIUSES");
 | 
	
		
			
				|  |  | + done:
 | 
	
		
			
				|  |  | +  ;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +test_nodelist_node_nodefamily(void *arg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)arg;
 | 
	
		
			
				|  |  | +  node_t mock_node1;
 | 
	
		
			
				|  |  | +  routerstatus_t mock_rs;
 | 
	
		
			
				|  |  | +  microdesc_t mock_md;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  node_t mock_node2;
 | 
	
		
			
				|  |  | +  routerinfo_t mock_ri;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  smartlist_t *nodes=smartlist_new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  memset(&mock_node1, 0, sizeof(mock_node1));
 | 
	
		
			
				|  |  | +  memset(&mock_node2, 0, sizeof(mock_node2));
 | 
	
		
			
				|  |  | +  memset(&mock_rs, 0, sizeof(mock_rs));
 | 
	
		
			
				|  |  | +  memset(&mock_md, 0, sizeof(mock_md));
 | 
	
		
			
				|  |  | +  memset(&mock_ri, 0, sizeof(mock_ri));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  mock_node1.rs = &mock_rs;
 | 
	
		
			
				|  |  | +  mock_node1.md = &mock_md;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  mock_node2.ri = &mock_ri;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  strlcpy(mock_rs.nickname, "nodeone", sizeof(mock_rs.nickname));
 | 
	
		
			
				|  |  | +  mock_ri.nickname = tor_strdup("nodetwo");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  memcpy(mock_node1.identity, "NodeOneNode1NodeOne1", DIGEST_LEN);
 | 
	
		
			
				|  |  | +  memcpy(mock_node2.identity, "SecondNodeWe'reTestn", DIGEST_LEN);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // empty families.
 | 
	
		
			
				|  |  | +  tt_assert(! node_family_contains(&mock_node1, &mock_node2));
 | 
	
		
			
				|  |  | +  tt_assert(! node_family_contains(&mock_node2, &mock_node1));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Families contain nodes, but not these nodes
 | 
	
		
			
				|  |  | +  mock_ri.declared_family = smartlist_new();
 | 
	
		
			
				|  |  | +  smartlist_add(mock_ri.declared_family, (char*)"NodeThree");
 | 
	
		
			
				|  |  | +  mock_md.family = nodefamily_parse("NodeFour", NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(! node_family_contains(&mock_node1, &mock_node2));
 | 
	
		
			
				|  |  | +  tt_assert(! node_family_contains(&mock_node2, &mock_node1));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Families contain one another.
 | 
	
		
			
				|  |  | +  smartlist_add(mock_ri.declared_family, (char*)
 | 
	
		
			
				|  |  | +                "4e6f64654f6e654e6f6465314e6f64654f6e6531");
 | 
	
		
			
				|  |  | +  tt_assert(! node_family_contains(&mock_node1, &mock_node2));
 | 
	
		
			
				|  |  | +  tt_assert(node_family_contains(&mock_node2, &mock_node1));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  nodefamily_free(mock_md.family);
 | 
	
		
			
				|  |  | +  mock_md.family = nodefamily_parse(
 | 
	
		
			
				|  |  | +            "NodeFour "
 | 
	
		
			
				|  |  | +            "5365636f6e644e6f64655765277265546573746e", NULL, 0);
 | 
	
		
			
				|  |  | +  tt_assert(node_family_contains(&mock_node1, &mock_node2));
 | 
	
		
			
				|  |  | +  tt_assert(node_family_contains(&mock_node2, &mock_node1));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Try looking up families now.
 | 
	
		
			
				|  |  | +  MOCK(node_get_by_nickname, mock_node_get_by_nickname);
 | 
	
		
			
				|  |  | +  MOCK(node_get_by_id, mock_node_get_by_id);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  node_lookup_declared_family(nodes, &mock_node1);
 | 
	
		
			
				|  |  | +  tt_int_op(smartlist_len(nodes), OP_EQ, 2);
 | 
	
		
			
				|  |  | +  const node_t *n = smartlist_get(nodes, 0);
 | 
	
		
			
				|  |  | +  tt_str_op(n->identity, OP_EQ, "NodeFour");
 | 
	
		
			
				|  |  | +  n = smartlist_get(nodes, 1);
 | 
	
		
			
				|  |  | +  tt_mem_op(n->identity, OP_EQ, "SecondNodeWe'reTestn", DIGEST_LEN);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // free, try the other one.
 | 
	
		
			
				|  |  | +  SMARTLIST_FOREACH(nodes, node_t *, x, tor_free(x));
 | 
	
		
			
				|  |  | +  smartlist_clear(nodes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  node_lookup_declared_family(nodes, &mock_node2);
 | 
	
		
			
				|  |  | +  tt_int_op(smartlist_len(nodes), OP_EQ, 2);
 | 
	
		
			
				|  |  | +  n = smartlist_get(nodes, 0);
 | 
	
		
			
				|  |  | +  tt_str_op(n->identity, OP_EQ, "NodeThree");
 | 
	
		
			
				|  |  | +  n = smartlist_get(nodes, 1);
 | 
	
		
			
				|  |  | +  // This gets a truncated hex hex ID since it was looked up by name
 | 
	
		
			
				|  |  | +  tt_str_op(n->identity, OP_EQ, "4e6f64654f6e654e6f6");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + done:
 | 
	
		
			
				|  |  | +  UNMOCK(node_get_by_nickname);
 | 
	
		
			
				|  |  | +  UNMOCK(node_get_by_id);
 | 
	
		
			
				|  |  | +  smartlist_free(mock_ri.declared_family);
 | 
	
		
			
				|  |  | +  nodefamily_free(mock_md.family);
 | 
	
		
			
				|  |  | +  tor_free(mock_ri.nickname);
 | 
	
		
			
				|  |  | +  // use tor_free, these aren't real nodes
 | 
	
		
			
				|  |  | +  SMARTLIST_FOREACH(nodes, node_t *, x, tor_free(x));
 | 
	
		
			
				|  |  | +  smartlist_free(nodes);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #define NODE(name, flags) \
 | 
	
		
			
				|  |  |    { #name, test_nodelist_##name, (flags), NULL, NULL }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -239,6 +618,10 @@ struct testcase_t nodelist_tests[] = {
 | 
	
		
			
				|  |  |    NODE(node_get_verbose_nickname_not_named, TT_FORK),
 | 
	
		
			
				|  |  |    NODE(node_is_dir, TT_FORK),
 | 
	
		
			
				|  |  |    NODE(ed_id, TT_FORK),
 | 
	
		
			
				|  |  | +  NODE(nodefamily, TT_FORK),
 | 
	
		
			
				|  |  | +  NODE(nodefamily_parse_err, TT_FORK),
 | 
	
		
			
				|  |  | +  NODE(nodefamily_lookup, TT_FORK),
 | 
	
		
			
				|  |  | +  NODE(nickname_matches, 0),
 | 
	
		
			
				|  |  | +  NODE(node_nodefamily, TT_FORK),
 | 
	
		
			
				|  |  |    END_OF_TESTCASES
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  | -
 |