static void nsec3_icmtl_destroy_nsec3rrsig_add(treeset_tree *tree) { if(!treeset_avl_isempty(tree)) { /* stuff to delete */ treeset_avl_iterator ts_avl_iter; treeset_avl_iterator_init(tree, &ts_avl_iter); while(treeset_avl_iterator_hasnext(&ts_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&ts_avl_iter); u8 *fqdn = (u8*)node->key; zdb_ttlrdata *ttlrdata = (zdb_ttlrdata*)node->data; free(fqdn); while(ttlrdata != NULL) { zdb_ttlrdata *tmp = ttlrdata->next; if(ttlrdata != NULL) { ZDB_RECORD_ZFREE(ttlrdata); } ttlrdata = tmp; } } treeset_avl_destroy(tree); } }
static void nsec3_load_context_record_delete(nsec3_context_record *r) { zdb_packed_ttlrdata *rrsig = r->rrsig; while(rrsig != NULL) { zdb_packed_ttlrdata *next = rrsig->next; ZDB_RECORD_ZFREE(rrsig); rrsig = next; } size_t digest_len = 1 + r->digest_then_rdata[0]; ZFREE_ARRAY(r, sizeof(nsec3_context_record) + digest_len + r->rdata_size); }
void nsec3_icmtl_replay_nsec3_rrsig_add(nsec3_icmtl_replay *replay, const u8* fqdn, zdb_packed_ttlrdata *packed_ttlrdata) { treeset_node *node = treeset_avl_insert(&replay->nsec3rrsig_add, (u8*)fqdn); if(node->data == NULL) { node->key = dnsname_dup(fqdn); node->data = packed_ttlrdata; } else { //zdb_ttlrdata_delete(node->data); zdb_packed_ttlrdata **nsec3_rrsig_ptr = (zdb_packed_ttlrdata **)&node->data; zdb_packed_ttlrdata *nsec3_rrsig = (zdb_packed_ttlrdata *)node->data; while(nsec3_rrsig != NULL) { if((nsec3_rrsig->rdata_size == packed_ttlrdata->rdata_size) && (RRSIG_KEY_NATIVETAG(nsec3_rrsig) == RRSIG_KEY_NATIVETAG(packed_ttlrdata))) { /* got a match ? */ if(memcmp(&nsec3_rrsig->rdata_start[0], &packed_ttlrdata->rdata_start[0], nsec3_rrsig->rdata_size) == 0) { /* got a match ! delete the previous one (?) */ packed_ttlrdata->next = nsec3_rrsig->next; ZDB_RECORD_ZFREE(nsec3_rrsig); break; } } nsec3_rrsig_ptr = &nsec3_rrsig->next; nsec3_rrsig = nsec3_rrsig->next; } *nsec3_rrsig_ptr = packed_ttlrdata; } }
ya_result nsec3_icmtl_replay_execute(nsec3_icmtl_replay *replay) { bool nsec3param_added = FALSE; if(!treeset_avl_isempty(&replay->nsec3param_add)) { treeset_avl_iterator n3p_avl_iter; treeset_avl_iterator_init(&replay->nsec3param_add, &n3p_avl_iter); while(treeset_avl_iterator_hasnext(&n3p_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&n3p_avl_iter); zdb_ttlrdata* nsec3param = (zdb_ttlrdata*)node->data; nsec3_zone* n3 = nsec3_zone_get_from_rdata(replay->zone, nsec3param->rdata_size, nsec3param->rdata_pointer); if(n3 == NULL) { /* * add the record */ zdb_packed_ttlrdata *packed_ttlrdata; ZDB_RECORD_ZALLOC(packed_ttlrdata, 0, nsec3param->rdata_size ,nsec3param->rdata_pointer); zdb_record_insert(&replay->zone->apex->resource_record_set, TYPE_NSEC3PARAM, packed_ttlrdata); nsec3_zone_add_from_rdata(replay->zone, nsec3param->rdata_size, nsec3param->rdata_pointer); //nsec3_load_chain_init(nsec3param->rdata_pointer, nsec3param->rdata_size); nsec3param_added = TRUE; } zdb_ttlrdata_delete(nsec3param); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3param_add); } if(!treeset_avl_isempty(&replay->nsec3_del)) { /* stuff to delete */ treeset_avl_iterator ts_avl_iter; treeset_avl_iterator_init(&replay->nsec3_del, &ts_avl_iter); while(treeset_avl_iterator_hasnext(&ts_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&ts_avl_iter); u8 *fqdn = (u8*)node->key; zdb_ttlrdata *ttlrdata = (zdb_ttlrdata*)node->data; #ifndef NDEBUG log_debug("journal: NSEC3: post/del %{dnsname}", fqdn); #endif treeset_node *add_node; if((add_node = treeset_avl_find(&replay->nsec3_add, fqdn)) != NULL) { /* replace */ #ifndef NDEBUG log_debug("journal: NSEC3: upd %{dnsname}", fqdn); rdata_desc type_len_rdata = {TYPE_NSEC3, ttlrdata->rdata_size, ttlrdata->rdata_pointer }; log_debug("journal: NSEC3: - %{typerdatadesc}", &type_len_rdata); #endif zdb_ttlrdata *add_ttlrdata = (zdb_ttlrdata *)add_node->data; #ifndef NDEBUG rdata_desc add_type_len_rdata = {TYPE_NSEC3, add_ttlrdata->rdata_size, add_ttlrdata->rdata_pointer }; log_debug("journal: NSEC3: + %{typerdatadesc}", &add_type_len_rdata); #endif /* * The node may need an update of the type bitmap * After all changes (del/upd/add) all the added records should be matched again (check) * * nsec3_zone_item_get_by_name(); * nsec3_zone_item_update_bitmap(item, rdata, rdata_len) */ nsec3_zone_item *add_item = nsec3_zone_item_find_by_record(replay->zone, fqdn, ttlrdata->rdata_size, ttlrdata->rdata_pointer); if(add_item != NULL) { nsec3_zone_item_update_bitmap(add_item, add_ttlrdata->rdata_pointer, add_ttlrdata->rdata_size); u8* add_key = add_node->key; treeset_avl_delete(&replay->nsec3_add, fqdn); zdb_ttlrdata_delete(add_ttlrdata); free(add_key); } else { log_err("journal: NSEC3: %{dnsname} has not been found in the NSEC3 database (del/add)", fqdn); return ERROR; } } else { #ifndef NDEBUG log_debug("journal: NSEC3: del %{dnsname}", fqdn); rdata_desc type_len_rdata = {TYPE_NSEC3, ttlrdata->rdata_size, ttlrdata->rdata_pointer }; log_debug("journal: NSEC3: - %{typerdatadesc}", &type_len_rdata); #endif /* delete */ nsec3_zone_item *add_item = nsec3_zone_item_find_by_record(replay->zone, fqdn, ttlrdata->rdata_size, ttlrdata->rdata_pointer); if(add_item != NULL) { nsec3_remove_nsec3_by_name(replay->zone, fqdn, ttlrdata->rdata_pointer); } else { log_err("journal: NSEC3: %{dnsname} has not been found in the NSEC3 database (del)", fqdn); } /* * The node has to be deleted */ } zdb_ttlrdata_delete(ttlrdata); free(fqdn); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3_del); } if(!treeset_avl_isempty(&replay->nsec3_add)) { /* stuff to add */ treeset_avl_iterator ts_avl_iter; treeset_avl_iterator_init(&replay->nsec3_add, &ts_avl_iter); while(treeset_avl_iterator_hasnext(&ts_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&ts_avl_iter); u8 *fqdn = (u8*)node->key; #ifndef NDEBUG log_debug("journal: NSEC3: post/add %{dnsname}", fqdn); #endif zdb_ttlrdata *ttlrdata = (zdb_ttlrdata*)node->data; #ifndef NDEBUG log_debug("journal: NSEC3: add %{dnsname}", fqdn); rdata_desc type_len_rdata = {TYPE_NSEC3, ttlrdata->rdata_size, ttlrdata->rdata_pointer }; log_debug("journal: NSEC3: + %{typerdatadesc}", &type_len_rdata); #endif /* * The node must be added. It should not exist already. * After all changes (del/upd/add) all the added records should be matched again (check) */ nsec3_zone_item *add_item = nsec3_zone_item_find_by_record(replay->zone, fqdn, ttlrdata->rdata_size, ttlrdata->rdata_pointer); if(add_item != NULL) { log_err("journal: NSEC3: already exists"); nsec3_zone *n3 = replay->zone->nsec.nsec3; if(n3 != NULL ) { zdb_packed_ttlrdata *nsec3; zdb_packed_ttlrdata *nsec3_rrsig; u8 owner[256]; nsec3_zone_item_to_zdb_packed_ttlrdata(n3, add_item, replay->zone->origin, owner, 600, &nsec3, &nsec3_rrsig); #ifndef NDEBUG rdata_desc type_len_rdata = {TYPE_NSEC3, nsec3->rdata_size, nsec3->rdata_start }; log_debug("journal: NSEC3: ? %{typerdatadesc}", &type_len_rdata); #endif free(nsec3); nsec3_remove_nsec3_by_digest(replay->zone, add_item->digest, ttlrdata->rdata_pointer); } } nsec3_add_nsec3_by_name(replay->zone, fqdn, ttlrdata->rdata_pointer, ttlrdata->rdata_size); zdb_ttlrdata_delete(ttlrdata); free(fqdn); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3_add); } if(!treeset_avl_isempty(&replay->nsec3rrsig_del)) { /* stuff to add */ treeset_avl_iterator ts_avl_iter; treeset_avl_iterator_init(&replay->nsec3rrsig_del, &ts_avl_iter); while(treeset_avl_iterator_hasnext(&ts_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&ts_avl_iter); u8 *fqdn = (u8*)node->key; #ifndef NDEBUG log_debug("journal: NSEC3: post/add %{dnsname}", fqdn); #endif zdb_ttlrdata *nsec3_rrsig = (zdb_ttlrdata*)node->data; #ifndef NDEBUG log_debug("journal: NSEC3: add %{dnsname}", fqdn); rdata_desc type_len_rdata = {TYPE_RRSIG, ZDB_RECORD_PTR_RDATASIZE(nsec3_rrsig), ZDB_RECORD_PTR_RDATAPTR(nsec3_rrsig) }; log_debug("journal: NSEC3: + %{typerdatadesc}", &type_len_rdata); #endif /* * The node must be added. It should not exist already. * After all changes (del/upd/add) all the added records should be matched again (check) */ nsec3_zone_item *item = nsec3_zone_item_find_by_name_ext(replay->zone, fqdn, NULL); if(item != NULL) { nsec3_zone_item_rrsig_del(item, nsec3_rrsig); } zdb_ttlrdata_delete(nsec3_rrsig); free(fqdn); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3rrsig_del); } if(!treeset_avl_isempty(&replay->nsec3rrsig_add)) { /* stuff to add */ treeset_avl_iterator ts_avl_iter; treeset_avl_iterator_init(&replay->nsec3rrsig_add, &ts_avl_iter); while(treeset_avl_iterator_hasnext(&ts_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&ts_avl_iter); u8 *fqdn = (u8*)node->key; #ifndef NDEBUG log_debug("journal: NSEC3: post/add %{dnsname}", fqdn); #endif zdb_packed_ttlrdata *nsec3_rrsig = (zdb_packed_ttlrdata*)node->data; #ifndef NDEBUG log_debug("journal: NSEC3: add %{dnsname}", fqdn); rdata_desc type_len_rdata = {TYPE_RRSIG, ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec3_rrsig), ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec3_rrsig) }; log_debug("journal: NSEC3: + %{typerdatadesc}", &type_len_rdata); #endif /* * The node must be added. It should not exist already. * After all changes (del/upd/add) all the added records should be matched again (check) */ nsec3_zone_item *item = nsec3_zone_item_find_by_name_ext(replay->zone, fqdn, NULL); if(item != NULL) { nsec3_zone_item_rrsig_add(item, nsec3_rrsig); } else { ZDB_RECORD_ZFREE(nsec3_rrsig); } free(fqdn); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3rrsig_add); } if(!treeset_avl_isempty(&replay->nsec3_labels)) { /* labels to update */ treeset_avl_iterator ts_avl_iter; treeset_avl_iterator_init(&replay->nsec3_labels, &ts_avl_iter); while(treeset_avl_iterator_hasnext(&ts_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&ts_avl_iter); u8 *fqdn = (u8*)node->key; zdb_rr_label *rr_label = (zdb_rr_label*)node->data; #ifndef NDEBUG log_debug("journal: NSEC3: lbl %{dnsname} (%{dnslabel})", fqdn, rr_label->name); #endif /* * The fqdn/label should be updated for self & star match. */ if(rr_label->nsec.nsec3 == NULL) { nsec3_label_link(replay->zone, rr_label, fqdn); } free(fqdn); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3_labels); } /**/ if(nsec3param_added) { /* * ALL the labels of the zone have to be linked again. */ zdb_zone_label_iterator label_iterator; u8 fqdn[MAX_DOMAIN_LENGTH]; zdb_zone_label_iterator_init(replay->zone, &label_iterator); while(zdb_zone_label_iterator_hasnext(&label_iterator)) { zdb_zone_label_iterator_nextname(&label_iterator, fqdn); zdb_rr_label* label = zdb_zone_label_iterator_next(&label_iterator); nsec3_label_link(replay->zone, label, fqdn); } } if(!treeset_avl_isempty(&replay->nsec3param_del)) { treeset_avl_iterator n3p_avl_iter; treeset_avl_iterator_init(&replay->nsec3param_del, &n3p_avl_iter); while(treeset_avl_iterator_hasnext(&n3p_avl_iter)) { treeset_node *node = treeset_avl_iterator_next_node(&n3p_avl_iter); zdb_ttlrdata* nsec3param = (zdb_ttlrdata*)node->data; nsec3_zone* n3 = nsec3_zone_get_from_rdata(replay->zone, nsec3param->rdata_size, nsec3param->rdata_pointer); if(n3 == NULL) { nsec3_zone_destroy(replay->zone, n3); zdb_record_delete_exact(&replay->zone->apex->resource_record_set, TYPE_NSEC3PARAM, nsec3param); } zdb_ttlrdata_delete(nsec3param); node->key = NULL; node->data = NULL; } treeset_avl_destroy(&replay->nsec3param_del); } return SUCCESS; }
ya_result nsec_update_zone(zdb_zone *zone, bool read_only) // read_only a.k.a slave { nsec_node *nsec_tree = NULL; nsec_node *first_node; nsec_node *node; u8 *prev_name; u8 *name; soa_rdata soa; u32 missing_nsec_records = 0; u32 sibling_count = 0; u32 nsec_under_delegation = 0; ya_result return_code; u8 name_buffer[2][MAX_DOMAIN_LENGTH]; u8 inverse_name[MAX_DOMAIN_LENGTH]; u8 tmp_bitmap[256 * (1 + 1 + 32)]; /* 'max window count' * 'max window length' */ yassert(zdb_zone_islocked_weak(zone)); if(FAIL(return_code = zdb_zone_getsoa(zone, &soa))) // zone is locked (weak) { return return_code; } #ifdef DEBUG memset(name_buffer, 0xde, sizeof(name_buffer)); #endif name = name_buffer[0]; prev_name = name_buffer[1]; zdb_zone_label_iterator label_iterator; zdb_zone_label_iterator_init(&label_iterator, zone); while(zdb_zone_label_iterator_hasnext(&label_iterator)) { zdb_zone_label_iterator_nextname(&label_iterator, name); zdb_rr_label* label = zdb_zone_label_iterator_next(&label_iterator); if(zdb_rr_label_is_glue(label) || (label->resource_record_set == NULL)) { // we are under a delegation or on an empty (non-terminal) // there should not be an NSEC record here zdb_packed_ttlrdata *nsec_record; if((nsec_record = zdb_record_find(&label->resource_record_set, TYPE_NSEC)) != NULL) // zone is locked { nsec_under_delegation++; log_err("nsec: %{dnsname}: unexpected NSEC record under a delegation", name); } continue; } nsec_inverse_name(inverse_name, name); nsec_node *node = nsec_avl_insert(&nsec_tree, inverse_name); node->label = label; label->nsec.nsec.node = node; } /* * Now that we have the NSEC chain */ type_bit_maps_context tbmctx; nsec_avl_iterator nsec_iter; nsec_avl_iterator_init(&nsec_tree, &nsec_iter); if(nsec_avl_iterator_hasnext(&nsec_iter)) { first_node = nsec_avl_iterator_next_node(&nsec_iter); node = first_node; do { nsec_node *next_node; nsec_update_zone_count++; if(nsec_avl_iterator_hasnext(&nsec_iter)) { next_node = nsec_avl_iterator_next_node(&nsec_iter); } else { next_node = first_node; } /* * Compute the type bitmap */ zdb_rr_label *label = node->label; if(label == NULL) { node = next_node; continue; } u32 tbm_size = type_bit_maps_initialise_from_label(&tbmctx, label, TRUE, TRUE); type_bit_maps_write(&tbmctx, tmp_bitmap); u8 *tmp_name = prev_name; prev_name = name; name = tmp_name; nsec_inverse_name(name, next_node->inverse_relative_name); /* * Get the NSEC record */ zdb_packed_ttlrdata *nsec_record; if((nsec_record = zdb_record_find(&label->resource_record_set, TYPE_NSEC)) != NULL) // zone is locked { /* * has record -> compare the type and the nsec next * if something does not match remove the record and its signature (no record) * */ if(nsec_record->next == NULL) // should only be one record => delete all if not the case (the rrsig is lost anyway) { const u8 *rdata = ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec_record); const u16 size = ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec_record); const u16 dname_len = dnsname_len(rdata); if(dname_len < size) { const u8 *type_bitmap = &rdata[dname_len]; /* * check the type bitmap */ if(memcmp(tmp_bitmap, type_bitmap, size - dname_len) == 0) { /* * check the nsec next */ if(dnsname_equals(rdata, name)) { /* All good */ label->flags |= ZDB_RR_LABEL_NSEC; node = next_node; continue; } else // else the "next fqdn" do not match (this is irrecoverable for a slave) { rdata_desc nsec_desc = {TYPE_NSEC, size, rdata}; log_debug("nsec: %{dnsname}: src: %{dnsname} %{typerdatadesc} next field do not match expected value (%{dnsname})", zone->origin, prev_name, &nsec_desc, name); } } else // else the type bitmap do not match (this is wrong) { rdata_desc nsec_desc = {TYPE_NSEC, size, rdata}; log_debug("nsec: %{dnsname}: src: %{dnsname} %{typerdatadesc} types map do not match expected value", zone->origin, prev_name, &nsec_desc); } } else // else the "next fqdn" do not match (early test, this is irrecoverable for a slave) { rdata_desc nsec_desc = {TYPE_NSEC, size, rdata}; log_debug("nsec: %{dnsname}: src: %{dnsname} %{typerdatadesc} next field do not match expected value (%{dnsname})", zone->origin, prev_name, &nsec_desc, name); } } else { sibling_count++; log_warn("nsec: %{dnsname}: %{dnsname}: multiple NSEC records where only one is expected", zone->origin, prev_name); } // wrong NSEC RRSET zdb_packed_ttlrdata *nsec_rec = nsec_record; do { zdb_ttlrdata unpacked_ttlrdata; unpacked_ttlrdata.ttl = nsec_rec->ttl; unpacked_ttlrdata.rdata_size = ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec_rec); unpacked_ttlrdata.rdata_pointer = ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec_rec); rdata_desc nsec_desc = {TYPE_NSEC, unpacked_ttlrdata.rdata_size, unpacked_ttlrdata.rdata_pointer}; if(!read_only) { log_warn("nsec: %{dnsname}: del: %{dnsname} %{typerdatadesc}", zone->origin, prev_name, &nsec_desc); #if ZDB_CHANGE_FEEDBACK_SUPPORT zdb_listener_notify_remove_record(zone, name, TYPE_NSEC, &unpacked_ttlrdata); #endif } else { log_err("nsec: %{dnsname}: got: %{dnsname} %{typerdatadesc}", zone->origin, prev_name, &nsec_desc); } nsec_rec = nsec_rec->next; } while(nsec_rec != NULL); if(!read_only) { zdb_record_delete(&label->resource_record_set, TYPE_NSEC); rrsig_delete(zone, name, label, TYPE_NSEC); nsec_record = NULL; } } /* * no record -> create one and schedule a signature (MASTER ONLY) */ if(nsec_record == NULL) { missing_nsec_records++; zdb_packed_ttlrdata *nsec_record; u16 dname_len = nsec_inverse_name(name, next_node->inverse_relative_name); u16 rdata_size = dname_len + tbm_size; ZDB_RECORD_ZALLOC_EMPTY(nsec_record, soa.minimum, rdata_size); u8* rdata = ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec_record); memcpy(rdata, name, dname_len); rdata += dname_len; memcpy(rdata, tmp_bitmap, tbm_size); rdata_desc nsec_desc = {TYPE_NSEC, ZDB_PACKEDRECORD_PTR_RDATASIZE(nsec_record), ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec_record)}; if(!read_only) { zdb_record_insert(&label->resource_record_set, TYPE_NSEC, nsec_record); #ifdef DEBUG log_debug("nsec: %{dnsname}: add: %{dnsname} %{typerdatadesc}", zone->origin, prev_name, &nsec_desc); #endif #if ZDB_CHANGE_FEEDBACK_SUPPORT if(zdb_listener_notify_enabled()) { dnsname_vector name_path; zdb_ttlrdata unpacked_ttlrdata; unpacked_ttlrdata.ttl = nsec_record->ttl; unpacked_ttlrdata.rdata_size = rdata_size; unpacked_ttlrdata.rdata_pointer = ZDB_PACKEDRECORD_PTR_RDATAPTR(nsec_record); dnsname_to_dnsname_vector(name, &name_path); zdb_listener_notify_add_record(zone, name_path.labels, name_path.size, TYPE_NSEC, &unpacked_ttlrdata); } #endif /* * Schedule a signature */ } else { log_warn("nsec: %{dnsname}: need: %{dnsname} %{typerdatadesc}", zone->origin, prev_name, &nsec_desc); ZDB_RECORD_ZFREE(nsec_record); } } label->flags |= ZDB_RR_LABEL_NSEC; node = next_node; } while(node != first_node); } zone->nsec.nsec = nsec_tree; if(read_only) { if(missing_nsec_records + sibling_count + nsec_under_delegation) { log_err("nsec: missing records: %u, nsec with siblings: %u, nsec under delegation: %u", missing_nsec_records, sibling_count, nsec_under_delegation); //return DNSSEC_ERROR_NSEC_INVALIDZONESTATE; } } else { if(missing_nsec_records + sibling_count + nsec_under_delegation) { log_warn("nsec: missing records: %u, nsec with siblings: %u, nsec under delegation: %u", missing_nsec_records, sibling_count, nsec_under_delegation); } } return SUCCESS; }