Commit d29e3a02 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Add a function to canonicalize nodefamilies per prop298

This is the same as the regular canonical nodefamily format, except
that unrecognized elements are preserved.
parent 0e9a963b
Loading
Loading
Loading
Loading
+43 −2
Original line number Diff line number Diff line
@@ -93,12 +93,46 @@ nodefamily_parse(const char *s, const uint8_t *rsa_id_self,
{
  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);
  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.
 **/
@@ -117,11 +151,15 @@ compare_members(const void *a, const void *b)
 * 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)
                        unsigned flags,
                        smartlist_t *unrecognized_out)
{
  const int n_self = rsa_id_self ? 1 : 0;
  int n_bad_elements = 0;
@@ -146,6 +184,9 @@ nodefamily_from_members(const smartlist_t *members,
        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) {
+4 −1
Original line number Diff line number Diff line
@@ -27,7 +27,8 @@ nodefamily_t *nodefamily_parse(const char *s,
                               unsigned flags);
nodefamily_t *nodefamily_from_members(const struct smartlist_t *members,
                                      const uint8_t *rsa_id_self,
                                      unsigned flags);
                                      unsigned flags,
                                      smartlist_t *unrecognized_out);
void nodefamily_free_(nodefamily_t *family);
#define nodefamily_free(family) \
  FREE_AND_NULL(nodefamily_t, nodefamily_free_, (family))
@@ -41,6 +42,8 @@ bool nodefamily_contains_node(const nodefamily_t *family,
void nodefamily_add_nodes_to_smartlist(const nodefamily_t *family,
                                       struct smartlist_t *out);
char *nodefamily_format(const nodefamily_t *family);
char *nodefamily_canonicalize(const char *s, const uint8_t *rsa_id_self,
                              unsigned flags);

void nodefamily_free_all(void);

+31 −0
Original line number Diff line number Diff line
@@ -610,6 +610,36 @@ test_nodelist_node_nodefamily(void *arg)
  smartlist_free(nodes);
}

static void
test_nodelist_nodefamily_canonicalize(void *arg)
{
  (void)arg;
  char *c = NULL;

  c = nodefamily_canonicalize("", NULL, 0);
  tt_str_op(c, OP_EQ, "");
  tor_free(c);

  uint8_t own_id[20];
  memset(own_id, 0, sizeof(own_id));
  c = nodefamily_canonicalize(
           "alice BOB caroL %potrzebie !!!@#@# "
           "$bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=fred "
           "ffffffffffffffffffffffffffffffffffffffff "
           "$cccccccccccccccccccccccccccccccccccccccc ", own_id, 0);
  tt_str_op(c, OP_EQ,
           "!!!@#@# "
           "$0000000000000000000000000000000000000000 "
           "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB "
           "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC "
           "$FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF "
           "%potrzebie "
           "alice bob carol");

 done:
  tor_free(c);
}

#define NODE(name, flags) \
  { #name, test_nodelist_##name, (flags), NULL, NULL }

@@ -623,5 +653,6 @@ struct testcase_t nodelist_tests[] = {
  NODE(nodefamily_lookup, TT_FORK),
  NODE(nickname_matches, 0),
  NODE(node_nodefamily, TT_FORK),
  NODE(nodefamily_canonicalize, 0),
  END_OF_TESTCASES
};