#include "config.h"
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "namedb.h"
#include "nsec3.h"
static domain_type *
allocate_domain_info(domain_table_type* table,
const dname_type* dname,
domain_type* parent)
{
domain_type *result;
assert(table);
assert(dname);
assert(parent);
result = (domain_type *) region_alloc(table->region,
sizeof(domain_type));
#ifdef USE_RADIX_TREE
result->dname
#else
result->node.key
#endif
= dname_partial_copy(
table->region, dname, domain_dname(parent)->label_count + 1);
result->parent = parent;
result->wildcard_child_closest_match = result;
result->rrsets = NULL;
result->usage = 0;
#ifdef NSEC3
result->nsec3 = NULL;
#endif
result->is_existing = 0;
result->is_apex = 0;
assert(table->numlist_last);
result->number = table->numlist_last->number+1;
result->numlist_next = NULL;
result->numlist_prev = table->numlist_last;
table->numlist_last->numlist_next = result;
table->numlist_last = result;
return result;
}
#ifdef NSEC3
void
allocate_domain_nsec3(domain_table_type* table, domain_type* result)
{
if(result->nsec3)
return;
result->nsec3 = (struct nsec3_domain_data*) region_alloc(table->region,
sizeof(struct nsec3_domain_data));
result->nsec3->nsec3_cover = NULL;
result->nsec3->nsec3_wcard_child_cover = NULL;
result->nsec3->nsec3_ds_parent_cover = NULL;
result->nsec3->nsec3_is_exact = 0;
result->nsec3->nsec3_ds_parent_is_exact = 0;
result->nsec3->hash_wc = NULL;
result->nsec3->ds_parent_hash = NULL;
result->nsec3->prehash_prev = NULL;
result->nsec3->prehash_next = NULL;
result->nsec3->nsec3_node.key = NULL;
}
#endif
void
numlist_make_last(domain_table_type* table, domain_type* domain)
{
uint32_t sw;
domain_type* last = table->numlist_last;
if(domain == last)
return;
sw = domain->number;
domain->number = last->number;
last->number = sw;
assert(domain->numlist_next);
assert(last->numlist_prev);
if(domain->numlist_next != last) {
domain_type* span_start = domain->numlist_next;
domain_type* span_end = last->numlist_prev;
if(domain->numlist_prev)
domain->numlist_prev->numlist_next = last;
last->numlist_prev = domain->numlist_prev;
last->numlist_next = span_start;
span_start->numlist_prev = last;
span_end->numlist_next = domain;
domain->numlist_prev = span_end;
domain->numlist_next = NULL;
} else {
if(domain->numlist_prev)
domain->numlist_prev->numlist_next = last;
last->numlist_prev = domain->numlist_prev;
last->numlist_next = domain;
domain->numlist_prev = last;
domain->numlist_next = NULL;
}
table->numlist_last = domain;
}
domain_type*
numlist_pop_last(domain_table_type* table)
{
domain_type* d = table->numlist_last;
table->numlist_last = table->numlist_last->numlist_prev;
if(table->numlist_last)
table->numlist_last->numlist_next = NULL;
return d;
}
static int
domain_can_be_deleted(domain_type* domain)
{
domain_type* n;
if(domain->rrsets) return 0;
if(domain->usage) return 0;
n = domain_next(domain);
if(n && domain_is_subdomain(n, domain))
return 0;
return 1;
}
#ifdef NSEC3
int domain_is_prehash(domain_table_type* table, domain_type* domain)
{
if(domain->nsec3
&& (domain->nsec3->prehash_prev || domain->nsec3->prehash_next))
return 1;
return (table->prehash_list == domain);
}
void
zone_del_domain_in_hash_tree(rbtree_type* tree, rbnode_type* node)
{
if(!node->key || !tree)
return;
rbtree_delete(tree, node->key);
node->key = NULL;
}
void prehash_clear(domain_table_type* table)
{
domain_type* d = table->prehash_list, *n;
while(d) {
n = d->nsec3->prehash_next;
d->nsec3->prehash_prev = NULL;
d->nsec3->prehash_next = NULL;
d = n;
}
table->prehash_list = NULL;
}
void
prehash_add(domain_table_type* table, domain_type* domain)
{
if(domain_is_prehash(table, domain))
return;
allocate_domain_nsec3(table, domain);
domain->nsec3->prehash_next = table->prehash_list;
if(table->prehash_list)
table->prehash_list->nsec3->prehash_prev = domain;
table->prehash_list = domain;
}
void
prehash_del(domain_table_type* table, domain_type* domain)
{
if(domain->nsec3->prehash_next)
domain->nsec3->prehash_next->nsec3->prehash_prev =
domain->nsec3->prehash_prev;
if(domain->nsec3->prehash_prev)
domain->nsec3->prehash_prev->nsec3->prehash_next =
domain->nsec3->prehash_next;
else table->prehash_list = domain->nsec3->prehash_next;
domain->nsec3->prehash_next = NULL;
domain->nsec3->prehash_prev = NULL;
}
#endif
static void
do_deldomain(namedb_type* db, domain_type* domain)
{
assert(domain && domain->parent);
numlist_make_last(db->domains, domain);
(void)numlist_pop_last(db->domains);
#ifdef NSEC3
if(domain_is_prehash(db->domains, domain))
prehash_del(db->domains, domain);
if(domain->nsec3) {
if(domain->nsec3->nsec3_node.key)
zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain)
->nsec3tree, &domain->nsec3->nsec3_node);
if(domain->nsec3->hash_wc) {
if(domain->nsec3->hash_wc->hash.node.key)
zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain)
->hashtree, &domain->nsec3->hash_wc->hash.node);
if(domain->nsec3->hash_wc->wc.node.key)
zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain)
->wchashtree, &domain->nsec3->hash_wc->wc.node);
}
if(domain->nsec3->ds_parent_hash && domain->nsec3->ds_parent_hash->node.key)
zone_del_domain_in_hash_tree(nsec3_tree_dszone(db, domain)
->dshashtree, &domain->nsec3->ds_parent_hash->node);
if(domain->nsec3->hash_wc) {
region_recycle(db->domains->region,
domain->nsec3->hash_wc,
sizeof(nsec3_hash_wc_node_type));
}
if(domain->nsec3->ds_parent_hash) {
region_recycle(db->domains->region,
domain->nsec3->ds_parent_hash,
sizeof(nsec3_hash_node_type));
}
region_recycle(db->domains->region, domain->nsec3,
sizeof(struct nsec3_domain_data));
}
#endif
if(domain->parent->wildcard_child_closest_match == domain)
domain->parent->wildcard_child_closest_match =
domain_previous_existing_child(domain);
#ifdef USE_RADIX_TREE
radix_delete(db->domains->nametree, domain->rnode);
#else
rbtree_delete(db->domains->names_to_domains, domain->node.key);
#endif
region_recycle(db->domains->region, domain_dname(domain),
dname_total_size(domain_dname(domain)));
region_recycle(db->domains->region, domain, sizeof(domain_type));
}
void
domain_table_deldomain(namedb_type* db, domain_type* domain)
{
domain_type* parent;
while(domain_can_be_deleted(domain)) {
parent = domain->parent;
do_deldomain(db, domain);
domain = parent;
}
}
void hash_tree_delete(region_type* region, rbtree_type* tree)
{
region_recycle(region, tree, sizeof(rbtree_type));
}
void zone_add_domain_in_hash_tree(region_type* region, rbtree_type** tree,
int (*cmpf)(const void*, const void*),
domain_type* domain, rbnode_type* node)
{
if(!*tree)
*tree = rbtree_create(region, cmpf);
if(node->key && node->key == domain
&& rbtree_search(*tree, domain) == node)
return;
memset(node, 0, sizeof(rbnode_type));
node->key = domain;
rbtree_insert(*tree, node);
}
domain_table_type *
domain_table_create(region_type* region)
{
const dname_type* origin;
domain_table_type* result;
domain_type* root;
assert(region);
origin = dname_make(region, (uint8_t *) "", 0);
root = (domain_type *) region_alloc(region, sizeof(domain_type));
#ifdef USE_RADIX_TREE
root->dname
#else
root->node.key
#endif
= origin;
root->parent = NULL;
root->wildcard_child_closest_match = root;
root->rrsets = NULL;
root->number = 1;
root->usage = 1;
root->is_existing = 0;
root->is_apex = 0;
root->numlist_prev = NULL;
root->numlist_next = NULL;
#ifdef NSEC3
root->nsec3 = NULL;
#endif
result = (domain_table_type *) region_alloc(region,
sizeof(domain_table_type));
result->region = region;
#ifdef USE_RADIX_TREE
result->nametree = radix_tree_create(region);
root->rnode = radname_insert(result->nametree, dname_name(root->dname),
root->dname->name_size, root);
#else
result->names_to_domains = rbtree_create(
region, (int (*)(const void *, const void *)) dname_compare);
rbtree_insert(result->names_to_domains, (rbnode_type *) root);
#endif
result->root = root;
result->numlist_last = root;
#ifdef NSEC3
result->prehash_list = NULL;
#endif
return result;
}
int
domain_table_search(domain_table_type *table,
const dname_type *dname,
domain_type **closest_match,
domain_type **closest_encloser)
{
int exact;
uint8_t label_match_count;
assert(table);
assert(dname);
assert(closest_match);
assert(closest_encloser);
#ifdef USE_RADIX_TREE
exact = radname_find_less_equal(table->nametree, dname_name(dname),
dname->name_size, (struct radnode**)closest_match);
*closest_match = (domain_type*)((*(struct radnode**)closest_match)->elem);
#else
exact = rbtree_find_less_equal(table->names_to_domains, dname, (rbnode_type **) closest_match);
#endif
assert(*closest_match);
*closest_encloser = *closest_match;
if (!exact) {
label_match_count = dname_label_match_count(
domain_dname(*closest_encloser),
dname);
assert(label_match_count < dname->label_count);
while (label_match_count < domain_dname(*closest_encloser)->label_count) {
(*closest_encloser) = (*closest_encloser)->parent;
assert(*closest_encloser);
}
}
return exact;
}
domain_type *
domain_table_find(domain_table_type* table,
const dname_type* dname)
{
domain_type* closest_match;
domain_type* closest_encloser;
int exact;
exact = domain_table_search(
table, dname, &closest_match, &closest_encloser);
return exact ? closest_encloser : NULL;
}
domain_type *
domain_table_insert(domain_table_type* table,
const dname_type* dname)
{
domain_type* closest_match;
domain_type* closest_encloser;
domain_type* result;
int exact;
assert(table);
assert(dname);
exact = domain_table_search(
table, dname, &closest_match, &closest_encloser);
if (exact) {
result = closest_encloser;
} else {
assert(domain_dname(closest_encloser)->label_count < dname->label_count);
do {
result = allocate_domain_info(table,
dname,
closest_encloser);
#ifdef USE_RADIX_TREE
result->rnode = radname_insert(table->nametree,
dname_name(result->dname),
result->dname->name_size, result);
#else
rbtree_insert(table->names_to_domains, (rbnode_type *) result);
#endif
if (label_compare(dname_name(domain_dname(result)),
(const uint8_t *) "\001*") <= 0
&& dname_compare(domain_dname(result),
domain_dname(closest_encloser->wildcard_child_closest_match)) > 0)
{
closest_encloser->wildcard_child_closest_match
= result;
}
closest_encloser = result;
} while (domain_dname(closest_encloser)->label_count < dname->label_count);
}
return result;
}
domain_type *domain_previous_existing_child(domain_type* domain)
{
domain_type* parent = domain->parent;
domain = domain_previous(domain);
while(domain && !domain->is_existing) {
if(domain == parent)
return parent;
domain = domain_previous(domain);
}
return domain;
}
void
domain_add_rrset(domain_type* domain, rrset_type* rrset)
{
#if 0
rrset->next = domain->rrsets;
domain->rrsets = rrset;
#else
rrset_type** p = &domain->rrsets;
while(*p)
p = &((*p)->next);
*p = rrset;
rrset->next = 0;
#endif
while (domain && !domain->is_existing) {
domain->is_existing = 1;
if(domain->parent
&& label_compare(dname_name(domain_dname(domain)),
(const uint8_t *) "\001*") <= 0
&& dname_compare(domain_dname(domain),
domain_dname(domain->parent->wildcard_child_closest_match)) > 0) {
domain->parent->wildcard_child_closest_match = domain;
}
domain = domain->parent;
}
}
rrset_type *
domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t type)
{
rrset_type* result = domain->rrsets;
while (result) {
if (result->zone == zone && rrset_rrtype(result) == type) {
return result;
}
result = result->next;
}
return NULL;
}
rrset_type *
domain_find_any_rrset(domain_type* domain, zone_type* zone)
{
rrset_type* result = domain->rrsets;
while (result) {
if (result->zone == zone) {
return result;
}
result = result->next;
}
return NULL;
}
rrset_type *
domain_find_rrset_and_prev(domain_type* domain, zone_type* zone, uint16_t type,
rrset_type** prev)
{
rrset_type* result = domain->rrsets, *prevp = NULL;
while (result) {
if (result->zone == zone && rrset_rrtype(result) == type) {
*prev = prevp;
return result;
}
prevp = result;
result = result->next;
}
*prev = NULL;
return NULL;
}
zone_type *
domain_find_zone(namedb_type* db, domain_type* domain)
{
rrset_type* rrset;
while (domain) {
if(domain->is_apex) {
for (rrset = domain->rrsets; rrset; rrset = rrset->next) {
if (rrset_rrtype(rrset) == TYPE_SOA) {
return rrset->zone;
}
}
return namedb_find_zone(db, domain_dname(domain));
}
domain = domain->parent;
}
return NULL;
}
zone_type *
domain_find_parent_zone(namedb_type* db, zone_type* zone)
{
rrset_type* rrset;
assert(zone);
for (rrset = zone->apex->rrsets; rrset; rrset = rrset->next) {
if (rrset->zone != zone && rrset_rrtype(rrset) == TYPE_NS) {
return rrset->zone;
}
}
if(zone->apex->parent)
return domain_find_zone(db, zone->apex->parent);
return NULL;
}
domain_type *
domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns)
{
domain_type* result = NULL;
rrset_type* rrset = NULL;
while (domain && domain != zone->apex) {
rrset = domain_find_rrset(domain, zone, TYPE_NS);
if (rrset) {
*ns = rrset;
result = domain;
}
domain = domain->parent;
}
if(result)
return result;
*ns = NULL;
return NULL;
}
domain_type *
find_dname_above(domain_type* domain, zone_type* zone)
{
domain_type* d = domain->parent;
while(d && d != zone->apex) {
if(domain_find_rrset(d, zone, TYPE_DNAME))
return d;
d = d->parent;
}
return NULL;
}
int
domain_is_glue(domain_type* domain, zone_type* zone)
{
rrset_type* unused;
domain_type* ns_domain = domain_find_ns_rrsets(domain, zone, &unused);
return (ns_domain != NULL &&
domain_find_rrset(ns_domain, zone, TYPE_SOA) == NULL);
}
domain_type *
domain_wildcard_child(domain_type* domain)
{
domain_type* wildcard_child;
assert(domain);
assert(domain->wildcard_child_closest_match);
wildcard_child = domain->wildcard_child_closest_match;
if (wildcard_child != domain
&& label_is_wildcard(dname_name(domain_dname(wildcard_child))))
{
return wildcard_child;
} else {
return NULL;
}
}
int
zone_is_secure(zone_type* zone)
{
assert(zone);
return zone->is_secure;
}
uint16_t
rr_rrsig_type_covered(rr_type* rr)
{
uint16_t type;
assert(rr->type == TYPE_RRSIG);
assert(rr->rdlength > 2);
memcpy(&type, rr->rdata, sizeof(type));
return ntohs(type);
}
zone_type *
namedb_find_zone(namedb_type* db, const dname_type* dname)
{
struct radnode* n = radname_search(db->zonetree, dname_name(dname),
dname->name_size);
if(n) return (zone_type*)n->elem;
return NULL;
}
rrset_type *
domain_find_non_cname_rrset(domain_type* domain, zone_type* zone)
{
rrset_type *result = domain->rrsets;
while (result) {
if (result->zone == zone &&
rrset_rrtype(result) != TYPE_CNAME &&
rrset_rrtype(result) != TYPE_RRSIG &&
rrset_rrtype(result) != TYPE_NXT &&
rrset_rrtype(result) != TYPE_SIG &&
rrset_rrtype(result) != TYPE_NSEC &&
rrset_rrtype(result) != TYPE_NSEC3 ) {
return result;
}
result = result->next;
}
return NULL;
}
int
namedb_lookup(struct namedb* db,
const dname_type* dname,
domain_type **closest_match,
domain_type **closest_encloser)
{
return domain_table_search(
db->domains, dname, closest_match, closest_encloser);
}
void zone_rr_iter_init(struct zone_rr_iter *iter, struct zone *zone)
{
assert(iter != NULL);
assert(zone != NULL);
memset(iter, 0, sizeof(*iter));
iter->zone = zone;
}
rr_type *zone_rr_iter_next(struct zone_rr_iter *iter)
{
assert(iter != NULL);
assert(iter->zone != NULL);
if(iter->index == -1) {
assert(iter->domain == NULL);
assert(iter->rrset == NULL);
return NULL;
} else if(iter->rrset == NULL) {
assert(iter->domain == NULL);
assert(iter->index == 0);
iter->rrset = iter->zone->soa_rrset;
}
while(iter->rrset != NULL) {
if(iter->index < iter->rrset->rr_count) {
return iter->rrset->rrs[iter->index++];
}
iter->index = 0;
if(iter->domain == NULL) {
assert(iter->rrset == iter->zone->soa_rrset);
iter->domain = iter->zone->apex;
iter->rrset = iter->domain->rrsets;
} else {
iter->rrset = iter->rrset->next;
}
while((iter->rrset == NULL && iter->domain != NULL) ||
(iter->rrset != NULL && (iter->rrset == iter->zone->soa_rrset ||
iter->rrset->zone != iter->zone)))
{
if(iter->rrset != NULL) {
iter->rrset = iter->rrset->next;
} else {
iter->domain = domain_next(iter->domain);
if(iter->domain != NULL &&
dname_is_subdomain(domain_dname(iter->domain),
domain_dname(iter->zone->apex)))
{
iter->rrset = iter->domain->rrsets;
}
}
}
}
assert(iter->rrset == NULL);
assert(iter->domain == NULL);
iter->index = -1;
return NULL;
}