summaryrefslogtreecommitdiff
path: root/src/libknot/zone/zone-contents.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/zone/zone-contents.c')
-rw-r--r--src/libknot/zone/zone-contents.c2396
1 files changed, 2396 insertions, 0 deletions
diff --git a/src/libknot/zone/zone-contents.c b/src/libknot/zone/zone-contents.c
new file mode 100644
index 0000000..d550728
--- /dev/null
+++ b/src/libknot/zone/zone-contents.c
@@ -0,0 +1,2396 @@
+/* Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+
+#include "zone/zone-contents.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common/base32hex.h"
+#include "consts.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+typedef struct {
+ void (*func)(knot_node_t *, void *);
+ void *data;
+} knot_zone_tree_func_t;
+
+typedef struct {
+ knot_node_t *first_node;
+ knot_zone_contents_t *zone;
+ knot_node_t *previous_node;
+ int check_ver;
+} knot_zone_adjust_arg_t;
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_tree_apply(knot_zone_tree_node_t *node,
+ void *data)
+{
+ if (node == NULL || data == NULL) {
+ return;
+ }
+
+ knot_zone_tree_func_t *f = (knot_zone_tree_func_t *)data;
+ f->func(node->node, f->data);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Checks if the given node can be inserted into the given zone.
+ *
+ * Checks if both the arguments are non-NULL and if the owner of the node
+ * belongs to the zone (i.e. is a subdomain of the zone apex).
+ *
+ * \param zone Zone to which the node is going to be inserted.
+ * \param node Node to check.
+ *
+ * \retval KNOT_EOK if both arguments are non-NULL and the node belongs to the
+ * zone.
+ * \retval KNOT_EBADARG if either of the arguments is NULL.
+ * \retval KNOT_EBADZONE if the node does not belong to the zone.
+ */
+static int knot_zone_contents_check_node(
+ const knot_zone_contents_t *contents, const knot_node_t *node)
+{
+ if (contents == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // assert or just check??
+ assert(contents->apex != NULL);
+
+ if (!knot_dname_is_subdomain(node->owner,
+ knot_node_owner(contents->apex))) {
+dbg_zone_exec(
+ char *node_owner = knot_dname_to_str(knot_node_owner(node));
+ char *apex_owner = knot_dname_to_str(contents->apex->owner);
+ dbg_zone("zone: Trying to insert foreign node to a "
+ "zone. Node owner: %s, zone apex: %s\n",
+ node_owner, apex_owner);
+ free(node_owner);
+ free(apex_owner);
+);
+ return KNOT_EBADZONE;
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Destroys all RRSets in a node.
+ *
+ * This function is designed to be used in the tree-iterating functions.
+ *
+ * \param node Node to destroy RRSets from.
+ * \param data Unused parameter.
+ */
+static void knot_zone_contents_destroy_node_rrsets_from_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ int free_rdata_dnames = (int)((intptr_t)data);
+ knot_node_free_rrsets(tnode->node, free_rdata_dnames);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Destroys node owner.
+ *
+ * This function is designed to be used in the tree-iterating functions.
+ *
+ * \param node Node to destroy the owner of.
+ * \param data Unused parameter.
+ */
+static void knot_zone_contents_destroy_node_owner_from_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ UNUSED(data);
+ /*!< \todo change completely! */
+ knot_node_free(&tnode->node, 0, 0);
+}
+
+/*!
+ * \brief Finds and sets wildcard child for given node's owner.
+ *
+ * \param zone Current zone.
+ * \param node Node to be used.
+ */
+static void find_and_set_wildcard_child(knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ knot_dname_t *chopped = knot_dname_left_chop(node->owner);
+ assert(chopped);
+ knot_node_t *wildcard_parent;
+ wildcard_parent =
+ knot_zone_contents_get_node(zone, chopped);
+
+ knot_dname_free(&chopped);
+
+ assert(wildcard_parent); /* it *has* to be there */
+
+ knot_node_set_wildcard_child(wildcard_parent, node);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts one RDATA item by replacing domain name by one present in the
+ * zone.
+ *
+ * This function tries to find the domain name in the zone. If the name is not
+ * in the zone, it does nothing. If it is there, it destroys the domain name
+ * stored in the RDATA item and replaces it by pointer to the domain name from
+ * the zone.
+ *
+ * \warning Call this function only with RDATA items which store domain names,
+ * otherwise the behaviour is undefined.
+ *
+ * \param rdata RDATA where the item is located.
+ * \param zone Zone to which the RDATA belongs.
+ * \param pos Position of the RDATA item in the RDATA.
+ */
+static void knot_zone_contents_adjust_rdata_item(knot_rdata_t *rdata,
+ knot_zone_contents_t *zone,
+ knot_node_t *node,
+ int pos)
+{
+ return;
+ const knot_rdata_item_t *dname_item
+ = knot_rdata_item(rdata, pos);
+
+ assert(dname_item);
+
+ if (dname_item != NULL) {
+ knot_dname_t *dname = dname_item->dname;
+ const knot_node_t *n = NULL;
+ const knot_node_t *closest_encloser = NULL;
+ const knot_node_t *prev = NULL;
+
+ if (knot_dname_is_wildcard(dname)) {
+ find_and_set_wildcard_child(zone, node);
+ }
+
+ int ret = knot_zone_contents_find_dname(zone, dname, &n,
+ &closest_encloser, &prev);
+
+ // n = knot_zone_find_node(zone, dname);
+
+ if (ret == KNOT_EBADARG || ret == KNOT_EBADZONE) {
+ // TODO: do some cleanup if needed
+ return;
+ }
+
+ assert(ret != KNOT_ZONE_NAME_FOUND
+ || n == closest_encloser);
+
+ if (ret != KNOT_ZONE_NAME_FOUND
+ && (closest_encloser != NULL)) {
+ dbg_zone("Saving closest encloser to RDATA.\n");
+ // save pointer to the closest encloser
+ knot_rdata_item_t *item =
+ knot_rdata_get_item(rdata, pos);
+ assert(item->dname != NULL);
+ assert(item->dname->node == NULL);
+ //skip_insert(list, (void *)item->dname,
+ // (void *)closest_encloser->owner, NULL);
+ item->dname->node = closest_encloser->owner->node;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts all RDATA in the given RRSet by replacing domain names by ones
+ * present in the zone.
+ *
+ * This function selects the RDATA items containing a domain name (according to
+ * RR type descriptor of the RRSet's type and adjusts the item using
+ * knot_zone_adjust_rdata_item().
+ *
+ * \param rrset RRSet to adjust RDATA in.
+ * \param zone Zone to which the RRSet belongs.
+ */
+static void knot_zone_contents_adjust_rdata_in_rrset(knot_rrset_t *rrset,
+ knot_zone_contents_t *zone,
+ knot_node_t *node)
+{
+ uint16_t type = knot_rrset_type(rrset);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc);
+
+ knot_rdata_t *rdata_first = knot_rrset_get_rdata(rrset);
+ knot_rdata_t *rdata = rdata_first;
+
+ if (rdata == NULL) {
+ return;
+ }
+
+ while (rdata->next != rdata_first) {
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Adjusting domain name at "
+ "position %d of RDATA of record with owner "
+ "%s and type %s.\n",
+ i, rrset->owner->name,
+ knot_rrtype_to_string(type));
+
+ knot_zone_contents_adjust_rdata_item(rdata,
+ zone,
+ node,
+ i);
+ }
+ }
+ rdata = rdata->next;
+ }
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (desc->wireformat[i]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || desc->wireformat[i]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Adjusting domain name at "
+ "position %d of RDATA of record with owner "
+ "%s and type %s.\n",
+ i, rrset->owner->name,
+ knot_rrtype_to_string(type));
+
+ knot_zone_contents_adjust_rdata_item(rdata, zone,
+ node, i);
+ }
+ }
+
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts all RRSets in the given node by replacing domain names in
+ * RDATA by ones present in the zone.
+ *
+ * This function just calls knot_zone_adjust_rdata_in_rrset() for all RRSets
+ * in the node (including all RRSIG RRSets).
+ *
+ * \param node Zone node to adjust the RRSets in.
+ * \param zone Zone to which the node belongs.
+ */
+static void knot_zone_contents_adjust_rrsets(knot_node_t *node,
+ knot_zone_contents_t *zone)
+{
+ //return;
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ short count = knot_node_rrset_count(node);
+
+ assert(count == 0 || rrsets != NULL);
+
+ for (int r = 0; r < count; ++r) {
+ assert(rrsets[r] != NULL);
+ dbg_zone("Adjusting next RRSet.\n");
+ knot_zone_contents_adjust_rdata_in_rrset(rrsets[r], zone,
+ node);
+ knot_rrset_t *rrsigs = rrsets[r]->rrsigs;
+ if (rrsigs != NULL) {
+ dbg_zone("Adjusting next RRSIGs.\n");
+ knot_zone_contents_adjust_rdata_in_rrset(rrsigs,
+ zone,
+ node);
+ }
+ }
+
+ free(rrsets);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts zone node for faster query processing.
+ *
+ * - Adjusts RRSets in the node (see knot_zone_adjust_rrsets()).
+ * - Marks the node as delegation point or non-authoritative (below a zone cut)
+ * if applicable.
+ * - Stores reference to corresponding NSEC3 node if applicable.
+ *
+ * \param node Zone node to adjust.
+ * \param zone Zone the node belongs to.
+ */
+static void knot_zone_contents_adjust_node(knot_node_t *node,
+ knot_zone_contents_t *zone,
+ int check_ver)
+{
+
+dbg_zone_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_zone("----- Adjusting node %s -----\n", name);
+ free(name);
+);
+
+ // adjust domain names in RDATA
+ knot_zone_contents_adjust_rrsets(node, zone);
+
+dbg_zone_exec(
+ if (knot_node_parent(node, 1)) {
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_node_parent(node, check_ver)));
+ dbg_zone("Parent: %s\n", name);
+ dbg_zone("Parent is delegation point: %s\n",
+ knot_node_is_deleg_point(knot_node_parent(node, check_ver))
+ ? "yes" : "no");
+ dbg_zone("Parent is non-authoritative: %s\n",
+ knot_node_is_non_auth(knot_node_parent(node, check_ver))
+ ? "yes" : "no");
+ free(name);
+ } else {
+ dbg_zone("No parent!\n");
+ }
+);
+ // delegation point / non-authoritative node
+ if (knot_node_parent(node, check_ver)
+ && (knot_node_is_deleg_point(knot_node_parent(node, check_ver))
+ || knot_node_is_non_auth(knot_node_parent(node, check_ver)))) {
+ knot_node_set_non_auth(node);
+ } else if (knot_node_rrset(node, KNOT_RRTYPE_NS) != NULL
+ && node != zone->apex) {
+ knot_node_set_deleg_point(node);
+ }
+
+ // authorative node?
+// if (!knot_node_is_non_auth(node)) {
+ zone->node_count++;
+// }
+
+ // assure that owner has proper node
+ if (knot_dname_node(knot_node_owner(node), 0) == NULL) {
+ knot_dname_set_node(knot_node_get_owner(node), node);
+ knot_dname_set_node(knot_node_get_owner(node), node);
+ }
+
+ // NSEC3 node (only if NSEC3 tree is not empty)
+ const knot_node_t *prev;
+ const knot_node_t *nsec3;
+ int match = knot_zone_contents_find_nsec3_for_name(zone,
+ knot_node_owner(node),
+ &nsec3, &prev, check_ver);
+ if (match != KNOT_ZONE_NAME_FOUND) {
+ nsec3 = NULL;
+ }
+
+ knot_node_set_nsec3_node(node, (knot_node_t *)nsec3);
+
+ dbg_zone("Set flags to the node: \n");
+ dbg_zone("Delegation point: %s\n",
+ knot_node_is_deleg_point(node) ? "yes" : "no");
+ dbg_zone("Non-authoritative: %s\n",
+ knot_node_is_non_auth(node) ? "yes" : "no");
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts zone node for faster query processing.
+ *
+ * This function is just a wrapper over knot_zone_adjust_node() to be used
+ * in tree-traversing functions.
+ *
+ * \param node Zone node to adjust.
+ * \param data Zone the node belongs to.
+ */
+static void knot_zone_contents_adjust_node_in_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(data != NULL);
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+ knot_node_t *node = tnode->node;
+ knot_node_set_previous(node, args->previous_node);
+ args->previous_node = node;
+ if (args->first_node == NULL) {
+ args->first_node = node;
+ }
+ knot_zone_contents_t *zone = args->zone;
+
+ knot_zone_contents_adjust_node(node, zone, args->check_ver);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adjusts NSEC3 node for faster query processing.
+ *
+ * This function is just a wrapper over knot_zone_adjust_nsec3_node() to be
+ * used in tree-traversing functions.
+ *
+ * \param node Zone node to adjust.
+ * \param data Zone the node belongs to.
+ */
+static void knot_zone_contents_adjust_nsec3_node_in_tree(
+ knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(data != NULL);
+ assert(tnode != NULL);
+ assert(tnode->node != NULL);
+
+ knot_zone_adjust_arg_t *args = (knot_zone_adjust_arg_t *)data;
+ knot_node_t *node = tnode->node;
+ knot_node_set_previous(node, args->previous_node);
+ args->previous_node = node;
+ if (args->first_node == NULL) {
+ args->first_node = node;
+ }
+
+ /* Not needed anymore. */
+// knot_zone_contents_t *zone = args->zone;
+// knot_zone_contents_adjust_nsec3_node(node, zone, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a NSEC3 hashed name for the given domain name.
+ *
+ * \note The zone's NSEC3PARAM record must be parsed prior to calling this
+ * function (see knot_zone_load_nsec3param()).
+ *
+ * \param zone Zone from which to take the NSEC3 parameters.
+ * \param name Domain name to hash.
+ * \param nsec3_name Hashed name.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENSEC3PAR
+ * \retval KNOT_ECRYPTO
+ * \retval KNOT_ERROR if an error occured while creating a new domain name
+ * from the hash or concatenating it with the zone name.
+ */
+static int knot_zone_contents_nsec3_name(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ knot_dname_t **nsec3_name)
+{
+ assert(nsec3_name != NULL);
+
+ *nsec3_name = NULL;
+
+ const knot_nsec3_params_t *nsec3_params =
+ knot_zone_contents_nsec3params(zone);
+
+ if (nsec3_params == NULL) {
+dbg_zone_exec(
+ char *n = knot_dname_to_str(zone->apex->owner);
+ dbg_zone("No NSEC3PARAM for zone %s.\n", n);
+ free(n);
+);
+ return KNOT_ENSEC3PAR;
+ }
+
+ uint8_t *hashed_name = NULL;
+ size_t hash_size = 0;
+
+dbg_zone_exec(
+ char *n = knot_dname_to_str(name);
+ dbg_zone("Hashing name %s.\n", n);
+ free(n);
+);
+
+ int res = knot_nsec3_sha1(nsec3_params, knot_dname_name(name),
+ knot_dname_size(name), &hashed_name,
+ &hash_size);
+
+ if (res != 0) {
+ char *n = knot_dname_to_str(name);
+ dbg_zone("Error while hashing name %s.\n", n);
+ free(n);
+ return KNOT_ECRYPTO;
+ }
+
+ dbg_zone("Hash: ");
+ dbg_zone_hex((char *)hashed_name, hash_size);
+ dbg_zone("\n");
+
+ char *name_b32 = NULL;
+ size_t size = base32hex_encode_alloc((char *)hashed_name, hash_size,
+ &name_b32);
+
+ if (size == 0) {
+ char *n = knot_dname_to_str(name);
+ dbg_zone("Error while encoding hashed name %s to "
+ "base32.\n", n);
+ free(n);
+ if (name_b32 != NULL) {
+ free(name_b32);
+ }
+ return KNOT_ECRYPTO;
+ }
+
+ assert(name_b32 != NULL);
+ free(hashed_name);
+
+ dbg_zone("Base32-encoded hash: %s\n", name_b32);
+
+ /* Will be returned to caller, make sure it is released after use. */
+ *nsec3_name = knot_dname_new_from_str(name_b32, size, NULL);
+
+ free(name_b32);
+
+ if (*nsec3_name == NULL) {
+ dbg_zone("Error while creating domain name for hashed"
+ " name.\n");
+ return KNOT_ERROR;
+ }
+
+ assert(zone->apex->owner != NULL);
+ knot_dname_t *ret = knot_dname_cat(*nsec3_name, zone->apex->owner);
+
+ if (ret == NULL) {
+ dbg_zone("Error while creating NSEC3 domain name for "
+ "hashed name.\n");
+ knot_dname_release(*nsec3_name);
+ return KNOT_ERROR;
+ }
+
+ assert(ret == *nsec3_name);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find the given domain name in the zone tree.
+ *
+ * \param zone Zone to search in.
+ * \param name Domain name to find.
+ * \param node Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a name in canonical order, regardless if the name
+ * is in the zone or not).
+ *
+ * \retval <> 0 if the domain name was found. In such case \a node holds the
+ * zone node with \a name as its owner. \a previous is set
+ * properly.
+ * \retval 0 if the domain name was not found. \a node may hold any (or none)
+ * node. \a previous is set properly.
+ */
+static int knot_zone_contents_find_in_tree(knot_zone_tree_t *tree,
+ const knot_dname_t *name,
+ knot_node_t **node,
+ knot_node_t **previous)
+{
+ assert(tree != NULL);
+ assert(name != NULL);
+ assert(node != NULL);
+ assert(previous != NULL);
+
+ knot_node_t *found = NULL, *prev = NULL;
+// knot_node_t *found2 = NULL, *prev2 = NULL;
+
+ int exact_match = knot_zone_tree_get_less_or_equal(
+ tree, name, &found, &prev, 1);
+
+// assert(prev != NULL);
+ assert(exact_match >= 0);
+ *node = found;
+ *previous = prev;
+
+// if (prev == NULL) {
+// // either the returned node is the root of the tree, or it is
+// // the leftmost node in the tree; in both cases node was found
+// // set the previous node of the found node
+// assert(exact_match);
+// assert(found != NULL);
+// *previous = knot_node_get_previous(found, 1);
+// } else {
+// // otherwise check if the previous node is not an empty
+// // non-terminal
+// *previous = (knot_node_rrset_count(prev) == 0)
+// ? knot_node_get_previous(prev, 1)
+// : prev;
+// }
+
+ return exact_match;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_contents_node_to_hash(knot_zone_tree_node_t *tnode,
+ void *data)
+{
+ assert(tnode != NULL && tnode->node != NULL
+ && tnode->node->owner != NULL && data != NULL);
+
+ knot_node_t *node = tnode->node;
+
+ knot_zone_contents_t *zone = (knot_zone_contents_t *)data;
+ /*
+ * By the original approach, only authoritative nodes and delegation
+ * points should be added to the hash table, but currently, all nodes
+ * are being added when the zone is created (don't know why actually:),
+ * so we will do no distinction here neither.
+ */
+
+#ifdef USE_HASH_TABLE
+//dbg_zone_exec(
+// char *name = knot_dname_to_str(node->owner);
+// dbg_zone("Adding node with owner %s to hash table.\n", name);
+// free(name);
+//);
+ //assert(zone->table != NULL);
+ // add the node also to the hash table if authoritative, or deleg. point
+ if (zone->table != NULL
+ && ck_insert_item(zone->table,
+ (const char *)node->owner->name,
+ node->owner->size, (void *)node) != 0) {
+ dbg_zone("Error inserting node into hash table!\n");
+ }
+#endif
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_dnames_from_rdata_to_table(
+ knot_dname_table_t *table, knot_rdata_t *rdata,
+ knot_rrtype_descriptor_t *d)
+{
+ unsigned int count = knot_rdata_item_count(rdata);
+ int rc = 0;
+ assert(count <= d->length);
+ // for each RDATA item
+ for (unsigned int j = 0; j < count; ++j) {
+ if (d->wireformat[j]
+ == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || d->wireformat[j]
+ == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || d->wireformat[j]
+ == KNOT_RDATA_WF_LITERAL_DNAME) {
+ dbg_zone("Saving dname from "
+ "rdata to dname table"
+ ".\n");
+ rc = knot_dname_table_add_dname_check(table,
+ &knot_rdata_get_item(rdata, j)->dname);
+ if (rc < 0) {
+ dbg_zone("Error: %s\n",
+ knot_strerror(rc));
+ return rc;
+ }
+ }
+ }
+
+ dbg_zone("RDATA OK.\n");
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_dnames_from_rrset_to_table(
+ knot_dname_table_t *table, knot_rrset_t *rrset, int replace_owner,
+ knot_dname_t *owner)
+{
+ assert(table != NULL && rrset != NULL && owner != NULL);
+
+ if (replace_owner) {
+ // discard the old owner and replace it with the new
+ knot_rrset_set_owner(rrset, owner);
+ }
+ dbg_zone("RRSet owner: %p\n", rrset->owner);
+
+ knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(
+ knot_rrset_type(rrset));
+ if (desc == NULL) {
+ // not recognized RR type, ignore
+ dbg_zone("RRSet type not recognized.\n");
+ return KNOT_EOK;
+ }
+ // for each RDATA in RRSet
+ knot_rdata_t *rdata = knot_rrset_get_rdata(rrset);
+ while (rdata != NULL) {
+ int rc = knot_zone_contents_dnames_from_rdata_to_table(table,
+ rdata, desc);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+
+ rdata = knot_rrset_rdata_get_next(rrset, rdata);
+ }
+
+ dbg_zone("RRSet OK.\n");
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_contents_dnames_from_node_to_table(
+ knot_dname_table_t *table, knot_node_t *node)
+{
+ /*
+ * Assuming that all the RRSets have the same owner as the node.
+ */
+
+ // insert owner
+ char *name = knot_dname_to_str(node->owner);
+ dbg_zone("Node owner before inserting to dname table: %p.\n",
+ node->owner);
+ dbg_zone("Node owner before inserting to dname table: %s.\n",
+ name);
+ free(name);
+ //knot_dname_t *old_owner = node->owner;
+ int rc = knot_dname_table_add_dname_check(table, &node->owner);
+ if (rc < 0) {
+ dbg_zone("Failed to add dname to dname table.\n");
+ return rc;
+ }
+ int replace_owner = (rc > 0);
+ dbg_zone("Node owner after inserting to dname table: %p.\n",
+ node->owner);
+ name = knot_dname_to_str(node->owner);
+ dbg_zone("Node owner after inserting to dname table: %s.\n",
+ name);
+ free(name);
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ // for each RRSet
+ for (int i = 0; i < knot_node_rrset_count(node); ++i) {
+ dbg_zone("Inserting RRSets from node to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(table,
+ rrsets[i], replace_owner, node->owner);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ }
+
+ free(rrsets);
+
+ dbg_zone("Node OK\n");
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
+ uint node_count,
+ int use_domain_table,
+ struct knot_zone *zone)
+{
+ knot_zone_contents_t *contents = (knot_zone_contents_t *)
+ calloc(1, sizeof(knot_zone_contents_t));
+ if (contents == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+// printf("created cont: %p (%s)\n",
+// contents, knot_dname_to_str(apex->owner));
+
+ contents->apex = apex;
+ contents->zone = zone;
+ knot_node_set_zone(apex, zone);
+
+ dbg_zone("Creating tree for normal nodes.\n");
+ contents->nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ goto cleanup;
+ }
+
+ dbg_zone("Creating tree for NSEC3 nodes.\n");
+ contents->nsec3_nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nsec3_nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ goto cleanup;
+ }
+
+ if (use_domain_table) {
+ dbg_zone("Creating domain name table.\n");
+ contents->dname_table = knot_dname_table_new();
+ if (contents->dname_table == NULL) {
+ ERR_ALLOC_FAILED;
+ goto cleanup;
+ }
+ } else {
+ contents->dname_table = NULL;
+ }
+
+ contents->node_count = node_count;
+
+ /* Initialize NSEC3 params */
+ dbg_zone("Initializing NSEC3 parameters.\n");
+ contents->nsec3_params.algorithm = 0;
+ contents->nsec3_params.flags = 0;
+ contents->nsec3_params.iterations = 0;
+ contents->nsec3_params.salt_length = 0;
+ contents->nsec3_params.salt = NULL;
+
+ dbg_zone("Initializing zone trees.\n");
+ if (knot_zone_tree_init(contents->nodes) != KNOT_EOK
+ || knot_zone_tree_init(contents->nsec3_nodes) != KNOT_EOK) {
+ goto cleanup;
+ }
+
+ dbg_zone("Inserting apex into the zone tree.\n");
+ if (knot_zone_tree_insert(contents->nodes, apex) != KNOT_EOK) {
+ dbg_zone("Failed to insert apex to the zone tree.\n");
+ goto cleanup;
+ }
+
+#ifdef USE_HASH_TABLE
+ if (contents->node_count > 0) {
+ dbg_zone("Creating hash table.\n");
+ contents->table = ck_create_table(contents->node_count);
+ if (contents->table == NULL) {
+ goto cleanup;
+ }
+
+ // insert the apex into the hash table
+ dbg_zone("Inserting apex into the hash table.\n");
+ if (ck_insert_item(contents->table,
+ (const char *)knot_dname_name(
+ knot_node_owner(apex)),
+ knot_dname_size(knot_node_owner(apex)),
+ (void *)apex) != 0) {
+ ck_destroy_table(&contents->table, NULL, 0);
+ goto cleanup;
+ }
+ } else {
+ contents->table = NULL;
+ }
+#endif
+
+ // insert names from the apex to the domain table
+ if (use_domain_table) {
+ dbg_zone("Inserting names from apex to table.\n");
+ int rc = knot_zone_contents_dnames_from_node_to_table(
+ contents->dname_table, apex);
+ if (rc != KNOT_EOK) {
+ ck_destroy_table(&contents->table, NULL, 0);
+ goto cleanup;
+ }
+ }
+
+ return contents;
+
+cleanup:
+ dbg_zone("Cleaning up.\n");
+ free(contents->dname_table);
+ free(contents->nodes);
+ free(contents->nsec3_nodes);
+ free(contents);
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//short knot_zone_contents_generation(const knot_zone_contents_t *zone)
+//{
+// return zone->generation;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_gen_is_old(const knot_zone_contents_t *contents)
+{
+ return (contents->generation == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_gen_is_new(const knot_zone_contents_t *contents)
+{
+ return (contents->generation == 1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_gen_is_finished(const knot_zone_contents_t *contents)
+{
+ return (contents->generation == -1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+//void knot_zone_contents_switch_generation(knot_zone_contents_t *zone)
+//{
+// zone->generation = 1 - zone->generation;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_set_gen_old(knot_zone_contents_t *contents)
+{
+ contents->generation = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_set_gen_new(knot_zone_contents_t *contents)
+{
+ contents->generation = 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_set_gen_new_finished(knot_zone_contents_t *contents)
+{
+ contents->generation = -1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_zone_contents_class(const knot_zone_contents_t *contents)
+{
+ if (contents == NULL || contents->apex == NULL
+ || knot_node_rrset(contents->apex, KNOT_RRTYPE_SOA) == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_rrset_class(knot_node_rrset(contents->apex,
+ KNOT_RRTYPE_SOA));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_node(knot_zone_contents_t *zone,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table)
+{
+ if (zone == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret = 0;
+ if ((ret = knot_zone_contents_check_node(zone, node)) != 0) {
+ return ret;
+ }
+
+ ret = knot_zone_tree_insert(zone->nodes, node);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert node into zone tree.\n");
+ return ret;
+ }
+
+#ifdef USE_HASH_TABLE
+ char *name = knot_dname_to_str(node->owner);
+// dbg_zone("Adding node with owner %s to hash table.\n", name);
+ free(name);
+ //assert(zone->table != NULL);
+ // add the node also to the hash table if authoritative, or deleg. point
+ if (zone->table != NULL
+ && ck_insert_item(zone->table,
+ (const char *)node->owner->name,
+ node->owner->size, (void *)node) != 0) {
+ dbg_zone("Error inserting node into hash table!\n");
+ /*! \todo Remove the node from the tree. */
+ return KNOT_EHASH;
+ }
+#endif
+ assert(knot_zone_contents_find_node(zone, node->owner));
+
+ if (use_domain_table) {
+ ret = knot_zone_contents_dnames_from_node_to_table(
+ zone->dname_table, node);
+ if (ret != KNOT_EOK) {
+ /*! \todo Remove node from the tree and hash table.*/
+ dbg_zone("Failed to add dnames into table.\n");
+ return ret;
+ }
+ }
+
+ knot_node_set_zone(node, zone->zone);
+
+ if (!create_parents) {
+ return KNOT_EOK;
+ }
+
+ dbg_zone("Creating parents of the node.\n");
+
+ knot_dname_t *chopped =
+ knot_dname_left_chop(knot_node_owner(node));
+ if (knot_dname_compare(knot_node_owner(zone->apex), chopped) == 0) {
+ dbg_zone("Zone apex is the parent.\n");
+ knot_node_set_parent(node, zone->apex);
+ } else {
+ knot_node_t *next_node;
+ while ((next_node
+ = knot_zone_contents_get_node(zone, chopped)) == NULL) {
+ /* Adding new dname to zone + add to table. */
+ dbg_zone("Creating new node.\n");
+ next_node = knot_node_new(chopped, NULL, flags);
+ if (next_node == NULL) {
+ /* Directly discard. */
+ knot_dname_free(&chopped);
+ return KNOT_ENOMEM;
+ }
+ if (use_domain_table) {
+ ret =
+ knot_zone_contents_dnames_from_node_to_table(
+ zone->dname_table, next_node);
+ if (ret != KNOT_EOK) {
+ /*! \todo Will next_node leak? */
+ knot_dname_release(chopped);
+ return ret;
+ }
+ }
+
+ if (next_node->owner != chopped) {
+ /* Node owner was in RDATA */
+ chopped = next_node->owner;
+ }
+
+ assert(knot_zone_contents_find_node(zone, chopped)
+ == NULL);
+ assert(knot_node_owner(next_node) == chopped);
+
+ dbg_zone("Inserting new node to zone tree.\n");
+// TREE_INSERT(zone->tree, knot_node, avl, next_node);
+
+ ret = knot_zone_tree_insert(zone->nodes,
+ next_node);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert new node "
+ "to zone tree.\n");
+ /*! \todo Delete the node?? */
+ /* Directly discard. */
+ knot_dname_release(chopped);
+ return ret;
+ }
+
+#ifdef USE_HASH_TABLE
+dbg_zone_exec(
+ char *name = knot_dname_to_str(
+ knot_node_owner(next_node));
+ dbg_zone("Adding new node with owner %s to "
+ "hash table.\n", name);
+ free(name);
+);
+
+ if (zone->table != NULL
+ && ck_insert_item(zone->table,
+ (const char *)knot_dname_name(
+ knot_node_owner(next_node)),
+ knot_dname_size(knot_node_owner(next_node)),
+ (void *)next_node) != 0) {
+ dbg_zone("Error inserting node into "
+ "hash table!\n");
+ /*! \todo Delete the node?? */
+ /* Directly discard. */
+ knot_dname_release(chopped);
+ return KNOT_EHASH;
+ }
+
+ // set parent
+ knot_node_set_parent(node, next_node);
+
+ // set zone
+ knot_node_set_zone(next_node, zone->zone);
+
+ // check if the node is not wildcard child of the parent
+ if (knot_dname_is_wildcard(
+ knot_node_owner(node))) {
+ knot_node_set_wildcard_child(next_node, node);
+ }
+#endif
+ dbg_zone("Next parent.\n");
+ node = next_node;
+ knot_dname_t *chopped_last = chopped;
+ chopped = knot_dname_left_chop(chopped);
+
+ /* Release last chop, reference is already stored
+ * in next_node.
+ */
+ knot_dname_release(chopped_last);
+
+ }
+ // set the found parent (in the zone) as the parent of the last
+ // inserted node
+ assert(knot_node_parent(node, 0) == NULL);
+ knot_node_set_parent(node, next_node);
+
+ dbg_zone("Created all parents.\n");
+ }
+
+ /* Directly discard. */
+ /*! \todo This may be double-release. */
+ knot_dname_release(chopped);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_rrset(knot_zone_contents_t *zone,
+ knot_rrset_t *rrset, knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
+{
+ if (zone == NULL || rrset == NULL || zone->apex == NULL
+ || zone->apex->owner == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // check if the RRSet belongs to the zone
+ if (knot_dname_compare(knot_rrset_owner(rrset),
+ zone->apex->owner) != 0
+ && !knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ zone->apex->owner)) {
+ return KNOT_EBADZONE;
+ }
+
+ if ((*node) == NULL
+ && (*node = knot_zone_contents_get_node(zone,
+ knot_rrset_owner(rrset))) == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ assert(*node != NULL);
+
+ // add all domain names from the RRSet to domain name table
+ int rc;
+
+ /*! \todo REMOVE RRSET */
+ rc = knot_node_add_rrset(*node, rrset,
+ dupl == KNOT_RRSET_DUPL_MERGE);
+ if (rc < 0) {
+ dbg_zone("Failed to add RRSet to node.\n");
+ return rc;
+ }
+
+ int ret = rc;
+
+ if (use_domain_table) {
+ dbg_zone("Saving RRSet to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(
+ zone->dname_table, rrset, 0, (*node)->owner);
+ if (rc != KNOT_EOK) {
+ dbg_zone("Error saving domain names from "
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent "
+ "state.\n");
+ // WARNING: the zone is not in consistent state now -
+ // there may be domain names in it that are not inserted
+ // into the domain table
+ return rc;
+ }
+ }
+
+ // replace RRSet's owner with the node's owner (that is already in the
+ // table)
+ /*! \todo Do even if domain table is not used?? */
+ if (ret == KNOT_EOK && rrset->owner != (*node)->owner) {
+ knot_rrset_set_owner(rrset, (*node)->owner);
+ }
+
+ dbg_zone("RRSet OK.\n");
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_rrsigs(knot_zone_contents_t *zone,
+ knot_rrset_t *rrsigs,
+ knot_rrset_t **rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
+{
+ if (zone == NULL || rrsigs == NULL || rrset == NULL || node == NULL
+ || zone->apex == NULL || zone->apex->owner == NULL) {
+dbg_zone_exec(
+ dbg_zone("Parameters: zone=%p, rrsigs=%p, rrset=%p, "
+ "node=%p\n", zone, rrsigs, rrset, node);
+ if (zone != NULL) {
+ dbg_zone("zone->apex=%p\n", zone->apex);
+ if (zone->apex != NULL) {
+ dbg_zone("zone->apex->owner=%p\n",
+ zone->apex->owner);
+ }
+ }
+);
+ return KNOT_EBADARG;
+ }
+
+ // check if the RRSet belongs to the zone
+ if (*rrset != NULL
+ && knot_dname_compare(knot_rrset_owner(*rrset),
+ zone->apex->owner) != 0
+ && !knot_dname_is_subdomain(knot_rrset_owner(*rrset),
+ zone->apex->owner)) {
+ return KNOT_EBADZONE;
+ }
+
+ // check if the RRSIGs belong to the RRSet
+ if (*rrset != NULL
+ && (knot_dname_compare(knot_rrset_owner(rrsigs),
+ knot_rrset_owner(*rrset)) != 0)) {
+ dbg_zone("RRSIGs does not belong to the given RRSet.\n");
+ return KNOT_EBADARG;
+ }
+
+ // if no RRSet given, try to find the right RRSet
+ if (*rrset == NULL) {
+ // even no node given
+ // find proper node
+ knot_node_t *(*get_node)(const knot_zone_contents_t *,
+ const knot_dname_t *)
+ = (knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrsigs)) == KNOT_RRTYPE_NSEC3)
+ ? knot_zone_contents_get_nsec3_node
+ : knot_zone_contents_get_node;
+
+ if (*node == NULL
+ && (*node = get_node(
+ zone, knot_rrset_owner(rrsigs))) == NULL) {
+ dbg_zone("Failed to find node for RRSIGs.\n");
+ return KNOT_ENONODE;
+ }
+
+ assert(*node != NULL);
+
+ // find the RRSet in the node
+ // take only the first RDATA from the RRSIGs
+ dbg_zone("Finding RRSet for type %s\n",
+ knot_rrtype_to_string(
+ knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrsigs))));
+ *rrset = knot_node_get_rrset(
+ *node, knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrsigs)));
+ if (*rrset == NULL) {
+ dbg_zone("Failed to find RRSet for RRSIGs.\n");
+ return KNOT_ENORRSET;
+ }
+ }
+
+ assert(*rrset != NULL);
+
+ // add all domain names from the RRSet to domain name table
+ int rc;
+ int ret = KNOT_EOK;
+
+ rc = knot_rrset_add_rrsigs(*rrset, rrsigs, dupl);
+ if (rc < 0) {
+ dbg_dname("Failed to add RRSIGs to RRSet.\n");
+ return rc;
+ } else if (rc > 0) {
+ assert(dupl == KNOT_RRSET_DUPL_MERGE);
+ ret = 1;
+ }
+
+ if (use_domain_table) {
+ dbg_zone("Saving RRSIG RRSet to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(
+ zone->dname_table, rrsigs, 0, (*rrset)->owner);
+ if (rc != KNOT_EOK) {
+ dbg_zone("Error saving domain names from "
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent "
+ "state.\n");
+ // WARNING: the zone is not in consistent state now -
+ // there may be domain names in it that are not inserted
+ // into the domain table
+ return rc;
+ }
+ }
+
+ // replace RRSet's owner with the node's owner (that is already in the
+ // table)
+ if ((*rrset)->owner != (*rrset)->rrsigs->owner) {
+ knot_rrset_set_owner((*rrset)->rrsigs, (*rrset)->owner);
+ }
+
+ dbg_zone("RRSIGs OK\n");
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table)
+{
+ UNUSED(create_parents);
+ UNUSED(flags);
+
+ if (zone == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret = 0;
+ if ((ret = knot_zone_contents_check_node(zone, node)) != 0) {
+ return ret;
+ }
+
+ // how to know if this is successfull??
+// TREE_INSERT(zone->nsec3_nodes, knot_node, avl, node);
+ knot_zone_tree_insert(zone->nsec3_nodes, node);
+
+ if (use_domain_table) {
+ ret = knot_zone_contents_dnames_from_node_to_table(
+ zone->dname_table, node);
+ if (ret != KNOT_EOK) {
+ /*! \todo Remove the node from the tree. */
+ dbg_zone("Failed to add dnames into table.\n");
+ return ret;
+ }
+ }
+
+ // no parents to be created, the only parent is the zone apex
+ // set the apex as the parent of the node
+ knot_node_set_parent(node, zone->apex);
+
+ // set the zone to the node
+ knot_node_set_zone(node, zone->zone);
+
+ // cannot be wildcard child, so nothing to be done
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *zone,
+ knot_rrset_t *rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table)
+{
+ if (zone == NULL || rrset == NULL || zone->apex == NULL
+ || zone->apex->owner == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // check if the RRSet belongs to the zone
+ if (knot_dname_compare(knot_rrset_owner(rrset),
+ zone->apex->owner) != 0
+ && !knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ zone->apex->owner)) {
+ return KNOT_EBADZONE;
+ }
+
+ if ((*node) == NULL
+ && (*node = knot_zone_contents_get_nsec3_node(
+ zone, knot_rrset_owner(rrset))) == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ assert(*node != NULL);
+
+ // add all domain names from the RRSet to domain name table
+ int rc;
+
+ /*! \todo REMOVE RRSET */
+ rc = knot_node_add_rrset(*node, rrset,
+ dupl == KNOT_RRSET_DUPL_MERGE);
+ if (rc < 0) {
+ return rc;
+ }
+
+ int ret = rc;
+
+ if (use_domain_table) {
+ dbg_zone("Saving NSEC3 RRSet to table.\n");
+ rc = knot_zone_contents_dnames_from_rrset_to_table(
+ zone->dname_table, rrset, 0, (*node)->owner);
+ if (rc != KNOT_EOK) {
+ dbg_zone("Error saving domain names from "
+ "RRSIGs to the domain name table.\n "
+ "The zone may be in an inconsistent "
+ "state.\n");
+ // WARNING: the zone is not in consistent state now -
+ // there may be domain names in it that are not inserted
+ // into the domain table
+ return rc;
+ }
+ }
+
+ // replace RRSet's owner with the node's owner (that is already in the
+ // table)
+ /*! \todo Do even if domain table is not used? */
+ if (rrset->owner != (*node)->owner) {
+ knot_rrset_set_owner(rrset, (*node)->owner);
+ }
+
+ dbg_zone("NSEC3 OK\n");
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_remove_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed_tree,
+ ck_hash_table_item_t **removed_hash)
+{
+ if (contents == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_dname_t *owner = knot_node_owner(node);
+
+ // 1) remove the node from hash table
+ *removed_hash = NULL;
+ *removed_hash = ck_remove_item(contents->table,
+ (const char *)knot_dname_name(owner),
+ knot_dname_size(owner));
+// int ret = ck_detete_item(contents->table,
+// (const char *)knot_dname_name(owner),
+// knot_dname_size(owner), NULL, 0);
+ if (*removed_hash == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ // 2) remove the node from the zone tree
+ *removed_tree = NULL;
+ int ret = knot_zone_tree_remove(contents->nodes, owner, removed_tree);
+ if (ret != KNOT_EOK) {
+ return KNOT_ENONODE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_remove_nsec3_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed)
+{
+ if (contents == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_dname_t *owner = knot_node_owner(node);
+
+ // remove the node from the zone tree
+ *removed = NULL;
+ int ret = knot_zone_tree_remove(contents->nsec3_nodes, owner, removed);
+ if (ret != KNOT_EOK) {
+ return KNOT_ENONODE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_create_and_fill_hash_table(
+ knot_zone_contents_t *zone)
+{
+ if (zone == NULL || zone->apex == NULL || zone->apex->owner == NULL) {
+ return KNOT_EBADARG;
+ }
+ /*
+ * 1) Create hash table.
+ */
+#ifdef USE_HASH_TABLE
+ if (zone->node_count > 0) {
+ zone->table = ck_create_table(zone->node_count);
+ if (zone->table == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // insert the apex into the hash table
+ if (ck_insert_item(zone->table,
+ (const char *)zone->apex->owner->name,
+ zone->apex->owner->size,
+ (void *)zone->apex) != 0) {
+ return KNOT_EHASH;
+ }
+ } else {
+ zone->table = NULL;
+ return KNOT_EOK; // OK?
+ }
+
+ /*
+ * 2) Fill in the hash table.
+ *
+ * In this point, the nodes in the zone must be adjusted, so that only
+ * relevant nodes (authoritative and delegation points are inserted.
+ *
+ * TODO: how to know if this was successful??
+ */
+ /*! \todo Replace by zone tree. */
+ int ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_contents_node_to_hash, zone);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to insert nodes to hash table.\n");
+ return ret;
+ }
+
+#endif
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_node(const knot_zone_contents_t *zone,
+ const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ // create dummy node to use for lookup
+// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL);
+// knot_node_t *n = TREE_FIND(zone->tree, knot_node, avl, tmp);
+// knot_node_free(&tmp, 0);
+
+ knot_node_t *n;
+ int ret = knot_zone_tree_get(zone->nodes, name, &n);
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to find name in the zone tree.\n");
+ return NULL;
+ }
+
+ return n;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_nsec3_node(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ // create dummy node to use for lookup
+// knot_node_t *tmp = knot_node_new((knot_dname_t *)name, NULL);
+// knot_node_t *n = TREE_FIND(zone->nsec3_nodes, knot_node, avl, tmp);
+// knot_node_free(&tmp, 0);
+ knot_node_t *n;
+ int ret = knot_zone_tree_get(zone->nsec3_nodes, name, &n);
+
+ if (ret != KNOT_EOK) {
+ dbg_zone("Failed to find NSEC3 name in the zone tree."
+ "\n");
+ return NULL;
+ }
+
+ return n;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_node(
+ const knot_zone_contents_t *zone,const knot_dname_t *name)
+{
+ return knot_zone_contents_get_node(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_find_dname(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser,
+ const knot_node_t **previous)
+{
+ if (zone == NULL || name == NULL || node == NULL
+ || closest_encloser == NULL || previous == NULL
+ || zone->apex == NULL || zone->apex->owner == NULL) {
+ return KNOT_EBADARG;
+ }
+
+dbg_zone_exec(
+ char *name_str = knot_dname_to_str(name);
+ char *zone_str = knot_dname_to_str(zone->apex->owner);
+ dbg_zone("Searching for name %s in zone %s...\n",
+ name_str, zone_str);
+ free(name_str);
+ free(zone_str);
+);
+
+ if (knot_dname_compare(name, zone->apex->owner) == 0) {
+ *node = zone->apex;
+ *closest_encloser = *node;
+ return KNOT_ZONE_NAME_FOUND;
+ }
+
+ if (!knot_dname_is_subdomain(name, zone->apex->owner)) {
+ *node = NULL;
+ *closest_encloser = NULL;
+ return KNOT_EBADZONE;
+ }
+
+ knot_node_t *found = NULL, *prev = NULL;
+
+ int exact_match = knot_zone_contents_find_in_tree(zone->nodes, name,
+ &found, &prev);
+ assert(exact_match >= 0);
+ *node = found;
+ *previous = prev;
+
+dbg_zone_exec(
+ char *name_str = (*node) ? knot_dname_to_str((*node)->owner)
+ : "(nil)";
+ char *name_str2 = (*previous != NULL)
+ ? knot_dname_to_str((*previous)->owner)
+ : "(nil)";
+ dbg_zone("Search function returned %d, node %s and prev: %s\n",
+ exact_match, name_str, name_str2);
+
+ if (*node) {
+ free(name_str);
+ }
+ if (*previous != NULL) {
+ free(name_str2);
+ }
+);
+
+ *closest_encloser = *node;
+
+ // there must be at least one node with domain name less or equal to
+ // the searched name if the name belongs to the zone (the root)
+ if (*node == NULL) {
+ return KNOT_EBADZONE;
+ }
+
+ // TODO: this could be replaced by saving pointer to closest encloser
+ // in node
+
+ if (!exact_match) {
+ int matched_labels = knot_dname_matched_labels(
+ knot_node_owner((*closest_encloser)), name);
+ while (matched_labels < knot_dname_label_count(
+ knot_node_owner((*closest_encloser)))) {
+ (*closest_encloser) =
+ knot_node_parent((*closest_encloser), 1);
+ assert(*closest_encloser);
+ }
+ }
+dbg_zone_exec(
+ char *n = knot_dname_to_str(knot_node_owner((*closest_encloser)));
+ dbg_zone("Closest encloser: %s\n", n);
+ free(n);
+);
+
+ dbg_zone("find_dname() returning %d\n", exact_match);
+
+ return (exact_match)
+ ? KNOT_ZONE_NAME_FOUND
+ : KNOT_ZONE_NAME_NOT_FOUND;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_previous(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ knot_node_t *found = NULL, *prev = NULL;
+
+ int exact_match = knot_zone_contents_find_in_tree(zone->nodes, name,
+ &found, &prev);
+ assert(exact_match >= 0);
+ assert(prev != NULL);
+
+ return prev;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_previous(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ return knot_zone_contents_get_previous(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_previous_nsec3(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ knot_node_t *found = NULL, *prev = NULL;
+
+ int exact_match = knot_zone_contents_find_in_tree(zone->nsec3_nodes,
+ name, &found, &prev);
+ assert(exact_match >= 0);
+ assert(prev != NULL);
+
+ return prev;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_previous_nsec3(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ return knot_zone_contents_get_previous(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_contents_left_chop(char *name, size_t *size)
+{
+ short label_size = name[0];
+
+ memmove(name, name + label_size + 1, *size -label_size - 1);
+ *size = *size - label_size - 1;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef USE_HASH_TABLE
+int knot_zone_contents_find_dname_hash(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser)
+{
+ if (zone == NULL || name == NULL || node == NULL
+ || closest_encloser == NULL) {
+ return KNOT_EBADARG;
+ }
+
+dbg_zone_exec(
+ char *name_str = knot_dname_to_str(name);
+ char *zone_str = knot_dname_to_str(zone->apex->owner);
+ dbg_zone("Searching for name %s in zone %s...\n",
+ name_str, zone_str);
+ free(name_str);
+ free(zone_str);
+);
+
+ if (knot_dname_compare(name, zone->apex->owner) == 0) {
+ *node = zone->apex;
+ *closest_encloser = *node;
+ return KNOT_ZONE_NAME_FOUND;
+ }
+
+ if (!knot_dname_is_subdomain(name, zone->apex->owner)) {
+ *node = NULL;
+ *closest_encloser = NULL;
+ return KNOT_EBADZONE;
+ }
+
+ // temporary name used for hashing
+ char name_tmp[KNOT_MAX_DNAME_LENGTH];
+ size_t name_size = name->size;
+ if (knot_dname_to_lower_copy(name, name_tmp, KNOT_MAX_DNAME_LENGTH)
+ != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ const ck_hash_table_item_t *item = ck_find_item(zone->table,
+ name_tmp, name_size);
+
+ if (item != NULL) {
+ *node = (const knot_node_t *)item->value;
+ *closest_encloser = *node;
+
+ dbg_zone("Found node in hash table: %p (owner %p, "
+ "labels: %d)\n", *node, (*node)->owner,
+ knot_dname_label_count((*node)->owner));
+ assert(*node != NULL);
+ assert(*closest_encloser != NULL);
+ return KNOT_ZONE_NAME_FOUND;
+ }
+
+ *node = NULL;
+
+ // chop leftmost labels until some node is found
+ // copy the name for chopping
+ /* Local allocation, will be discarded. */
+ //knot_dname_t *name_copy = knot_dname_deep_copy(name);
+dbg_zone_exec(
+ //char *n = knot_dname_to_str(name_copy);
+ dbg_zone("Finding closest encloser..\nStarting with: %.*s\n",
+ (int)name_size, name_tmp);
+ //free(n);
+);
+
+ while (item == NULL) {
+ //knot_dname_left_chop_no_copy(name_copy);
+ knot_zone_contents_left_chop(name_tmp, &name_size);
+dbg_zone_exec(
+ //char *n = knot_dname_to_str(name_copy);
+ dbg_zone("Chopped leftmost label: %.*s\n",
+ (int)name_size, name_tmp);
+ //free(n);
+);
+ // not satisfied in root zone!!
+ //assert(name_copy->label_count > 0);
+ assert(name_size > 0);
+
+ item = ck_find_item(zone->table, name_tmp, name_size);
+ }
+
+ /* Directly discard. */
+ //knot_dname_free(&name_copy);
+
+ assert(item != NULL);
+ *closest_encloser = (const knot_node_t *)item->value;
+
+ return KNOT_ZONE_NAME_NOT_FOUND;
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_find_nsec3_node(
+ const knot_zone_contents_t *zone, const knot_dname_t *name)
+{
+ return knot_zone_contents_get_nsec3_node(zone, name);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_find_nsec3_for_name(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ const knot_node_t **nsec3_node,
+ const knot_node_t **nsec3_previous,
+ int check_ver)
+{
+ if (zone == NULL || name == NULL
+ || nsec3_node == NULL || nsec3_previous == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_t *nsec3_name = NULL;
+ int ret = knot_zone_contents_nsec3_name(zone, name, &nsec3_name);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+dbg_zone_exec(
+ char *n = knot_dname_to_str(nsec3_name);
+ dbg_zone("NSEC3 node name: %s.\n", n);
+ free(n);
+);
+
+ const knot_node_t *found = NULL, *prev = NULL;
+
+ // create dummy node to use for lookup
+ int exact_match = knot_zone_tree_find_less_or_equal(
+ zone->nsec3_nodes, nsec3_name, &found, &prev, check_ver);
+ assert(exact_match >= 0);
+
+ knot_dname_release(nsec3_name);
+
+dbg_zone_exec(
+ if (found) {
+ char *n = knot_dname_to_str(found->owner);
+ dbg_zone("Found NSEC3 node: %s.\n", n);
+ free(n);
+ } else {
+ dbg_zone("Found no NSEC3 node.\n");
+ }
+
+ if (prev) {
+ assert(prev->owner);
+ char *n = knot_dname_to_str(prev->owner);
+ dbg_zone("Found previous NSEC3 node: %s.\n", n);
+ free(n);
+ } else {
+ dbg_zone("Found no previous NSEC3 node.\n");
+ }
+);
+ *nsec3_node = found;
+
+ if (prev == NULL) {
+ // either the returned node is the root of the tree, or it is
+ // the leftmost node in the tree; in both cases node was found
+ // set the previous node of the found node
+ assert(exact_match);
+ assert(*nsec3_node != NULL);
+ *nsec3_previous = knot_node_previous(*nsec3_node, check_ver);
+ } else {
+ *nsec3_previous = prev;
+ }
+
+ dbg_zone("find_nsec3_for_name() returning %d\n", exact_match);
+
+ return (exact_match)
+ ? KNOT_ZONE_NAME_FOUND
+ : KNOT_ZONE_NAME_NOT_FOUND;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_zone_contents_apex(
+ const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return zone->apex;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_zone_contents_get_apex(const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return zone->apex;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//knot_dname_t *knot_zone_contents_name(const knot_zone_contents_t *zone)
+//{
+// if (zone == NULL) {
+// return NULL;
+// }
+
+// return zone->name;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_adjust(knot_zone_contents_t *zone, int check_ver)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // load NSEC3PARAM (needed on adjusting function)
+ knot_zone_contents_load_nsec3param(zone);
+
+ knot_zone_adjust_arg_t adjust_arg;
+ adjust_arg.zone = zone;
+ adjust_arg.first_node = NULL;
+ adjust_arg.previous_node = NULL;
+ adjust_arg.check_ver = check_ver;
+
+ dbg_zone("Adjusting normal nodes.\n");
+ int ret = knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_contents_adjust_node_in_tree,
+ &adjust_arg);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ dbg_zone("Done.\n");
+
+ assert(zone->apex == adjust_arg.first_node);
+ knot_node_set_previous(zone->apex, adjust_arg.previous_node);
+
+ adjust_arg.first_node = NULL;
+ adjust_arg.previous_node = NULL;
+
+ dbg_zone("Adjusting NSEC3 nodes.\n");
+ ret = knot_zone_tree_forward_apply_inorder(
+ zone->nsec3_nodes,
+ knot_zone_contents_adjust_nsec3_node_in_tree,
+ &adjust_arg);
+
+ dbg_zone("Done.\n");
+ if (adjust_arg.first_node) {
+ knot_node_set_previous(adjust_arg.first_node,
+ adjust_arg.previous_node);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_load_nsec3param(knot_zone_contents_t *zone)
+{
+ if (zone == NULL || zone->apex == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ const knot_rrset_t *rrset = knot_node_rrset(zone->apex,
+ KNOT_RRTYPE_NSEC3PARAM);
+
+ if (rrset != NULL) {
+ knot_nsec3_params_from_wire(&zone->nsec3_params, rrset);
+ } else {
+ memset(&zone->nsec3_params, 0, sizeof(knot_nsec3_params_t));
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_enabled(const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ //return (zone->nsec3_params.algorithm != 0);
+ return (zone->nsec3_nodes->th_root != NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_nsec3_params_t *knot_zone_contents_nsec3params(
+ const knot_zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ return &zone->nsec3_params;
+ } else {
+ return NULL;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_tree_apply_postorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_postorder(zone->nodes,
+ knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_tree_apply_inorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_inorder(zone->nodes,
+ knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_tree_apply_inorder_reverse(
+ knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data), void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_reverse_apply_inorder(zone->nodes,
+ knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_apply_postorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_postorder(
+ zone->nsec3_nodes, knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_apply_inorder(knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data),
+ void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_forward_apply_inorder(
+ zone->nsec3_nodes, knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_nsec3_apply_inorder_reverse(
+ knot_zone_contents_t *zone,
+ void (*function)(knot_node_t *node, void *data), void *data)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_func_t f;
+ f.func = function;
+ f.data = data;
+
+ return knot_zone_tree_reverse_apply_inorder(
+ zone->nsec3_nodes, knot_zone_tree_apply, &f);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_tree_t *knot_zone_contents_get_nodes(
+ knot_zone_contents_t *contents)
+{
+ return contents->nodes;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_tree_t *knot_zone_contents_get_nsec3_nodes(
+ knot_zone_contents_t *contents)
+{
+ return contents->nsec3_nodes;
+}
+
+/*----------------------------------------------------------------------------*/
+
+ck_hash_table_t *knot_zone_contents_get_hash_table(
+ knot_zone_contents_t *contents)
+{
+ return contents->table;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_dname_table_apply(knot_zone_contents_t *contents,
+ void (*function)(knot_dname_t *,
+ void *),
+ void *data)
+{
+ if (contents == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_table_tree_inorder_apply(contents->dname_table,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
+ knot_zone_contents_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ /* Copy to same destination as source. */
+ if (from == *to) {
+ return KNOT_EBADARG;
+ }
+
+ int ret = KNOT_EOK;
+
+ knot_zone_contents_t *contents = (knot_zone_contents_t *)calloc(
+ 1, sizeof(knot_zone_contents_t));
+ if (contents == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ contents->apex = from->apex;
+
+ contents->nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+
+ contents->nsec3_nodes = malloc(sizeof(knot_zone_tree_t));
+ if (contents->nsec3_nodes == NULL) {
+ ERR_ALLOC_FAILED;
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+
+ if (from->dname_table != NULL) {
+ contents->dname_table = knot_dname_table_new();
+ if (contents->dname_table == NULL) {
+ ERR_ALLOC_FAILED;
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+ if ((ret = knot_dname_table_shallow_copy(from->dname_table,
+ contents->dname_table)) != KNOT_EOK) {
+ goto cleanup;
+ }
+ } else {
+ contents->dname_table = NULL;
+ }
+
+ contents->node_count = from->node_count;
+ contents->generation = from->generation;
+
+ contents->zone = from->zone;
+
+ /* Initialize NSEC3 params */
+ memcpy(&contents->nsec3_params, &from->nsec3_params,
+ sizeof(knot_nsec3_params_t));
+
+ if ((ret = knot_zone_tree_shallow_copy(from->nodes,
+ contents->nodes)) != KNOT_EOK
+ || (ret = knot_zone_tree_shallow_copy(from->nsec3_nodes,
+ contents->nsec3_nodes)) != KNOT_EOK) {
+ goto cleanup;
+ }
+
+#ifdef USE_HASH_TABLE
+ if (from->table != NULL) {
+// ret = ck_copy_table(from->table, &contents->table);
+ ret = ck_shallow_copy(from->table, &contents->table);
+ if (ret != 0) {
+ dbg_zone("knot_zone_contents_shallow_copy: "
+ "hash table copied\n");
+ ret = KNOT_ERROR;
+ goto cleanup;
+ }
+ }
+#endif
+
+ dbg_zone("knot_zone_contents_shallow_copy: "
+ "finished OK\n");
+
+ *to = contents;
+ return KNOT_EOK;
+
+cleanup:
+ knot_zone_tree_free(&contents->nodes);
+ knot_zone_tree_free(&contents->nsec3_nodes);
+ free(contents->dname_table);
+ free(contents);
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_free(knot_zone_contents_t **contents)
+{
+ if (contents == NULL || *contents == NULL) {
+ return;
+ }
+
+ // free the zone tree, but only the structure
+ knot_zone_tree_free(&(*contents)->nodes);
+ knot_zone_tree_free(&(*contents)->nsec3_nodes);
+
+#ifdef USE_HASH_TABLE
+ if ((*contents)->table != NULL) {
+ ck_destroy_table(&(*contents)->table, NULL, 0);
+ }
+#endif
+ knot_nsec3_params_free(&(*contents)->nsec3_params);
+
+ knot_dname_table_free(&(*contents)->dname_table);
+
+ free(*contents);
+ *contents = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_contents_deep_free(knot_zone_contents_t **contents,
+ int destroy_dname_table)
+{
+ if (contents == NULL || *contents == NULL) {
+ return;
+ }
+
+ if ((*contents) != NULL) {
+
+#ifdef USE_HASH_TABLE
+ if ((*contents)->table != NULL) {
+ ck_destroy_table(&(*contents)->table, NULL, 0);
+ }
+#endif
+ /* has to go through zone twice, rdata may contain references to
+ node owners earlier in the zone which may be already freed */
+ /* NSEC3 tree is deleted first as it may contain references to
+ the normal tree. */
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nsec3_nodes,
+ knot_zone_contents_destroy_node_rrsets_from_tree,
+ (void*)1);
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nsec3_nodes,
+ knot_zone_contents_destroy_node_owner_from_tree, 0);
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nodes,
+ knot_zone_contents_destroy_node_rrsets_from_tree,
+ (void*)1);
+
+ knot_zone_tree_reverse_apply_postorder(
+ (*contents)->nodes,
+ knot_zone_contents_destroy_node_owner_from_tree, 0);
+
+ // free the zone tree, but only the structure
+ // (nodes are already destroyed)
+ dbg_zone("Destroying zone tree.\n");
+ knot_zone_tree_free(&(*contents)->nodes);
+ dbg_zone("Destroying NSEC3 zone tree.\n");
+ knot_zone_tree_free(&(*contents)->nsec3_nodes);
+
+ knot_nsec3_params_free(&(*contents)->nsec3_params);
+
+ if (destroy_dname_table) {
+ /*
+ * Hack, used in zcompile - destroys the table using
+ * dname_free() instead of dname_retain().
+ */
+ knot_dname_table_destroy(&(*contents)->dname_table);
+ } else {
+ knot_dname_table_deep_free(&(*contents)->dname_table);
+ }
+ }
+
+ free((*contents));
+ *contents = NULL;
+}
+