/** make the domain last in the numlist, changes numbers of domains */
static void
numlist_make_last(domain_table_type* table, domain_type* domain)
{
uint32_t sw;
domain_type* last = table->numlist_last;
if(domain == last)
return;
/* swap numbers with the last element */
sw = domain->number;
domain->number = last->number;
last->number = sw;
/* swap list position with the last element */
assert(domain->numlist_next);
assert(last->numlist_prev);
if(domain->numlist_next != last) {
/* case 1: there are nodes between domain .. last */
domain_type* span_start = domain->numlist_next;
domain_type* span_end = last->numlist_prev;
/* these assignments walk the new list from start to end */
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 {
/* case 2: domain and last are neighbors */
/* these assignments walk the new list from start to end */
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;
}
/** pop the biggest domain off the numlist */
static 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;
}
/** see if a domain is eligible to be deleted, and thus is not used */
static int
domain_can_be_deleted(domain_type* domain)
{
domain_type* n;
/* it has data or it has usage, do not delete it */
if(domain->rrsets) return 0;
if(domain->usage) return 0;
n = domain_next(domain);
/* it has children domains, do not delete it */
if(n && domain_is_subdomain(n, domain))
return 0;
return 1;
}
#ifdef NSEC3
/** see if domain is on the prehash list */
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);
}
/** remove domain node from NSEC3 tree in hash space */
void
zone_del_domain_in_hash_tree(rbtree_type* tree, rbnode_type* node)
{
if(!node->key)
return;
rbtree_delete(tree, node->key);
/* note that domain is no longer in the tree */
node->key = NULL;
}
/** clear the prehash list */
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;
}
/** perform domain name deletion */
static void
do_deldomain(namedb_type* db, domain_type* domain)
{
assert(domain && domain->parent); /* exists and not root */
/* first adjust the number list so that domain is the last one */
numlist_make_last(db->domains, domain);
/* pop off the domain from the number list */
(void)numlist_pop_last(db->domains);
#ifdef NSEC3
/* if on prehash list, remove from prehash */
if(domain_is_prehash(db->domains, domain))
prehash_del(db->domains, domain);
/* see if this domain is someones wildcard-child-closest-match,
* which can only be the parent, and then it should use the
* one-smaller than this domain as closest-match. */
if(domain->parent->wildcard_child_closest_match == domain)
domain->parent->wildcard_child_closest_match =
domain_previous_existing_child(domain);
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);
/* Insert new node(s). */
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 the newly added domain name is larger
* than the parent's current
* wildcard_child_closest_match but smaller or
* equal to the wildcard domain name, update
* the parent's wildcard_child_closest_match
* field.
*/
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) /* do not walk back above parent */
return parent;
domain = domain_previous(domain);
}
return domain;
}
void
domain_add_rrset(domain_type* domain, rrset_type* rrset)
{
#if 0 /* fast */
rrset->next = domain->rrsets;
domain->rrsets = rrset;
#else
/* preserve ordering, add at end */
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;
/* does this name in existance update the parent's
* wildcard closest match? */
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;
}
}
for (rrset = zone->apex->rrsets; rrset; rrset = rrset->next) {
if (rrset->zone != zone && rrset_rrtype(rrset) == TYPE_NS) {
return rrset->zone;
}
}
/* the NS record in the parent zone above this zone is not present,
* workaround to find that parent zone anyway */
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)
{
/* return highest NS RRset in the zone that is a delegation above */
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;
}
rrset_type *
domain_find_non_cname_rrset(domain_type* domain, zone_type* zone)
{
/* find any rrset type that is not allowed next to a CNAME */
/* nothing is allowed next to a CNAME, except RRSIG, NSEC, NSEC3 */
rrset_type *result = domain->rrsets;
while (result) {
if (result->zone == zone && /* here is the list of exceptions*/
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;
}