summaryrefslogtreecommitdiff
path: root/src/libknot/zone
diff options
context:
space:
mode:
Diffstat (limited to 'src/libknot/zone')
-rw-r--r--src/libknot/zone/dname-table.c310
-rw-r--r--src/libknot/zone/dname-table.h168
-rw-r--r--src/libknot/zone/node.c906
-rw-r--r--src/libknot/zone/node.h436
-rw-r--r--src/libknot/zone/zone-contents.c2396
-rw-r--r--src/libknot/zone/zone-contents.h556
-rw-r--r--src/libknot/zone/zone-tree.c470
-rw-r--r--src/libknot/zone/zone-tree.h300
-rw-r--r--src/libknot/zone/zone.c246
-rw-r--r--src/libknot/zone/zone.h157
-rw-r--r--src/libknot/zone/zonedb.c389
-rw-r--r--src/libknot/zone/zonedb.h145
12 files changed, 6479 insertions, 0 deletions
diff --git a/src/libknot/zone/dname-table.c b/src/libknot/zone/dname-table.c
new file mode 100644
index 0000000..c41b4bd
--- /dev/null
+++ b/src/libknot/zone/dname-table.c
@@ -0,0 +1,310 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "zone/dname-table.h"
+#include "util/error.h"
+
+/*!< Tree functions. */
+TREE_DEFINE(dname_table_node, avl);
+
+struct knot_dname_table_fnc_data {
+ void (*func)(knot_dname_t *dname, void *data);
+ void *data;
+};
+
+static void knot_dname_table_apply(struct dname_table_node *node, void *data)
+{
+ assert(data != NULL);
+ assert(node != NULL);
+ struct knot_dname_table_fnc_data *d =
+ (struct knot_dname_table_fnc_data *)data;
+ d->func(node->dname, d->data);
+}
+
+/*!
+ * \brief Comparison function to be used with tree.
+ *
+ * \param n1 First dname to be compared.
+ * \param n2 Second dname to be compared.
+ *
+ * \return strncmp of dname's wireformats.
+ */
+static int compare_dname_table_nodes(struct dname_table_node *n1,
+ struct dname_table_node *n2)
+{
+ assert(n1 && n2);
+ return (strncmp((char *)n1->dname->name, (char *)n2->dname->name,
+ (n1->dname->size < n2->dname->size) ?
+ (n1->dname->size):(n2->dname->size)));
+}
+
+/*!
+ * \brief Deletes tree node along with its domain name.
+ *
+ * \param node Node to be deleted.
+ * \param data If <> 0, dname in the node will be freed as well.
+ */
+static void delete_dname_table_node(struct dname_table_node *node, void *data)
+{
+ if ((ssize_t)data == 1) {
+ knot_dname_release(node->dname);
+ } else if ((ssize_t)data == 2) {
+ knot_dname_free(&node->dname);
+ }
+
+ /*!< \todo it would be nice to set pointers to NULL. */
+ free(node);
+}
+
+static void knot_dname_table_delete_subtree(struct dname_table_node *root)
+{
+ if (root == NULL) {
+ return;
+ }
+
+ knot_dname_table_delete_subtree(root->avl.avl_left);
+ knot_dname_table_delete_subtree(root->avl.avl_right);
+ free(root);
+}
+
+static int knot_dname_table_copy_node(const struct dname_table_node *from,
+ struct dname_table_node **to)
+{
+ if (from == NULL) {
+ return KNOT_EOK;
+ }
+
+ *to = (struct dname_table_node *)
+ malloc(sizeof(struct dname_table_node));
+ if (*to == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(*to, 0, sizeof(struct dname_table_node));
+
+ (*to)->dname = from->dname;
+ knot_dname_retain((*to)->dname);
+ (*to)->avl.avl_height = from->avl.avl_height;
+
+ int ret = knot_dname_table_copy_node(from->avl.avl_left,
+ &(*to)->avl.avl_left);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_dname_table_copy_node(from->avl.avl_right,
+ &(*to)->avl.avl_right);
+ if (ret != KNOT_EOK) {
+ knot_dname_table_delete_subtree((*to)->avl.avl_left);
+ (*to)->avl.avl_left = NULL;
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+knot_dname_table_t *knot_dname_table_new()
+{
+ knot_dname_table_t *ret = malloc(sizeof(knot_dname_table_t));
+ CHECK_ALLOC_LOG(ret, NULL);
+
+ ret->tree = malloc(sizeof(table_tree_t));
+ if (ret->tree == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ret);
+ return NULL;
+ }
+
+ TREE_INIT(ret->tree, compare_dname_table_nodes);
+
+ ret->id_counter = 1;
+
+ return ret;
+}
+
+knot_dname_t *knot_dname_table_find_dname(const knot_dname_table_t *table,
+ knot_dname_t *dname)
+{
+ if (table == NULL || dname == NULL) {
+ return NULL;
+ }
+
+ struct dname_table_node *node = NULL;
+ struct dname_table_node sought;
+ sought.dname = dname;
+
+ node = TREE_FIND(table->tree, dname_table_node, avl, &sought);
+
+ if (node == NULL) {
+ return NULL;
+ } else {
+ /* Increase reference counter. */
+ knot_dname_retain(node->dname);
+
+ return node->dname;
+ }
+}
+
+int knot_dname_table_add_dname(knot_dname_table_t *table,
+ knot_dname_t *dname)
+{
+ if (dname == NULL || table == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ /* Node for insertion has to be created */
+ struct dname_table_node *node =
+ malloc(sizeof(struct dname_table_node));
+ CHECK_ALLOC_LOG(node, KNOT_ENOMEM);
+
+ // convert the dname to lowercase
+ knot_dname_to_lower(dname);
+
+ node->dname = dname;
+ node->avl.avl_height = 0;
+ node->avl.avl_left = NULL;
+ node->avl.avl_right = NULL;
+
+ node->dname->id = table->id_counter++;
+ assert(node->dname->id != 0);
+
+ /* Increase reference counter. */
+ knot_dname_retain(dname);
+
+ TREE_INSERT(table->tree, dname_table_node, avl, node);
+ return KNOT_EOK;
+}
+
+int knot_dname_table_add_dname_check(knot_dname_table_t *table,
+ knot_dname_t **dname)
+{
+ knot_dname_t *found_dname = NULL;
+
+ if (table == NULL || dname == NULL || *dname == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ /* Fetch dname, need to release it later. */
+ found_dname = knot_dname_table_find_dname(table ,*dname);
+
+ if (!found_dname) {
+ /* Store reference in table. */
+ return knot_dname_table_add_dname(table, *dname);
+ } else {
+ /*! \todo Remove the check for equality. */
+ if (found_dname != *dname) {
+ /* Replace dname with found. */
+ knot_dname_release(*dname);
+ *dname = found_dname;
+ return 1; /*! \todo Error code? */
+
+ } else {
+
+ /* If the dname is already in the table, there is already
+ * a reference to it.
+ */
+ knot_dname_release(found_dname);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_dname_table_shallow_copy(knot_dname_table_t *from,
+ knot_dname_table_t *to)
+{
+ to->id_counter = from->id_counter;
+
+ if (to->tree == NULL) {
+ to->tree = malloc(sizeof(table_tree_t));
+ if (to->tree == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ TREE_INIT(to->tree, compare_dname_table_nodes);
+ }
+
+ return knot_dname_table_copy_node(from->tree->th_root,
+ &to->tree->th_root);
+}
+
+void knot_dname_table_free(knot_dname_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ /* Walk the tree and free each node, but not the dnames. */
+ TREE_POST_ORDER_APPLY((*table)->tree, dname_table_node, avl,
+ delete_dname_table_node, 0);
+
+ free((*table)->tree);
+
+ free(*table);
+ *table = NULL;
+}
+
+void knot_dname_table_deep_free(knot_dname_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ /* Walk the tree and free each node, but free the dnames. */
+ TREE_POST_ORDER_APPLY((*table)->tree, dname_table_node, avl,
+ delete_dname_table_node, (void *) 1);
+
+ free((*table)->tree);
+
+ free(*table);
+ *table = NULL;
+}
+
+void knot_dname_table_destroy(knot_dname_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ /* Walk the tree and free each node, but free the dnames. */
+ TREE_POST_ORDER_APPLY((*table)->tree, dname_table_node, avl,
+ delete_dname_table_node, (void *) 2);
+
+ free((*table)->tree);
+
+ free(*table);
+ *table = NULL;
+}
+
+void knot_dname_table_tree_inorder_apply(const knot_dname_table_t *table,
+ void (*applied_function)(knot_dname_t *node,
+ void *data),
+ void *data)
+{
+ struct knot_dname_table_fnc_data d;
+ d.data = data;
+ d.func = applied_function;
+
+ TREE_FORWARD_APPLY(table->tree, dname_table_node, avl,
+ knot_dname_table_apply, &d);
+}
+
diff --git a/src/libknot/zone/dname-table.h b/src/libknot/zone/dname-table.h
new file mode 100644
index 0000000..dd86eaf
--- /dev/null
+++ b/src/libknot/zone/dname-table.h
@@ -0,0 +1,168 @@
+/*!
+ * \file dname-table.h
+ *
+ * \author Jan Kadlec <jan.kadlec.@nic.cz>
+ *
+ * \brief Structures representing dname table and functions for
+ * manipulating these structures.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/* 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/>.
+ */
+
+#ifndef _KNOT_DNAME_TABLE_H_
+#define _KNOT_DNAME_TABLE_H_
+
+#include <config.h>
+
+#include "common/tree.h"
+
+#include "dname.h"
+#include "common.h"
+
+
+/*!
+ * \brief Structure encapsulating
+ */
+struct dname_table_node {
+ knot_dname_t *dname; /*!< Dname stored in node. */
+ TREE_ENTRY(dname_table_node) avl; /*!< Tree variables. */
+};
+
+/*!
+ * \brief Tree structure.
+ */
+typedef TREE_HEAD(avl, dname_table_node) table_tree_t;
+
+/*!
+ * \brief Structure holding tree together with dname ID counter.
+ */
+struct knot_dname_table {
+ unsigned int id_counter; /*!< ID counter (starts from 1) */
+ table_tree_t *tree; /*!< AVL tree */
+};
+
+typedef struct knot_dname_table knot_dname_table_t;
+
+/*!
+ * \brief Creates new empty domain name table.
+ *
+ * \retval Created table on success.
+ * \retval NULL on memory error.
+ */
+knot_dname_table_t *knot_dname_table_new();
+
+/*!
+ * \brief Finds name in the domain name table.
+ *
+ * \note Reference count to dname will be incremented, caller is responsible
+ * for releasing it.
+ *
+ * \param table Domain name table to be searched.
+ * \param dname Dname to be searched.
+ *
+ * \retval Pointer to found dname when dname is present in the table.
+ * \retval NULL when dname is not present.
+ */
+knot_dname_t *knot_dname_table_find_dname(const knot_dname_table_t *table,
+ knot_dname_t *dname);
+
+/*!
+ * \brief Adds domain name to domain name table.
+ *
+ * \param table Domain name table to be added to.
+ * \param dname Domain name to be added.
+ *
+ * \warning Function does not check for duplicates!
+ *
+ * \note This function encapsulates dname in a structure and saves it to a tree.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM when memory runs out.
+ */
+int knot_dname_table_add_dname(knot_dname_table_t *table,
+ knot_dname_t *dname);
+
+/*!
+ * \brief Adds domain name to domain name table and checks for duplicates.
+ *
+ * \param table Domain name table to be added to.
+ * \param dname Domain name to be added.
+ *
+ * \note This function encapsulates dname in a structure and saves it to a tree.
+ * \note If a duplicate is found, \a dname is replaced by the name from table.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM when memory runs out.
+ */
+int knot_dname_table_add_dname_check(knot_dname_table_t *table,
+ knot_dname_t **dname);
+
+/*!
+ * \brief Creates a shallow copy of the domain name table.
+ *
+ * Expects an existing knot_dname_table_t structure to be passed via \a to,
+ * and fills it with the same data (domain names) as the original. Actual
+ * tree nodes are created, but domain names are not copied (just referenced).
+ *
+ * \param from Original domain name table.
+ * \param to Copy of the domain name table.
+ */
+int knot_dname_table_shallow_copy(knot_dname_table_t *from,
+ knot_dname_table_t *to);
+
+/*!
+ * \brief Frees dname table without its nodes. Sets pointer to NULL.
+ *
+ * \param table Table to be freed.
+ */
+void knot_dname_table_free(knot_dname_table_t **table);
+
+/*!
+ * \brief Frees dname table and all its nodes (and release dnames in the nodes)
+ * Sets pointer to NULL.
+ *
+ * \param table Table to be freed.
+ */
+void knot_dname_table_deep_free(knot_dname_table_t **table);
+
+/*!
+ * \brief Frees dname table and all its nodes (including dnames in the nodes)
+ * Sets pointer to NULL.
+ *
+ * \param table Table to be freed.
+ */
+void knot_dname_table_destroy(knot_dname_table_t **table);
+
+/*!
+ * \brief Encapsulation of domain name table tree traversal function.
+ *
+ * \param table Table containing tree to be traversed.
+ * \param applied_function Function to be used to process nodes.
+ * \param data Data to be passed to processing function.
+ */
+void knot_dname_table_tree_inorder_apply(const knot_dname_table_t *table,
+ void (*applied_function)(knot_dname_t *dname,
+ void *data),
+ void *data);
+
+
+#endif // _KNOT_DNAME_TABLE_H_
+
+/*! @} */
+
diff --git a/src/libknot/zone/node.c b/src/libknot/zone/node.c
new file mode 100644
index 0000000..1d2f938
--- /dev/null
+++ b/src/libknot/zone/node.c
@@ -0,0 +1,906 @@
+/* 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 <config.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include <urcu.h>
+
+#include "common.h"
+#include "zone/node.h"
+#include "rrset.h"
+#include "util/error.h"
+#include "common/skip-list.h"
+#include "common/tree.h"
+#include "util/debug.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the delegation point flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the delegation point flag set if it was set in
+ * \a flags.
+ */
+static inline uint8_t knot_node_flags_get_deleg(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_DELEG;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the delegation point flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_deleg(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_DELEG;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the non-authoritative node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the non-authoritative node flag set if it was set in
+ * \a flags.
+ */
+static inline uint8_t knot_node_flags_get_nonauth(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_NONAUTH;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the non-authoritative node flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_nonauth(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_NONAUTH;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the old node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the old node flag set if it was set in \a flags.
+ */
+static inline uint8_t knot_node_flags_get_old(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_OLD;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the old node flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_new(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_NEW;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the new node flag
+ *
+ * \param flags Flags to retrieve the flag from.
+ *
+ * \return A byte with only the new node flag set if it was set in \a flags.
+ */
+static inline uint8_t knot_node_flags_get_new(uint8_t flags)
+{
+ return flags & KNOT_NODE_FLAGS_NEW;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the new node flag.
+ *
+ * \param flags Flags to set the flag in.
+ */
+static inline void knot_node_flags_set_old(uint8_t *flags)
+{
+ *flags |= KNOT_NODE_FLAGS_OLD;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline void knot_node_flags_clear_new(uint8_t *flags)
+{
+ *flags &= ~KNOT_NODE_FLAGS_NEW;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline void knot_node_flags_clear_old(uint8_t *flags)
+{
+ *flags &= ~KNOT_NODE_FLAGS_OLD;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares the two keys as RR types.
+ *
+ * \note This function may be used in data structures requiring generic
+ * comparation function.
+ *
+ * \param key1 First RR type.
+ * \param key2 Second RR type.
+ *
+ * \retval 0 if \a key1 is equal to \a key2.
+ * \retval < 0 if \a key1 is lower than \a key2.
+ * \retval > 0 if \a key1 is higher than \a key2.
+ */
+static int compare_rrset_types(void *rr1, void *rr2)
+{
+ knot_rrset_t *rrset1 = (knot_rrset_t *)rr1;
+ knot_rrset_t *rrset2 = (knot_rrset_t *)rr2;
+ return ((rrset1->type > rrset2->type) ? 1 :
+ (rrset1->type == rrset2->type) ? 0 : -1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_node_zone_gen_is_new(const knot_node_t *node)
+{
+ assert(node->zone != NULL);
+ knot_zone_contents_t *cont = rcu_dereference(node->zone->contents);
+ assert(cont != NULL);
+ return knot_zone_contents_gen_is_new(cont);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_node_zone_gen_is_old(const knot_node_t *node)
+{
+ assert(node->zone != NULL);
+ knot_zone_contents_t *cont = rcu_dereference(node->zone->contents);
+ assert(cont != NULL);
+ return knot_zone_contents_gen_is_old(cont);
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
+ uint8_t flags)
+{
+ knot_node_t *ret = (knot_node_t *)calloc(1, sizeof(knot_node_t));
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ /* Store reference to owner. */
+ knot_dname_retain(owner);
+ ret->owner = owner;
+ knot_node_set_parent(ret, parent);
+ ret->rrset_tree = gen_tree_new(compare_rrset_types);
+ ret->flags = flags;
+
+ assert(ret->children == 0);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
+ int merge)
+{
+ int ret = 0;
+
+ if ((ret = (gen_tree_add(node->rrset_tree, rrset,
+ (merge) ? knot_rrset_merge : NULL))) < 0) {
+ dbg_node("Failed to add rrset to node->rrset_tree.\n");
+ return KNOT_ERROR;
+ }
+
+ if (ret >= 0) {
+ node->rrset_count += (ret > 0 ? 0 : 1);
+ return ret;
+ } else {
+ return KNOT_ERROR;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_node_rrset(const knot_node_t *node,
+ uint16_t type)
+{
+ assert(node != NULL);
+ assert(node->rrset_tree != NULL);
+ knot_rrset_t rrset;
+ rrset.type = type;
+ return (const knot_rrset_t *)gen_tree_find(node->rrset_tree, &rrset);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_node_get_rrset(knot_node_t *node, uint16_t type)
+{
+ knot_rrset_t rrset;
+ rrset.type = type;
+ return (knot_rrset_t *)gen_tree_find(node->rrset_tree, &rrset);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type)
+{
+ knot_rrset_t dummy_rrset;
+ dummy_rrset.type = type;
+ knot_rrset_t *rrset =
+ (knot_rrset_t *)gen_tree_find(node->rrset_tree, &dummy_rrset);
+ if (rrset != NULL) {
+ gen_tree_remove(node->rrset_tree, rrset);
+ node->rrset_count--;
+ }
+ return rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_remove_all_rrsets(knot_node_t *node)
+{
+ // remove RRSets but do not delete them
+ gen_tree_clear(node->rrset_tree);
+ node->rrset_count = 0;
+
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_node_rrset_count(const knot_node_t *node)
+{
+ return node->rrset_count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+struct knot_node_save_rrset_arg {
+ knot_rrset_t **array;
+ size_t count;
+};
+
+static void save_rrset_to_array(void *node, void *data)
+{
+ knot_rrset_t *rrset = (knot_rrset_t *)node;
+ struct knot_node_save_rrset_arg *args =
+ (struct knot_node_save_rrset_arg *)data;
+ args->array[args->count++] = rrset;
+}
+
+knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node)
+{
+// knot_node_dump(node, 1);
+ if (node->rrset_count == 0) {
+ return NULL;
+ }
+ knot_rrset_t **rrsets = (knot_rrset_t **)malloc(
+ node->rrset_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(rrsets, NULL);
+ struct knot_node_save_rrset_arg args;
+ args.array = rrsets;
+ args.count = 0;
+
+ gen_tree_apply_inorder(node->rrset_tree, save_rrset_to_array,
+ &args);
+
+ assert(args.count == node->rrset_count);
+
+ return rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t **knot_node_rrsets(const knot_node_t *node)
+{
+ //knot_node_dump((knot_node_t *)node, (void*)1);
+ if (node->rrset_count == 0) {
+ return NULL;
+ }
+
+ knot_rrset_t **rrsets = (knot_rrset_t **)malloc(
+ node->rrset_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(rrsets, NULL);
+ struct knot_node_save_rrset_arg args;
+ args.array = rrsets;
+ args.count = 0;
+
+ gen_tree_apply_inorder(node->rrset_tree, save_rrset_to_array,
+ &args);
+
+ assert(args.count == node->rrset_count);
+ assert(args.count);
+
+ return (const knot_rrset_t **)rrsets;
+
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_parent(const knot_node_t *node,
+ int check_version)
+{
+// assert(!check_version
+// || (node->zone != NULL && node->zone->contents != NULL));
+
+ knot_node_t *parent = node->parent;
+
+ if (check_version && node->zone != NULL) {
+ int new_gen = knot_node_zone_gen_is_new(node);
+// short ver = knot_node_zone_generation(node);
+
+ /*! \todo Remove, this will not be true during the reference
+ * fixing.
+ */
+// assert(new_gen || parent == NULL
+// || !knot_node_is_new(parent));
+
+ if (new_gen && parent != NULL) {
+ // we want the new node
+ assert(node->parent->new_node != NULL);
+ parent = parent->new_node;
+ }
+ }
+
+ return parent;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_parent(knot_node_t *node, knot_node_t *parent)
+{
+ // decrease number of children of previous parent
+ if (node->parent != NULL) {
+ --parent->children;
+ }
+ // set the parent
+ node->parent = parent;
+
+ // increase the count of children of the new parent
+ if (parent != NULL) {
+ ++parent->children;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned int knot_node_children(const knot_node_t *node)
+{
+ return node->children;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_previous(const knot_node_t *node,
+ int check_version)
+{
+ return knot_node_get_previous(node, check_version);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_get_previous(const knot_node_t *node,
+ int check_version)
+{
+ assert(!check_version
+ || (node->zone != NULL && node->zone->contents != NULL));
+
+ knot_node_t *prev = node->prev;
+
+ if (check_version && prev != NULL) {
+ int new_gen = knot_node_zone_gen_is_new(node);
+ int old_gen = knot_node_zone_gen_is_old(node);
+// short ver = knot_node_zone_generation(node);
+
+ if (old_gen) { // we want old node
+ while (knot_node_is_new(prev)) {
+ prev = prev->prev;
+ }
+ assert(!knot_node_is_new(prev));
+ } else if (new_gen) { // we want new node
+ while (knot_node_is_old(prev)) {
+ if (prev->new_node) {
+ prev = prev->new_node;
+ } else {
+ prev = prev;
+ }
+ }
+ assert(knot_node_is_new(prev));
+ }
+ }
+
+ return prev;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_previous(knot_node_t *node, knot_node_t *prev)
+{
+ node->prev = prev;
+ if (prev != NULL) {
+ // set the prev pointer of the next node to the given node
+ if (prev->next != NULL) {
+ assert(prev->next->prev == prev);
+ prev->next->prev = node;
+ }
+ node->next = prev->next;
+ prev->next = node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_nsec3_node(const knot_node_t *node,
+ int check_version)
+{
+ knot_node_t *nsec3_node = node->nsec3_node;
+ if (nsec3_node == NULL) {
+ return NULL;
+ }
+
+ if (check_version) {
+ int new_gen = knot_node_zone_gen_is_new(node);
+ int old_gen = knot_node_zone_gen_is_old(node);
+// short ver = knot_node_zone_generation(node);
+ assert(new_gen || !knot_node_is_new(nsec3_node));
+ if (old_gen && knot_node_is_new(nsec3_node)) {
+ return NULL;
+ } else if (new_gen && knot_node_is_old(nsec3_node)) {
+ nsec3_node = nsec3_node->new_node;
+ }
+ }
+
+ return nsec3_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_nsec3_node(knot_node_t *node, knot_node_t *nsec3_node)
+{
+ node->nsec3_node = nsec3_node;
+ if (nsec3_node != NULL) {
+ nsec3_node->nsec3_referer = node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_node_owner(const knot_node_t *node)
+{
+ return node->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_node_get_owner(const knot_node_t *node)
+{
+ return node->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_owner(knot_node_t *node, knot_dname_t* owner)
+{
+ if (node) {
+ /* Retain new owner and release old owner. */
+ knot_dname_retain(owner);
+ knot_dname_release(node->owner);
+ node->owner = owner;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_wildcard_child(const knot_node_t *node,
+ int check_version)
+{
+ knot_node_t *w = node->wildcard_child;
+
+ if (check_version && w != 0) {
+ int new_gen = knot_node_zone_gen_is_new(node);
+ int old_gen = knot_node_zone_gen_is_old(node);
+// short ver = knot_node_zone_generation(node);
+
+ if (old_gen && knot_node_is_new(w)) {
+ return NULL;
+ } else if (new_gen && knot_node_is_old(w)) {
+ assert(w->new_node != NULL);
+ w = w->new_node;
+ }
+ }
+
+ return w;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_wildcard_child(knot_node_t *node,
+ knot_node_t *wildcard_child)
+{
+ node->wildcard_child = wildcard_child;
+// assert(wildcard_child->parent == node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_current(const knot_node_t *node)
+{
+ if (node == NULL || node->zone == NULL
+ || knot_zone_contents(node->zone) == NULL) {
+ return node;
+ }
+
+ int new_gen = knot_node_zone_gen_is_new(node);
+ int old_gen = knot_node_zone_gen_is_old(node);
+// short ver = knot_node_zone_generation(node);
+
+ if (old_gen && knot_node_is_new(node)) {
+ return NULL;
+ } else if (new_gen && knot_node_is_old(node)) {
+ assert(node->new_node != NULL);
+ return node->new_node;
+ }
+ return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_get_current(knot_node_t *node)
+{
+ if (node == NULL || node->zone == NULL
+ || knot_zone_contents(node->zone) == NULL) {
+ return node;
+ }
+
+ int new_gen = knot_node_zone_gen_is_new(node);
+ int old_gen = knot_node_zone_gen_is_old(node);
+// short ver = knot_node_zone_generation(node);
+
+ if (old_gen && knot_node_is_new(node)) {
+ return NULL;
+ } else if (new_gen && knot_node_is_old(node)) {
+ assert(node->new_node != NULL);
+ return node->new_node;
+ }
+
+ assert((old_gen && knot_node_is_old(node))
+ || (new_gen && knot_node_is_new(node))
+ || (!old_gen && !new_gen));
+
+ return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_node_t *knot_node_new_node(const knot_node_t *node)
+{
+ return node->new_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_node_t *knot_node_get_new_node(const knot_node_t *node)
+{
+ return node->new_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_new_node(knot_node_t *node,
+ knot_node_t *new_node)
+{
+ node->new_node = new_node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_zone(knot_node_t *node, knot_zone_t *zone)
+{
+ node->zone = zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_update_ref(knot_node_t **ref)
+{
+ if (*ref != NULL && knot_node_is_old(*ref)) {
+ *ref = (*ref)->new_node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_update_refs(knot_node_t *node)
+{
+ /* CLEANUP */
+ /* OMG! */
+ // reference to previous node
+ knot_node_update_ref(&node->prev);
+// if (node->prev && knot_node_is_old(node->prev)) {
+// assert(node->prev->new_node != NULL);
+// node->prev = node->prev->new_node;
+// }
+
+ // reference to next node
+ knot_node_update_ref(&node->next);
+// if (node->next && knot_node_is_old(node->next)) {
+// assert(node->next->new_node != NULL);
+// node->next = node->next->new_node;
+// }
+
+ // reference to parent
+// if (node->parent && knot_node_is_old(node->parent)) {
+// assert(node->parent->new_node != NULL);
+// // do not use the API function to set parent, so that children count
+// // is not changed
+// //knot_node_set_parent(node, node->parent->new_node);
+// node->parent = node->parent->new_node;
+// }
+ knot_node_update_ref(&node->parent);
+
+ // reference to wildcard child
+ knot_node_update_ref(&node->wildcard_child);
+// if (node->wildcard_child && knot_node_is_old(node->wildcard_child)) {
+// assert(node->wildcard_child->new_node != NULL);
+// node->wildcard_child = node->wildcard_child->new_node;
+// }
+
+ // reference to NSEC3 node
+ knot_node_update_ref(&node->nsec3_node);
+// if (node->nsec3_node && knot_node_is_old(node->nsec3_node)) {
+// assert(node->nsec3_node->new_node != NULL);
+// node->nsec3_node = node->nsec3_node->new_node;
+// }
+
+ // reference to NSEC3 referrer
+ knot_node_update_ref(&node->nsec3_referer);
+// if (node->nsec3_referer && knot_node_is_old(node->nsec3_referer)) {
+// assert(node->nsec3_referer->new_node != NULL);
+// node->nsec3_referer = node->nsec3_referer->new_node;
+// }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_deleg_point(knot_node_t *node)
+{
+ knot_node_flags_set_deleg(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_deleg_point(const knot_node_t *node)
+{
+ return knot_node_flags_get_deleg(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_non_auth(knot_node_t *node)
+{
+ knot_node_flags_set_nonauth(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_non_auth(const knot_node_t *node)
+{
+ return knot_node_flags_get_nonauth(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_auth(const knot_node_t *node)
+{
+ return (node->flags == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_new(const knot_node_t *node)
+{
+ return knot_node_flags_get_new(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_is_old(const knot_node_t *node)
+{
+ return knot_node_flags_get_old(node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_new(knot_node_t *node)
+{
+ knot_node_flags_set_new(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_set_old(knot_node_t *node)
+{
+ knot_node_flags_set_old(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_clear_new(knot_node_t *node)
+{
+ knot_node_flags_clear_new(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_clear_old(knot_node_t *node)
+{
+ knot_node_flags_clear_old(&node->flags);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_node_free_rrsets_from_tree(void *item, void *data)
+{
+ if (item == NULL) {
+ return;
+ }
+
+ knot_rrset_t *rrset = (knot_rrset_t *)(item);
+ knot_rrset_deep_free(&rrset, 0, 1, *((int *)data));
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames)
+{
+ /* CLEANUP */
+// knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+// for (int i = 0; i < node->rrset_count; i++) {
+// knot_rrset_deep_free(&(rrsets[i]), 0, 1, free_rdata_dnames);
+// }
+
+// free(rrsets);
+
+ char *name = knot_dname_to_str(node->owner);
+ free(name);
+
+ gen_tree_destroy(&node->rrset_tree, knot_node_free_rrsets_from_tree,
+ (void *)&free_rdata_dnames);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_node_free(knot_node_t **node, int free_owner, int fix_refs)
+{
+ if (node == NULL || *node == NULL) {
+ return;
+ }
+
+ dbg_node("Freeing node.\n");
+ if ((*node)->rrset_tree != NULL) {
+ dbg_node("Freeing RRSets.\n");
+ gen_tree_destroy(&(*node)->rrset_tree, NULL, NULL);
+ }
+
+ /*! \todo Always release owner? */
+ //if (free_owner) {
+ dbg_node("Releasing owner.\n");
+ knot_dname_release((*node)->owner);
+ //}
+
+ // check nodes referencing this node and fix the references
+
+ if (fix_refs) {
+ // previous node
+ dbg_node("Checking previous.\n");
+ if ((*node)->prev && (*node)->prev->next == (*node)) {
+ (*node)->prev->next = (*node)->next;
+ }
+
+ dbg_node("Checking next.\n");
+ if ((*node)->next && (*node)->next->prev == (*node)) {
+ (*node)->next->prev = (*node)->prev;
+ }
+
+ // NSEC3 node
+ dbg_node("Checking NSEC3.\n");
+ if ((*node)->nsec3_node
+ && (*node)->nsec3_node->nsec3_referer == (*node)) {
+ (*node)->nsec3_node->nsec3_referer = NULL;
+ }
+
+ dbg_node("Checking NSEC3 ref.\n");
+ if ((*node)->nsec3_referer
+ && (*node)->nsec3_referer->nsec3_node == (*node)) {
+ (*node)->nsec3_referer->nsec3_node = NULL;
+ }
+
+ // wildcard child node
+ dbg_node("Checking parent's wildcard child.\n");
+ if ((*node)->parent
+ && (*node)->parent->wildcard_child == (*node)) {
+ (*node)->parent->wildcard_child = NULL;
+ }
+
+ // fix parent's children count
+ if ((*node)->parent) {
+ --(*node)->parent->children;
+ }
+ }
+
+ free(*node);
+ *node = NULL;
+
+ dbg_node("Done.\n");
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_compare(knot_node_t *node1, knot_node_t *node2)
+{
+ return knot_dname_compare(node1->owner, node2->owner);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to)
+{
+ // create new node
+ *to = knot_node_new(from->owner, from->parent, from->flags);
+ if (*to == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Free old rrset_tree, as it will be replaced by shallow copy. */
+ gen_tree_destroy(&(*to)->rrset_tree, 0, 0);
+
+ // copy references
+ // do not use the API function to set parent, so that children count
+ // is not changed
+ memcpy(*to, from, sizeof(knot_node_t));
+
+ // copy RRSets
+ // copy the skip list with the old references
+ /* CLEANUP */
+ (*to)->rrset_tree = gen_tree_shallow_copy(from->rrset_tree);
+// assert((*to)->rrset_tree != from->rrset_tree);
+// (*to)->rrsets = skip_copy_list(from->rrsets);
+ if ((*to)->rrset_tree == NULL) {
+ free(*to);
+ *to = NULL;
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/zone/node.h b/src/libknot/zone/node.h
new file mode 100644
index 0000000..fcb612d
--- /dev/null
+++ b/src/libknot/zone/node.h
@@ -0,0 +1,436 @@
+/*!
+ * \file node.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure representing one node in domain name tree and API for
+ * manipulating it.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/* 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/>.
+ */
+
+#ifndef _KNOT_NODE_H_
+#define _KNOT_NODE_H_
+
+#include "dname.h"
+#include "common/skip-list.h"
+#include "rrset.h"
+#include "common/tree.h"
+#include "common/general-tree.h"
+
+struct knot_zone;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure representing one node in a domain name tree, i.e. one domain
+ * name in a zone.
+ *
+ * RRSets are ordered by type and stored in a skip-list to allow fast lookup.
+ */
+struct knot_node {
+ knot_dname_t *owner; /*!< Domain name being the owner of this node. */
+ struct knot_node *parent; /*!< Parent node in the name hierarchy. */
+
+ /*! \brief Type-ordered list of RRSets belonging to this node. */
+ general_tree_t *rrset_tree;
+
+ unsigned short rrset_count; /*!< Number of RRSets stored in the node. */
+
+ /*! \brief Wildcard node being the direct descendant of this node. */
+ struct knot_node *wildcard_child;
+
+ /*!
+ * \brief Previous node in canonical order.
+ *
+ * Only authoritative nodes or delegation points are referenced by this,
+ * as only they may contain NSEC records needed for authenticating
+ * negative answers.
+ */
+ struct knot_node *prev;
+
+ struct knot_node *next;
+
+ /*!
+ * \brief NSEC3 node corresponding to this node.
+ *
+ * Such NSEC3 node has owner in form of the hashed domain name of this
+ * node prepended as a single label to the zone name.
+ */
+ struct knot_node *nsec3_node;
+
+ struct knot_node *nsec3_referer;
+
+ /*!
+ * \brief Various flags.
+ *
+ * Currently only two:
+ * 0x01 - node is a delegation point
+ * 0x02 - node is non-authoritative (under a delegation point)
+ * 0x80 - node is old and will be removed (during update)
+ * 0x40 - node is new, should not be used while zone is old
+ */
+ uint8_t flags;
+
+ struct knot_node *new_node;
+
+ unsigned int children;
+
+ /*!
+ * \brief Generation of node to be used.
+ *
+ * If set to 0, the old node will be used. Otherwise new nodes will
+ * be used. This applies when getting some referenced node.
+
+ */
+// short **generation;
+
+ struct knot_zone *zone;
+};
+
+typedef struct knot_node knot_node_t;
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Flags used to mark nodes with some property. */
+typedef enum {
+ /*! \brief Node is a delegation point (i.e. marking a zone cut). */
+ KNOT_NODE_FLAGS_DELEG = (uint8_t)0x01,
+ /*! \brief Node is not authoritative (i.e. below a zone cut). */
+ KNOT_NODE_FLAGS_NONAUTH = (uint8_t)0x02,
+ /*! \brief Node is old and will be removed (during update). */
+ KNOT_NODE_FLAGS_OLD = (uint8_t)0x80,
+ /*! \brief Node is new and should not be used while zoen is old. */
+ KNOT_NODE_FLAGS_NEW = (uint8_t)0x40
+} knot_node_flags_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates and initializes new node structure.
+ *
+ * \todo Owner reference counter will be increased.
+ *
+ * \param owner Owner of the created node.
+ * \param parent Parent of the created node.
+ * \param flags Document me.
+ *
+ * \todo Document missing parameters.
+ *
+ * \return Newly created node or NULL if an error occured.
+ */
+knot_node_t *knot_node_new(knot_dname_t *owner, knot_node_t *parent,
+ uint8_t flags);
+
+/*!
+ * \brief Adds an RRSet to the node.
+ *
+ * \param node Node to add the RRSet to.
+ * \param rrset RRSet to add.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ERROR if the RRSet could not be inserted.
+ */
+int knot_node_add_rrset(knot_node_t *node, knot_rrset_t *rrset,
+ int merge);
+
+/*!
+ * \brief Returns the RRSet of the given type from the node.
+ *
+ * \param node Node to get the RRSet from.
+ * \param type Type of the RRSet to retrieve.
+ *
+ * \return RRSet from node \a node having type \a type, or NULL if no such
+ * RRSet exists in this node.
+ */
+const knot_rrset_t *knot_node_rrset(const knot_node_t *node,
+ uint16_t type);
+
+/*!
+ * \brief Returns the RRSet of the given type from the node (non-const version).
+ *
+ * \param node Node to get the RRSet from.
+ * \param type Type of the RRSet to retrieve.
+ *
+ * \return RRSet from node \a node having type \a type, or NULL if no such
+ * RRSet exists in this node.
+ */
+knot_rrset_t *knot_node_get_rrset(knot_node_t *node, uint16_t type);
+
+knot_rrset_t *knot_node_remove_rrset(knot_node_t *node, uint16_t type);
+
+void knot_node_remove_all_rrsets(knot_node_t *node);
+
+/*!
+ * \brief Returns number of RRSets in the node.
+ *
+ * \param node Node to get the RRSet count from.
+ *
+ * \return Number of RRSets in \a node.
+ */
+short knot_node_rrset_count(const knot_node_t *node);
+
+/*!
+ * \brief Returns all RRSets from the node.
+ *
+ * \param node Node to get the RRSets from.
+ *
+ * \return Newly allocated array of RRSets or NULL if an error occured.
+ */
+knot_rrset_t **knot_node_get_rrsets(const knot_node_t *node);
+
+/*!
+ * \brief Returns all RRSets from the node.
+ *
+ * \note This function is identical to knot_node_get_rrsets(), only it returns
+ * non-modifiable data.
+ *
+ * \param node Node to get the RRSets from.
+ *
+ * \return Newly allocated array of RRSets or NULL if an error occured.
+ */
+const knot_rrset_t **knot_node_rrsets(const knot_node_t *node);
+
+/*!
+ * \brief Returns the parent of the node.
+ *
+ * \param node Node to get the parent of.
+ *
+ * \return Parent node of the given node or NULL if no parent has been set (e.g.
+ * node in a zone apex has no parent).
+ */
+const knot_node_t *knot_node_parent(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the parent of the node.
+ *
+ * \param node Node to set the parent of.
+ * \param parent Parent to set to the node.
+ */
+void knot_node_set_parent(knot_node_t *node, knot_node_t *parent);
+
+unsigned int knot_node_children(const knot_node_t *node);
+
+/*!
+ * \brief Returns the previous authoritative node or delegation point in
+ * canonical order or the first node in zone.
+ *
+ * \param node Node to get the previous node of.
+ *
+ * \return Previous authoritative node or delegation point in canonical order or
+ * the first node in zone if \a node is the last node in zone.
+ * \retval NULL if previous node is not set.
+ */
+const knot_node_t *knot_node_previous(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Returns the previous authoritative node or delegation point in
+ * canonical order or the first node in zone.
+ *
+ * \note This function is identical to knot_node_previous() except that it
+ * returns non-const node.
+ *
+ * \param node Node to get the previous node of.
+ *
+ * \return Previous authoritative node or delegation point in canonical order or
+ * the first node in zone if \a node is the last node in zone.
+ * \retval NULL if previous node is not set.
+ */
+knot_node_t *knot_node_get_previous(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the previous node of the given node.
+ *
+ * \param node Node to set the previous node to.
+ * \param prev Previous node to set.
+ */
+void knot_node_set_previous(knot_node_t *node, knot_node_t *prev);
+
+/*!
+ * \brief Returns the NSEC3 node corresponding to the given node.
+ *
+ * \param node Node to get the NSEC3 node for.
+ *
+ * \return NSEC3 node corresponding to \a node (i.e. node with owner name
+ * created by concatenating the hash of owner domain name of \a node
+ * and the name of the zone \a node belongs to).
+ * \retval NULL if the NSEC3 node is not set.
+ */
+const knot_node_t *knot_node_nsec3_node(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the corresponding NSEC3 node of the given node.
+ *
+ * \param node Node to set the NSEC3 node to.
+ * \param nsec3_node NSEC3 node to set.
+ */
+void knot_node_set_nsec3_node(knot_node_t *node, knot_node_t *nsec3_node);
+
+/*!
+ * \brief Returns the owner of the node.
+ *
+ * \param node Node to get the owner of.
+ *
+ * \return Owner of the given node.
+ */
+const knot_dname_t *knot_node_owner(const knot_node_t *node);
+
+/*!
+ * \todo Document me.
+ */
+knot_dname_t *knot_node_get_owner(const knot_node_t *node);
+
+/*!
+ * \brief Set node owner to specified dname.
+ *
+ * Previous owner will be replaced if exist.
+ *
+ * \param node Specified node.
+ * \param owner New owner dname.
+ */
+void knot_node_set_owner(knot_node_t *node, knot_dname_t* owner);
+
+/*!
+ * \brief Returns the wildcard child of the node.
+ *
+ * \param node Node to get the owner of.
+ *
+ * \return Wildcard child of the given node or NULL if it has none.
+ */
+const knot_node_t *knot_node_wildcard_child(const knot_node_t *node,
+ int check_version);
+
+/*!
+ * \brief Sets the wildcard child of the node.
+ *
+ * \param node Node to set the wildcard child of.
+ * \param wildcard_child Wildcard child of the node.
+ */
+void knot_node_set_wildcard_child(knot_node_t *node,
+ knot_node_t *wildcard_child);
+
+const knot_node_t *knot_node_current(const knot_node_t *node);
+
+knot_node_t *knot_node_get_current(knot_node_t *node);
+
+const knot_node_t *knot_node_new_node(const knot_node_t *node);
+
+knot_node_t *knot_node_get_new_node(const knot_node_t *node);
+
+void knot_node_set_new_node(knot_node_t *node,
+ knot_node_t *new_node);
+
+void knot_node_set_zone(knot_node_t *node, struct knot_zone *zone);
+
+void knot_node_update_ref(knot_node_t **ref);
+
+void knot_node_update_refs(knot_node_t *node);
+
+/*!
+ * \brief Mark the node as a delegation point.
+ *
+ * \param node Node to mark as a delegation point.
+ */
+void knot_node_set_deleg_point(knot_node_t *node);
+
+/*!
+ * \brief Checks if the node is a delegation point.
+ *
+ * \param node Node to check.
+ *
+ * \retval <> 0 if \a node is marked as delegation point.
+ * \retval 0 otherwise.
+ */
+int knot_node_is_deleg_point(const knot_node_t *node);
+
+/*!
+ * \brief Mark the node as non-authoritative.
+ *
+ * \param node Node to mark as non-authoritative.
+ */
+void knot_node_set_non_auth(knot_node_t *node);
+
+/*!
+ * \brief Checks if the node is non-authoritative.
+ *
+ * \param node Node to check.
+ *
+ * \retval <> 0 if \a node is marked as non-authoritative.
+ * \retval 0 otherwise.
+ */
+int knot_node_is_non_auth(const knot_node_t *node);
+
+int knot_node_is_auth(const knot_node_t *node);
+
+int knot_node_is_new(const knot_node_t *node);
+
+int knot_node_is_old(const knot_node_t *node);
+
+void knot_node_set_new(knot_node_t *node);
+
+void knot_node_set_old(knot_node_t *node);
+
+void knot_node_clear_new(knot_node_t *node);
+
+void knot_node_clear_old(knot_node_t *node);
+
+/*!
+ * \brief Destroys the RRSets within the node structure.
+ *
+ * \param node Node to be destroyed.
+ * \param free_rdata_dnames Set to <> 0 if you want to delete ALL domain names
+ * present in RDATA. Set to 0 otherwise. (See
+ * knot_rdata_deep_free().)
+ */
+void knot_node_free_rrsets(knot_node_t *node, int free_rdata_dnames);
+
+/*!
+ * \brief Destroys the node structure.
+ *
+ * Does not destroy the RRSets within the node.
+ * Also sets the given pointer to NULL.
+ *
+ * \param node Node to be destroyed.
+ * \param free_owner Set to 0 if you do not want the owner domain name to be
+ * destroyed also. Set to <> 0 otherwise.
+ * \param fix_refs
+ *
+ * \todo Document missing parameters.
+ */
+void knot_node_free(knot_node_t **node, int free_owner, int fix_refs);
+
+/*!
+ * \brief Compares two nodes according to their owner.
+ *
+ * \param node1 First node.
+ * \param node2 Second node.
+ *
+ * \retval < 0 if \a node1 goes before \a node2 according to canonical order
+ * of their owner names.
+ * \retval 0 if they are equal.
+ * \retval > 0 if \a node1 goes after \a node2.
+ */
+int knot_node_compare(knot_node_t *node1, knot_node_t *node2);
+
+int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to);
+
+#endif /* _KNOT_NODE_H_ */
+
+/*! @} */
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;
+}
+
diff --git a/src/libknot/zone/zone-contents.h b/src/libknot/zone/zone-contents.h
new file mode 100644
index 0000000..2856f76
--- /dev/null
+++ b/src/libknot/zone/zone-contents.h
@@ -0,0 +1,556 @@
+/*!
+ * \file zone-contents.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone contents structure and API for manipulating it.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/* 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/>.
+ */
+
+#ifndef _KNOT_ZONE_CONTENTS_H_
+#define _KNOT_ZONE_CONTENTS_H_
+
+//#include <time.h>
+
+#include "zone/node.h"
+#include "dname.h"
+#include "nsec3.h"
+#include "zone/dname-table.h"
+#include "common/tree.h"
+#include "hash/cuckoo-hash-table.h"
+
+#include "zone-tree.h"
+
+struct knot_zone;
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct knot_zone_contents_t {
+ knot_node_t *apex; /*!< Apex node of the zone (holding SOA) */
+
+ ck_hash_table_t *table; /*!< Hash table for holding zone nodes. */
+ knot_zone_tree_t *nodes;
+ knot_zone_tree_t *nsec3_nodes;
+
+ /*!
+ * \todo Unify the use of this field - authoritative nodes vs. all.
+ */
+ uint node_count;
+
+ knot_dname_table_t *dname_table;
+
+ knot_nsec3_params_t nsec3_params;
+
+ /*! \brief Generation of the zone during update.
+ *
+ * Possible values:
+ * - 0 - Original version of the zone. Old nodes should be used.
+ * - 1 - New (updated) zone. New nodes should be used.
+ * - -1 - New (updated) zone, but exactly the stored nodes should be
+ * used, no matter their generation.
+ */
+ short generation;
+
+ struct knot_zone *zone;
+} knot_zone_contents_t;
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_contents_new(knot_node_t *apex,
+ uint node_count,
+ int use_domain_table,
+ struct knot_zone *zone);
+
+time_t knot_zone_contents_version(const knot_zone_contents_t *contents);
+
+void knot_zone_contents_set_version(knot_zone_contents_t *contents,
+ time_t version);
+
+//short knot_zone_contents_generation(const knot_zone_contents_t *contents);
+
+int knot_zone_contents_gen_is_old(const knot_zone_contents_t *contents);
+int knot_zone_contents_gen_is_new(const knot_zone_contents_t *contents);
+int knot_zone_contents_gen_is_finished(const knot_zone_contents_t *contents);
+
+//void knot_zone_contents_switch_generation(knot_zone_contents_t *contents);
+
+void knot_zone_contents_set_gen_old(knot_zone_contents_t *contents);
+void knot_zone_contents_set_gen_new(knot_zone_contents_t *contents);
+void knot_zone_contents_set_gen_new_finished(knot_zone_contents_t *contents);
+
+uint16_t knot_zone_contents_class(const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Adds a node to the given zone.
+ *
+ * Checks if the node belongs to the zone, i.e. if its owner is a subdomain of
+ * the zone's apex. It thus also forbids adding node with the same name as the
+ * zone apex.
+ *
+ * \warning This function may destroy domain names saved in the node, that
+ * are already present in the zone.
+ *
+ * \param zone Zone to add the node into.
+ * \param node Node to add into the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ * \retval KNOT_EHASH
+ */
+int knot_zone_contents_add_node(knot_zone_contents_t *contents,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table);
+
+/*!
+ * \brief Adds a RRSet to the given zone.
+ *
+ * Checks if the RRSet belongs to the zone, i.e. if its owner is a subdomain of
+ * the zone's apex. The RRSet is inserted only if the node is given, or if
+ * a node where the RRSet should belong is found in the zone.
+ *
+ * \warning The function does not check if the node is already inserted in the
+ * zone, just assumes that it is.
+ * \warning This function may destroy domain names saved in the RRSet, that
+ * are already present in the zone.
+ *
+ * \param zone Zone to add the node into.
+ * \param rrset RRSet to add into the zone.
+ * \param node Node the RRSet should be inserted into. (Should be a node of the
+ * given zone.) If set to NULL, the function will find proper node
+ * and set it to this parameter.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_add_rrset(knot_zone_contents_t *contents,
+ knot_rrset_t *rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table);
+
+int knot_zone_contents_add_rrsigs(knot_zone_contents_t *contents,
+ knot_rrset_t *rrsigs,
+ knot_rrset_t **rrset, knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table);
+
+/*!
+ * \brief Adds a node holding NSEC3 records to the given zone.
+ *
+ * Checks if the node belongs to the zone, i.e. if its owner is a subdomain of
+ * the zone's apex. It does not check if the node really contains any NSEC3
+ * records, nor if the name is a hash (as there is actually no way of
+ * determining this).
+ *
+ * \param zone Zone to add the node into.
+ * \param node Node to add into the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *contents,
+ knot_node_t *node, int create_parents,
+ uint8_t flags, int use_domain_table);
+
+int knot_zone_contents_add_nsec3_rrset(knot_zone_contents_t *contents,
+ knot_rrset_t *rrset,
+ knot_node_t **node,
+ knot_rrset_dupl_handling_t dupl,
+ int use_domain_table);
+
+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);
+
+//knot_zone_tree_node_t *knot_zone_contents_remove_node(
+// knot_zone_contents_t *contents, const knot_node_t *node);
+
+int knot_zone_contents_remove_nsec3_node(knot_zone_contents_t *contents,
+ const knot_node_t *node, knot_zone_tree_node_t **removed);
+
+/*!
+ * \warning Always call knot_zone_adjust_dnames() prior to calling this
+ * function. Otherwise the node count would not be set.
+ *
+ * \note Currently, all nodes (even non-authoritative) are inserted into the
+ * hash table.
+ */
+int knot_zone_contents_create_and_fill_hash_table(
+ knot_zone_contents_t *contents);
+
+/*!
+ * \brief Tries to find a node with the specified name in the zone.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+knot_node_t *knot_zone_contents_get_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find a node with the specified name among the NSEC3 nodes
+ * of the zone.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+knot_node_t *knot_zone_contents_get_nsec3_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find a node with the specified name in the zone.
+ *
+ * \note This function is identical to knot_zone_contents_get_node(), only it returns
+ * constant reference.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+const knot_node_t *knot_zone_contents_find_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find domain name in the given zone using AVL tree.
+ *
+ * \param[in] zone Zone to search for the name.
+ * \param[in] name Domain name to search for.
+ * \param[out] node The found node (if it was found, otherwise it may contain
+ * arbitrary node).
+ * \param[out] closest_encloser Closest encloser of the given name in the zone.
+ * \param[out] previous Previous domain name in canonical order.
+ *
+ * \retval KNOT_ZONE_NAME_FOUND if node with owner \a name was found.
+ * \retval KNOT_ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_find_dname(const knot_zone_contents_t *contents,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser,
+ const knot_node_t **previous);
+
+/*!
+ * \brief Finds previous name in canonical order to the given name in the zone.
+ *
+ * \param zone Zone to search for the name.
+ * \param name Domain name to find the previous domain name of.
+ *
+ * \return Previous node in canonical order, or NULL if some parameter is wrong.
+ */
+const knot_node_t *knot_zone_contents_find_previous(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+knot_node_t *knot_zone_contents_get_previous(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+const knot_node_t *knot_zone_contents_find_previous_nsec3(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+knot_node_t *knot_zone_contents_get_previous_nsec3(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+#ifdef USE_HASH_TABLE
+/*!
+ * \brief Tries to find domain name in the given zone using the hash table.
+ *
+ * \param[in] zone Zone to search for the name.
+ * \param[in] name Domain name to search for.
+ * \param[out] node The found node (if it was found, otherwise it may contain
+ * arbitrary node).
+ * \param[out] closest_encloser Closest encloser of the given name in the zone.
+ * \param[out] previous Previous domain name in canonical order.
+ *
+ * \retval KNOT_ZONE_NAME_FOUND if node with owner \a name was found.
+ * \retval KNOT_ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EBADZONE
+ */
+int knot_zone_contents_find_dname_hash(const knot_zone_contents_t *contents,
+ const knot_dname_t *name,
+ const knot_node_t **node,
+ const knot_node_t **closest_encloser);
+#endif
+
+/*!
+ * \brief Tries to find a node with the specified name among the NSEC3 nodes
+ * of the zone.
+ *
+ * \note This function is identical to knot_zone_contents_get_nsec3_node(), only it
+ * returns constant reference.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+const knot_node_t *knot_zone_contents_find_nsec3_node(
+ const knot_zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Finds NSEC3 node and previous NSEC3 node in canonical order,
+ * corresponding to the given domain name.
+ *
+ * This functions creates a NSEC3 hash of \a name and tries to find NSEC3 node
+ * with the hashed domain name as owner.
+ *
+ * \param[in] zone Zone to search in.
+ * \param[in] name Domain name to get the corresponding NSEC3 nodes for.
+ * \param[out] nsec3_node NSEC3 node corresponding to \a name (if found,
+ * otherwise this may be an arbitrary NSEC3 node).
+ * \param[out] nsec3_previous The NSEC3 node immediately preceding hashed domain
+ * name corresponding to \a name in canonical order.
+ *
+ * \retval KNOT_ZONE_NAME_FOUND if the corresponding NSEC3 node was found.
+ * \retval KNOT_ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENSEC3PAR
+ * \retval KNOT_ECRYPTO
+ * \retval KNOT_ERROR
+ */
+int knot_zone_contents_find_nsec3_for_name(
+ const knot_zone_contents_t *contents,
+ const knot_dname_t *name,
+ const knot_node_t **nsec3_node,
+ const knot_node_t **nsec3_previous,
+ int check_ver);
+/*!
+ * \brief Returns the apex node of the zone.
+ *
+ * \param zone Zone to get the apex of.
+ *
+ * \return Zone apex node.
+ */
+const knot_node_t *knot_zone_contents_apex(
+ const knot_zone_contents_t *contents);
+
+knot_node_t *knot_zone_contents_get_apex(
+ const knot_zone_contents_t *contents);
+
+//knot_dname_t *knot_zone_contents_name(
+// const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Optimizes zone by replacing domain names in RDATA with references to
+ * domain names present in zone (as node owners).
+ *
+ * \param zone Zone to adjust domain names in.
+ */
+int knot_zone_contents_adjust(knot_zone_contents_t *contents, int check_ver);
+
+/*!
+ * \brief Parses the NSEC3PARAM record stored in the zone.
+ *
+ * This function properly fills in the nsec3_params field of the zone structure
+ * according to data stored in the NSEC3PARAM record. This is necessary to do
+ * before any NSEC3 operations on the zone are requested, otherwise they will
+ * fail (error KNOT_ENSEC3PAR).
+ *
+ * \note If there is no NSEC3PARAM record in the zone, this function clears
+ * the nsec3_params field of the zone structure (fills it with zeros).
+ *
+ * \param zone Zone to get the NSEC3PARAM record from.
+ */
+int knot_zone_contents_load_nsec3param(knot_zone_contents_t *contents);
+
+/*!
+ * \brief Checks if the zone uses NSEC3.
+ *
+ * This function will return 0 if the NSEC3PARAM record was not parse prior to
+ * calling it.
+ *
+ * \param zone Zone to check.
+ *
+ * \retval <> 0 if the zone uses NSEC3.
+ * \retval 0 if it does not.
+ *
+ * \see knot_zone_contents_load_nsec3param()
+ */
+int knot_zone_contents_nsec3_enabled(const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Returns the parsed NSEC3PARAM record of the zone.
+ *
+ * \note You must parse the NSEC3PARAM record prior to calling this function
+ * (knot_zone_contents_load_nsec3param()).
+ *
+ * \param zone Zone to get the NSEC3PARAM record from.
+ *
+ * \return Parsed NSEC3PARAM from the zone or NULL if the zone does not use
+ * NSEC3 or the record was not parsed before.
+ *
+ * \see knot_zone_contents_load_nsec3param()
+ */
+const knot_nsec3_params_t *knot_zone_contents_nsec3params(
+ const knot_zone_contents_t *contents);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * This function uses post-order depth-first forward traversal, i.e. the
+ * function is first recursively applied to subtrees and then to the root.
+ *
+ * \param zone Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_tree_apply_postorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * This function uses in-order depth-first forward traversal, i.e. the function
+ * is first recursively applied to left subtree, then to the root and then to
+ * the right subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_tree_apply_inorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * This function uses in-order depth-first reverse traversal, i.e. the function
+ * is first recursively applied to right subtree, then to the root and then to
+ * the left subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_tree_apply_inorder_reverse(
+ knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data), void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * This function uses post-order depth-first forward traversal, i.e. the
+ * function is first recursively applied to subtrees and then to the root.
+ *
+ * \param zone NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_nsec3_apply_postorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * This function uses in-order depth-first forward traversal, i.e. the function
+ * is first recursively applied to left subtree, then to the root and then to
+ * the right subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_nsec3_apply_inorder(knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * This function uses in-order depth-first reverse traversal, i.e. the function
+ * is first recursively applied to right subtree, then to the root and then to
+ * the left subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param zone NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int knot_zone_contents_nsec3_apply_inorder_reverse(
+ knot_zone_contents_t *contents,
+ void (*function)(knot_node_t *node, void *data), void *data);
+
+knot_zone_tree_t *knot_zone_contents_get_nodes(
+ knot_zone_contents_t *contents);
+
+knot_zone_tree_t *knot_zone_contents_get_nsec3_nodes(
+ knot_zone_contents_t *contents);
+
+ck_hash_table_t *knot_zone_contents_get_hash_table(
+ knot_zone_contents_t *contents);
+
+int knot_zone_contents_dname_table_apply(knot_zone_contents_t *contents,
+ void (*function)(knot_dname_t *,
+ void *),
+ void *data);
+
+/*!
+ * \brief Creates a shallow copy of the zone (no stored data are copied).
+ *
+ * This function creates a new zone structure in \a to, creates new trees for
+ * regular nodes and for NSEC3 nodes, creates new hash table and a new domain
+ * table. It also fills these structures with the exact same data as the
+ * original zone is - no copying of stored data is done, just pointers are
+ * copied.
+ *
+ * \param from Original zone.
+ * \param to Copy of the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
+ knot_zone_contents_t **to);
+
+void knot_zone_contents_free(knot_zone_contents_t **contents);
+
+void knot_zone_contents_deep_free(knot_zone_contents_t **contents,
+ int destroy_dname_table);
+
+#endif
+
+/*! @} */
diff --git a/src/libknot/zone/zone-tree.c b/src/libknot/zone/zone-tree.c
new file mode 100644
index 0000000..cdf128e
--- /dev/null
+++ b/src/libknot/zone/zone-tree.c
@@ -0,0 +1,470 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+
+#include "zone-tree.h"
+#include "zone/node.h"
+#include "util/error.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+// AVL tree functions
+TREE_DEFINE(knot_zone_tree_node, avl);
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_tree_node_compare(knot_zone_tree_node_t *node1,
+ knot_zone_tree_node_t *node2)
+{
+ assert(node1 != NULL);
+ assert(node2 != NULL);
+ assert(node1->node != NULL);
+ assert(node2->node != NULL);
+ assert(knot_node_owner(node1->node) != NULL);
+ assert(knot_node_owner(node2->node) != NULL);
+
+ return knot_node_compare(node1->node, node2->node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_tree_delete_subtree(knot_zone_tree_node_t *root)
+{
+ if (root == NULL) {
+ return;
+ }
+
+ knot_zone_tree_delete_subtree(root->avl.avl_left);
+ knot_zone_tree_delete_subtree(root->avl.avl_right);
+ free(root);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_zone_tree_copy_node(knot_zone_tree_node_t *from,
+ knot_zone_tree_node_t **to)
+{
+ if (from == NULL) {
+ *to = NULL;
+ return KNOT_EOK;
+ }
+
+ *to = (knot_zone_tree_node_t *)
+ malloc(sizeof(knot_zone_tree_node_t));
+ if (*to == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ (*to)->node = from->node;
+ (*to)->avl.avl_height = from->avl.avl_height;
+
+ int ret = knot_zone_tree_copy_node(from->avl.avl_left,
+ &(*to)->avl.avl_left);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_zone_tree_copy_node(from->avl.avl_right,
+ &(*to)->avl.avl_right);
+ if (ret != KNOT_EOK) {
+ knot_zone_tree_delete_subtree((*to)->avl.avl_left);
+ (*to)->avl.avl_left = NULL;
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void knot_zone_tree_free_node(knot_zone_tree_node_t *node,
+ int free_data, int free_owner)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ knot_zone_tree_free_node(node->avl.avl_left, free_data, free_owner);
+
+ knot_zone_tree_free_node(node->avl.avl_right, free_data, free_owner);
+
+ if (free_data) {
+ knot_node_free(&node->node, free_owner, 0);
+ }
+
+ free(node);
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_init(knot_zone_tree_t *tree)
+{
+ if (tree == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_INIT(tree, knot_zone_tree_node_compare);
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_insert(knot_zone_tree_t *tree, knot_node_t *node)
+{
+ if (tree == NULL || node == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_node_t *znode = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (znode == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ znode->node = node;
+ znode->avl.avl_left = NULL;
+ znode->avl.avl_right = NULL;
+ znode->avl.avl_height = 0;
+
+ /*! \todo How to know if this was successful? */
+ TREE_INSERT(tree, knot_zone_tree_node, avl, znode);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_find(knot_zone_tree_t *tree, const knot_dname_t *owner,
+ const knot_node_t **found)
+{
+ if (tree == NULL || owner == NULL || found == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_node_t *f = NULL;
+ int ret = knot_zone_tree_get(tree, owner, &f);
+ *found = f;
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_get(knot_zone_tree_t *tree, const knot_dname_t *owner,
+ knot_node_t **found)
+{
+ if (tree == NULL || owner == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *found = NULL;
+
+ // create dummy node to use for lookup
+ knot_zone_tree_node_t *tmp = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // create dummy data node to use for lookup
+ knot_node_t *tmp_data = knot_node_new(
+ (knot_dname_t *)owner, NULL, 0);
+ if (tmp_data == NULL) {
+ free(tmp);
+ return KNOT_ENOMEM;
+ }
+ tmp->node = tmp_data;
+
+ knot_zone_tree_node_t *n = TREE_FIND(tree, knot_zone_tree_node, avl,
+ tmp);
+
+ knot_node_free(&tmp_data, 0, 0);
+ free(tmp);
+
+ if (n != NULL) {
+ *found = n->node;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_find_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ const knot_node_t **found,
+ const knot_node_t **previous,
+ int check_version)
+{
+ if (tree == NULL || owner == NULL || found == NULL || previous == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_node_t *f, *p;
+ int ret = knot_zone_tree_get_less_or_equal(tree, owner, &f, &p, check_version);
+
+ *found = f;
+ *previous = p;
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_node_t **found,
+ knot_node_t **previous,
+ int check_version)
+{
+ if (tree == NULL || owner == NULL || found == NULL
+ || previous == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_tree_node_t *f = NULL, *prev = NULL;
+
+ // create dummy node to use for lookup
+ knot_zone_tree_node_t *tmp = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // create dummy data node to use for lookup
+ knot_node_t *tmp_data = knot_node_new(
+ (knot_dname_t *)owner, NULL, 0);
+ if (tmp_data == NULL) {
+ free(tmp);
+ return KNOT_ENOMEM;
+ }
+ tmp->node = tmp_data;
+
+ int exact_match = TREE_FIND_LESS_EQUAL(
+ tree, knot_zone_tree_node, avl, tmp, &f, &prev);
+
+ knot_node_free(&tmp_data, 0, 0);
+ free(tmp);
+
+ *found = (exact_match > 0) ? f->node : NULL;
+
+ if (exact_match < 0) {
+ // previous is not really previous but should be the leftmost
+ // node in the tree; take it's previous
+ assert(prev != NULL);
+ *previous = knot_node_get_previous(prev->node, check_version);
+ exact_match = 0;
+ } else if (prev == NULL) {
+ if (!exact_match) {
+ printf("Searched for owner %s in zone tree.\n",
+ knot_dname_to_str(owner));
+ printf("Exact match: %d\n", exact_match);
+ printf("Found node: %p: %s.\n", f, (f)
+ ? knot_dname_to_str(knot_node_owner(f->node))
+ : "none");
+ printf("Previous node: %p: %s.\n", prev, (prev)
+ ? knot_dname_to_str(knot_node_owner(prev->node))
+ : "none");
+ }
+
+ // 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 > 0);
+ assert(f != NULL);
+ *previous = knot_node_get_previous(f->node, check_version);
+ } else {
+ // otherwise check if the previous node is not an empty
+ // non-terminal
+ /*! \todo Here we assume that the 'prev' pointer always points
+ * to an empty non-terminal.
+ */
+ *previous = (knot_node_rrset_count(prev->node) == 0)
+ ? knot_node_get_previous(prev->node, check_version)
+ : prev->node;
+ }
+
+ assert(exact_match >= 0);
+
+ return exact_match;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_remove(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_zone_tree_node_t **removed)
+{
+ if (tree == NULL || owner == NULL || removed == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // create dummy node to use for lookup
+ knot_zone_tree_node_t *tmp = (knot_zone_tree_node_t *)malloc(
+ sizeof(knot_zone_tree_node_t));
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // create dummy data node to use for lookup
+ knot_node_t *tmp_data = knot_node_new(
+ (knot_dname_t *)owner, NULL, 0);
+ if (tmp_data == NULL) {
+ free(tmp);
+ return KNOT_ENOMEM;
+ }
+ tmp->node = tmp_data;
+
+ // we must first find the node, so that it may be destroyed
+ knot_zone_tree_node_t *n = TREE_FIND(tree, knot_zone_tree_node, avl,
+ tmp);
+
+ /*! \todo How to know if this was successful? */
+ TREE_REMOVE(tree, knot_zone_tree_node, avl, tmp);
+
+ knot_node_free(&tmp_data, 0, 0);
+ free(tmp);
+
+// *removed = (n) ? n->node : NULL;
+// free(n);
+ *removed = n;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_forward_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_FORWARD_APPLY(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_forward_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_POST_ORDER_APPLY(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_reverse_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_REVERSE_APPLY(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_reverse_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data)
+{
+ if (tree == NULL || function == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ TREE_REVERSE_APPLY_POST(tree, knot_zone_tree_node, avl,
+ function, data);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zone_tree_shallow_copy(knot_zone_tree_t *from,
+ knot_zone_tree_t *to)
+{
+ if (to == NULL || from == NULL) {
+ return KNOT_EBADARG;
+ }
+ /*
+ * This function will copy the tree by hand, so that the nodes
+ * do not have to be inserted the normal way. It should be substantially
+ * faster.
+ */
+
+ to->th_cmp = from->th_cmp;
+
+ return knot_zone_tree_copy_node(from->th_root, &to->th_root);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_tree_free(knot_zone_tree_t **tree)
+{
+ if (tree == NULL || *tree == NULL) {
+ return;
+ }
+ knot_zone_tree_free_node((*tree)->th_root, 0, 0);
+ free(*tree);
+ *tree = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_tree_deep_free(knot_zone_tree_t **tree, int free_owners)
+{
+ if (tree == NULL || *tree == NULL) {
+ return;
+ }
+ knot_zone_tree_free_node((*tree)->th_root, 1, free_owners);
+ free(*tree);
+ *tree = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
diff --git a/src/libknot/zone/zone-tree.h b/src/libknot/zone/zone-tree.h
new file mode 100644
index 0000000..0971749
--- /dev/null
+++ b/src/libknot/zone/zone-tree.h
@@ -0,0 +1,300 @@
+/*!
+ * \file zone-tree.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone tree structure and API for manipulating it.
+ *
+ * Implemented as AVL tree.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/* 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/>.
+ */
+
+#ifndef _KNOT_ZONE_TREE_H_
+#define _KNOT_ZONE_TREE_H_
+
+#include "common/tree.h"
+#include "zone/node.h"
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct knot_zone_tree_node {
+ /*! \brief Structure for connecting this node to an AVL tree. */
+ TREE_ENTRY(knot_zone_tree_node) avl;
+ /*! \brief Zone tree data. */
+ knot_node_t *node;
+ /*! \brief Owner of the node. */
+// knot_dname_t *owner;
+} knot_zone_tree_node_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef TREE_HEAD(knot_zone_tree, knot_zone_tree_node) knot_zone_tree_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Initializes the zone tree.
+ *
+ * Does not allocate the structure. Must be called before any use of the tree.
+ *
+ * \param tree Zone tree structure to initialize.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_init(knot_zone_tree_t *tree);
+
+/*!
+ * \brief Inserts the given node into the zone tree.
+ *
+ * \param tree Zone tree to insert the node into.
+ * \param node Node to insert.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_insert(knot_zone_tree_t *tree, knot_node_t *node);
+
+/*!
+ * \brief Finds node with the given owner in the zone tree.
+ *
+ * \param tree Zone tree to search in.
+ * \param owner Owner of the node to find.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_find(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ const knot_node_t **found);
+
+/*!
+ * \brief Finds node with the given owner in the zone tree.
+ *
+ * \note This function is identical to knot_zone_tree_find() except that it
+ * returns non-const node.
+ *
+ * \param tree Zone tree to search in.
+ * \param owner Owner of the node to find.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_get(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_node_t **found);
+
+/*!
+ * \brief Tries to find the given domain name in the zone tree and returns the
+ * associated node and previous node in canonical order.
+ *
+ * \param zone Zone to search in.
+ * \param owner Owner of the node to find.
+ * \param found Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a owner 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 found holds the
+ * zone node with \a owner as its owner.
+ * \a previous is set properly.
+ * \retval 0 if the domain name was not found. \a found may hold any (or none)
+ * node. \a previous is set properly.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_find_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ const knot_node_t **found,
+ const knot_node_t **previous,
+ int check_version);
+
+/*!
+ * \brief Tries to find the given domain name in the zone tree and returns the
+ * associated node and previous node in canonical order.
+ *
+ * \note This function is identical to knot_zone_tree_find_less_or_equal()
+ * except that it returns non-const nodes.
+ *
+ * \param zone Zone to search in.
+ * \param owner Owner of the node to find.
+ * \param found Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a owner 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 found holds the
+ * zone node with \a owner as its owner.
+ * \a previous is set properly.
+ * \retval 0 if the domain name was not found. \a found may hold any (or none)
+ * node. \a previous is set properly.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_get_less_or_equal(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_node_t **found,
+ knot_node_t **previous,
+ int check_version);
+
+/*!
+ * \brief Removes node with the given owner from the zone tree and returns it.
+ *
+ * \param tree Zone tree to remove the node from.
+ * \param owner Owner of the node to find.
+ * \param removed The removed node.
+ *
+ * \retval The removed node.
+ */
+int knot_zone_tree_remove(knot_zone_tree_t *tree,
+ const knot_dname_t *owner,
+ knot_zone_tree_node_t **removed);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses in-order depth-first forward traversal, i.e. the function
+ * is first recursively applied to left subtree, then to the root and then to
+ * the right subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_forward_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses post-order depth-first forward traversal, i.e. the
+ * function is first recursively applied to subtrees and then to the root.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_forward_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses in-order depth-first reverse traversal, i.e. the function
+ * is first recursively applied to right subtree, then to the root and then to
+ * the left subtree.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_reverse_apply_inorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Applies the given function to each node in the zone.
+ *
+ * This function uses post-order depth-first reverse traversal, i.e. the
+ * function is first recursively applied to right subtree, then to the
+ * left subtree and then to the root.
+ *
+ * \note This implies that the zone is stored in a binary tree. Is there a way
+ * to make this traversal independent on the underlying structure?
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_zone_tree_reverse_apply_postorder(knot_zone_tree_t *tree,
+ void (*function)(
+ knot_zone_tree_node_t *node,
+ void *data),
+ void *data);
+
+/*!
+ * \brief Copies the whole zone tree structure (but not the data contained
+ * within).
+ *
+ * \warning This function does not check if the target zone tree is empty,
+ * it just replaces the root pointer.
+ *
+ * \param from Original zone tree.
+ * \param to Zone tree to copy the original one into.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_zone_tree_shallow_copy(knot_zone_tree_t *from,
+ knot_zone_tree_t *to);
+
+/*!
+ * \brief Destroys the zone tree, not touching the saved data.
+ *
+ * \param tree Zone tree to be destroyed.
+ */
+void knot_zone_tree_free(knot_zone_tree_t **tree);
+
+/*!
+ * \brief Destroys the zone tree, together with the saved data.
+ *
+ * \param tree Zone tree to be destroyed.
+ * \param free_owners Set to <> 0 if owners of the nodes should be destroyed
+ * as well. Set to 0 otherwise.
+ */
+void knot_zone_tree_deep_free(knot_zone_tree_t **tree, int free_owners);
+
+/*----------------------------------------------------------------------------*/
+
+#endif // _KNOT_ZONE_TREE_H_
+
+/*! @} */
+
diff --git a/src/libknot/zone/zone.c b/src/libknot/zone/zone.c
new file mode 100644
index 0000000..9de1a53
--- /dev/null
+++ b/src/libknot/zone/zone.c
@@ -0,0 +1,246 @@
+/* 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 <config.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <urcu.h>
+
+#include "common.h"
+#include "zone/zone.h"
+#include "zone/node.h"
+#include "dname.h"
+#include "consts.h"
+#include "util/descriptor.h"
+#include "nsec3.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "util/utils.h"
+#include "common/tree.h"
+#include "common/base32hex.h"
+#include "hash/cuckoo-hash-table.h"
+#include "zone/zone-contents.h"
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zone_new_empty(knot_dname_t *name)
+{
+ if (!name) {
+ return 0;
+ }
+
+ dbg_zone("Creating new zone!\n");
+
+ knot_zone_t *zone = malloc(sizeof(knot_zone_t));
+ if (zone == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ memset(zone, 0, sizeof(knot_zone_t));
+
+ // save the zone name
+ dbg_zone("Setting zone name.\n");
+ zone->name = name;
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zone_new(knot_node_t *apex, uint node_count,
+ int use_domain_table)
+{
+ knot_zone_t *zone = knot_zone_new_empty(
+ knot_dname_deep_copy(knot_node_owner(apex)));
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ dbg_zone("Creating zone contents.\n");
+ zone->contents = knot_zone_contents_new(apex, node_count,
+ use_domain_table, zone);
+ if (zone->contents == NULL) {
+ knot_dname_release(zone->name);
+ free(zone);
+ return NULL;
+ }
+
+ zone->contents->zone = zone;
+
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_get_contents(
+ const knot_zone_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return rcu_dereference(zone->contents);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_zone_contents_t *knot_zone_contents(
+ const knot_zone_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return rcu_dereference(zone->contents);
+}
+
+/*----------------------------------------------------------------------------*/
+
+time_t knot_zone_version(const knot_zone_t *zone)
+{
+ if (zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return zone->version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_set_version(knot_zone_t *zone, time_t version)
+{
+ if (zone == NULL) {
+ return;
+ }
+
+ zone->version = version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_zone_is_master(const knot_zone_t *zone)
+{
+ return zone->master;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_set_master(knot_zone_t *zone, short master)
+{
+ zone->master = master;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const void *knot_zone_data(const knot_zone_t *zone)
+{
+ return zone->data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_set_data(knot_zone_t *zone, void *data)
+{
+ zone->data = data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_zone_name(const knot_zone_t *zone)
+{
+ return zone->name;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zone_switch_contents(knot_zone_t *zone,
+ knot_zone_contents_t *new_contents)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ knot_zone_contents_t *old_contents =
+ rcu_xchg_pointer(&zone->contents, new_contents);
+ return old_contents;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_free(knot_zone_t **zone)
+{
+ if (zone == NULL || *zone == NULL) {
+ return;
+ }
+
+ dbg_zone("zone_free().\n");
+
+ if ((*zone)->contents
+ && !knot_zone_contents_gen_is_old((*zone)->contents)) {
+ // zone is in the middle of an update, report
+ dbg_zone("Destroying zone that is in the middle of an "
+ "update.\n");
+ }
+
+ knot_dname_release((*zone)->name);
+
+ /* Call zone data destructor if exists. */
+ if ((*zone)->dtor) {
+ (*zone)->dtor(*zone);
+ }
+
+ knot_zone_contents_free(&(*zone)->contents);
+ free(*zone);
+ *zone = NULL;
+
+ dbg_zone("Done.\n");
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table)
+{
+ if (zone == NULL || *zone == NULL) {
+ return;
+ }
+
+ if ((*zone)->contents
+ && !knot_zone_contents_gen_is_old((*zone)->contents)) {
+ // zone is in the middle of an update, report
+ dbg_zone("Destroying zone that is in the middle of an "
+ "update.\n");
+ }
+
+dbg_zone_exec(
+ char *name = knot_dname_to_str((*zone)->name);
+ dbg_zone("Destroying zone %p, name: %s.\n", *zone, name);
+ free(name);
+);
+
+ knot_dname_release((*zone)->name);
+
+ /* Call zone data destructor if exists. */
+ if ((*zone)->dtor) {
+ (*zone)->dtor(*zone);
+ }
+
+ knot_zone_contents_deep_free(&(*zone)->contents, destroy_dname_table);
+ free(*zone);
+ *zone = NULL;
+}
diff --git a/src/libknot/zone/zone.h b/src/libknot/zone/zone.h
new file mode 100644
index 0000000..331ef1f
--- /dev/null
+++ b/src/libknot/zone/zone.h
@@ -0,0 +1,157 @@
+/*!
+ * \file zone.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone structure and API for manipulating it.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/* 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/>.
+ */
+
+#ifndef _KNOT_ZONE_H_
+#define _KNOT_ZONE_H_
+
+#include <time.h>
+
+#include "zone/node.h"
+#include "dname.h"
+#include "nsec3.h"
+#include "zone/dname-table.h"
+#include "common/tree.h"
+#include "hash/cuckoo-hash-table.h"
+
+#include "zone-tree.h"
+
+#include "zone/zone-contents.h"
+
+/*----------------------------------------------------------------------------*/
+
+//typedef TREE_HEAD(avl_tree, knot_node) avl_tree_t;
+//struct event_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Return values for search functions.
+ *
+ * Used in knot_zone_find_dname() and knot_zone_find_dname_hash().
+ */
+enum knot_zone_retvals {
+ KNOT_ZONE_NAME_FOUND = 1,
+ KNOT_ZONE_NAME_NOT_FOUND = 0
+};
+
+typedef enum knot_zone_retvals knot_zone_retvals_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Structure for holding DNS zone.
+ *
+ * \warning Make sure not to insert the same nodes using both the normal and
+ * NSEC3 functions. Although this will be successfull, it will produce
+ * double-free errors when destroying the zone.
+ */
+struct knot_zone {
+ knot_dname_t *name;
+
+ knot_zone_contents_t *contents;
+
+ time_t version;
+
+ /*! \todo Set when loading zone. */
+ short master;
+
+ void *data; /*!< Pointer to generic zone-related data. */
+ int (*dtor)(struct knot_zone *); /*!< Data destructor. */
+};
+
+typedef struct knot_zone knot_zone_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates new empty DNS zone.
+ *
+ * \notice Zone will be created without contents.
+ *
+ * \param name Zone owner.
+ *
+ * \return The initialized zone structure or NULL if an error occured.
+ */
+knot_zone_t *knot_zone_new_empty(knot_dname_t *name);
+
+/*!
+ * \brief Creates new DNS zone.
+ *
+ * \param apex Node representing the zone apex.
+ * \param node_count Number of authorative nodes in the zone.
+ *
+ * \return The initialized zone structure or NULL if an error occured.
+ */
+knot_zone_t *knot_zone_new(knot_node_t *apex, uint node_count,
+ int use_domain_table);
+
+knot_zone_contents_t *knot_zone_get_contents(
+ const knot_zone_t *zone);
+
+const knot_zone_contents_t *knot_zone_contents(
+ const knot_zone_t *zone);
+
+
+time_t knot_zone_version(const knot_zone_t *zone);
+
+void knot_zone_set_version(knot_zone_t *zone, time_t version);
+
+short knot_zone_is_master(const knot_zone_t *zone);
+
+void knot_zone_set_master(knot_zone_t *zone, short master);
+
+const void *knot_zone_data(const knot_zone_t *zone);
+
+void knot_zone_set_data(knot_zone_t *zone, void *data);
+
+const knot_dname_t *knot_zone_name(const knot_zone_t *zone);
+
+knot_zone_contents_t *knot_zone_switch_contents(knot_zone_t *zone,
+ knot_zone_contents_t *new_contents);
+
+/*!
+ * \brief Correctly deallocates the zone structure, without deleting its nodes.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param zone Zone to be freed.
+ */
+void knot_zone_free(knot_zone_t **zone);
+
+/*!
+ * \brief Correctly deallocates the zone structure and all nodes within.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param zone Zone to be freed.
+ * \param free_rdata_dnames Set to <> 0 if you want to delete ALL domain names
+ * present in RDATA. Set to 0 otherwise. (See
+ * knot_rdata_deep_free().)
+ */
+void knot_zone_deep_free(knot_zone_t **zone, int destroy_dname_table);
+
+#endif
+
+/*! @} */
diff --git a/src/libknot/zone/zonedb.c b/src/libknot/zone/zonedb.c
new file mode 100644
index 0000000..8f07d45
--- /dev/null
+++ b/src/libknot/zone/zonedb.c
@@ -0,0 +1,389 @@
+/* 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 <config.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <urcu.h>
+
+#include "common.h"
+#include "zone/zone.h"
+#include "zone/zonedb.h"
+#include "dname.h"
+#include "zone/node.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common/general-tree.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares the two arguments interpreted as zone names (domain names).
+ *
+ * Use this function with generic data structures (such as the skip list).
+ *
+ * \param d1 First zone name.
+ * \param d2 Second zone name.
+ *
+ * \retval 0 if the two zone names are equal.
+ * \retval < 0 if \a d1 is before \a d2 in canonical order.
+ * \retval > 0 if \a d1 is after \a d2 in canonical order.
+ */
+static int knot_zonedb_compare_zone_names(void *p1, void *p2)
+{
+ const knot_zone_t *zone1 = (const knot_zone_t *)p1;
+ const knot_zone_t *zone2 = (const knot_zone_t *)p2;
+
+ int ret = knot_dname_compare(zone1->name, zone2->name);
+
+dbg_zonedb_exec(
+ char *name1 = knot_dname_to_str(zone1->name);
+ char *name2 = knot_dname_to_str(zone2->name);
+ dbg_zonedb("Compared names %s and %s, result: %d.\n",
+ name1, name2, ret);
+ free(name1);
+ free(name2);
+);
+
+ return (ret);
+}
+
+/*----------------------------------------------------------------------------*/
+
+//static int knot_zonedb_replace_zone_in_list(void **list_item, void **new_zone)
+//{
+// assert(list_item != NULL);
+// assert(*list_item != NULL);
+// assert(new_zone != NULL);
+// assert(*new_zone != NULL);
+
+// dbg_zonedb("Replacing list item %p with new zone %p\n",
+// *list_item, *new_zone);
+
+// *list_item = *new_zone;
+
+// return 0;
+//}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_zonedb_t *knot_zonedb_new()
+{
+ knot_zonedb_t *db =
+ (knot_zonedb_t *)malloc(sizeof(knot_zonedb_t));
+ CHECK_ALLOC_LOG(db, NULL);
+
+ db->zone_tree = gen_tree_new(knot_zonedb_compare_zone_names);
+ if (db->zone_tree == NULL) {
+ free(db);
+ return NULL;
+ }
+
+ db->zone_count = 0;
+
+ return db;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone)
+{
+ if (db == NULL || zone == NULL) {
+ return KNOT_EBADARG;
+ }
+dbg_zonedb_exec(
+ char *name = knot_dname_to_str(zone->name);
+ dbg_zonedb("Inserting zone %s into zone db.\n", name);
+ free(name);
+);
+
+ int ret = KNOT_EOK;
+ if (knot_zone_contents(zone)) {
+ ret = knot_zone_contents_load_nsec3param(
+ knot_zone_get_contents(zone));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ ret = gen_tree_add(db->zone_tree, zone, NULL);
+
+ if (ret == 0) {
+ db->zone_count++;
+ }
+
+ return (ret != 0) ? KNOT_EZONEIN : KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zonedb_remove_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name)
+{
+ knot_zone_t dummy_zone;
+ memset(&dummy_zone, 0, sizeof(knot_zone_t));
+ dummy_zone.name = (knot_dname_t *)zone_name;
+
+ // add some lock to avoid multiple removals
+ knot_zone_t *z = (knot_zone_t *)gen_tree_find(db->zone_tree,
+ &dummy_zone);
+
+ if (z == NULL) {
+ return NULL;
+ }
+
+ // remove the zone from the skip list, but do not destroy it
+ gen_tree_remove(db->zone_tree, &dummy_zone);
+
+// if (destroy_zone) {
+// // properly destroy the zone and all its contents
+// knot_zone_deep_free(&z, 0);
+// }
+
+ db->zone_count--;
+
+ //return KNOT_EOK;
+ return z;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db,
+// knot_zone_t *zone)
+//{
+// knot_zone_t *z = knot_zonedb_find_zone(db,
+// knot_node_owner(knot_zone_apex(zone)));
+// if (z == NULL) {
+// return NULL;
+// }
+
+// /*! \todo The replace should be atomic!!! */
+
+// dbg_zonedb("Found zone: %p\n", z);
+
+// int ret = skip_remove(db->zones,
+// (void *)knot_node_owner(knot_zone_apex(zone)),
+// NULL, NULL);
+// if (ret != 0) {
+// return NULL;
+// }
+
+// dbg_zonedb("Removed zone, return value: %d\n", ret);
+// dbg_zonedb("Old zone: %p\n", z);
+
+// ret = skip_insert(db->zones,
+// (void *)knot_node_owner(knot_zone_apex(zone)),
+// (void *)zone, NULL);
+
+// dbg_zonedb("Inserted zone, return value: %d\n", ret);
+
+// if (ret != 0) {
+// // return the removed zone back
+// skip_insert(db->zones,
+// (void *)knot_node_owner(knot_zone_apex(z)),
+// (void *)z, NULL);
+// /*! \todo There may be problems and the zone may remain
+// removed. */
+// return NULL;
+// }
+
+// return z;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db,
+ const knot_dname_t *zone_name)
+{
+ knot_zone_t dummy_zone;
+ dummy_zone.name = (knot_dname_t *)zone_name;
+ return (knot_zone_t *)gen_tree_find(db->zone_tree, &dummy_zone);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db,
+ const knot_dname_t *dname)
+{
+ if (db == NULL || dname == NULL) {
+ return NULL;
+ }
+
+ knot_zone_t dummy_zone;
+ dummy_zone.name = (knot_dname_t *)dname;
+ void *found = NULL;
+ int exact_match = gen_tree_find_less_or_equal(db->zone_tree,
+ &dummy_zone,
+ &found);
+ UNUSED(exact_match);
+
+ knot_zone_t *zone = (found) ? (knot_zone_t *)found : NULL;
+
+dbg_zonedb_exec(
+ char *name = knot_dname_to_str(dname);
+ dbg_zonedb("Found zone for name %s: %p\n", name, zone);
+ free(name);
+);
+ if (zone != NULL && zone->contents != NULL
+ && knot_dname_compare(zone->contents->apex->owner, dname) != 0
+ && !knot_dname_is_subdomain(dname, zone->contents->apex->owner)) {
+ zone = NULL;
+ }
+
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name)
+{
+ if (db == NULL || zone_name == NULL) {
+ return NULL;
+ }
+
+ // Remove the contents from the zone, but keep the zone in the zonedb.
+
+ knot_zone_t *zone = knot_zonedb_find_zone(db, zone_name);
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ return knot_zone_switch_contents(zone, NULL);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_zonedb_t *knot_zonedb_copy(const knot_zonedb_t *db)
+{
+ knot_zonedb_t *db_new =
+ (knot_zonedb_t *)malloc(sizeof(knot_zonedb_t));
+ CHECK_ALLOC_LOG(db_new, NULL);
+
+ db_new->zone_tree = gen_tree_shallow_copy(db->zone_tree);
+ if (db_new->zone_tree == NULL) {
+ free(db_new);
+ return NULL;
+ }
+
+ return db_new;
+}
+
+size_t knot_zonedb_zone_count(const knot_zonedb_t *db)
+{
+ return db->zone_count;
+}
+
+struct knot_zone_db_tree_arg {
+ const knot_zone_t **zones;
+ size_t count;
+};
+
+static void save_zone_to_array(void *node, void *data)
+{
+ knot_zone_t *zone = (knot_zone_t *)node;
+ struct knot_zone_db_tree_arg *args =
+ (struct knot_zone_db_tree_arg *)data;
+ assert(data);
+ args->zones[args->count++] = zone;
+}
+
+const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db)
+{
+ struct knot_zone_db_tree_arg args;
+ args.zones = malloc(sizeof(knot_zone_t) * db->zone_count);
+ args.count = 0;
+ CHECK_ALLOC_LOG(args.zones, NULL);
+
+ gen_tree_apply_inorder(db->zone_tree, save_zone_to_array,
+ &args);
+ assert(db->zone_count == args.count);
+
+ return args.zones;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_zonedb_free(knot_zonedb_t **db)
+{
+ gen_tree_destroy(&((*db)->zone_tree), NULL ,NULL);
+ free(*db);
+ *db = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void delete_zone_from_db(void *node, void *data)
+{
+ UNUSED(data);
+ knot_zone_t *zone = (knot_zone_t *)node;
+ assert(zone);
+ synchronize_rcu();
+ knot_zone_deep_free(&zone, 0);
+}
+
+void knot_zonedb_deep_free(knot_zonedb_t **db)
+{
+ dbg_zonedb("Deleting zone db (%p).\n", *db);
+// dbg_zonedb("Is it empty (%p)? %s\n",
+// (*db)->zones, skip_is_empty((*db)->zones) ? "yes" : "no");
+
+//dbg_zonedb_exec(
+// int i = 1;
+// char *name = NULL;
+// while (zn != NULL) {
+// dbg_zonedb("%d. zone: %p, key: %p\n", i, zn->value,
+// zn->key);
+// assert(zn->key == ((knot_zone_t *)zn->value)->apex->owner);
+// name = knot_dname_to_str((knot_dname_t *)zn->key);
+// dbg_zonedb(" zone name: %s\n", name);
+// free(name);
+
+// zn = skip_next(zn);
+// }
+
+// zn = skip_first((*db)->zones);
+//);
+
+// while (zn != NULL) {
+// zone = (knot_zone_t *)zn->value;
+// assert(zone != NULL);
+
+// // remove the zone from the database
+// skip_remove((*db)->zones, zn->key, NULL, NULL);
+// // wait for all readers to finish
+// synchronize_rcu;
+// // destroy the zone
+// knot_zone_deep_free(&zone, 0);
+
+// zn = skip_first((*db)->zones);
+// }
+
+// assert(skip_is_empty((*db)->zones));
+
+// skip_destroy_list(&(*db)->zones, NULL, NULL);
+ gen_tree_destroy(&((*db)->zone_tree), delete_zone_from_db, NULL);
+ assert((*db)->zone_tree == NULL);
+ free(*db);
+ *db = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
diff --git a/src/libknot/zone/zonedb.h b/src/libknot/zone/zonedb.h
new file mode 100644
index 0000000..d5a4992
--- /dev/null
+++ b/src/libknot/zone/zonedb.h
@@ -0,0 +1,145 @@
+/*!
+ * \file zonedb.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Zone database structure and API for manipulating it.
+ *
+ * Zone database groups several zones and provides functions for finding
+ * suitable zone for a domain name, for searching in a particular zone, etc.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+/* 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/>.
+ */
+
+#ifndef _KNOT_ZONEDB_H_
+#define _KNOT_ZONEDB_H_
+
+#include "common/general-tree.h"
+#include "zone/zone.h"
+#include "zone/node.h"
+#include "dname.h"
+
+/*!
+ * \brief Zone database structure. Contains all zones managed by the server.
+ */
+struct knot_zonedb {
+ general_tree_t *zone_tree; /*!< AVL tree of zones. */
+ size_t zone_count;
+};
+
+typedef struct knot_zonedb knot_zonedb_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Allocates and initializes the zone database structure.
+ *
+ * \return Pointer to the created zone database structure or NULL if an error
+ * occured.
+ */
+knot_zonedb_t *knot_zonedb_new();
+
+/*!
+ * \brief Adds new zone to the database.
+ *
+ * \param db Zone database to store the zone.
+ * \param zone Parsed zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EZONEIN
+ */
+int knot_zonedb_add_zone(knot_zonedb_t *db, knot_zone_t *zone);
+
+/*!
+ * \brief Removes the given zone from the database if it exists.
+ *
+ * \note Assumes that the zone was adjusted using knot_zone_adjust_dnames().
+ * If it was not, it may leak some memory due to checks used in
+ * knot_rdata_deep_free().
+ *
+ * \param db Zone database to remove from.
+ * \param zone_name Name of the zone to be removed.
+ * \param destroy_zone Set to <> 0 if you do want the function to destroy the
+ * zone after removing from zone database. Set to 0
+ * otherwise.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOZONE
+ */
+knot_zone_t * knot_zonedb_remove_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name);
+
+//knot_zone_t *knot_zonedb_replace_zone(knot_zonedb_t *db,
+// knot_zone_t *zone);
+
+/*!
+ * \brief Finds zone exactly matching the given zone name.
+ *
+ * \param db Zone database to search in.
+ * \param zone_name Domain name representing the zone name.
+ *
+ * \return Zone with \a zone_name being the owner of the zone apex or NULL if
+ * not found.
+ */
+knot_zone_t *knot_zonedb_find_zone(const knot_zonedb_t *db,
+ const knot_dname_t *zone_name);
+
+
+/*!
+ * \brief Finds zone the given domain name should belong to.
+ *
+ * \param db Zone database to search in.
+ * \param dname Domain name to find zone for.
+ *
+ * \retval Zone in which the domain name should be present or NULL if no such
+ * zone is found.
+ */
+const knot_zone_t *knot_zonedb_find_zone_for_name(knot_zonedb_t *db,
+ const knot_dname_t *dname);
+
+knot_zone_contents_t *knot_zonedb_expire_zone(knot_zonedb_t *db,
+ const knot_dname_t *zone_name);
+
+size_t knot_zonedb_zone_count(const knot_zonedb_t *db);
+const knot_zone_t **knot_zonedb_zones(const knot_zonedb_t *db);
+
+/*!
+ * \brief Destroys and deallocates the zone database structure (but not the
+ * zones within).
+ *
+ * \param db Zone database to be destroyed.
+ */
+void knot_zonedb_free(knot_zonedb_t **db);
+
+/*!
+ * \brief Destroys and deallocates the whole zone database including the zones.
+ *
+ * \note Assumes that the zone was adjusted using knot_zone_adjust_dnames().
+ * If it was not, it may leak some memory due to checks used in
+ * knot_rdata_deep_free().
+ *
+ * \param db Zone database to be destroyed.
+ */
+void knot_zonedb_deep_free(knot_zonedb_t **db);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_ZONEDB_H_ */
+
+/*! @} */