#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>
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;
}
static inline unsigned int
nodefamily_hash(const nodefamily_t *nf)
{
return (unsigned) siphash24g(nf->family_members,
NODEFAMILY_ARRAY_SIZE(nf->n_members));
}
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_);
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;
}
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;
}
static int
compare_members(const void *a, const void *b)
{
return fast_memcmp(a, b, NODEFAMILY_MEMBER_LEN);
}
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); 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) {
ptr[0] = NODEFAMILY_BY_RSA_ID;
memcpy(ptr+1, rsa_id_self, DIGEST_LEN);
}
n_members -= n_bad_elements;
qsort(tmp->family_members, n_members, NODEFAMILY_MEMBER_LEN,
compare_members);
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;
nodefamily_t *found = HT_FIND(nodefamily_map, &the_node_families, tmp);
if (found) {
++found->refcnt;
tor_free(tmp);
return found;
} else {
if (n_members_alloc != n_members) {
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;
}
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);
}
}
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;
}
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);
if (ptr[0] == NODEFAMILY_BY_NICKNAME && !strcasecmp((char*)ptr+1, name)) {
return true;
}
}
return false;
}
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));
}
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:
tor_assert_nonfatal_unreached();
break;
}
if (node)
smartlist_add(out, (void *)node);
}
}
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:
tor_assert_nonfatal_unreached();
break;
}
}
char *result = smartlist_join_strings(sl, " ", 0, NULL);
SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
smartlist_free(sl);
return result;
}
void
nodefamily_free_all(void)
{
HT_CLEAR(nodefamily_map, &the_node_families);
}