/** * Find closest existing parent data for the given name. * @param zone: to look in. * @param nm: name to look for * @param nm_len: length of nm * @param labs: labelcount of nm. * @return the data or NULL if none found. */ static struct val_neg_data* neg_closest_data_parent( struct val_neg_zone* zone, uint8_t* nm, size_t nm_len, int labs) { struct val_neg_data key; struct val_neg_data* result; rbnode_t* res = NULL; key.node.key = &key; key.name = nm; key.len = nm_len; key.labs = labs; if(rbtree_find_less_equal(&zone->tree, &key, &res)) { /* exact match */ result = (struct val_neg_data*)res; } else { /* smaller element (or no element) */ int m; result = (struct val_neg_data*)res; if(!result) return NULL; /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->labs, key.name, key.labs, &m); while(result) { /* go up until qname is subdomain of stub */ if(result->labs <= m) break; result = result->parent; } } return result; }
/** initialise parent pointers in the tree */ static void fwd_init_parents(struct iter_forwards* fwd) { struct iter_forward_zone* node, *prev = NULL, *p; int m; RBTREE_FOR(node, struct iter_forward_zone*, fwd->tree) { node->parent = NULL; if(!prev || prev->dclass != node->dclass) { prev = node; continue; } (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, node->namelabs, &m); /* we know prev is smaller */ /* sort order like: . com. bla.com. zwb.com. net. */ /* find the previous, or parent-parent-parent */ for(p = prev; p; p = p->parent) /* looking for name with few labels, a parent */ if(p->namelabs <= m) { /* ==: since prev matched m, this is closest*/ /* <: prev matches more, but is not a parent, * this one is a (grand)parent */ node->parent = p; break; } prev = node; } }
/** * Find best signer name in this set of rrsigs. * @param rrset: which rrsigs to look through. * @param qinf: the query name that needs validation. * @param signer_name: the best signer_name. Updated if a better one is found. * @param signer_len: length of signer name. * @param matchcount: count of current best name (starts at 0 for no match). * Updated if match is improved. */ static void val_find_best_signer(struct ub_packed_rrset_key* rrset, struct query_info* qinf, uint8_t** signer_name, size_t* signer_len, int* matchcount) { struct packed_rrset_data* d = (struct packed_rrset_data*) rrset->entry.data; uint8_t* sign; size_t i; int m; for(i=d->count; i<d->count+d->rrsig_count; i++) { sign = d->rr_data[i]+2+18; /* look at signatures that are valid (long enough), * and have a signer name that is a superdomain of qname, * and then check the number of labels in the shared topdomain * improve the match if possible */ if(d->rr_len[i] > 2+19 && /* rdata, sig + root label*/ dname_subdomain_c(qinf->qname, sign)) { (void)dname_lab_cmp(qinf->qname, dname_count_labels(qinf->qname), sign, dname_count_labels(sign), &m); if(m > *matchcount) { *matchcount = m; *signer_name = sign; (void)dname_count_size_labels(*signer_name, signer_len); } } } }
void anchors_init_parents_locked(struct val_anchors* anchors) { struct trust_anchor* node, *prev = NULL, *p; int m; /* nobody else can grab locks because we hold the main lock. * Thus the previous items, after unlocked, are not deleted */ RBTREE_FOR(node, struct trust_anchor*, anchors->tree) { lock_basic_lock(&node->lock); node->parent = NULL; if(!prev || prev->dclass != node->dclass) { prev = node; lock_basic_unlock(&node->lock); continue; } (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, node->namelabs, &m); /* we know prev is smaller */ /* sort order like: . com. bla.com. zwb.com. net. */ /* find the previous, or parent-parent-parent */ for(p = prev; p; p = p->parent) /* looking for name with few labels, a parent */ if(p->namelabs <= m) { /* ==: since prev matched m, this is closest*/ /* <: prev matches more, but is not a parent, * this one is a (grand)parent */ node->parent = p; break; } lock_basic_unlock(&node->lock); prev = node; } }
struct delegpt* forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) { /* lookup the forward zone in the tree */ rbnode_t* res = NULL; struct iter_forward_zone *result; struct iter_forward_zone key; key.node.key = &key; key.dclass = qclass; key.name = qname; key.namelabs = dname_count_size_labels(qname, &key.namelen); if(rbtree_find_less_equal(fwd->tree, &key, &res)) { /* exact */ result = (struct iter_forward_zone*)res; } else { /* smaller element (or no element) */ int m; result = (struct iter_forward_zone*)res; if(!result || result->dclass != qclass) return NULL; /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->namelabs, key.name, key.namelabs, &m); while(result) { /* go up until qname is subdomain of stub */ if(result->namelabs <= m) break; result = result->parent; } } if(result) return result->dp; return NULL; }
/** setup parent pointers, so that a lookup can be done for closest match */ static void init_parents(struct local_zones* zones) { struct local_zone* node, *prev = NULL, *p; int m; lock_rw_wrlock(&zones->lock); RBTREE_FOR(node, struct local_zone*, &zones->ztree) { lock_rw_wrlock(&node->lock); node->parent = NULL; if(!prev || prev->dclass != node->dclass) { prev = node; lock_rw_unlock(&node->lock); continue; } (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, node->namelabs, &m); /* we know prev is smaller */ /* sort order like: . com. bla.com. zwb.com. net. */ /* find the previous, or parent-parent-parent */ for(p = prev; p; p = p->parent) /* looking for name with few labels, a parent */ if(p->namelabs <= m) { /* ==: since prev matched m, this is closest*/ /* <: prev matches more, but is not a parent, * this one is a (grand)parent */ node->parent = p; break; } prev = node; if(node->override_tree) addr_tree_init_parents(node->override_tree); lock_rw_unlock(&node->lock); } lock_rw_unlock(&zones->lock); }
/** * Find domain name in tree, returns exact and closest match. * @param tree: root of tree. * @param dname: pointer to uncompressed dname. * @param labs: number of labels in domain name. * @param match: closest or exact match. * guaranteed to be smaller or equal to the sought dname. * can be null if the tree is empty. * @param matchlabels: number of labels that match with closest match. * can be zero is there is no match. * @param insertpt: insert location for dname, if not found. * @return: 0 if no exact match. */ static int compress_tree_search(struct compress_tree_node** tree, uint8_t* dname, int labs, struct compress_tree_node** match, int* matchlabels, struct compress_tree_node*** insertpt) { int c, n, closen=0; struct compress_tree_node* p = *tree; struct compress_tree_node* close = 0; struct compress_tree_node** prev = tree; while(p) { if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n)) == 0) { *matchlabels = n; *match = p; return 1; } if(c<0) { prev = &p->left; p = p->left; } else { closen = n; close = p; /* p->dname is smaller than dname */ prev = &p->right; p = p->right; } } *insertpt = prev; *matchlabels = closen; *match = close; return 0; }
struct local_zone* local_zones_tags_lookup(struct local_zones* zones, uint8_t* name, size_t len, int labs, uint16_t dclass, uint8_t* taglist, size_t taglen, int ignoretags) { rbnode_t* res = NULL; struct local_zone *result; struct local_zone key; int m; key.node.key = &key; key.dclass = dclass; key.name = name; key.namelen = len; key.namelabs = labs; rbtree_find_less_equal(&zones->ztree, &key, &res); result = (struct local_zone*)res; /* exact or smaller element (or no element) */ if(!result || result->dclass != dclass) return NULL; /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->namelabs, key.name, key.namelabs, &m); while(result) { /* go up until qname is zone or subdomain of zone */ if(result->namelabs <= m) if(ignoretags || !result->taglist || taglist_intersect(result->taglist, result->taglen, taglist, taglen)) break; result = result->parent; } return result; }
uint8_t* dname_get_shared_topdomain(uint8_t* d1, uint8_t* d2) { int labs1, labs2, m; size_t len = LDNS_MAX_DOMAINLEN; labs1 = dname_count_labels(d1); labs2 = dname_count_labels(d2); (void)dname_lab_cmp(d1, labs1, d2, labs2, &m); dname_remove_labels(&d1, &len, labs1-m); return d1; }
int name_tree_compare(const void* k1, const void* k2) { struct name_tree_node* x = (struct name_tree_node*)k1; struct name_tree_node* y = (struct name_tree_node*)k2; int m; if(x->dclass != y->dclass) { if(x->dclass < y->dclass) return -1; return 1; } return dname_lab_cmp(x->name, x->labs, y->name, y->labs, &m); }
int dname_strict_subdomain(uint8_t* d1, int labs1, uint8_t* d2, int labs2) { int m; /* check subdomain: d1: www.example.com. and d2: example.com. */ if(labs2 >= labs1) return 0; if(dname_lab_cmp(d1, labs1, d2, labs2, &m) > 0) { /* subdomain if all labels match */ return (m == labs2); } return 0; }
int fwd_cmp(const void* k1, const void* k2) { int m; struct iter_forward_zone* n1 = (struct iter_forward_zone*)k1; struct iter_forward_zone* n2 = (struct iter_forward_zone*)k2; if(n1->dclass != n2->dclass) { if(n1->dclass < n2->dclass) return -1; return 1; } return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, &m); }
int anchor_cmp(const void* k1, const void* k2) { int m; struct trust_anchor* n1 = (struct trust_anchor*)k1; struct trust_anchor* n2 = (struct trust_anchor*)k2; /* no need to ntohs(class) because sort order is irrelevant */ if(n1->dclass != n2->dclass) { if(n1->dclass < n2->dclass) return -1; return 1; } return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, &m); }
int local_zone_cmp(const void* z1, const void* z2) { /* first sort on class, so that hierarchy can be maintained within * a class */ struct local_zone* a = (struct local_zone*)z1; struct local_zone* b = (struct local_zone*)z2; int m; if(a->dclass != b->dclass) { if(a->dclass < b->dclass) return -1; return 1; } return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m); }
int dname_subdomain_c(uint8_t* d1, uint8_t* d2) { int m; /* check subdomain: d1: www.example.com. and d2: example.com. */ /* or d1: example.com. and d2: example.com. */ int labs1 = dname_count_labels(d1); int labs2 = dname_count_labels(d2); if(labs2 > labs1) return 0; if(dname_lab_cmp(d1, labs1, d2, labs2, &m) < 0) { /* must have been example.com , www.example.com - wrong */ /* or otherwise different dnames */ return 0; } return (m == labs2); }
struct trust_anchor* anchors_lookup(struct val_anchors* anchors, uint8_t* qname, size_t qname_len, uint16_t qclass) { struct trust_anchor key; struct trust_anchor* result; rbnode_t* res = NULL; key.node.key = &key; key.name = qname; key.namelabs = dname_count_labels(qname); key.namelen = qname_len; key.dclass = qclass; lock_basic_lock(&anchors->lock); if(rbtree_find_less_equal(anchors->tree, &key, &res)) { /* exact */ result = (struct trust_anchor*)res; } else { /* smaller element (or no element) */ int m; result = (struct trust_anchor*)res; if(!result || result->dclass != qclass) { lock_basic_unlock(&anchors->lock); return NULL; } /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->namelabs, key.name, key.namelabs, &m); while(result) { /* go up until qname is subdomain of stub */ if(result->namelabs <= m) break; result = result->parent; } } if(result) { lock_basic_lock(&result->lock); } lock_basic_unlock(&anchors->lock); return result; }
/** test dname_lab_cmp */ static void dname_test_dname_lab_cmp(void) { int ml = 0; /* number of labels that matched exactly */ unit_show_func("util/data/dname.c", "dname_lab_cmp"); /* test for equality succeeds */ unit_assert(dname_lab_cmp((uint8_t*)"", 1, (uint8_t*)"", 1, &ml) == 0); unit_assert(ml == 1); unit_assert(dname_lab_cmp( (uint8_t*)"\003net", 2, (uint8_t*)"\003net", 2, &ml) == 0); unit_assert(ml == 2); unit_assert(dname_lab_cmp( (uint8_t*)"\007example\003net", 3, (uint8_t*)"\007example\003net", 3, &ml) == 0); unit_assert(ml == 3); unit_assert(dname_lab_cmp( (uint8_t*)"\004test\007example\003net", 4, (uint8_t*)"\004test\007example\003net", 4, &ml) == 0); unit_assert(ml == 4); /* root is smaller than anything else */ unit_assert(dname_lab_cmp( (uint8_t*)"", 1, (uint8_t*)"\003net", 2, &ml) == -1); unit_assert(ml == 1); unit_assert(dname_lab_cmp( (uint8_t*)"\003net", 2, (uint8_t*)"", 1, &ml) == 1); unit_assert(ml == 1); unit_assert(dname_lab_cmp( (uint8_t*)"", 1, (uint8_t*)"\007example\003net", 3, &ml) == -1); unit_assert(ml == 1); unit_assert(dname_lab_cmp( (uint8_t*)"\007example\003net", 3, (uint8_t*)"", 1, &ml) == 1); unit_assert(ml == 1); /* label length makes a difference */ unit_assert(dname_lab_cmp( (uint8_t*)"\004neta", 2, (uint8_t*)"\003net", 2, &ml) != 0); unit_assert(ml == 1); unit_assert(dname_lab_cmp( (uint8_t*)"\002ne", 2, (uint8_t*)"\004neta", 2, &ml) != 0); unit_assert(ml == 1); /* contents follow the zone apex */ unit_assert(dname_lab_cmp( (uint8_t*)"\003bla\007example\003net", 4, (uint8_t*)"\007example\003net", 3, &ml) == 1); unit_assert(ml == 3); unit_assert(dname_lab_cmp( (uint8_t*)"\007example\003net", 3, (uint8_t*)"\003bla\007example\003net", 4, &ml) == -1); unit_assert(ml == 3); /* label content makes a difference */ unit_assert(dname_lab_cmp( (uint8_t*)"\003aag\007example\003net", 4, (uint8_t*)"\003bla\007example\003net", 4, &ml) == -1); unit_assert(ml == 3); unit_assert(dname_lab_cmp( (uint8_t*)"\003aag\007example\003net", 4, (uint8_t*)"\003bla\007example\003net", 4, &ml) == -1); unit_assert(ml == 3); unit_assert(dname_lab_cmp( (uint8_t*)"\003bla\003aag\007example\003net", 5, (uint8_t*)"\003aag\003bla\007example\003net", 5, &ml) == -1); unit_assert(ml == 3); unit_assert(dname_lab_cmp( (uint8_t*)"\02sn\003opt\003aag\007example\003net", 6, (uint8_t*)"\02sn\003opt\003bla\007example\003net", 6, &ml) == -1); unit_assert(ml == 3); /* but lowercase/uppercase does not make a difference. */ unit_assert(dname_lab_cmp( (uint8_t*)"\003bLa\007examPLe\003net", 4, (uint8_t*)"\003bla\007eXAmple\003nET", 4, &ml) == 0); unit_assert(ml == 4); }
/** enter implicit transparent zone for local-data: without local-zone: */ static int lz_setup_implicit(struct local_zones* zones, struct config_file* cfg) { /* walk over all items that have no parent zone and find * the name that covers them all (could be the root) and * add that as a transparent zone */ struct config_strlist* p; int have_name = 0; int have_other_classes = 0; uint16_t dclass = 0; uint8_t* nm = 0; size_t nmlen = 0; int nmlabs = 0; int match = 0; /* number of labels match count */ init_parents(zones); /* to enable local_zones_lookup() */ for(p = cfg->local_data; p; p = p->next) { uint8_t* rr_name; uint16_t rr_class; size_t len; int labs; if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) { log_err("Bad local-data RR %s", p->str); return 0; } labs = dname_count_size_labels(rr_name, &len); lock_rw_rdlock(&zones->lock); if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) { if(!have_name) { dclass = rr_class; nm = rr_name; nmlen = len; nmlabs = labs; match = labs; have_name = 1; } else { int m; if(rr_class != dclass) { /* process other classes later */ free(rr_name); have_other_classes = 1; lock_rw_unlock(&zones->lock); continue; } /* find smallest shared topdomain */ (void)dname_lab_cmp(nm, nmlabs, rr_name, labs, &m); free(rr_name); if(m < match) match = m; } } else free(rr_name); lock_rw_unlock(&zones->lock); } if(have_name) { uint8_t* n2; struct local_zone* z; /* allocate zone of smallest shared topdomain to contain em */ n2 = nm; dname_remove_labels(&n2, &nmlen, nmlabs - match); n2 = memdup(n2, nmlen); free(nm); if(!n2) { log_err("out of memory"); return 0; } log_nametypeclass(VERB_ALGO, "implicit transparent local-zone", n2, 0, dclass); if(!(z=lz_enter_zone_dname(zones, n2, nmlen, match, local_zone_transparent, dclass))) { return 0; } lock_rw_unlock(&z->lock); } if(have_other_classes) { /* restart to setup other class */ return lz_setup_implicit(zones, cfg); } return 1; }