diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:24:08 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:24:08 +0000 |
commit | f449f278dd3c70e479a035f50a9bb817a9b433ba (patch) | |
tree | 8ca2bfb785dda9bb4d573acdf9b42aea9cd51383 /src/knot/dnssec/nsec3-chain.c | |
parent | Initial commit. (diff) | |
download | knot-f449f278dd3c70e479a035f50a9bb817a9b433ba.tar.xz knot-f449f278dd3c70e479a035f50a9bb817a9b433ba.zip |
Adding upstream version 3.2.6.upstream/3.2.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/knot/dnssec/nsec3-chain.c')
-rw-r--r-- | src/knot/dnssec/nsec3-chain.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c new file mode 100644 index 0000000..97010be --- /dev/null +++ b/src/knot/dnssec/nsec3-chain.c @@ -0,0 +1,733 @@ +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libknot/dname.h" +#include "knot/dnssec/nsec-chain.h" +#include "knot/dnssec/nsec3-chain.h" +#include "knot/dnssec/zone-sign.h" +#include "knot/dnssec/zone-nsec.h" +#include "knot/zone/adjust.h" +#include "knot/zone/zone-diff.h" +#include "contrib/base32hex.h" +#include "contrib/wire_ctx.h" + +static bool nsec3_empty(const zone_node_t *node, const dnssec_nsec3_params_t *params) +{ + bool opt_out = (params->flags & KNOT_NSEC3_FLAG_OPT_OUT); + return opt_out ? !(node->flags & NODE_FLAGS_SUBTREE_AUTH) : !(node->flags & NODE_FLAGS_SUBTREE_DATA); +} + +/*! + * \brief Check whether at least one RR type in node should be signed, + * used when signing with NSEC3. + * + * \param node Node for which the check is done. + * + * \return true/false. + */ +static bool node_should_be_signed_nsec3(const zone_node_t *n) +{ + for (int i = 0; i < n->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(n, i); + if (rrset.type == KNOT_RRTYPE_NSEC || + rrset.type == KNOT_RRTYPE_RRSIG) { + continue; + } + + if (knot_zone_sign_rr_should_be_signed(n, &rrset)) { + return true; + } + } + + return false; +} + +/*! + * \brief Custom NSEC3 tree free function. + * + */ +static void free_nsec3_tree(zone_tree_t *nodes) +{ + assert(nodes); + + zone_tree_it_t it = { 0 }; + for ((void)zone_tree_it_begin(nodes, &it); !zone_tree_it_finished(&it); zone_tree_it_next(&it)) { + zone_node_t *node = zone_tree_it_val(&it); + // newly allocated NSEC3 nodes + knot_rdataset_t *nsec3 = node_rdataset(node, KNOT_RRTYPE_NSEC3); + knot_rdataset_t *rrsig = node_rdataset(node, KNOT_RRTYPE_RRSIG); + knot_rdataset_clear(nsec3, NULL); + knot_rdataset_clear(rrsig, NULL); + node_free(node, NULL); + } + + zone_tree_it_free(&it); + zone_tree_free(&nodes); +} + +/* - NSEC3 nodes construction ----------------------------------------------- */ + +/*! + * \brief Get NSEC3 RDATA size. + */ +static size_t nsec3_rdata_size(const dnssec_nsec3_params_t *params, + const dnssec_nsec_bitmap_t *rr_types) +{ + assert(params); + assert(rr_types); + + return 6 + params->salt.size + + dnssec_nsec3_hash_length(params->algorithm) + + dnssec_nsec_bitmap_size(rr_types); +} + +/*! + * \brief Fill NSEC3 RDATA. + * + * \note Content of next hash field is not changed. + */ +static int nsec3_fill_rdata(uint8_t *rdata, size_t rdata_len, + const dnssec_nsec3_params_t *params, + const dnssec_nsec_bitmap_t *rr_types, + const uint8_t *next_hashed) +{ + assert(rdata); + assert(params); + assert(rr_types); + + uint8_t hash_length = dnssec_nsec3_hash_length(params->algorithm); + + wire_ctx_t wire = wire_ctx_init(rdata, rdata_len); + + wire_ctx_write_u8(&wire, params->algorithm); + wire_ctx_write_u8(&wire, params->flags); + wire_ctx_write_u16(&wire, params->iterations); + wire_ctx_write_u8(&wire, params->salt.size); + wire_ctx_write(&wire, params->salt.data, params->salt.size); + wire_ctx_write_u8(&wire, hash_length); + + if (next_hashed != NULL) { + wire_ctx_write(&wire, next_hashed, hash_length); + } else { + wire_ctx_skip(&wire, hash_length); + } + + if (wire.error != KNOT_EOK) { + return wire.error; + } + + dnssec_nsec_bitmap_write(rr_types, wire.position); + + return KNOT_EOK; +} + +/*! + * \brief Creates NSEC3 RRSet. + * + * \param owner Owner for the RRSet. + * \param params Parsed NSEC3PARAM. + * \param rr_types Bitmap. + * \param next_hashed Next hashed. + * \param ttl TTL for the RRSet. + * + * \return Pointer to created RRSet on success, NULL on errors. + */ +static int create_nsec3_rrset(knot_rrset_t *rrset, + const knot_dname_t *owner, + const dnssec_nsec3_params_t *params, + const dnssec_nsec_bitmap_t *rr_types, + const uint8_t *next_hashed, + uint32_t ttl) +{ + assert(rrset); + assert(owner); + assert(params); + assert(rr_types); + + knot_dname_t *owner_copy = knot_dname_copy(owner, NULL); + if (owner_copy == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_init(rrset, owner_copy, KNOT_RRTYPE_NSEC3, KNOT_CLASS_IN, ttl); + + size_t rdata_size = nsec3_rdata_size(params, rr_types); + uint8_t rdata[rdata_size]; + memset(rdata, 0, rdata_size); + int ret = nsec3_fill_rdata(rdata, rdata_size, params, rr_types, + next_hashed); + if (ret != KNOT_EOK) { + knot_dname_free(owner_copy, NULL); + return ret; + } + + ret = knot_rrset_add_rdata(rrset, rdata, rdata_size, NULL); + if (ret != KNOT_EOK) { + knot_dname_free(owner_copy, NULL); + return ret; + } + + return KNOT_EOK; +} + +/*! + * \brief Create NSEC3 node. + */ +static zone_node_t *create_nsec3_node(const knot_dname_t *owner, + const dnssec_nsec3_params_t *nsec3_params, + zone_node_t *apex_node, + const dnssec_nsec_bitmap_t *rr_types, + uint32_t ttl) +{ + assert(owner); + assert(nsec3_params); + assert(apex_node); + assert(rr_types); + + zone_node_t *new_node = node_new(owner, false, false, NULL); + if (!new_node) { + return NULL; + } + + knot_rrset_t nsec3_rrset; + int ret = create_nsec3_rrset(&nsec3_rrset, owner, nsec3_params, + rr_types, NULL, ttl); + if (ret != KNOT_EOK) { + node_free(new_node, NULL); + return NULL; + } + + ret = node_add_rrset(new_node, &nsec3_rrset, NULL); + knot_rrset_clear(&nsec3_rrset, NULL); + if (ret != KNOT_EOK) { + node_free(new_node, NULL); + return NULL; + } + + return new_node; +} + +/*! + * \brief Create new NSEC3 node for given regular node. + * + * \param node Node for which the NSEC3 node is created. + * \param apex Zone apex node. + * \param params NSEC3 hash function parameters. + * \param ttl TTL of the new NSEC3 node. + * + * \return Error code, KNOT_EOK if successful. + */ +static zone_node_t *create_nsec3_node_for_node(const zone_node_t *node, + zone_node_t *apex, + const dnssec_nsec3_params_t *params, + uint32_t ttl) +{ + assert(node); + assert(apex); + assert(params); + + knot_dname_storage_t nsec3_owner; + int ret = knot_create_nsec3_owner(nsec3_owner, sizeof(nsec3_owner), + node->owner, apex->owner, params); + if (ret != KNOT_EOK) { + return NULL; + } + + dnssec_nsec_bitmap_t *rr_types = dnssec_nsec_bitmap_new(); + if (!rr_types) { + return NULL; + } + + bitmap_add_node_rrsets(rr_types, node, false); + if (node->rrset_count > 0 && node_should_be_signed_nsec3(node)) { + dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_RRSIG); + } + if (node == apex) { + dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_NSEC3PARAM); + } + + zone_node_t *nsec3_node = create_nsec3_node(nsec3_owner, params, apex, + rr_types, ttl); + dnssec_nsec_bitmap_free(rr_types); + + return nsec3_node; +} + +/* - NSEC3 chain creation --------------------------------------------------- */ + +// see connect_nsec3_nodes() for what this function does +static int connect_nsec3_base(knot_rdataset_t *a_rrs, const knot_dname_t *b_name) +{ + assert(a_rrs); + uint8_t algorithm = knot_nsec3_alg(a_rrs->rdata); + if (algorithm == 0) { + return KNOT_EINVAL; + } + + uint8_t raw_length = knot_nsec3_next_len(a_rrs->rdata); + assert(raw_length == dnssec_nsec3_hash_length(algorithm)); + uint8_t *raw_hash = (uint8_t *)knot_nsec3_next(a_rrs->rdata); + if (raw_hash == NULL) { + return KNOT_EINVAL; + } + + assert(b_name); + uint8_t b32_length = b_name[0]; + const uint8_t *b32_hash = &(b_name[1]); + int32_t written = knot_base32hex_decode(b32_hash, b32_length, raw_hash, raw_length); + if (written != raw_length) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +/*! + * \brief Connect two nodes by filling 'hash' field of NSEC3 RDATA of the first node. + * + * \param a First node. Gets modified in-place! + * \param b Second node (immediate follower of a). + * \param data Unused parameter. + * + * \return Error code, KNOT_EOK if successful. + */ +static int connect_nsec3_nodes(zone_node_t *a, zone_node_t *b, + _unused_ nsec_chain_iterate_data_t *data) +{ + assert(a); + assert(b); + assert(a->rrset_count == 1); + + return connect_nsec3_base(node_rdataset(a, KNOT_RRTYPE_NSEC3), b->owner); +} + +/*! + * \brief Connect two nodes by updating the changeset. + * + * \param a First node. + * \param b Second node. + * \param data Contains the changeset to be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +static int connect_nsec3_nodes2(zone_node_t *a, zone_node_t *b, + nsec_chain_iterate_data_t *data) +{ + assert(data); + + knot_rrset_t aorig = node_rrset(a, KNOT_RRTYPE_NSEC3); + assert(!knot_rrset_empty(&aorig)); + + // prepare a copy of NSEC3 rrsets in question + knot_rrset_t *acopy = knot_rrset_copy(&aorig, NULL); + if (acopy == NULL) { + return KNOT_ENOMEM; + } + + // connect the copied rrset + int ret = connect_nsec3_base(&acopy->rrs, b->owner); + if (ret != KNOT_EOK || knot_rrset_equal(&aorig, acopy, true)) { + knot_rrset_free(acopy, NULL); + return ret; + } + + // add the removed original and the updated copy to changeset + ret = zone_update_remove(data->update, &aorig); + if (ret == KNOT_EOK) { + ret = zone_update_add(data->update, acopy); + } + knot_rrset_free(acopy, NULL); + return ret; +} + +/*! + * \brief Replace the "next hash" field in b's NSEC3 by that in a's NSEC3, by updating the changeset. + * + * \param a A node to take the "next hash" from. + * \param b A node to put the "next hash" into. + * \param data Contains the changeset to be updated. + * + * \return KNOT_E* + */ +static int reconnect_nsec3_nodes2(zone_node_t *a, zone_node_t *b, + nsec_chain_iterate_data_t *data) +{ + assert(data); + + knot_rrset_t an = node_rrset(a, KNOT_RRTYPE_NSEC3); + assert(!knot_rrset_empty(&an)); + + knot_rrset_t bnorig = node_rrset(b, KNOT_RRTYPE_NSEC3); + assert(!knot_rrset_empty(&bnorig)); + + // prepare a copy of NSEC3 rrsets in question + knot_rrset_t *bnnew = knot_rrset_copy(&bnorig, NULL); + if (bnnew == NULL) { + return KNOT_ENOMEM; + } + + uint8_t raw_length = knot_nsec3_next_len(an.rrs.rdata); + uint8_t *a_hash = (uint8_t *)knot_nsec3_next(an.rrs.rdata); + uint8_t *bnew_hash = (uint8_t *)knot_nsec3_next(bnnew->rrs.rdata); + if (a_hash == NULL || bnew_hash == NULL || + raw_length != knot_nsec3_next_len(bnnew->rrs.rdata)) { + knot_rrset_free(bnnew, NULL); + return KNOT_ERROR; + } + memcpy(bnew_hash, a_hash, raw_length); + + int ret = zone_update_remove(data->update, &bnorig); + if (ret == KNOT_EOK) { + ret = zone_update_add(data->update, bnnew); + } + knot_rrset_free(bnnew, NULL); + return ret; +} + +/*! + * \brief Create NSEC3 node for each regular node in the zone. + * + * \param zone Zone. + * \param params NSEC3 params. + * \param ttl TTL for the created NSEC records. + * \param cds_in_apex Hint to guess apex node type bitmap: false=just DNSKEY, true=DNSKEY,CDS,CDNSKEY. + * \param nsec3_nodes Tree whereto new NSEC3 nodes will be added. + * \param update Zone update for possible NSEC removals + * + * \return Error code, KNOT_EOK if successful. + */ +static int create_nsec3_nodes(const zone_contents_t *zone, + const dnssec_nsec3_params_t *params, + uint32_t ttl, + zone_tree_t *nsec3_nodes, + zone_update_t *update) +{ + assert(zone); + assert(nsec3_nodes); + assert(update); + + zone_tree_delsafe_it_t it = { 0 }; + int result = zone_tree_delsafe_it_begin(zone->nodes, &it, false); // delsafe - removing nodes that contain only NSEC+RRSIG + + while (!zone_tree_delsafe_it_finished(&it)) { + zone_node_t *node = zone_tree_delsafe_it_val(&it); + + /*! + * Remove possible NSEC from the node. (Do not allow both NSEC + * and NSEC3 in the zone at once.) + */ + result = knot_nsec_changeset_remove(node, update); + if (result != KNOT_EOK) { + break; + } + if (node->flags & NODE_FLAGS_NONAUTH || nsec3_empty(node, params) || node->flags & NODE_FLAGS_DELETED) { + zone_tree_delsafe_it_next(&it); + continue; + } + + zone_node_t *nsec3_node; + nsec3_node = create_nsec3_node_for_node(node, zone->apex, + params, ttl); + if (!nsec3_node) { + result = KNOT_ENOMEM; + break; + } + + result = zone_tree_insert(nsec3_nodes, &nsec3_node); + if (result != KNOT_EOK) { + break; + } + + zone_tree_delsafe_it_next(&it); + } + + zone_tree_delsafe_it_free(&it); + + return result; +} + +/*! + * \brief For given dname, check if anything changed in zone_update, and recreate (possibly unconnected) NSEC3 nodes appropriately. + * + * \param update Zone update structure holding zone contents changes. + * \param params NSEC3 params. + * \param ttl TTL for newly created NSEC3 records. + * \param for_node Domain name of the node in question. + * + * \retval KNOT_ENORECORD if the NSEC3 chain shall be rather recreated completely. + * \return KNOT_EOK, KNOT_E* if any error. + */ +static int fix_nsec3_for_node(zone_update_t *update, const dnssec_nsec3_params_t *params, + uint32_t ttl, const knot_dname_t *for_node) +{ + // check if we need to do something + const zone_node_t *old_n = zone_contents_find_node(update->zone->contents, for_node); + const zone_node_t *new_n = zone_contents_find_node(update->new_cont, for_node); + + bool had_no_nsec = (old_n == NULL || old_n->nsec3_node == NULL || !(old_n->flags & NODE_FLAGS_NSEC3_NODE)); + bool shall_no_nsec = (new_n == NULL || new_n->flags & NODE_FLAGS_NONAUTH || nsec3_empty(new_n, params) || new_n->flags & NODE_FLAGS_DELETED); + + if (had_no_nsec == shall_no_nsec && node_bitmap_equal(old_n, new_n)) { + return KNOT_EOK; + } + + knot_dname_storage_t for_node_hashed; + int ret = knot_create_nsec3_owner(for_node_hashed, sizeof(for_node_hashed), + for_node, update->new_cont->apex->owner, params); + if (ret != KNOT_EOK) { + return ret; + } + + // saved hash of next node + uint8_t *next_hash = NULL; + uint8_t next_length = 0; + + // remove (all) existing NSEC3 + const zone_node_t *old_nsec3_n = zone_contents_find_nsec3_node(update->new_cont, for_node_hashed); + assert((bool)(old_nsec3_n == NULL) == had_no_nsec); + if (old_nsec3_n != NULL) { + knot_rrset_t rem_nsec3 = node_rrset(old_nsec3_n, KNOT_RRTYPE_NSEC3); + if (!knot_rrset_empty(&rem_nsec3)) { + knot_rrset_t rem_rrsig = node_rrset(old_nsec3_n, KNOT_RRTYPE_RRSIG); + ret = zone_update_remove(update, &rem_nsec3); + if (ret == KNOT_EOK && !knot_rrset_empty(&rem_rrsig)) { + ret = zone_update_remove(update, &rem_rrsig); + } + assert(update->flags & UPDATE_INCREMENTAL); // to make sure the following pointer remains valid + next_hash = (uint8_t *)knot_nsec3_next(rem_nsec3.rrs.rdata); + next_length = knot_nsec3_next_len(rem_nsec3.rrs.rdata); + } + } + + // add NSEC3 with correct bitmap + if (!shall_no_nsec && ret == KNOT_EOK) { + zone_node_t *new_nsec3_n = create_nsec3_node_for_node(new_n, update->new_cont->apex, params, ttl); + if (new_nsec3_n == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_t nsec3 = node_rrset(new_nsec3_n, KNOT_RRTYPE_NSEC3); + assert(!knot_rrset_empty(&nsec3)); + + // copy hash of next element from removed record + if (next_hash != NULL) { + uint8_t *raw_hash = (uint8_t *)knot_nsec3_next(nsec3.rrs.rdata); + uint8_t raw_length = knot_nsec3_next_len(nsec3.rrs.rdata); + assert(raw_hash != NULL); + if (raw_length != next_length) { + ret = KNOT_EMALF; + } else { + memcpy(raw_hash, next_hash, raw_length); + } + } + if (ret == KNOT_EOK) { + ret = zone_update_add(update, &nsec3); + } + binode_unify(new_nsec3_n, false, NULL); + node_free_rrsets(new_nsec3_n, NULL); + node_free(new_nsec3_n, NULL); + } + + return ret; +} + +static int fix_nsec3_nodes(zone_update_t *update, const dnssec_nsec3_params_t *params, + uint32_t ttl) +{ + assert(update); + + zone_tree_it_t it = { 0 }; + int ret = zone_tree_it_begin(update->a_ctx->node_ptrs, &it); + + while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) { + zone_node_t *n = zone_tree_it_val(&it); + ret = fix_nsec3_for_node(update, params, ttl, n->owner); + zone_tree_it_next(&it); + } + zone_tree_it_free(&it); + + return ret; +} + +static int zone_update_nsec3_nodes(zone_update_t *up, zone_tree_t *nsec3n) +{ + int ret = KNOT_EOK; + zone_tree_delsafe_it_t dit = { 0 }; + zone_tree_it_t it = { 0 }; + if (up->new_cont->nsec3_nodes == NULL) { + goto add_nsec3n; + } + ret = zone_tree_delsafe_it_begin(up->new_cont->nsec3_nodes, &dit, false); + while (ret == KNOT_EOK && !zone_tree_delsafe_it_finished(&dit)) { + zone_node_t *nold = zone_tree_delsafe_it_val(&dit); + knot_rrset_t ns3old = node_rrset(nold, KNOT_RRTYPE_NSEC3); + zone_node_t *nnew = zone_tree_get(nsec3n, nold->owner); + if (!knot_rrset_empty(&ns3old)) { + knot_rrset_t ns3new = node_rrset(nnew, KNOT_RRTYPE_NSEC3); + if (knot_rrset_equal(&ns3old, &ns3new, true)) { + node_remove_rdataset(nnew, KNOT_RRTYPE_NSEC3); + } else { + ret = knot_nsec_changeset_remove(nold, up); + } + } else if (node_rrtype_exists(nold, KNOT_RRTYPE_RRSIG)) { + ret = knot_nsec_changeset_remove(nold, up); + } + zone_tree_delsafe_it_next(&dit); + } + zone_tree_delsafe_it_free(&dit); + if (ret != KNOT_EOK) { + return ret; + } + +add_nsec3n: + ret = zone_tree_it_begin(nsec3n, &it); + while (ret == KNOT_EOK && !zone_tree_it_finished(&it)) { + zone_node_t *nnew = zone_tree_it_val(&it); + knot_rrset_t ns3new = node_rrset(nnew, KNOT_RRTYPE_NSEC3); + if (!knot_rrset_empty(&ns3new)) { + ret = zone_update_add(up, &ns3new); + } + zone_tree_it_next(&it); + } + zone_tree_it_free(&it); + return ret; +} + +/* - Public API ------------------------------------------------------------- */ + +int delete_nsec3_chain(zone_update_t *up) +{ + zone_tree_t *empty = zone_tree_create(false); + if (empty == NULL) { + return KNOT_ENOMEM; + } + int ret = zone_update_nsec3_nodes(up, empty); + zone_tree_free(&empty); + return ret; +} + +/*! + * \brief Create new NSEC3 chain, add differences from current into a changeset. + */ +int knot_nsec3_create_chain(const zone_contents_t *zone, + const dnssec_nsec3_params_t *params, + uint32_t ttl, + zone_update_t *update) +{ + assert(zone); + assert(params); + + zone_tree_t *nsec3_nodes = zone_tree_create(false); + if (!nsec3_nodes) { + return KNOT_ENOMEM; + } + + int result = create_nsec3_nodes(zone, params, ttl, nsec3_nodes, update); + if (result != KNOT_EOK) { + free_nsec3_tree(nsec3_nodes); + return result; + } + + result = knot_nsec_chain_iterate_create(nsec3_nodes, + connect_nsec3_nodes, NULL); + if (result != KNOT_EOK) { + free_nsec3_tree(nsec3_nodes); + return result; + } + + result = zone_update_nsec3_nodes(update, nsec3_nodes); + + free_nsec3_tree(nsec3_nodes); + + return result; +} + +int knot_nsec3_fix_chain(zone_update_t *update, + const dnssec_nsec3_params_t *params, + uint32_t ttl) +{ + assert(update); + assert(params); + + // ensure that the salt has not changed + if (!knot_nsec3param_uptodate(update->new_cont, params)) { + int ret = knot_nsec3param_update(update, params, ttl); + if (ret != KNOT_EOK) { + return ret; + } + return knot_nsec3_create_chain(update->new_cont, params, ttl, update); + } + + int ret = fix_nsec3_nodes(update, params, ttl); + if (ret != KNOT_EOK) { + return ret; + } + + ret = zone_adjust_contents(update->new_cont, NULL, adjust_cb_void, false, true, 1, update->a_ctx->nsec3_ptrs); + if (ret != KNOT_EOK) { + return ret; + } + + // ensure that nsec3 node for zone root is in list of changed nodes + const zone_node_t *nsec3_for_root = NULL, *unused; + ret = zone_contents_find_nsec3_for_name(update->new_cont, update->zone->name, &nsec3_for_root, &unused); + if (ret >= 0) { + assert(ret == ZONE_NAME_FOUND); + assert(!(nsec3_for_root->flags & NODE_FLAGS_DELETED)); + assert(!(binode_counterpart((zone_node_t *)nsec3_for_root)->flags & NODE_FLAGS_DELETED)); + ret = zone_tree_insert(update->a_ctx->nsec3_ptrs, (zone_node_t **)&nsec3_for_root); + } + if (ret != KNOT_EOK) { + return ret; + } + + nsec_chain_iterate_data_t data = { ttl, update, KNOT_RRTYPE_NSEC3 }; + + ret = knot_nsec_chain_iterate_fix(update->a_ctx->nsec3_ptrs, + connect_nsec3_nodes2, reconnect_nsec3_nodes2, &data); + + return ret; +} + +int knot_nsec3_check_chain(zone_update_t *update, const dnssec_nsec3_params_t *params) +{ + nsec_chain_iterate_data_t data = { 0, update, KNOT_RRTYPE_NSEC3, params }; + + int ret = nsec_check_bitmaps(update->new_cont->nodes, &data); + if (ret != KNOT_EOK) { + return ret; + } + + return knot_nsec_chain_iterate_create(update->new_cont->nsec3_nodes, + nsec_check_connect_nodes, &data); +} + +int knot_nsec3_check_chain_fix(zone_update_t *update, const dnssec_nsec3_params_t *params) +{ + nsec_chain_iterate_data_t data = { 0, update, KNOT_RRTYPE_NSEC3, params }; + + int ret = nsec_check_bitmaps(update->a_ctx->node_ptrs, &data); + if (ret != KNOT_EOK) { + return ret; + } + + ret = nsec_check_bitmaps(update->a_ctx->adjust_ptrs, &data); // adjust_ptrs contain also NSEC3-nodes. See check_nsec_bitmap() how this is handled. + if (ret != KNOT_EOK) { + return ret; + } + + return nsec_check_new_connects(update->a_ctx->nsec3_ptrs, &data); +} |