diff options
Diffstat (limited to 'usr.sbin/nsd/namedb.c')
-rw-r--r-- | usr.sbin/nsd/namedb.c | 435 |
1 files changed, 356 insertions, 79 deletions
diff --git a/usr.sbin/nsd/namedb.c b/usr.sbin/nsd/namedb.c index 82801fb545a..b30e96b01c7 100644 --- a/usr.sbin/nsd/namedb.c +++ b/usr.sbin/nsd/namedb.c @@ -7,7 +7,7 @@ * */ -#include <config.h> +#include "config.h" #include <sys/types.h> @@ -18,12 +18,12 @@ #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) +allocate_domain_info(domain_table_type* table, + const dname_type* dname, + domain_type* parent) { domain_type *result; @@ -33,62 +33,312 @@ allocate_domain_info(domain_table_type *table, result = (domain_type *) region_alloc(table->region, sizeof(domain_type)); - result->node.key = dname_partial_copy( + result->dname = 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->number = 0; + result->usage = 0; #ifdef NSEC3 - result->nsec3_cover = NULL; - result->nsec3_wcard_child_cover = NULL; - result->nsec3_ds_parent_cover = NULL; - result->nsec3_lookup = NULL; - result->nsec3_is_exact = 0; - result->nsec3_ds_parent_is_exact = 0; + result->nsec3 = NULL; #endif result->is_existing = 0; result->is_apex = 0; + assert(table->numlist_last); /* it exists because root exists */ + /* push this domain at the end of the numlist */ + 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->have_nsec3_hash = 0; + result->nsec3->have_nsec3_wc_hash = 0; + result->nsec3->have_nsec3_ds_parent_hash = 0; + result->nsec3->prehash_prev = NULL; + result->nsec3->prehash_next = NULL; + result->nsec3->nsec3_node.key = NULL; + result->nsec3->hash_node.key = NULL; + result->nsec3->wchash_node.key = NULL; + result->nsec3->dshash_node.key = NULL; +} +#endif /* NSEC3 */ + +/** make the domain last in the numlist, changes numbers of domains */ +static void +numlist_make_last(domain_table_type* table, domain_type* domain) +{ + size_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_t* tree, rbnode_t* 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; +} + +/** add domain to prehash list */ +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; +} + +/** remove domain from prehash list */ +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 /* NSEC3 */ + +/** 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 nsec3-nodes are used */ + 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_node.key) + zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain) + ->hashtree, &domain->nsec3->hash_node); + if(domain->nsec3->wchash_node.key) + zone_del_domain_in_hash_tree(nsec3_tree_zone(db, domain) + ->wchashtree, &domain->nsec3->wchash_node); + if(domain->nsec3->dshash_node.key) + zone_del_domain_in_hash_tree(nsec3_tree_dszone(db, domain) + ->dshashtree, &domain->nsec3->dshash_node); + region_recycle(db->domains->region, domain->nsec3, + sizeof(struct nsec3_domain_data)); + } +#endif /* NSEC3 */ + + /* 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); + + /* actual removal */ + radix_delete(db->domains->nametree, domain->rnode); + region_recycle(db->domains->region, (dname_type*)domain->dname, + dname_total_size(domain->dname)); + region_recycle(db->domains->region, domain, sizeof(domain_type)); +} + +void +domain_table_deldomain(namedb_type* db, domain_type* domain) +{ + while(domain_can_be_deleted(domain)) { + /* delete it */ + do_deldomain(db, domain); + /* test parent */ + domain = domain->parent; + } +} + +/** clear hash tree */ +void +hash_tree_clear(rbtree_t* tree) +{ + rbnode_t* n; + if(!tree) return; + + /* note that elements are no longer in the tree */ + for(n=rbtree_first(tree); n!=RBTREE_NULL; n=rbtree_next(n)) { + n->key = NULL; + } + tree->count = 0; + tree->root = RBTREE_NULL; +} + +void hash_tree_delete(region_type* region, rbtree_t* tree) +{ + region_recycle(region, tree, sizeof(rbtree_t)); +} + +/** add domain nsec3 node to hashedspace tree */ +void zone_add_domain_in_hash_tree(region_type* region, rbtree_t** tree, + int (*cmpf)(const void*, const void*), + domain_type* domain, rbnode_t* node) +{ + if(!*tree) + *tree = rbtree_create(region, cmpf); + memset(node, 0, sizeof(rbnode_t)); + node->key = domain; + rbtree_insert(*tree, node); +} + domain_table_type * -domain_table_create(region_type *region) +domain_table_create(region_type* region) { - const dname_type *origin; - domain_table_type *result; - domain_type *root; + 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)); - root->node.key = origin; + root->dname = origin; root->parent = NULL; root->wildcard_child_closest_match = root; root->rrsets = NULL; root->number = 1; /* 0 is used for after header */ + root->usage = 1; /* do not delete root, ever */ root->is_existing = 0; root->is_apex = 0; + root->numlist_prev = NULL; + root->numlist_next = NULL; #ifdef NSEC3 - root->nsec3_is_exact = 0; - root->nsec3_ds_parent_is_exact = 0; - root->nsec3_cover = NULL; - root->nsec3_wcard_child_cover = NULL; - root->nsec3_ds_parent_cover = NULL; - root->nsec3_lookup = NULL; + root->nsec3 = NULL; #endif result = (domain_table_type *) region_alloc(region, sizeof(domain_table_type)); result->region = region; - result->names_to_domains = rbtree_create( - region, (int (*)(const void *, const void *)) dname_compare); - rbtree_insert(result->names_to_domains, (rbnode_t *) root); + result->nametree = radix_tree_create(region); + root->rnode = radname_insert(result->nametree, dname_name(root->dname), + root->dname->name_size, root); result->root = root; + result->numlist_last = root; +#ifdef NSEC3 + result->prehash_list = NULL; +#endif return result; } @@ -107,7 +357,9 @@ domain_table_search(domain_table_type *table, assert(closest_match); assert(closest_encloser); - exact = rbtree_find_less_equal(table->names_to_domains, dname, (rbnode_t **) closest_match); + 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); assert(*closest_match); *closest_encloser = *closest_match; @@ -127,11 +379,11 @@ domain_table_search(domain_table_type *table, } domain_type * -domain_table_find(domain_table_type *table, - const dname_type *dname) +domain_table_find(domain_table_type* table, + const dname_type* dname) { - domain_type *closest_match; - domain_type *closest_encloser; + domain_type* closest_match; + domain_type* closest_encloser; int exact; exact = domain_table_search( @@ -141,12 +393,12 @@ domain_table_find(domain_table_type *table, domain_type * -domain_table_insert(domain_table_type *table, - const dname_type *dname) +domain_table_insert(domain_table_type* table, + const dname_type* dname) { - domain_type *closest_match; - domain_type *closest_encloser; - domain_type *result; + domain_type* closest_match; + domain_type* closest_encloser; + domain_type* result; int exact; assert(table); @@ -164,8 +416,9 @@ domain_table_insert(domain_table_type *table, result = allocate_domain_info(table, dname, closest_encloser); - rbtree_insert(table->names_to_domains, (rbnode_t *) result); - result->number = table->names_to_domains->count; + result->rnode = radname_insert(table->nametree, + dname_name(result->dname), + result->dname->name_size, result); /* * If the newly added domain name is larger @@ -191,26 +444,32 @@ domain_table_insert(domain_table_type *table, } int -domain_table_iterate(domain_table_type *table, +domain_table_iterate(domain_table_type* table, domain_table_iterator_type iterator, - void *user_data) + void* user_data) { - const void *dname; - void *node; int error = 0; - - assert(table); - - RBTREE_WALK(table->names_to_domains, dname, node) { - error += iterator((domain_type *) node, user_data); + struct radnode* n; + for(n = radix_first(table->nametree); n; n = radix_next(n)) { + error += iterator((domain_type*)n->elem, user_data); } - return error; } +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) +domain_add_rrset(domain_type* domain, rrset_type* rrset) { #if 0 /* fast */ rrset->next = domain->rrsets; @@ -226,15 +485,24 @@ domain_add_rrset(domain_type *domain, rrset_type *rrset) 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; } } rrset_type * -domain_find_rrset(domain_type *domain, zone_type *zone, uint16_t type) +domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t type) { - rrset_type *result = domain->rrsets; + rrset_type* result = domain->rrsets; while (result) { if (result->zone == zone && rrset_rrtype(result) == type) { @@ -246,9 +514,9 @@ domain_find_rrset(domain_type *domain, zone_type *zone, uint16_t type) } rrset_type * -domain_find_any_rrset(domain_type *domain, zone_type *zone) +domain_find_any_rrset(domain_type* domain, zone_type* zone) { - rrset_type *result = domain->rrsets; + rrset_type* result = domain->rrsets; while (result) { if (result->zone == zone) { @@ -260,14 +528,17 @@ domain_find_any_rrset(domain_type *domain, zone_type *zone) } zone_type * -domain_find_zone(domain_type *domain) +domain_find_zone(namedb_type* db, domain_type* domain) { - rrset_type *rrset; + rrset_type* rrset; while (domain) { - for (rrset = domain->rrsets; rrset; rrset = rrset->next) { - if (rrset_rrtype(rrset) == TYPE_SOA) { - return rrset->zone; + 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; } @@ -275,9 +546,9 @@ domain_find_zone(domain_type *domain) } zone_type * -domain_find_parent_zone(zone_type *zone) +domain_find_parent_zone(zone_type* zone) { - rrset_type *rrset; + rrset_type* rrset; assert(zone); @@ -290,7 +561,7 @@ domain_find_parent_zone(zone_type *zone) } domain_type * -domain_find_ns_rrsets(domain_type *domain, zone_type *zone, rrset_type **ns) +domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns) { while (domain && domain != zone->apex) { *ns = domain_find_rrset(domain, zone, TYPE_NS); @@ -304,18 +575,18 @@ domain_find_ns_rrsets(domain_type *domain, zone_type *zone, rrset_type **ns) } int -domain_is_glue(domain_type *domain, zone_type *zone) +domain_is_glue(domain_type* domain, zone_type* zone) { - rrset_type *unused; - domain_type *ns_domain = domain_find_ns_rrsets(domain, zone, &unused); + 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_wildcard_child(domain_type* domain) { - domain_type *wildcard_child; + domain_type* wildcard_child; assert(domain); assert(domain->wildcard_child_closest_match); @@ -331,14 +602,14 @@ domain_wildcard_child(domain_type *domain) } int -zone_is_secure(zone_type *zone) +zone_is_secure(zone_type* zone) { assert(zone); return zone->is_secure; } uint16_t -rr_rrsig_type_covered(rr_type *rr) +rr_rrsig_type_covered(rr_type* rr) { assert(rr->type == TYPE_RRSIG); assert(rr->rdata_count > 0); @@ -348,20 +619,16 @@ rr_rrsig_type_covered(rr_type *rr) } zone_type * -namedb_find_zone(namedb_type *db, domain_type *domain) +namedb_find_zone(namedb_type* db, const dname_type* dname) { - zone_type *zone; - - for (zone = db->zones; zone; zone = zone->next) { - if (zone->apex == domain) - break; - } - - return zone; + 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) +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 */ @@ -381,3 +648,13 @@ domain_find_non_cname_rrset(domain_type *domain, zone_type *zone) } 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); +} |