summaryrefslogtreecommitdiff
path: root/src/libknot
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
committerOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
commitc8d5977bb546dae9ed59d81556639c49badd8121 (patch)
tree4c86750db26c1c3502b60f2cd78ca9611cfa01d6 /src/libknot
downloadknot-c8d5977bb546dae9ed59d81556639c49badd8121.tar.gz
Imported Upstream version 0.8.0~pre1upstream/0.8.0_pre1
Diffstat (limited to 'src/libknot')
-rw-r--r--src/libknot/common.h105
-rw-r--r--src/libknot/consts.h108
-rw-r--r--src/libknot/dname.c1070
-rw-r--r--src/libknot/dname.h428
-rw-r--r--src/libknot/edns.c416
-rw-r--r--src/libknot/edns.h273
-rw-r--r--src/libknot/hash/cuckoo-hash-table.c1688
-rw-r--r--src/libknot/hash/cuckoo-hash-table.h333
-rw-r--r--src/libknot/hash/hash-functions.c241
-rw-r--r--src/libknot/hash/hash-functions.h85
-rw-r--r--src/libknot/hash/universal-system.c116
-rw-r--r--src/libknot/hash/universal-system.h109
-rw-r--r--src/libknot/libknot.h48
-rw-r--r--src/libknot/nameserver/name-server.c3663
-rw-r--r--src/libknot/nameserver/name-server.h358
-rw-r--r--src/libknot/nsec3.c265
-rw-r--r--src/libknot/nsec3.h92
-rw-r--r--src/libknot/packet/packet.c1532
-rw-r--r--src/libknot/packet/packet.h538
-rw-r--r--src/libknot/packet/query.c228
-rw-r--r--src/libknot/packet/query.h93
-rw-r--r--src/libknot/packet/response.c1170
-rw-r--r--src/libknot/packet/response.h198
-rw-r--r--src/libknot/rdata.c838
-rw-r--r--src/libknot/rdata.h339
-rw-r--r--src/libknot/rrset.c719
-rw-r--r--src/libknot/rrset.h306
-rw-r--r--src/libknot/tsig-op.c1089
-rw-r--r--src/libknot/tsig-op.h161
-rw-r--r--src/libknot/tsig.c618
-rw-r--r--src/libknot/tsig.h145
-rw-r--r--src/libknot/updates/changesets.c296
-rw-r--r--src/libknot/updates/changesets.h102
-rw-r--r--src/libknot/updates/ddns.c638
-rw-r--r--src/libknot/updates/ddns.h74
-rw-r--r--src/libknot/updates/xfr-in.c3013
-rw-r--r--src/libknot/updates/xfr-in.h184
-rw-r--r--src/libknot/util/debug.c233
-rw-r--r--src/libknot/util/debug.h755
-rw-r--r--src/libknot/util/descriptor.c501
-rw-r--r--src/libknot/util/descriptor.h332
-rw-r--r--src/libknot/util/error.h87
-rw-r--r--src/libknot/util/libknot_error.c53
-rw-r--r--src/libknot/util/tolower.c276
-rw-r--r--src/libknot/util/tolower.h57
-rw-r--r--src/libknot/util/utils.c127
-rw-r--r--src/libknot/util/utils.h196
-rw-r--r--src/libknot/util/wire.h926
-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
60 files changed, 31701 insertions, 0 deletions
diff --git a/src/libknot/common.h b/src/libknot/common.h
new file mode 100644
index 0000000..9b2d8ae
--- /dev/null
+++ b/src/libknot/common.h
@@ -0,0 +1,105 @@
+/*!
+ * \file common.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Common macros, includes and utilities.
+ *
+ * \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/>.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_LIBLDNS
+#define TEST_WITH_LDNS
+#endif
+
+#ifndef _KNOT_COMMON_H_
+#define _KNOT_COMMON_H_
+
+#define KNOT_NAME "lib" PACKAGE_NAME // Project name
+#define KNOT_VER PACKAGE_VERSION // 0xMMIIRR (MAJOR,MINOR,REVISION)
+
+#ifndef UINT_DEFINED
+typedef unsigned int uint; /*!< \brief Unsigned. */
+#define UINT_DEFINED
+#endif
+
+/*! \brief If defined, zone structures will use hash table for lookup. */
+#define USE_HASH_TABLE
+
+/*! \brief Eliminate compiler warning with unused parameters. */
+#define UNUSED(param) (void)(param)
+
+/*! \brief Type-safe minimum macro. */
+#define MIN(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a < _b ? _a : _b; })
+
+/*! \brief Type-safe maximum macro. */
+#define MAX(a, b) \
+ ({ typeof (a) _a = (a); typeof (b) _b = (b); _a > _b ? _a : _b; })
+
+/* Optimisation macros. */
+#ifndef likely
+/*! \brief Optimize for x to be true value. */
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+/*! \brief Optimize for x to be false value. */
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+
+/* Optimisation macros. */
+#ifndef likely
+/*! \brief Optimize for x to be true value. */
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+/*! \brief Optimize for x to be false value. */
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+
+/*! \todo Refactor theese. We should have an allocator function handling this.*/
+#ifndef ERR_ALLOC_FAILED
+#define ERR_ALLOC_FAILED fprintf(stderr, "Allocation failed at %s:%d (%s ver.%s)\n", \
+ __FILE__, __LINE__, KNOT_NAME, KNOT_VER)
+#endif
+
+#ifndef CHECK_ALLOC_LOG
+#define CHECK_ALLOC_LOG(var, ret) \
+ do { \
+ if ((var) == NULL) { \
+ ERR_ALLOC_FAILED; \
+ return (ret); \
+ } \
+ } while (0)
+#endif
+
+#ifndef CHECK_ALLOC
+#define CHECK_ALLOC(var, ret) \
+ do { \
+ if ((var) == NULL) { \
+ return (ret); \
+ } \
+ } while (0)
+#endif
+
+#endif /* _KNOT_COMMON_H_ */
+
+/*! @} */
diff --git a/src/libknot/consts.h b/src/libknot/consts.h
new file mode 100644
index 0000000..4249763
--- /dev/null
+++ b/src/libknot/consts.h
@@ -0,0 +1,108 @@
+/*!
+ * \file consts.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Contains some DNS-related constants.
+ *
+ * \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_CONSTS_H_
+#define _KNOT_CONSTS_H_
+
+#include <stdint.h>
+#include "util/descriptor.h"
+
+/*
+ * OPCODEs
+ */
+typedef enum knot_opcode {
+ KNOT_OPCODE_QUERY = 0, /* a standard query (QUERY) */
+ KNOT_OPCODE_IQUERY = 1, /* an inverse query (IQUERY) */
+ KNOT_OPCODE_STATUS = 2, /* a server status request (STATUS) */
+ KNOT_OPCODE_NOTIFY = 4, /* NOTIFY */
+ KNOT_OPCODE_UPDATE = 5, /* Dynamic update */
+ KNOT_OPCODE_OFFSET = 14
+} knot_opcode_t;
+
+/*!
+ * \brief Query types (internal use only).
+ *
+ * This type encompasses the different query types distinguished by both the
+ * OPCODE and the QTYPE.
+ */
+typedef enum knot_packet_type {
+ KNOT_QUERY_NORMAL, /*!< Normal query. */
+ KNOT_QUERY_AXFR, /*!< Request for AXFR transfer. */
+ KNOT_QUERY_IXFR, /*!< Request for IXFR transfer. */
+ KNOT_QUERY_NOTIFY, /*!< NOTIFY query. */
+ KNOT_QUERY_UPDATE, /*!< Dynamic update. */
+ KNOT_RESPONSE_NORMAL, /*!< Normal response. */
+ KNOT_RESPONSE_AXFR, /*!< AXFR transfer response. */
+ KNOT_RESPONSE_IXFR, /*!< IXFR transfer response. */
+ KNOT_RESPONSE_NOTIFY /*!< NOTIFY response. */
+} knot_packet_type_t;
+
+/*
+ * RCODEs
+ */
+typedef enum knot_rcode {
+ KNOT_RCODE_NOERROR = 0, /* No error condition */
+ KNOT_RCODE_FORMERR = 1, /* Format error */
+ KNOT_RCODE_SERVFAIL = 2, /* Server failure */
+ KNOT_RCODE_NXDOMAIN = 3, /* Name Error */
+ KNOT_RCODE_NOTIMPL = 4, /* Not implemented */
+ KNOT_RCODE_REFUSED = 5, /* Refused */
+ KNOT_RCODE_YXDOMAIN = 6, /* name should not exist */
+ KNOT_RCODE_YXRRSET = 7, /* rrset should not exist */
+ KNOT_RCODE_NXRRSET = 8, /* rrset does not exist */
+ KNOT_RCODE_NOTAUTH = 9, /* server not authoritative */
+ KNOT_RCODE_NOTZONE = 10, /* name not inside zone */
+} knot_rcode_t;
+
+typedef enum knot_tsig_rcode {
+ KNOT_TSIG_RCODE_BADSIG = 16,
+ KNOT_TSIG_RCODE_BADKEY = 17,
+ KNOT_TSIG_RCODE_BADTIME = 18
+} knot_tsig_rcode_t;
+
+/*
+ * CLASSes
+ */
+//typedef enum knot_class {
+// KNOT_CLASS_IN = 1, /* Class IN */
+// KNOT_CLASS_CS = 2, /* Class CS */
+// KNOT_CLASS_CH = 3, /* Class CHAOS */
+// KNOT_CLASS_HS = 4, /* Class HS */
+// KNOT_CLASS_NONE = 254, /* Class NONE rfc2136 */
+// KNOT_CLASS_ANY = 255 /* Class ANY */
+//} knot_class_t;
+
+/*
+ * Other
+ */
+typedef enum knot_const {
+ KNOT_MAX_DNAME_LENGTH = 255,
+ KNOT_MAX_DNAME_LABELS = 127 // 1-char labels
+} knot_const_t;
+
+#endif /* _KNOT_CONSTS_H_ */
+
+/*! @} */
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
new file mode 100644
index 0000000..869b342
--- /dev/null
+++ b/src/libknot/dname.c
@@ -0,0 +1,1070 @@
+/* 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 <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h> // tolower()
+
+#include "common.h"
+#include "util/error.h"
+#include "dname.h"
+#include "consts.h"
+#include "util/tolower.h"
+#include "util/debug.h"
+#include "util/utils.h"
+#include "util/wire.h"
+
+/*! \todo dnames allocated from TLS cache will be discarded after thread
+ * termination. This shouldn't happpen.
+ */
+#if 0
+/*
+ * Memory cache.
+ */
+#include "common/slab/slab.h"
+#include <stdio.h>
+#include <pthread.h>
+
+/*! \brief TLS unique key for each thread cache. */
+static pthread_key_t dname_ckey;
+static pthread_once_t dname_once = PTHREAD_ONCE_INIT;
+
+/*! \brief Destroy thread dname cache (automatically called). */
+static void knot_dname_cache_free(void *ptr)
+{
+ slab_cache_t* cache = (slab_cache_t*)ptr;
+ if (cache) {
+ slab_cache_destroy(cache);
+ free(cache);
+ }
+}
+
+/*! \brief Cleanup for main() TLS. */
+static void knot_dname_cache_main_free()
+{
+ knot_dname_cache_free(pthread_getspecific(dname_ckey));
+}
+
+static void knot_dname_cache_init()
+{
+ (void) pthread_key_create(&dname_ckey, knot_dname_cache_free);
+ atexit(knot_dname_cache_main_free); // Main thread cleanup
+}
+#endif
+
+/*!
+ * \brief Allocate item from thread cache.
+ * \retval Allocated dname instance on success.
+ * \retval NULL on error.
+ */
+static knot_dname_t* knot_dname_alloc()
+{
+ return malloc(sizeof(knot_dname_t));
+
+ /*! \todo dnames allocated from TLS cache will be discarded after thread
+ * termination. This shouldn't happpen.
+ */
+#if 0
+ /* Initialize dname cache TLS key. */
+ (void)pthread_once(&dname_once, knot_dname_cache_init);
+
+ /* Create cache if not exists. */
+ slab_cache_t* cache = pthread_getspecific(dname_ckey);
+ if (unlikely(!cache)) {
+ cache = malloc(sizeof(slab_cache_t));
+ if (!cache) {
+ return 0;
+ }
+
+ /* Initialize cache. */
+ slab_cache_init(cache, sizeof(knot_dname_t));
+ (void)pthread_setspecific(dname_ckey, cache);
+ }
+
+ return slab_cache_alloc(cache);
+#endif
+}
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+static int knot_dname_set(knot_dname_t *dname, uint8_t *wire,
+ short wire_size, const uint8_t *labels,
+ short label_count)
+{
+ dname->name = wire;
+ dname->size = wire_size;
+ dname->label_count = label_count;
+
+ assert(label_count >= 0);
+
+ dname->labels = (uint8_t *)malloc(dname->label_count * sizeof(uint8_t));
+ CHECK_ALLOC_LOG(dname->labels, -1);
+ memcpy(dname->labels, labels, dname->label_count);
+
+ return 0;
+}
+
+/*!
+ * \brief Converts domain name from string representation to wire format.
+ *
+ * This function also allocates the space for the wire format.
+ *
+ * \param name Domain name in string representation (presentation format).
+ * \param size Size of the given domain name in characters (not counting the
+ * terminating 0 character.
+ * \param dname Domain name where to store the wire format.
+ *
+ * \return Size of the wire format of the domain name in octets. If 0, no
+ * space has been allocated.
+ *
+ * \todo handle \X and \DDD (RFC 1035 5.1) or it can be handled by the parser?
+ */
+static int knot_dname_str_to_wire(const char *name, uint size,
+ knot_dname_t *dname)
+{
+ if (size > KNOT_MAX_DNAME_LENGTH) {
+ return -1;
+ }
+
+ uint wire_size;
+ int root = (*name == '.' && size == 1);
+ // root => different size
+ if (root) {
+ wire_size = 1;
+ } else {
+ wire_size = size + 1;
+ }
+
+ uint8_t *wire;
+ uint8_t labels[KNOT_MAX_DNAME_LABELS];
+ short label_count = 0;
+
+ // signed / unsigned issues??
+ wire = (uint8_t *)malloc(wire_size * sizeof(uint8_t));
+ if (wire == NULL) {
+ return -1;
+ }
+
+ dbg_dname("Allocated space for wire format of dname: %p\n",
+ wire);
+
+ if (root) {
+ *wire = '\0';
+ label_count = 0;
+ return knot_dname_set(dname, wire, wire_size, labels,
+ label_count);
+ }
+
+ const uint8_t *ch = (const uint8_t *)name;
+ uint8_t *label_start = wire;
+ uint8_t *w = wire + 1;
+ uint8_t label_length = 0;
+
+ while (ch - (const uint8_t *)name < size) {
+ assert(w - wire - 1 == ch - (const uint8_t *)name);
+
+ if (*ch == '.') {
+ dbg_dname("Position %zd (%p): "
+ "label length: %u\n",
+ label_start - wire,
+ label_start, label_length);
+ *label_start = label_length;
+ labels[label_count++] = label_start - wire;
+ label_start = w;
+ label_length = 0;
+ } else {
+ assert(w - wire < wire_size);
+ dbg_dname("Position %zd (%p): character: %c\n",
+ w - wire, w, *ch);
+ *w = *ch;
+ ++label_length;
+ }
+
+ ++w;
+ ++ch;
+ assert(ch >= (const uint8_t *)name);
+ }
+
+ --ch;
+ if (*ch == '.') { // put 0 for root label if the name ended with .
+ --w;
+ dbg_dname("Position %zd (%p): character: (null)\n",
+ w - wire, w);
+ *w = 0;
+ } else { // otherwise we did not save the last label length
+ dbg_dname("Position %zd (%p): "
+ "label length: %u\n",
+ label_start - wire,
+ label_start, label_length);
+ *label_start = label_length;
+ labels[label_count++] = label_start - wire;
+ }
+
+ return knot_dname_set(dname, wire, wire_size, labels, label_count);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static inline int knot_dname_tolower(uint8_t c, int cs)
+{
+ return (cs) ? c : knot_tolower(c);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_dname_compare_labels(const uint8_t *label1,
+ const uint8_t *label2, int cs)
+{
+ const uint8_t *pos1 = label1;
+ const uint8_t *pos2 = label2;
+
+ int label_length = (*pos1 < *pos2) ? *pos1 : *pos2;
+ int i = 0;
+
+ while (i < label_length
+ && knot_dname_tolower(*(++pos1), cs)
+ == knot_dname_tolower(*(++pos2), cs)) {
+ ++i;
+ }
+
+ if (i < label_length) { // difference in some octet
+ return (knot_dname_tolower(*pos1, cs)
+ - knot_dname_tolower(*pos2, cs));
+ }
+
+ return (label1[0] - label2[0]);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_dname_find_labels(knot_dname_t *dname, int alloc)
+{
+ const uint8_t *name = dname->name;
+ const uint8_t *pos = name;
+ const uint size = dname->size;
+
+ uint8_t labels[KNOT_MAX_DNAME_LABELS];
+ short label_count = 0;
+
+ while (pos - name < size && *pos != '\0') {
+ labels[label_count++] = pos - name;
+ pos += *pos + 1;
+ }
+
+ // TODO: how to check if the domain name has right format?
+ if (pos - name > size || *pos != '\0') {
+ dbg_dname("Wrong wire format of domain name!\n");
+ dbg_dname("Position: %d, character: %d, expected"
+ " size: %d\n", pos - name, *pos, size);
+ return -1;
+ }
+
+ if (alloc) {
+ dname->labels
+ = (uint8_t *)malloc(label_count * sizeof(uint8_t));
+ CHECK_ALLOC_LOG(dname->labels, KNOT_ENOMEM);
+ }
+
+ memcpy(dname->labels, labels, label_count);
+ dname->label_count = label_count;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2,
+ int cs)
+{
+dbg_dname_exec(
+ char *name1 = knot_dname_to_str(d1);
+ char *name2 = knot_dname_to_str(d2);
+
+ dbg_dname("Comparing dnames %s and %s\n",
+ name1, name2);
+
+ for (int i = 0; i < strlen(name1); ++i) {
+ name1[i] = knot_tolower(name1[i]);
+ }
+ for (int i = 0; i < strlen(name2); ++i) {
+ name2[i] = knot_tolower(name2[i]);
+ }
+
+ dbg_dname("After to lower: %s and %s\n",
+ name1, name2);
+
+ free(name1);
+ free(name2);
+);
+
+ if (!cs && d1 == d2) {
+ return 0;
+ }
+
+ int l1 = d1->label_count;
+ int l2 = d2->label_count;
+ dbg_dname("Label counts: %d and %d\n", l1, l2);
+ assert(l1 >= 0);
+ assert(l2 >= 0);
+
+ // compare labels from last to first
+ while (l1 > 0 && l2 > 0) {
+ dbg_dname("Comparing labels %d and %d\n",
+ l1 - 1, l2 - 1);
+ dbg_dname(" at offsets: %d and %d\n",
+ d1->labels[l1 - 1], d2->labels[l2 - 1]);
+ int res = knot_dname_compare_labels(
+ &d1->name[d1->labels[--l1]],
+ &d2->name[d2->labels[--l2]],
+ cs);
+ if (res != 0) {
+ return res;
+ } // otherwise the labels are identical, continue with previous
+ }
+
+ // if all labels matched, the shorter name is first
+ if (l1 == 0 && l2 > 0) {
+ return -1;
+ }
+
+ if (l1 > 0 && l2 == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*! \brief Destructor for reference counter. */
+static void knot_dname_dtor(struct ref_t *p)
+{
+ knot_dname_t *dname = (knot_dname_t *)p;
+ knot_dname_free(&dname);
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_new()
+{
+ knot_dname_t *dname = knot_dname_alloc();
+ dname->name = NULL;
+ dname->size = 0;
+ dname->node = NULL;
+ dname->labels = NULL;
+ dname->label_count = -1;
+ dname->id = 0;
+
+ /* Initialize reference counting. */
+ ref_init(&dname->ref, knot_dname_dtor);
+
+ /* Set reference counter to 1, caller should release it after use. */
+ knot_dname_retain(dname);
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_new_from_str(const char *name, uint size,
+ struct knot_node *node)
+{
+ if (name == NULL || size == 0) {
+ return NULL;
+ }
+
+// knot_dname_t *dname = knot_dname_alloc();
+ knot_dname_t *dname = knot_dname_new();
+
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ knot_dname_str_to_wire(name, size, dname);
+ dbg_dname("Created dname with size: %d\n", dname->size);
+ dbg_dname("Label offsets: ");
+ for (int i = 0; i < dname->label_count; ++i) {
+ dbg_dname("%d, ", dname->labels[i]);
+ }
+ dbg_dname("\n");
+
+ if (dname->size <= 0) {
+ fprintf(stderr, "Could not parse domain name "
+ "from string: '%.*s'\n", size, name);
+ }
+ assert(dname->name != NULL);
+
+ dname->node = node;
+ dname->id = 0;
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+//int knot_dname_from_wire(knot_dname_t *dname, const uint8_t *name,
+// uint size)
+//{
+// int i = 0;
+// uint8_t labels[KNOT_MAX_DNAME_LABELS];
+// int label_i = 0;
+
+// while (name[i] != 0) {
+// labels[label_i++] = i;
+// uint8_t label_length = name[i];
+// if (i + label_length >= size) {
+// return -2;
+// }
+// for (int j = 1; j <= label_length; ++j) {
+// }
+// }
+//}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_new_from_wire(const uint8_t *name, uint size,
+ struct knot_node *node)
+{
+ if (name == NULL) { /* && size != 0) { !OS: Nerozumjaju */
+ dbg_dname("No name given!\n");
+ return NULL;
+ }
+
+ knot_dname_t *dname = knot_dname_new();
+
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ dname->name = (uint8_t *)malloc(size * sizeof(uint8_t));
+ if (dname->name == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&dname);
+ return NULL;
+ }
+
+ memcpy(dname->name, name, size);
+ dname->size = size;
+
+ if (knot_dname_find_labels(dname, 1) != 0) {
+ knot_dname_free(&dname);
+ return NULL;
+ }
+
+ dname->node = node;
+ dname->id = 0;
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
+ size_t *pos, size_t size,
+ knot_node_t *node)
+{
+ uint8_t name[KNOT_MAX_DNAME_LENGTH];
+ uint8_t labels[KNOT_MAX_DNAME_LABELS];
+
+ short l = 0;
+ size_t i = 0, p = *pos;
+ int pointer_used = 0;
+
+ while (p < size && wire[p] != 0) {
+ labels[l] = i;
+ dbg_dname("Next label (%d.) position: %zu\n", l, i);
+
+ if (knot_wire_is_pointer(wire + p)) {
+ // pointer.
+// printf("Pointer.\n");
+ p = knot_wire_get_pointer(wire + p);
+ if (!pointer_used) {
+ *pos += 2;
+ pointer_used = 1;
+ }
+ if (p >= size) {
+ return NULL;
+ }
+ } else {
+ // label; first byte is label length
+ uint8_t length = *(wire + p);
+// printf("Label, length: %u.\n", length);
+ memcpy(name + i, wire + p, length + 1);
+ p += length + 1;
+ i += length + 1;
+ if (!pointer_used) {
+ *pos += length + 1;
+ }
+ ++l;
+ }
+ }
+ if (p >= size) {
+ return NULL;
+ }
+
+ name[i] = 0;
+ if (!pointer_used) {
+ *pos += 1;
+ }
+
+ knot_dname_t *dname = knot_dname_new();
+
+ if (dname == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ dname->name = (uint8_t *)malloc((i + 1) * sizeof(uint8_t));
+ if (dname->name == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&dname);
+ return NULL;
+ }
+
+ memcpy(dname->name, name, i + 1);
+ dname->size = i + 1;
+
+ dname->labels = (uint8_t *)malloc((l + 1) * sizeof(uint8_t));
+ if (dname->labels == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&dname);
+ return NULL;
+ }
+ memcpy(dname->labels, labels, l + 1);
+
+ dname->label_count = l;
+
+ dname->node = node;
+
+ return dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_from_wire(const uint8_t *name, uint size,
+ struct knot_node *node, knot_dname_t *target)
+{
+ if (name == NULL || target == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ memcpy(target->name, name, size);
+ target->size = size;
+ target->node = node;
+ target->id = 0;
+
+ return knot_dname_find_labels(target, 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname)
+{
+ return knot_dname_new_from_wire(dname->name, dname->size,
+ dname->node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+char *knot_dname_to_str(const knot_dname_t *dname)
+{
+ if (!dname || dname->size == 0) {
+ return 0;
+ }
+
+ char *name;
+
+ // root => special treatment
+ if (dname->size == 1) {
+ assert(dname->name[0] == 0);
+ name = (char *)malloc(2 * sizeof(char));
+ name[0] = '.';
+ name[1] = '\0';
+ return name;
+ }
+
+ name = (char *)malloc(dname->size * sizeof(char));
+ if (name == NULL) {
+ return NULL;
+ }
+
+ uint8_t *w = dname->name;
+ char *ch = name;
+ int i = 0;
+
+ do {
+ assert(*w != 0);
+ int label_size = *(w++);
+ // copy the label
+ memcpy(ch, w, label_size);
+ i += label_size;
+ ch += label_size;
+ w += label_size;
+ if (w - dname->name < dname->size) { // another label following
+ *(ch++) = '.';
+ ++i;
+ }
+ } while (i < dname->size - 1);
+
+ *ch = 0;
+ assert(ch - name == dname->size - 1);
+
+ return name;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_to_lower(knot_dname_t *dname)
+{
+ if (dname == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ for (int i = 0; i < dname->size; ++i) {
+ dname->name[i] = knot_tolower(dname->name[i]);
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_to_lower_copy(const knot_dname_t *dname, char *name,
+ size_t size)
+{
+ if (dname == NULL || name == NULL || size < dname->size) {
+ return KNOT_EBADARG;
+ }
+
+ for (int i = 0; i < dname->size; ++i) {
+ name[i] = knot_tolower(dname->name[i]);
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const uint8_t *knot_dname_name(const knot_dname_t *dname)
+{
+ return dname->name;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint knot_dname_size(const knot_dname_t *dname)
+{
+ return dname->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned int knot_dname_id(const knot_dname_t *dname)
+{
+ return dname->id;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_dname_size_part(const knot_dname_t *dname, int labels)
+{
+ assert(labels < dname->label_count);
+ assert(dname->labels != NULL);
+ return (dname->labels[labels]);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const struct knot_node *knot_dname_node(const knot_dname_t *dname,
+ int check_version)
+
+{
+ if (check_version) {
+ return knot_node_current(dname->node);
+ } else {
+ return dname->node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+struct knot_node *knot_dname_get_node(knot_dname_t *dname,
+ int check_version)
+{
+ if (check_version) {
+ return knot_node_get_current(dname->node);
+ } else {
+ return dname->node;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_set_node(knot_dname_t *dname, knot_node_t *node)
+{
+ dname->node = node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_update_node(knot_dname_t *dname)
+{
+ knot_node_update_ref(&dname->node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_is_fqdn(const knot_dname_t *dname)
+{
+ return (dname->name[dname->size - 1] == '\0');
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname)
+{
+ knot_dname_t *parent = knot_dname_new();
+ if (parent == NULL) {
+ return NULL;
+ }
+
+ parent->size = dname->size - dname->name[0] - 1;
+ parent->name = (uint8_t *)malloc(parent->size);
+ if (parent->name == NULL) {
+ ERR_ALLOC_FAILED;
+ knot_dname_free(&parent);
+ return NULL;
+ }
+
+ parent->labels = (uint8_t *)malloc(dname->label_count - 1);
+ if (parent->labels == NULL) {
+ ERR_ALLOC_FAILED;
+ free(parent->name);
+ knot_dname_free(&parent);
+ return NULL;
+ }
+
+ memcpy(parent->name, &dname->name[dname->name[0] + 1], parent->size);
+
+ short first_label_length = dname->labels[1];
+
+ for (int i = 0; i < dname->label_count - 1; ++i) {
+ parent->labels[i] = dname->labels[i + 1] - first_label_length;
+ }
+ parent->label_count = dname->label_count - 1;
+
+ return parent;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_left_chop_no_copy(knot_dname_t *dname)
+{
+ // copy the name
+ short first_label_length = dname->labels[1];
+
+ if (dname->label_count > 1) {
+ memmove(dname->name, &dname->name[dname->labels[1]],
+ dname->size - first_label_length);
+ // adjust labels
+ for (int i = 0; i < dname->label_count - 1; ++i) {
+ dname->labels[i] = dname->labels[i + 1]
+ - first_label_length;
+ }
+ dname->label_count = dname->label_count - 1;
+ dname->size -= first_label_length;
+ } else {
+ dname->name[0] = '\0';
+ dname->size = 1;
+ dname->label_count = 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_is_subdomain(const knot_dname_t *sub,
+ const knot_dname_t *domain)
+{
+dbg_dname_exec(
+ char *name1 = knot_dname_to_str(sub);
+ char *name2 = knot_dname_to_str(domain);
+
+ dbg_dname("Checking if %s is subdomain of %s\n",
+ name1, name2);
+ free(name1);
+ free(name2);
+);
+
+ if (sub == domain) {
+ return 0;
+ }
+
+ // if one of the names is fqdn and the other is not
+ if ((sub->name[sub->size - 1] == '\0'
+ && domain->name[domain->size - 1] != '\0')
+ || (sub->name[sub->size - 1] != '\0'
+ && domain->name[domain->size - 1] == '\0')) {
+ return 0;
+ }
+
+ int l1 = sub->label_count;
+ int l2 = domain->label_count;
+
+ dbg_dname("Label counts: %d and %d\n", l1, l2);
+
+ if (l1 <= l2) { // if sub does not have more labes than domain
+ return 0; // it is not its subdomain
+ }
+
+ // compare labels from last to first
+ while (l1 > 0 && l2 > 0) {
+ dbg_dname("Comparing labels %d and %d\n",
+ l1 - 1, l2 - 1);
+ dbg_dname(" at offsets: %d and %d\n",
+ sub->labels[l1 - 1], domain->labels[l2 - 1]);
+ // if some labels do not match
+ if (knot_dname_compare_labels(&sub->name[sub->labels[--l1]],
+ &domain->name[domain->labels[--l2]], 0)
+ != 0) {
+ return 0; // sub is not a subdomain of domain
+ } // otherwise the labels are identical, continue with previous
+ }
+
+ // if all labels matched, it should be subdomain (more labels)
+ assert(l1 > l2);
+
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_is_wildcard(const knot_dname_t *dname)
+{
+ return (dname->size >= 2
+ && dname->name[0] == 1
+ && dname->name[1] == '*');
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_matched_labels(const knot_dname_t *dname1,
+ const knot_dname_t *dname2)
+{
+ int l1 = dname1->label_count;
+ int l2 = dname2->label_count;
+
+ // compare labels from last to first
+ int matched = 0;
+ while (l1 > 0 && l2 > 0) {
+ int res = knot_dname_compare_labels(
+ &dname1->name[dname1->labels[--l1]],
+ &dname2->name[dname2->labels[--l2]], 0);
+ if (res == 0) {
+ ++matched;
+ } else {
+ break;
+ }
+ }
+
+ return matched;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_label_count(const knot_dname_t *dname)
+{
+ return dname->label_count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_dname_label_size(const knot_dname_t *dname, int i)
+{
+// printf("Returning size of %d. label starting on %d\n",
+// i, dname->labels[i]);
+// printf("Label count: %d, size of %d. label: %d, size of %d.label: %d\n",
+// dname->label_count, i, dname->labels[i], i + 1,
+// dname->labels[i+1]);
+// printf("Size from the name: %u\n", dname->name[dname->labels[i]]);
+// printf("Size from label offsets: %u\n",
+// dname->labels[i + 1] - dname->labels[i]);
+
+ assert(i >= 0);
+ assert(dname->size == 1 || i + 1 == dname->label_count
+ || dname->labels[i + 1] - dname->labels[i] - 1
+ == dname->name[dname->labels[i]]);
+ return dname->name[dname->labels[i]];
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname,
+ int size,
+ const knot_dname_t *suffix)
+{
+dbg_dname_exec(
+ char *name = knot_dname_to_str(dname);
+ dbg_dname("Replacing suffix of name %s, size %d with ", name,
+ size);
+ free(name);
+ name = knot_dname_to_str(suffix);
+ dbg_dname("%s (size %d)\n", name, suffix->size);
+ free(name);
+);
+ knot_dname_t *res = knot_dname_new();
+ CHECK_ALLOC(res, NULL);
+
+ res->size = dname->size - size + suffix->size;
+
+ dbg_dname("Allocating %d bytes...\n", res->size);
+ res->name = (uint8_t *)malloc(res->size);
+ if (res->name == NULL) {
+ knot_dname_free(&res);
+ return NULL;
+ }
+
+ dbg_dname_hex((char *)res->name, res->size);
+
+ dbg_dname("Copying %d bytes from the original name.\n",
+ dname->size - size);
+ memcpy(res->name, dname->name, dname->size - size);
+ dbg_dname_hex((char *)res->name, res->size);
+
+ dbg_dname("Copying %d bytes from the suffix.\n", suffix->size);
+ memcpy(res->name + dname->size - size, suffix->name, suffix->size);
+
+ dbg_dname_hex((char *)res->name, res->size);
+
+ knot_dname_find_labels(res, 1);
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_dname_free(knot_dname_t **dname)
+{
+ if (dname == NULL || *dname == NULL) {
+ return;
+ }
+
+// char *name = knot_dname_to_str((*dname));
+
+// printf("freeing in dname: %s %p\n", name, *dname);
+
+// free(name);
+
+
+ if ((*dname)->name != NULL) {
+ free((*dname)->name);
+ }
+
+ if((*dname)->labels != NULL) {
+ free((*dname)->labels);
+ }
+
+
+// slab_free(*dname);
+ free(*dname);
+ *dname = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ return knot_dname_cmp(d1, d2, 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_dname_compare_cs(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ return knot_dname_cmp(d1, d2, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2)
+{
+ if (d2->size == 0) {
+ return d1;
+ }
+
+ if (knot_dname_is_fqdn(d1)) {
+ return NULL;
+ }
+
+ // allocate new space
+ uint8_t *new_dname = (uint8_t *)malloc(d1->size + d2->size);
+ CHECK_ALLOC_LOG(new_dname, NULL);
+
+ uint8_t *new_labels = (uint8_t *)malloc(d1->label_count
+ + d2->label_count);
+ if (new_labels == NULL) {
+ ERR_ALLOC_FAILED;
+ free(new_dname);
+ return NULL;
+ }
+
+ dbg_dname("1: copying %d bytes from adress %p to %p\n",
+ d1->size, d1->name, new_dname);
+
+ memcpy(new_dname, d1->name, d1->size);
+
+ dbg_dname("2: copying %d bytes from adress %p to %p\n",
+ d2->size, d2->name, new_dname + d1->size);
+
+ memcpy(new_dname + d1->size, d2->name, d2->size);
+
+ // update labels
+ memcpy(new_labels, d1->labels, d1->label_count);
+ for (int i = 0; i < d2->label_count; ++i) {
+ new_labels[d1->label_count + i] = d2->labels[i] + d1->size;
+ }
+
+ uint8_t *old_labels = d1->labels;
+ d1->labels = new_labels;
+ free(old_labels);
+ d1->label_count += d2->label_count;
+
+ uint8_t *old_name = d1->name;
+ d1->name = new_dname;
+ free(old_name);
+
+ d1->size += d2->size;
+
+ return d1;
+}
+
+void knot_dname_set_id(knot_dname_t *dname, unsigned int id)
+{
+ dname->id = id;
+}
+
+unsigned int knot_dname_get_id(const knot_dname_t *dname)
+{
+ if (dname != NULL) {
+ return dname->id;
+ } else {
+ return 0; /* 0 should never be used and is reserved for err. */
+ }
+}
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
new file mode 100644
index 0000000..c0e3f35
--- /dev/null
+++ b/src/libknot/dname.h
@@ -0,0 +1,428 @@
+/*!
+ * \file dname.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Domain name 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_DNAME_H_
+#define _KNOT_DNAME_H_
+
+#include <stdint.h>
+#include <string.h>
+#include "common/ref.h"
+
+struct knot_node;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for representing a domain name.
+ *
+ * Stores the domain name in wire format.
+ *
+ * \todo Consider restricting to FQDN only (see knot_dname_new_from_str()).
+ */
+struct knot_dname {
+ ref_t ref; /*!< Reference counting. */
+ uint8_t *name; /*!< Wire format of the domain name. */
+ /*!
+ * \brief Size of the domain name in octets.
+ * \todo Is this needed? Every dname should end with \0 or pointer.
+ */
+ unsigned int size;
+ uint8_t *labels;
+ unsigned short label_count;
+ struct knot_node *node; /*!< Zone node the domain name belongs to. */
+ unsigned int id; /*!< ID of domain name used in zone dumping. */
+};
+
+typedef struct knot_dname knot_dname_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates empty dname structure (no name, no owner node).
+ *
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ * it after use.
+ *
+ * \return Newly allocated and initialized dname structure.
+ *
+ * \todo Possibly useless.
+ */
+knot_dname_t *knot_dname_new();
+
+/*!
+ * \brief Creates a dname structure from domain name given in presentation
+ * format.
+ *
+ * The resulting domain name is stored in wire format, but it may not end with
+ * root label (0).
+ *
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ * it after use.
+ *
+ * \param name Domain name in presentation format (labels separated by dots).
+ * \param size Size of the domain name (count of characters with all dots).
+ * \param node Zone node the domain name belongs to. Set to NULL if not
+ * applicable.
+ *
+ * \return Newly allocated and initialized dname structure representing the
+ * given domain name.
+ */
+knot_dname_t *knot_dname_new_from_str(const char *name, unsigned int size,
+ struct knot_node *node);
+
+/*!
+ * \brief Creates a dname structure from domain name given in wire format.
+ *
+ * \note The name is copied into the structure.
+ * \note If the given name is not a FQDN, the result will be neither.
+ * \note Newly created dname is referenced, caller is responsible for releasing
+ * it after use.
+ *
+ * \param name Domain name in wire format.
+ * \param size Size of the domain name in octets.
+ * \param node Zone node the domain name belongs to. Set to NULL if not
+ * applicable.
+ *
+ * \return Newly allocated and initialized dname structure representing the
+ * given domain name.
+ *
+ * \todo This function does not check if the given data is in correct wire
+ * format at all. It thus creates a invalid domain name, which if passed
+ * e.g. to knot_dname_to_str() may result in crash. Decide whether it
+ * is OK to retain this and check the data in other functions before
+ * calling this one, or if it should verify the given data.
+ */
+knot_dname_t *knot_dname_new_from_wire(const uint8_t *name,
+ unsigned int size,
+ struct knot_node *node);
+
+knot_dname_t *knot_dname_parse_from_wire(const uint8_t *wire,
+ size_t *pos, size_t size,
+ struct knot_node *node);
+
+/*!
+ * \brief Initializes domain name by the name given in wire format.
+ *
+ * \note The name is copied into the structure.
+ * \note If there is any name in the structure, it will be replaced.
+ * \note If the given name is not a FQDN, the result will be neither.
+ *
+ * \param name Domain name in wire format.
+ * \param size Size of the domain name in octets.
+ * \param node Zone node the domain name belongs to. Set to NULL if not
+ * applicable.
+ * \param target Domain name structure to initialize.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM if allocation of labels info failed.
+ * \retval KNOT_EBADARG if name or target is null.
+ *
+ * \todo This function does not check if the given data is in correct wire
+ * format at all. It thus creates a invalid domain name, which if passed
+ * e.g. to knot_dname_to_str() may result in crash. Decide whether it
+ * is OK to retain this and check the data in other functions before
+ * calling this one, or if it should verify the given data.
+ */
+int knot_dname_from_wire(const uint8_t *name, unsigned int size,
+ struct knot_node *node, knot_dname_t *target);
+
+/*!
+ * \brief Duplicates the given domain name.
+ *
+ * \note Copied dname referense count is reset to 1, caller is responsible
+ * for releasing it after use.
+ *
+ * \param dname Domain name to be copied.
+ *
+ * \return New domain name which is an exact copy of \a dname.
+ */
+knot_dname_t *knot_dname_deep_copy(const knot_dname_t *dname);
+
+/*!
+ * \brief Converts the given domain name to string representation.
+ *
+ * \note Allocates new memory, remember to free it.
+ *
+ * \param dname Domain name to be converted.
+ *
+ * \return 0-terminated string representing the given domain name in
+ * presentation format.
+ */
+char *knot_dname_to_str(const knot_dname_t *dname);
+
+int knot_dname_to_lower(knot_dname_t *dname);
+
+int knot_dname_to_lower_copy(const knot_dname_t *dname, char *name,
+ size_t size);
+
+/*!
+ * \brief Returns the domain name in wire format.
+ *
+ * \param dname Domain name.
+ *
+ * \return Wire format of the domain name.
+ */
+const uint8_t *knot_dname_name(const knot_dname_t *dname);
+
+/*!
+ * \brief Returns size of the given domain name.
+ *
+ * \param dname Domain name to get the size of.
+ *
+ * \return Size of the domain name in wire format in octets.
+ */
+unsigned int knot_dname_size(const knot_dname_t *dname);
+
+unsigned int knot_dname_id(const knot_dname_t *dname);
+
+/*!
+ * \brief Returns size of a part of domain name.
+ *
+ * \param dname Domain name.
+ * \param labels Count of labels to get the size of (counted from left).
+ *
+ * \return Size of first \a labels labels of \a dname, counted from left.
+ */
+uint8_t knot_dname_size_part(const knot_dname_t *dname, int labels);
+
+/*!
+ * \brief Returns the zone node the domain name belongs to.
+ *
+ * \param dname Domain name to get the zone node of.
+ *
+ * \return Zone node the domain name belongs to or NULL if none.
+ */
+const struct knot_node *knot_dname_node(const knot_dname_t *dname,
+ int check_version);
+
+struct knot_node *knot_dname_get_node(knot_dname_t *dname,
+ int check_version);
+
+void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
+
+void knot_dname_update_node(knot_dname_t *dname);
+
+void knot_dname_set_node(knot_dname_t *dname, struct knot_node *node);
+
+/*!
+ * \brief Checks if the given domain name is a fully-qualified domain name.
+ *
+ * \param dname Domain name to check.
+ *
+ * \retval <> 0 if \a dname is a FQDN.
+ * \retval 0 otherwise.
+ */
+int knot_dname_is_fqdn(const knot_dname_t *dname);
+
+/*!
+ * \brief Creates new domain name by removing leftmost label from \a dname.
+ *
+ * \note Newly created dname reference count is set to 1, caller is responsible
+ * for releasing it after use.
+ *
+ * \param dname Domain name to remove the first label from.
+ *
+ * \return New domain name with the same labels as \a dname, except for the
+ * leftmost label, which is removed.
+ */
+knot_dname_t *knot_dname_left_chop(const knot_dname_t *dname);
+
+/*!
+ * \brief Removes leftmost label from \a dname.
+ *
+ * \param dname Domain name to remove the first label from.
+ */
+void knot_dname_left_chop_no_copy(knot_dname_t *dname);
+
+/*!
+ * \brief Checks if one domain name is a subdomain of other.
+ *
+ * \param sub Domain name to be the possible subdomain.
+ * \param domain Domain name to be the possible parent domain.
+ *
+ * \retval <> 0 if \a sub is a subdomain of \a domain.
+ * \retval 0 otherwise.
+ */
+int knot_dname_is_subdomain(const knot_dname_t *sub,
+ const knot_dname_t *domain);
+
+/*!
+ * \brief Checks if the domain name is a wildcard.
+ *
+ * \param dname Domain name to check.
+ *
+ * \retval <> 0 if \a dname is a wildcard domain name.
+ * \retval 0 otherwise.
+ */
+int knot_dname_is_wildcard(const knot_dname_t *dname);
+
+/*!
+ * \brief Returns the number of labels common for the two domain names (counted
+ * from the rightmost label.
+ *
+ * \param dname1 First domain name.
+ * \param dname2 Second domain name.
+ *
+ * \return Number of labels common for the two domain names.
+ */
+int knot_dname_matched_labels(const knot_dname_t *dname1,
+ const knot_dname_t *dname2);
+
+/*!
+ * \brief Returns the number of labels in the domain name.
+ *
+ * \param dname Domain name to get the label count of.
+ *
+ * \return Number of labels in \a dname.
+ *
+ * \todo Find out if this counts the root label also.
+ */
+int knot_dname_label_count(const knot_dname_t *dname);
+
+/*!
+ * \brief Returns the size of the requested label in the domain name.
+ *
+ * \param dname Domain name to get the label size from.
+ * \param i Index of the label (0 is the leftmost label).
+ *
+ * \return Size of \a i-th label in \a dname (counted from left).
+ */
+uint8_t knot_dname_label_size(const knot_dname_t *dname, int i);
+
+/*!
+ * \brief Replaces the suffix of given size in one domain name with other domain
+ * name.
+ *
+ * \param dname Domain name where to replace the suffix.
+ * \param size Size of the suffix to be replaced.
+ * \param suffix New suffix to be used as a replacement.
+ *
+ * \return New domain name created by replacing suffix of \a dname of size
+ * \a size with \a suffix.
+ */
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *dname,
+ int size,
+ const knot_dname_t *suffix);
+
+/*!
+ * \brief Destroys the given domain name.
+ *
+ * Frees also the data within the struct. This is somewhat different behaviour
+ * than that of RDATA and RRSet structures which do not deallocate their
+ * contents.
+ *
+ * Sets the given pointer to NULL.
+ *
+ * \param dname Domain name to be destroyed.
+ */
+void knot_dname_free(knot_dname_t **dname);
+
+/*!
+ * \brief Compares two domain names (case insensitive).
+ *
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
+ *
+ * \retval < 0 if \a d1 goes before \a d2 in canonical order.
+ * \retval > 0 if \a d1 goes after \a d2 in canonical order.
+ * \retval 0 if the domain names are identical.
+ */
+int knot_dname_compare(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Compares two domain names (case sensitive).
+ *
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
+ *
+ * \retval < 0 if \a d1 goes before \a d2 in canonical order.
+ * \retval > 0 if \a d1 goes after \a d2 in canonical order.
+ * \retval 0 if the domain names are identical.
+ */
+int knot_dname_compare_cs(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Concatenates two domain names.
+ *
+ * \note Member \a node is ignored, i.e. preserved.
+ *
+ * \param d1 First domain name (will be modified).
+ * \param d2 Second domain name (will not be modified).
+ *
+ * \return The concatenated domain name (i.e. modified \a d1) or NULL if
+ * the operation is not valid (e.g. \a d1 is a FQDN).
+ */
+knot_dname_t *knot_dname_cat(knot_dname_t *d1, const knot_dname_t *d2);
+
+void knot_dname_set_id(knot_dname_t *dname, unsigned int id);
+
+unsigned int knot_dname_get_id(const knot_dname_t *dname);
+
+/*!
+ * \brief Increment reference counter for dname.
+ *
+ * Function makes shallow copy (reference).
+ *
+ * \param dname Referenced dname.
+ */
+static inline void knot_dname_retain(knot_dname_t *dname) {
+ if (dname) {
+ ref_retain(&dname->ref);
+// char *name = knot_dname_to_str(dname);
+// printf("retain: %s %p %d\n", name, dname, dname->ref.count);
+// free(name);
+
+ }
+}
+
+/*#define knot_dname_retain(d) \
+ knot_dname_retain_((d));\
+ if ((d))\
+ printf("dname_retain: %s() at %s:%d, %p refcount=%zu\n",\
+ __func__, __FILE__, __LINE__, d, (d)->ref.count) */
+
+/*!
+ * \brief Decrement reference counter for dname.
+ *
+ * \param dname Referenced dname.
+ */
+static inline void knot_dname_release(knot_dname_t *dname) {
+ if (dname) {
+// char *name = knot_dname_to_str(dname);
+// printf("releasing: %p %s %d\n", dname, name, dname->ref.count - 1);
+// free(name);
+ ref_release(&dname->ref);
+ }
+}
+
+/*#define knot_dname_release(d) \
+ if ((d))\
+ printf("dname_release: %s() at %s:%d, %p refcount=%zu\n",\
+ __func__, __FILE__, __LINE__, d, (d)->ref.count-1);\
+ knot_dname_release_((d)) */
+
+#endif /* _KNOT_DNAME_H_ */
+
+/*! @} */
diff --git a/src/libknot/edns.c b/src/libknot/edns.c
new file mode 100644
index 0000000..05ebd7b
--- /dev/null
+++ b/src/libknot/edns.c
@@ -0,0 +1,416 @@
+/* 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 <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "edns.h"
+#include "common.h"
+#include "util/descriptor.h"
+#include "util/debug.h"
+#include "util/error.h"
+
+/*! \brief Various EDNS constatns. */
+enum knot_edns_consts {
+ /*! \brief Mask for the DO bit. */
+ KNOT_EDNS_DO_MASK = (uint16_t)0x8000,
+ /*! \brief Step for allocation of space for option entries. */
+ KNOT_EDNS_OPTION_STEP = 1
+};
+
+/*! \brief Minimum size of EDNS OPT RR in wire format. */
+static const short KNOT_EDNS_MIN_SIZE = 11;
+
+/*----------------------------------------------------------------------------*/
+
+knot_opt_rr_t *knot_edns_new()
+{
+ knot_opt_rr_t *opt_rr = (knot_opt_rr_t *)malloc(
+ sizeof(knot_opt_rr_t));
+ CHECK_ALLOC_LOG(opt_rr, NULL);
+ opt_rr->size = KNOT_EDNS_MIN_SIZE;
+ opt_rr->option_count = 0;
+ opt_rr->options_max = 0;
+
+ opt_rr->ext_rcode = 0;
+ opt_rr->flags = 0;
+ opt_rr->version = 0;
+
+ return opt_rr;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire,
+ size_t max_size)
+{
+ const uint8_t *pos = wire;
+ int parsed = 0;
+
+ if (pos == NULL || max_size == 0 || opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (max_size < KNOT_EDNS_MIN_SIZE) {
+ dbg_edns("Not enough data to parse OPT RR header.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ // owner of EDNS OPT RR must be root (0)
+ if (*pos != 0) {
+ dbg_edns("EDNS packet malformed (expected root "
+ "domain as owner).\n");
+ return KNOT_EMALF;
+ }
+ pos += 1;
+
+ // check the type of the record (must be OPT)
+ if (knot_wire_read_u16(pos) != KNOT_RRTYPE_OPT) {
+ dbg_edns("EDNS packet malformed (expected OPT type"
+ ".\n");
+ return KNOT_EMALF;
+ }
+ pos += 2;
+
+ opt_rr->payload = knot_wire_read_u16(pos);
+ dbg_edns("Parsed payload: %u\n", opt_rr->payload);
+
+ pos += 2;
+ opt_rr->ext_rcode = *(pos++);
+ opt_rr->version = *(pos++);
+ opt_rr->flags = knot_wire_read_u16(pos);
+ pos += 2;
+
+ parsed = KNOT_EDNS_MIN_SIZE;
+
+ // ignore RDATA, but move pos behind them
+ uint16_t rdlength = knot_wire_read_u16(pos);
+ pos += 2;
+
+ if (max_size - parsed < rdlength) {
+ dbg_edns("Not enough data to parse OPT RR.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ while (parsed < rdlength + KNOT_EDNS_MIN_SIZE) {
+ if (max_size - parsed < 4) {
+ dbg_edns("Not enough data to parse OPT RR"
+ " OPTION header.\n");
+ return KNOT_EFEWDATA;
+ }
+ uint16_t code = knot_wire_read_u16(pos);
+ pos += 2;
+ uint16_t length = knot_wire_read_u16(pos);
+ pos += 2;
+ dbg_edns("EDNS OPTION: Code: %u, Length: %u\n",
+ code, length);
+ if (max_size - parsed - 4 < length) {
+ dbg_edns("Not enough data to parse OPT RR"
+ " OPTION data.\n");
+ return KNOT_EFEWDATA;
+ }
+ int ret;
+ if ((ret =
+ knot_edns_add_option(opt_rr, code, length, pos)) != 0) {
+ dbg_edns("Error parsing OPT option field.\n");
+ return ret;
+ }
+ pos += length;
+ parsed += length + 4;
+ }
+
+ return parsed;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr,
+ const knot_rrset_t *rrset)
+{
+ if (opt_rr == NULL || rrset == NULL
+ || knot_rrset_type(rrset) != KNOT_RRTYPE_OPT) {
+ return KNOT_EBADARG;
+ }
+
+ dbg_edns("Parsing payload.\n");
+ opt_rr->payload = knot_rrset_class(rrset);
+
+ // the TTL has switched bytes
+ uint32_t ttl;
+ dbg_edns("TTL: %u\n", knot_rrset_ttl(rrset));
+ knot_wire_write_u32((uint8_t *)&ttl, knot_rrset_ttl(rrset));
+ // first byte of TTL is extended RCODE
+ dbg_edns("TTL: %u\n", ttl);
+ memcpy(&opt_rr->ext_rcode, &ttl, 1);
+ dbg_edns("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode);
+ // second is the version
+ memcpy(&opt_rr->version, (const uint8_t *)(&ttl) + 1, 1);
+ dbg_edns("Parsed version: %u.\n", opt_rr->version);
+ // third and fourth are flags
+ opt_rr->flags = knot_wire_read_u16((const uint8_t *)(&ttl) + 2);
+ dbg_edns("Parsed flags: %u.\n", opt_rr->flags);
+ // size of the header, options are counted elsewhere
+ opt_rr->size = 11;
+
+ int rc = 0;
+ dbg_edns("Parsing options.\n");
+ const knot_rdata_t *rdata = knot_rrset_rdata(rrset);
+
+ // in OPT RR, all RDATA are in one RDATA item stored as BINARY data,
+ // i.e. preceded by their length
+ if (rdata != NULL) {
+ assert(knot_rdata_item_count(rdata) == 1);
+ const uint8_t *raw = (const uint8_t *)
+ knot_rdata_item(rdata, 0)->raw_data;
+ uint16_t size = knot_wire_read_u16(raw);
+ int pos = 2;
+ assert(size > 0);
+ while (pos - 2 < size) {
+ // ensure there is enough data to parse the OPTION CODE
+ // and OPTION LENGTH
+ if (size - pos + 2 < 4) {
+ return KNOT_EMALF;
+ }
+ uint16_t opt_code = knot_wire_read_u16(raw + pos);
+ uint16_t opt_size = knot_wire_read_u16(raw + pos + 2);
+
+ // there should be enough data for parsing the OPTION
+ // data
+ if (size - pos - 2 < opt_size) {
+ return KNOT_EMALF;
+ }
+ rc = knot_edns_add_option(opt_rr, opt_code, opt_size,
+ raw + pos + 4);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ pos += 4 + opt_size;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_edns_get_payload(const knot_opt_rr_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->payload;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_payload(knot_opt_rr_t *opt_rr,
+ uint16_t payload)
+{
+ assert(opt_rr != NULL);
+ opt_rr->payload = payload;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_edns_get_ext_rcode(const knot_opt_rr_t *opt_rr)
+{
+ return opt_rr->ext_rcode;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_ext_rcode(knot_opt_rr_t *opt_rr,
+ uint8_t ext_rcode)
+{
+ assert(opt_rr != NULL);
+ opt_rr->ext_rcode = ext_rcode;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_edns_get_version(const knot_opt_rr_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_version(knot_opt_rr_t *opt_rr,
+ uint8_t version)
+{
+ assert(opt_rr != NULL);
+ opt_rr->version = version;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_edns_get_flags(const knot_opt_rr_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->flags;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_do(const knot_opt_rr_t *opt_rr)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ dbg_edns("Flags: %u\n", opt_rr->flags);
+ return (opt_rr->flags & KNOT_EDNS_DO_MASK);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_set_do(knot_opt_rr_t *opt_rr)
+{
+ if (opt_rr == NULL) {
+ return;
+ }
+
+ opt_rr->flags |= KNOT_EDNS_DO_MASK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_add_option(knot_opt_rr_t *opt_rr, uint16_t code,
+ uint16_t length, const uint8_t *data)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (opt_rr->option_count == opt_rr->options_max) {
+ knot_opt_option_t *options_new =
+ (knot_opt_option_t *)calloc(
+ (opt_rr->options_max + KNOT_EDNS_OPTION_STEP),
+ sizeof(knot_opt_option_t));
+ CHECK_ALLOC_LOG(options_new, KNOT_ENOMEM);
+ memcpy(options_new, opt_rr->options, opt_rr->option_count);
+ opt_rr->options = options_new;
+ opt_rr->options_max += KNOT_EDNS_OPTION_STEP;
+ }
+
+ dbg_edns("Adding option.\n");
+ dbg_edns("Code: %u.\n", code);
+ dbg_edns("Length: %u.\n", length);
+ dbg_edns("Data: %p.\n", data);
+
+ opt_rr->options[opt_rr->option_count].data = (uint8_t *)malloc(length);
+ CHECK_ALLOC_LOG(opt_rr->options[opt_rr->option_count].data, KNOT_ENOMEM);
+ memcpy(opt_rr->options[opt_rr->option_count].data, data, length);
+
+ opt_rr->options[opt_rr->option_count].code = code;
+ opt_rr->options[opt_rr->option_count].length = length;
+
+ ++opt_rr->option_count;
+ opt_rr->size += 4 + length;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_edns_has_option(const knot_opt_rr_t *opt_rr, uint16_t code)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int i = 0;
+ while (i < opt_rr->option_count && opt_rr->options[i].code != code) {
+ ++i;
+ }
+
+ assert(i >= opt_rr->option_count || opt_rr->options[i].code == code);
+
+ return (i < opt_rr->option_count);
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_edns_to_wire(const knot_opt_rr_t *opt_rr, uint8_t *wire,
+ size_t max_size)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(KNOT_EDNS_MIN_SIZE <= max_size);
+
+ if (max_size < opt_rr->size) {
+ dbg_edns("Not enough place for OPT RR wire format.\n");
+ return KNOT_ESPACE;
+ }
+
+ uint8_t *pos = wire;
+ *(pos++) = 0;
+ knot_wire_write_u16(pos, KNOT_RRTYPE_OPT);
+ pos += 2;
+ knot_wire_write_u16(pos, opt_rr->payload);
+ pos += 2;
+ *(pos++) = opt_rr->ext_rcode;
+ *(pos++) = opt_rr->version;
+ knot_wire_write_u16(pos, opt_rr->flags);
+ pos += 2;
+
+ uint8_t *rdlen = pos;
+ uint16_t len = 0;
+ pos += 2;
+
+ // OPTIONs
+ for (int i = 0; i < opt_rr->option_count; ++i) {
+ knot_wire_write_u16(pos, opt_rr->options[i].code);
+ pos += 2;
+ knot_wire_write_u16(pos, opt_rr->options[i].length);
+ pos += 2;
+ memcpy(pos, opt_rr->options[i].data, opt_rr->options[i].length);
+ pos += opt_rr->options[i].length;
+ len += 4 + opt_rr->options[i].length;
+ }
+
+ knot_wire_write_u16(rdlen, len);
+
+ return opt_rr->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_edns_size(knot_opt_rr_t *opt_rr)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return opt_rr->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_edns_free(knot_opt_rr_t **opt_rr)
+{
+ if (opt_rr == NULL || *opt_rr == NULL) {
+ return;
+ }
+
+ if ((*opt_rr)->option_count > 0) {
+ free((*opt_rr)->options);
+ }
+ free(*opt_rr);
+ *opt_rr = NULL;
+}
diff --git a/src/libknot/edns.h b/src/libknot/edns.h
new file mode 100644
index 0000000..010d155
--- /dev/null
+++ b/src/libknot/edns.h
@@ -0,0 +1,273 @@
+/*!
+ * \file edns.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Functions for manipulating and parsing EDNS OPT pseudo-RR.
+ *
+ * \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_EDNS_H_
+#define _KNOT_EDNS_H_
+
+#include <stdint.h>
+
+#include "util/utils.h"
+#include "rrset.h"
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Structure representing one OPT RR Option. */
+struct knot_opt_option {
+ uint16_t code;
+ uint16_t length;
+ uint8_t *data;
+};
+
+/*! \brief Structure representing one OPT RR Option. */
+typedef struct knot_opt_option knot_opt_option_t;
+
+/*!
+ * \brief Structure for holding EDNS parameters.
+ *
+ * \todo NSID
+ */
+struct knot_opt_rr {
+ uint16_t payload; /*!< UDP payload. */
+ uint8_t ext_rcode; /*!< Extended RCODE. */
+
+ /*!
+ * \brief Supported version of EDNS.
+ *
+ * Set to EDNS_NOT_SUPPORTED if not supported.
+ */
+ uint8_t version;
+
+ uint16_t flags; /*!< EDNS flags. */
+ knot_opt_option_t *options; /*!< EDNS options. */
+ short option_count; /*!< Count of EDNS options in this OPT RR.*/
+ short options_max; /*!< Maximum count of options. */
+ short size; /*!< Total size of the OPT RR in wire format. */
+};
+
+/*! \brief Structure for holding EDNS parameters. */
+typedef struct knot_opt_rr knot_opt_rr_t;
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Constants for supported versions of EDNS. */
+enum knot_edns_versions {
+ EDNS_VERSION_0 = (uint8_t)0, /*!< EDNS version 0. */
+ EDNS_NOT_SUPPORTED = (uint8_t)255 /*!< EDNS not supported. */
+};
+
+/*! \brief Constants for EDNS option codes. */
+enum knot_edns_option_codes {
+ EDNS_OPTION_NSID = (uint16_t)3 /*!< NSID option code. */
+};
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates new empty OPT RR structure for holding EDNS parameters.
+ *
+ * \return New empty knot_opt_rr_t structure, or NULL if not successful.
+ */
+knot_opt_rr_t *knot_edns_new();
+
+/*!
+ * \brief Initializes OPT RR structure from given OPT RR in wire format.
+ *
+ * \param opt_rr OPT RR structure to initialize.
+ * \param wire Wire format of the OPT RR to parse.
+ * \param max_size Maximum size of the wire format in bytes (may be more
+ * than acutal size of the OPT RR).
+ *
+ * \return Size of the parserd OPT RR in bytes if successful (always > 0).
+ * \retval KNOT_EBADARG
+ * \retval KNOT_EFEWDATA
+ * \retval KNOT_EMALF
+ * \retval KNOT_ENOMEM
+ */
+int knot_edns_new_from_wire(knot_opt_rr_t *opt_rr, const uint8_t *wire,
+ size_t max_size);
+
+int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr,
+ const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the UDP payload stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the payload from.
+ *
+ * \return UDP payload in bytes.
+ */
+uint16_t knot_edns_get_payload(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the UDP payload field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to set the payload to.
+ * \param payload UDP payload in bytes.
+ */
+void knot_edns_set_payload(knot_opt_rr_t *opt_rr, uint16_t payload);
+
+/*!
+ * \brief Returns the Extended RCODE stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the Extended RCODE from.
+ *
+ * \return Extended RCODE.
+ */
+uint8_t knot_edns_get_ext_rcode(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the Extended RCODE field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to set the Extended RCODE to.
+ * \param ext_rcode Extended RCODE to set.
+ */
+void knot_edns_set_ext_rcode(knot_opt_rr_t *opt_rr, uint8_t ext_rcode);
+
+/*!
+ * \brief Returns the EDNS version stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the EDNS version from.
+ *
+ * \return EDNS version.
+ */
+uint8_t knot_edns_get_version(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the EDNS version field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to set the EDNS version to.
+ * \param version EDNS version to set.
+ */
+void knot_edns_set_version(knot_opt_rr_t *opt_rr, uint8_t version);
+
+/*!
+ * \brief Returns the flags stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR structure to get the flags from.
+ *
+ * \return EDNS flags.
+ */
+uint16_t knot_edns_get_flags(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Returns the state of the DO bit in the OPT RR flags.
+ *
+ * \param opt_rr OPT RR structure to get the DO bit from.
+ *
+ * \return <> 0 if the DO bit is set.
+ * \return 0 if the DO bit is not set.
+ */
+int knot_edns_do(const knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Sets the DO bit in the OPT RR.
+ *
+ * \param opt_rr OPT RR structure to set the DO bit in.
+ */
+void knot_edns_set_do(knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Adds EDNS Option to the OPT RR.
+ *
+ * \param opt_rr OPT RR structure to add the Option to.
+ * \param code Option code.
+ * \param length Option data length in bytes.
+ * \param data Option data.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_edns_add_option(knot_opt_rr_t *opt_rr, uint16_t code,
+ uint16_t length, const uint8_t *data);
+
+/*!
+ * \brief Checks if the OPT RR contains Option with the specified code.
+ *
+ * \param opt_rr OPT RR structure to check for the Option in.
+ * \param code Option code to check for.
+ *
+ * \retval <> 0 if the OPT RR contains Option with Option code \a code.
+ * \retval 0 otherwise.
+ */
+int knot_edns_has_option(const knot_opt_rr_t *opt_rr, uint16_t code);
+
+/*!
+ * \brief Converts the given OPT RR into wire format.
+ *
+ * \param opt_rr OPT RR structure to convert into wire format.
+ * \param wire Place to put the wire format to.
+ * \param max_size Maximum space available for the wire format in bytes.
+ *
+ * \return Size of the wire format in bytes if successful.
+ * \retval KNOT_ESPACE
+ */
+short knot_edns_to_wire(const knot_opt_rr_t *opt_rr, uint8_t *wire,
+ size_t max_size);
+
+/*!
+ * \brief Returns size of the OPT RR in wire format.
+ *
+ * \param opt_rr OPT RR to get the size of.
+ *
+ * \return Size of the OPT RR in bytes.
+ */
+short knot_edns_size(knot_opt_rr_t *opt_rr);
+
+/*!
+ * \brief Properly destroys the OPT RR structure.
+ *
+ * \note Also sets the given pointer to NULL.
+ */
+void knot_edns_free(knot_opt_rr_t **opt_rr);
+
+#endif /* _KNOT_EDNS_H_ */
+
+/*! @} */
diff --git a/src/libknot/hash/cuckoo-hash-table.c b/src/libknot/hash/cuckoo-hash-table.c
new file mode 100644
index 0000000..c5d1c4f
--- /dev/null
+++ b/src/libknot/hash/cuckoo-hash-table.c
@@ -0,0 +1,1688 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h> /* defines uint32_t etc */
+#include <assert.h>
+#include <pthread.h>
+#include <math.h>
+
+#include <urcu.h>
+
+#include "util/utils.h"
+#include "common.h"
+#include "util/debug.h"
+#include "hash/cuckoo-hash-table.h"
+#include "hash/hash-functions.h"
+#include "common/dynamic-array.h"
+
+/*----------------------------------------------------------------------------*/
+/* Macros and inline functions */
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Default size table holding information about used hash table cells
+ * when hashing.
+ */
+#define RELOCATIONS_DEFAULT 200
+
+/*!
+ * \brief Maximum size table holding information about used hash table cells
+ * when hashing (just for debug issues).
+ */
+#define RELOCATIONS_MAX 1000
+
+/*!
+ * \brief Macro for hashing the given key using the universal system.
+ *
+ * \param system Universal system to use for the hashing.
+ * \param key Key to hash.
+ * \param length Size of the key in bytes.
+ * \param exp Exponent of the hash table size (the size is a power of 2).
+ * \param table Hash table index.
+ * \param gen Universal system generation.
+ *
+ * \return Hashed key.
+ */
+#define HASH(system, key, length, exp, gen, table) \
+ us_hash(system, fnv_32_buf(key, length, FNV1_32_INIT), exp, table, gen)
+
+/*!
+ * \brief Approximate ratio of hash table size to number of hashed items when 2
+ * tables are used.
+ */
+static const float SIZE_RATIO_2 = 2;
+
+/*!
+ * \brief Approximate ratio of hash table size to number of hashed items when 3
+ * tables are used.
+ */
+static const float SIZE_RATIO_3 = 1.15;
+
+/*!
+ * \brief Approximate ratio of hash table size to number of hashed items when 4
+ * tables are used.
+ */
+static const float SIZE_RATIO_4 = 1.08;
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Flag marking the generation of hash table or its item to be 1. */
+static const uint8_t FLAG_GENERATION1 = 0x1; // 00000001
+/*! \brief Flag marking the generation of hash table or its item to be 2. */
+static const uint8_t FLAG_GENERATION2 = 0x2; // 00000010
+/*! \brief Flag marking both generations. */
+static const uint8_t FLAG_GENERATION_BOTH = 0x3; // 00000011
+
+/*! \brief Flag used to mark the table when it's being rehashed. */
+static const uint8_t FLAG_REHASH = 0x4; // 00000100
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Clears the table / item flags. */
+static inline void CLEAR_FLAGS(uint8_t *flags)
+{
+ *flags = (uint8_t)0x0;
+}
+
+/*! \brief Returns the generation stored in the flags. */
+static inline uint8_t GET_GENERATION(uint8_t flags)
+{
+ return (flags & FLAG_GENERATION_BOTH);
+}
+
+/*! \brief Checks if the generation stored in both flags are the same. */
+static inline int EQUAL_GENERATIONS(uint8_t flags1, uint8_t flags2)
+{
+ return (GET_GENERATION(flags1) == GET_GENERATION(flags2));
+}
+
+/*! \brief Checks if the generation stored in the flags is 1. */
+static inline int IS_GENERATION1(uint8_t flags)
+{
+ return ((flags & FLAG_GENERATION1) != 0);
+}
+
+/*! \brief Sets the generation stored in the flags to 1. */
+static inline void SET_GENERATION1(uint8_t *flags)
+{
+ *flags = ((*flags) & ~FLAG_GENERATION2) | FLAG_GENERATION1;
+}
+
+/*! \brief Checks if the generation stored in the flags is 2. */
+static inline int IS_GENERATION2(uint8_t flags)
+{
+ return ((flags & FLAG_GENERATION2) != 0);
+}
+
+/*! \brief Sets the generation stored in the flags to 2. */
+static inline void SET_GENERATION2(uint8_t *flags)
+{
+ *flags = ((*flags) & ~FLAG_GENERATION1) | FLAG_GENERATION2;
+}
+
+/*! \brief Sets the generation stored in the flags to the given generation. */
+static inline void SET_GENERATION(uint8_t *flags, uint8_t generation)
+{
+ *flags = ((*flags) & ~FLAG_GENERATION_BOTH) | generation;
+}
+
+/*! \brief Sets the generation stored in the flags to the next one (cyclic). */
+static inline uint8_t SET_NEXT_GENERATION(uint8_t *flags)
+{
+ return ((*flags) ^= FLAG_GENERATION_BOTH);
+}
+
+/*! \brief Returns the next generation to the one stored in flags (cyclic). */
+static inline uint8_t NEXT_GENERATION(uint8_t flags)
+{
+ return ((flags & FLAG_GENERATION_BOTH) ^ FLAG_GENERATION_BOTH);
+}
+
+/*! \brief Sets the rehashing flag to the flags. */
+static inline void SET_REHASHING_ON(uint8_t *flags)
+{
+ *flags = (*flags | FLAG_REHASH);
+}
+
+/*! \brief Removes the rehashing flag from the flags. */
+static inline void SET_REHASHING_OFF(uint8_t *flags)
+{
+ *flags = (*flags & ~FLAG_REHASH);
+}
+
+/*! \brief Checks if the rehashing flag is set in the flags. */
+static inline int IS_REHASHING(uint8_t flags)
+{
+ return ((flags & FLAG_REHASH) != 0);
+}
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Returns the exponent of the nearest larger power of two.
+ */
+static uint get_larger_exp(uint n)
+{
+ uint res = 0;
+ while (hashsize(++res) < n) {}
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Counts the ideal table count and the exponent of those tables' sizes.
+ *
+ * Only 3 or 4 hash tables are considered. The setup in which less items are
+ * wasted is recommended.
+ *
+ * \param items Number of items to hash.
+ * \param table_count Recommended number of tables will be saved here.
+ *
+ * \return Exponent of the tables' sizes.
+ */
+static uint get_table_exp_and_count(uint items, uint *table_count)
+{
+ // considering only 3 or 4 tables
+ int exp3 = get_larger_exp((items * SIZE_RATIO_3) / 3);
+ int exp4 = get_larger_exp(items * SIZE_RATIO_4) - 2;
+
+ if (exp4 < 0) {
+ exp4 = 1;
+ }
+
+ dbg_ck("Determining ideal table size...\n");
+ dbg_ck("\tNumber of items: %u\n", items);
+ dbg_ck("\tThree tables: size of one table: %u, total size: %u\n",
+ hashsize(exp3), 3 * hashsize(exp3));
+ dbg_ck("\tFour tables: size of one table: %u, total size: %u\n",
+ hashsize(exp4), 4 * hashsize(exp4));
+
+ // we need exponent at least 1 (this is quite ugly..)
+ if (exp3 == 0) {
+ exp3 = 1;
+ }
+ if (exp4 == 0) {
+ exp4 = 1;
+ }
+
+ if (exp3 >= 32 || exp4 >= 32) {
+ return 0;
+ }
+
+ if (((hashsize(exp3) * 3) - (items)) < ((hashsize(exp4) * 4) - items)) {
+ *table_count = 3;
+ return exp3;
+ } else {
+ *table_count = 4;
+ return exp4;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Counts the maximum effective item count based on size of the tables.
+ *
+ * For 3 tables, the effective utilization should be around 91%.
+ * For 4 tables it is 97%.
+ *
+ * See Fotakis, Dimitris, et al. - Space Efficient Hash Tables with Worst Case
+ * Constant Access Time. CiteSeerX. 2003
+ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.5337
+ */
+static uint get_max_table_items(uint table_count, int table_exponent)
+{
+ assert(table_count == 3 || table_count == 4);
+
+ float coef;
+
+ if (table_count == 3) {
+ coef = 0.91;
+ } else {
+ coef = 0.97;
+ }
+
+ return (uint)floor((table_count * hashsize(table_exponent)) * coef);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_is_full(const ck_hash_table_t *table)
+{
+ return (table->items >= get_max_table_items(table->table_count,
+ table->table_size_exp));
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_stash_is_full(const ck_hash_table_t *table)
+{
+ return (table->items_in_stash >= STASH_SIZE_MAX);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Clears the given item by assigning a NULL pointer to it.
+ */
+static inline void ck_clear_item(ck_hash_table_item_t **item)
+{
+ *item = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Insert given contents to the hash table item.
+ */
+static void ck_fill_item(const char *key, size_t key_length, void *value,
+ uint generation, ck_hash_table_item_t *item)
+{
+ // must allocate new space for key and value, otherwise it will be lost!
+ item->key = key;
+ item->key_length = key_length;
+ item->value = value;
+ CLEAR_FLAGS(&item->timestamp);
+ item->timestamp = generation;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Swaps two hash table items.
+ */
+static inline void ck_swap_items(ck_hash_table_item_t **item1,
+ ck_hash_table_item_t **item2)
+{
+ ck_hash_table_item_t *tmp = *item1;
+ *item1 = *item2;
+ *item2 = tmp;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets the \a item pointer to the \a to pointer.
+ */
+static inline void ck_put_item(ck_hash_table_item_t **to,
+ ck_hash_table_item_t *item)
+{
+ *to = item;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the hash was already used twice.
+ *
+ * If yes, it means we entered a loop in the hashing process, so we must stop.
+ * Otherwise it remembers that we used the hash.
+ *
+ * \note According to Kirsch, et al. a check that at most one hash was used
+ * twice should be sufficient. We will retain our version for now.
+ *
+ * \param used Array of used table indices (hashes).
+ * \param hash Hash to check.
+ *
+ * \retval -1 if the hash was already used twice.
+ * \retval -2 if an error occured.
+ * \retval 0 if the hash was not used twice yet.
+ */
+static uint ck_check_used_twice(da_array_t *used, uint32_t hash)
+{
+ uint i = 0, found = 0;
+ while (i <= da_get_count(used) && found < 2) {
+ ++i;
+ if (((uint *)(da_get_items(used)))[i] == hash) {
+ ++found;
+ }
+ }
+
+ if (i <= da_get_count(used) && found == 2) {
+ dbg_ck_hash("Hashing entered infinite loop.\n");
+ return -1;
+ } else {
+ if (da_reserve(used, 1) < 0) {
+ ERR_ALLOC_FAILED;
+ return -2;
+ }
+ ((uint *)da_get_items(used))[da_get_count(used)] = hash;
+ da_occupy(used, 1);
+ assert(da_get_count(used) < RELOCATIONS_MAX);
+ return 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares the key of item with the given key.
+ *
+ * \param item Item to compare with.
+ * \param key Key to compare.
+ * \param length Size of the key in bytes.
+ *
+ * \return <> 0 if the keys match.
+ * \return 0 if they don't.
+ */
+static inline uint ck_items_match(const ck_hash_table_item_t *item,
+ const char *key, size_t length)
+{
+ return (length == item->key_length
+ && (strncmp(item->key, key, length) == 0));
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Switches the given table number to a randomly chosen other table
+ * number.
+ */
+static inline void ck_next_table(uint *table, uint table_count)
+{
+ uint next;
+ while ((*table) == (next = knot_quick_rand() % table_count)) {}
+ *table = next;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find the given key in the hash table's stash.
+ *
+ * \param table Hash table to search in.
+ * \param key Key to find.
+ * \param length Size of the key in bytes.
+ *
+ * \return Hash table item matching the key or NULL if not found in the stash.
+ */
+static ck_hash_table_item_t **ck_find_in_stash(const ck_hash_table_t *table,
+ const char *key, uint length)
+{
+ ck_stash_item_t *item = table->stash;
+ while (item != NULL) {
+ dbg_ck("Comparing item in stash (key: %.*s (size %zu))"
+ "with searched item (key %.*s (size %u)).\n",
+ (int)item->item->key_length, item->item->key,
+ item->item->key_length, (int)length, key, length);
+ if (ck_items_match(item->item, key, length)) {
+ return &item->item;
+ }
+ item = item->next;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find item with given key using hash functions from the given
+ * generation.
+ *
+ * \param table Hash table to search in.
+ * \param key Key to find.
+ * \param length Size of the key in bytes.
+ * \param generation Generation of items (table) to use. Items having other
+ * generation are ignored.
+ */
+static ck_hash_table_item_t **ck_find_gen(const ck_hash_table_t *table,
+ const char *key,
+ size_t length, uint8_t generation)
+{
+ uint32_t hash;
+ dbg_ck("Finding item in generation: %u\n", generation);
+
+ // check hash tables
+ for (uint t = 0; t < table->table_count; ++t) {
+ hash = HASH(&table->hash_system, key, length,
+ table->table_size_exp, generation, t);
+
+ dbg_ck("Hash: %u, key: %.*s\n", hash, (int)length, key);
+ dbg_ck("Table %d, hash: %u, item: %p\n", t + 1, hash,
+ table->tables[t][hash]);
+ if (table->tables[t][hash] != NULL) {
+ dbg_ck("Table %u, key: %.*s, value: %p, key "
+ "length: %zu\n",
+ t + 1, (int)table->tables[t][hash]->key_length,
+ table->tables[t][hash]->key,
+ table->tables[t][hash]->value,
+ table->tables[t][hash]->key_length);
+ }
+
+ if (table->tables[t][hash] &&
+ ck_items_match(table->tables[t][hash], key, length)) {
+ // found
+ return &table->tables[t][hash];
+ }
+ }
+
+ // try to find in stash
+ dbg_ck("Searching in stash...\n");
+
+ ck_hash_table_item_t **found =
+ ck_find_in_stash(table, key, length);
+
+ dbg_ck("Found pointer: %p\n", found);
+ if (found != NULL) {
+ dbg_ck("Stash, key: %.*s, value: %p, key length: %zu\n",
+ (int)(*found)->key_length, (*found)->key,
+ (*found)->value, (*found)->key_length);
+ }
+
+ // ck_find_in_buffer returns NULL if not found, otherwise pointer to
+ // item
+ return found;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds item with given key and returns non-constant pointer to pointer
+ * to the appropriate hash table item.
+ *
+ * \param table Hash table to search in.
+ * \param key Key to find.
+ * \param length Size of the key in bytes.
+ */
+static ck_hash_table_item_t **ck_find_item_nc(const ck_hash_table_t *table,
+ const char *key, size_t length)
+{
+ // get the generation of the table so that we use the same value
+ uint8_t generation = table->generation;
+
+ // find item using the table generation's hash functions
+ ck_hash_table_item_t **found = ck_find_gen(table, key, length,
+ GET_GENERATION(generation));
+ // if rehashing is in progress, try the next generation's functions
+ if (!found && IS_REHASHING(generation)) {
+ found = ck_find_gen(table, key, length,
+ NEXT_GENERATION(generation));
+ }
+
+ return found;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Hashes the given item using the given generation.
+ *
+ * \param table Hash table where to put the item.
+ * \param to_hash In: Item to hash. Out: NULL if successful, item that failed
+ * to hash if not.
+ * \param free Free place where to put the last moved item when the hasing
+ * is unsuccessful.
+ * \param generation Generation of items (table) to be used for hashing.
+ *
+ * \retval 0 if successful and no loop occured.
+ * \retval 1 if a loop occured and the item was inserted to the \a free place.
+ */
+static int ck_hash_item(ck_hash_table_t *table, ck_hash_table_item_t **to_hash,
+ ck_hash_table_item_t **free, uint8_t generation)
+{
+ da_array_t used[table->table_count];
+ for (uint i = 0; i < table->table_count; ++i) {
+ da_initialize(&used[i], RELOCATIONS_DEFAULT, sizeof(uint));
+ }
+
+ // hash until empty cell is encountered or until loop appears
+
+ dbg_ck_hash("Hashing key: %.*s of size %zu.\n",
+ (int)(*to_hash)->key_length, (*to_hash)->key,
+ (*to_hash)->key_length);
+
+ uint next_table = 0;
+
+ uint32_t hash = HASH(&table->hash_system, (*to_hash)->key,
+ (*to_hash)->key_length, table->table_size_exp,
+ generation, next_table);
+
+ dbg_ck_hash("New hash: %u.\n", hash);
+ assert(hash < hashsize(table->table_size_exp));
+
+ ((uint *)da_get_items(&used[next_table]))
+ [da_get_count(&used[next_table])] = hash;
+ ck_hash_table_item_t **next = &table->tables[next_table][hash];
+ dbg_ck_hash("Item to be moved: %p, place in table: %p\n",
+ *next, next);
+ ck_hash_table_item_t **moving = to_hash;
+
+ int loop = 0;
+
+ while (*next != NULL) {
+ dbg_ck_hash("Swapping items to hash: %p and Moving: %p\n",
+ to_hash, moving);
+ ck_swap_items(to_hash, moving); // first time it's unnecessary
+
+ // set the generation of the inserted item to the next
+ SET_GENERATION(&(*moving)->timestamp, generation);
+
+ moving = next;
+
+ dbg_ck_hash("Moving item from table %u, key: %.*s, hash %u ",
+ next_table + 1, (int)(*moving)->key_length,
+ (*moving)->key, hash);
+
+ // if rehashing and the 'next' item is from the old generation,
+ // start from table 1
+ if (generation != table->generation &&
+ EQUAL_GENERATIONS((*next)->timestamp, table->generation)) {
+ next_table = 0;
+ } else {
+ ck_next_table(&next_table, table->table_count);
+ }
+
+ hash = HASH(&table->hash_system, (*next)->key,
+ (*next)->key_length, table->table_size_exp,
+ generation, next_table);
+
+ next = &table->tables[next_table][hash];
+
+ dbg_ck_hash("to table %u, hash %u, item: %p, place: %p\n",
+ next_table + 1, hash, *next, next);
+
+ if ((*next) != NULL) {
+ dbg_ck_hash("Table %u, hash: %u, key: %.*s\n",
+ next_table + 1, hash,
+ (int)(*next)->key_length, (*next)->key);
+ }
+
+ // check if this cell wasn't already used in this item's hashing
+ if (ck_check_used_twice(&used[next_table], hash) != 0) {
+ next = free;
+ loop = -1;
+ break;
+ }
+ }
+
+ dbg_ck_hash("Putting pointer %p (*moving) to item %p (next).\n",
+ *moving, next);
+
+ ck_put_item(next, *moving);
+ // set the new generation for the inserted item
+ SET_GENERATION(&(*next)->timestamp, generation);
+ dbg_ck_hash("Putting pointer %p (*old) to item %p (moving).\n",
+ *to_hash, moving);
+
+ ck_put_item(moving, *to_hash);
+
+ // set the new generation for the inserted item
+ SET_GENERATION(&(*moving)->timestamp, generation);
+ *to_hash = NULL;
+
+ for (uint i = 0; i < table->table_count; ++i) {
+ da_destroy(&used[i]);
+ }
+
+ return loop;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void ck_rollback_rehash(ck_hash_table_t *table)
+{
+ // set old generation in tables
+ for (int i = 0; i < hashsize(table->table_size_exp); ++i) {
+ // no need for locking - timestamp is not used in lookup
+ // and two paralel insertions (and thus rehashings) are
+ // impossible
+ for (uint t = 0; t < table->table_count; ++t) {
+ if (table->tables[t][i] != NULL) {
+ SET_GENERATION(&table->tables[t][i]->timestamp,
+ table->generation);
+ }
+ }
+ }
+
+ // set old generation in stash
+ ck_stash_item_t *item = table->stash;
+ while (item != NULL) {
+ assert(item->item != NULL);
+ SET_GENERATION(&item->item->timestamp, table->generation);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds the given item to the hash table's stash.
+ *
+ * \param table Hash table to add the item to.
+ * \param item Item to add.
+ *
+ * \retval 0 if successful.
+ * \retval -1 if an error occured.
+ */
+int ck_add_to_stash(ck_hash_table_t *table, ck_hash_table_item_t *item)
+{
+ ck_stash_item_t *new_item
+ = (ck_stash_item_t *)malloc(sizeof(ck_stash_item_t));
+ if (new_item == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ new_item->item = item;
+ new_item->next = table->stash;
+ table->stash = new_item;
+
+ dbg_ck_hash("First item in stash (now inserted): key: %.*s (size %zu)"
+ ", value: %p\n", (int)table->stash->item->key_length,
+ table->stash->item->key, table->stash->item->key_length,
+ table->stash->item->value);
+
+ // increase count of items in stash
+ ++table->items_in_stash;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ck_new_table(ck_hash_table_item_t ***table, int exp)
+{
+ *table = (ck_hash_table_item_t **)
+ malloc(hashsize(exp) * sizeof(ck_hash_table_item_t *));
+ if (*table == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ // set to 0
+ memset(*table, 0, hashsize(exp) * sizeof(ck_hash_table_item_t *));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+ck_hash_table_t *ck_create_table(uint items)
+{
+ ck_hash_table_t *table =
+ (ck_hash_table_t *)malloc(sizeof(ck_hash_table_t));
+
+ if (table == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ memset(table, 0, sizeof(ck_hash_table_t));
+
+ // determine ideal size of one table in powers of 2 and save the
+ // exponent
+ table->table_size_exp = get_table_exp_and_count(items,
+ &table->table_count);
+ assert(table->table_size_exp <= 32);
+
+ if (table->table_size_exp == 0) {
+ dbg_ck("Failed to count exponent of the hash table.\n");
+ return NULL;
+ }
+
+ dbg_ck("Creating hash table for %u items.\n", items);
+ dbg_ck("Exponent: %u, number of tables: %u\n ",
+ table->table_size_exp, table->table_count);
+ dbg_ck("Table size: %u items, each %zu bytes, total %zu bytes\n",
+ hashsize(table->table_size_exp),
+ sizeof(ck_hash_table_item_t *),
+ hashsize(table->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+
+ // create tables
+ for (uint t = 0; t < table->table_count; ++t) {
+ dbg_ck("Creating table %u...\n", t);
+ if (ck_new_table(&table->tables[t], table->table_size_exp)
+ != 0) {
+ for (uint i = 0; i < t; ++i) {
+ free(table->tables[i]);
+ }
+ free(table);
+ return NULL;
+ }
+ }
+
+ assert(table->stash == NULL);
+ assert(table->hashed == NULL);
+ assert(table->items == 0);
+ assert(table->items_in_stash == 0);
+ assert(table->table_count == MAX_TABLES
+ || table->tables[table->table_count] == NULL);
+
+ // initialize rehash/insert mutex
+ pthread_mutex_init(&table->mtx_table, NULL);
+
+ // set the generation to 1 and initialize the universal system
+ CLEAR_FLAGS(&table->generation);
+ SET_GENERATION1(&table->generation);
+
+ us_initialize(&table->hash_system);
+
+ return table;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void ck_destroy_table(ck_hash_table_t **table, void (*dtor_value)(void *value),
+ int delete_key)
+{
+ assert(table);
+ assert(*table);
+ pthread_mutex_lock(&(*table)->mtx_table);
+
+ // destroy items in tables
+ for (uint i = 0; i < hashsize((*table)->table_size_exp); ++i) {
+ for (uint t = 0; t < (*table)->table_count; ++t) {
+ if ((*table)->tables[t][i] != NULL) {
+ if (dtor_value) {
+ dtor_value(
+ (*table)->tables[t][i]->value);
+ }
+ if (delete_key != 0) {
+ free(
+ (void *)(*table)->tables[t][i]->key);
+ }
+ free((void *)(*table)->tables[t][i]);
+ }
+ }
+ }
+
+ // destroy items in stash
+// ck_hash_table_item_t **stash =
+// ((ck_hash_table_item_t **)(da_get_items(&(*table)->stash)));
+// for (uint i = 0; i < da_get_count(&(*table)->stash); ++i) {
+// assert(stash[i] != NULL);
+// if (dtor_value) {
+// dtor_value(stash[i]->value);
+// }
+// if (delete_key != 0) {
+// free((void *)stash[i]->key);
+// }
+// free((void *)stash[i]);
+// }
+ ck_stash_item_t *item = (*table)->stash;
+ while (item != NULL) {
+ // disconnect the item
+ (*table)->stash = item->next;
+ /*! \todo Investigate this. */
+ assert(item->item != NULL);
+
+ if (dtor_value) {
+ dtor_value(item->item->value);
+ }
+ if (delete_key) {
+ free((void *)item->item->key);
+ }
+
+ free((void *)item->item);
+ free(item);
+ item = (*table)->stash;
+ }
+
+ // deallocate tables
+ for (uint t = 0; t < (*table)->table_count; ++t) {
+ free((*table)->tables[t]);
+ }
+ // destroy stash
+// da_destroy(&(*table)->stash);
+
+ pthread_mutex_unlock(&(*table)->mtx_table);
+ // destroy mutex, assuming that here noone will lock the mutex again
+ pthread_mutex_destroy(&(*table)->mtx_table);
+
+ free(*table);
+ (*table) = NULL;
+}
+
+void ck_table_free(ck_hash_table_t **table)
+{
+ if (table == NULL || *table == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&(*table)->mtx_table);
+
+ ck_stash_item_t *item = (*table)->stash;
+ while (item != NULL) {
+ // disconnect the item
+ (*table)->stash = item->next;
+ free(item);
+ item = (*table)->stash;
+ }
+
+ // deallocate tables
+ for (uint t = 0; t < (*table)->table_count; ++t) {
+ free((*table)->tables[t]);
+ }
+
+ pthread_mutex_unlock(&(*table)->mtx_table);
+ pthread_mutex_destroy(&(*table)->mtx_table);
+
+ free(*table);
+ (*table) = NULL;
+}
+
+int ck_resize_table(ck_hash_table_t *table)
+{
+ dbg_ck("Resizing hash table.\n");
+
+ /*
+ * Easiest is just to increment the exponent, resulting in doubling
+ * the table sizes. This is not very memory-effective, but should do
+ * the job.
+ */
+
+ if (table->table_size_exp == 31) {
+ dbg_ck("Hash tables achieved max size (exponent 31).\n");
+ return -1;
+ }
+
+ ck_hash_table_item_t **tables_new[MAX_TABLES];
+ ck_hash_table_item_t **tables_old[MAX_TABLES];
+ int exp_new = table->table_size_exp + 1;
+
+ dbg_ck("New tables exponent: %d\n", exp_new);
+
+ for (int t = 0; t < table->table_count; ++t) {
+ if (ck_new_table(&tables_new[t], exp_new) != 0) {
+ dbg_ck("Failed to create new table.\n");
+ for (int i = 0; i < t; ++i) {
+ free(tables_new[i]);
+ }
+ return -1;
+ }
+ }
+
+ dbg_ck("Created new tables, copying data to them.\n");
+
+ for (int t = 0; t < table->table_count; ++t) {
+ size_t old_size = hashsize(table->table_size_exp)
+ * sizeof(ck_hash_table_item_t *);
+
+ // copy the old table items
+ dbg_ck("Copying to: %p, from %p, size: %zu\n",
+ tables_new[t], table->tables[t], old_size);
+ memcpy(tables_new[t], table->tables[t], old_size);
+ // set the rest to 0
+ dbg_ck("Setting to 0 from %p, size %zu\n",
+ tables_new[t] + hashsize(table->table_size_exp),
+ (hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
+ - old_size);
+ memset(tables_new[t] + hashsize(table->table_size_exp), 0,
+ (hashsize(exp_new) * sizeof(ck_hash_table_item_t *))
+ - old_size);
+ }
+
+ dbg_ck("Done, switching the tables and running rehash.\n");
+
+
+ memcpy(tables_old, table->tables,
+ MAX_TABLES * sizeof(ck_hash_table_item_t **));
+ memcpy(table->tables, tables_new,
+ MAX_TABLES * sizeof(ck_hash_table_item_t **));
+
+ table->table_size_exp = exp_new;
+
+ // delete the old tables
+ for (int t = 0; t < table->table_count; ++t) {
+ free(tables_old[t]);
+ }
+
+ return ck_rehash(table);
+ //return 0;
+}
+
+int ck_insert_item(ck_hash_table_t *table, const char *key,
+ size_t length, void *value)
+{
+ // lock mutex to avoid write conflicts
+ pthread_mutex_lock(&table->mtx_table);
+
+ assert(value != NULL);
+
+ dbg_ck_hash("Inserting item with key: %.*s.\n", (int)length, key);
+ dbg_ck_hash_hex(key, length);
+ dbg_ck_hash("\n");
+
+ // create item structure and fill in the given data, key won't be copied
+ ck_hash_table_item_t *new_item =
+ (ck_hash_table_item_t *)malloc((sizeof(ck_hash_table_item_t)));
+ ck_fill_item(key, length, value, GET_GENERATION(table->generation),
+ new_item);
+
+ // check if the table is not full; if yes, resize and rehash!
+ if (ck_is_full(table)) {
+ dbg_ck("Table is full, resize needed.\n");
+ if (ck_resize_table(table) != 0) {
+ dbg_ck("Failed to resize hash table!\n");
+ return -1;
+ }
+ }
+
+ // there should be at least 2 free places
+ //assert(da_try_reserve(&table->stash, 2) == 0);
+ //da_reserve(&table->stash, 1);
+ ck_hash_table_item_t *free_place = NULL;
+ if (ck_hash_item(table, &new_item, &free_place,
+ table->generation) != 0) {
+
+ dbg_ck("Adding item with key %.*s to stash.\n",
+ (int)free_place->key_length, free_place->key);
+
+ // maybe some limit on the stash and rehash if full
+ if (ck_add_to_stash(table, free_place) != 0) {
+ dbg_ck_hash("Could not add item to stash!!\n");
+ assert(0);
+ }
+
+ if (ck_stash_is_full(table)) {
+ dbg_ck("Stash is full, resize needed.\n");
+ if (ck_resize_table(table) != 0) {
+ dbg_ck("Failed to resize hash table!\n");
+ return -1;
+ }
+ }
+ }
+
+ ++table->items;
+ pthread_mutex_unlock(&table->mtx_table);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const ck_hash_table_item_t *ck_find_item(const ck_hash_table_t *table,
+ const char *key, size_t length)
+{
+ dbg_ck("ck_find_item(), key: %.*s, size: %zu\n",
+ (int)length, key, length);
+
+ ck_hash_table_item_t **found = ck_find_item_nc(table, key, length);
+
+ return (found == NULL) ? NULL : rcu_dereference(*found);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_update_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void *new_value, void (*dtor_value)(void *value))
+{
+ rcu_read_lock(); // is needed?
+
+ assert(new_value != NULL);
+
+ ck_hash_table_item_t **item = ck_find_item_nc(table, key, length);
+
+ if (item == NULL || (*item) == NULL) {
+ return -1;
+ }
+
+ void *old = rcu_xchg_pointer(&(*item)->value, new_value);
+ rcu_read_unlock();
+
+ synchronize_rcu();
+ if (dtor_value) {
+ dtor_value(old);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_delete_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void (*dtor_value)(void *value), int delete_key)
+{
+ rcu_read_lock(); // is needed?
+ ck_hash_table_item_t **place = ck_find_item_nc(table, key, length);
+
+ if (place == NULL) {
+ return -1;
+ }
+
+ ck_hash_table_item_t *item = *place;
+
+ assert(item != NULL);
+
+ ck_put_item(place, NULL);
+ rcu_read_unlock();
+
+ synchronize_rcu();
+ if (dtor_value) {
+ dtor_value(item->value);
+ }
+ item->value = NULL;
+ if (delete_key != 0) {
+ free((void *)item->key);
+ }
+ free(item);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+ck_hash_table_item_t *ck_remove_item(ck_hash_table_t *table, const char *key,
+ size_t length)
+{
+ ck_hash_table_item_t **place = ck_find_item_nc(table, key, length);
+ if (place == NULL) {
+ return NULL;
+ }
+
+ ck_hash_table_item_t *item = *place;
+ *place = NULL;
+ return item;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return -1;
+ }
+
+ *to = (ck_hash_table_t *)malloc(sizeof(ck_hash_table_t));
+
+ if (*to == NULL) {
+ ERR_ALLOC_FAILED;
+ return -2;
+ }
+ memset(*to, 0, sizeof(ck_hash_table_t));
+
+ // copy table count and table size exponent
+ (*to)->table_size_exp = from->table_size_exp;
+ (*to)->table_count = from->table_count;
+ assert((*to)->table_size_exp <= 32);
+
+ dbg_ck("Creating hash table for %u items.\n", from->table_count);
+ dbg_ck("Exponent: %u, number of tables: %u\n ",
+ (*to)->table_size_exp, (*to)->table_count);
+ dbg_ck("Table size: %u items, each %zu bytes, total %zu bytes\n",
+ hashsize((*to)->table_size_exp),
+ sizeof(ck_hash_table_item_t *),
+ hashsize((*to)->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+
+ // create tables
+ for (uint t = 0; t < (*to)->table_count; ++t) {
+ dbg_ck("Creating table %u...\n", t);
+ (*to)->tables[t] = (ck_hash_table_item_t **)malloc(
+ hashsize((*to)->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+ if ((*to)->tables[t] == NULL) {
+ ERR_ALLOC_FAILED;
+ for (uint i = 0; i < t; ++i) {
+ free((*to)->tables[i]);
+ }
+ free(*to);
+ return -2;
+ }
+
+ // copy the table
+ memcpy((*to)->tables[t], from->tables[t],
+ hashsize((*to)->table_size_exp)
+ * sizeof(ck_hash_table_item_t *));
+ }
+
+ // copy the stash - we must explicitly copy each stash item, but do not
+ // copy the ck_hash_table_item_t within them.
+ ck_stash_item_t *si = from->stash;
+ ck_stash_item_t **pos = &(*to)->stash;
+ dbg_ck_verb(stderr, "Copying hash table stash.\n");
+ while (si != NULL) {
+ ck_stash_item_t *si_new = (ck_stash_item_t *)
+ malloc(sizeof(ck_stash_item_t));
+ if (si_new == NULL) {
+ ERR_ALLOC_FAILED;
+ // delete tables
+ for (uint i = 0; i < (*to)->table_count; ++i) {
+ free((*to)->tables[i]);
+ }
+ // delete created stash items
+ si_new = (*to)->stash;
+ while (si_new != NULL) {
+ ck_stash_item_t *prev = si_new;
+ si_new = si_new->next;
+ free(prev);
+ }
+ free(*to);
+ return -2;
+ }
+
+ dbg_ck("Copying stash item: %p with item %p, ", si, si->item);
+ dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
+
+ si_new->item = si->item;
+ *pos = si_new;
+ pos = &si_new->next;
+ si = si->next;
+
+
+ dbg_ck("Old stash item: %p with item %p, ", si,
+ ((si == NULL) ? NULL : si->item));
+ if (si != NULL) {
+ dbg_ck("key: %.*s\n", (int)si->item->key_length, si->item->key);
+ } else {
+ dbg_ck("\n");
+ }
+ dbg_ck("New stash item: %p with item %p, ", si_new,
+ si_new->item);
+ dbg_ck("key: %.*s\n", (int)si_new->item->key_length,
+ si_new->item->key);
+ }
+
+ *pos = NULL;
+
+ // there should be no item being hashed right now
+ /*! \todo This operation should not be done while inserting / rehashing.
+ */
+ assert(from->hashed == NULL);
+ (*to)->hashed = NULL;
+
+ // initialize rehash/insert mutex
+ pthread_mutex_init(&(*to)->mtx_table, NULL);
+
+ // copy the generation
+ (*to)->generation = from->generation;
+
+ // copy the hash functions
+ memcpy(&(*to)->hash_system, &from->hash_system, sizeof(us_system_t));
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_apply(ck_hash_table_t *table,
+ void (*function)(ck_hash_table_item_t *item, void *data),
+ void *data)
+{
+ if (table == NULL || function == NULL) {
+ return -1;
+ }
+
+ /*! \todo Ensure that no insertion nor rehash is made during applying.*/
+
+ // apply the function to all items in all tables
+ for (int t = 0; t < table->table_count; ++t) {
+ for (int i = 0; i < hashsize(table->table_size_exp); ++i) {
+ function(table->tables[t][i], data);
+ }
+ }
+
+ // apply the function to the stash items
+ ck_stash_item_t *si = table->stash;
+ while (si != NULL) {
+ function(si->item, data);
+ si = si->next;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ck_rehash(ck_hash_table_t *table)
+{
+ dbg_ck_hash("Rehashing items in table.\n");
+ SET_REHASHING_ON(&table->generation);
+
+ ck_stash_item_t *free_stash_items = NULL;
+
+ do {
+ // 1) Rehash items from stash
+ dbg_ck_rehash("Rehashing items from stash.\n");
+ ck_stash_item_t *item = table->stash;
+ ck_stash_item_t **item_place = &table->stash;
+ // terminate when at the end; this way the newly added items
+ // (added to the beginning) will be properly ignored
+ while (item != NULL) {
+ dbg_ck_rehash("Rehashing item with "
+ "key (length %zu): %.*s, generation: %hu, "
+ "table generation: %hu.\n", item->item->key_length,
+ (int)item->item->key_length, item->item->key,
+ GET_GENERATION(
+ item->item->timestamp),
+ GET_GENERATION(table->generation));
+
+ // put the hashed item to the prepared space
+ table->hashed = item->item;
+ item->item = NULL;
+ // we may use the place in the stash item as the free
+ // place for rehashing
+ if (ck_hash_item(table, &table->hashed, &item->item,
+ NEXT_GENERATION(table->generation)) != 0) {
+ // the free place was used
+ assert(item->item != NULL);
+ // we may leave the item there (in the stash)
+ assert(EQUAL_GENERATIONS(item->item->timestamp,
+ NEXT_GENERATION(table->generation)));
+ //assert(item->item == table->hashed);
+
+ item_place = &item->next;
+ item = item->next;
+ } else {
+ // the free place should be free
+ assert(item->item == NULL);
+ // and the item should be hashed too
+// assert(table->hashed == NULL);
+
+ // fix the pointer from the previous hash item
+ *item_place = item->next;
+ // and do not change the item place pointer
+
+ // put the stash item into list of free stash
+ // items
+ item->next = free_stash_items;
+ free_stash_items = item;
+
+ item = *item_place;
+ }
+ }
+
+ // 2) Rehash items from tables
+
+ // in case of failure, save the item in a temp variable
+ // which will be put to the stash
+ ck_hash_table_item_t *free = NULL;
+ assert(table->hashed == NULL);
+// ck_hash_table_item_t *old = table->hashed;
+
+ for (uint t = 0; t < table->table_count; ++t) {
+ uint rehashed = 0;
+
+ dbg_ck_rehash("Rehashing table %d.\n", t);
+
+ while (rehashed < hashsize(table->table_size_exp)) {
+
+ // if item's generation is the new generation,
+ // skip
+ if (table->tables[t][rehashed] == NULL
+ || !(EQUAL_GENERATIONS(
+ table->tables[t][rehashed]->timestamp,
+ table->generation))) {
+ dbg_ck_rehash("Skipping item.\n");
+ ++rehashed;
+ continue;
+ }
+
+ dbg_ck_rehash("Rehashing item with hash %u, "
+ "key (length %zu): %.*s, generation: %hu, "
+ "table generation: %hu.\n", rehashed,
+ table->tables[t][rehashed]->key_length,
+ (int)(table->tables[t][rehashed]->key_length),
+ table->tables[t][rehashed]->key,
+ GET_GENERATION(
+ table->tables[t][rehashed]->timestamp),
+ GET_GENERATION(table->generation));
+
+ // otherwise copy the item for rehashing
+ ck_put_item(&table->hashed, table->tables[t][rehashed]);
+ // clear the place so that this item will not
+ // get rehashed again
+ ck_clear_item(&table->tables[t][rehashed]);
+
+ dbg_ck_rehash("Table generation: %hu, next "
+ "generation: %hu.\n",
+ GET_GENERATION(table->generation),
+ NEXT_GENERATION(table->generation));
+
+ if (ck_hash_item(table, &table->hashed, &free,
+ NEXT_GENERATION(table->generation)) != 0) {
+ // loop occured
+ dbg_ck_hash("Hashing entered a loop."
+ "\n");
+ dbg_ck_rehash("Item with key %.*s "
+ "inserted into the free slot.\n",
+ free->key_length, free->key);
+
+ //assert(old == free);
+
+ // put the item into the stash, but
+ // try the free stash items first
+ if (free_stash_items != NULL) {
+ // take first
+ ck_stash_item_t *item =
+ free_stash_items;
+ free_stash_items = item->next;
+
+ item->item = free;
+ item->next = table->stash;
+ table->stash = item;
+ } else {
+ if (ck_add_to_stash(table, free)
+ != 0) {
+ ck_rollback_rehash(
+ table);
+ }
+ }
+
+ free = NULL;
+ table->hashed = NULL;
+ }
+ ++rehashed;
+ }
+ }
+
+ dbg_ck_rehash("Old table generation: %u\n",
+ GET_GENERATION(table->generation));
+ // rehashing completed, switch generation of the table
+ SET_NEXT_GENERATION(&table->generation);
+ dbg_ck_rehash("New table generation: %u\n",
+ GET_GENERATION(table->generation));
+ // generate new hash functions for the old generation
+ dbg_ck_rehash("Generating coeficients for generation: %u\n",
+ NEXT_GENERATION(table->generation));
+ us_next(&table->hash_system,
+ NEXT_GENERATION(table->generation));
+
+ } while (false /*! \todo Add proper condition!! */);
+
+ SET_REHASHING_OFF(&table->generation);
+
+ assert(table->hashed == NULL);
+
+
+ while (free_stash_items != NULL) {
+ ck_stash_item_t *item = free_stash_items;
+ free_stash_items = item->next;
+ assert(item->item == NULL);
+ free(item);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Rehashes the whole table.
+ *
+ * \param table Hash table to be rehashed.
+ *
+ * \note While rehashing no item should be inserted as it will result in a
+ * deadlock.
+ *
+ * \retval 0 No error.
+ * \retval -1 Rehashing failed. Some items may have been already moved and the
+ * rehashing flag remains set.
+ *
+ * \todo What if the stash is reallocated during ck_hash_item()? We'd be using
+ * the old stash for saving items! The old stash would not get deallocated
+ * (due to RCU - maybe put some rcu_read_lock() here), but the item
+ * would not be saved into the new stash!
+ * Maybe add a function for getting a pointer to particular item from
+ * the dynamic array and protect it using rcu_read_lock().
+ * Other option: Do not use pointer to an item in stash in the call to
+ * ck_hash_item(). Use some new place & put the item to the stash
+ * afterwards, protecting it using rcu_read_lock() and rcu_assign_pointer.
+ */
+//int ck_rehash(ck_hash_table_t *table)
+//{
+// dbg_ck_rehash("Rehashing items in table.\n");
+// SET_REHASHING_ON(&table->generation);
+
+// // we already have functions for the next generation, begin rehashing
+// // we wil use the last item in the buffer as free cell for hashing
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// ck_hash_table_item_t *old = (ck_hash_table_item_t *)
+// (malloc(sizeof(ck_hash_table_item_t)));
+
+// do {
+// dbg_ck_hash("Rehash!\n");
+
+// if (da_get_count(&table->stash) > STASH_SIZE) {
+// dbg_ck_hash("STASH RESIZED!!! (new stash size: %d)\n",
+// da_get_count(&table->stash));
+// }
+
+// // rehash items from stash, starting from the last old item
+// int stash_i = da_get_count(&table->stash) - 1;
+// while (stash_i >= 0) {
+// // if item's generation is the new generation, skip
+// if (STASH_ITEMS(&table->stash)[stash_i] == NULL
+// || !(EQUAL_GENERATIONS(STASH_ITEMS(&table->stash)
+// [stash_i]->timestamp,
+// table->generation))) {
+// dbg_ck_rehash("Skipping item.\n");
+// --stash_i;
+// continue;
+// }
+
+// dbg_ck_rehash("Rehashing item from buffer position %u"
+// ", key (length %u): %.*s, generation: "
+// "%hu, table generation: %hu.\n",
+// stash_i,
+// STASH_ITEMS(&table->stash)[stash_i]->key_length,
+// (int)STASH_ITEMS(&table->stash)[stash_i]->key_length,
+// STASH_ITEMS(&table->stash)[stash_i]->key,
+// GET_GENERATION(
+// STASH_ITEMS(&table->stash)[stash_i]->timestamp),
+// GET_GENERATION(table->generation));
+
+// // otherwise copy the item for rehashing
+// ck_put_item(&old, STASH_ITEMS(&table->stash)[stash_i]);
+// // clear the place so that this item will not get
+// // rehashed again
+// ck_clear_item(&STASH_ITEMS(&table->stash)[stash_i]);
+// da_release(&table->stash, 1);
+
+// // there should be at least one place in the stash
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// da_reserve(&table->stash, 1);
+
+// assert(STASH_ITEMS(&table->stash)[stash_i] == NULL);
+
+// // and start rehashing
+// if (ck_hash_item(table, &old,
+// &STASH_ITEMS(&table->stash)[stash_i],
+// NEXT_GENERATION(table->generation)) != 0) {
+// // loop occured
+// dbg_ck_hash("Hashing entered a loop.\n");
+
+// dbg_ck_rehash("Item with key %.*s inserted "
+// "into the stash on position %d.\n",
+// STASH_ITEMS(&table->stash)
+// [stash_i]->key_length,
+// STASH_ITEMS(&table->stash)
+// [stash_i]->key,
+// da_get_count(&table->stash));
+
+// // hashing unsuccessful, the item was inserted
+// // into the stash
+// da_occupy(&table->stash, 1);
+// assert(STASH_ITEMS(&table->stash)[stash_i]
+// != NULL);
+
+// // if only one place left, resize the stash
+// // TODO: Why???
+// if (da_reserve(&table->stash, 2) < 0) {
+// // stash could not be resized => !!!
+// dbg_ck_hash("Failed to rehash items "
+// "from "
+// "table, no other rehash possible!\n");
+// // so rollback
+// ck_rollback_rehash(table);
+// // clear the 'old' item
+// ck_clear_item(&old);
+// return -1;
+// }
+// }
+
+// // clear the 'old' item
+// ck_clear_item(&old);
+// // decrement the index
+// --stash_i;
+// }
+
+// uint i = 0;
+// while (i < da_get_count(&table->stash)) {
+// assert(STASH_ITEMS(&table->stash)[i] != NULL);
+// ++i;
+// }
+// dbg_ck_hash("OK\n");
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// assert(STASH_ITEMS(&table->stash)[da_get_count(&table->stash)]
+// == NULL);
+
+// // rehash items from hash tables
+// for (uint t = TABLE_FIRST;
+// t <= TABLE_LAST(table->table_count); ++t) {
+// dbg_ck_rehash("Rehashing items from table %d.\n",
+// t + 1);
+// uint rehashed = 0;
+
+// while (rehashed < hashsize(table->table_size_exp)) {
+
+// // if item's generation is the new generation,
+// // skip
+// if (table->tables[t][rehashed] == NULL
+// || !(EQUAL_GENERATIONS(
+// table->tables[t][rehashed]->timestamp,
+// table->generation))) {
+// dbg_ck_rehash("Skipping item.\n");
+// ++rehashed;
+// continue;
+// }
+
+// dbg_ck_rehash("Rehashing item with hash %u, "
+// "key (length %u): %.*s, generation: %hu, "
+// "table generation: %hu.\n", rehashed,
+// table->tables[t][rehashed]->key_length,
+// (int)(table->tables[t][rehashed]->key_length),
+// table->tables[t][rehashed]->key,
+// GET_GENERATION(
+// table->tables[t][rehashed]->timestamp),
+// GET_GENERATION(table->generation));
+
+// // otherwise copy the item for rehashing
+// ck_put_item(&old, table->tables[t][rehashed]);
+// // clear the place so that this item will not
+// // get rehashed again
+// ck_clear_item(&table->tables[t][rehashed]);
+
+// dbg_ck_rehash("Table generation: %hu, next "
+// "generation: %hu.\n",
+// GET_GENERATION(table->generation),
+// NEXT_GENERATION(table->generation));
+
+// // and start rehashing
+// assert(&old != &STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)]);
+// assert(da_try_reserve(&table->stash, 1) == 0);
+// da_reserve(&table->stash, 1);
+
+// if (ck_hash_item(table, &old,
+// &STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)],
+// NEXT_GENERATION(table->generation)) != 0) {
+// // loop occured
+// dbg_ck_hash("Hashing entered a loop."
+// "\n");
+// dbg_ck_rehash("Item with key %.*s "
+// "inserted into the stash on position "
+// "%d.\n", STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)]
+// ->key_length,
+// STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)]->key,
+// da_get_count(&table->stash));
+
+// assert(STASH_ITEMS(&table->stash)[
+// da_get_count(&table->stash)] != NULL);
+// // loop occured, the item is already at
+// // its new place in the buffer, so just
+// // increment the index
+// da_occupy(&table->stash, 1);
+
+// // if only one place left, resize the
+// // stash TODO: Why?
+// if (da_reserve(&table->stash, 2) < 0) {
+// // stash could not be resized
+// dbg_ck_hash("Failed to rehash"
+// " items from table, no other "
+// "rehash possible!\n");
+// // so rollback
+// ck_rollback_rehash(table);
+// // clear the 'old' item
+// ck_clear_item(&old);
+// return -1;
+// }
+// }
+// ++rehashed;
+// }
+// }
+
+// dbg_ck_rehash("Old table generation: %u\n",
+// GET_GENERATION(table->generation));
+// // rehashing completed, switch generation of the table
+// SET_NEXT_GENERATION(&table->generation);
+// dbg_ck_rehash("New table generation: %u\n",
+// GET_GENERATION(table->generation));
+// // generate new hash functions for the old generation
+// dbg_ck_rehash("Generating coeficients for generation: %u\n",
+// NEXT_GENERATION(table->generation));
+// us_next(NEXT_GENERATION(table->generation));
+
+// // repeat rehashing while there are more items in the stash than
+// // its initial size
+// if (da_get_count(&table->stash) > STASH_SIZE) {
+// dbg_ck_rehash("Rehashing again!\n");
+// }
+// } while (da_get_count(&table->stash) > STASH_SIZE);
+
+// SET_REHASHING_OFF(&table->generation);
+
+// return 0;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+void ck_dump_table(const ck_hash_table_t *table)
+{
+#ifdef CUCKOO_DEBUG
+ uint i = 0;
+ dbg_ck("----------------------------------------------\n");
+ dbg_ck("Hash table dump:\n\n");
+ dbg_ck("Size of each table: %u\n\n", hashsize(table->table_size_exp));
+
+ for (uint t = 0; t < table->table_count; ++t) {
+ dbg_ck("Table %d:\n", t + 1);
+
+ for (i = 0; i < hashsize(table->table_size_exp); i++) {
+ dbg_ck("Hash: %u, Key: %.*s, Value: %p.\n", i,
+ (int)(table->tables[t])[i]->key_length,
+ (table->tables[t])[i]->key,
+ (table->tables[t])[i]->value);
+ }
+ }
+
+ dbg_ck("Stash:\n");
+// for (i = 0; i < da_get_count(&table->stash); ++i) {
+// dbg_ck("Index: %u, Key: %.*s Value: %p.\n", i,
+// ((ck_hash_table_item_t **)
+// da_get_items(&table->stash))[i]->key_length,
+// ((ck_hash_table_item_t **)
+// da_get_items(&table->stash))[i]->key,
+// ((ck_hash_table_item_t **)
+// da_get_items(&table->stash))[i]->value);
+// }
+ ck_stash_item_t *item = table->stash;
+ while (item != NULL) {
+ dbg_ck("Hash: %u, Key: %.*s, Value: %p.\n", i,
+ (int)item->item->key_length, item->item->key,
+ item->item->value);
+ item = item->next;
+ }
+
+ dbg_ck("\n");
+#endif
+}
diff --git a/src/libknot/hash/cuckoo-hash-table.h b/src/libknot/hash/cuckoo-hash-table.h
new file mode 100644
index 0000000..dd78294
--- /dev/null
+++ b/src/libknot/hash/cuckoo-hash-table.h
@@ -0,0 +1,333 @@
+/*!
+ * \file cuckoo-hash-table.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Implementation of Cuckoo hashing scheme.
+ *
+ * Uses d-ary Cuckoo hashing with stash.
+ *
+ * \todo Maybe provide some way to resize the whole table if the number of items
+ * grows too much.
+ * \todo Check size of integers, the table size may be larger than unsigned int.
+ * \todo Maybe do not return ck_hash_table_item from ck_find_item(), but only
+ * its value.
+ * \todo When hashing an item, only the first table is tried for this item.
+ * We may try all tables. (But it is not neccessary.)
+ *
+ * \addtogroup hashing
+ * @{
+ */
+/* 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_CUCKOO_HASH_TABLE_H_
+#define _KNOT_CUCKOO_HASH_TABLE_H_
+
+#include <stdint.h> /* uint32_t */
+#include <stdlib.h> /* size_t */
+#include <pthread.h>
+
+#include "hash/universal-system.h"
+#include "common/dynamic-array.h"
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Macro for getting one hash table size. */
+#define hashsize(n) ((uint32_t)1 << (n))
+
+/*!
+ * \brief Max number of hash tables - must be the same as number of the hash
+ * functions in each generation of the universal system.
+ */
+#define MAX_TABLES US_FNC_COUNT
+
+/*! \brief Default stash size. */
+static const uint STASH_SIZE = 10;
+
+/*! \brief Maximum stash size. When achieved, rehashing is needed. */
+static const uint STASH_SIZE_MAX = 30;
+
+/*----------------------------------------------------------------------------*/
+/* Public structures */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for storing the hashed data.
+ */
+struct ck_hash_table_item {
+ const char *key; /*!< Key of the item, used for hashing. */
+
+ size_t key_length; /*!< Length of the key in octets. */
+
+ void *value; /*!< The actual item stored in the table. */
+
+ /*!
+ * \brief Flags. Currently used for keeping the generation of the item,
+ * i.e. the generation of the functions used for hashing this
+ * item.
+ *
+ * Form: 000000xy;
+ * xy - generation; may be 01 (1) or 10 (2).
+ */
+ uint8_t timestamp;
+};
+
+typedef struct ck_hash_table_item ck_hash_table_item_t;
+
+struct ck_stash_item {
+ ck_hash_table_item_t *item;
+ struct ck_stash_item *next;
+};
+
+typedef struct ck_stash_item ck_stash_item_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Hash table structure which uses cuckoo hashing.
+ *
+ * Keys are expected to be strings of characters (char *), not necesarily
+ * null-terminated. It uses the Fowler/Noll/Vo (FNV) hash function to
+ * obtain a 32bit unsigned integer from the character data and a function
+ * randomly chosen from an universal system (see universal-system.h) to obtain
+ * the final hash. The FNV hash was taken from
+ * http://home.comcast.net/~bretm/hash/6.html and the universal system is
+ * constructed according to Katajainen J., Lykke M., Experiments with universal
+ * hashing (obtained from
+ * http://www.diku.dk/OLD/publikationer/tekniske.rapporter/rapporter/96-08.pdf).
+ *
+ * The table uses either 3-ary or 4-ary cuckoo hashing (and thus 3 or 4 tables)
+ * with stash, according to the number of items provided to ck_create_table()
+ * function. The number of table pointers is however set to be the larger value
+ * (4) always, so the \a tables array may be statically allocated. Size of one
+ * table is always a power of 2 (due to the character of the hash function).
+ * The stash has a default size STASH_SIZE, but can be resized if needed.
+ * However, the resizing is only done in rehashing process, if the items do not
+ * fit into the table and the original stash.
+ *
+ * Rehashing is done when the stash gets full (actually, last item is always
+ * free and is used in the rehashing process as a temporary variable).
+ */
+struct ck_hash_table {
+ uint table_count; /*!< Actual number of hash tables used. */
+
+ /*!
+ * \brief Exponent of one table's size (2^table_size_exp is table size).
+ */
+ int table_size_exp;
+
+ ck_hash_table_item_t **tables[MAX_TABLES]; /*!< Array of hash tables. */
+
+ //da_array_t stash; /*!< Stash implemented as a dynamic array. */
+ ck_stash_item_t *stash;
+
+ /*! \brief Temporary storage for item being hashed. */
+ ck_hash_table_item_t *hashed;
+
+ /*! \brief Mutex for avoiding multiple insertions / rehashes at once. */
+ pthread_mutex_t mtx_table;
+
+ /*!
+ * \brief Flags used for determining which hash functions are currently
+ * used
+ *
+ * Form: 00000xyz.
+ * x - rehash flag (1 if rehashing is in progress)
+ * yz - generation (may be 10 = 2, or 01 = 1)
+ *
+ * There are always two sets of hash functions available via the
+ * us_hash() function (see universal-hashing.h). Normally all items in
+ * the table are hashed using one set of functions. However, during
+ * rehash, the other set is used for rehashing. In this case the rehash
+ * flag (x) is set, so the lookup function (ck_find_item()) tries to use
+ * both sets of functions when searching for item.
+ */
+ uint8_t generation;
+
+ us_system_t hash_system; /*!< Universal system of hash functions. */
+
+ size_t items;
+ size_t items_in_stash;
+};
+
+typedef struct ck_hash_table ck_hash_table_t;
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates and initializes the hash table structure.
+ *
+ * All hash tables are allocated and their items initialized to 0 (NULL).
+ * A stash of default size is also created. The \a generation flags are set to
+ * 0.
+ *
+ * \param items Number of items to be hashed to the table. This number
+ * determines the size of the hash table that will be created.
+ *
+ *
+ * \return Pointer to the initialized hash table.
+ */
+ck_hash_table_t *ck_create_table(uint items);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Destroys the whole hash table together with the saved values.
+ *
+ * \param table Pointer to pointer to the hash table.
+ * \param dtor_value Destructor function for the values that are be stored in
+ * the hash table. Set to NULL if you do not want the values
+ * to be deleted.
+ * \param delete_key Set to 0 if you do not want the function to delete the
+ * key of the item (e.g. when used elsewhere). Set to any
+ * other value otherwise.
+ *
+ * \note Make sure the table and its items are not used anymore when calling
+ * this function.
+ */
+void ck_destroy_table(ck_hash_table_t **table,
+ void (*dtor_value)(void *value), int delete_key);
+
+/*!
+ * \brief Destroys the table structures, but does not remove the individual
+ * hash table items.
+ */
+void ck_table_free(ck_hash_table_t **table);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Inserts item into the hash table.
+ *
+ * Insertion starts always by trying to hash the item into the first table. The
+ * possible displaced item is then hashed into randomly chosen other table,
+ * etc., until a free place is found or a loop occured. A loop occurs when one
+ * position in one table is tried more than twice.
+ *
+ * \param table Hash table the item should be inserted into.
+ * \param key Item's key. It can be any string of octets. The key is not copied
+ * by the function.
+ * \param length Length of the key in bytes (octets).
+ * \param value Pointer to the actual item to be inserted into the hash table.
+ *
+ * \note This function does not copy the key.
+ * \note This function may trigger rehash of the whole table in case the stash
+ * gets full.
+ *
+ * \retval 0 No error.
+ * \retval -1 Insertion failed. This may occur only when the rehashing fails.
+ * In this case it is necessary to somehow manually force another
+ * rehash as no other rehash would be possible.
+ */
+int ck_insert_item(ck_hash_table_t *table, const char *key, size_t length,
+ void *value);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds item in table.
+ *
+ * \param table Hash table to search in.
+ * \param key Key of the item. It can be an arbitrary string of octets.
+ * \param length Length of the key in bytes (octets).
+ *
+ * \return Pointer to the item if found. NULL otherwise.
+ */
+const ck_hash_table_item_t *ck_find_item(const ck_hash_table_t *table,
+ const char *key, size_t length);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Updates item with the given key by replacing its value.
+ *
+ * The update process is synchronized using RCU mechanism, so the old item's
+ * value will not be deleted while some thread is using it.
+ *
+ * \param table Hash table where to search for the item.
+ * \param key Key of the item to be updated. It can be an arbitrary string of
+ * octets.
+ * \param length Length of the key in bytes (octets).
+ * \param new_value New value for the item with key \a key.
+ * \param dtor_value Destructor function for the values that are be stored in
+ * the hash table. Set to NULL if you do not want the values
+ * to be deleted.
+ *
+ * \retval 0 If successful.
+ * \retval -1 If the item was not found in the table. No changes are made.
+ */
+int ck_update_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void *new_value, void (*dtor_value)(void *value));
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Removes item with the given key from table.
+ *
+ * The deletion process is synchronized using RCU mechanism, so the old item
+ * will not be deleted while some thread is using it.
+ *
+ * \param table Hash table where to search for the item.
+ * \param key Key of the item to be removed. It can be an arbitrary string of
+ * octets.
+ * \param length Length of the key in bytes (octets).
+ * \param dtor_value Destructor function for the values that are be stored in
+ * the hash table. Set to NULL if you do not want the values
+ * to be deleted.
+ * \param delete_key Set to 0 if you do not want the function to delete the
+ * key of the item (e.g. when used elsewhere). Set to any
+ * other value otherwise.
+ *
+ * \retval 0 If successful.
+ * \retval -1 If the item was not found in the table.
+ */
+int ck_delete_item(const ck_hash_table_t *table, const char *key, size_t length,
+ void (*dtor_value)(void *value), int delete_key);
+
+ck_hash_table_item_t *ck_remove_item(ck_hash_table_t *table, const char *key,
+ size_t length);
+
+/*!
+ * \brief Creates a shallow copy of the cuckoo hash table.
+ *
+ * This function creates just the ck_hash_table_t structure and its tables and
+ * stash. It does not copy individual ck_hash_table_item_t structures.
+ *
+ * \param from Table to copy.
+ * \param to The new copy will be stored here.
+ *
+ * \retval 0 if successful.
+ * \retval
+ */
+int ck_shallow_copy(const ck_hash_table_t *from, ck_hash_table_t **to);
+
+int ck_apply(ck_hash_table_t *table,
+ void (*function)(ck_hash_table_item_t *item, void *data),
+ void *data);
+
+/*----------------------------------------------------------------------------*/
+
+int ck_rehash(ck_hash_table_t *table);
+
+// for testing purposes only
+int ck_resize_table(ck_hash_table_t *table);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Dumps the whole hash table to the standard output.
+ */
+void ck_dump_table(const ck_hash_table_t *table);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_CUCKOO_HASH_TABLE_H_ */
+
+/*! @} */
diff --git a/src/libknot/hash/hash-functions.c b/src/libknot/hash/hash-functions.c
new file mode 100644
index 0000000..a33dd6b
--- /dev/null
+++ b/src/libknot/hash/hash-functions.c
@@ -0,0 +1,241 @@
+/* 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 "hash-functions.h"
+
+/*--------------------------------- FNV HASH ---------------------------------*/
+
+unsigned long int fnv_hash(const char *data, int size, int bits)
+{
+ int shift, i;
+ unsigned long int mask;
+ unsigned long int hash = 2166136261;
+
+ if (bits == -1) {
+ shift = 0;
+ mask = 0xFFFFFFFF;
+ } else {
+ shift = 32 - bits;
+ mask = (1U << shift) - 1U;
+ }
+
+ for (i = 0; i < size; i++) {
+ hash = (hash * 16777619) ^ data[i];
+ }
+
+ if (shift == 0) {
+ return hash;
+ }
+
+ return (hash ^(hash >> shift)) & mask;
+}
+
+/*------------------------------- JENKINS HASH -------------------------------*/
+
+/* The mixing step */
+/*
+#define mix(a,b,c) \
+ { \
+ a=a-b; a=a-c; a=a^(c>>13); \
+ b=b-c; b=b-a; b=b^(a<<8); \
+ c=c-a; c=c-b; c=c^(b>>13); \
+ a=a-b; a=a-c; a=a^(c>>12); \
+ b=b-c; b=b-a; b=b^(a<<16); \
+ c=c-a; c=c-b; c=c^(b>>5); \
+ a=a-b; a=a-c; a=a^(c>>3); \
+ b=b-c; b=b-a; b=b^(a<<10); \
+ c=c-a; c=c-b; c=c^(b>>15); \
+ }
+*/
+
+///* The whole new hash function */
+//u4 jhash(register u1 *k, u4 length, u4 initval)
+//{
+// register u4 a, b, c; /* the internal state */
+// u4 len; /* how many key bytes still need mixing */
+
+// /* Set up the internal state */
+// len = length;
+// a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+// c = initval; /* variable initialization of internal state */
+
+// /*---------------------------------------- handle most of the key */
+// while (len >= 12) {
+// a = a + (k[0] + ((u4)k[1] << 8)
+// + ((u4)k[2] << 16) + ((u4)k[3] << 24));
+// b = b + (k[4] + ((u4)k[5] << 8)
+// + ((u4)k[6] << 16) + ((u4)k[7] << 24));
+// c = c + (k[8] + ((u4)k[9] << 8)
+// + ((u4)k[10] << 16) + ((u4)k[11] << 24));
+// mix(a, b, c);
+// k = k + 12;
+// len = len - 12;
+// }
+
+// /*------------------------------------- handle the last 11 bytes */
+// c = c + length;
+// switch (len) { /* all the case statements fall through */
+// case 11:
+// c = c + ((u4)k[10] << 24);
+// case 10:
+// c = c + ((u4)k[9] << 16);
+// case 9 :
+// c = c + ((u4)k[8] << 8);
+// /* the first byte of c is reserved for the length */
+// case 8 :
+// b = b + ((u4)k[7] << 24);
+// case 7 :
+// b = b + ((u4)k[6] << 16);
+// case 6 :
+// b = b + ((u4)k[5] << 8);
+// case 5 :
+// b = b + k[4];
+// case 4 :
+// a = a + ((u4)k[3] << 24);
+// case 3 :
+// a = a + ((u4)k[2] << 16);
+// case 2 :
+// a = a + ((u4)k[1] << 8);
+// case 1 :
+// a = a + k[0];
+// /* case 0: nothing left to add */
+// }
+// mix(a, b, c);
+// /*-------------------------------------------- report the result */
+// return c;
+//}
+
+
+
+#define hashsize(n) ((ub4)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bits set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/*
+--------------------------------------------------------------------
+hash() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Every 1-bit and 2-bit delta achieves avalanche.
+About 6*len+35 instructions.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+ub4 jhash(k, length, initval)
+register ub1 *k; /* the key */
+register ub4 length; /* the length of the key */
+register ub4 initval; /* the previous hash, or an arbitrary value */
+{
+ register ub4 a,b,c,len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = initval; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch(len) /* all the case statements fall through */
+ {
+ case 11: c+=((ub4)k[10]<<24);
+ case 10: c+=((ub4)k[9]<<16);
+ case 9 : c+=((ub4)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((ub4)k[7]<<24);
+ case 7 : b+=((ub4)k[6]<<16);
+ case 6 : b+=((ub4)k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=((ub4)k[3]<<24);
+ case 3 : a+=((ub4)k[2]<<16);
+ case 2 : a+=((ub4)k[1]<<8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ mix(a,b,c);
+ /*-------------------------------------------- report the result */
+ return c;
+}
+
+#undef hashsize
+#undef hashmask
+
diff --git a/src/libknot/hash/hash-functions.h b/src/libknot/hash/hash-functions.h
new file mode 100644
index 0000000..f23730b
--- /dev/null
+++ b/src/libknot/hash/hash-functions.h
@@ -0,0 +1,85 @@
+/*!
+ * \file hash-functions.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Various hash functions.
+ *
+ * All of the hash functions are downloaded from various sources.
+ *
+ * \todo Add references to sources.
+ *
+ * \addtogroup hashing
+ * @{
+ */
+/* 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_HASH_FUNCTIONS_H_
+#define _KNOT_HASH_FUNCTIONS_H_
+
+#include <stdint.h>
+#include <string.h>
+
+/*
+ * Fowler / Noll / Vo Hash (FNV Hash)
+ * http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * This is an implementation of the algorithms posted above.
+ * This file is placed in the public domain by Peter Wemm.
+ *
+ * $FreeBSD: src/sys/sys/fnv_hash.h,v 1.2.2.1 2001/03/21 10:50:59 peter Exp $
+ */
+
+typedef uint32_t Fnv32_t;
+
+#define FNV1_32_INIT ((Fnv32_t) 33554467UL)
+
+#define FNV_32_PRIME ((Fnv32_t) 0x01000193UL)
+
+static __inline Fnv32_t
+fnv_32_buf(const void *buf, size_t len, Fnv32_t hval)
+{
+ const uint8_t *s = (const uint8_t *)buf;
+
+ while (len-- != 0) {
+ hval *= FNV_32_PRIME;
+ hval ^= *s++;
+ }
+ return hval;
+}
+
+/*!
+ * \brief Jenkins hash function.
+ *
+ * Downloaded from http://burtleburtle.net/bob/hash/evahash.html
+ *
+ * \param k Data to hash
+ * \param length Size of the data in bytes.
+ * \param initval The previous hash or an arbitrary value.
+ *
+ * \return Hash of the data.
+ *
+ * \todo Add source.
+ */
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+typedef unsigned char ub1; /* unsigned 1-byte quantities */
+
+ub4 jhash(register ub1 *k, register ub4 length, register ub4 initval);
+
+#endif /* _KNOT_HASH_FUNCTIONS_H_ */
+
+/*! @} */
diff --git a/src/libknot/hash/universal-system.c b/src/libknot/hash/universal-system.c
new file mode 100644
index 0000000..096974c
--- /dev/null
+++ b/src/libknot/hash/universal-system.c
@@ -0,0 +1,116 @@
+/* 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 <limits.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "universal-system.h"
+#include "common.h"
+#include "util/utils.h"
+
+/*----------------------------------------------------------------------------*/
+
+const uint MAX_UINT_EXP = 32;
+const unsigned long MAX_UINT_MY = UINT32_MAX; /* 4294967295 */
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Generates new set of coeficients.
+ *
+ * \param system Universal system to generate the coeficients for.
+ * \param from First coeficient to be replaced.
+ * \param to Up to this the coeficients will be replaced.
+ */
+static void us_generate_coefs(us_system_t *system, uint from, uint to)
+{
+ assert(system != NULL);
+
+ for (uint i = from; i < to; ++i) {
+ int used = 0;
+
+ do {
+ // generate random odd number
+ system->coefs[i] = knot_quick_rand() % MAX_UINT_MY;
+ if (system->coefs[i] % 2 == 0) {
+ system->coefs[i] = (system->coefs[i] == 0)
+ ? 1
+ : system->coefs[i] - 1;
+ }
+ // check if this coeficient is already used
+ uint j = from;
+ while (used == 0 && j < i) {
+ if (system->coefs[j++] == system->coefs[i]) {
+ used = 1;
+ }
+ }
+ // if already used, generate again
+ } while (used != 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+void us_initialize(us_system_t *system)
+{
+ assert(system != NULL);
+ assert(UINT_MAX == MAX_UINT_MY);
+
+ // Initialize both generations of functions by generating random odd
+ // numbers
+ us_generate_coefs(system, 0, US_FNC_COUNT * GEN_COUNT);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \note \a generation starts from 1
+ */
+int us_next(us_system_t *system, uint generation)
+{
+ assert(system != NULL);
+ // generate new coeficients for the new generation
+ us_generate_coefs(system, (generation - 1) * US_FNC_COUNT,
+ generation * US_FNC_COUNT);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint32_t us_hash(const us_system_t *system, uint32_t value, uint table_exp,
+ uint fnc, uint generation)
+{
+ /*
+ * multiplication should overflow if larger than MAX_UINT
+ * this is the same as (coef * value) mod MAX_UINT
+ *
+ * TODO: maybe we should not rely on this
+ */
+ assert(system != NULL);
+ assert(table_exp <= 32);
+ assert(fnc < US_FNC_COUNT);
+ assert(generation <= GEN_COUNT);
+
+ return ((system->coefs[((generation - 1) * US_FNC_COUNT) + fnc] * value)
+ >> (MAX_UINT_EXP - table_exp));
+}
diff --git a/src/libknot/hash/universal-system.h b/src/libknot/hash/universal-system.h
new file mode 100644
index 0000000..25330de
--- /dev/null
+++ b/src/libknot/hash/universal-system.h
@@ -0,0 +1,109 @@
+/*!
+ * \file universal-system.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * This file provides interface to a 2-universal system of hash functions that
+ * hash from 32-bit unsigned integer to a 32-bit unsigned integer within a given
+ * range. The range is always a power of two and is given by the exponent (see
+ * function us_hash().
+ *
+ * Before using the system, it must be initialized by calling us_initialize().
+ * The system stores 2 sets (generations), each of US_FNC_COUNT functions.
+ * For generating a new set of coeficients (i.e. hash functions) use the
+ * us_next() function.
+ *
+ * For hashing use the us_hash() function.
+ *
+ * \todo What if all numbers are tried and still need rehash?
+ * (that means 2mld rehashes - we can probably live with that ;)
+ * \todo Consider counting generations from 0, will be easier!
+ * \todo Check out some better random number generator.
+ *
+ * \addtogroup hashing
+ * @{
+ */
+/* 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_UNIVERSAL_SYSTEM_H_
+#define _KNOT_UNIVERSAL_SYSTEM_H_
+
+#include <stdint.h>
+#include "common.h"
+
+
+enum { US_FNC_COUNT = 4 /*!< Number of functions for one generation. */ };
+
+enum { GEN_COUNT = 2 /*!< Number of generations. */ };
+
+/*----------------------------------------------------------------------------*/
+/*! \brief Analytically defined universal system of hashing functions. */
+struct us_system {
+ /*! \brief Coeficients for the functions */
+ uint coefs[US_FNC_COUNT * GEN_COUNT];
+};
+
+typedef struct us_system us_system_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Initializes the universal system by generating coeficients for all
+ * hash functions and all generations.
+ *
+ * \param system Universal system to be used.
+ */
+void us_initialize(us_system_t *system);
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Generates new hash functions' coeficients for the given \a generation.
+ *
+ * \param system Universal system to be used.
+ * \param generation Generation for which to generate the new coeficients.
+ *
+ * \return 0
+ */
+int us_next(us_system_t *system, uint generation);
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Hashes the \a value using the given \a exponent and function.
+ *
+ * The actual formula of the hash is:
+ * h = ((coef * value) mod 2^32) / 2^(32 - table_exp)
+ * where \a coef is the proper coeficient.
+ *
+ * \param system Universal system to be used.
+ * \param value Value to be hashed.
+ * \param table_exp Determines the upper bound for the result - the hash will
+ * be between 0 and 2^(32 - table_exp).
+ * \param fnc Which function from the set should be used.
+ * \param generation Which set (generation) of functions should be used.
+ *
+ * \todo Make inline?
+ *
+ * \return Hash value (32bit unsigned).
+ */
+uint32_t us_hash(const us_system_t *system, uint32_t value, uint table_exp,
+ uint fnc, uint generation);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_UNIVERSAL_SYSTEM_H_ */
+
+/*! @} */
diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h
new file mode 100644
index 0000000..a401be7
--- /dev/null
+++ b/src/libknot/libknot.h
@@ -0,0 +1,48 @@
+/*!
+ * \file libknot.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief Convenience header for including whole library.
+ *
+ * \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_LIBKNOT_H_
+#define _KNOT_LIBKNOT_H_
+
+#include "consts.h"
+#include "util/descriptor.h"
+#include "dname.h"
+#include "edns.h"
+#include "zone/node.h"
+#include "nsec3.h"
+#include "util/wire.h"
+#include "rdata.h"
+#include "packet/response.h"
+#include "rrset.h"
+#include "util/tolower.h"
+#include "util/utils.h"
+#include "zone/zone.h"
+#include "zone/zonedb.h"
+#include "util/error.h"
+
+#endif
+
+/*! @} */
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
new file mode 100644
index 0000000..f88f802
--- /dev/null
+++ b/src/libknot/nameserver/name-server.c
@@ -0,0 +1,3663 @@
+/* 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 <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include <urcu.h>
+
+#include "nameserver/name-server.h"
+#include "updates/xfr-in.h"
+
+#include "util/error.h"
+#include "libknot.h"
+#include "util/debug.h"
+#include "packet/packet.h"
+#include "packet/response.h"
+#include "packet/query.h"
+#include "consts.h"
+#include "updates/changesets.h"
+#include "updates/ddns.h"
+#include "tsig-op.h"
+
+/*----------------------------------------------------------------------------*/
+
+/*! \brief Maximum UDP payload with EDNS enabled. */
+static const uint16_t MAX_UDP_PAYLOAD_EDNS = 4096;
+/*! \brief Maximum UDP payload with EDNS disabled. */
+static const uint16_t MAX_UDP_PAYLOAD = 504; // 512 - 8B header
+/*! \brief Maximum size of one AXFR response packet. */
+static const uint16_t MAX_AXFR_PAYLOAD = 65535;
+/*! \brief Supported EDNS version. */
+static const uint8_t EDNS_VERSION = 0;
+/*! \brief Determines whether EDNS is enabled. */
+static const int EDNS_ENABLED = 1;
+
+/*! \brief TTL of a CNAME synthetized from a DNAME. */
+static const uint32_t SYNTH_CNAME_TTL = 0;
+
+/*! \brief Determines whether DNSSEC is enabled. */
+static const int DNSSEC_ENABLED = 1;
+
+/*! \brief Determines whether NSID is enabled. */
+static const int NSID_ENABLED = 1;
+
+/*! \brief Length of NSID option data. */
+static const uint16_t NSID_LENGTH = 6;
+/*! \brief NSID option data. */
+static const uint8_t NSID_DATA[6] = {0x46, 0x6f, 0x6f, 0x42, 0x61, 0x72};
+
+/*! \brief Internal error code to propagate need for SERVFAIL response. */
+static const int NS_ERR_SERVFAIL = -999;
+
+/*----------------------------------------------------------------------------*/
+/* Private functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds zone where to search for the QNAME.
+ *
+ * \note As QTYPE DS requires special handling, this function finds a zone for
+ * a direct predecessor of QNAME in such case.
+ *
+ * \param zdb Zone database where to search for the proper zone.
+ * \param qname QNAME.
+ * \param qtype QTYPE.
+ *
+ * \return Zone to which QNAME belongs (according to QTYPE), or NULL if no such
+ * zone was found.
+ */
+static const knot_zone_t *ns_get_zone_for_qname(knot_zonedb_t *zdb,
+ const knot_dname_t *qname,
+ uint16_t qtype)
+{
+ const knot_zone_t *zone;
+ /*
+ * Find a zone in which to search.
+ *
+ * In case of DS query, we strip the leftmost label when searching for
+ * the zone (but use whole qname in search for the record), as the DS
+ * records are only present in a parent zone.
+ */
+ if (qtype == KNOT_RRTYPE_DS) {
+ /*! \todo Optimize, do not deep copy dname. */
+ knot_dname_t *name = knot_dname_left_chop(qname);
+ zone = knot_zonedb_find_zone_for_name(zdb, name);
+ /* Directly discard. */
+ knot_dname_free(&name);
+ } else {
+ zone = knot_zonedb_find_zone_for_name(zdb, qname);
+ }
+
+ return zone;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Synthetizes RRSet from a wildcard RRSet using the given QNAME.
+ *
+ * The synthetized RRSet is identical to the wildcard RRSets, except that the
+ * owner name is replaced by \a qname.
+ *
+ * \param wildcard_rrset Wildcard RRSet to synthetize from.
+ * \param qname Domain name to be used as the owner of the synthetized RRset.
+ *
+ * \return The synthetized RRSet (this is a newly created RRSet, remember to
+ * free it).
+ */
+static knot_rrset_t *ns_synth_from_wildcard(
+ const knot_rrset_t *wildcard_rrset, const knot_dname_t *qname)
+{
+ dbg_ns("Synthetizing RRSet from wildcard...\n");
+
+ knot_dname_t *owner = knot_dname_deep_copy(qname);
+// printf("Copied owner ptr: %p\n", owner);
+
+ knot_rrset_t *synth_rrset = knot_rrset_new(
+ owner, knot_rrset_type(wildcard_rrset),
+ knot_rrset_class(wildcard_rrset),
+ knot_rrset_ttl(wildcard_rrset));
+
+ /* Release owner, as it's retained in rrset. */
+ knot_dname_release(owner);
+
+ if (synth_rrset == NULL) {
+ return NULL;
+ }
+
+ dbg_ns("Created RRSet header:\n");
+ knot_rrset_dump(synth_rrset, 1);
+
+ // copy all RDATA
+ const knot_rdata_t *rdata = knot_rrset_rdata(wildcard_rrset);
+ while (rdata != NULL) {
+ // we could use the RDATA from the wildcard rrset
+ // but there is no way to distinguish it when deleting
+ // temporary RRSets
+ knot_rdata_t *rdata_copy = knot_rdata_deep_copy(rdata,
+ knot_rrset_type(synth_rrset));
+ if (rdata_copy == NULL) {
+ knot_rrset_deep_free(&synth_rrset, 1, 1, 0);
+ return NULL;
+ }
+
+ dbg_ns("Copied RDATA:\n");
+ knot_rdata_dump(rdata_copy,
+ knot_rrset_type(synth_rrset), 1);
+
+ knot_rrset_add_rdata(synth_rrset, rdata_copy);
+ rdata = knot_rrset_rdata_next(wildcard_rrset, rdata);
+ }
+
+// printf("Synthetized RRSet pointer: %p\n", synth_rrset);
+ return synth_rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the given RRSet is a wildcard RRSet and replaces it with
+ * a synthetized RRSet if required.
+ *
+ * \param name Domain name to be used as the owner of the possibly synthetized
+ * RRSet
+ * \param resp Response to which the synthetized RRSet should be stored (as a
+ * temporary RRSet).
+ * \param rrset RRSet to check (and possibly replace).
+ */
+static void ns_check_wildcard(const knot_dname_t *name, knot_packet_t *resp,
+ const knot_rrset_t **rrset)
+{
+ assert(name != NULL);
+ assert(resp != NULL);
+ assert(rrset != NULL);
+ assert(*rrset != NULL);
+
+ if (knot_dname_is_wildcard((*rrset)->owner)) {
+ knot_rrset_t *synth_rrset =
+ ns_synth_from_wildcard(*rrset, name);
+ dbg_ns("Synthetized RRSet:\n");
+ knot_rrset_dump(synth_rrset, 1);
+ knot_packet_add_tmp_rrset(resp, synth_rrset);
+ *rrset = synth_rrset;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds signatures (RRSIGs) for the given RRSet to the response.
+ *
+ * This function first checks if DNSSEC is enabled and if it was requested in
+ * the response (DO bit set). If not, it does nothing and returns 0. If yes,
+ * it retrieves RRSIGs stored in the RRSet, deals with possible wildcard owner
+ * and adds the RRSIGs to response using the given function (that determines
+ * to which section of the response they will be added).
+ *
+ * \param rrset RRSet to get the RRSIGs from.
+ * \param resp Response where to add the RRSIGs.
+ * \param name Actual name to be used as owner in case of wildcard RRSet.
+ * \param add_rrset_to_resp Function for adding the RRSIG RRset to the response.
+ * \param tc Set to 1 if omitting the RRSIG RRSet should result in setting the
+ * TC bit in the response.
+ *
+ * \return KNOT_EOK
+ * \return KNOT_ENOMEM
+ * \return KNOT_ESPACE
+ */
+static int ns_add_rrsigs(const knot_rrset_t *rrset, knot_packet_t *resp,
+ const knot_dname_t *name,
+ int (*add_rrset_to_resp)(knot_packet_t *,
+ const knot_rrset_t *,
+ int, int, int),
+ int tc)
+{
+ const knot_rrset_t *rrsigs;
+
+ dbg_ns("Adding RRSIGs for RRSet, type: %s.\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+
+ assert(resp != NULL);
+ assert(add_rrset_to_resp != NULL);
+
+ dbg_ns("DNSSEC requested: %d\n",
+ knot_query_dnssec_requested(knot_packet_query(resp)));
+ dbg_ns("RRSIGS: %p\n", knot_rrset_rrsigs(rrset));
+
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))
+ && (rrsigs = knot_rrset_rrsigs(rrset)) != NULL) {
+ if (name != NULL) {
+ ns_check_wildcard(name, resp, &rrsigs);
+ }
+ return add_rrset_to_resp(resp, rrsigs, tc, 0, 0);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Resolves CNAME chain starting in \a node, stores all the CNAMEs in the
+ * response and updates \a node and \a qname to the last node in the
+ * chain.
+ *
+ * \param node Node (possibly) containing a CNAME RR.
+ * \param qname Searched name. Will be updated to the canonical name.
+ * \param resp Response where to add the CNAME RRs.
+ * \param add_rrset_to_resp Function for adding the CNAME RRs to the response.
+ * \param tc Set to 1 if omitting the RRSIG RRSet should result in setting the
+ * TC bit in the response.
+ */
+static void ns_follow_cname(const knot_node_t **node,
+ const knot_dname_t **qname,
+ knot_packet_t *resp,
+ int (*add_rrset_to_resp)(knot_packet_t *,
+ const knot_rrset_t *,
+ int, int, int),
+ int tc)
+{
+ dbg_ns("Resolving CNAME chain...\n");
+ const knot_rrset_t *cname_rrset;
+
+ while (*node != NULL
+ && (cname_rrset = knot_node_rrset(*node, KNOT_RRTYPE_CNAME))
+ != NULL) {
+ /* put the CNAME record to answer, but replace the possible
+ wildcard name with qname */
+
+ assert(cname_rrset != NULL);
+
+ dbg_ns("CNAME RRSet: %p, owner: %p\n", cname_rrset,
+ cname_rrset->owner);
+
+ const knot_rrset_t *rrset = cname_rrset;
+
+ // ignoring other than the first record
+ if (knot_dname_is_wildcard(knot_node_owner(*node))) {
+ /* if wildcard node, we must copy the RRSet and
+ replace its owner */
+ rrset = ns_synth_from_wildcard(cname_rrset, *qname);
+ knot_packet_add_tmp_rrset(resp, (knot_rrset_t *)rrset);
+ add_rrset_to_resp(resp, rrset, tc, 0, 0);
+ ns_add_rrsigs(cname_rrset, resp, *qname,
+ add_rrset_to_resp, tc);
+ } else {
+ add_rrset_to_resp(resp, rrset, tc, 0, 0);
+ ns_add_rrsigs(rrset, resp, *qname, add_rrset_to_resp,
+ tc);
+ }
+
+ dbg_ns("Using RRSet: %p, owner: %p\n", rrset, rrset->owner);
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(rrset));
+ dbg_ns("CNAME record for owner %s put to response.\n", name);
+ free(name);
+);
+
+ // get the name from the CNAME RDATA
+ const knot_dname_t *cname = knot_rdata_cname_name(
+ knot_rrset_rdata(cname_rrset));
+ dbg_ns("CNAME name from RDATA: %p\n", cname);
+ // change the node to the node of that name
+ *node = knot_dname_node(cname, 1);
+ dbg_ns("This name's node: %p\n", *node);
+// // it is not an old node and if yes, skip it
+// if (knot_node_is_old(*node)) {
+// *node = knot_node_new_node(*node);
+// }
+
+ // save the new name which should be used for replacing wildcard
+ *qname = cname;
+ };
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves RRSet(s) of given type from the given node and adds them to
+ * the response's Answer section.
+ *
+ * \param node Node where to take the RRSet from.
+ * \param name Actual searched name (used in case of wildcard RRSet(s)).
+ * \param type Type of the RRSet(s). If set to KNOT_RRTYPE_ANY, all RRSets
+ * from the node will be added to the answer.
+ * \param resp Response where to add the RRSets.
+ *
+ * \return Number of RRSets added.
+ */
+static int ns_put_answer(const knot_node_t *node, const knot_dname_t *name,
+ uint16_t type, knot_packet_t *resp)
+{
+ int added = 0;
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(node->owner);
+ dbg_ns("Putting answers from node %s.\n", name_str);
+ free(name_str);
+);
+
+ switch (type) {
+ case KNOT_RRTYPE_ANY: {
+ dbg_ns("Returning all RRTYPES.\n");
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ break;
+ }
+ int i = 0;
+ int ret = 0;
+ const knot_rrset_t *rrset;
+ while (i < knot_node_rrset_count(node)) {
+ assert(rrsets[i] != NULL);
+ rrset = rrsets[i];
+
+ dbg_ns(" Type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+
+ ns_check_wildcard(name, resp, &rrset);
+ ret = knot_response_add_rrset_answer(resp, rrset, 1,
+ 0, 0);
+ if (ret >= 0 && (added += 1)
+ && (ret = ns_add_rrsigs(rrset, resp, name,
+ knot_response_add_rrset_answer, 1))
+ >=0 ) {
+ added += 1;
+ } else {
+ free(rrsets);
+ rrsets = NULL;
+ break;
+ }
+
+ ++i;
+ }
+ if (rrsets != NULL) {
+ free(rrsets);
+ }
+ break;
+ }
+ case KNOT_RRTYPE_RRSIG: {
+ dbg_ns("Returning all RRSIGs.\n");
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ break;
+ }
+ int i = 0;
+ int ret = 0;
+ const knot_rrset_t *rrset;
+ while (i < knot_node_rrset_count(node)) {
+ assert(rrsets[i] != NULL);
+ rrset = knot_rrset_rrsigs(rrsets[i]);
+
+ if (rrset == NULL) {
+ ++i;
+ continue;
+ }
+
+ ns_check_wildcard(name, resp, &rrset);
+ ret = knot_response_add_rrset_answer(resp, rrset, 1,
+ 0, 0);
+
+ if (ret < 0) {
+ break;
+ }
+
+ added += 1;
+ ++i;
+ }
+ free(rrsets);
+ break;
+ }
+ default: {
+ int ret = 0;
+ const knot_rrset_t *rrset = knot_node_rrset(node, type);
+ const knot_rrset_t *rrset2 = rrset;
+ if (rrset != NULL) {
+ dbg_ns("Found RRSet of type %s\n",
+ knot_rrtype_to_string(type));
+ ns_check_wildcard(name, resp, &rrset2);
+ ret = knot_response_add_rrset_answer(resp, rrset2, 1,
+ 0, 0);
+ if (ret >= 0 && (added += 1)
+ && (ret = ns_add_rrsigs(rrset, resp, name,
+ knot_response_add_rrset_answer, 1)) > 0) {
+ added += 1;
+ }
+ }
+ }
+ }
+
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ return added;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds RRSets to Additional section of the response.
+ *
+ * This function uses knot_rdata_get_name() to get the domain name from the
+ * RDATA of the RRSet according to its type. It also does not search for the
+ * retrieved domain name, but just uses its node field. Thus to work correctly,
+ * the zone where the RRSet is from should be adjusted using
+ * knot_zone_adjust_dnames().
+ *
+ * A and AAAA RRSets (and possible CNAMEs) for the found domain names are added.
+ *
+ * \warning Use this function only with types containing some domain name,
+ * otherwise it will crash (or behave strangely).
+ *
+ * \param resp Response where to add the Additional data.
+ * \param rrset RRSet to get the Additional data for.
+ */
+static void ns_put_additional_for_rrset(knot_packet_t *resp,
+ const knot_rrset_t *rrset)
+{
+ const knot_node_t *node = NULL;
+ const knot_rdata_t *rdata = NULL;
+ const knot_dname_t *dname = NULL;
+
+ // for all RRs in the RRset
+ rdata = knot_rrset_rdata(rrset);
+ while (rdata != NULL) {
+ dbg_ns("Getting name from RDATA, type %s..\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+ dname = knot_rdata_get_name(rdata,
+ knot_rrset_type(rrset));
+ assert(dname != NULL);
+ node = knot_dname_node(dname, 1);
+// // check if the node is not old and if yes, take the new one
+// if (knot_node_is_old(node)) {
+// node = knot_node_new_node(node);
+// }
+
+ dbg_ns_detail("Node saved in RDATA dname: %p\n", node);
+
+ if (node != NULL && node->owner != dname) {
+ // the stored node should be the closest encloser
+ assert(knot_dname_is_subdomain(dname, node->owner));
+ // try the wildcard child, if any
+ node = knot_node_wildcard_child(node, 1);
+// // this should not be old node!!
+// assert(!knot_node_is_old(node));
+ }
+
+ const knot_rrset_t *rrset_add;
+
+ if (node != NULL) {
+dbg_ns_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_ns("Putting additional from node %s\n", name);
+ free(name);
+);
+ dbg_ns("Checking CNAMEs...\n");
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME)
+ != NULL) {
+ dbg_ns("Found CNAME in node, following...\n");
+ const knot_dname_t *dname
+ = knot_node_owner(node);
+ ns_follow_cname(&node, &dname, resp,
+ knot_response_add_rrset_additional, 0);
+ }
+
+ // A RRSet
+ dbg_ns("A RRSets...\n");
+ rrset_add = knot_node_rrset(node, KNOT_RRTYPE_A);
+ if (rrset_add != NULL) {
+ dbg_ns("Found A RRsets.\n");
+ const knot_rrset_t *rrset_add2 = rrset_add;
+ ns_check_wildcard(dname, resp, &rrset_add2);
+ knot_response_add_rrset_additional(
+ resp, rrset_add2, 0, 1, 0);
+ ns_add_rrsigs(rrset_add, resp, dname,
+ knot_response_add_rrset_additional, 0);
+ }
+
+ // AAAA RRSet
+ dbg_ns("AAAA RRSets...\n");
+ rrset_add = knot_node_rrset(node, KNOT_RRTYPE_AAAA);
+ if (rrset_add != NULL) {
+ dbg_ns("Found AAAA RRsets.\n");
+ const knot_rrset_t *rrset_add2 = rrset_add;
+ ns_check_wildcard(dname, resp, &rrset_add2);
+ knot_response_add_rrset_additional(
+ resp, rrset_add2, 0, 1, 0);
+ ns_add_rrsigs(rrset_add, resp, dname,
+ knot_response_add_rrset_additional, 0);
+ }
+ }
+
+ assert(rrset != NULL);
+ assert(rdata != NULL);
+ rdata = knot_rrset_rdata_next(rrset, rdata);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks whether the given type requires additional processing.
+ *
+ * Only MX, NS and SRV types require additional processing.
+ *
+ * \param qtype Type to check.
+ *
+ * \retval <> 0 if additional processing is needed for \a qtype.
+ * \retval 0 otherwise.
+ */
+static int ns_additional_needed(uint16_t qtype)
+{
+ return (qtype == KNOT_RRTYPE_MX ||
+ qtype == KNOT_RRTYPE_NS ||
+ qtype == KNOT_RRTYPE_SRV);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds whatever Additional RRSets are required for the response.
+ *
+ * For each RRSet in Answer and Authority sections this function checks if
+ * additional processing is needed and if yes, it puts any Additional RRSets
+ * available to the Additional section of the response.
+ *
+ * \param resp Response to process.
+ */
+static void ns_put_additional(knot_packet_t *resp)
+{
+ dbg_ns("ADDITIONAL SECTION PROCESSING\n");
+
+ const knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < knot_packet_answer_rrset_count(resp); ++i) {
+ rrset = knot_packet_answer_rrset(resp, i);
+ assert(rrset != NULL);
+ if (ns_additional_needed(knot_rrset_type(rrset))) {
+ ns_put_additional_for_rrset(resp, rrset);
+ }
+ }
+
+ for (int i = 0; i < knot_packet_authority_rrset_count(resp); ++i) {
+ rrset = knot_packet_authority_rrset(resp, i);
+ if (ns_additional_needed(knot_rrset_type(rrset))) {
+ ns_put_additional_for_rrset(resp, rrset);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts authority NS RRSet to the Auhority section of the response.
+ *
+ * \param zone Zone to take the authority NS RRSet from.
+ * \param resp Response where to add the RRSet.
+ */
+static void ns_put_authority_ns(const knot_zone_contents_t *zone,
+ knot_packet_t *resp)
+{
+ const knot_rrset_t *ns_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_NS);
+
+ if (ns_rrset != NULL) {
+ knot_response_add_rrset_authority(resp, ns_rrset, 0, 1, 0);
+ ns_add_rrsigs(ns_rrset, resp, knot_node_owner(
+ knot_zone_contents_apex(zone)),
+ knot_response_add_rrset_authority, 1);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts SOA RRSet to the Auhority section of the response.
+ *
+ * \param zone Zone to take the SOA RRSet from.
+ * \param resp Response where to add the RRSet.
+ */
+static void ns_put_authority_soa(const knot_zone_contents_t *zone,
+ knot_packet_t *resp)
+{
+ const knot_rrset_t *soa_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
+ assert(soa_rrset != NULL);
+
+ knot_response_add_rrset_authority(resp, soa_rrset, 0, 0, 0);
+ ns_add_rrsigs(soa_rrset, resp,
+ knot_node_owner(knot_zone_contents_apex(zone)),
+ knot_response_add_rrset_authority, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a 'next closer name' to the given domain name.
+ *
+ * For definition of 'next closer name', see RFC5155, Page 6.
+ *
+ * \param closest_encloser Closest encloser of \a name.
+ * \param name Domain name to create the 'next closer' name to.
+ *
+ * \return 'Next closer name' to the given domain name or NULL if an error
+ * occured.
+ */
+static knot_dname_t *ns_next_closer(const knot_dname_t *closest_encloser,
+ const knot_dname_t *name)
+{
+ int ce_labels = knot_dname_label_count(closest_encloser);
+ int qname_labels = knot_dname_label_count(name);
+
+ assert(ce_labels < qname_labels);
+
+ // the common labels should match
+ assert(knot_dname_matched_labels(closest_encloser, name)
+ == ce_labels);
+
+ // chop some labels from the qname
+ knot_dname_t *next_closer = knot_dname_deep_copy(name);
+ if (next_closer == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < (qname_labels - ce_labels - 1); ++i) {
+ knot_dname_left_chop_no_copy(next_closer);
+ }
+
+ return next_closer;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds NSEC3 RRSet (together with corresponding RRSIGs) from the given
+ * node into the response.
+ *
+ * \param node Node to get the NSEC3 RRSet from.
+ * \param resp Response where to add the RRSets.
+ */
+static void ns_put_nsec3_from_node(const knot_node_t *node,
+ knot_packet_t *resp)
+{
+ assert(DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp)));
+
+ const knot_rrset_t *rrset = knot_node_rrset(node,
+ KNOT_RRTYPE_NSEC3);
+ assert(rrset != NULL);
+
+ int res = knot_response_add_rrset_authority(resp, rrset, 1, 1, 0);
+ // add RRSIG for the RRSet
+ if (res == 0 && (rrset = knot_rrset_rrsigs(rrset)) != NULL) {
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Finds and adds NSEC3 covering the given domain name (and their
+ * associated RRSIGs) to the response.
+ *
+ * \param zone Zone used for answering.
+ * \param name Domain name to cover.
+ * \param resp Response where to add the RRSets.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL if a runtime collision occured. The server should
+ * respond with SERVFAIL in such case.
+ */
+static int ns_put_covering_nsec3(const knot_zone_contents_t *zone,
+ const knot_dname_t *name,
+ knot_packet_t *resp)
+{
+ const knot_node_t *prev, *node;
+ /*! \todo Check version. */
+ int match = knot_zone_contents_find_nsec3_for_name(zone, name,
+ &node, &prev, 1);
+ assert(match >= 0);
+ node = knot_node_current(node);
+ prev = knot_node_current(prev);
+
+ if (match == KNOT_ZONE_NAME_FOUND){
+ // run-time collision => SERVFAIL
+ return KNOT_EOK;
+ }
+
+// // check if the prev node is not old and if yes, take the new one
+// if (knot_node_is_old(prev)) {
+// prev = knot_node_new_node(prev);
+// assert(prev != NULL);
+// }
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(prev->owner);
+ dbg_ns("Covering NSEC3 node: %s\n", name);
+ free(name);
+);
+
+ ns_put_nsec3_from_node(prev, resp);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds NSEC3s comprising the 'closest encloser proof' for the given
+ * (non-existent) domain name (and their associated RRSIGs) to the
+ * response.
+ *
+ * For definition of 'closest encloser proof', see RFC5155, section 7.2.1,
+ * Page 18.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param qname Searched (non-existent) name.
+ * \param resp Response where to add the NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_closest_encloser_proof(
+ const knot_zone_contents_t *zone,
+ const knot_node_t **closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ assert(zone != NULL);
+ assert(closest_encloser != NULL);
+ assert(*closest_encloser != NULL);
+ assert(qname != NULL);
+ assert(resp != NULL);
+
+ if (knot_zone_contents_nsec3params(zone) == NULL) {
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_ns("No NSEC3PARAM found in zone %s.\n", name);
+ free(name);
+);
+ return KNOT_EOK;
+ }
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_node_owner(*closest_encloser));
+ dbg_ns("Closest encloser: %s\n", name);
+ free(name);
+);
+
+ /*
+ * 1) NSEC3 that matches closest provable encloser.
+ */
+ const knot_node_t *nsec3_node = NULL;
+ const knot_dname_t *next_closer = NULL;
+ while ((nsec3_node = knot_node_nsec3_node((*closest_encloser), 1))
+ == NULL) {
+ next_closer = knot_node_owner((*closest_encloser));
+ *closest_encloser = knot_node_parent(*closest_encloser, 1);
+ if (*closest_encloser == NULL) {
+ // there are no NSEC3s to add
+ return KNOT_EOK;
+ }
+ }
+
+ assert(nsec3_node != NULL);
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(nsec3_node->owner);
+ dbg_ns("NSEC3 node: %s\n", name);
+ free(name);
+ name = knot_dname_to_str((*closest_encloser)->owner);
+ dbg_ns("Closest provable encloser: %s\n", name);
+ free(name);
+ if (next_closer != NULL) {
+ name = knot_dname_to_str(next_closer);
+ dbg_ns("Next closer name: %s\n", name);
+ free(name);
+ } else {
+ dbg_ns("Next closer name: none\n");
+ }
+);
+
+ ns_put_nsec3_from_node(nsec3_node, resp);
+
+ /*
+ * 2) NSEC3 that covers the "next closer" name.
+ */
+ int ret = 0;
+ if (next_closer == NULL) {
+ // create the "next closer" name by appending from qname
+ next_closer = ns_next_closer(
+ knot_node_owner(*closest_encloser), qname);
+
+ if (next_closer == NULL) {
+ return NS_ERR_SERVFAIL;
+ }
+dbg_ns_exec(
+ char *name = knot_dname_to_str(next_closer);
+ dbg_ns("Next closer name: %s\n", name);
+ free(name);
+);
+ ret = ns_put_covering_nsec3(zone, next_closer, resp);
+
+ // the cast is ugly, but no better way around it
+ knot_dname_release((knot_dname_t *)next_closer);
+ } else {
+ ret = ns_put_covering_nsec3(zone, next_closer, resp);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a name of a wildcard child of \a name.
+ *
+ * \param name Domain name to get the wildcard child name of.
+ *
+ * \return Wildcard child name or NULL if an error occured.
+ */
+static knot_dname_t *ns_wildcard_child_name(const knot_dname_t *name)
+{
+ assert(name != NULL);
+
+ knot_dname_t *wildcard = knot_dname_new_from_str("*", 1, NULL);
+ if (wildcard == NULL) {
+ return NULL;
+ }
+
+ if (knot_dname_cat(wildcard, name) == NULL) {
+ /* Directly discard dname. */
+ knot_dname_free(&wildcard);
+ return NULL;
+ }
+
+dbg_ns_exec(
+ char *name = knot_dname_to_str(wildcard);
+ dbg_ns("Wildcard: %s\n", name);
+ free(name);
+);
+ return wildcard;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSEC3s covering the non-existent wildcard child of a node
+ * (and their associated RRSIGs) into the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param node Node whose non-existent wildcard child should be covered.
+ * \param resp Response where to add the NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_no_wildcard_child(const knot_zone_contents_t *zone,
+ const knot_node_t *node,
+ knot_packet_t *resp)
+{
+ assert(node != NULL);
+ assert(resp != NULL);
+ assert(node->owner != NULL);
+
+ int ret = 0;
+ knot_dname_t *wildcard = ns_wildcard_child_name(node->owner);
+ if (wildcard == NULL) {
+ ret = NS_ERR_SERVFAIL;
+ } else {
+ ret = ns_put_covering_nsec3(zone, wildcard, resp);
+
+ /* Directly discard wildcard. */
+ knot_dname_free(&wildcard);
+ }
+
+ return ret;
+}
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for NODATA error (and their associated RRSIGs)
+ * to the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query.
+ * \note Note that for each zone there are either NSEC or NSEC3 records used.
+ *
+ * \param node Node which generated the NODATA response (i.e. not containing
+ * RRSets of the requested type).
+ * \param resp Response where to add the NSECs or NSEC3s.
+ */
+static void ns_put_nsec_nsec3_nodata(const knot_node_t *node,
+ knot_packet_t *resp)
+{
+ if (!DNSSEC_ENABLED ||
+ !knot_query_dnssec_requested(knot_packet_query(resp))) {
+ return;
+ }
+
+ const knot_node_t *nsec3_node = knot_node_nsec3_node(node, 1);
+ const knot_rrset_t *rrset = NULL;
+ if ((rrset = knot_node_rrset(node, KNOT_RRTYPE_NSEC)) != NULL
+ || (nsec3_node != NULL && (rrset =
+ knot_node_rrset(nsec3_node, KNOT_RRTYPE_NSEC3)) != NULL)) {
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ // add RRSIG for the RRSet
+ if ((rrset = knot_rrset_rrsigs(rrset)) != NULL) {
+ knot_response_add_rrset_authority(resp, rrset, 1,
+ 0, 0);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs for NXDOMAIN error to the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param qname QNAME which generated the NXDOMAIN error (i.e. not found in the
+ * zone).
+ * \param zone Zone used for answering.
+ * \param previous Previous node to \a qname in the zone. May also be NULL. In
+ * such case the function finds the previous node in the zone.
+ * \param closest_encloser Closest encloser of \a qname. Must not be NULL.
+ * \param resp Response where to put the NSECs.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nxdomain(const knot_dname_t *qname,
+ const knot_zone_contents_t *zone,
+ const knot_node_t *previous,
+ const knot_node_t *closest_encloser,
+ knot_packet_t *resp)
+{
+ const knot_rrset_t *rrset = NULL;
+
+ // check if we have previous; if not, find one using the tree
+ if (previous == NULL) {
+ /*! \todo Check version. */
+ previous = knot_zone_contents_find_previous(zone, qname);
+
+ while (!knot_node_is_auth(previous)) {
+ previous = knot_node_previous(previous, 1);
+ }
+
+ previous = knot_node_current(previous);
+ assert(previous != NULL);
+ }
+
+ char *name = knot_dname_to_str(previous->owner);
+ dbg_ns("Previous node: %s\n", name);
+ free(name);
+
+ // 1) NSEC proving that there is no node with the searched name
+ rrset = knot_node_rrset(previous, KNOT_RRTYPE_NSEC);
+ if (rrset == NULL) {
+ // no NSEC records
+ //return NS_ERR_SERVFAIL;
+ return KNOT_EOK;
+
+ }
+
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ rrset = knot_rrset_rrsigs(rrset);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+
+ // 2) NSEC proving that there is no wildcard covering the name
+ // this is only different from 1) if the wildcard would be
+ // before 'previous' in canonical order, i.e. we can
+ // search for previous until we find name lesser than wildcard
+ assert(closest_encloser != NULL);
+
+ knot_dname_t *wildcard =
+ ns_wildcard_child_name(closest_encloser->owner);
+ if (wildcard == NULL) {
+ return NS_ERR_SERVFAIL;
+ }
+
+ const knot_node_t *prev_new = previous;
+
+ while (knot_dname_compare(knot_node_owner(prev_new),
+ wildcard) > 0) {
+ dbg_ns("Previous node: %s\n",
+ knot_dname_to_str(knot_node_owner(prev_new)));
+ assert(prev_new != knot_zone_contents_apex(zone));
+ prev_new = knot_node_previous(prev_new, 1);
+ }
+ assert(knot_dname_compare(knot_node_owner(prev_new),
+ wildcard) < 0);
+
+ dbg_ns("Previous node: %s\n",
+ knot_dname_to_str(knot_node_owner(prev_new)));
+
+ /* Directly discard dname. */
+ knot_dname_free(&wildcard);
+
+ if (prev_new != previous) {
+ rrset = knot_node_rrset(prev_new, KNOT_RRTYPE_NSEC);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ rrset = knot_rrset_rrsigs(rrset);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSEC3s for NXDOMAIN error to the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param closest_encloser Closest encloser of \a qname.
+ * \param qname Domain name which generated the NXDOMAIN error (i.e. not found
+ * in the zone.
+ * \param resp Response where to put the NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_nxdomain(const knot_zone_contents_t *zone,
+ const knot_node_t *closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ // 1) Closest encloser proof
+ dbg_ns("Putting closest encloser proof.\n");
+ int ret = ns_put_nsec3_closest_encloser_proof(zone, &closest_encloser,
+ qname, resp);
+ // 2) NSEC3 covering non-existent wildcard
+ if (ret == KNOT_EOK && closest_encloser != NULL) {
+ dbg_ns("Putting NSEC3 for no wildcard child of closest "
+ "encloser.\n");
+ ret = ns_put_nsec3_no_wildcard_child(zone, closest_encloser,
+ resp);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for the NXDOMAIN error to the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query.
+ * \note Note that for each zone there are either NSEC or NSEC3 records used.
+ *
+ * \param zone Zone used for answering.
+ * \param previous Previous node to \a qname in the zone. May also be NULL. In
+ * such case the function finds the previous node in the zone.
+ * \param closest_encloser Closest encloser of \a qname. Must not be NULL.
+ * \param qname QNAME which generated the NXDOMAIN error (i.e. not found in the
+ * zone).
+ * \param resp Response where to put the NSECs.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nsec3_nxdomain(const knot_zone_contents_t *zone,
+ const knot_node_t *previous,
+ const knot_node_t *closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ int ret = 0;
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))) {
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ ret = ns_put_nsec3_nxdomain(zone, closest_encloser,
+ qname, resp);
+ } else {
+ ret = ns_put_nsec_nxdomain(qname, zone, previous,
+ closest_encloser, resp);
+ }
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSEC3s for wildcard answer into the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone. In this
+ * case it is the parent of the source of synthesis.
+ * \param qname Domain name covered by the wildcard used for answering the
+ * query.
+ * \param resp Response to put the NSEC3s into.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec3_wildcard(const knot_zone_contents_t *zone,
+ const knot_node_t *closest_encloser,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ assert(closest_encloser != NULL);
+ assert(qname != NULL);
+ assert(resp != NULL);
+ assert(DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp)));
+
+ if (!knot_zone_contents_nsec3_enabled(zone)) {
+ return KNOT_EOK;
+ }
+
+ /*
+ * NSEC3 that covers the "next closer" name.
+ */
+ // create the "next closer" name by appending from qname
+ dbg_ns("Finding next closer name for wildcard NSEC3.\n");
+ knot_dname_t *next_closer =
+ ns_next_closer(closest_encloser->owner, qname);
+
+ if (next_closer == NULL) {
+ return NS_ERR_SERVFAIL;
+ }
+dbg_ns_exec(
+ char *name = knot_dname_to_str(next_closer);
+ dbg_ns("Next closer name: %s\n", name);
+ free(name);
+);
+ int ret = ns_put_covering_nsec3(zone, next_closer, resp);
+
+
+ /* Duplicate from ns_next_close(), safe to discard. */
+ knot_dname_release(next_closer);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs for wildcard answer into the response.
+ *
+ * \note This function does not check if DNSSEC is enabled, nor if it is
+ * requested by the query.
+ *
+ * \param zone Zone used for answering.
+ * \param qname Domain name covered by the wildcard used for answering the
+ * query.
+ * \param previous Previous node of \a qname in canonical order.
+ * \param resp Response to put the NSEC3s into.
+ */
+static void ns_put_nsec_wildcard(const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ const knot_node_t *previous,
+ knot_packet_t *resp)
+{
+ assert(DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp)));
+
+ // check if we have previous; if not, find one using the tree
+ if (previous == NULL) {
+ previous = knot_zone_contents_find_previous(zone, qname);
+
+ while (!knot_node_is_auth(previous)) {
+ previous = knot_node_previous(previous, 1);
+ }
+
+ previous = knot_node_current(previous);
+ assert(previous != NULL);
+ }
+
+ const knot_rrset_t *rrset =
+ knot_node_rrset(previous, KNOT_RRTYPE_NSEC);
+ if (rrset != NULL) {
+ // NSEC proving that there is no node with the searched name
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ rrset = knot_rrset_rrsigs(rrset);
+ assert(rrset != NULL);
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for wildcard NODATA answer into the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query.
+ *
+ * \param node Node used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param previous Previous node of \a qname in canonical order.
+ * \param zone Zone used for answering.
+ * \param qname Actual searched domain name.
+ * \param resp Response where to put the NSECs and NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nsec3_wildcard_nodata(const knot_node_t *node,
+ const knot_node_t *closest_encloser,
+ const knot_node_t *previous,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ int ret = KNOT_EOK;
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))) {
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ ret = ns_put_nsec3_closest_encloser_proof(zone,
+ &closest_encloser,
+ qname, resp);
+
+ const knot_node_t *nsec3_node;
+ if (ret == KNOT_EOK
+ && (nsec3_node = knot_node_nsec3_node(node, 1))
+ != NULL) {
+ ns_put_nsec3_from_node(nsec3_node, resp);
+ }
+ } else {
+ ns_put_nsec_wildcard(zone, qname, previous, resp);
+ }
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Puts NSECs or NSEC3s for wildcard answer into the response.
+ *
+ * \note This function first checks if DNSSEC is enabled and requested by the
+ * query and if the node's owner is a wildcard.
+ *
+ * \param node Node used for answering.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param previous Previous node of \a qname in canonical order.
+ * \param zone Zone used for answering.
+ * \param qname Actual searched domain name.
+ * \param resp Response where to put the NSECs and NSEC3s.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_put_nsec_nsec3_wildcard_answer(const knot_node_t *node,
+ const knot_node_t *closest_encloser,
+ const knot_node_t *previous,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ int r = KNOT_EOK;
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))
+ && knot_dname_is_wildcard(knot_node_owner(node))) {
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ r = ns_put_nsec3_wildcard(zone, closest_encloser, qname,
+ resp);
+ } else {
+ ns_put_nsec_wildcard(zone, qname, previous, resp);
+ }
+ }
+ return r;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a referral response.
+ *
+ * This function puts the delegation NS RRSet to the Authority section of the
+ * response, possibly adds DS and their associated RRSIGs (if DNSSEC is enabled
+ * and requested by the query) and adds any available additional data (A and
+ * AAAA RRSets for the names in the NS RRs) with their associated RRSIGs
+ * to the Additional section.
+ *
+ * \param node Delegation point node.
+ * \param zone Parent zone (the one from which the response is generated).
+ * \param qname Searched name (which caused the referral).
+ * \param resp Response.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static inline int ns_referral(const knot_node_t *node,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+ dbg_ns("Referral response.\n");
+
+ while (!knot_node_is_deleg_point(node)) {
+ assert(knot_node_parent(node, 1) != NULL);
+ node = knot_node_parent(node, 1);
+ }
+
+ const knot_rrset_t *rrset = knot_node_rrset(node, KNOT_RRTYPE_NS);
+ assert(rrset != NULL);
+
+ // TODO: wildcards??
+ //ns_check_wildcard(name, resp, &rrset);
+
+ knot_response_add_rrset_authority(resp, rrset, 1, 0, 0);
+ ns_add_rrsigs(rrset, resp, node->owner,
+ knot_response_add_rrset_authority, 1);
+
+ int ret = KNOT_EOK;
+ // add DS records
+ dbg_ns("DNSSEC requested: %d\n",
+ knot_query_dnssec_requested(knot_packet_query(resp)));
+ dbg_ns("DS records: %p\n", knot_node_rrset(node, KNOT_RRTYPE_DS));
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))) {
+ rrset = knot_node_rrset(node, KNOT_RRTYPE_DS);
+ if (rrset != NULL) {
+ knot_response_add_rrset_authority(resp, rrset, 1, 0,
+ 0);
+ ns_add_rrsigs(rrset, resp, node->owner,
+ knot_response_add_rrset_authority, 1);
+ } else {
+ // no DS, add NSEC3 or NSEC
+ // if NSEC3 enabled, search for NSEC3
+ if (knot_zone_contents_nsec3_enabled(zone)) {
+ const knot_node_t *nsec3_node =
+ knot_node_nsec3_node(node, 1);
+ dbg_ns("There is no DS, putting NSEC3s...\n");
+ if (nsec3_node != NULL) {
+ dbg_ns("Putting NSEC3s from the node.\n");
+ ns_put_nsec3_from_node(nsec3_node, resp);
+ } else {
+ dbg_ns("Putting Opt-Out NSEC3s.\n");
+ // no NSEC3 (probably Opt-Out)
+ // TODO: check if the zone is Opt-Out
+ ret = ns_put_nsec3_closest_encloser_proof(zone,
+ &node, qname, resp);
+ }
+ } else {
+ const knot_rrset_t *nsec = knot_node_rrset(
+ node, KNOT_RRTYPE_NSEC);
+ if (nsec) {
+ /*! \todo Check return value? */
+ knot_response_add_rrset_authority(
+ resp, nsec, 1, 1, 0);
+ if ((nsec = knot_rrset_rrsigs(nsec)) != NULL) {
+ knot_response_add_rrset_authority(resp, nsec, 1,
+ 1, 0);
+ }
+ }
+ }
+ }
+ }
+
+ if (ret == KNOT_EOK) {
+ ns_put_additional(resp);
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Tries to answer the query from the given node.
+ *
+ * Tries to put RRSets of requested type (\a qtype) to the Answer section of the
+ * response. If successful, it also adds authority NS RRSet to the Authority
+ * section and it may add NSEC or NSEC3s in case of a wildcard answer (\a node
+ * is a wildcard node). If not successful (there are no such RRSets), it adds
+ * the SOA record to the Authority section and may add NSEC or NSEC3s according
+ * to the type of the response (NXDOMAIN if \a node is an empty non-terminal,
+ * NODATA if it is a regular node). It also adds any additional data that may
+ * be required.
+ *
+ * \param node Node to answer from.
+ * \param closest_encloser Closest encloser of \a qname in the zone.
+ * \param previous Previous domain name of \a qname in canonical order.
+ * \param zone Zone used for answering.
+ * \param qname Searched domain name.
+ * \param qtype Searched RR type.
+ * \param resp Response.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_answer_from_node(const knot_node_t *node,
+ const knot_node_t *closest_encloser,
+ const knot_node_t *previous,
+ const knot_zone_contents_t *zone,
+ const knot_dname_t *qname, uint16_t qtype,
+ knot_packet_t *resp)
+{
+ dbg_ns("Putting answers from found node to the response...\n");
+ int answers = ns_put_answer(node, qname, qtype, resp);
+
+ int ret = KNOT_EOK;
+ if (answers == 0) { // if NODATA response, put SOA
+ if (knot_node_rrset_count(node) == 0
+ && !knot_zone_contents_nsec3_enabled(zone)) {
+ // node is an empty non-terminal => NSEC for NXDOMAIN
+ //assert(knot_node_rrset_count(closest_encloser) > 0);
+ dbg_ns("Adding NSEC/NSEC3 for NXDOMAIN.\n");
+ ret = ns_put_nsec_nsec3_nxdomain(zone,
+ knot_node_previous(node, 1), closest_encloser,
+ qname, resp);
+ } else {
+ dbg_ns("Adding NSEC/NSEC3 for NODATA.\n");
+ ns_put_nsec_nsec3_nodata(node, resp);
+ if (knot_dname_is_wildcard(node->owner)) {
+ dbg_ns("Putting NSEC/NSEC3 for wildcard"
+ " NODATA\n");
+ ret = ns_put_nsec_nsec3_wildcard_nodata(node,
+ closest_encloser, previous, zone, qname,
+ resp);
+ }
+ }
+ ns_put_authority_soa(zone, resp);
+ } else { // else put authority NS
+ // if wildcard answer, add NSEC / NSEC3
+ dbg_ns("Adding NSEC/NSEC3 for wildcard answer.\n");
+ ret = ns_put_nsec_nsec3_wildcard_answer(node, closest_encloser,
+ previous, zone, qname, resp);
+ ns_put_authority_ns(zone, resp);
+ }
+
+ if (ret == KNOT_EOK) {
+ ns_put_additional(resp);
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Synthetizes a CNAME RR from a DNAME.
+ *
+ * \param dname_rrset DNAME RRSet to synthetize from (only the first RR is
+ * used).
+ * \param qname Name to be used as the owner name of the synthetized CNAME.
+ *
+ * \return Synthetized CNAME RRset (this is a newly created RRSet, remember to
+ * free it).
+ */
+static knot_rrset_t *ns_cname_from_dname(const knot_rrset_t *dname_rrset,
+ const knot_dname_t *qname)
+{
+ dbg_ns("Synthetizing CNAME from DNAME...\n");
+
+ // create new CNAME RRSet
+
+ knot_dname_t *owner = knot_dname_deep_copy(qname);
+ if (owner == NULL) {
+ return NULL;
+ }
+
+ knot_rrset_t *cname_rrset = knot_rrset_new(
+ owner, KNOT_RRTYPE_CNAME, KNOT_CLASS_IN, SYNTH_CNAME_TTL);
+
+ /* Release owner, as it's retained in rrset. */
+ knot_dname_release(owner);
+
+ if (cname_rrset == NULL) {
+ return NULL;
+ }
+
+ // replace last labels of qname with DNAME
+ knot_dname_t *cname = knot_dname_replace_suffix(qname,
+ knot_dname_size(knot_rrset_owner(dname_rrset)),
+ knot_rdata_get_item(knot_rrset_rdata(dname_rrset), 0)->dname);
+dbg_ns_exec(
+ char *name = knot_dname_to_str(cname);
+ dbg_ns("CNAME canonical name: %s.\n", name);
+ free(name);
+);
+ knot_rdata_t *cname_rdata = knot_rdata_new();
+ knot_rdata_item_t cname_rdata_item;
+ cname_rdata_item.dname = cname;
+ knot_rdata_set_items(cname_rdata, &cname_rdata_item, 1);
+
+ knot_rrset_add_rdata(cname_rrset, cname_rdata);
+
+ return cname_rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if the name created by replacing the owner of \a dname_rrset
+ * in the \a qname by the DNAME's target would be longer than allowed.
+ *
+ * \param dname_rrset DNAME RRSet to be used for the check.
+ * \param qname Name whose part is to be replaced.
+ *
+ * \retval <>0 if the created domain name would be too long.
+ * \retval 0 otherwise.
+ */
+static int ns_dname_is_too_long(const knot_rrset_t *dname_rrset,
+ const knot_dname_t *qname)
+{
+ // TODO: add function for getting DNAME target
+ if (knot_dname_label_count(qname)
+ - knot_dname_label_count(knot_rrset_owner(dname_rrset))
+ + knot_dname_label_count(knot_rdata_get_item(
+ knot_rrset_rdata(dname_rrset), 0)->dname)
+ > KNOT_MAX_DNAME_LENGTH) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief DNAME processing.
+ *
+ * This function adds the DNAME RRSet (and possibly its associated RRSIGs to the
+ * Answer section of the response, synthetizes CNAME record from the DNAME and
+ * adds it there too. It also stores the synthetized CNAME in the temporary
+ * RRSets of the response.
+ *
+ * \param dname_rrset DNAME RRSet to use.
+ * \param qname Searched name.
+ * \param resp Response.
+ */
+static void ns_process_dname(const knot_rrset_t *dname_rrset,
+ const knot_dname_t *qname,
+ knot_packet_t *resp)
+{
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(dname_rrset));
+ dbg_ns("Processing DNAME for owner %s...\n", name);
+ free(name);
+);
+ // TODO: check the number of RRs in the RRSet??
+
+ // put the DNAME RRSet into the answer
+ knot_response_add_rrset_answer(resp, dname_rrset, 1, 0, 0);
+ ns_add_rrsigs(dname_rrset, resp, qname,
+ knot_response_add_rrset_answer, 1);
+
+ if (ns_dname_is_too_long(dname_rrset, qname)) {
+ knot_response_set_rcode(resp, KNOT_RCODE_YXDOMAIN);
+ return;
+ }
+
+ // synthetize CNAME (no way to tell that client supports DNAME)
+ knot_rrset_t *synth_cname = ns_cname_from_dname(dname_rrset, qname);
+ // add the synthetized RRSet to the Answer
+ knot_response_add_rrset_answer(resp, synth_cname, 1, 0, 0);
+
+ // no RRSIGs for this RRSet
+
+ // add the synthetized RRSet into list of temporary RRSets of response
+ knot_packet_add_tmp_rrset(resp, synth_cname);
+
+ // do not search for the name in new zone (out-of-bailiwick)
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Adds DNSKEY RRSet from the apex of a zone to the response.
+ *
+ * \param apex Zone apex node.
+ * \param resp Response.
+ */
+static void ns_add_dnskey(const knot_node_t *apex, knot_packet_t *resp)
+{
+ const knot_rrset_t *rrset =
+ knot_node_rrset(apex, KNOT_RRTYPE_DNSKEY);
+ if (rrset != NULL) {
+ knot_response_add_rrset_additional(resp, rrset, 0, 0, 0);
+ ns_add_rrsigs(rrset, resp, apex->owner,
+ knot_response_add_rrset_additional, 0);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Answers the query from the given zone.
+ *
+ * This function performs the actual answering logic.
+ *
+ * \param zone Zone to use for answering.
+ * \param qname QNAME from the query.
+ * \param qtype QTYPE from the query.
+ * \param resp Response to fill in.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ *
+ * \todo Describe the answering logic in detail.
+ */
+static int ns_answer_from_zone(const knot_zone_contents_t *zone,
+ const knot_dname_t *qname, uint16_t qtype,
+ knot_packet_t *resp)
+{
+ const knot_node_t *node = NULL, *closest_encloser = NULL,
+ *previous = NULL;
+ int cname = 0, auth_soa = 0, ret = 0, find_ret = 0;
+
+search:
+#ifdef USE_HASH_TABLE
+ /*! \todo Check version. */
+ find_ret = knot_zone_contents_find_dname_hash(zone, qname, &node,
+ &closest_encloser);
+// node = knot_node_current(node);
+// closest_encloser = knot_node_current(closest_encloser);
+#else
+ /*! \todo Check version. */
+ find_ret = knot_zone_contents_find_dname(zone, qname, &node,
+ &closest_encloser, &previous);
+ node = knot_node_current(node);
+ closest_encloser = knot_node_current(closest_encloser);
+ previous = knot_node_current(previous);
+#endif
+ if (find_ret == KNOT_EBADARG) {
+ return NS_ERR_SERVFAIL;
+ }
+
+dbg_ns_exec(
+ char *name;
+ if (node) {
+ name = knot_dname_to_str(node->owner);
+ dbg_ns("zone_find_dname() returned node %s ", name);
+ free(name);
+ } else {
+ dbg_ns("zone_find_dname() returned no node,");
+ }
+
+ if (closest_encloser != NULL) {
+ name = knot_dname_to_str(closest_encloser->owner);
+ dbg_ns(" closest encloser %s.\n", name);
+ free(name);
+ } else {
+ dbg_ns(" closest encloser (nil).\n");
+ }
+ if (previous != NULL) {
+ name = knot_dname_to_str(previous->owner);
+ dbg_ns(" and previous node: %s.\n", name);
+ free(name);
+ } else {
+ dbg_ns(" and previous node: (nil).\n");
+ }
+);
+ if (find_ret == KNOT_EBADZONE) {
+ // possible only if we followed cname
+ assert(cname != 0);
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+ auth_soa = 1;
+ knot_response_set_aa(resp);
+ goto finalize;
+ }
+
+have_node:
+ dbg_ns("Closest encloser is deleg. point? %s\n",
+ (knot_node_is_deleg_point(closest_encloser)) ? "yes" : "no");
+
+ dbg_ns("Closest encloser is non authoritative? %s\n",
+ (knot_node_is_non_auth(closest_encloser)) ? "yes" : "no");
+
+ if (knot_node_is_deleg_point(closest_encloser)
+ || knot_node_is_non_auth(closest_encloser)) {
+ ret = ns_referral(closest_encloser, zone, qname, resp);
+ goto finalize;
+ }
+
+ if (find_ret == KNOT_ZONE_NAME_NOT_FOUND) {
+ // DNAME?
+ const knot_rrset_t *dname_rrset = knot_node_rrset(
+ closest_encloser, KNOT_RRTYPE_DNAME);
+ if (dname_rrset != NULL) {
+ ns_process_dname(dname_rrset, qname, resp);
+ auth_soa = 1;
+ knot_response_set_aa(resp);
+ goto finalize;
+ }
+ // else check for a wildcard child
+ const knot_node_t *wildcard_node =
+ knot_node_wildcard_child(closest_encloser, 1);
+
+ if (wildcard_node == NULL) {
+ dbg_ns("No wildcard node. (cname: %d)\n",
+ cname);
+ auth_soa = 1;
+ if (cname == 0) {
+ dbg_ns("Setting NXDOMAIN RCODE.\n");
+ // return NXDOMAIN
+ knot_response_set_rcode(resp,
+ KNOT_RCODE_NXDOMAIN);
+ if (ns_put_nsec_nsec3_nxdomain(zone, previous,
+ closest_encloser, qname, resp) != 0) {
+ return NS_ERR_SERVFAIL;
+ }
+ } else {
+ knot_response_set_rcode(resp,
+ KNOT_RCODE_NOERROR);
+ }
+ knot_response_set_aa(resp);
+ goto finalize;
+ }
+ // else set the node from which to take the answers to wild.node
+ node = wildcard_node;
+ }
+
+ // now we have the node for answering
+ if (knot_node_is_deleg_point(node) || knot_node_is_non_auth(node)) {
+ ret = ns_referral(node, zone, qname, resp);
+ goto finalize;
+ }
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_CNAME) != NULL) {
+dbg_ns_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_ns("Node %s has CNAME record, resolving...\n",
+ name);
+ free(name);
+);
+ const knot_dname_t *act_name = qname;
+ ns_follow_cname(&node, &act_name, resp,
+ knot_response_add_rrset_answer, 1);
+dbg_ns_exec(
+ char *name = (node != NULL) ? knot_dname_to_str(node->owner)
+ : "(nil)";
+ char *name2 = knot_dname_to_str(act_name);
+ dbg_ns("Canonical name: %s (%p), node found: %p\n",
+ name2, act_name, node);
+ dbg_ns("The node's owner: %s (%p)\n", name, (node != NULL)
+ ? node->owner : NULL);
+ if (node != NULL) {
+ free(name);
+ }
+ free(name2);
+);
+ qname = act_name;
+ cname = 1;
+
+ // otherwise search for the new name
+ if (node == NULL) {
+ goto search;
+ } else if (node->owner != act_name) {
+ // the stored node is closest encloser
+ find_ret = KNOT_ZONE_NAME_NOT_FOUND;
+ closest_encloser = node;
+ node = NULL;
+ goto have_node;
+ } // else do nothing, just continue
+ }
+
+ ret = ns_answer_from_node(node, closest_encloser, previous, zone, qname,
+ qtype, resp);
+ if (ret == NS_ERR_SERVFAIL) {
+ // in this case we should drop the response and send an error
+ // for now, just send the error code with a non-complete answer
+// knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL);
+// goto finalize;
+ return ret;
+ } else if (ret != KNOT_EOK) {
+ /*! \todo Handle RCODE return values!!! */
+ goto finalize;
+ }
+ knot_response_set_aa(resp);
+ knot_response_set_rcode(resp, KNOT_RCODE_NOERROR);
+
+ // this is the only case when the servers answers from
+ // particular node, i.e. the only case when it may return SOA
+ // or NS records in Answer section
+ if (DNSSEC_ENABLED
+ && knot_query_dnssec_requested(knot_packet_query(resp))
+ && node == knot_zone_contents_apex(zone)
+ && (qtype == KNOT_RRTYPE_SOA || qtype == KNOT_RRTYPE_NS)) {
+ ns_add_dnskey(node, resp);
+ }
+
+finalize:
+ if (ret == KNOT_EOK && auth_soa) {
+ ns_put_authority_soa(zone, resp);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Answers the query from the given zone database.
+ *
+ * First it searches for a zone to answer from. If there is none, it sets
+ * RCODE REFUSED to the response and ends. Otherwise it tries to answer the
+ * query using the found zone (see ns_answer_from_zone()).
+ *
+ * \param db Zone database to use for answering.
+ * \param resp Response that holds the parsed query.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_answer(knot_zonedb_t *db, knot_packet_t *resp)
+{
+ const knot_dname_t *qname = knot_packet_qname(resp);
+ assert(qname != NULL);
+
+ uint16_t qtype = knot_packet_qtype(resp);
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(qname);
+ dbg_ns("Trying to find zone for QNAME %s\n", name_str);
+ free(name_str);
+);
+ // find zone in which to search for the name
+ const knot_zone_t *zone =
+ ns_get_zone_for_qname(db, qname, qtype);
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+
+ // if no zone found, return REFUSED
+ if (zone == NULL) {
+ dbg_ns("No zone found.\n");
+ knot_response_set_rcode(resp, KNOT_RCODE_REFUSED);
+ //knot_dname_free(&qname);
+ return KNOT_EOK;
+ } else if (contents == NULL) {
+ dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n");
+ knot_response_set_rcode(resp, KNOT_RCODE_SERVFAIL);
+ return KNOT_EOK;
+ }
+
+dbg_ns_exec(
+ char *name_str2 = knot_dname_to_str(zone->contents->apex->owner);
+ dbg_ns("Found zone for QNAME %s\n", name_str2);
+ free(name_str2);
+);
+
+ // take the zone contents and use only them for answering
+
+ return ns_answer_from_zone(contents, qname, qtype, resp);
+
+ //knot_dname_free(&qname);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Converts the response to wire format.
+ *
+ * \param resp Response to convert.
+ * \param wire Place for the wire format of the response.
+ * \param wire_size In: space available for the wire format in bytes.
+ * Out: actual size of the wire format in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_response_to_wire(knot_packet_t *resp, uint8_t *wire,
+ size_t *wire_size)
+{
+ uint8_t *rwire = NULL;
+ size_t rsize = 0;
+ int ret = 0;
+
+ if ((ret = knot_packet_to_wire(resp, &rwire, &rsize))
+ != KNOT_EOK) {
+ dbg_ns("Error converting response packet "
+ "to wire format (error %d).\n", ret);
+ return NS_ERR_SERVFAIL;
+ }
+
+ if (rsize > *wire_size) {
+ dbg_ns("Reponse size (%zu) larger than allowed wire size "
+ "(%zu).\n", rsize, *wire_size);
+ return NS_ERR_SERVFAIL;
+ }
+
+ if (rwire != wire) {
+ dbg_ns("Wire format reallocated, copying to place for "
+ "wire.\n");
+ memcpy(wire, rwire, rsize);
+ } else {
+ dbg_ns("Using the same space or wire format.\n");
+ }
+
+ *wire_size = rsize;
+ //free(rwire);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a wire format of an error response from partially created
+ * response.
+ *
+ * \param resp Response to use.
+ * \param wire Place for the wire format of the response.
+ * \param wire_size In: space available for the wire format in bytes.
+ * Out: actual size of the wire format in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval NS_ERR_SERVFAIL
+ */
+static int ns_error_response_to_wire(knot_packet_t *resp, uint8_t *wire,
+ size_t *wire_size)
+{
+ /* Do not call the packet conversion function
+ * wire format is assembled, but COUNTs in header are not set.
+ * This is ideal, we just truncate the packet after the question.
+ */
+ dbg_ns("Creating error response.\n");
+
+ size_t rsize = knot_packet_question_size(knot_packet_query(resp));
+ dbg_ns("Error response (~ query) size: %zu\n", rsize);
+
+ // take 'qsize' from the current wireformat of the response
+ // it is already assembled - Header and Question section are copied
+ const uint8_t *rwire = knot_packet_wireformat(resp);
+ if (rsize > *wire_size) {
+ dbg_ns("Reponse size (%zu) larger than allowed wire size"
+ " (%zu).\n", rsize, *wire_size);
+ return NS_ERR_SERVFAIL;
+ }
+
+ assert(rwire != wire);
+
+ /*! \todo Why is this copied?? Why we cannot use resp->wireformat?? */
+ memcpy(wire, rwire, rsize);
+
+ *wire_size = rsize;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct ns_axfr_params {
+ knot_ns_xfr_t *xfr;
+ int ret;
+} ns_axfr_params_t;
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_tsig_required(int packet_nr)
+{
+ dbg_ns_detail("ns_tsig_required(%d): %d\n", packet_nr,
+ (packet_nr % KNOT_NS_TSIG_FREQ == 0));
+ return (packet_nr % KNOT_NS_TSIG_FREQ == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_xfr_send_and_clear(knot_ns_xfr_t *xfr, int add_tsig)
+{
+ assert(xfr != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(xfr->wire != NULL);
+ assert(xfr->send != NULL);
+
+ // Transform the packet into wire format
+ dbg_ns("Converting response to wire format..\n");
+ size_t real_size = xfr->wire_size;
+ if (ns_response_to_wire(xfr->response, xfr->wire, &real_size) != 0) {
+ return NS_ERR_SERVFAIL;
+// // send back SERVFAIL (as this is our problem)
+// ns_error_response(nameserver,
+// knot_wire_get_id(query_wire),
+// KNOT_RCODE_SERVFAIL, response_wire,
+// rsize);
+ }
+
+ int res = 0;
+
+ size_t digest_real_size = xfr->digest_max_size;
+
+ dbg_ns_detail("xfr->tsig_key=%p\n", xfr->tsig_key);
+ /*! \note [TSIG] Generate TSIG if required (during XFR/IN). */
+ if (xfr->tsig_key && add_tsig) {
+ if (xfr->packet_nr == 0) {
+ /* Add key, digest and digest length. */
+ dbg_ns_detail("Calling tsig_sign(): %p, %zu, %zu, "
+ "%p, %zu, %p, %zu, %p\n",
+ xfr->wire, real_size, xfr->wire_size,
+ xfr->digest, xfr->digest_size, xfr->digest,
+ digest_real_size, xfr->tsig_key);
+ res = knot_tsig_sign(xfr->wire, &real_size,
+ xfr->wire_size, xfr->digest,
+ xfr->digest_size, xfr->digest,
+ &digest_real_size,
+ xfr->tsig_key);
+ } else {
+ /* Add key, digest and digest length. */
+ dbg_ns_detail("Calling tsig_sign_next()\n");
+ res = knot_tsig_sign_next(xfr->wire, &real_size,
+ xfr->wire_size,
+ xfr->digest,
+ xfr->digest_size,
+ xfr->digest,
+ &digest_real_size,
+ xfr->tsig_key);
+ }
+
+ dbg_ns_detail("Sign function returned: %s\n",
+ knot_strerror(res));
+ dbg_ns_detail("Real size of digest: %zu\n", digest_real_size);
+
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ assert(digest_real_size > 0);
+ // save the new previous digest size
+ xfr->digest_size = digest_real_size;
+ }
+
+ // Send the response
+ dbg_ns("Sending response (size %zu)..\n", real_size);
+ //dbg_ns_hex((const char *)xfr->wire, real_size);
+ res = xfr->send(xfr->session, &xfr->addr, xfr->wire, real_size);
+ if (res < 0) {
+ dbg_ns("Send returned %d\n", res);
+ return res;
+ } else if (res != real_size) {
+ dbg_ns("AXFR did not send right amount of bytes."
+ " Transfer size: %zu, sent: %d\n",
+ real_size, res);
+ }
+
+ // Clean the response structure
+ dbg_ns("Clearing response structure..\n");
+ knot_response_clear(xfr->response, 0);
+
+ // increment the packet number
+ ++xfr->packet_nr;
+ if (xfr->tsig_key && add_tsig) {
+ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
+ } else {
+ knot_packet_set_tsig_size(xfr->response, 0);
+ }
+
+ dbg_ns("Response structure after clearing:\n");
+ knot_packet_dump(xfr->response);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void ns_axfr_from_node(knot_node_t *node, void *data)
+{
+ assert(node != NULL);
+ assert(data != NULL);
+
+ ns_axfr_params_t *params = (ns_axfr_params_t *)data;
+
+ if (params->ret != KNOT_EOK) {
+ // just skip (will be called on next node with the same params
+ dbg_ns("Params contain error: %s, skipping node...\n",
+ knot_strerror(params->ret));
+ return;
+ }
+
+ dbg_ns("Params OK, answering AXFR from node %p.\n", node);
+dbg_ns_exec(
+ char *name = knot_dname_to_str(knot_node_owner(node));
+ dbg_ns("Node ownerr: %s\n", name);
+ free(name);
+);
+
+ if (knot_node_rrset_count(node) == 0) {
+ return;
+ }
+
+ const knot_rrset_t **rrsets = knot_node_rrsets(node);
+ if (rrsets == NULL) {
+ params->ret = KNOT_ENOMEM;
+ return;
+ }
+
+ int i = 0;
+ int ret = 0;
+ const knot_rrset_t *rrset = NULL;
+ while (i < knot_node_rrset_count(node)) {
+ assert(rrsets[i] != NULL);
+ rrset = rrsets[i];
+rrset:
+ dbg_ns(" Type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rrset)));
+
+ // do not add SOA
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_SOA) {
+ ++i;
+ continue;
+ }
+
+ ret = knot_response_add_rrset_answer(params->xfr->response,
+ rrset, 0, 0, 1);
+
+ if (ret == KNOT_ESPACE) {
+ // TODO: send the packet and clean the structure
+ dbg_ns("Packet full, sending..\n");
+ ret = ns_xfr_send_and_clear(params->xfr,
+ knot_ns_tsig_required(params->xfr->packet_nr));
+ if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+ // otherwise try once more with the same RRSet
+ goto rrset;
+ } else if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+
+ // we can send the RRSets in any order, so add the RRSIGs now
+ rrset = knot_rrset_rrsigs(rrset);
+rrsigs:
+ if (rrset == NULL) {
+ ++i;
+ continue;
+ }
+
+ ret = knot_response_add_rrset_answer(params->xfr->response,
+ rrset, 0, 0, 1);
+
+ if (ret == KNOT_ESPACE) {
+ // TODO: send the packet and clean the structure
+ dbg_ns("Packet full, sending..\n");
+ ret = ns_xfr_send_and_clear(params->xfr,
+ knot_ns_tsig_required(params->xfr->packet_nr));
+ if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+ // otherwise try once more with the same RRSet
+ goto rrsigs;
+ } else if (ret != KNOT_EOK) {
+ // some wierd problem, we should end
+ params->ret = KNOT_ERROR;
+ break;
+ }
+
+ // this way only whole RRSets are always sent
+ // we guess it will not create too much overhead
+
+ ++i;
+ }
+ if (rrsets != NULL) {
+ free(rrsets);
+ }
+
+ /*! \todo maybe distinguish some error codes. */
+ //params->ret = (ret == 0) ? KNOT_EOK : KNOT_ERROR;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_axfr_from_zone(knot_zone_contents_t *zone, knot_ns_xfr_t *xfr)
+{
+ assert(xfr != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(xfr->wire != NULL);
+ assert(xfr->send != NULL);
+
+ ns_axfr_params_t params;
+ memset(&params, 0, sizeof(ns_axfr_params_t));
+ params.xfr = xfr;
+ params.ret = KNOT_EOK;
+
+ xfr->packet_nr = 0;
+
+ /*
+ * First SOA
+ */
+
+ // retrieve SOA - must be send as first and last RR
+ const knot_rrset_t *soa_rrset = knot_node_rrset(
+ knot_zone_contents_apex(zone), KNOT_RRTYPE_SOA);
+ if (soa_rrset == NULL) {
+ // some really serious error
+ return KNOT_ERROR;
+ }
+
+ int ret;
+
+ // add SOA RR to the response
+ ret = knot_response_add_rrset_answer(xfr->response, soa_rrset, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ // something is really wrong
+ return KNOT_ERROR;
+ }
+
+ // add the SOA's RRSIG
+ const knot_rrset_t *rrset = knot_rrset_rrsigs(soa_rrset);
+ if (rrset != NULL
+ && (ret = knot_response_add_rrset_answer(xfr->response, rrset,
+ 0, 0, 1)) != KNOT_EOK) {
+ // something is really wrong, these should definitely fit in
+ return KNOT_ERROR;
+ }
+
+ knot_zone_contents_tree_apply_inorder(zone, ns_axfr_from_node,
+ &params);
+
+ if (params.ret != KNOT_EOK) {
+ return KNOT_ERROR; // maybe do something with the code
+ }
+
+ knot_zone_contents_nsec3_apply_inorder(zone, ns_axfr_from_node,
+ &params);
+
+ if (params.ret != KNOT_EOK) {
+ return KNOT_ERROR; // maybe do something with the code
+ }
+
+ /*
+ * Last SOA
+ */
+
+ // try to add the SOA to the response again (last RR)
+ ret = knot_response_add_rrset_answer(xfr->response, soa_rrset, 0, 0, 1);
+ if (ret == KNOT_ESPACE) {
+
+ // if there is not enough space, send the response and
+ // add the SOA record to a new packet
+ dbg_ns("Packet full, sending..\n");
+ ret = ns_xfr_send_and_clear(xfr,
+ knot_ns_tsig_required(xfr->packet_nr));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_response_add_rrset_answer(xfr->response,
+ soa_rrset, 0, 0, 1);
+ if (ret != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ } else if (ret != KNOT_EOK) {
+ // something is really wrong
+ return KNOT_ERROR;
+ }
+
+ dbg_ns("Sending packet...\n");
+ return ns_xfr_send_and_clear(xfr, 1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr_put_rrset(knot_ns_xfr_t *xfr, const knot_rrset_t *rrset)
+{
+ int res = knot_response_add_rrset_answer(xfr->response, rrset,
+ 0, 0, 0);
+ if (res == KNOT_ESPACE) {
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_NOERROR);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, knot_ns_tsig_required(xfr->packet_nr));
+
+ res = knot_response_add_rrset_answer(xfr->response,
+ rrset, 0, 0, 0);
+ }
+
+ if (res != KNOT_EOK) {
+ dbg_ns("Error putting origin SOA to IXFR reply: %s\n",
+ knot_strerror(res));
+ /*! \todo Probably send back AXFR instead. */
+ knot_response_set_rcode(xfr->response,
+ KNOT_RCODE_SERVFAIL);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session); /*! \todo Remove for UDP.*/
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr_put_changeset(knot_ns_xfr_t *xfr, const knot_changeset_t *chgset)
+{
+ // 1) put origin SOA
+ int res = ns_ixfr_put_rrset(xfr, chgset->soa_from);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ // 2) put remove RRSets
+ for (int i = 0; i < chgset->remove_count; ++i) {
+ res = ns_ixfr_put_rrset(xfr, chgset->remove[i]);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+ }
+
+ // 1) put target SOA
+ res = ns_ixfr_put_rrset(xfr, chgset->soa_to);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ // 2) put remove RRSets
+ for (int i = 0; i < chgset->add_count; ++i) {
+ res = ns_ixfr_put_rrset(xfr, chgset->add[i]);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr_from_zone(knot_ns_xfr_t *xfr)
+{
+ assert(xfr != NULL);
+ assert(xfr->zone != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(knot_packet_authority_rrset_count(xfr->query) > 0);
+ assert(xfr->data != NULL);
+
+ /*! \todo REMOVE start */
+// const knot_rrset_t *zone_soa =
+// knot_node_rrset(knot_zone_contents_apex(
+// knot_zone_contents(xfr->zone)),
+// KNOT_RRTYPE_SOA);
+// // retrieve origin (xfr) serial and target (zone) serial
+// uint32_t zone_serial = knot_rdata_soa_serial(
+// knot_rrset_rdata(zone_soa));
+// uint32_t xfr_serial = knot_rdata_soa_serial(knot_rrset_rdata(
+// knot_packet_authority_rrset(xfr->query, 0)));
+
+// // 3) load changesets from journal
+// knot_changesets_t *chgsets = (knot_changesets_t *)
+// calloc(1, sizeof(knot_changesets_t));
+// int res = xfr_load_changesets(xfr->zone, chgsets, xfr_serial,
+// zone_serial);
+// if (res != KNOT_EOK) {
+// dbg_ns("IXFR query cannot be answered: %s.\n",
+// knot_strerror(res));
+// /*! \todo Probably send back AXFR instead. */
+// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL);
+// /*! \todo Probably rename the function. */
+// ns_axfr_send_and_clear(xfr);
+// //socket_close(xfr->session); /*! \todo Remove for UDP. */
+// return 1;
+// }
+
+ /*! \todo REMOVE end */
+
+ knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data;
+ knot_zone_contents_t* contents = knot_zone_get_contents(xfr->zone);
+ assert(contents);
+ const knot_rrset_t *zone_soa =
+ knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+
+ // 4) put the zone SOA as the first Answer RR
+ int res = knot_response_add_rrset_answer(xfr->response, zone_soa, 0,
+ 0, 0);
+ if (res != KNOT_EOK) {
+ dbg_ns("IXFR query cannot be answered: %s.\n",
+ knot_strerror(res));
+ knot_response_set_rcode(xfr->response,
+ KNOT_RCODE_SERVFAIL);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+// socket_close(xfr->session); /*! \todo Remove for UDP.*/
+ return 1;
+ }
+
+ // 5) put the changesets into the response while they fit in
+ for (int i = 0; i < chgsets->count; ++i) {
+ res = ns_ixfr_put_changeset(xfr, &chgsets->sets[i]);
+ if (res != KNOT_EOK) {
+ // answer is sent, socket is closed
+ return KNOT_EOK;
+ }
+ }
+
+ if (chgsets->count > 0) {
+ res = ns_ixfr_put_rrset(xfr, zone_soa);
+ }
+
+ if (res == KNOT_EOK) {
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session); /*! \todo Remove for UDP.*/
+ return 1;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int ns_ixfr(knot_ns_xfr_t *xfr)
+{
+ assert(xfr != NULL);
+ assert(xfr->query != NULL);
+ assert(xfr->response != NULL);
+ assert(knot_packet_qtype(xfr->response) == KNOT_RRTYPE_IXFR);
+
+ // check if there is the required authority record
+ if ((knot_packet_authority_rrset_count(xfr->query) <= 0)) {
+ // malformed packet
+ dbg_ns("IXFR query does not contain authority record.\n");
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session);
+ return 1;
+ }
+
+ const knot_rrset_t *soa = knot_packet_authority_rrset(xfr->query, 0);
+ const knot_dname_t *qname = knot_packet_qname(xfr->response);
+
+ // check if XFR QNAME and SOA correspond
+ if (knot_packet_qtype(xfr->query) != KNOT_RRTYPE_IXFR
+ || knot_rrset_type(soa) != KNOT_RRTYPE_SOA
+ || knot_dname_compare(qname, knot_rrset_owner(soa)) != 0) {
+ // malformed packet
+ dbg_ns("IXFR query is malformed.\n");
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_FORMERR);
+ /*! \todo Probably rename the function. */
+ ns_xfr_send_and_clear(xfr, 1);
+ //socket_close(xfr->session); /*! \todo Remove for UDP. */
+ return 1;
+ }
+
+ return ns_ixfr_from_zone(xfr);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ns_prepare_response(knot_nameserver_t *nameserver,
+ knot_packet_t *query, knot_packet_t **resp,
+ size_t max_size)
+{
+ assert(max_size >= 500);
+
+ // initialize response packet structure
+ *resp = knot_packet_new(KNOT_PACKET_PREALLOC_RESPONSE);
+ if (*resp == NULL) {
+ dbg_ns("Failed to create packet structure.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_packet_set_max_size(*resp, max_size);
+ //(*resp)->wireformat = response_wire;;
+ //(*resp)->max_size = max_size;
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to init response structure.\n");
+ knot_packet_free(resp);
+ return ret;
+ }
+
+ ret = knot_response_init_from_query(*resp, query);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to init response structure.\n");
+ knot_packet_free(resp);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int32_t ns_serial_difference(uint32_t s1, uint32_t s2)
+{
+ return (((int64_t)s1 - s2) % ((int64_t)1 << 32));
+}
+
+/*----------------------------------------------------------------------------*/
+/* Public functions */
+/*----------------------------------------------------------------------------*/
+
+knot_nameserver_t *knot_ns_create()
+{
+ knot_nameserver_t *ns = malloc(sizeof(knot_nameserver_t));
+ if (ns == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ ns->data = 0;
+
+ // Create zone database structure
+ dbg_ns("Creating Zone Database structure...\n");
+ ns->zone_db = knot_zonedb_new();
+ if (ns->zone_db == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ns);
+ return NULL;
+ }
+
+ // prepare empty response with SERVFAIL error
+ knot_packet_t *err = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (err == NULL) {
+ ERR_ALLOC_FAILED;
+ free(ns);
+ return NULL;
+ }
+
+ dbg_ns("Created default empty response...\n");
+
+ int rc = knot_packet_set_max_size(err, KNOT_WIRE_HEADER_SIZE);
+ if (rc != KNOT_EOK) {
+ dbg_ns("Error creating default error response: %s.\n",
+ knot_strerror(rc));
+ free(ns);
+ knot_packet_free(&err);
+ return NULL;
+ }
+
+ rc = knot_response_init(err);
+ if (rc != KNOT_EOK) {
+ dbg_ns("Error initializing default error response:"
+ " %s.\n", knot_strerror(rc));
+ free(ns);
+ knot_packet_free(&err);
+ return NULL;
+ }
+
+ knot_response_set_rcode(err, KNOT_RCODE_SERVFAIL);
+ ns->err_resp_size = 0;
+
+ dbg_ns("Converting default empty response to wire format...\n");
+
+ uint8_t *error_wire = NULL;
+
+ if (knot_packet_to_wire(err, &error_wire, &ns->err_resp_size) != 0) {
+ dbg_ns("Error while converting "
+ "default error response to "
+ "wire format \n");
+ knot_packet_free(&err);
+ free(ns);
+ return NULL;
+ }
+
+ ns->err_response = (uint8_t *)malloc(ns->err_resp_size);
+ if (ns->err_response == NULL) {
+ dbg_ns("Error while converting default "
+ "error response to wire format \n");
+ knot_packet_free(&err);
+ free(ns);
+ return NULL;
+ }
+
+ memcpy(ns->err_response, error_wire, ns->err_resp_size);
+
+ dbg_ns("Done..\n");
+
+ knot_packet_free(&err);
+
+ if (EDNS_ENABLED) {
+ ns->opt_rr = knot_edns_new();
+ if (ns->opt_rr == NULL) {
+ dbg_ns("Error while preparing OPT RR of the"
+ " server.\n");
+ knot_packet_free(&err);
+ free(ns);
+ return NULL;
+ }
+ knot_edns_set_version(ns->opt_rr, EDNS_VERSION);
+ knot_edns_set_payload(ns->opt_rr, MAX_UDP_PAYLOAD_EDNS);
+ } else {
+ ns->opt_rr = NULL;
+ }
+
+ knot_packet_free(&err);
+
+ return ns;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
+ knot_packet_t *packet, knot_packet_type_t *type)
+{
+ if (packet == NULL || query_wire == NULL || type == NULL) {
+ dbg_ns("Missing parameter to query parsing.\n");
+ return KNOT_EBADARG;
+ }
+
+ dbg_ns("ns_parse_packet() called with query size %zu.\n", qsize);
+ //dbg_ns_hex((char *)query_wire, qsize);
+
+ if (qsize < 2) {
+ return KNOT_EMALF;
+ }
+
+ // 1) create empty response
+ dbg_ns("Parsing packet...\n");
+ //parsed = knot_response_new_empty(NULL);
+
+ int ret = 0;
+
+ if ((ret = knot_packet_parse_from_wire(packet, query_wire,
+ qsize, 1)) != 0) {
+ dbg_ns("Error while parsing packet, "
+ "libknot error '%s'.\n", knot_strerror(ret));
+// knot_response_free(&parsed);
+ return KNOT_RCODE_FORMERR;
+ }
+
+ dbg_ns("Parsed packet header and Question:\n");
+ knot_packet_dump(packet);
+
+ // 3) determine the query type
+ switch (knot_packet_opcode(packet)) {
+ case KNOT_OPCODE_QUERY:
+ switch (knot_packet_qtype(packet)) {
+ case KNOT_RRTYPE_AXFR:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_AXFR : KNOT_RESPONSE_AXFR;
+ break;
+ case KNOT_RRTYPE_IXFR:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_IXFR : KNOT_RESPONSE_IXFR;
+ break;
+ default:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_NORMAL : KNOT_RESPONSE_NORMAL;
+ }
+
+ break;
+ case KNOT_OPCODE_NOTIFY:
+ *type = (knot_packet_is_query(packet))
+ ? KNOT_QUERY_NOTIFY : KNOT_RESPONSE_NOTIFY;
+ break;
+ case KNOT_OPCODE_UPDATE:
+ if(knot_packet_is_query(packet)) {
+ *type = KNOT_QUERY_UPDATE;
+ } else {
+ return KNOT_RCODE_FORMERR;
+ }
+ break;
+ default:
+ return KNOT_RCODE_NOTIMPL;
+ }
+
+// knot_packet_free(&packet);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_id,
+ uint8_t rcode, uint8_t *response_wire, size_t *rsize)
+{
+ //dbg_ns("Error response: \n");
+ //dbg_ns_hex((const char *)nameserver->err_response,
+ // nameserver->err_resp_size);
+
+ memcpy(response_wire, nameserver->err_response,
+ nameserver->err_resp_size);
+ // copy ID of the query
+ knot_wire_set_id(response_wire, query_id);
+ // set the RCODE
+ knot_wire_set_rcode(response_wire, rcode);
+ *rsize = nameserver->err_resp_size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_error_response_full(knot_nameserver_t *nameserver,
+ knot_packet_t *response, uint8_t rcode,
+ uint8_t *response_wire, size_t *rsize)
+{
+ knot_response_set_rcode(response, rcode);
+
+ if (ns_error_response_to_wire(response, response_wire, rsize) != 0) {
+ knot_ns_error_response(nameserver, knot_packet_id(
+ knot_packet_query(response)),
+ KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize)
+{
+ dbg_ns("ns_answer_normal()\n");
+
+ // first, parse the rest of the packet
+ assert(knot_packet_is_query(query));
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
+ int ret;
+
+ ret = knot_packet_parse_rest(query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ (ret == KNOT_EMALF)
+ ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ /*
+ * Semantic checks - if ANCOUNT > 0 or NSCOUNT > 0, return FORMERR.
+ *
+ * If any xxCOUNT is less or more than actual RR count
+ * the previously called knot_packet_parse_rest() will recognize this.
+ *
+ * Check the QDCOUNT and in case of anything but 1 send back
+ * FORMERR
+ */
+ if (knot_packet_ancount(query) > 0
+ || knot_packet_nscount(query) > 0
+ || knot_packet_qdcount(query) != 1) {
+ dbg_ns("ANCOUNT or NSCOUNT not 0 in query, reply FORMERR.\n");
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ KNOT_RCODE_FORMERR, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ size_t resp_max_size = 0;
+
+ assert(*rsize >= MAX_UDP_PAYLOAD);
+
+ knot_packet_dump(query);
+
+ if (knot_query_edns_supported(query)) {
+ if (knot_edns_get_payload(&query->opt_rr) <
+ knot_edns_get_payload(nameserver->opt_rr)) {
+ resp_max_size = knot_edns_get_payload(&query->opt_rr);
+ } else {
+ resp_max_size = knot_edns_get_payload(
+ nameserver->opt_rr);
+ }
+ }
+
+ if (resp_max_size < MAX_UDP_PAYLOAD) {
+ resp_max_size = MAX_UDP_PAYLOAD;
+ }
+
+ knot_packet_t *response;
+ ret = knot_ns_prepare_response(nameserver, query, &response,
+ resp_max_size);
+ if (ret != KNOT_EOK) {
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
+ dbg_ns("Opt RR: version: %d, payload: %d\n",
+ query->opt_rr.version, query->opt_rr.payload);
+
+ // get the answer for the query
+ rcu_read_lock();
+ knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+
+ dbg_ns("EDNS supported in query: %d\n",
+ knot_query_edns_supported(query));
+
+ // set the OPT RR to the response
+ if (knot_query_edns_supported(query)) {
+ /*! \todo API. */
+// if (knot_edns_get_payload(&query->opt_rr) > MAX_UDP_PAYLOAD) {
+// ret = knot_packet_set_max_size(response,
+// knot_edns_get_payload(&query->opt_rr));
+// } else {
+// ret = knot_packet_set_max_size(response,
+// MAX_UDP_PAYLOAD);
+// }
+
+// if (ret != KNOT_EOK) {
+// dbg_ns("Failed to set max size.\n");
+// knot_ns_error_response_full(nameserver, response,
+// KNOT_RCODE_SERVFAIL,
+// response_wire, rsize);
+// return KNOT_EOK;
+// }
+
+ ret = knot_response_add_opt(response, nameserver->opt_rr, 1);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to set OPT RR to the response"
+ ": %s\n",knot_strerror(ret));
+ } else {
+ // copy the DO bit from the query
+ if (knot_query_dnssec_requested(query)) {
+ /*! \todo API for this. */
+ knot_edns_set_do(&response->opt_rr);
+ }
+ }
+ }/* else {
+ dbg_ns("Setting max size to %u.\n", MAX_UDP_PAYLOAD);
+ ret = knot_packet_set_max_size(response, MAX_UDP_PAYLOAD);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to set max size to %u\n",
+ MAX_UDP_PAYLOAD);
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ return KNOT_EOK;
+ }
+ }*/
+
+ dbg_ns("Response max size: %zu\n", response->max_size);
+
+ ret = ns_answer(zonedb, response);
+ if (ret != 0) {
+ // now only one type of error (SERVFAIL), later maybe more
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ } else {
+ dbg_ns("Created response packet.\n");
+ //knot_response_dump(resp);
+ knot_packet_dump(response);
+
+ // 4) Transform the packet into wire format
+ if (ns_response_to_wire(response, response_wire, rsize) != 0) {
+ // send back SERVFAIL (as this is our problem)
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ }
+ }
+
+ rcu_read_unlock();
+ knot_packet_free(&response);
+
+ dbg_ns("Returning response with wire size %zu\n", *rsize);
+ //dbg_ns_hex((char *)response_wire, *rsize);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ dbg_ns("knot_ns_init_xfr()\n");
+
+ if (nameserver == NULL || xfr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // no need to parse rest of the packet
+ /*! \todo Parse rest of packet because of EDNS. */
+ int ret = knot_packet_parse_rest(xfr->query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: %s\n",
+ knot_strerror(ret));
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL,
+ xfr->wire, &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ return ret;
+ }
+
+ dbg_packet("Parsed XFR query:\n");
+ knot_packet_dump(xfr->query);
+
+ // initialize response packet structure
+ knot_packet_t *response = knot_packet_new(
+ KNOT_PACKET_PREALLOC_RESPONSE);
+ if (response == NULL) {
+ dbg_ns("Failed to create packet structure.\n");
+ /*! \todo xfr->wire is not NULL, will fail on assert! */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ knot_packet_free(&response);
+ return ret;
+ }
+
+ //int ret = knot_packet_set_max_size(response, xfr->wire_size);
+ response->wireformat = xfr->wire;
+ response->max_size = xfr->wire_size;
+
+// if (ret != KNOT_EOK) {
+// dbg_ns("Failed to init response structure.\n");
+// /*! \todo xfr->wire is not NULL, will fail on assert! */
+// knot_ns_error_response(nameserver, xfr->query->header.id,
+// KNOT_RCODE_SERVFAIL, xfr->wire,
+// &xfr->wire_size);
+// int res = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+// xfr->wire_size);
+// knot_packet_free(&response);
+// return res;
+// }
+
+ ret = knot_response_init_from_query(response, xfr->query);
+
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to init response structure.\n");
+ /*! \todo xfr->wire is not NULL, will fail on assert! */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ int res = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ knot_packet_free(&response);
+ return res;
+ }
+
+ xfr->response = response;
+
+ knot_zonedb_t *zonedb = rcu_dereference(nameserver->zone_db);
+
+ const knot_dname_t *qname = knot_packet_qname(xfr->response);
+
+ assert(knot_packet_qtype(xfr->response) == KNOT_RRTYPE_AXFR ||
+ knot_packet_qtype(xfr->response) == KNOT_RRTYPE_IXFR);
+
+dbg_ns_exec(
+ char *name_str = knot_dname_to_str(qname);
+ dbg_ns("Trying to find zone with name %s\n", name_str);
+ free(name_str);
+);
+ // find zone in which to search for the name
+ knot_zone_t *zone = knot_zonedb_find_zone(zonedb, qname);
+
+ // if no zone found, return NotAuth
+ if (zone == NULL) {
+ dbg_ns("No zone found.\n");
+ knot_response_set_rcode(xfr->response, KNOT_RCODE_NOTAUTH);
+ ns_xfr_send_and_clear(xfr, 1);
+ return KNOT_ENOZONE;
+ }
+
+dbg_ns_exec(
+ char *name2_str = knot_dname_to_str(qname);
+ dbg_ns("Found zone for name %s\n", name2_str);
+ free(name2_str);
+);
+ xfr->zone = zone;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ns_serial_compare(uint32_t s1, uint32_t s2)
+{
+ int32_t diff = ns_serial_difference(s1, s2);
+ return (s1 == s2) /* s1 equal to s2 */
+ ? 0
+ :((diff >= 1 && diff < ((uint32_t)1 << 31))
+ ? 1 /* s1 larger than s2 */
+ : -1); /* s1 less than s2 */
+}
+
+/*----------------------------------------------------------------------------*/
+
+int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
+ uint32_t *serial_to)
+{
+ if (xfr == NULL || xfr->zone == NULL || serial_from == NULL
+ || serial_to == NULL) {
+ dbg_ns_detail("Wrong parameters: xfr=%p,"
+ " xfr->zone = %p\n", xfr, xfr->zone);
+ return KNOT_EBADARG;
+ }
+
+ const knot_zone_t *zone = xfr->zone;
+ const knot_zone_contents_t *contents = knot_zone_contents(zone);
+ if (!contents) {
+ dbg_ns_detail("Missing contents\n");
+ return KNOT_EBADARG;
+ }
+
+ if (knot_zone_contents_apex(contents) == NULL) {
+ dbg_ns_detail("No apex.\n");
+ return KNOT_EBADARG;
+ }
+
+ const knot_rrset_t *zone_soa =
+ knot_node_rrset(knot_zone_contents_apex(contents),
+ KNOT_RRTYPE_SOA);
+ if (zone_soa == NULL) {
+ dbg_ns_verb("No SOA.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (knot_packet_nscount(xfr->query) < 1) {
+ dbg_ns_verb("No Authority record.\n");
+ return KNOT_EMALF;
+ }
+
+ if (knot_packet_authority_rrset(xfr->query, 0) == NULL) {
+ dbg_ns_verb("Authority record missing.\n");
+ return KNOT_ERROR;
+ }
+
+ // retrieve origin (xfr) serial and target (zone) serial
+ *serial_to = knot_rdata_soa_serial(knot_rrset_rdata(zone_soa));
+ *serial_from = knot_rdata_soa_serial(knot_rrset_rdata(
+ knot_packet_authority_rrset(xfr->query, 0)));
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr, knot_rcode_t rcode)
+{
+ /*! \todo Handle TSIG errors differently. */
+ knot_response_set_rcode(xfr->response, rcode);
+
+ /*! \todo Probably rename the function. */
+ int ret = 0;
+ if ((ret = ns_xfr_send_and_clear(xfr, 1)) != KNOT_EOK) {
+ size_t size = 0;
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire, &size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || nameserver == NULL || xfr->zone == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ rcu_read_lock();
+
+ // take the contents and answer from them
+ int ret = 0;
+ knot_zone_contents_t *contents = knot_zone_get_contents(xfr->zone);
+ if (!contents) {
+ dbg_ns("AXFR failed on stub zone\n");
+ /*! \todo replace with knot_ns_xfr_send_error() */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ rcu_read_unlock();
+ knot_packet_free(&xfr->response);
+ return KNOT_EOK;
+ }
+
+ /*!
+ * \todo [TSIG] The TSIG data should already be stored in 'xfr'.
+ * Now just count the expected size of the TSIG RR and save it
+ * to the response structure.
+ */
+
+ /*! \todo [TSIG] Get the TSIG size from some API function. */
+ if (xfr->tsig_size > 0) {
+ dbg_ns_detail("Setting TSIG size in packet: %zu\n",
+ xfr->tsig_size);
+ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
+ }
+
+ ret = ns_axfr_from_zone(contents, xfr);
+
+ /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
+ * and when it does not. E.g. if there was problem in sending
+ * packet, it will probably fail when sending the SERVFAIL also.
+ */
+ if (ret < 0) {
+ dbg_ns("AXFR failed, sending SERVFAIL.\n");
+ // now only one type of error (SERVFAIL), later maybe more
+ /*! \todo xfr->wire is not NULL, will fail on assert! */
+ /*! \todo replace with knot_ns_xfr_send_error() */
+ knot_ns_error_response(nameserver, xfr->query->header.id,
+ KNOT_RCODE_SERVFAIL, xfr->wire,
+ &xfr->wire_size);
+ ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+ xfr->wire_size);
+ } else if (ret > 0) {
+ ret = KNOT_ERROR;
+ }
+
+ rcu_read_unlock();
+
+ knot_packet_free(&xfr->response);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ if (nameserver == NULL || xfr == NULL || xfr->zone == NULL
+ || xfr->response == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ //uint8_t *wire = NULL;
+ //size_t size = xfr->wire_size;
+
+ // parse rest of the packet (we need the Authority record)
+ int ret = knot_packet_parse_rest(xfr->query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the packet. Reply FORMERR.\n");
+// knot_ns_error_response_full(nameserver, xfr->response,
+// KNOT_RCODE_FORMERR, xfr->wire,
+// &size);
+ knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_FORMERR);
+
+ //ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
+ knot_packet_free(&xfr->response);
+ return ret;
+ }
+
+ // check if the zone has contents
+ if (knot_zone_contents(xfr->zone) == NULL) {
+ dbg_ns("Zone expired or not bootstrapped. Reply SERVFAIL.\n");
+ ret = knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
+// knot_ns_error_response_full(nameserver, xfr->response,
+// KNOT_RCODE_SERVFAIL, xfr->wire,
+// &size);
+
+// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire, size);
+ knot_packet_free(&xfr->response);
+ return ret;
+ }
+
+ /*!
+ * \todo [TSIG] The TSIG data should already be stored in 'xfr'.
+ * Now just count the expected size of the TSIG RR and save it
+ * to the response structure. This should be optional, only if
+ * the request contained TSIG, i.e. if there is the data in 'xfr'.
+ */
+
+ /*! \todo [TSIG] Get the TSIG size from some API function. */
+ if (xfr->tsig_size > 0) {
+ knot_packet_set_tsig_size(xfr->response, xfr->tsig_size);
+ }
+
+ ret = ns_ixfr(xfr);
+
+ /*! \todo Somehow distinguish when it makes sense to send the SERVFAIL
+ * and when it does not. E.g. if there was problem in sending
+ * packet, it will probably fail when sending the SERVFAIL also.
+ */
+ if (ret < 0) {
+ dbg_ns("IXFR failed, sending SERVFAIL.\n");
+ // now only one type of error (SERVFAIL), later maybe more
+
+ /*! \todo Extract this to some function. */
+// knot_response_set_rcode(xfr->response, KNOT_RCODE_SERVFAIL);
+// uint8_t *wire = NULL;
+// ret = knot_packet_to_wire(xfr->response, &wire, &size);
+// if (ret != KNOT_EOK) {
+//// knot_ns_error_response(nameserver,
+//// xfr->query->header.id,
+//// KNOT_RCODE_SERVFAIL, xfr->wire,
+//// &size);
+//// ret = xfr->send(xfr->session, &xfr->addr, xfr->wire,
+//// size);
+// knot_ns_xfr_send_error(xfr, KNOT_RCODE_SERVFAIL);
+// knot_packet_free(&xfr->response);
+// return ret;
+// } else {
+// ret = xfr->send(xfr->session, &xfr->addr, wire, size);
+// }
+ knot_ns_xfr_send_error(nameserver, xfr, KNOT_RCODE_SERVFAIL);
+ } /*else if (ret > 0) {
+ ret = KNOT_ERROR;
+ }*/
+
+ knot_packet_free(&xfr->response);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_process_axfrin(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr)
+{
+ /*!
+ * \todo [TSIG] Here we assume that 'xfr' contains TSIG information
+ * and the digest of the query sent to the master or the previous
+ * digest.
+ */
+
+ dbg_ns("ns_process_axfrin: incoming packet, wire size: %zu\n",
+ xfr->wire_size);
+
+ int ret = xfrin_process_axfr_packet(/*xfr->wire, xfr->wire_size,*/
+ /*(xfrin_constructed_zone_t **)(&xfr->data)*/
+ xfr);
+
+ if (ret > 0) { // transfer finished
+ dbg_ns("ns_process_axfrin: AXFR finished, zone created.\n");
+ /*
+ * Adjust zone so that node count is set properly and nodes are
+ * marked authoritative / delegation point.
+ */
+ xfrin_constructed_zone_t *constr_zone =
+ (xfrin_constructed_zone_t *)xfr->data;
+ knot_zone_contents_t *zone = constr_zone->contents;
+ assert(zone != NULL);
+
+ dbg_ns("ns_process_axfrin: adjusting zone.\n");
+ knot_zone_contents_adjust(zone, 0);
+
+ /* Create and fill hash table */
+ dbg_ns("ns_process_axfrin: filling hash table.\n");
+ int rc = knot_zone_contents_create_and_fill_hash_table(zone);
+ if (rc != KNOT_EOK) {
+ return KNOT_ERROR; // TODO: change error code
+ }
+
+ // save the zone contents to the xfr->data
+ xfr->data = zone;
+
+ // free the structure used for processing XFR
+ assert(constr_zone->rrsigs == NULL);
+ free(constr_zone);
+
+ //knot_zone_contents_dump(zone, 0);
+ }
+
+ /*!
+ * \todo In case of error, shouldn't the zone be destroyed here?
+ */
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_switch_zone(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr)
+{
+ if (xfr == NULL || nameserver == NULL || xfr->data == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_contents_t *zone = (knot_zone_contents_t *)xfr->data;
+
+ dbg_ns("Replacing zone by new one: %p\n", zone);
+
+ // find the zone in the zone db
+ knot_zone_t *z = knot_zonedb_find_zone(nameserver->zone_db,
+ knot_node_owner(knot_zone_contents_apex(zone)));
+ if (z == NULL) {
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_ns("Failed to replace zone %s, old zone "
+ "not found\n", name);
+ free(name);
+ } else {
+ zone->zone = z;
+ }
+
+ knot_zone_contents_t *old = rcu_xchg_pointer(&z->contents, zone);
+
+// knot_zone_t *old = knot_zonedb_replace_zone(nameserver->zone_db,
+// zone);
+ dbg_ns("Old zone: %p\n", old);
+// if (old == NULL) {
+// char *name = knot_dname_to_str(
+// knot_node_owner(knot_zone_apex(zone)));
+// dbg_ns("Failed to replace zone %s\n", name);
+// free(name);
+// }
+
+ // wait for readers to finish
+ dbg_ns("Waiting for readers to finish...\n");
+ synchronize_rcu();
+ // destroy the old zone
+ dbg_ns("Freeing old zone: %p\n", old);
+ knot_zone_contents_deep_free(&old, 0);
+
+dbg_ns_exec(
+ dbg_ns("Zone db contents: (zone count: %zu)\n",
+ nameserver->zone_db->zone_count);
+
+ const knot_zone_t **zones = knot_zonedb_zones(nameserver->zone_db);
+ for (int i = 0; i < knot_zonedb_zone_count
+ (nameserver->zone_db); i++) {
+ dbg_ns("%d. zone: %p", i, zones[i]);
+ char *name = knot_dname_to_str(zones[i]->name);
+ dbg_ns(" zone name: %s\n", name);
+ free(name);
+ }
+ free(zones);
+);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*! \todo In this function, xfr->zone is properly set. If this is so, we do not
+ * have to search for the zone after the transfer has finished.
+ */
+int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr)
+{
+ dbg_ns("ns_process_ixfrin: incoming packet\n");
+
+ /*!
+ * \todo [TSIG] Here we assume that 'xfr' contains TSIG information
+ * and the digest of the query sent to the master or the previous
+ * digest.
+ */
+
+ int ret = xfrin_process_ixfr_packet(xfr/*xfr->wire, xfr->wire_size,
+ (knot_changesets_t **)(&xfr->data)*/);
+
+ if (ret == XFRIN_RES_FALLBACK) {
+ dbg_ns("ns_process_ixfrin: Fallback to AXFR.\n");
+ assert(xfr->data == NULL);
+// dbg_ns("xfr->zone = %p\n", xfr->zone);
+// dbg_ns("Zone name: %.*s\n",
+// xfr->zone->name->size, xfr->zone->name->name);
+// assert(xfr->zone == NULL);
+ knot_packet_free(&xfr->query);
+ return KNOT_ENOIXFR;
+ }
+
+ if (ret > 0) {
+ dbg_ns("ns_process_ixfrin: IXFR finished\n");
+
+ knot_changesets_t *chgsets = (knot_changesets_t *)xfr->data;
+ if (chgsets == NULL || chgsets->first_soa == NULL) {
+ // nothing to be done??
+ dbg_ns("No changesets created for incoming IXFR!\n");
+ return ret;
+ }
+
+ // find zone associated with the changesets
+ knot_zone_t *zone = knot_zonedb_find_zone(
+ nameserver->zone_db,
+ knot_rrset_owner(chgsets->first_soa));
+ if (zone == NULL) {
+ dbg_ns("No zone found for incoming IXFR!\n");
+ knot_free_changesets(
+ (knot_changesets_t **)(&xfr->data));
+ return KNOT_ENOZONE; /*! \todo Other error code? */
+ }
+
+ switch (ret) {
+ case XFRIN_RES_COMPLETE:
+ xfr->zone = zone;
+ break;
+ case XFRIN_RES_SOA_ONLY: {
+ // compare the SERIAL from the changeset with the zone's
+ // serial
+ const knot_node_t *apex = knot_zone_contents_apex(
+ knot_zone_contents(zone));
+ if (apex == NULL) {
+ return KNOT_ERROR;
+ }
+
+ const knot_rrset_t *zone_soa = knot_node_rrset(
+ apex, KNOT_RRTYPE_SOA);
+ if (zone_soa == NULL) {
+ return KNOT_ERROR;
+ }
+
+ if (knot_rdata_soa_serial(knot_rrset_rdata(
+ chgsets->first_soa))
+ != knot_rdata_soa_serial(knot_rrset_rdata(
+ zone_soa))) {
+ dbg_ns("Update did not fit.\n");
+ return KNOT_EAGAIN;
+ } else {
+ // free changesets
+ dbg_ns("No update needed.\n");
+ knot_free_changesets(
+ (knot_changesets_t **)(&xfr->data));
+ return KNOT_ENOXFR;
+ }
+ } break;
+ }
+ }
+
+ /*!
+ * \todo In case of error, shouldn't the zone be destroyed here?
+ */
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize,
+ knot_zone_t **zone, knot_changeset_t **changeset)
+{
+ // 1) Parse the rest of the packet
+ assert(knot_packet_is_query(query));
+
+ knot_packet_t *response;
+ assert(*rsize >= MAX_UDP_PAYLOAD);
+ int ret = knot_ns_prepare_response(nameserver, query, &response,
+ MAX_UDP_PAYLOAD);
+ if (ret != KNOT_EOK) {
+ knot_ns_error_response(nameserver, knot_packet_id(query),
+ KNOT_RCODE_SERVFAIL, response_wire,
+ rsize);
+ return KNOT_EOK;
+ }
+
+ assert(response != NULL);
+
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ query->parsed, query->size);
+
+ if (knot_packet_parsed(query) < knot_packet_size(query)) {
+ ret = knot_packet_parse_rest(query);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to parse rest of the query: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response,
+ (ret == KNOT_EMALF)
+ ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+ }
+
+ dbg_ns("Query - parsed: %zu, total wire size: %zu\n",
+ knot_packet_parsed(query), knot_packet_size(query));
+
+ /*! \todo API for EDNS values. */
+ dbg_ns("Opt RR: version: %d, payload: %d\n",
+ query->opt_rr.version, query->opt_rr.payload);
+
+ // 2) Find zone for the query
+ // we do not check if there is only one entry in the Question section
+ // because the packet structure does not allow it
+ /*! \todo Check number of Question entries while parsing. */
+ if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) {
+ dbg_ns("Question is not of type SOA.\n");
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_FORMERR,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ *zone = knot_zonedb_find_zone(nameserver->zone_db,
+ knot_packet_qname(query));
+ if (*zone == NULL) {
+ dbg_ns("Zone not found for the update.\n");
+ knot_ns_error_response_full(nameserver, response,
+ KNOT_RCODE_NOTAUTH,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ uint8_t rcode = 0;
+ // 3) Check zone
+ ret = knot_ddns_check_zone(*zone, query, &rcode);
+ if (ret == KNOT_EBADZONE) {
+ // zone is slave, forward the request
+ /*! \todo Implement forwarding. */
+ return KNOT_EBADZONE;
+ } else if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ // 4) Convert prerequisities
+ knot_ddns_prereq_t *prereqs = NULL;
+ ret = knot_ddns_process_prereqs(query, &prereqs, &rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ assert(prereqs != NULL);
+
+ // 5) Check prerequisities
+ /*! \todo Somehow ensure the zone will not be changed until the update
+ * is finished.
+ */
+ ret = knot_ddns_check_prereqs(knot_zone_contents(*zone), &prereqs,
+ &rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_ddns_prereqs_free(&prereqs);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ // 6) Convert update to changeset
+ ret = knot_ddns_process_update(query, changeset, &rcode);
+ if (ret != KNOT_EOK) {
+ dbg_ns("Failed to check zone for update: "
+ "%s.\n", knot_strerror(ret));
+ knot_ns_error_response_full(nameserver, response, rcode,
+ response_wire, rsize);
+ knot_ddns_prereqs_free(&prereqs);
+ knot_packet_free(&response);
+ return KNOT_EOK;
+ }
+
+ assert(changeset != NULL);
+
+ // 7) Create response
+ dbg_ns("Update converted successfuly.\n");
+
+ /*! \todo No response yet. Distinguish somehow in the caller.
+ * Maybe only this case will be EOK, other cases some error.
+ */
+
+ knot_packet_free(&response);
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_create_forward_query(const knot_packet_t *query,
+ uint8_t *query_wire, size_t *size)
+{
+ // just copy the wireformat of the query and set a new random ID to it
+ if (knot_packet_size(query) > *size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(query_wire, knot_packet_wireformat(query),
+ knot_packet_size(query));
+ *size = knot_packet_size(query);
+
+ knot_wire_set_id(query_wire, knot_random_id());
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ns_process_forward_response(const knot_packet_t *response,
+ uint16_t original_id,
+ uint8_t *response_wire, size_t *size)
+{
+ // just copy the wireformat of the response and set the original ID
+
+ if (knot_packet_size(response) > *size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(response_wire, knot_packet_wireformat(response),
+ knot_packet_size(response));
+ *size = knot_packet_size(response);
+
+ knot_wire_set_id(response_wire, original_id);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *knot_ns_data(knot_nameserver_t *nameserver)
+{
+ return nameserver->data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void *knot_ns_get_data(knot_nameserver_t *nameserver)
+{
+ return nameserver->data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_set_data(knot_nameserver_t *nameserver, void *data)
+{
+ nameserver->data = data;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ns_destroy(knot_nameserver_t **nameserver)
+{
+ synchronize_rcu();
+
+ free((*nameserver)->err_response);
+ if ((*nameserver)->opt_rr != NULL) {
+ knot_edns_free(&(*nameserver)->opt_rr);
+ }
+
+ // destroy the zone db
+ knot_zonedb_deep_free(&(*nameserver)->zone_db);
+
+ free(*nameserver);
+ *nameserver = NULL;
+}
diff --git a/src/libknot/nameserver/name-server.h b/src/libknot/nameserver/name-server.h
new file mode 100644
index 0000000..0d93df6
--- /dev/null
+++ b/src/libknot/nameserver/name-server.h
@@ -0,0 +1,358 @@
+/*!
+ * \file name-server.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * Contains the "name server" structure and interface for the main DNS
+ * functions. Currently only supports answering simple queries, without any
+ * extensions.
+ *
+ * \todo Consider saving pointer to the zdb_find_name() function in the
+ * nameserver structure. Probably not needed, these modules can be
+ * inter-connected.
+ * \todo Provide interface for other DNS functions - zone transfers, dynamic
+ * updates, etc.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/* 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_NAME_SERVER_H_
+#define _KNOT_NAME_SERVER_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "zone/zonedb.h"
+#include "edns.h"
+#include "consts.h"
+#include "tsig.h"
+#include "packet/packet.h"
+#include "common/sockaddr.h"
+#include "updates/changesets.h"
+
+struct conf_t;
+struct server_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Name server structure. Holds all important data needed for the
+ * supported DNS functions.
+ *
+ * Currently only holds pointer to the zone database for answering queries.
+ */
+typedef struct knot_nameserver {
+ /*!
+ * \brief Pointer to the zone database structure used for answering
+ * queries.
+ */
+ knot_zonedb_t *zone_db;
+ uint8_t *err_response; /*!< Prepared generic error response. */
+ size_t err_resp_size; /*!< Size of the prepared error response. */
+ knot_opt_rr_t *opt_rr; /*!< OPT RR with the server's EDNS0 info. */
+
+ void *data;
+} knot_nameserver_t;
+
+/*! \brief Callback for sending one packet back through a TCP connection. */
+typedef int (*xfr_callback_t)(int session, sockaddr_t *addr,
+ uint8_t *packet, size_t size);
+
+/*!
+ * \brief Single XFR operation structure.
+ *
+ * Used for communication with XFR handler.
+ */
+typedef struct knot_ns_xfr {
+ int type;
+ int flags;
+ sockaddr_t addr;
+ knot_packet_t *query;
+ knot_packet_t *response;
+ xfr_callback_t send;
+ int session;
+
+ /*!
+ * XFR-out: Output buffer.
+ * XFR-in: Buffer for query or incoming packet.
+ */
+ uint8_t *wire;
+
+ /*!
+ * XFR-out: Size of the output buffer.
+ * XFR-in: Size of the current packet.
+ */
+ size_t wire_size;
+ void *data;
+ knot_zone_t *zone;
+ void *owner;
+
+ /*! \note [TSIG] TSIG fields */
+ /*! \brief Message(s) to sign in wireformat.
+ *
+ * This field should be allocated at the start of transfer and
+ * freed at the end. During the transfer it is only rewritten.
+ */
+ uint8_t *tsig_data;
+ size_t tsig_data_size; /*!< Size of the message(s) in bytes */
+// const knot_rrset_t *tsig; /*!< Response TSIG.
+// \todo [TSIG] Replace with separate data. */
+ size_t tsig_size; /*!< Size of the TSIG RR wireformat in bytes.*/
+ knot_key_t *tsig_key; /*!< Associated TSIG key for signing. */
+
+ uint8_t *digest; /*!< Buffer for counting digest. */
+ size_t digest_size; /*!< Size of the digest. */
+ size_t digest_max_size; /*!< Size of the buffer. */
+
+ /*! \brief Previous digest or request digest.
+ *
+ * Should be allocated before the transfer (known size).
+ */
+// uint8_t *prev_digest;
+// size_t prev_digest_size; /*!< Size of previous digest in bytes. */
+
+ /*!
+ * \brief Number of the packet currently assembled.
+ *
+ * In case of XFR-in, this is not the overall number of packet, just
+ * number counted from last TSIG check.
+ */
+ int packet_nr;
+} knot_ns_xfr_t;
+
+
+static const int KNOT_NS_TSIG_FREQ = 100;
+
+static const size_t KNOT_NS_TSIG_DATA_MAX_SIZE = 100 * 64 * 1024;
+
+/*!
+ * \brief XFR request flags.
+ */
+enum knot_ns_xfr_flag_t {
+ XFR_FLAG_TCP = 1 << 0, /*!< XFR request is on TCP. */
+ XFR_FLAG_UDP = 1 << 1 /*!< XFR request is on UDP. */
+};
+
+/*!
+ * \brief XFR request types.
+ */
+typedef enum knot_ns_xfr_type_t {
+ /* Special events. */
+ XFR_TYPE_CLOSE = -1, /*!< Close connection event. */
+
+ /* DNS events. */
+ XFR_TYPE_AIN = 0, /*!< AXFR-IN request (start transfer). */
+ XFR_TYPE_AOUT, /*!< AXFR-OUT request (incoming transfer). */
+ XFR_TYPE_IIN, /*!< IXFR-IN request (start transfer). */
+ XFR_TYPE_IOUT, /*!< IXFR-OUT request (incoming transfer). */
+ XFR_TYPE_SOA, /*!< Pending SOA request. */
+ XFR_TYPE_NOTIFY /*!< Pending NOTIFY query. */
+} knot_ns_xfr_type_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Allocates and initializes the name server structure.
+ *
+ * \return Pointer to the name server structure.
+ */
+knot_nameserver_t *knot_ns_create();
+
+/*!
+ * \brief Parses the given query into the response structure and recognizes
+ * type of the query.
+ *
+ * Some query types are distinguished by OPCODE (NOTIFY, UPDATE, etc.), some
+ * by QTYPE (AXFR, IXFR). As these information are needed on the same layer
+ * to decide what to do with the query, the knot_query_t type is used for this
+ * purpose.
+ *
+ * \param query_wire Wire format of the query.
+ * \param qsize Size of the query in octets.
+ * \param packet Packet structure to be filled with the parsed query.
+ * \param type Type of the query.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EMALF if the query is totally unusable. Such query must be
+ * ignored.
+ * \retval KNOT_RCODE_SERVFAIL if there was some internal error. Call
+ * ns_error_response() with \a rcode set to this
+ * value to get proper error response.
+ * \retval KNOT_RCODE_FORMERR if the query was malformed, but can be used to
+ * construct an error response. Call
+ * ns_error_response() with \a rcode set to this
+ * value to get proper error response.
+ * \retval KNOT_RCODE_NOTIMPL if the query has an unsupported type. Call
+ * ns_error_response() with \a rcode set to this
+ * value to get proper error response.
+ */
+int knot_ns_parse_packet(const uint8_t *query_wire, size_t qsize,
+ knot_packet_t *packet, knot_packet_type_t *type);
+
+/*!
+ * \brief Prepares wire format of an error response using generic error template
+ * stored in the nameserver structure.
+ *
+ * The error response will not contain the Question section from the query, just
+ * a header with ID copied from the query and the given RCODE.
+ *
+ * \param nameserver Nameserver structure containing the error template.
+ * \param query_id ID of the query.
+ * \param rcode RCODE to set in the response.
+ * \param response_wire Place for wire format of the response.
+ * \param rsize Size of the error response will be stored here.
+ */
+void knot_ns_error_response(const knot_nameserver_t *nameserver, uint16_t query_id,
+ uint8_t rcode, uint8_t *response_wire, size_t *rsize);
+
+/*!
+ * \brief Creates a response for the given normal query using the data of the
+ * nameserver.
+ *
+ * \param nameserver Name server structure to provide the needed data.
+ * \param resp Response structure with parsed query.
+ * \param response_wire Place for the response in wire format.
+ * \param rsize Input: maximum acceptable size of the response. Output: real
+ * size of the response.
+ *
+ * \retval KNOT_EOK if a valid response was created.
+ * \retval KNOT_EMALF if an error occured and the response is not valid.
+ */
+int knot_ns_answer_normal(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize);
+
+int knot_ns_init_xfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Compares two zone serials.
+ *
+ * \retval < 0 if s1 is less than s2.
+ * \retval > 0 if s1 is larger than s2.
+ * \retval == 0 if s1 is equal to s2.
+ */
+int ns_serial_compare(uint32_t s1, uint32_t s2);
+
+int ns_ixfr_load_serials(const knot_ns_xfr_t *xfr, uint32_t *serial_from,
+ uint32_t *serial_to);
+
+int knot_ns_xfr_send_error(const knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr, knot_rcode_t rcode);
+
+/*!
+ * \brief Processes an AXFR query.
+ *
+ * This function sequentially creates DNS packets to be sent as a response
+ * to the AXFR query and sends each packet using the given callback (\a
+ * send_packet).
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \note Currently only a stub which sends one error response using the given
+ * callback.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ERROR
+ *
+ * \todo Maybe the place for the wire format should be passed in as in
+ * the ns_answer_request() function...?
+ */
+int knot_ns_answer_axfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Processes an IXFR query.
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \todo Document properly.
+ */
+int knot_ns_answer_ixfr(knot_nameserver_t *nameserver, knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Processes an AXFR-IN packet.
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \todo Document me.
+ */
+int knot_ns_process_axfrin(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr);
+
+int knot_ns_switch_zone(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Processes an IXFR-IN packet.
+ *
+ * \param nameserver Name server structure to provide the data for answering.
+ * \param xfr Persistent transfer-specific data.
+ *
+ * \retval KNOT_EOK If this packet was processed successfuly and another packet
+ * is expected. (RFC1995bis, case c)
+ * \retval KNOT_ENOXFR If the transfer is not taking place because server's
+ * SERIAL is the same as this client's SERIAL. The client
+ * should close the connection and do no further processing.
+ * (RFC1995bis case a).
+ * \retval KNOT_EAGAIN If the server could not fit the transfer into the packet.
+ * This should happen only if UDP was used. In this case
+ * the client should retry the request via TCP. If UDP was
+ * not used, it should be considered that the transfer was
+ * malformed and the connection should be closed.
+ * (RFC1995bis case b).
+ * \retval >0 Transfer successully finished. Changesets are created and furter
+ * processing is needed.
+ * \retval Other If any other error occured. The connection should be closed.
+ *
+ * \todo Document me.
+ */
+int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
+ knot_ns_xfr_t *xfr);
+
+int knot_ns_process_update(knot_nameserver_t *nameserver, knot_packet_t *query,
+ uint8_t *response_wire, size_t *rsize,
+ knot_zone_t **zone, knot_changeset_t **changeset);
+
+int knot_ns_create_forward_query(const knot_packet_t *query,
+ uint8_t *query_wire, size_t *size);
+
+int knot_ns_process_forward_response(const knot_packet_t *response,
+ uint16_t original_id,
+ uint8_t *response_wire, size_t *size);
+
+void *knot_ns_data(knot_nameserver_t *nameserver);
+
+void *knot_ns_get_data(knot_nameserver_t *nameserver);
+
+void knot_ns_set_data(knot_nameserver_t *nameserver, void *data);
+
+int knot_ns_tsig_required(int packet_nr);
+
+/*!
+ * \brief Properly destroys the name server structure.
+ *
+ * \param nameserver Nameserver to destroy.
+ */
+void knot_ns_destroy(knot_nameserver_t **nameserver);
+
+
+#endif /* _KNOTNAME_SERVER_H_ */
+
+/*! @} */
diff --git a/src/libknot/nsec3.c b/src/libknot/nsec3.c
new file mode 100644
index 0000000..303d2e6
--- /dev/null
+++ b/src/libknot/nsec3.c
@@ -0,0 +1,265 @@
+/* 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 <stdint.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include "nsec3.h"
+#include "common.h"
+#include "util/descriptor.h"
+#include "util/utils.h"
+#include "util/tolower.h"
+#include "util/error.h"
+#include "util/debug.h"
+
+/*----------------------------------------------------------------------------*/
+
+int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
+ const knot_rrset_t *nsec3param)
+{
+ if (params == NULL || nsec3param == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(knot_rrset_type(nsec3param) == KNOT_RRTYPE_NSEC3PARAM);
+ const knot_rdata_t *rdata = knot_rrset_rdata(nsec3param);
+
+ assert(rdata->count == 4);
+
+ params->algorithm = *(uint8_t *)
+ (&knot_rdata_item(rdata, 0)->raw_data[1]);
+ params->flags = *(uint8_t *)
+ (&knot_rdata_item(rdata, 1)->raw_data[1]);
+ params->iterations = knot_wire_read_u16(
+ (uint8_t *)(knot_rdata_item(rdata, 2)->raw_data + 1));
+
+ params->salt_length =
+ ((uint8_t *)knot_rdata_item(rdata, 3)->raw_data)[2];
+
+ if (params->salt_length > 0) {
+ params->salt = (uint8_t *)malloc(params->salt_length);
+ CHECK_ALLOC_LOG(params->salt, -1);
+ memcpy(params->salt,
+ (uint8_t *)knot_rdata_item(rdata, 3)->raw_data + 3,
+ params->salt_length);
+ } else {
+ params->salt = NULL;
+ }
+
+ dbg_nsec3("Parsed NSEC3PARAM:\n");
+ dbg_nsec3("Algorithm: %hu\n", params->algorithm);
+ dbg_nsec3("Flags: %hu\n", params->flags);
+ dbg_nsec3("Iterations: %hu\n", params->iterations);
+ dbg_nsec3("Salt length: %hu\n", params->salt_length);
+ dbg_nsec3("Salt: ");
+ if (params->salt != NULL) {
+ dbg_nsec3_hex((char *)params->salt,
+ params->salt_length);
+ dbg_nsec3("\n");
+ } else {
+ dbg_nsec3("none\n");
+ }
+
+ return KNOT_EOK;
+}
+
+static uint8_t *knot_nsec3_to_lowercase(const uint8_t *data, size_t size)
+{
+ uint8_t *out = (uint8_t *)malloc(size);
+ CHECK_ALLOC_LOG(out, NULL);
+
+ for (int i = 0; i < size; ++i) {
+ out[i] = knot_tolower(data[i]);
+ }
+
+ return out;
+}
+
+/*----------------------------------------------------------------------------*/
+#if KNOT_NSEC3_SHA_USE_EVP
+int knot_nsec3_sha1(const knot_nsec3_params_t *params,
+ const uint8_t *data, size_t size, uint8_t **digest,
+ size_t *digest_size)
+{
+ if (digest == NULL || digest_size == NULL || data == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ uint8_t *salt = params->salt;
+ uint8_t salt_length = params->salt_length;
+ uint16_t iterations = params->iterations;
+
+ EVP_MD_CTX mdctx;
+ EVP_MD_CTX_init(&mdctx);
+
+ *digest = (uint8_t *)malloc(EVP_MD_size(EVP_sha1()));
+ if (*digest == NULL) {
+ ERR_ALLOC_FAILED;
+ return -1;
+ }
+
+ uint8_t *data_low = knot_nsec3_to_lowercase(data, size);
+ if (data_low == NULL) {
+ free(*digest);
+ return -1;
+ }
+
+ const uint8_t *in = data_low;
+ unsigned in_size = size;
+
+ int res = 0;
+
+#ifdef KNOT_NSEC3_DEBUG
+ unsigned long long total_time = 0;
+ unsigned long calls = 0;
+ long time = 0;
+#endif
+
+ for (int i = 0; i <= iterations; ++i) {
+#ifdef KNOT_NSEC3_DEBUG
+ perf_begin();
+#endif
+
+ EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
+
+ res = EVP_DigestUpdate(&mdctx, in, in_size);
+
+ if (salt_length > 0) {
+ res = EVP_DigestUpdate(&mdctx, salt, salt_length);
+ }
+
+ EVP_DigestFinal_ex(&mdctx, *digest, digest_size);
+ in = *digest;
+ in_size = *digest_size;
+
+#ifdef KNOT_NSEC3_DEBUG
+ perf_end(time);
+ total_time += time;
+ ++calls;
+#endif
+
+ if (res != 1) {
+ dbg_nsec3("Error calculating SHA-1 hash.\n");
+ free(data_low);
+ free(*digest);
+ return -2;
+ }
+ }
+
+ EVP_MD_CTX_cleanup(&mdctx);
+
+ dbg_nsec3("NSEC3 hashing: calls: %lu, avg time per call: %f."
+ "\n", calls, (double)(total_time) / calls);
+
+ free(data_low);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+#else
+
+int knot_nsec3_sha1(const knot_nsec3_params_t *params,
+ const uint8_t *data, size_t size, uint8_t **digest,
+ size_t *digest_size)
+{
+ if (params == NULL || digest == NULL || digest_size == NULL
+ || data == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ uint8_t *salt = params->salt;
+ uint8_t salt_length = params->salt_length;
+ uint16_t iterations = params->iterations;
+
+ dbg_nsec3("Hashing: \n");
+ dbg_nsec3(" Data: %.*s \n", size, data);
+ dbg_nsec3_hex((const char *)data, size);
+ dbg_nsec3(" (size %d)\n Iterations: %u\n", (int)size, iterations);
+ dbg_nsec3(" Salt length: %u\n", salt_length);
+ dbg_nsec3(" Salt: ");
+ if (salt_length > 0) {
+ dbg_nsec3_hex((char *)salt, salt_length);
+ dbg_nsec3("\n");
+ } else {
+ dbg_nsec3("none\n");
+ }
+
+ SHA_CTX ctx;
+
+ *digest = (uint8_t *)malloc(SHA_DIGEST_LENGTH);
+ if (*digest == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ uint8_t *data_low = knot_nsec3_to_lowercase(data, size);
+ if (data_low == NULL) {
+ free(*digest);
+ return KNOT_ENOMEM;
+ }
+
+ const uint8_t *in = data_low;
+ unsigned in_size = size;
+
+ int res = 0;
+
+ // other iterations
+ for (int i = 0; i <= iterations; ++i) {
+ SHA1_Init(&ctx);
+
+ res = SHA1_Update(&ctx, in, in_size);
+
+ if (salt_length > 0) {
+ res = SHA1_Update(&ctx, salt, salt_length);
+ }
+
+ SHA1_Final(*digest, &ctx);
+
+ in = *digest;
+ in_size = SHA_DIGEST_LENGTH;
+
+ if (res != 1) {
+ dbg_nsec3("Error calculating SHA-1 hash.\n");
+ free(data_low);
+ free(*digest);
+ return KNOT_ECRYPTO;
+ }
+ }
+
+ *digest_size = SHA_DIGEST_LENGTH;
+
+ dbg_nsec3("Hash: %.*s\n", *digest_size, *digest);
+ dbg_nsec3_hex((const char *)*digest, *digest_size);
+ dbg_nsec3("\n");
+
+ free(data_low);
+ return KNOT_EOK;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+void knot_nsec3_params_free(knot_nsec3_params_t *params)
+{
+ if (params->salt != NULL) {
+ free(params->salt);
+ }
+}
diff --git a/src/libknot/nsec3.h b/src/libknot/nsec3.h
new file mode 100644
index 0000000..0ce6899
--- /dev/null
+++ b/src/libknot/nsec3.h
@@ -0,0 +1,92 @@
+/*!
+ * \file nsec3.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Functions for calcularing NSEC3 hashes.
+ *
+ * \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_NSEC3_H_
+#define _KNOT_NSEC3_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "rrset.h"
+
+#define KNOT_NSEC3_SHA_USE_EVP 0
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure representing the NSEC3PARAM resource record.
+ */
+struct knot_nsec3_params {
+ uint8_t algorithm; /*!< Hash algorithm. */
+ uint8_t flags; /*!< Flags. */
+ uint16_t iterations; /*!< Additional iterations of the hash function.*/
+ uint8_t salt_length; /*!< Length of the salt field in bytes. */
+ uint8_t *salt; /*!< Salt used in hashing. */
+};
+
+typedef struct knot_nsec3_params knot_nsec3_params_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Initializes the NSEC3PARAM structure.
+ *
+ * \param params NSEC3PARAM structure to initialize.
+ * \param nsec3param The NSEC3PARAM RRset.
+ *
+ * \retval KNOT_EOK on success (always).
+ */
+int knot_nsec3_params_from_wire(knot_nsec3_params_t *params,
+ const knot_rrset_t *nsec3param);
+
+/*!
+ * \brief Hashes the given data using the SHA1 hash and the given parameters.
+ *
+ * \param[in] params NSEC3PARAM structure with the required parameters for
+ * hashing.
+ * \param[in] data Data to hash.
+ * \param[in] size Size of the data in bytes.
+ * \param[out] digest Result will be store here.
+ * \param[out] digest_size Size of the result in octets will be stored here.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ECRYPTO
+ */
+int knot_nsec3_sha1(const knot_nsec3_params_t *params, const uint8_t *data,
+ size_t size, uint8_t **digest, size_t *digest_size);
+
+/*!
+ * \brief Properly cleans up (but does not deallocate) the NSEC3PARAM structure.
+ *
+ * \param params NSEC3PARAMS structure to clean up.
+ */
+void knot_nsec3_params_free(knot_nsec3_params_t *params);
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* _KNOT_NSEC3_H_ */
+
+/*! @} */
diff --git a/src/libknot/packet/packet.c b/src/libknot/packet/packet.c
new file mode 100644
index 0000000..82e818f
--- /dev/null
+++ b/src/libknot/packet/packet.c
@@ -0,0 +1,1532 @@
+/* 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 "packet/packet.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common.h"
+#include "util/descriptor.h"
+#include "util/wire.h"
+
+/*----------------------------------------------------------------------------*/
+
+#define DEFAULT_RRCOUNT_QUERY(type) DEFAULT_##type##COUNT_QUERY
+#define DEFAULT_RRCOUNT(type) DEFAULT_##type##COUNT
+
+#define DEFAULT_RRSET_COUNT(type, packet) \
+ ((packet->prealloc_type == KNOT_PACKET_PREALLOC_NONE) \
+ ? 0 \
+ : (packet->prealloc_type == KNOT_PACKET_PREALLOC_QUERY) \
+ ? DEFAULT_##type##_QUERY \
+ : DEFAULT_##type)
+
+
+
+typedef enum {
+ KNOT_PACKET_DUPL_IGNORE,
+ KNOT_PACKET_DUPL_SKIP,
+ KNOT_PACKET_DUPL_MERGE
+} knot_packet_duplicate_handling_t;
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets all the pointers in the packet structure to the respective
+ * parts of the pre-allocated space.
+ */
+static void knot_packet_init_pointers_response(knot_packet_t *pkt)
+{
+ dbg_packet("Packet pointer: %p\n", pkt);
+
+ char *pos = (char *)pkt + PREALLOC_PACKET;
+
+ // put QNAME directly after the structure
+ pkt->question.qname = (knot_dname_t *)pos;
+ pos += PREALLOC_QNAME_DNAME;
+
+ dbg_packet("QNAME: %p\n", pkt->question.qname);
+
+ pkt->question.qname->name = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_NAME;
+ pkt->question.qname->labels = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_LABELS;
+
+ pkt->owner_tmp = (uint8_t *)pos;
+ dbg_packet("Tmp owner: %p\n", pkt->owner_tmp);
+ pos += PREALLOC_RR_OWNER;
+
+ // then answer, authority and additional sections
+ if (DEFAULT_ANCOUNT == 0) {
+ pkt->answer = NULL;
+ } else {
+ pkt->answer = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ANCOUNT * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_NSCOUNT == 0) {
+ pkt->authority = NULL;
+ } else {
+ pkt->authority = (const knot_rrset_t **)pos;
+ pos += DEFAULT_NSCOUNT * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_ARCOUNT == 0) {
+ pkt->additional = NULL;
+ } else {
+ pkt->additional = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ARCOUNT * sizeof(const knot_rrset_t *);
+ }
+
+ dbg_packet("Answer section: %p\n", pkt->answer);
+ dbg_packet("Authority section: %p\n", pkt->authority);
+ dbg_packet("Additional section: %p\n", pkt->additional);
+
+ pkt->max_an_rrsets = DEFAULT_ANCOUNT;
+ pkt->max_ns_rrsets = DEFAULT_NSCOUNT;
+ pkt->max_ar_rrsets = DEFAULT_ARCOUNT;
+
+ // then domain names for compression and offsets
+ pkt->compression.dnames = (const knot_dname_t **)pos;
+ pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(const knot_dname_t *);
+ pkt->compression.offsets = (size_t *)pos;
+ pos += DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t);
+
+ dbg_packet("Compression dnames: %p\n", pkt->compression.dnames);
+ dbg_packet("Compression offsets: %p\n", pkt->compression.offsets);
+
+ pkt->compression.max = DEFAULT_DOMAINS_IN_RESPONSE;
+
+ pkt->tmp_rrsets = (const knot_rrset_t **)pos;
+ pos += DEFAULT_TMP_RRSETS * sizeof(const knot_rrset_t *);
+
+ dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+
+ pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS;
+
+ assert((char *)pos == (char *)pkt + PREALLOC_RESPONSE);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Sets all the pointers in the packet structure to the respective
+ * parts of the pre-allocated space.
+ */
+static void knot_packet_init_pointers_query(knot_packet_t *pkt)
+{
+ dbg_packet("Packet pointer: %p\n", pkt);
+
+ char *pos = (char *)pkt + PREALLOC_PACKET;
+
+ // put QNAME directly after the structure
+ pkt->question.qname = (knot_dname_t *)pos;
+ pos += PREALLOC_QNAME_DNAME;
+
+ dbg_packet("QNAME: %p (%zu after start of packet)\n",
+ pkt->question.qname,
+ (void *)pkt->question.qname - (void *)pkt);
+
+ pkt->question.qname->name = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_NAME;
+ pkt->question.qname->labels = (uint8_t *)pos;
+ pos += PREALLOC_QNAME_LABELS;
+
+// pkt->owner_tmp = (uint8_t *)((char *)pkt->question.qname->labels
+// + PREALLOC_QNAME_LABELS);
+
+ // then answer, authority and additional sections
+ if (DEFAULT_ANCOUNT_QUERY == 0) {
+ pkt->answer = NULL;
+ } else {
+ pkt->answer = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ANCOUNT_QUERY * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_NSCOUNT_QUERY == 0) {
+ pkt->authority = NULL;
+ } else {
+ pkt->authority = (const knot_rrset_t **)pos;
+ pos += DEFAULT_NSCOUNT_QUERY * sizeof(const knot_rrset_t *);
+ }
+
+ if (DEFAULT_ARCOUNT_QUERY == 0) {
+ pkt->additional = NULL;
+ } else {
+ pkt->additional = (const knot_rrset_t **)pos;
+ pos += DEFAULT_ARCOUNT_QUERY * sizeof(const knot_rrset_t *);
+ }
+
+ dbg_packet("Answer section: %p\n", pkt->answer);
+ dbg_packet("Authority section: %p\n", pkt->authority);
+ dbg_packet("Additional section: %p\n", pkt->additional);
+
+ pkt->max_an_rrsets = DEFAULT_ANCOUNT_QUERY;
+ pkt->max_ns_rrsets = DEFAULT_NSCOUNT_QUERY;
+ pkt->max_ar_rrsets = DEFAULT_ARCOUNT_QUERY;
+
+ pkt->tmp_rrsets = (const knot_rrset_t **)pos;
+ pos += DEFAULT_TMP_RRSETS_QUERY * sizeof(const knot_rrset_t *);
+
+ dbg_packet("Tmp rrsets: %p\n", pkt->tmp_rrsets);
+
+ pkt->tmp_rrsets_max = DEFAULT_TMP_RRSETS_QUERY;
+
+// dbg_packet("End of data: %p (%zu after start of packet)\n",
+// pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY,
+// (void *)(pkt->tmp_rrsets + DEFAULT_TMP_RRSETS_QUERY)
+// - (void *)pkt);
+ dbg_packet("Allocated total: %u\n", PREALLOC_QUERY);
+
+ assert(pos == (char *)pkt + PREALLOC_QUERY);
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Parses DNS header from the wire format.
+ *
+ * \note This function also adjusts the position (\a pos) and size of remaining
+ * bytes in the wire format (\a remaining) according to what was parsed
+ * (though it actually always parses the 12 bytes of the header).
+ *
+ * \param[in,out] pos Wire format to parse the header from.
+ * \param[in,out] remaining Remaining size of the wire format.
+ * \param[out] header Header structure to fill in.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EFEWDATA
+ */
+static int knot_packet_parse_header(const uint8_t *wire, size_t *pos,
+ size_t size, knot_header_t *header)
+{
+ assert(wire != NULL);
+ assert(pos != NULL);
+ assert(header != NULL);
+
+ if (size - *pos < KNOT_WIRE_HEADER_SIZE) {
+ dbg_response("Not enough data to parse header.\n");
+ return KNOT_EFEWDATA;
+ }
+
+ header->id = knot_wire_get_id(wire);
+ // copy some of the flags: OPCODE and RD
+ // do this by copying flags1 and setting QR to 1, AA to 0 and TC to 0
+ header->flags1 = knot_wire_get_flags1(wire);
+// knot_wire_flags_set_qr(&header->flags1);
+// knot_wire_flags_clear_aa(&header->flags1);
+// knot_wire_flags_clear_tc(&header->flags1);
+ // do not copy flags2 (all set by server)
+ header->qdcount = knot_wire_get_qdcount(wire);
+ header->ancount = knot_wire_get_ancount(wire);
+ header->nscount = knot_wire_get_nscount(wire);
+ header->arcount = knot_wire_get_arcount(wire);
+
+ *pos += KNOT_WIRE_HEADER_SIZE;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Parses DNS Question entry from the wire format.
+ *
+ * \note This function also adjusts the position (\a pos) and size of remaining
+ * bytes in the wire format (\a remaining) according to what was parsed.
+ *
+ * \param[in,out] pos Wire format to parse the Question from.
+ * \param[in,out] remaining Remaining size of the wire format.
+ * \param[out] question DNS Question structure to be filled.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EFEWDATA
+ * \retval KNOT_ENOMEM
+ */
+static int knot_packet_parse_question(const uint8_t *wire, size_t *pos,
+ size_t size,
+ knot_question_t *question, int alloc)
+{
+ assert(pos != NULL);
+ assert(wire != NULL);
+ assert(question != NULL);
+
+ if (size - *pos < KNOT_WIRE_QUESTION_MIN_SIZE) {
+ dbg_response("Not enough data to parse question.\n");
+ return KNOT_EFEWDATA; // malformed
+ }
+
+ dbg_response("Parsing Question starting on position %zu.\n",
+ *pos);
+
+ // domain name must end with 0, so just search for 0
+ int i = *pos;
+ while (i < size && wire[i] != 0) {
+ ++i;
+ }
+
+ if (size - i - 1 < 4) {
+ dbg_response("Not enough data to parse question.\n");
+ return KNOT_EFEWDATA; // no 0 found or not enough data left
+ }
+
+ dbg_response("Parsing dname starting on position %zu and "
+ "%zu bytes long.\n", *pos, i - *pos + 1);
+ dbg_response("Alloc: %d\n", alloc);
+ if (alloc) {
+ question->qname = knot_dname_new_from_wire(
+ wire + *pos, i - *pos + 1, NULL);
+ if (question->qname == NULL) {
+ return KNOT_ENOMEM;
+ }
+ } else {
+ int res = knot_dname_from_wire(wire + *pos, i - *pos + 1,
+ NULL, question->qname);
+ if (res != KNOT_EOK) {
+ assert(res != KNOT_EBADARG);
+ return res;
+ }
+ }
+
+ *pos = i + 1;
+ question->qtype = knot_wire_read_u16(wire + i + 1);
+ //*pos += 2;
+ question->qclass = knot_wire_read_u16(wire + i + 3);
+ //*pos += 2;
+
+ *pos += 4;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Reallocate space for RRSets.
+ *
+ * \param rrsets Space for RRSets.
+ * \param max_count Size of the space available for the RRSets.
+ * \param default_max_count Size of the space pre-allocated for the RRSets when
+ * the response structure was initialized.
+ * \param step How much the space should be increased.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_packet_realloc_rrsets(const knot_rrset_t ***rrsets,
+ short *max_count,
+ short default_max_count, short step)
+{
+ dbg_packet("Max count: %d, default max count: %d\n",
+ *max_count, default_max_count);
+ int free_old = (*max_count) != default_max_count;
+ const knot_rrset_t **old = *rrsets;
+
+ short new_max_count = *max_count + step;
+ const knot_rrset_t **new_rrsets = (const knot_rrset_t **)malloc(
+ new_max_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(new_rrsets, KNOT_ENOMEM);
+
+ memcpy(new_rrsets, *rrsets, (*max_count) * sizeof(knot_rrset_t *));
+
+ *rrsets = new_rrsets;
+ *max_count = new_max_count;
+
+ if (free_old) {
+ free(old);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rdata_t *knot_packet_parse_rdata(const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc)
+{
+// if (desc->type == 0) {
+// dbg_packet("Unknown RR type.\n");
+// return NULL;
+// }
+
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (rdata == NULL) {
+ return NULL;
+ }
+
+ int rc = knot_rdata_from_wire(rdata, wire, pos, total_size, rdlength,
+ desc);
+
+ if (rc != KNOT_EOK) {
+ dbg_packet("rdata_from_wire() returned: %s\n",
+ knot_strerror(rc));
+ knot_rdata_free(&rdata);
+ return NULL;
+ }
+
+ return rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rrset_t *knot_packet_parse_rr(const uint8_t *wire, size_t *pos,
+ size_t size)
+{
+// knot_rrset_t *rrset =
+// (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
+// CHECK_ALLOC_LOG(rrset, NULL);
+
+ dbg_packet("Parsing RR from position: %zu, total size: %zu\n",
+ *pos, size);
+
+ knot_dname_t *owner = knot_dname_parse_from_wire(wire, pos, size,
+ NULL);
+ dbg_packet("Created owner: %p, actual position: %zu\n", owner,
+ *pos);
+ if (owner == NULL) {
+ return NULL;
+ }
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str(owner);
+ dbg_packet("Parsed name: %s\n", name);
+ free(name);
+);
+
+ //*remaining -= knot_dname_size(rrset->owner);
+
+ /*! @todo Get rid of the numerical constant. */
+ if (size - *pos < 10) {
+ dbg_packet("Malformed RR: Not enough data to parse RR"
+ " header.\n");
+ knot_dname_release(owner);
+ return NULL;
+ }
+
+ dbg_packet("Reading type from position %zu\n", *pos);
+
+ uint16_t type = knot_wire_read_u16(wire + *pos);
+ uint16_t rclass = knot_wire_read_u16(wire + *pos + 2);
+ uint32_t ttl = knot_wire_read_u32(wire + *pos + 4);
+
+ knot_rrset_t *rrset = knot_rrset_new(owner, type, rclass, ttl);
+
+ /* Owner is either referenced in rrset or rrset creation failed. */
+ knot_dname_release(owner);
+
+ /* Check rrset allocation. */
+ if (rrset == NULL) {
+ return NULL;
+ }
+
+ uint16_t rdlength = knot_wire_read_u16(wire + *pos + 8);
+
+ dbg_packet("Read RR header: type %u, class %u, ttl %u, "
+ "rdlength %u\n", rrset->type, rrset->rclass,
+ rrset->ttl, rdlength);
+
+ *pos += 10;
+
+ if (size - *pos < rdlength) {
+ dbg_packet("Malformed RR: Not enough data to parse RR"
+ " RDATA (size: %zu, position: %zu).\n",
+ size, *pos);
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ rrset->rrsigs = NULL;
+
+ if (rdlength == 0) {
+ return rrset;
+ }
+
+ // parse RDATA
+ knot_rdata_t *rdata = knot_packet_parse_rdata(wire, pos, size,
+ rdlength,
+ knot_rrtype_descriptor_by_type(rrset->type));
+ if (rdata == NULL) {
+ dbg_packet("Malformed RR: Could not parse RDATA.\n");
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ if (knot_rrset_add_rdata(rrset, rdata) != KNOT_EOK) {
+ dbg_packet("Malformed RR: Could not add RDATA to RRSet"
+ ".\n");
+ knot_rdata_free(&rdata);
+ knot_rrset_deep_free(&rrset, 1, 1, 0);
+// free(rrset);
+ return NULL;
+ }
+
+ return rrset;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_add_rrset(knot_rrset_t *rrset,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count,
+ short *max_rrsets,
+ short default_rrsets,
+ const knot_packet_t *packet,
+ knot_packet_duplicate_handling_t dupl)
+{
+
+ assert(rrset != NULL);
+ assert(rrsets != NULL);
+ assert(rrset_count != NULL);
+ assert(max_rrsets != NULL);
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_packet("packet_add_rrset(), owner: %s, type: %s\n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+);
+
+ if (*rrset_count == *max_rrsets
+ && knot_packet_realloc_rrsets(rrsets, max_rrsets, default_rrsets,
+ STEP_ANCOUNT) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ if (dupl == KNOT_PACKET_DUPL_SKIP &&
+ knot_packet_contains(packet, rrset, KNOT_RRSET_COMPARE_PTR)) {
+ /*! \todo This should also return > 0, as it means that the
+ RRSet was not used actually. */
+ return KNOT_EOK;
+ }
+
+ if (dupl == KNOT_PACKET_DUPL_MERGE) {
+ // try to find the RRSet in this array of RRSets
+ for (int i = 0; i < *rrset_count; ++i) {
+
+dbg_packet_exec(
+ char *name = knot_dname_to_str((*rrsets)[i]->owner);
+ dbg_packet("Comparing to RRSet: owner: %s, "
+ "type: %s\n", name,
+ knot_rrtype_to_string(
+ (*rrsets)[i]->type));
+ free(name);
+);
+
+ if (knot_rrset_compare((*rrsets)[i], rrset,
+ KNOT_RRSET_COMPARE_HEADER)) {
+ //const knot_rrset_t *r = (*rrsets)
+ /*! \todo Test this!!! */
+ int rc = knot_rrset_merge(
+ (void **)((*rrsets) + i), (void **)&rrset);
+ if (rc != KNOT_EOK) {
+ return rc;
+ }
+ return 1;
+ }
+ }
+ }
+
+ (*rrsets)[*rrset_count] = rrset;
+ ++(*rrset_count);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_parse_rrs(const uint8_t *wire, size_t *pos,
+ size_t size, uint16_t rr_count,
+ const knot_rrset_t ***rrsets,
+ short *rrset_count, short *max_rrsets,
+ short default_rrsets,
+ knot_packet_t *packet)
+{
+ assert(pos != NULL);
+ assert(wire != NULL);
+ assert(rrsets != NULL);
+ assert(rrset_count != NULL);
+ assert(max_rrsets != NULL);
+ assert(packet != NULL);
+
+ dbg_packet("Parsing RRSets starting on position: %zu\n",
+ *pos);
+
+// if (*rrsets == NULL) {
+// knot_packet_realloc_rrsets(rrsets, max_rrsets, 0, 1);
+// }
+
+ /*
+ * The RRs from one RRSet may be scattered in the current section.
+ * We must parse all RRs separately and try to add them to already
+ * parsed RRSets.
+ */
+ int err = KNOT_EOK;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < rr_count; ++i) {
+ rrset = knot_packet_parse_rr(wire, pos, size);
+ if (rrset == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ err = KNOT_EMALF;
+ break;
+ }
+
+ err = knot_packet_add_rrset(rrset, rrsets, rrset_count,
+ max_rrsets, default_rrsets, packet,
+ KNOT_PACKET_DUPL_MERGE);
+ if (err < 0) {
+ break;
+ } else if (err > 0) { // merged
+ dbg_packet("RRSet merged, freeing.\n");
+ knot_rrset_deep_free(&rrset, 1, 0, 0); // TODO: ok??
+ continue;
+ }
+
+ err = knot_packet_add_tmp_rrset(packet, rrset);
+ if (err != KNOT_EOK) {
+ // remove the last RRSet from the list of RRSets
+ // - just decrement the count
+ --(*rrset_count);
+ knot_rrset_deep_free(&rrset, 1, 1, 1);
+ break;
+ }
+ }
+
+ return (err < 0) ? err : KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Deallocates all space which was allocated additionally to the
+ * pre-allocated space of the response structure.
+ *
+ * \param resp Response structure that holds pointers to the allocated space.
+ */
+static void knot_packet_free_allocated_space(knot_packet_t *pkt)
+{
+ dbg_packet("Freeing additional space in packet.\n");
+ if (pkt->prealloc_type == KNOT_PACKET_PREALLOC_NONE) {
+ dbg_packet("Freeing QNAME.\n");
+ knot_dname_release(pkt->question.qname);
+ }
+
+ if (pkt->max_an_rrsets > DEFAULT_RRSET_COUNT(ANCOUNT, pkt)) {
+ free(pkt->answer);
+ }
+ if (pkt->max_ns_rrsets > DEFAULT_RRSET_COUNT(NSCOUNT, pkt)) {
+ free(pkt->authority);
+ }
+ if (pkt->max_ar_rrsets > DEFAULT_RRSET_COUNT(ARCOUNT, pkt)) {
+ free(pkt->additional);
+ }
+
+ if (pkt->compression.max > DEFAULT_DOMAINS_IN_RESPONSE) {
+ free(pkt->compression.dnames);
+ free(pkt->compression.offsets);
+ }
+
+ if (pkt->tmp_rrsets_max > DEFAULT_RRSET_COUNT(TMP_RRSETS, pkt)) {
+ free(pkt->tmp_rrsets);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_packet_parse_rr_sections(knot_packet_t *packet,
+ size_t *pos)
+{
+ assert(packet != NULL);
+ assert(packet->wireformat != NULL);
+ assert(packet->size > 0);
+ assert(pos != NULL);
+ assert(*pos > 0);
+
+ int err;
+
+ dbg_packet("Parsing Answer RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.ancount, &packet->answer,
+ &packet->an_rrsets, &packet->max_an_rrsets,
+ DEFAULT_RRSET_COUNT(ANCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Parsing Authority RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.nscount, &packet->authority,
+ &packet->ns_rrsets, &packet->max_ns_rrsets,
+ DEFAULT_RRSET_COUNT(NSCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Parsing Additional RRs...\n");
+ if ((err = knot_packet_parse_rrs(packet->wireformat, pos,
+ packet->size, packet->header.arcount, &packet->additional,
+ &packet->ar_rrsets, &packet->max_ar_rrsets,
+ DEFAULT_RRSET_COUNT(ARCOUNT, packet), packet)) != KNOT_EOK) {
+ return err;
+ }
+
+ dbg_packet("Trying to find OPT RR in the packet.\n");
+
+ for (int i = 0; i < packet->ar_rrsets; ++i) {
+ assert(packet->additional[i] != NULL);
+ if (knot_rrset_type(packet->additional[i])
+ == KNOT_RRTYPE_OPT) {
+ dbg_packet("Found OPT RR, filling.\n");
+ err = knot_edns_new_from_rr(&packet->opt_rr,
+ packet->additional[i]);
+ if (err != KNOT_EOK) {
+ return err;
+ }
+ break;
+ }
+ }
+
+ packet->parsed = *pos;
+
+ if (*pos < packet->size) {
+ // some trailing garbage; ignore, but log
+ dbg_response("Packet: %zu bytes of trailing garbage "
+ "in packet.\n", packet->size - (*pos));
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc)
+{
+ knot_packet_t *pkt;
+ void (*init_pointers)(knot_packet_t *pkt) = NULL;
+ size_t size = 0;
+
+ switch (prealloc) {
+ case KNOT_PACKET_PREALLOC_NONE:
+ size = sizeof(knot_packet_t);
+ break;
+ case KNOT_PACKET_PREALLOC_QUERY:
+ size = PREALLOC_QUERY;
+ init_pointers = knot_packet_init_pointers_query;
+ break;
+ case KNOT_PACKET_PREALLOC_RESPONSE:
+ size = PREALLOC_RESPONSE;
+ init_pointers = knot_packet_init_pointers_response;
+ break;
+ }
+
+ pkt = (knot_packet_t *)malloc(size);
+ CHECK_ALLOC_LOG(pkt, NULL);
+ memset(pkt, 0, size);
+ if (init_pointers != NULL) {
+ init_pointers(pkt);
+ }
+
+ pkt->prealloc_type = prealloc;
+
+ // set EDNS version to not supported
+ pkt->opt_rr.version = EDNS_NOT_SUPPORTED;
+
+ return pkt;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_from_wire(knot_packet_t *packet,
+ const uint8_t *wireformat, size_t size,
+ int question_only)
+{
+ if (packet == NULL || wireformat == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int err;
+
+ // save the wireformat in the packet
+ // TODO: can we just save the pointer, or we have to copy the data??
+ assert(packet->wireformat == NULL);
+ packet->wireformat = (uint8_t*)wireformat;
+ packet->size = size;
+ packet->free_wireformat = 0;
+
+ //uint8_t *pos = wireformat;
+ size_t pos = 0;
+ //size_t remaining = size;
+
+ dbg_packet("Parsing wire format of packet (size %zu).\nHeader\n",
+ size);
+ if ((err = knot_packet_parse_header(wireformat, &pos, size,
+ &packet->header)) != KNOT_EOK) {
+ return err;
+ }
+
+ packet->parsed = pos;
+
+ dbg_packet("Question (prealloc type: %d)...\n", packet->prealloc_type);
+
+ if (packet->header.qdcount > 1) {
+ dbg_packet("QDCOUNT larger than 1, FORMERR.\n");
+ return KNOT_EMALF;
+ }
+
+ knot_packet_dump(packet);
+
+ if (packet->header.qdcount == 1) {
+ if ((err = knot_packet_parse_question(wireformat, &pos, size,
+ &packet->question, packet->prealloc_type
+ == KNOT_PACKET_PREALLOC_NONE)
+ ) != KNOT_EOK) {
+ return err;
+ }
+ packet->parsed = pos;
+ }
+
+ knot_packet_dump(packet);
+
+ if (question_only) {
+ return KNOT_EOK;
+ }
+
+ /*! \todo Replace by call to parse_rest()? */
+ err = knot_packet_parse_rr_sections(packet, &pos);
+
+#ifdef KNOT_PACKET_DEBUG
+ knot_packet_dump(packet);
+#endif /* KNOT_RESPONSE_DEBUG */
+
+ return err;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_rest(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+// if (packet->parsed >= packet->size) {
+// return KNOT_EOK;
+// }
+
+ if (packet->parsed == packet->size) {
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ return knot_packet_parse_rr_sections(packet, &pos);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
+ knot_rrset_t **rr)
+{
+ if (packet == NULL || rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *rr = NULL;
+
+ if (packet->parsed >= packet->size) {
+ assert(packet->an_rrsets <= packet->header.ancount);
+ if (packet->an_rrsets != packet->header.ancount) {
+ dbg_packet("Parsed less RRs than expected.\n");
+ return KNOT_EMALF;
+ } else {
+ dbg_packet("Whole packet parsed\n");
+ return KNOT_EOK;
+ }
+ }
+
+ if (packet->an_rrsets == packet->header.ancount) {
+ assert(packet->parsed < packet->size);
+ //dbg_packet("Trailing garbage, ignoring...\n");
+ // there may be other data in the packet
+ // (authority or additional).
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ dbg_packet("Parsing next Answer RR (pos: %zu)...\n", pos);
+ *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
+ if (*rr == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet("Parsed. Pos: %zu.\n", pos);
+
+ packet->parsed = pos;
+ // increment the number of answer RRSets, though there are no saved
+ // in the packet; it is OK, because packet->answer is NULL
+ ++packet->an_rrsets;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
+ knot_rrset_t **rr)
+{
+ /*! \todo Implement. */
+ if (packet == NULL || rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *rr = NULL;
+
+ if (packet->parsed >= packet->size) {
+ assert(packet->ar_rrsets <= packet->header.arcount);
+ if (packet->ar_rrsets != packet->header.arcount) {
+ dbg_packet("Parsed less RRs than expected.\n");
+ return KNOT_EMALF;
+ } else {
+ dbg_packet("Whole packet parsed\n");
+ return KNOT_EOK;
+ }
+ }
+
+ if (packet->ar_rrsets == packet->header.arcount) {
+ assert(packet->parsed < packet->size);
+ dbg_packet("Trailing garbage, ignoring...\n");
+ /*! \todo Do not ignore. */
+ return KNOT_EOK;
+ }
+
+ size_t pos = packet->parsed;
+
+ dbg_packet("Parsing next Additional RR (pos: %zu)...\n", pos);
+ *rr = knot_packet_parse_rr(packet->wireformat, &pos, packet->size);
+ if (*rr == NULL) {
+ dbg_packet("Failed to parse RR!\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_packet("Parsed. Pos: %zu.\n", pos);
+
+ packet->parsed = pos;
+ // increment the number of answer RRSets, though there are no saved
+ // in the packet; it is OK, because packet->answer is NULL
+ ++packet->ar_rrsets;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_size(const knot_packet_t *packet)
+{
+ return packet->size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_question_size(const knot_packet_t *packet)
+{
+ return (KNOT_WIRE_HEADER_SIZE + 4
+ + knot_dname_size(packet->question.qname));
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_packet_parsed(const knot_packet_t *packet)
+{
+ return packet->parsed;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_set_max_size(knot_packet_t *packet, int max_size)
+{
+ if (packet == NULL || max_size <= 0) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->max_size < max_size) {
+ // reallocate space for the wire format (and copy anything
+ // that might have been there before
+ uint8_t *wire_new = (uint8_t *)malloc(max_size);
+ if (wire_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ uint8_t *wire_old = packet->wireformat;
+
+ memcpy(wire_new, packet->wireformat, packet->max_size);
+ packet->wireformat = wire_new;
+
+ if (packet->max_size > 0 && packet->free_wireformat) {
+ free(wire_old);
+ }
+
+ packet->free_wireformat = 1;
+ }
+
+ // set max size
+ packet->max_size = max_size;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_id(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->header.id;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_id(knot_packet_t *packet, uint16_t id)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ packet->header.id = id;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_random_id(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ packet->header.id = knot_random_id();
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint8_t knot_packet_opcode(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return knot_wire_flags_get_opcode(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_packet_qname(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ return packet->question.qname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_qtype(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->question.qtype;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_qtype(knot_packet_t *packet, knot_rr_type_t qtype)
+{
+ assert(packet != NULL);
+ packet->question.qtype = qtype;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_packet_qclass(const knot_packet_t *packet)
+{
+ assert(packet != NULL);
+ return packet->question.qclass;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_is_query(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return (knot_wire_flags_get_qr(packet->header.flags1) == 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_packet_t *knot_packet_query(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ return packet->query;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_rcode(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_wire_flags_get_rcode(packet->header.flags2);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_tc(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_wire_flags_get_tc(packet->header.flags1);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_qdcount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.qdcount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_ancount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.ancount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_nscount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.nscount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_arcount(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->header.arcount;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size)
+{
+ packet->tsig_size = tsig_size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_answer_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->an_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_authority_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->ns_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+short knot_packet_additional_rrset_count(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return packet->ar_rrsets;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_answer_rrset(
+ const knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->an_rrsets) {
+ return NULL;
+ }
+
+ return packet->answer[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_authority_rrset(
+ knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->ns_rrsets) {
+ return NULL;
+ }
+
+ return packet->authority[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_packet_additional_rrset(
+ knot_packet_t *packet, short pos)
+{
+ if (packet == NULL || pos > packet->ar_rrsets) {
+ return NULL;
+ }
+
+ return packet->additional[pos];
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_contains(const knot_packet_t *packet,
+ const knot_rrset_t *rrset,
+ knot_rrset_compare_type_t cmp)
+{
+ if (packet == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ for (int i = 0; i < packet->header.ancount; ++i) {
+ if (knot_rrset_compare(packet->answer[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < packet->header.nscount; ++i) {
+ if (knot_rrset_compare(packet->authority[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < packet->header.arcount; ++i) {
+ if (knot_rrset_compare(packet->additional[i], rrset, cmp)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_add_tmp_rrset(knot_packet_t *packet,
+ knot_rrset_t *tmp_rrset)
+{
+ if (packet == NULL || tmp_rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->tmp_rrsets_count == packet->tmp_rrsets_max
+ && knot_packet_realloc_rrsets(&packet->tmp_rrsets,
+ &packet->tmp_rrsets_max,
+ DEFAULT_RRSET_COUNT(TMP_RRSETS, packet),
+ STEP_TMP_RRSETS) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ packet->tmp_rrsets[packet->tmp_rrsets_count++] = tmp_rrset;
+ dbg_packet("Current tmp RRSets count: %d, max count: %d\n",
+ packet->tmp_rrsets_count, packet->tmp_rrsets_max);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Frees all temporary RRSets stored in the response structure.
+ *
+ * \param resp Response structure to free the temporary RRSets from.
+ */
+void knot_packet_free_tmp_rrsets(knot_packet_t *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < pkt->tmp_rrsets_count; ++i) {
+dbg_packet_exec(
+ char *name = knot_dname_to_str(
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->owner);
+ dbg_packet("Freeing tmp RRSet on ptr: %p (ptr to ptr:"
+ " %p, type: %s, owner: %s)\n",
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
+ &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]),
+ knot_rrtype_to_string(
+ (((knot_rrset_t **)(pkt->tmp_rrsets))[i])->type),
+ name);
+ free(name);
+);
+ // TODO: this is quite ugly, but better than copying whole
+ // function (for reallocating rrset array)
+ knot_rrset_deep_free(
+ &(((knot_rrset_t **)(pkt->tmp_rrsets))[i]), 1, 1, 1);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_header_to_wire(const knot_header_t *header,
+ uint8_t **pos, size_t *size)
+{
+ if (header == NULL || pos == NULL || *pos == NULL || size == NULL) {
+ return;
+ }
+
+ knot_wire_set_id(*pos, header->id);
+ knot_wire_set_flags1(*pos, header->flags1);
+ knot_wire_set_flags2(*pos, header->flags2);
+ knot_wire_set_qdcount(*pos, header->qdcount);
+ knot_wire_set_ancount(*pos, header->ancount);
+ knot_wire_set_nscount(*pos, header->nscount);
+ knot_wire_set_arcount(*pos, header->arcount);
+
+ *pos += KNOT_WIRE_HEADER_SIZE;
+ *size += KNOT_WIRE_HEADER_SIZE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_question_to_wire(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (packet->size > KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ERROR;
+ }
+
+ // TODO: get rid of the numeric constants
+ size_t qsize = 4 + knot_dname_size(packet->question.qname);
+ if (qsize > packet->max_size - KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ESPACE;
+ }
+
+ // create the wireformat of Question
+ uint8_t *pos = packet->wireformat + KNOT_WIRE_HEADER_SIZE;
+ memcpy(pos, knot_dname_name(packet->question.qname),
+ knot_dname_size(packet->question.qname));
+
+ pos += knot_dname_size(packet->question.qname);
+ knot_wire_write_u16(pos, packet->question.qtype);
+ pos += 2;
+ knot_wire_write_u16(pos, packet->question.qclass);
+
+// int err = 0;
+ // TODO: put the qname into the compression table
+// // TODO: get rid of the numeric constants
+// if ((err = knot_response_store_dname_pos(&packet->compression,
+// packet->question.qname,0, 12, 12)) != KNOT_EOK) {
+// return err;
+// }
+
+ packet->size += knot_dname_size(packet->question.qname) + 4;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_edns_to_wire(knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ packet->size += knot_edns_to_wire(&packet->opt_rr,
+ packet->wireformat + packet->size,
+ packet->max_size - packet->size);
+
+ packet->header.arcount += 1;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_packet_to_wire(knot_packet_t *packet,
+ uint8_t **wire, size_t *wire_size)
+{
+ if (packet == NULL || wire == NULL || wire_size == NULL
+ || *wire != NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(packet->size <= packet->max_size);
+
+ // if there are no additional RRSets, add EDNS OPT RR
+ if (packet->header.arcount == 0
+ && packet->opt_rr.version != EDNS_NOT_SUPPORTED) {
+ knot_packet_edns_to_wire(packet);
+ }
+
+ // set QDCOUNT (in response it is already set, in query it is needed)
+ knot_wire_set_qdcount(packet->wireformat, packet->header.qdcount);
+ // set ANCOUNT to the packet
+ knot_wire_set_ancount(packet->wireformat, packet->header.ancount);
+ // set NSCOUNT to the packet
+ knot_wire_set_nscount(packet->wireformat, packet->header.nscount);
+ // set ARCOUNT to the packet
+ knot_wire_set_arcount(packet->wireformat, packet->header.arcount);
+
+ //assert(response->size == size);
+ *wire = packet->wireformat;
+ *wire_size = packet->size;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const uint8_t *knot_packet_wireformat(const knot_packet_t *packet)
+{
+ return packet->wireformat;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_free(knot_packet_t **packet)
+{
+ if (packet == NULL || *packet == NULL) {
+ return;
+ }
+
+ // free temporary domain names
+ dbg_packet("Freeing tmp RRSets...\n");
+ knot_packet_free_tmp_rrsets(*packet);
+
+ // check if some additional space was allocated for the packet
+ dbg_packet("Freeing additional allocated space...\n");
+ knot_packet_free_allocated_space(*packet);
+
+ // free the space for wireformat
+// assert((*packet)->wireformat != NULL);
+// free((*packet)->wireformat);
+ if ((*packet)->wireformat != NULL && (*packet)->free_wireformat) {
+ free((*packet)->wireformat);
+ }
+
+ dbg_packet("Freeing packet structure\n");
+ free(*packet);
+ *packet = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef KNOT_PACKET_DEBUG
+static void knot_packet_dump_rrsets(const knot_rrset_t **rrsets,
+ int count)
+{
+ assert((rrsets != NULL && *rrsets != NULL) || count < 1);
+
+ for (int i = 0; i < count; ++i) {
+ knot_rrset_dump(rrsets[i], 0);
+ }
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+void knot_packet_dump(const knot_packet_t *packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+#ifdef KNOT_PACKET_DEBUG
+ dbg_packet("DNS packet:\n-----------------------------\n");
+
+ dbg_packet("\nHeader:\n");
+ dbg_packet(" ID: %u", packet->header.id);
+ dbg_packet(" FLAGS: %s %s %s %s %s %s %s\n",
+ knot_wire_flags_get_qr(packet->header.flags1) ? "qr" : "",
+ knot_wire_flags_get_aa(packet->header.flags1) ? "aa" : "",
+ knot_wire_flags_get_tc(packet->header.flags1) ? "tc" : "",
+ knot_wire_flags_get_rd(packet->header.flags1) ? "rd" : "",
+ knot_wire_flags_get_ra(packet->header.flags2) ? "ra" : "",
+ knot_wire_flags_get_ad(packet->header.flags2) ? "ad" : "",
+ knot_wire_flags_get_cd(packet->header.flags2) ? "cd" : "");
+ dbg_packet(" QDCOUNT: %u\n", packet->header.qdcount);
+ dbg_packet(" ANCOUNT: %u\n", packet->header.ancount);
+ dbg_packet(" NSCOUNT: %u\n", packet->header.nscount);
+ dbg_packet(" ARCOUNT: %u\n", packet->header.arcount);
+
+ if (knot_packet_qdcount(packet) > 0) {
+ dbg_packet("\nQuestion:\n");
+ char *qname = knot_dname_to_str(packet->question.qname);
+ dbg_packet(" QNAME: %s\n", qname);
+ free(qname);
+ dbg_packet(" QTYPE: %u (%s)\n", packet->question.qtype,
+ knot_rrtype_to_string(packet->question.qtype));
+ dbg_packet(" QCLASS: %u (%s)\n", packet->question.qclass,
+ knot_rrclass_to_string(packet->question.qclass));
+ }
+
+ dbg_packet("\nAnswer RRSets:\n");
+ knot_packet_dump_rrsets(packet->answer, packet->an_rrsets);
+ dbg_packet("\nAuthority RRSets:\n");
+ knot_packet_dump_rrsets(packet->authority, packet->ns_rrsets);
+ dbg_packet("\nAdditional RRSets:\n");
+ knot_packet_dump_rrsets(packet->additional, packet->ar_rrsets);
+
+ /*! \todo Dumping of Answer, Authority and Additional sections. */
+
+ dbg_packet("\nEDNS:\n");
+ dbg_packet(" Version: %u\n", packet->opt_rr.version);
+ dbg_packet(" Payload: %u\n", packet->opt_rr.payload);
+ dbg_packet(" Extended RCODE: %u\n",
+ packet->opt_rr.ext_rcode);
+
+ dbg_packet("\nPacket size: %zu\n", packet->size);
+ dbg_packet("\n-----------------------------\n");
+#endif
+}
+
diff --git a/src/libknot/packet/packet.h b/src/libknot/packet/packet.h
new file mode 100644
index 0000000..1bf74a9
--- /dev/null
+++ b/src/libknot/packet/packet.h
@@ -0,0 +1,538 @@
+/*!
+ * \file packet.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure for holding DNS packet data and metadata.
+ *
+ * \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_PACKET_H_
+#define _KNOT_PACKET_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dname.h"
+#include "rrset.h"
+#include "edns.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for holding information needed for compressing domain names.
+ *
+ * It's a simple table of domain names and their offsets in wire format of the
+ * packet.
+ *
+ * \todo Consider using some better lookup structure, such as skip-list.
+ */
+struct knot_compressed_dnames {
+ const knot_dname_t **dnames; /*!< Domain names present in packet. */
+ size_t *offsets; /*!< Offsets of domain names in the packet. */
+ short count; /*!< Count of items in the previous arrays. */
+ short max; /*!< Capacity of the structure (allocated). */
+};
+
+typedef struct knot_compressed_dnames knot_compressed_dnames_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure representing the DNS packet header.
+ */
+struct knot_header {
+ uint16_t id; /*!< ID stored in host byte order. */
+ uint8_t flags1; /*!< First octet of header flags. */
+ uint8_t flags2; /*!< Second octet of header flags. */
+ uint16_t qdcount; /*!< Number of Question RRs, in host byte order. */
+ uint16_t ancount; /*!< Number of Answer RRs, in host byte order. */
+ uint16_t nscount; /*!< Number of Authority RRs, in host byte order. */
+ uint16_t arcount; /*!< Number of Additional RRs, in host byte order. */
+};
+
+typedef struct knot_header knot_header_t;
+
+/*!
+ * \brief Structure representing one Question entry in the DNS packet.
+ */
+struct knot_question {
+ knot_dname_t *qname; /*!< Question domain name. */
+ uint16_t qtype; /*!< Question TYPE. */
+ uint16_t qclass; /*!< Question CLASS. */
+};
+
+typedef struct knot_question knot_question_t;
+
+enum knot_packet_prealloc_type {
+ KNOT_PACKET_PREALLOC_NONE,
+ KNOT_PACKET_PREALLOC_QUERY,
+ KNOT_PACKET_PREALLOC_RESPONSE
+};
+
+typedef enum knot_packet_prealloc_type knot_packet_prealloc_type_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure representing a DNS packet.
+ *
+ * \note QNAME, Answer, Authority and Additonal sections are by default put to
+ * preallocated space after the structure with default sizes. If the
+ * space is not enough, more space is allocated dynamically.
+ */
+struct knot_packet {
+ /*! \brief DNS header. */
+ knot_header_t header;
+
+ /*!
+ * \brief Question section.
+ *
+ * \note Only one Question is supported!
+ */
+ knot_question_t question;
+
+ uint8_t *owner_tmp; /*!< Allocated space for RRSet owner wire format.*/
+
+ const knot_rrset_t **answer; /*!< Answer RRSets. */
+ const knot_rrset_t **authority; /*!< Authority RRSets. */
+ const knot_rrset_t **additional; /*!< Additional RRSets. */
+
+ short an_rrsets; /*!< Count of Answer RRSets in the response. */
+ short ns_rrsets; /*!< Count of Authority RRSets in the response. */
+ short ar_rrsets; /*!< Count of Additional RRSets in the response. */
+
+ short max_an_rrsets; /*!< Allocated space for Answer RRsets. */
+ short max_ns_rrsets; /*!< Allocated space for Authority RRsets. */
+ short max_ar_rrsets; /*!< Allocated space for Additional RRsets. */
+
+ knot_opt_rr_t opt_rr; /*!< OPT RR included in the packet. */
+
+ uint8_t *wireformat; /*!< Wire format of the packet. */
+
+ short free_wireformat;
+ size_t parsed;
+
+ size_t size; /*!< Current wire size of the packet. */
+ size_t max_size; /*!< Maximum allowed size of the packet. */
+
+ /*! \brief Information needed for compressing domain names in packet. */
+ knot_compressed_dnames_t compression;
+
+ /*! \brief RRSets to be destroyed with the packet structure. */
+ const knot_rrset_t **tmp_rrsets;
+ short tmp_rrsets_count; /*!< Count of temporary RRSets. */
+ short tmp_rrsets_max; /*!< Allocated space for temporary RRSets. */
+
+ struct knot_packet *query; /*!< Associated query. */
+
+ knot_packet_prealloc_type_t prealloc_type;
+
+ size_t tsig_size; /*!< Space to reserve for the TSIG RR. */
+};
+
+typedef struct knot_packet knot_packet_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Default sizes for response structure parts and steps for increasing
+ * them.
+ */
+enum {
+ DEFAULT_ANCOUNT = 6, /*!< Default count of Answer RRSets. */
+ DEFAULT_NSCOUNT = 8, /*!< Default count of Authority RRSets. */
+ DEFAULT_ARCOUNT = 28, /*!< Default count of Additional RRSets. */
+
+ DEFAULT_ANCOUNT_QUERY = 1, /*!< Default count of Answer RRSets. */
+ DEFAULT_NSCOUNT_QUERY = 0, /*!< Default count of Authority RRSets. */
+ DEFAULT_ARCOUNT_QUERY = 1, /*!< Default count of Additional RRSets. */
+ /*!
+ * \brief Default count of all domain names in response.
+ *
+ * Used for compression table.
+ */
+ DEFAULT_DOMAINS_IN_RESPONSE = 22,
+
+ /*! \brief Default count of temporary RRSets stored in response. */
+ DEFAULT_TMP_RRSETS = 5,
+
+ /*! \brief Default count of temporary RRSets stored in query. */
+ DEFAULT_TMP_RRSETS_QUERY = 2,
+
+ STEP_ANCOUNT = 6, /*!< Step for increasing space for Answer RRSets. */
+ STEP_NSCOUNT = 8, /*!< Step for increasing space for Authority RRSets.*/
+ STEP_ARCOUNT = 8,/*!< Step for increasing space for Additional RRSets.*/
+ STEP_DOMAINS = 10, /*!< Step for resizing compression table. */
+ STEP_TMP_RRSETS = 5 /*!< Step for increasing temorary RRSets count. */
+};
+
+/*----------------------------------------------------------------------------*/
+#define PREALLOC_RRSETS(count) (count * sizeof(knot_rrset_t *))
+
+/*! \brief Sizes for preallocated space in the response structure. */
+enum {
+ /*! \brief Size of the response structure itself. */
+ PREALLOC_PACKET = sizeof(knot_packet_t),
+ /*! \brief Space for QNAME dname structure. */
+ PREALLOC_QNAME_DNAME = sizeof(knot_dname_t),
+ /*! \brief Space for QNAME name (maximum domain name size). */
+ PREALLOC_QNAME_NAME = 256,
+ /*! \brief Space for QNAME labels (maximum label count). */
+ PREALLOC_QNAME_LABELS = 127,
+ /*! \brief Total space for QNAME. */
+ PREALLOC_QNAME = PREALLOC_QNAME_DNAME
+ + PREALLOC_QNAME_NAME
+ + PREALLOC_QNAME_LABELS,
+ /*!
+ * \brief Space for RR owner wire format.
+ *
+ * Temporary buffer, used when putting RRSets to the response.
+ */
+ PREALLOC_RR_OWNER = 256,
+
+// /*! \brief Space for Answer RRSets. */
+// PREALLOC_ANSWER = DEFAULT_ANCOUNT * sizeof(knot_dname_t *),
+// /*! \brief Space for Authority RRSets. */
+// PREALLOC_AUTHORITY = DEFAULT_NSCOUNT * sizeof(knot_dname_t *),
+// /*! \brief Space for Additional RRSets. */
+// PREALLOC_ADDITIONAL = DEFAULT_ARCOUNT * sizeof(knot_dname_t *),
+// /*! \brief Total size for Answer, Authority and Additional RRSets. */
+// PREALLOC_RRSETS = PREALLOC_ANSWER
+// + PREALLOC_AUTHORITY
+// + PREALLOC_ADDITIONAL,
+ /*! \brief Space for one part of the compression table (domain names).*/
+ PREALLOC_DOMAINS =
+ DEFAULT_DOMAINS_IN_RESPONSE * sizeof(knot_dname_t *),
+ /*! \brief Space for other part of the compression table (offsets). */
+ PREALLOC_OFFSETS =
+ DEFAULT_DOMAINS_IN_RESPONSE * sizeof(size_t),
+ PREALLOC_COMPRESSION = PREALLOC_DOMAINS + PREALLOC_OFFSETS,
+
+// /*! \brief Space for temporary RRSets. */
+// PREALLOC_TMP_RRSETS =
+// DEFAULT_TMP_RRSETS * sizeof(knot_rrset_t *),
+
+ PREALLOC_QUERY = PREALLOC_PACKET
+ + PREALLOC_QNAME
+ + PREALLOC_RRSETS(DEFAULT_ANCOUNT_QUERY)
+ + PREALLOC_RRSETS(DEFAULT_NSCOUNT_QUERY)
+ + PREALLOC_RRSETS(DEFAULT_ARCOUNT_QUERY)
+ + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS_QUERY),
+
+ /*! \brief Total preallocated size for the response. */
+ PREALLOC_RESPONSE = PREALLOC_PACKET
+ + PREALLOC_QNAME
+ + PREALLOC_RR_OWNER
+ + PREALLOC_RRSETS(DEFAULT_ANCOUNT)
+ + PREALLOC_RRSETS(DEFAULT_NSCOUNT)
+ + PREALLOC_RRSETS(DEFAULT_ARCOUNT)
+ + PREALLOC_COMPRESSION
+ + PREALLOC_RRSETS(DEFAULT_TMP_RRSETS)
+};
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates new empty packet structure.
+ *
+ * \param prealloc What space should be preallocated in the structure.
+ *
+ * \return New packet structure or NULL if an error occured.
+ */
+knot_packet_t *knot_packet_new(knot_packet_prealloc_type_t prealloc);
+
+/*!
+ * \brief Parses the DNS packet from wire format.
+ *
+ * \param packet Packet structure to parse into.
+ * \param wireformat Wire format of the DNS packet.
+ * \param size Size of the wire format in bytes.
+ * \param question_only Set to <> 0 if you do not want to parse the whole
+ * packet. In such case the parsing will end after the
+ * Question section. Set to 0 to parse the whole packet.
+ *
+ * \retval KNOT_EOK
+ */
+int knot_packet_parse_from_wire(knot_packet_t *packet,
+ const uint8_t *wireformat, size_t size,
+ int question_only);
+
+int knot_packet_parse_rest(knot_packet_t *packet);
+
+int knot_packet_parse_next_rr_answer(knot_packet_t *packet,
+ knot_rrset_t **rr);
+
+int knot_packet_parse_next_rr_additional(knot_packet_t *packet,
+ knot_rrset_t **rr);
+
+size_t knot_packet_size(const knot_packet_t *packet);
+
+/*! \brief Returns size of the wireformat of Header and Question sections. */
+size_t knot_packet_question_size(const knot_packet_t *packet);
+
+size_t knot_packet_parsed(const knot_packet_t *packet);
+
+/*!
+ * \brief Sets the maximum size of the packet and allocates space for wire
+ * format (if needed).
+ *
+ * This function also allocates space for the wireformat of the packet, if
+ * the given max size is larger than the current maximum size of the packet
+ * and copies the current wireformat over to the new space.
+ *
+ * \warning Do not call this function if you are not completely sure that the
+ * current wire format of the packet fits into the new space.
+ * It does not update the current size of the wire format, so the
+ * produced packet may be larger than the given max size.
+ *
+ * \param packet Packet to set the maximum size of.
+ * \param max_size Maximum size of the packet in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ *
+ * \todo Needs test.
+ */
+int knot_packet_set_max_size(knot_packet_t *packet, int max_size);
+
+uint16_t knot_packet_id(const knot_packet_t *packet);
+
+void knot_packet_set_id(knot_packet_t *packet, uint16_t id);
+
+void knot_packet_set_random_id(knot_packet_t *packet);
+
+/*!
+ * \brief Returns the OPCODE of the packet.
+ *
+ * \param packet Packet (with parsed query) to get the OPCODE from.
+ *
+ * \return OPCODE stored in the packet.
+ */
+uint8_t knot_packet_opcode(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns the QNAME from the packet.
+ *
+ * \param packet Packet (with parsed query) to get the QNAME from.
+ *
+ * \return QNAME stored in the packet.
+ */
+const knot_dname_t *knot_packet_qname(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns the QTYPE from the packet.
+ *
+ * \param packet Packet (with parsed query) to get the QTYPE from.
+ *
+ * \return QTYPE stored in the packet.
+ */
+uint16_t knot_packet_qtype(const knot_packet_t *packet);
+
+/*!
+ * \brief Set the QTYPE of the packet.
+ *
+ * \param packet Packet containing question.
+ * \param qtype New QTYPE for question.
+ */
+void knot_packet_set_qtype(knot_packet_t *packet, knot_rr_type_t qtype);
+
+
+/*!
+ * \brief Returns the QCLASS from the packet.
+ *
+ * \param response Packet (with parsed query) to get the QCLASS from.
+ *
+ * \return QCLASS stored in the packet.
+ */
+uint16_t knot_packet_qclass(const knot_packet_t *packet);
+
+int knot_packet_is_query(const knot_packet_t *packet);
+
+const knot_packet_t *knot_packet_query(const knot_packet_t *packet);
+
+int knot_packet_rcode(const knot_packet_t *packet);
+
+int knot_packet_tc(const knot_packet_t *packet);
+
+int knot_packet_qdcount(const knot_packet_t *packet);
+
+int knot_packet_ancount(const knot_packet_t *packet);
+
+int knot_packet_nscount(const knot_packet_t *packet);
+
+int knot_packet_arcount(const knot_packet_t *packet);
+
+void knot_packet_set_tsig_size(knot_packet_t *packet, size_t tsig_size);
+
+/*!
+ * \brief Returns number of RRSets in Answer section of the packet.
+ *
+ * \param response Packet to get the Answer RRSet count from.
+ */
+short knot_packet_answer_rrset_count(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Authority section of the packet.
+ *
+ * \param response Packet to get the Authority RRSet count from.
+ */
+short knot_packet_authority_rrset_count(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns number of RRSets in Additional section of the packet.
+ *
+ * \param response Packet to get the Additional RRSet count from.
+ */
+short knot_packet_additional_rrset_count(const knot_packet_t *packet);
+
+/*!
+ * \brief Returns the requested Answer RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Answer section (RRSets are stored
+ * in the order they were added to the response or parsed from the
+ * query).
+ *
+ * \return The RRSet on position \a pos in the Answer section of \a packet
+ * or NULL if there is no such RRSet.
+ */
+const knot_rrset_t *knot_packet_answer_rrset(
+ const knot_packet_t *packet, short pos);
+
+/*!
+ * \brief Returns the requested Authority RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Authority section (RRSets are stored
+ * in the order they were added to the response or parsed from the
+ * query).
+ *
+ * \return The RRSet on position \a pos in the Authority section of \a packet
+ * or NULL if there is no such RRSet.
+ */
+const knot_rrset_t *knot_packet_authority_rrset(
+ knot_packet_t *packet, short pos);
+
+/*!
+ * \brief Returns the requested Additional RRset.
+ *
+ * \param packet Packet to get the RRSet from.
+ * \param pos Position of the RRSet in the Additional section (RRSets are stored
+ * in the order they were added to the response or parsed from the
+ * query).
+ *
+ * \return The RRSet on position \a pos in the Additional section of \a packet
+ * or NULL if there is no such RRSet.
+ */
+const knot_rrset_t *knot_packet_additional_rrset(
+ knot_packet_t *packet, short pos);
+
+/*!
+ * \brief Checks if the packet already contains the given RRSet.
+ *
+ * It searches for the RRSet in the three lists of RRSets corresponding to
+ * Answer, Authority and Additional sections of the packet.
+ *
+ * \note Only pointers are compared, i.e. two instances of knot_rrset_t with
+ * the same data will be considered different.
+ *
+ * \param packet Packet to look for the RRSet in.
+ * \param rrset RRSet to look for.
+ *
+ * \retval 0 if \a resp does not contain \a rrset.
+ * \retval <> 0 if \a resp does contain \a rrset.
+ */
+int knot_packet_contains(const knot_packet_t *packet,
+ const knot_rrset_t *rrset,
+ knot_rrset_compare_type_t cmp);
+
+/*!
+ * \brief Adds RRSet to the list of temporary RRSets.
+ *
+ * Temporary RRSets are fully freed when the response structure is destroyed.
+ *
+ * \param response Response to which the temporary RRSet should be added.
+ * \param tmp_rrset Temporary RRSet to be stored in the response.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_packet_add_tmp_rrset(knot_packet_t *response,
+ knot_rrset_t *tmp_rrset);
+
+void knot_packet_free_tmp_rrsets(knot_packet_t *pkt);
+
+/*!
+ * \brief Converts the header structure to wire format.
+ *
+ * \note This function also adjusts the position (\a pos) according to
+ * the size of the converted wire format.
+ *
+ * \param[in] header DNS header structure to convert.
+ * \param[out] pos Position where to put the converted header. The space has
+ * to be allocated before calling this function.
+ * \param[out] size Size of the wire format of the header in bytes.
+ */
+void knot_packet_header_to_wire(const knot_header_t *header,
+ uint8_t **pos, size_t *size);
+
+int knot_packet_question_to_wire(knot_packet_t *packet);
+
+/*!
+ * \brief Converts the stored response OPT RR to wire format and adds it to
+ * the response wire format.
+ *
+ * \param resp Response structure.
+ */
+int knot_packet_edns_to_wire(knot_packet_t *packet);
+
+/*!
+ * \brief Converts the packet to wire format.
+ *
+ * \param packet Packet to be converted to wire format.
+ * \param wire Here the wire format of the packet will be stored.
+ * Space for the packet will be allocated. *resp_wire must
+ * be set to NULL (to avoid leaks).
+ * \param wire_size The size of the packet in wire format will be stored here.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_packet_to_wire(knot_packet_t *packet, uint8_t **wire,
+ size_t *wire_size);
+
+const uint8_t *knot_packet_wireformat(const knot_packet_t *packet);
+
+/*!
+ * \brief Properly destroys the packet structure.
+ *
+ * \param response Packet to be destroyed.
+ */
+void knot_packet_free(knot_packet_t **packet);
+
+/*!
+ * \brief Dumps the whole packet in human-readable form.
+ *
+ * \note This function is empty unless KNOT_PACKET_DEBUG is defined.
+ *
+ * \param resp Packet to dump.
+ */
+void knot_packet_dump(const knot_packet_t *packet);
+
+#endif /* _KNOT_PACKET_H_ */
+
+/*! @} */
diff --git a/src/libknot/packet/query.c b/src/libknot/packet/query.c
new file mode 100644
index 0000000..63e902a
--- /dev/null
+++ b/src/libknot/packet/query.c
@@ -0,0 +1,228 @@
+/* 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 <stdlib.h>
+#include "packet/query.h"
+
+#include "util/error.h"
+#include "util/wire.h"
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_rr_to_wire(const knot_rrset_t *rrset, const knot_rdata_t *rdata,
+ uint8_t **wire, uint8_t *endp)
+{
+ /* Store owner. */
+ knot_dname_t *owner = rrset->owner;
+ if (*wire + owner->size > endp) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(*wire, owner->name, owner->size);
+ *wire += owner->size;
+
+ if (*wire + 10 > endp) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Write RR header. */
+ knot_wire_write_u16(*wire, rrset->type); *wire += 2;
+ knot_wire_write_u16(*wire, rrset->rclass); *wire += 2;
+ knot_wire_write_u32(*wire, rrset->ttl); *wire += 4;
+ knot_wire_write_u16(*wire, 0); *wire += 2; /* RDLENGTH reserve. */
+ uint8_t *rdlength_p = *wire - 2;
+ uint16_t rdlength = 0;
+
+ /* Write data. */
+ knot_dname_t *dname = 0;
+ uint16_t *raw_data = 0;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ for (int i = 0; i < rdata->count; ++i) {
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+
+ /* Check space for dname. */
+ dname = knot_rdata_item(rdata, i)->dname;
+ if (*wire + dname->size > endp) {
+ return KNOT_ESPACE;
+ }
+
+ /* Save domain name. */
+ memcpy(*wire, dname->name, dname->size);
+ *wire += dname->size;
+ rdlength += dname->size;
+ break;
+ default:
+ raw_data = knot_rdata_item(rdata, i)->raw_data;
+ if (*wire + raw_data[0] > endp) {
+ return KNOT_ESPACE;
+ }
+
+ /* Copy data. */
+ memcpy(*wire, raw_data + 1, raw_data[0]);
+ *wire += raw_data[0];
+ rdlength += raw_data[0];
+ break;
+
+ }
+ }
+
+ /* Store rdlength. */
+ knot_wire_write_u16(rdlength_p, rdlength);
+
+ return KNOT_EOK;
+}
+/*----------------------------------------------------------------------------*/
+
+int knot_query_dnssec_requested(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return ((knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED)
+ && knot_edns_do(&query->opt_rr));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_nsid_requested(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return ((knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED)
+ && knot_edns_has_option(&query->opt_rr, EDNS_OPTION_NSID));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_edns_supported(const knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return (knot_edns_get_version(&query->opt_rr) != EDNS_NOT_SUPPORTED);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_init(knot_packet_t *query)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+ // set the qr bit to 0
+ knot_wire_flags_clear_qr(&query->header.flags1);
+
+ uint8_t *pos = query->wireformat;
+ knot_packet_header_to_wire(&query->header, &pos, &query->size);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_set_question(knot_packet_t *query,
+ const knot_question_t *question)
+{
+ if (query == NULL || question == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ query->question.qname = question->qname;
+ query->question.qclass = question->qclass;
+ query->question.qtype = question->qtype;
+ query->header.qdcount = 1;
+
+ // convert the Question to wire format right away
+ knot_packet_question_to_wire(query);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode)
+{
+ if (query == NULL) {
+ return KNOT_EBADARG;
+ }
+ // set the OPCODE in the structure
+ knot_wire_flags_set_opcode(&query->header.flags1, opcode);
+ // set the OPCODE in the wire format
+ knot_wire_set_opcode(query->wireformat, opcode);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_query_add_rrset_authority(knot_packet_t *query,
+ const knot_rrset_t *rrset)
+{
+ if (query == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (query->ns_rrsets == query->max_ns_rrsets) {
+ size_t oldsize = query->max_ns_rrsets * sizeof(knot_rrset_t *);
+ ++query->max_ns_rrsets;
+ size_t newsize = query->max_ns_rrsets * sizeof(knot_rrset_t *);
+ const knot_rrset_t ** na = malloc(newsize);
+ if (na == 0) {
+ query->max_ns_rrsets = 0;
+ return KNOT_ENOMEM;
+ } else {
+ memcpy(na, query->authority, oldsize);
+ free(query->authority);
+ query->authority = na;
+ }
+ }
+
+ /* Append to packet. */
+ query->authority[query->ns_rrsets] = rrset;
+
+ /* Write to wire. */
+ uint8_t *startp = query->wireformat + query->size;
+ uint8_t *endp = query->wireformat + query->max_size;
+
+ assert(endp - startp > query->opt_rr.size + query->tsig_size);
+ // reserve space for OPT RR
+ endp -= query->opt_rr.size;
+ /*! \note [TSIG] reserve space for TSIG RR */
+ endp -= query->tsig_size;
+
+ uint8_t *pos = startp;
+
+ const knot_rdata_t *rdata = 0;
+ while ((rdata = knot_rrset_rdata_next(rrset, rdata))) {
+ knot_query_rr_to_wire(rrset, rdata, &pos, endp);
+ }
+
+ size_t written = (pos - startp);
+ query->size += written;
+ ++query->ns_rrsets;
+ ++query->header.nscount;
+
+ return KNOT_EOK;
+}
+
diff --git a/src/libknot/packet/query.h b/src/libknot/packet/query.h
new file mode 100644
index 0000000..a979641
--- /dev/null
+++ b/src/libknot/packet/query.h
@@ -0,0 +1,93 @@
+/*!
+ * \file query.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for manipulating queries.
+ *
+ * \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_QUERY_H_
+#define _KNOT_QUERY_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "packet/packet.h"
+#include "dname.h"
+#include "rrset.h"
+#include "edns.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Checks if DNSSEC was requested in the query (i.e. the DO bit was set).
+ *
+ * \param query Packet where the parsed query is stored.
+ *
+ * \retval 0 if the DO bit was not set in the query, or the query is not yet
+ * parsed.
+ * \retval > 0 if DO bit was set in the query.
+ */
+int knot_query_dnssec_requested(const knot_packet_t *query);
+
+/*!
+ * \brief Checks if NSID was requested in the query (i.e. the NSID option was
+ * present in the query OPT RR).
+ *
+ * \param query Packet where the parsed query is stored.
+ *
+ * \retval 0 if the NSID option was not present in the query, or the query is
+ * not yet parsed.
+ * \retval > 0 if the NSID option was present in the query.
+ */
+int knot_query_nsid_requested(const knot_packet_t *query);
+
+int knot_query_edns_supported(const knot_packet_t *query);
+
+//int knot_query_set_qname(knot_packet_t *query, const knot_dname_t *qname);
+
+//int knot_query_set_qtype(knot_packet_t *query, uint16_t qtype);
+
+//int knot_query_set_qclass(knot_packet_t *query, uint16_t qclass);
+
+int knot_query_init(knot_packet_t *query);
+
+int knot_query_set_question(knot_packet_t *query,
+ const knot_question_t *question);
+
+int knot_query_set_opcode(knot_packet_t *query, uint8_t opcode);
+
+/*!
+ * \brief Adds a RRSet to the Authority section of the query.
+ *
+ * \param query Query to add the RRSet into.
+ * \param rrset RRSet to be added.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the query.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_query_add_rrset_authority(knot_packet_t *query,
+ const knot_rrset_t *rrset);
+
+
+#endif /* _KNOT_QUERY_H_ */
+
+/*! @} */
diff --git a/src/libknot/packet/response.c b/src/libknot/packet/response.c
new file mode 100644
index 0000000..e6f89d0
--- /dev/null
+++ b/src/libknot/packet/response.c
@@ -0,0 +1,1170 @@
+/* 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 <stdlib.h>
+
+#include "packet/response.h"
+#include "util/wire.h"
+#include "util/descriptor.h"
+#include "common.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "packet/packet.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Holds information about compressed domain name.
+ *
+ * Used only to pass information between functions.
+ *
+ * \todo This description should be revised and clarified.
+ */
+struct knot_compr_owner {
+ /*!
+ * \brief Place where the name is stored in the wire format of the
+ * packet.
+ */
+ uint8_t *wire;
+ short size; /*!< Size of the domain name in bytes. */
+ /*! \brief Position of the name relative to the start of the packet. */
+ size_t pos;
+};
+
+typedef struct knot_compr_owner knot_compr_owner_t;
+
+/*!
+ * \brief Holds information about compressed domain names in packet.
+ *
+ * Used only to pass information between functions.
+ *
+ * \todo This description should be revised and clarified.
+ */
+struct knot_compr {
+ knot_compressed_dnames_t *table; /*!< Compression table. */
+ size_t wire_pos; /*!< Current position in the wire format. */
+ knot_compr_owner_t owner; /*!< Information about the current name. */
+};
+
+typedef struct knot_compr knot_compr_t;
+
+static const size_t KNOT_RESPONSE_MAX_PTR = 16383;
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Reallocates space for compression table.
+ *
+ * \param table Compression table to reallocate space for.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_response_realloc_compr(knot_compressed_dnames_t *table)
+{
+ int free_old = table->max != DEFAULT_DOMAINS_IN_RESPONSE;
+ size_t *old_offsets = table->offsets;
+ const knot_dname_t **old_dnames = table->dnames;
+
+ short new_max_count = table->max + STEP_DOMAINS;
+
+ size_t *new_offsets = (size_t *)malloc(new_max_count * sizeof(size_t));
+ CHECK_ALLOC_LOG(new_offsets, -1);
+
+ const knot_dname_t **new_dnames = (const knot_dname_t **)malloc(
+ new_max_count * sizeof(knot_dname_t *));
+ if (new_dnames == NULL) {
+ ERR_ALLOC_FAILED;
+ free(new_offsets);
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(new_offsets, table->offsets, table->max * sizeof(size_t));
+ memcpy(new_dnames, table->dnames,
+ table->max * sizeof(knot_dname_t *));
+
+ table->offsets = new_offsets;
+ table->dnames = new_dnames;
+ table->max = new_max_count;
+
+ if (free_old) {
+ free(old_offsets);
+ free(old_dnames);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Stores new mapping between domain name and offset in the compression
+ * table.
+ *
+ * If the domain name is already present in the table, it is not inserted again.
+ *
+ * \param table Compression table to save the mapping into.
+ * \param dname Domain name to insert.
+ * \param pos Position of the domain name in the packet's wire format.
+ */
+static void knot_response_compr_save(knot_compressed_dnames_t *table,
+ const knot_dname_t *dname, size_t pos)
+{
+ assert(table->count < table->max);
+
+ for (int i = 0; i < table->count; ++i) {
+ if (table->dnames[i] == dname) {
+ dbg_response("Already present, skipping..\n");
+ return;
+ }
+ }
+
+ table->dnames[table->count] = dname;
+ table->offsets[table->count] = pos;
+ ++table->count;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Stores domain name position and positions of its parent domain names
+ * to the compression table.
+ *
+ * If part of the domain name (\a dname) was not found previously in the
+ * compression table, this part and all its parent domains is stored also, to
+ * maximize compression potential.
+ *
+ * \param table Compression table to save the information into.
+ * \param dname Domain name to save.
+ * \param not_matched Count of labels not matched when previously searching in
+ * the compression table for \a dname.
+ * \param pos Position of the domain name in the wire format of the packet.
+ * \param unmatched_offset Position of the unmatched parent domain of \a dname.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_response_store_dname_pos(knot_compressed_dnames_t *table,
+ const knot_dname_t *dname,
+ int not_matched, size_t pos,
+ size_t unmatched_offset,
+ int compr_cs)
+{
+dbg_response_exec(
+ char *name = knot_dname_to_str(dname);
+ dbg_response("Putting dname %s into compression table."
+ " Labels not matched: %d, position: %zu,"
+ ", pointer: %p, unmatched off: %zu\n", name,
+ not_matched, pos, dname, unmatched_offset);
+ free(name);
+);
+ if (pos > KNOT_RESPONSE_MAX_PTR) {
+ dbg_response("Pointer larger than it can be, not"
+ " saving\n");
+ return KNOT_EDNAMEPTR;
+ }
+
+ if (table->count == table->max &&
+ knot_response_realloc_compr(table) != 0) {
+ return KNOT_ENOMEM;
+ }
+
+ // store the position of the name
+// table->dnames[table->count] = dname;
+// table->offsets[table->count] = pos;
+// ++table->count;
+
+ /*
+ * Store positions of ancestors if more than 1 label was not matched.
+ *
+ * In case the name is not in the zone, the counting to not_matched
+ * may be limiting, because the search stopped before after the first
+ * label (i.e. not_matched == 1). So we do not store the parents in
+ * this case. However, storing them will require creating those domain
+ * names, as they do not exist.
+ *
+ * The same problem is with domain names synthetized from wildcards.
+ * These also do not have any node to follow.
+ *
+ * We accept this as performance has higher
+ * priority than the best possible compression.
+ */
+ const knot_dname_t *to_save = dname;
+ size_t parent_pos = pos;
+ int i = 0;
+
+ while (to_save != NULL && i < knot_dname_label_count(dname)) {
+ if (i == not_matched) {
+ parent_pos = unmatched_offset;
+ }
+
+dbg_response_exec(
+ char *name = knot_dname_to_str(to_save);
+ dbg_response("Putting dname %s into compression table."
+ " Position: %zu, pointer: %p\n",
+ name, parent_pos, to_save);
+ free(name);
+);
+
+ if (table->count == table->max &&
+ knot_response_realloc_compr(table) != 0) {
+ dbg_response("Unable to realloc.\n");
+ return KNOT_ENOMEM;
+ }
+
+// dbg_response("Saving..\n");
+ knot_response_compr_save(table, to_save, parent_pos);
+
+ /*! \todo Remove '!compr_cs'. */
+ // This is a temporary hack to avoid the wrong behaviour
+ // when the wrong not_matched count is used to compare with i
+ // and resulting in using the 0 offset.
+ // If case-sensitive search is in place, we should not save the
+ // node's parent's positions.
+
+ to_save = !compr_cs && (knot_dname_node(to_save, 1) != NULL
+ && knot_node_parent(knot_dname_node(to_save, 1), 1)
+ != NULL) ? knot_node_owner(knot_node_parent(
+ knot_dname_node(to_save, 1), 1))
+ : NULL;
+
+ dbg_response("i: %d\n", i);
+ parent_pos += knot_dname_label_size(dname, i) + 1;
+// parent_pos += (i > 0)
+// ? knot_dname_label_size(dname, i - 1) + 1 : 0;
+ ++i;
+ }
+
+ return KNOT_EOK;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to find offset of domain name in the compression table.
+ *
+ * \param table Compression table to search in.
+ * \param dname Domain name to search for.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Offset of \a dname stored in the compression table or -1 if the name
+ * was not found in the table.
+ */
+static size_t knot_response_find_dname_pos(
+ const knot_compressed_dnames_t *table,
+ const knot_dname_t *dname, int compr_cs)
+{
+ for (int i = 0; i < table->count; ++i) {
+// dbg_response("Comparing dnames %p and %p\n",
+// dname, table->dnames[i]);
+//dbg_response_exec(
+// char *name = knot_dname_to_str(dname);
+// dbg_response("(%s and ", name);
+// name = knot_dname_to_str(table->dnames[i]);
+// dbg_response("%s)\n", name);
+// free(name);
+//);
+ //if (table->dnames[i] == dname) {
+ int ret = (compr_cs)
+ ? knot_dname_compare_cs(table->dnames[i], dname)
+ : knot_dname_compare(table->dnames[i], dname);
+ if (ret == 0) {
+ dbg_response("Found offset: %zu\n",
+ table->offsets[i]);
+ return table->offsets[i];
+ }
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Put a compressed domain name to the wire format of the packet.
+ *
+ * Puts the not matched part of the domain name to the wire format and puts
+ * a pointer to the rest of the name after that.
+ *
+ * \param dname Domain name to put to the wire format.
+ * \param not_matched Size of the part of domain name that cannot be compressed.
+ * \param offset Position of the rest of the domain name in the packet's wire
+ * format.
+ * \param wire Place where to put the wire format of the name.
+ * \param max Maximum available size of the place for the wire format.
+ *
+ * \return Size of the compressed domain name put into the wire format or
+ * KNOT_ESPACE if it did not fit.
+ */
+static int knot_response_put_dname_ptr(const knot_dname_t *dname,
+ int not_matched, size_t offset,
+ uint8_t *wire, size_t max)
+{
+ // put the not matched labels
+ short size = knot_dname_size_part(dname, not_matched);
+ if (size + 2 > max) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(wire, knot_dname_name(dname), size);
+ knot_wire_put_pointer(wire + size, offset);
+
+ dbg_response("Size of the dname with ptr: %d\n", size + 2);
+
+ return size + 2;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to compress domain name and creates its wire format.
+ *
+ * \param dname Domain name to convert and compress.
+ * \param compr Compression table holding information about offsets of domain
+ * names in the packet.
+ * \param dname_wire Place where to put the wire format of the name.
+ * \param max Maximum available size of the place for the wire format.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Size of the domain name's wire format or KNOT_ESPACE if it did not
+ * fit into the provided space.
+ */
+static int knot_response_compress_dname(const knot_dname_t *dname,
+ knot_compr_t *compr, uint8_t *dname_wire, size_t max, int compr_cs)
+{
+ int size = 0;
+ /*!
+ * \todo Compress!!
+ *
+ * if pos == 0, do not store the position!
+ */
+
+ // try to find the name or one of its ancestors in the compr. table
+#ifdef COMPRESSION_PEDANTIC
+ //knot_dname_t *to_find = knot_dname_copy(dname);
+ knot_dname_t *to_find = (knot_dname_t *)dname;
+ int copied = 0;
+#else
+ const knot_dname_t *to_find = dname;
+#endif
+ size_t offset = 0;
+ int not_matched = 0;
+
+ while (to_find != NULL && knot_dname_label_count(to_find) != 0) {
+dbg_response_exec(
+ char *name = knot_dname_to_str(to_find);
+ dbg_response("Searching for name %s in the compression"
+ " table, not matched labels: %d\n", name,
+ not_matched);
+ free(name);
+);
+ offset = knot_response_find_dname_pos(compr->table, to_find,
+ compr_cs);
+ if (offset == 0) {
+ ++not_matched;
+ } else {
+ break;
+ }
+#ifdef COMPRESSION_PEDANTIC
+ if (compr_cs || to_find->node == NULL
+ || to_find->node->owner != to_find
+ || to_find->node->parent == NULL) {
+ if (!copied) {
+ to_find = knot_dname_left_chop(to_find);
+ copied = 1;
+ } else {
+ knot_dname_left_chop_no_copy(to_find);
+ }
+ } else {
+ assert(to_find->node != to_find->node->parent);
+ assert(to_find != to_find->node->parent->owner);
+ to_find = to_find->node->parent->owner;
+ }
+#else
+ // if case-sensitive comparation, we cannot just take the parent
+ if (compr_cs || knot_dname_node(to_find, 1) == NULL
+ || knot_node_owner(knot_dname_node(to_find, 1)) != to_find
+ || knot_node_parent(knot_dname_node(to_find, 1), 1)
+ == NULL) {
+ dbg_response("compr_cs: %d\n", compr_cs);
+ dbg_response("knot_dname_node(to_find, 1) == %p"
+ "\n", knot_dname_node(to_find, 1));
+
+ if (knot_dname_node(to_find, 1) != NULL) {
+ dbg_response("knot_node_owner(knot_dname_node("
+ "to_find, 1)) = %p, to_find = %p\n",
+ knot_node_owner(knot_dname_node(to_find, 1)),
+ to_find);
+ dbg_response("knot_node_parent(knot_dname_node("
+ "to_find, 1), 1) = %p\n",
+ knot_node_parent(knot_dname_node(to_find, 1), 1));
+ }
+ break;
+ } else {
+ assert(knot_dname_node(to_find, 1) !=
+ knot_node_parent(knot_dname_node(to_find, 1), 1));
+ assert(to_find != knot_node_owner(
+ knot_node_parent(knot_dname_node(to_find, 1), 1)));
+ to_find = knot_node_owner(
+ knot_node_parent(knot_dname_node(to_find, 1), 1));
+ dbg_response("New to_find: %p\n", to_find);
+ }
+#endif
+ }
+
+#ifdef COMPRESSION_PEDANTIC
+ if (copied) {
+ knot_dname_free(&to_find);
+ }
+#endif
+
+ dbg_response("Max size available for domain name: %zu\n", max);
+
+ if (offset > 0) { // found such dname somewhere in the packet
+ dbg_response("Found name in the compression table.\n");
+ assert(offset >= KNOT_WIRE_HEADER_SIZE);
+ size = knot_response_put_dname_ptr(dname, not_matched, offset,
+ dname_wire, max);
+ if (size <= 0) {
+ return KNOT_ESPACE;
+ }
+ } else {
+ dbg_response("Not found, putting whole name.\n");
+ // now just copy the dname without compressing
+ if (dname->size > max) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(dname_wire, dname->name, dname->size);
+ size = dname->size;
+ }
+
+ // in either way, put info into the compression table
+ /*! \todo This is useless if the name was already in the table.
+ * It is meaningful only if the found name is the one from QNAME
+ * and thus its parents are not stored yet.
+ */
+ assert(compr->wire_pos >= 0);
+
+ if (knot_response_store_dname_pos(compr->table, dname, not_matched,
+ compr->wire_pos, offset, compr_cs)
+ != 0) {
+ dbg_response("Compression info could not be stored."
+ "\n");
+ }
+
+ return size;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Convert one RR into wire format.
+ *
+ * \param[in] rrset RRSet to which the RR belongs.
+ * \param[in] rdata The actual RDATA of this RR.
+ * \param[in] compr Information about compressed domain names in the packet.
+ * \param[out] rrset_wire Place to put the wire format of the RR into.
+ * \param[in] max_size Size of space available for the wire format.
+ * \param[in] compr_cs Set to <> 0 if dname compression should use case
+ * sensitive comparation. Set to 0 otherwise.
+ *
+ * \return Size of the RR's wire format or KNOT_ESPACE if it did not fit into
+ * the provided space.
+ */
+static int knot_response_rr_to_wire(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata,
+ knot_compr_t *compr,
+ uint8_t **rrset_wire, size_t max_size,
+ int compr_cs)
+{
+ int size = 0;
+
+ dbg_response("Max size: %zu, owner pos: %zu, owner size: %d\n",
+ max_size, compr->owner.pos, compr->owner.size);
+
+ if (size + ((compr->owner.pos == 0
+ || compr->owner.pos > KNOT_RESPONSE_MAX_PTR)
+ ? compr->owner.size : 2) + 10
+ > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ dbg_response("Owner position: %zu\n", compr->owner.pos);
+
+ // put owner if needed (already compressed)
+ if (compr->owner.pos == 0 || compr->owner.pos > KNOT_RESPONSE_MAX_PTR) {
+ memcpy(*rrset_wire, compr->owner.wire, compr->owner.size);
+ compr->owner.pos = compr->wire_pos;
+ *rrset_wire += compr->owner.size;
+ size += compr->owner.size;
+ } else {
+ dbg_response("Putting pointer: %zu\n",
+ compr->owner.pos);
+ knot_wire_put_pointer(*rrset_wire, compr->owner.pos);
+ *rrset_wire += 2;
+ size += 2;
+ }
+
+ dbg_response("Max size: %zu, size: %d\n", max_size, size);
+
+ dbg_response("Wire format:\n");
+
+ // put rest of RR 'header'
+ knot_wire_write_u16(*rrset_wire, rrset->type);
+ dbg_response(" Type: %u\n", rrset->type);
+ dbg_response(" Type in wire: ");
+ dbg_response_hex((char *)*rrset_wire, 2);
+ *rrset_wire += 2;
+
+ knot_wire_write_u16(*rrset_wire, rrset->rclass);
+ dbg_response(" Class: %u\n", rrset->rclass);
+ dbg_response(" Class in wire: ");
+ dbg_response_hex((char *)*rrset_wire, 2);
+ *rrset_wire += 2;
+
+ knot_wire_write_u32(*rrset_wire, rrset->ttl);
+ dbg_response(" TTL: %u\n", rrset->ttl);
+ dbg_response(" TTL in wire: ");
+ dbg_response_hex((char *)*rrset_wire, 4);
+ *rrset_wire += 4;
+
+ // save space for RDLENGTH
+ uint8_t *rdlength_pos = *rrset_wire;
+ *rrset_wire += 2;
+
+ size += 10;
+ compr->wire_pos += size;
+
+ dbg_response("Max size: %zu, size: %d\n", max_size, size);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ uint16_t rdlength = 0;
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (max_size < size + rdlength) {
+ return KNOT_ESPACE;
+ }
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME: {
+ int ret = knot_response_compress_dname(
+ knot_rdata_item(rdata, i)->dname,
+ compr, *rrset_wire, max_size - size - rdlength,
+ compr_cs);
+
+ if (ret < 0) {
+ return KNOT_ESPACE;
+ }
+
+ dbg_response("Compressed dname size: %d\n",
+ ret);
+ *rrset_wire += ret;
+ rdlength += ret;
+ compr->wire_pos += ret;
+ // TODO: compress domain name
+ break;
+ }
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME: {
+ knot_dname_t *dname =
+ knot_rdata_item(rdata, i)->dname;
+ if (size + rdlength + dname->size > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // save whole domain name
+ memcpy(*rrset_wire, dname->name, dname->size);
+ dbg_response("Uncompressed dname size: %d\n",
+ dname->size);
+ *rrset_wire += dname->size;
+ rdlength += dname->size;
+ compr->wire_pos += dname->size;
+ break;
+ }
+// case KNOT_RDATA_WF_BINARYWITHLENGTH: {
+// uint16_t *raw_data =
+// knot_rdata_item(rdata, i)->raw_data;
+
+// if (size + raw_data[0] + 1 > max_size) {
+// return KNOT_ESPACE;
+// }
+
+// // copy also the rdata item size
+// assert(raw_data[0] < 256);
+// **rrset_wire = raw_data[0];
+// *rrset_wire += 1;
+// memcpy(*rrset_wire, raw_data + 1, raw_data[0]);
+// dbg_response("Raw data size: %d\n",
+// raw_data[0] + 1);
+// *rrset_wire += raw_data[0];
+// rdlength += raw_data[0] + 1;
+// compr->wire_pos += raw_data[0] + 1;
+// break;
+// }
+ default: {
+ uint16_t *raw_data =
+ knot_rdata_item(rdata, i)->raw_data;
+
+ if (size + rdlength + raw_data[0] > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // copy just the rdata item data (without size)
+ memcpy(*rrset_wire, raw_data + 1, raw_data[0]);
+ dbg_response("Raw data size: %d\n",
+ raw_data[0]);
+ *rrset_wire += raw_data[0];
+ rdlength += raw_data[0];
+ compr->wire_pos += raw_data[0];
+ break;
+ }
+ }
+ }
+
+ dbg_response("Max size: %zu, size: %d\n", max_size, size);
+
+ assert(size + rdlength <= max_size);
+ size += rdlength;
+ knot_wire_write_u16(rdlength_pos, rdlength);
+
+ return size;
+}
+
+/*---------------------------------------------------------------------------*/
+/*!
+ * \brief Convert whole RRSet into wire format.
+ *
+ * \param[in] rrset RRSet to convert
+ * \param[out] pos Place where to put the wire format.
+ * \param[out] size Size of the converted wire format.
+ * \param[in] max_size Maximum available space for the wire format.
+ * \param wire_pos Current position in the wire format of the whole packet.
+ * \param owner_tmp Wire format of the RRSet's owner, possibly compressed.
+ * \param compr Information about compressed domain names in the packet.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Size of the RRSet's wire format or KNOT_ESPACE if it did not fit
+ * into the provided space.
+ */
+static int knot_response_rrset_to_wire(const knot_rrset_t *rrset,
+ uint8_t **pos, size_t *size,
+ size_t max_size, size_t wire_pos,
+ uint8_t *owner_tmp,
+ knot_compressed_dnames_t *compr,
+ int compr_cs)
+{
+dbg_response_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_response("Converting RRSet with owner %s, type %s\n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+ dbg_response(" Size before: %zu\n", *size);
+);
+
+ // if no RDATA in RRSet, return
+ if (rrset->rdata == NULL) {
+ return KNOT_EOK;
+ }
+
+ //uint8_t *rrset_wire = (uint8_t *)malloc(PREALLOC_RRSET_WIRE);
+ //short rrset_size = 0;
+
+ //uint8_t *owner_wire = (uint8_t *)malloc(rrset->owner->size);
+ /*
+ * We may pass the current position to the compression function
+ * because if the owner will be put somewhere, it will be on the
+ * current position (first item of a RR). If it will not be put into
+ * the wireformat, we may remove the dname (and possibly its parents)
+ * from the compression table.
+ */
+
+ knot_compr_t compr_info;
+ //compr_info.new_entries = 0;
+ compr_info.table = compr;
+ compr_info.wire_pos = wire_pos;
+ compr_info.owner.pos = 0;
+ compr_info.owner.wire = owner_tmp;
+ compr_info.owner.size =
+ knot_response_compress_dname(rrset->owner, &compr_info,
+ owner_tmp, max_size, compr_cs);
+
+ dbg_response(" Owner size: %d, position: %zu\n",
+ compr_info.owner.size, compr_info.owner.pos);
+ if (compr_info.owner.size < 0) {
+ return KNOT_ESPACE;
+ }
+
+ int rrs = 0;
+ short rrset_size = 0;
+
+ const knot_rdata_t *rdata = rrset->rdata;
+ do {
+ int ret = knot_response_rr_to_wire(rrset, rdata, &compr_info,
+ pos, max_size - rrset_size,
+ compr_cs);
+
+ assert(ret != 0);
+
+ if (ret < 0) {
+ // some RR didn't fit in, so no RRs should be used
+ // TODO: remove last entries from compression table
+ dbg_response("Some RR didn't fit in.\n");
+ return KNOT_ESPACE;
+ }
+
+ dbg_response("RR of size %d added.\n", ret);
+ rrset_size += ret;
+ ++rrs;
+ } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
+
+ //memcpy(*pos, rrset_wire, rrset_size);
+ //*size += rrset_size;
+ //*pos += rrset_size;
+
+ // the whole RRSet did fit in
+ assert (rrset_size <= max_size);
+ *size += rrset_size;
+
+ dbg_response(" Size after: %zu\n", *size);
+
+ return rrs;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Tries to add RRSet to the response.
+ *
+ * This function tries to convert the RRSet to wire format and add it to the
+ * wire format of the response and if successful, adds the RRSet to the given
+ * list (and updates its size). If the RRSet did not fit into the available
+ * space (\a max_size), it is omitted as a whole and the TC bit may be set
+ * (according to \a tc).
+ *
+ * \param rrsets Lists of RRSets to which this RRSet should be added.
+ * \param rrset_count Number of RRSets in the list.
+ * \param resp Response structure where the RRSet should be added.
+ * \param max_size Maximum available space in wire format of the response.
+ * \param rrset RRSet to add.
+ * \param tc Set to <> 0 if omitting the RRSet should cause the TC bit to be
+ * set in the response.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \return Count of RRs added to the response or KNOT_ESPACE if the RRSet did
+ * not fit in the available space.
+ */
+static int knot_response_try_add_rrset(const knot_rrset_t **rrsets,
+ short *rrset_count,
+ knot_packet_t *resp,
+ size_t max_size,
+ const knot_rrset_t *rrset, int tc,
+ int compr_cs)
+{
+ //short size = knot_response_rrset_size(rrset, &resp->compression);
+
+dbg_response_exec(
+ char *name = knot_dname_to_str(rrset->owner);
+ dbg_response("\nAdding RRSet with owner %s and type %s: \n",
+ name, knot_rrtype_to_string(rrset->type));
+ free(name);
+);
+
+ uint8_t *pos = resp->wireformat + resp->size;
+ size_t size = 0;
+ int rrs = knot_response_rrset_to_wire(rrset, &pos, &size, max_size,
+ resp->size, resp->owner_tmp,
+ &resp->compression, compr_cs);
+
+ if (rrs >= 0) {
+ rrsets[(*rrset_count)++] = rrset;
+ resp->size += size;
+ dbg_response("RRset added, size: %zu, RRs: %d, total "
+ "size of response: %zu\n\n", size, rrs,
+ resp->size);
+ } else if (tc) {
+ dbg_response("Setting TC bit.\n");
+ knot_wire_flags_set_tc(&resp->header.flags1);
+ knot_wire_set_tc(resp->wireformat);
+ }
+
+ return rrs;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Reallocate space for RRSets.
+ *
+ * \param rrsets Space for RRSets.
+ * \param max_count Size of the space available for the RRSets.
+ * \param default_max_count Size of the space pre-allocated for the RRSets when
+ * the response structure was initialized.
+ * \param step How much the space should be increased.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+static int knot_response_realloc_rrsets(const knot_rrset_t ***rrsets,
+ short *max_count,
+ short default_max_count, short step)
+{
+ int free_old = (*max_count) != default_max_count;
+ const knot_rrset_t **old = *rrsets;
+
+ short new_max_count = *max_count + step;
+ const knot_rrset_t **new_rrsets = (const knot_rrset_t **)malloc(
+ new_max_count * sizeof(knot_rrset_t *));
+ CHECK_ALLOC_LOG(new_rrsets, KNOT_ENOMEM);
+
+ memcpy(new_rrsets, *rrsets, (*max_count) * sizeof(knot_rrset_t *));
+
+ *rrsets = new_rrsets;
+ *max_count = new_max_count;
+
+ if (free_old) {
+ free(old);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int knot_response_init(knot_packet_t *response)
+{
+ if (response == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (response->max_size < KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ESPACE;
+ }
+
+ // set the qr bit to 1
+ knot_wire_flags_set_qr(&response->header.flags1);
+
+ uint8_t *pos = response->wireformat;
+ knot_packet_header_to_wire(&response->header, &pos,
+ &response->size);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_init_from_query(knot_packet_t *response,
+ knot_packet_t *query)
+{
+
+ if (response == NULL || query == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // copy the header from the query
+ memcpy(&response->header, &query->header, sizeof(knot_header_t));
+// memmove(&response->header, &query->header, sizeof(knot_header_t));
+
+ // copy the Question section (but do not copy the QNAME)
+ memcpy(&response->question, &query->question,
+ sizeof(knot_question_t));
+// memmove(&response->question, &query->question,
+// sizeof(knot_question_t));
+
+ int err = 0;
+ // put the qname into the compression table
+ // TODO: get rid of the numeric constants
+ if ((err = knot_response_store_dname_pos(&response->compression,
+ response->question.qname, 0, 12, 12, 0)) != KNOT_EOK) {
+ return err;
+ }
+
+ // copy the wireformat of Header and Question from the query
+ // TODO: get rid of the numeric constants
+ size_t to_copy = 12 + 4 + knot_dname_size(response->question.qname);
+
+ assert(response->max_size >= to_copy);
+// printf("Resp init from query: Copying from: %p to: %p size: %d\n",
+// response->wireformat, query->wireformat,
+// to_copy);
+// printf("Resp init from query: Question name size: %d Query name size: %d\n",
+// knot_dname_size(response->question.qname),
+// knot_dname_size(query->question.qname));
+ memcpy(response->wireformat, query->wireformat, to_copy);
+ response->size = to_copy;
+
+ // set the qr bit to 1
+ knot_wire_flags_set_qr(&response->header.flags1);
+ knot_wire_set_qr(response->wireformat);
+
+ // clear AD flag
+ knot_wire_flags_clear_ad(&response->header.flags2);
+ knot_wire_clear_ad(response->wireformat);
+
+ // clear RA flag
+ knot_wire_flags_clear_ra(&response->header.flags2);
+ knot_wire_clear_ad(response->wireformat);
+
+ // set counts to 0
+ response->header.ancount = 0;
+ response->header.nscount = 0;
+ response->header.arcount = 0;
+
+ response->query = query;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_clear(knot_packet_t *resp, int clear_question)
+{
+ if (resp == NULL) {
+ return;
+ }
+
+ resp->size = (clear_question) ? KNOT_WIRE_HEADER_SIZE
+ : KNOT_WIRE_HEADER_SIZE + 4
+ + knot_dname_size(resp->question.qname);
+ resp->an_rrsets = 0;
+ resp->ns_rrsets = 0;
+ resp->ar_rrsets = 0;
+ resp->compression.count = 0;
+ knot_packet_free_tmp_rrsets(resp);
+ resp->tmp_rrsets_count = 0;
+ resp->header.ancount = 0;
+ resp->header.nscount = 0;
+ resp->header.arcount = 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_opt(knot_packet_t *resp,
+ const knot_opt_rr_t *opt_rr,
+ int override_max_size)
+{
+ if (resp == NULL || opt_rr == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // copy the OPT RR
+ resp->opt_rr.version = opt_rr->version;
+ resp->opt_rr.ext_rcode = opt_rr->ext_rcode;
+ resp->opt_rr.payload = opt_rr->payload;
+ resp->opt_rr.size = opt_rr->size;
+
+ // if max size is set, it means there is some reason to be that way,
+ // so we can't just set it to higher value
+
+ if (override_max_size && resp->max_size > 0
+ && resp->max_size < opt_rr->payload) {
+ return KNOT_EPAYLOAD;
+ }
+
+ // set max size (less is OK)
+ if (override_max_size) {
+ dbg_response("Overriding max size to: %u\n",
+ resp->opt_rr.payload);
+ return knot_packet_set_max_size(resp, resp->opt_rr.payload);
+ //resp->max_size = resp->opt_rr.payload;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_rrset_answer(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs)
+{
+ if (response == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ dbg_response("add_rrset_answer()\n");
+ assert(response->header.arcount == 0);
+ assert(response->header.nscount == 0);
+
+ if (response->an_rrsets == response->max_an_rrsets
+ && knot_response_realloc_rrsets(&response->answer,
+ &response->max_an_rrsets, DEFAULT_ANCOUNT, STEP_ANCOUNT)
+ != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ if (check_duplicates && knot_packet_contains(response, rrset,
+ KNOT_RRSET_COMPARE_PTR)) {
+ return KNOT_EOK;
+ }
+
+ dbg_response("Trying to add RRSet to Answer section.\n");
+ dbg_response("RRset: %p\n", rrset);
+ dbg_response("Owner: %p\n", rrset->owner);
+
+ int rrs = knot_response_try_add_rrset(response->answer,
+ &response->an_rrsets, response,
+ response->max_size
+ - response->size
+ - response->opt_rr.size
+ - response->tsig_size,
+ rrset, tc, compr_cs);
+
+ if (rrs >= 0) {
+ response->header.ancount += rrs;
+ return KNOT_EOK;
+ }
+
+ return KNOT_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_rrset_authority(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs)
+{
+ if (response == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(response->header.arcount == 0);
+
+ if (response->ns_rrsets == response->max_ns_rrsets
+ && knot_response_realloc_rrsets(&response->authority,
+ &response->max_ns_rrsets, DEFAULT_NSCOUNT, STEP_NSCOUNT)
+ != 0) {
+ return KNOT_ENOMEM;
+ }
+
+ if (check_duplicates && knot_packet_contains(response, rrset,
+ KNOT_RRSET_COMPARE_PTR)) {
+ return KNOT_EOK;
+ }
+
+ dbg_response("Trying to add RRSet to Authority section.\n");
+
+ int rrs = knot_response_try_add_rrset(response->authority,
+ &response->ns_rrsets, response,
+ response->max_size
+ - response->size
+ - response->opt_rr.size
+ - response->tsig_size,
+ rrset, tc, compr_cs);
+
+ if (rrs >= 0) {
+ response->header.nscount += rrs;
+ return KNOT_EOK;
+ }
+
+ return KNOT_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_rrset_additional(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs)
+{
+ if (response == NULL || rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret;
+
+ // if this is the first additional RRSet, add EDNS OPT RR first
+ if (response->header.arcount == 0
+ && response->opt_rr.version != EDNS_NOT_SUPPORTED
+ && (ret = knot_packet_edns_to_wire(response)) != KNOT_EOK) {
+ return ret;
+ }
+
+ if (response->ar_rrsets == response->max_ar_rrsets
+ && knot_response_realloc_rrsets(&response->additional,
+ &response->max_ar_rrsets, DEFAULT_ARCOUNT, STEP_ARCOUNT)
+ != 0) {
+ return KNOT_ENOMEM;
+ }
+
+ if (check_duplicates && knot_packet_contains(response, rrset,
+ KNOT_RRSET_COMPARE_PTR)) {
+ return KNOT_EOK;
+ }
+
+ dbg_response("Trying to add RRSet to Additional section.\n");
+
+ int rrs = knot_response_try_add_rrset(response->additional,
+ &response->ar_rrsets, response,
+ response->max_size
+ - response->size
+ - response->tsig_size, rrset,
+ tc, compr_cs);
+
+ if (rrs >= 0) {
+ response->header.arcount += rrs;
+ return KNOT_EOK;
+ }
+
+ return KNOT_ESPACE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_set_rcode(knot_packet_t *response, short rcode)
+{
+ if (response == NULL) {
+ return;
+ }
+
+ knot_wire_flags_set_rcode(&response->header.flags2, rcode);
+ knot_wire_set_rcode(response->wireformat, rcode);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_set_aa(knot_packet_t *response)
+{
+ if (response == NULL) {
+ return;
+ }
+
+ knot_wire_flags_set_aa(&response->header.flags1);
+ knot_wire_set_aa(response->wireformat);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_response_set_tc(knot_packet_t *response)
+{
+ if (response == NULL) {
+ return;
+ }
+
+ knot_wire_flags_set_tc(&response->header.flags1);
+ knot_wire_set_tc(response->wireformat);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_response_add_nsid(knot_packet_t *response, const uint8_t *data,
+ uint16_t length)
+{
+ if (response == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ return knot_edns_add_option(&response->opt_rr,
+ EDNS_OPTION_NSID, length, data);
+}
diff --git a/src/libknot/packet/response.h b/src/libknot/packet/response.h
new file mode 100644
index 0000000..38bd9a8
--- /dev/null
+++ b/src/libknot/packet/response.h
@@ -0,0 +1,198 @@
+/*!
+ * \file response.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief API for response manipulation.
+ *
+ * \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_response_H_
+#define _KNOT_response_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "packet/packet.h"
+
+#include "dname.h"
+#include "rrset.h"
+#include "edns.h"
+
+/*!
+ * \brief Default maximum DNS response size
+ *
+ * This size must be supported by all servers and clients.
+ */
+static const short KNOT_MAX_RESPONSE_SIZE = 512;
+
+/*----------------------------------------------------------------------------*/
+int knot_response_init(knot_packet_t *response);
+
+/*!
+ * \brief Initializes response from the given query.
+ *
+ * Copies the header, Changes QR bit to 1, copies the Question section and
+ * stores pointer to the query packet structure in the response packet
+ * structure.
+ *
+ * \warning Never free the query packet structure after calling this function,
+ * it will be freed when the response structure is freed.
+ *
+ * \param response Packet structure representing the response.
+ * \param query Packet structure representing the query.
+ *
+ * \retval KNOT_EOK
+ */
+int knot_response_init_from_query(knot_packet_t *response,
+ knot_packet_t *query);
+
+/*!
+ * \brief Clears the response structure for reuse.
+ *
+ * After call to this function, the response will be in the same state as if
+ * knot_response_new() was called. The maximum wire size is retained.
+ *
+ * \param response Response structure to clear.
+ *
+ * \todo Replace the use of this function with something else maybe?
+ */
+void knot_response_clear(knot_packet_t *resp, int clear_question);
+
+/*!
+ * \brief Sets the OPT RR of the response.
+ *
+ * This function also allocates space for the wireformat of the response, if
+ * the payload in the OPT RR is larger than the current maximum size of the
+ * response and copies the current wireformat over to the new space.
+ *
+ * \note The contents of the OPT RR are copied.
+ *
+ * \param resp Response to set the OPT RR to.
+ * \param opt_rr OPT RR to set.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ *
+ * \todo Needs test.
+ */
+int knot_response_add_opt(knot_packet_t *resp,
+ const knot_opt_rr_t *opt_rr,
+ int override_max_size);
+
+/*!
+ * \brief Adds a RRSet to the Answer section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ * Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ * response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the answer.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_response_add_rrset_answer(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Adds a RRSet to the Authority section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ * Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ * response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the answer.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_response_add_rrset_authority(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Adds a RRSet to the Additional section of the response.
+ *
+ * \param response Response to add the RRSet into.
+ * \param rrset RRSet to be added.
+ * \param tc Set to <> 0 if omitting this RRSet should result in the TC bit set.
+ * Otherwise set to 0.
+ * \param check_duplicates Set to <> 0 if the RRSet should not be added to the
+ * response in case it is already there.
+ * \param compr_cs Set to <> 0 if dname compression should use case sensitive
+ * comparation. Set to 0 otherwise.
+ *
+ * \retval KNOT_EOK if successful, or the RRSet was already in the answer.
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_ESPACE
+ */
+int knot_response_add_rrset_additional(knot_packet_t *response,
+ const knot_rrset_t *rrset, int tc,
+ int check_duplicates, int compr_cs);
+
+/*!
+ * \brief Sets the RCODE of the response.
+ *
+ * \param response Response to set the RCODE in.
+ * \param rcode RCODE to set.
+ */
+void knot_response_set_rcode(knot_packet_t *response, short rcode);
+
+/*!
+ * \brief Sets the AA bit of the response to 1.
+ *
+ * \param response Response in which the AA bit should be set.
+ */
+void knot_response_set_aa(knot_packet_t *response);
+
+/*!
+ * \brief Sets the TC bit of the response to 1.
+ *
+ * \param response Response in which the TC bit should be set.
+ */
+void knot_response_set_tc(knot_packet_t *response);
+
+/*!
+ * \brief Adds NSID option to the response.
+ *
+ * \param response Response to add the NSID option into.
+ * \param data NSID data.
+ * \param length Size of NSID data in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_response_add_nsid(knot_packet_t *response, const uint8_t *data,
+ uint16_t length);
+
+#endif /* _KNOT_response_H_ */
+
+/*! @} */
diff --git a/src/libknot/rdata.c b/src/libknot/rdata.c
new file mode 100644
index 0000000..0c51f5b
--- /dev/null
+++ b/src/libknot/rdata.c
@@ -0,0 +1,838 @@
+/* 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 <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "rdata.h"
+#include "util/descriptor.h"
+#include "dname.h"
+#include "util/error.h"
+#include "zone/node.h"
+#include "util/utils.h"
+#include "util/debug.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Compares two RDATA items as binary data.
+ *
+ * \param d1 First item.
+ * \param d2 Second item.
+ * \param count1 Size of the first item in bytes. If set to < 0, the size will
+ * be taken from the first two bytes of \a d1.
+ * \param count2 Size of the second item in bytes. If set to < 0, the size will
+ * be taken from the first two bytes of \a d2.
+ *
+ * \retval 0 if the items are identical.
+ * \retval < 0 if \a d1 goes before \a d2 in canonical order.
+ * \retval > 0 if \a d1 goes after \a d2 in canonical order.
+ */
+static int knot_rdata_compare_binary(const uint8_t *d1, const uint8_t *d2,
+ int count1, int count2)
+{
+ int i1 = 0, i2 = 0;
+
+ // length stored in the first octet
+ if (count1 < 0) {
+ // take count from the first two bytes
+ count1 = (int)(*(uint16_t *)d1);
+ // and start from the third byte
+ i1 = 2;
+ }
+ if (count2 < 0) { // dtto
+ count2 = (int)(*(uint16_t *)d2);
+ i2 = 2;
+ }
+
+
+ while (i1 < count1 && i2 < count2 && d1[i1] == d2[i2]) {
+ ++i1;
+ ++i2;
+ }
+
+ if (i1 == count1 && i2 == count2) {
+ return 0;
+ }
+
+ if (i1 == count1 && i2 < count2) {
+ return -1;
+ } else if (i2 == count2 && i1 < count1) {
+ return 1;
+ } else {
+ assert(i1 < count1 && i2 < count2);
+ return (d1[i1] < d2[i2]) ? -1 : 1;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves the domain name from MX RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the second
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the MX domain name from.
+ *
+ * \return MX domain name stored in \a rdata or NULL if \a rdata has less than 2
+ * items.
+ */
+static const knot_dname_t *knot_rdata_mx_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 2) {
+ return NULL;
+ }
+ return rdata->items[1].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves the domain name from NS RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the first
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the NS domain name from.
+ *
+ * \return NS domain name stored in \a rdata or NULL if \a rdata has no items.
+ */
+static const knot_dname_t *knot_rdata_ns_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return NULL;
+ }
+ return rdata->items[0].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Retrieves the domain name from SRV RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the fourth
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the SRV domain name from.
+ *
+ * \return SRV domain name stored in \a rdata or NULL if \a rdata has less than
+ * 4 items.
+ */
+static const knot_dname_t *knot_rdata_srv_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 4) {
+ return NULL;
+ }
+ return rdata->items[3].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rdata_new()
+{
+ knot_rdata_t *rdata =
+ (knot_rdata_t *)malloc(sizeof(knot_rdata_t));
+ if (rdata == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ rdata->items = NULL;
+ rdata->count = 0;
+ rdata->next = NULL;
+
+ return rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc)
+{
+ int i = 0;
+ uint8_t item_type;
+ size_t parsed = 0;
+
+ if (rdlength == 0) {
+ rdata->items = NULL;
+ return KNOT_EOK;
+ }
+
+ knot_rdata_item_t *items = (knot_rdata_item_t *)malloc(
+ desc->length * sizeof(knot_rdata_item_t));
+ CHECK_ALLOC_LOG(items, KNOT_ENOMEM);
+
+ size_t item_size = 0;
+ uint8_t gateway_type = 0; // only to handle IPSECKEY record
+ knot_dname_t *dname = NULL;
+
+ while (i < desc->length && (desc->fixed_items || parsed < rdlength)) {
+
+ item_type = desc->wireformat[i];
+ item_size = 0;
+
+ size_t pos2;
+
+ switch (item_type) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME:
+ pos2 = *pos;
+ dname = knot_dname_parse_from_wire(
+ wire, &pos2, total_size, NULL);
+ if (dname == NULL) {
+ free(items);
+ return KNOT_ERROR;
+ }
+ items[i].dname = dname;
+ //*pos += dname->size;
+ parsed += pos2 - *pos;
+ *pos = pos2;
+ dname = 0;
+ break;
+ case KNOT_RDATA_WF_BYTE:
+ if (desc->type == KNOT_RRTYPE_IPSECKEY && i == 1) {
+ gateway_type = *(wire + *pos);
+ }
+ item_size = 1;
+ break;
+ case KNOT_RDATA_WF_SHORT:
+ item_size = 2;
+ break;
+ case KNOT_RDATA_WF_LONG:
+ item_size = 4;
+ break;
+ case KNOT_RDATA_WF_UINT48:
+ item_size = 6;
+ break;
+ case KNOT_RDATA_WF_TEXT:
+ item_size = rdlength - parsed;
+ break;
+ case KNOT_RDATA_WF_TEXT_SINGLE:
+ item_size = *(wire + *pos) + 1;
+ break;
+ case KNOT_RDATA_WF_A:
+ item_size = 4;
+ break;
+ case KNOT_RDATA_WF_AAAA:
+ item_size = 16;
+ break;
+ case KNOT_RDATA_WF_BINARY:
+ item_size = rdlength - parsed;
+ break;
+ case KNOT_RDATA_WF_BINARYWITHLENGTH:
+ item_size = *(wire + *pos) + 1;
+ break;
+ case KNOT_RDATA_WF_BINARYWITHSHORT:
+ item_size = knot_wire_read_u16(wire + *pos) + 2;
+ break;
+ case KNOT_RDATA_WF_APL:
+ // WTF? what to do with this??
+ // Same as TXT, I guess.
+ item_size = rdlength - parsed;
+ break;
+ case KNOT_RDATA_WF_IPSECGATEWAY:
+ // determine size based on the 'gateway type' field
+ switch (gateway_type) {
+ case 0:
+ item_size = 0;
+ break;
+ case 1:
+ item_size = 4;
+ break;
+ case 2:
+ item_size = 16;
+ break;
+ case 3:
+ pos2 = *pos;
+ fprintf(stderr, "reading dname from pos: %zu\n", pos2);
+ dname =
+ knot_dname_parse_from_wire(
+ wire, &pos2, total_size, NULL);
+ if (dname == NULL) {
+ return KNOT_ERROR;
+ }
+ items[i].dname = dname;
+ //*pos += dname->size;
+ parsed += pos2 - *pos;
+
+ fprintf(stderr, "read %zu bytes.\n", parsed);
+ *pos = pos2;
+ dname = 0;
+ break;
+ default:
+ assert(0);
+ }
+
+ break;
+ default:
+ return KNOT_EMALF;
+
+ }
+
+ if (item_size != 0) {
+ if (parsed + item_size > rdlength) {
+ free(items);
+ return KNOT_EFEWDATA;
+ }
+
+ items[i].raw_data = (uint16_t *)malloc(item_size + 2);
+ if (items[i].raw_data == NULL) {
+ free(items);
+ return KNOT_ENOMEM;
+ }
+ memcpy(items[i].raw_data, &item_size, 2);
+ memcpy(items[i].raw_data + 1, wire + *pos, item_size);
+ *pos += item_size;
+ parsed += item_size;
+ } else if (item_type == KNOT_RDATA_WF_BINARY
+ || item_type == KNOT_RDATA_WF_IPSECGATEWAY) {
+ fprintf(stderr, "item_size was 0, creating empty rdata item.\n");
+ // in this case we are at the end of the RDATA
+ // and should create an empty RDATA item
+ items[i].raw_data = (uint16_t *)malloc(2);
+ if (items[i].raw_data == NULL) {
+ free(items);
+ return KNOT_ENOMEM;
+ }
+ memcpy(items[i].raw_data, &item_size, 2);
+ } else if (item_type != KNOT_RDATA_WF_COMPRESSED_DNAME
+ && item_type != KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ && item_type != KNOT_RDATA_WF_LITERAL_DNAME) {
+ fprintf(stderr, "RDATA item not set (i: %d), type: %u"
+ " RDATA item type: %d\n", i, desc->type ,item_type);
+ assert(0);
+ }
+
+ ++i;
+ }
+
+ assert(!desc->fixed_items || i == desc->length);
+
+ // all items are parsed, insert into the RDATA
+ int rc;
+ rc = knot_rdata_set_items(rdata, items, i);
+
+ for (int j = 0; j < i; ++j) {
+ assert(rdata->items[j].raw_data != NULL);
+ }
+
+ free(items);
+ return rc;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_set_item(knot_rdata_t *rdata, uint pos,
+ knot_rdata_item_t item)
+{
+ if (pos >= rdata->count) {
+ return KNOT_EBADARG;
+ }
+
+ /*! \todo As in set_items() we should increment refcounter for dnames,
+ * but we don't know the item type.
+ */
+
+ rdata->items[pos] = item; // this should copy the union; or use memcpy?
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+unsigned int knot_rdata_item_count(const knot_rdata_t *rdata)
+{
+ return rdata->count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_set_items(knot_rdata_t *rdata,
+ const knot_rdata_item_t *items, uint count)
+{
+ if (rdata == NULL || items == NULL || count == 0 ||
+ rdata->items != NULL) {
+ return KNOT_EBADARG;
+ }
+
+ assert(rdata->count == 0);
+ if ((rdata->items = (knot_rdata_item_t *)malloc(
+ count * sizeof(knot_rdata_item_t))) == NULL) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(rdata->items, items, count * sizeof(knot_rdata_item_t));
+ rdata->count = count;
+
+ /*! \todo Cannot determine items type, so the dname
+ * refcounters should be increased in caller.
+ */
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_item_t *knot_rdata_item(const knot_rdata_t *rdata,
+ uint pos)
+{
+ if (pos >= rdata->count) {
+ return NULL;
+ } else {
+ return &rdata->items[pos];
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_item_t *knot_rdata_get_item(const knot_rdata_t *rdata,
+ uint pos)
+{
+ if (pos >= rdata->count) {
+ return NULL;
+ } else {
+ return &rdata->items[pos];
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_item_set_dname(knot_rdata_t *rdata, uint pos,
+ knot_dname_t *dname)
+{
+ if (pos >= rdata->count) {
+ return KNOT_EBADARG;
+ }
+
+ /* Retain dname. */
+ knot_dname_retain(dname);
+
+ rdata->items[pos].dname = dname;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, uint pos,
+ uint16_t *raw_data)
+{
+ if (pos >= rdata->count) {
+ return KNOT_EBADARG;
+ }
+
+ rdata->items[pos].raw_data = raw_data;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rdata_free(knot_rdata_t **rdata)
+{
+ if (rdata == NULL || *rdata == NULL) {
+ return;
+ }
+
+ if ((*rdata)->items) {
+ free((*rdata)->items);
+ }
+ free(*rdata);
+ *rdata = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rdata_deep_free(knot_rdata_t **rdata, uint type,
+ int free_all_dnames)
+{
+ if (rdata == NULL || *rdata == NULL) {
+ return;
+ }
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+
+ assert((*rdata)->count <= desc->length);
+
+ for (int i = 0; i < (*rdata)->count; i++) {
+ if (&((*rdata)->items[i]) == NULL) {
+ continue;
+ }
+ 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 ) {
+ if (((*rdata)->items[i].dname != NULL)) {
+ /*! \todo This is hack to prevent memory errors,
+ * as the rdata_set_items() cannot determine
+ * items type and so cannot increment
+ * reference count in case of dname type.
+ * Free would then release dnames that
+ * aren't referenced by the rdata.
+ */
+ if (free_all_dnames) {
+ knot_dname_release((*rdata)->items[i].dname);
+ }
+ }
+ } else {
+ free((*rdata)->items[i].raw_data);
+ }
+ }
+
+ if ((*rdata)->items) {
+ free((*rdata)->items);
+ }
+ free(*rdata);
+ *rdata = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/* CLEANUP */
+//uint knot_rdata_wire_size(const knot_rdata_t *rdata,
+// const uint8_t *format)
+//{
+// uint size = 0;
+
+// for (int i = 0; i < rdata->count; ++i) {
+// switch (format[i]) {
+// case KNOT_RDATA_WF_COMPRESSED_DNAME:
+// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+// case KNOT_RDATA_WF_LITERAL_DNAME:
+// size += knot_dname_size(rdata->items[i].dname);
+// break;
+// case KNOT_RDATA_WF_BYTE:
+// size += 1;
+// break;
+// case KNOT_RDATA_WF_SHORT:
+// size += 2;
+// break;
+// case KNOT_RDATA_WF_LONG:
+// size += 4;
+// break;
+// case KNOT_RDATA_WF_A:
+// size += 4;
+// break;
+// case KNOT_RDATA_WF_AAAA:
+// size += 16;
+// break;
+// case KNOT_RDATA_WF_BINARY:
+// case KNOT_RDATA_WF_APL: // saved as binary
+// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
+// size += rdata->items[i].raw_data[0];
+// break;
+// case KNOT_RDATA_WF_TEXT:
+// case KNOT_RDATA_WF_BINARYWITHLENGTH:
+// size += rdata->items[i].raw_data[0] + 1;
+// break;
+// default:
+// assert(0);
+// }
+// }
+// return size;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+//int knot_rdata_to_wire(const knot_rdata_t *rdata, const uint8_t *format,
+// uint8_t *buffer, uint buf_size)
+//{
+// uint copied = 0;
+// uint8_t tmp[KNOT_MAX_RDATA_WIRE_SIZE];
+// uint8_t *to = tmp;
+
+// for (int i = 0; i < rdata->count; ++i) {
+// assert(copied < KNOT_MAX_RDATA_WIRE_SIZE);
+
+// const uint8_t *from = (uint8_t *)rdata->items[i].raw_data;
+// uint size = 0;
+
+// switch (format[i]) {
+// case KNOT_RDATA_WF_COMPRESSED_DNAME:
+// case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+// case KNOT_RDATA_WF_LITERAL_DNAME:
+// size = knot_dname_size(rdata->items[i].dname);
+// from = knot_dname_name(rdata->items[i].dname);
+
+// break;
+// case KNOT_RDATA_WF_BYTE:
+// size = 1;
+// break;
+// case KNOT_RDATA_WF_SHORT:
+// size = 2;
+// break;
+// case KNOT_RDATA_WF_LONG:
+// size = 4;
+// break;
+// case KNOT_RDATA_WF_A:
+// size = 4;
+// break;
+// case KNOT_RDATA_WF_AAAA:
+// size = 16;
+// break;
+// case KNOT_RDATA_WF_TEXT:
+// case KNOT_RDATA_WF_BINARYWITHLENGTH:
+// // size stored in the first two bytes, but in little
+// // endian and we need only the lower byte from it
+// *to = *from; // lower byte is the first in little endian
+// to += 1;
+// case KNOT_RDATA_WF_BINARY:
+// case KNOT_RDATA_WF_APL: // saved as binary
+// case KNOT_RDATA_WF_IPSECGATEWAY: // saved as binary
+// // size stored in the first two bytes, those bytes
+// // must not be copied
+// size = rdata->items[i].raw_data[0];
+// from += 2; // skip the first two bytes
+// break;
+// default:
+// assert(0);
+// }
+
+// assert(size != 0);
+// assert(copied + size < KNOT_MAX_RDATA_WIRE_SIZE);
+
+// memcpy(to, from, size);
+// to += size;
+// copied += size;
+// }
+
+// if (copied > buf_size) {
+// dbg_rdata("Not enough place allocated for function "
+// "knot_rdata_to_wire(). Allocated %u, need %u\n",
+// buf_size, copied);
+// return -1;
+// }
+
+// memcpy(buffer, tmp, copied);
+// return 0;
+//}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rdata_deep_copy(const knot_rdata_t *rdata,
+ uint16_t type)
+{
+ knot_rdata_t *copy = knot_rdata_new();
+ CHECK_ALLOC_LOG(copy, NULL);
+
+
+ if ((copy->items = (knot_rdata_item_t *)malloc(
+ rdata->count * sizeof(knot_rdata_item_t))) == NULL) {
+ knot_rdata_free(&copy);
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ copy->count = rdata->count;
+
+ knot_rrtype_descriptor_t *d = knot_rrtype_descriptor_by_type(type);
+
+ assert(copy->count <= d->length);
+
+ // copy all items one by one
+ for (int i = 0; i < copy->count; ++i) {
+ if (d->wireformat[i] == KNOT_RDATA_WF_COMPRESSED_DNAME
+ || d->wireformat[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME
+ || d->wireformat[i] == KNOT_RDATA_WF_LITERAL_DNAME) {
+ copy->items[i].dname =
+ knot_dname_deep_copy(rdata->items[i].dname);
+ } else {
+ copy->items[i].raw_data = (uint16_t *)malloc(
+ rdata->items[i].raw_data[0] + 2);
+ if (copy->items[i].raw_data == NULL) {
+ knot_rdata_deep_free(&copy, type, 1);
+ return NULL;
+ }
+ memcpy(copy->items[i].raw_data,
+ rdata->items[i].raw_data,
+ rdata->items[i].raw_data[0] + 2);
+ }
+ }
+
+ return copy;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rdata_compare(const knot_rdata_t *r1, const knot_rdata_t *r2,
+ const uint8_t *format)
+{
+ uint count = (r1->count < r2->count) ? r1->count : r2->count;
+
+ int cmp = 0;
+
+ for (int i = 0; i < count; ++i) {
+ /* CLEANUP */
+// const uint8_t *data1, *data2;
+// int size1, size2;
+
+ if (format[i] == KNOT_RDATA_WF_COMPRESSED_DNAME ||
+ format[i] == KNOT_RDATA_WF_UNCOMPRESSED_DNAME ||
+ format[i] == KNOT_RDATA_WF_LITERAL_DNAME) {
+ cmp = knot_dname_compare(r1->items[i].dname,
+ r2->items[i].dname);
+// data1 = knot_dname_name(r1->items[i].dname);
+// data2 = knot_dname_name(r2->items[i].dname);
+// size1 = knot_dname_size(r2->items[i].dname);
+// size2 = knot_dname_size(r2->items[i].dname);
+ } else {
+ cmp = knot_rdata_compare_binary(
+ (uint8_t *)(r1->items[i].raw_data + 1),
+ (uint8_t *)(r2->items[i].raw_data + 1),
+ r1->items[i].raw_data[0],
+ r1->items[i].raw_data[0]);
+// data1 = (uint8_t *)(r1->items[i].raw_data + 1);
+// data2 = (uint8_t *)(r2->items[i].raw_data + 1);
+// size1 = r1->items[i].raw_data[0];
+// size2 = r1->items[i].raw_data[0];
+ }
+
+// cmp =
+
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+
+ assert(cmp == 0);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rdata_cname_name(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return NULL;
+ }
+ return rdata->items[0].dname;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rdata_dname_target(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return NULL;
+ }
+ return rdata->items[0].dname;
+}
+
+/*---------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rdata_get_name(const knot_rdata_t *rdata,
+ uint16_t type)
+{
+ // iterate over the rdata items or act as if we knew where the name is?
+
+ switch (type) {
+ case KNOT_RRTYPE_NS:
+ return knot_rdata_ns_name(rdata);
+ case KNOT_RRTYPE_MX:
+ return knot_rdata_mx_name(rdata);
+ case KNOT_RRTYPE_SRV:
+ return knot_rdata_srv_name(rdata);
+ case KNOT_RRTYPE_CNAME:
+ return knot_rdata_cname_name(rdata);
+ }
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------------------*/
+int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return -1;
+ }
+
+ if (rdata->count < 3) {
+ return -1;
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[2].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return 0;
+ }
+
+ if (rdata->count < 4) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[3].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return 0;
+ }
+
+ if (rdata->count < 5) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[4].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata)
+{
+ if (!rdata) {
+ return -1;
+ }
+
+ if (rdata->count < 6) {
+ return 0; /*! \todo Some other error value. */
+ }
+
+ // the number is in network byte order, transform it
+ return knot_wire_read_u32((uint8_t *)(rdata->items[5].raw_data + 1));
+}
+
+/*---------------------------------------------------------------------------*/
+
+uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata)
+{
+ if (rdata->count < 1) {
+ return 0;
+ }
+
+ return knot_wire_read_u16((uint8_t *)(rdata->items[0].raw_data + 1));
+}
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
new file mode 100644
index 0000000..5d328c9
--- /dev/null
+++ b/src/libknot/rdata.h
@@ -0,0 +1,339 @@
+/*!
+ * \file rdata.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structures representing RDATA and its items and API for manipulating
+ * both.
+ *
+ * \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_RDATA_H_
+#define _KNOT_RDATA_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dname.h"
+#include "util/descriptor.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief RDATA item structure.
+ *
+ * Each RDATA may be logically divided into items, each of possible different
+ * type. This structure distinguishes between general data (\a raw_data)
+ * represented as an array of octets, and domain name (\a dname) as domain names
+ * require special treatment within some RDATA (e.g. compressing in packets).
+ */
+union knot_rdata_item {
+ knot_dname_t *dname; /*!< RDATA item represented as a domain name. */
+
+ /*!
+ * \brief RDATA item represented as raw array of octets.
+ *
+ * The first two bytes hold the length of the item in bytes. The length
+ * is stored in little endian.
+ *
+ * In some cases the stored length is also used in the wire format of
+ * RDATA (e.g. character-data as defined in RFC1035). In such case,
+ * the length should be less than 256, so that it fits into one byte
+ * in the wireformat.
+ *
+ * \todo Store the length in system byte order.
+ */
+ uint16_t *raw_data;
+};
+
+typedef union knot_rdata_item knot_rdata_item_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief RDATA structure.
+ *
+ * Each RDATA may be logically divided into items, each of possible different
+ * type (see knot_rdata_item). This structure stores an array of such items.
+ * It is not dynamic, so any RDATA structure may hold either 0 or one specified
+ * number of items which cannot be changed later. However, the number of items
+ * may be different for each RDATA structure. The number of items should be
+ * given by descriptors for each RR type. Some types may have variable number
+ * of items. In such cases, the last item in the array will be set tu NULL
+ * to distinguish the actual count of items.
+ *
+ * This structure does not hold information about the RDATA items, such as
+ * what type is which item or how long are they. This information should be
+ * stored elsewhere (in descriptors) as it is RR-specific and given for each
+ * RR type.
+ *
+ * \todo Find out whether NULL is appropriate value. If it is a possible
+ * value for some of the items, we must find some other way to deal with
+ * this.
+ * \todo Add some function for freeing particular item? Or a non-const getter?
+ */
+struct knot_rdata {
+ knot_rdata_item_t *items; /*!< RDATA items comprising this RDATA. */
+ unsigned int count; /*! < Count of RDATA items in this RDATA. */
+ struct knot_rdata *next; /*!< Next RDATA item in a linked list. */
+};
+
+typedef struct knot_rdata knot_rdata_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates an empty RDATA structure.
+ *
+ * \return Pointer to the new RDATA structure or NULL if an error occured.
+ */
+knot_rdata_t *knot_rdata_new();
+
+/*!
+ * \brief Parses RDATA from the given data in wire format.
+ *
+ * \param rdata RDATA to fill.
+ * \param wire Wire format of the whole data in which the RDATA are present.
+ * \param pos Position in \a wire where to start parsing.
+ * \param total_size Size of the whole data.
+ * \param rdlength Size of the RDATA to parse in bytes.
+ * \param desc RR type descriptor for the RDATA type.
+ *
+ * \retval KNOT_ENOMEM
+ * \retval KNOT_EFEWDATA
+ * \retval KNOT_EMALF
+ * \retval KNOT_ERROR
+ * \retval KNOT_EOK
+ */
+int knot_rdata_from_wire(knot_rdata_t *rdata, const uint8_t *wire,
+ size_t *pos, size_t total_size, size_t rdlength,
+ const knot_rrtype_descriptor_t *desc);
+
+/*!
+ * \brief Sets the RDATA item on position \a pos.
+ *
+ * \param rdata RDATA structure in which the item should be set.
+ * \param pos Position of the RDATA item to be set.
+ * \param item RDATA item value to be set.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_EBADARG if \a pos is not a valid position.
+ *
+ * \todo Use the union or a pointer to it as parameter? IMHO there is always
+ * only one pointer that is copied, so it doesn't matter.
+ */
+int knot_rdata_set_item(knot_rdata_t *rdata, unsigned int pos,
+ knot_rdata_item_t item);
+
+/*!
+ * \brief Sets all RDATA items within the given RDATA structure.
+ *
+ * \a rdata must be empty so far (\a rdata->count == 0). The necessary space
+ * is allocated.
+ *
+ * This function copies the array of RDATA items from \a items to \a rdata.
+ *
+ * \param rdata RDATA structure to store the items in.
+ * \param items An array of RDATA items to be stored in this RDATA structure.
+ * \param count Count of RDATA items to be stored.
+ *
+ * \retval 0 if successful.
+ * \retval KNOT_EBADARG
+ * \retval KNOT_ENOMEM
+ */
+int knot_rdata_set_items(knot_rdata_t *rdata,
+ const knot_rdata_item_t *items,
+ unsigned int count);
+
+unsigned int knot_rdata_item_count(const knot_rdata_t *rdata);
+
+/*!
+ * \brief Returns the RDATA item on position \a pos.
+ *
+ * \note Although returning union would be OK (no overhead), we need to be able
+ * to distinguish errors (in this case by returning NULL).
+ *
+ * \param rdata RDATA structure to get the item from.
+ * \param pos Position of the item to retrieve.
+ *
+ * \return The RDATA item on position \a pos, or NULL if such position does not
+ * exist within the given RDATA structure.
+ */
+knot_rdata_item_t *knot_rdata_get_item(const knot_rdata_t *rdata,
+ unsigned int pos);
+
+/*!
+ * \brief Returns the RDATA item on position \a pos.
+ *
+ * \note Although returning union would be OK (no overhead), we need to be able
+ * to distinguish errors (in this case by returning NULL).
+ * \note This function is identical to knot_rdata_get_item(), only it returns
+ * constant data.
+ *
+ * \param rdata RDATA structure to get the item from.
+ * \param pos Position of the item to retrieve.
+ *
+ * \return The RDATA item on position \a pos, or NULL if such position does not
+ * exist within the given RDATA structure.
+ */
+const knot_rdata_item_t *knot_rdata_item(const knot_rdata_t *rdata,
+ unsigned int pos);
+
+/*!
+ * \brief Sets the given domain name as a value of RDATA item on position
+ * \a pos.
+ *
+ * \param rdata RDATA structure to set the item in.
+ * \param pos Position of the RDATA item to set.
+ * \param dname Domain name to set to the item.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_EBADARG
+ */
+int knot_rdata_item_set_dname(knot_rdata_t *rdata, unsigned int pos,
+ knot_dname_t *dname);
+
+/*!
+ * \brief Sets the given raw data as a value of RDATA item on position \a pos.
+ *
+ * \param rdata RDATA structure to set the item in.
+ * \param pos Position of the RDATA item to set.
+ * \param raw_data Raw data to set to the item.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_EBADARG
+ */
+int knot_rdata_item_set_raw_data(knot_rdata_t *rdata, unsigned int pos,
+ uint16_t *raw_data);
+
+/*!
+ * \brief Copies the given RDATA.
+ *
+ * \param rdata RDATA to copy.
+ * \param type RR type of the RDATA.
+ *
+ * \return Copy of \a rdata.
+ */
+knot_rdata_t *knot_rdata_deep_copy(const knot_rdata_t *rdata,
+ uint16_t type);
+
+/*!
+ * \brief Destroys the RDATA structure without deleting RDATA items.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rdata RDATA structure to be destroyed.
+ */
+void knot_rdata_free(knot_rdata_t **rdata);
+
+/*!
+ * \brief Destroys the RDATA structure and all its RDATA items.
+ *
+ * RDATA items are deleted according to the given RR Type. In case of domain
+ * name, it is deallocated only if either the free_all_dnames parameter is set
+ * to <> 0 or the name does not contain reference to a node (i.e. it is not an
+ * owner of some node) or if it does contain a reference to a node, but is
+ * not equal to its owner. (If free_all_dnames is set to <> 0, no other
+ * condition is evaluated.)
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rdata RDATA structure to be destroyed.
+ * \param type RR Type of the RDATA.
+ * \param free_all_dnames Set to <> 0 if you want to delete ALL domain names
+ * from the RDATA. Set to 0 otherwise.
+ */
+void knot_rdata_deep_free(knot_rdata_t **rdata, unsigned int type,
+ int free_all_dnames);
+
+/*!
+ * \brief Compares two RDATAs of the same type.
+ *
+ * \note Compares domain names normally (dname_compare()), i.e.
+ * case-insensitive.
+ *
+ * \param r1 First RDATA.
+ * \param r2 Second RDATA.
+ * \param format Descriptor of the RDATA format.
+ *
+ * \retval 0 if RDATAs are equal.
+ * \retval < 0 if \a r1 goes before \a r2 in canonical order.
+ * \retval > 0 if \a r1 goes after \a r2 in canonical order.
+ */
+int knot_rdata_compare(const knot_rdata_t *r1, const knot_rdata_t *r2,
+ const uint8_t *format);
+
+/*!
+ * \brief Retrieves the domain name from CNAME RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the first
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the CNAME domain name from.
+ *
+ * \return Canonical name stored in \a rdata or NULL if \a rdata has no items.
+ */
+const knot_dname_t *knot_rdata_cname_name(const knot_rdata_t *rdata);
+
+/*!
+ * \brief Retrieves the domain name from DNAME RDATA.
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the first
+ * RDATA item, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the DNAME domain name from.
+ *
+ * \return Target domain name stored in \a rdata or NULL if \a rdata has no
+ * items.
+ */
+const knot_dname_t *knot_rdata_dname_target(const knot_rdata_t *rdata);
+
+/*!
+ * \brief Retrieves the domain name from RDATA of given type.
+ *
+ * Supported types:
+ * - KNOT_RRTYPE_NS
+ * - KNOT_RRTYPE_MX
+ * - KNOT_RRTYPE_SRV
+ * - KNOT_RRTYPE_CNAME
+ *
+ * \note This is only convenience function. It does not (and cannot) check if
+ * the given RDATA is of the right type, so it always returns the RDATA
+ * item according to the given type, even if it is not a domain name.
+ *
+ * \param rdata RDATA to get the domain name from.
+ * \param type RR type of the RDATA.
+ *
+ * \return Domain name stored in \a rdata or NULL if \a rdata has not enough
+ * items.
+ */
+const knot_dname_t *knot_rdata_get_name(const knot_rdata_t *rdata,
+ uint16_t type);
+
+int64_t knot_rdata_soa_serial(const knot_rdata_t *rdata);
+
+uint32_t knot_rdata_soa_refresh(const knot_rdata_t *rdata);
+uint32_t knot_rdata_soa_retry(const knot_rdata_t *rdata);
+uint32_t knot_rdata_soa_expire(const knot_rdata_t *rdata);
+
+uint16_t knot_rdata_rrsig_type_covered(const knot_rdata_t *rdata);
+
+#endif /* _KNOT_RDATA_H */
+
+/*! @} */
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
new file mode 100644
index 0000000..6083f77
--- /dev/null
+++ b/src/libknot/rrset.c
@@ -0,0 +1,719 @@
+/* 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 <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "rrset.h"
+#include "util/descriptor.h"
+#include "util/error.h"
+#include "util/utils.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+static void knot_rrset_disconnect_rdata(knot_rrset_t *rrset,
+ knot_rdata_t *prev, knot_rdata_t *rdata)
+{
+ if (prev == NULL) {
+ // find the previous RDATA in the series, as its pointer must
+ // be changed
+ prev = rdata->next;
+ while (prev->next != rdata) {
+ prev = prev->next;
+ }
+ }
+
+ assert(prev);
+ assert(prev->next == rdata);
+
+ prev->next = rdata->next;
+
+ if (rrset->rdata == rdata) {
+ if (rdata->next == rdata) {
+ rrset->rdata = NULL;
+ } else {
+ rrset->rdata = rdata->next;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl)
+{
+ knot_rrset_t *ret = malloc(sizeof(knot_rrset_t));
+ if (ret == NULL) {
+ ERR_ALLOC_FAILED;
+ return NULL;
+ }
+
+ ret->rdata = NULL;
+
+ /* Retain reference to owner. */
+ knot_dname_retain(owner);
+
+ ret->owner = owner;
+ ret->type = type;
+ ret->rclass = rclass;
+ ret->ttl = ttl;
+ ret->rrsigs = NULL;
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (rrset->rdata == NULL) {
+ rrset->rdata = rdata;
+ rrset->rdata->next = rrset->rdata;
+ } else {
+ knot_rdata_t *tmp;
+
+ tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata) {
+ tmp = tmp->next;
+ }
+ rdata->next = tmp->next;
+ tmp->next = rdata;
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_remove_rdata(knot_rrset_t *rrset,
+ const knot_rdata_t *rdata)
+{
+ if (rrset == NULL || rdata == NULL) {
+ return NULL;
+ }
+
+ knot_rdata_t *prev = NULL;
+ knot_rdata_t *rr = rrset->rdata;
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ if (desc == NULL) {
+ return NULL;
+ }
+
+ while (rr != NULL) {
+ /*! \todo maybe the dnames should be compared case-insensitive*/
+ if (knot_rdata_compare(rr, rdata, desc->wireformat) == 0) {
+ knot_rrset_disconnect_rdata(rrset, prev, rr);
+ return rr;
+ }
+ prev = rr;
+ rr = knot_rrset_rdata_get_next(rrset, rr);
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs)
+{
+ if (rrset == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ rrset->rrsigs = rrsigs;
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
+ knot_rrset_dupl_handling_t dupl)
+{
+ if (rrset == NULL || rrsigs == NULL
+ || knot_dname_compare(rrset->owner, rrsigs->owner) != 0) {
+ return KNOT_EBADARG;
+ }
+
+ int rc;
+ if (rrset->rrsigs != NULL) {
+ if (dupl == KNOT_RRSET_DUPL_MERGE) {
+ rc = knot_rrset_merge((void **)&rrset->rrsigs,
+ (void **)&rrsigs);
+ if (rc != KNOT_EOK) {
+ return rc;
+ } else {
+ return 1;
+ }
+ } else if (dupl == KNOT_RRSET_DUPL_SKIP) {
+ return 2;
+ } else if (dupl == KNOT_RRSET_DUPL_REPLACE) {
+ rrset->rrsigs = rrsigs;
+ }
+ } else {
+ rrset->rrsigs = rrsigs;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset)
+{
+ return rrset->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset)
+{
+ return rrset->owner;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner)
+{
+ if (rrset) {
+ /* Retain new owner and release old owner. */
+ knot_dname_retain(owner);
+ knot_dname_release(rrset->owner);
+ rrset->owner = owner;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_rrset_type(const knot_rrset_t *rrset)
+{
+ return rrset->type;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint16_t knot_rrset_class(const knot_rrset_t *rrset)
+{
+ return rrset->rclass;
+}
+
+/*----------------------------------------------------------------------------*/
+
+uint32_t knot_rrset_ttl(const knot_rrset_t *rrset)
+{
+ return rrset->ttl;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_t *knot_rrset_rdata(const knot_rrset_t *rrset)
+{
+ return rrset->rdata;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rdata_t *knot_rrset_rdata_next(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata)
+{
+ if (rdata == NULL) {
+ return rrset->rdata;
+ }
+ if (rdata->next == rrset->rdata) {
+ return NULL;
+ } else {
+ return rdata->next;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_get_rdata(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ return NULL;
+ } else {
+ return rrset->rdata;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
+ knot_rdata_t *rdata)
+{
+ if (rdata->next == rrset->rdata) {
+ return NULL;
+ } else {
+ return rdata->next;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset)
+{
+ int count = 0;
+ const knot_rdata_t *rdata = rrset->rdata;
+
+ while (rdata != NULL) {
+ ++count;
+ rdata = knot_rrset_rdata_next(rrset, rdata);
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ assert(0);
+ return NULL;
+ } else {
+ return rrset->rrsigs;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ assert(0);
+ return NULL;
+ } else {
+ return rrset->rrsigs;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2)
+{
+ if (r1 == NULL || r2 == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(r1->type);
+ if (desc == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // compare RDATA sets (order is not significant)
+ const knot_rdata_t *rdata1= knot_rrset_rdata(r1);
+ const knot_rdata_t *rdata2;
+
+ // find all RDATA from r1 in r2
+ while (rdata1 != NULL) {
+ rdata2 = knot_rrset_rdata(r2);
+ while (rdata2 != NULL && knot_rdata_compare(rdata1, rdata2,
+ desc->wireformat)) {
+ rdata2 = knot_rrset_rdata_next(r2, rdata2);
+ }
+
+ if (rdata2 == NULL) {
+ // RDATA from r1 not found in r2
+ return 0;
+ }
+
+ // otherwise it was found, continue with next r1 RDATA
+ rdata1 = knot_rrset_rdata_next(r1, rdata1);
+ }
+
+ // find all RDATA from r2 in r1 (this can be done in a better way)
+ rdata2 = knot_rrset_rdata(r2);
+ while (rdata2 != NULL) {
+ rdata1 = knot_rrset_rdata(r1);
+ while (rdata2 != NULL && knot_rdata_compare(rdata1, rdata2,
+ desc->wireformat)) {
+ rdata1 = knot_rrset_rdata_next(r1, rdata1);
+ }
+
+ if (rdata1 == NULL) {
+ // RDATA from r1 not found in r2
+ return 0;
+ }
+
+ // otherwise it was found, continue with next r1 RDATA
+ rdata2 = knot_rrset_rdata_next(r2, rdata2);
+ }
+
+ // all RDATA found
+ return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_rrset_rr_to_wire(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata, uint8_t **pos,
+ size_t max_size)
+{
+ int size = 0;
+
+ assert(rrset != NULL);
+ assert(rrset->owner != NULL);
+ assert(rdata != NULL);
+ assert(pos != NULL);
+ assert(*pos != NULL);
+
+ fprintf(stderr, "Max size: %zu, owner: %p, owner size: %d\n",
+ max_size, rrset->owner, rrset->owner->size);
+
+ // check if owner fits
+ if (size + knot_dname_size(rrset->owner) + 10 > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ memcpy(*pos, knot_dname_name(rrset->owner),
+ knot_dname_size(rrset->owner));
+ *pos += knot_dname_size(rrset->owner);
+ size += knot_dname_size(rrset->owner);
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ fprintf(stderr, "Wire format:\n");
+
+ // put rest of RR 'header'
+ knot_wire_write_u16(*pos, rrset->type);
+ fprintf(stderr, " Type: %u\n", rrset->type);
+ *pos += 2;
+
+ knot_wire_write_u16(*pos, rrset->rclass);
+ fprintf(stderr, " Class: %u\n", rrset->rclass);
+ *pos += 2;
+
+ knot_wire_write_u32(*pos, rrset->ttl);
+ fprintf(stderr, " TTL: %u\n", rrset->ttl);
+ *pos += 4;
+
+ // save space for RDLENGTH
+ uint8_t *rdlength_pos = *pos;
+ *pos += 2;
+
+ size += 10;
+// compr->wire_pos += size;
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(rrset->type);
+
+ uint16_t rdlength = 0;
+
+ for (int i = 0; i < rdata->count; ++i) {
+ if (max_size < size + rdlength) {
+ return KNOT_ESPACE;
+ }
+
+ switch (desc->wireformat[i]) {
+ case KNOT_RDATA_WF_COMPRESSED_DNAME:
+ case KNOT_RDATA_WF_UNCOMPRESSED_DNAME:
+ case KNOT_RDATA_WF_LITERAL_DNAME: {
+ knot_dname_t *dname =
+ knot_rdata_item(rdata, i)->dname;
+ if (size + rdlength + dname->size > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // save whole domain name
+ memcpy(*pos, knot_dname_name(dname),
+ knot_dname_size(dname));
+ fprintf(stderr, "Uncompressed dname size: %d\n",
+ knot_dname_size(dname));
+ *pos += knot_dname_size(dname);
+ rdlength += knot_dname_size(dname);
+// compr->wire_pos += dname->size;
+ break;
+ }
+ default: {
+ uint16_t *raw_data =
+ knot_rdata_item(rdata, i)->raw_data;
+
+ if (size + rdlength + raw_data[0] > max_size) {
+ return KNOT_ESPACE;
+ }
+
+ // copy just the rdata item data (without size)
+ memcpy(*pos, raw_data + 1, raw_data[0]);
+ fprintf(stderr, "Raw data size: %d\n", raw_data[0]);
+ *pos += raw_data[0];
+ rdlength += raw_data[0];
+// compr->wire_pos += raw_data[0];
+ break;
+ }
+ }
+ }
+
+ fprintf(stderr, "Max size: %zu, size: %d\n", max_size, size);
+
+ assert(size + rdlength <= max_size);
+ size += rdlength;
+ knot_wire_write_u16(rdlength_pos, rdlength);
+
+ return size;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
+ int *rr_count)
+{
+ // if no RDATA in RRSet, return
+ if (rrset->rdata == NULL) {
+ *size = 0;
+ *rr_count = 0;
+ return KNOT_EOK;
+ }
+
+
+ uint8_t *pos = wire;
+ int rrs = 0;
+ short rrset_size = 0;
+
+ const knot_rdata_t *rdata = rrset->rdata;
+ do {
+ int ret = knot_rrset_rr_to_wire(rrset, rdata, &pos,
+ *size - rrset_size);
+
+ assert(ret != 0);
+
+ if (ret < 0) {
+ // some RR didn't fit in, so no RRs should be used
+ // TODO: remove last entries from compression table
+ fprintf(stderr, "Some RR didn't fit in.\n");
+ return KNOT_ESPACE;
+ }
+
+ fprintf(stderr, "RR of size %d added.\n", ret);
+ rrset_size += ret;
+ ++rrs;
+ } while ((rdata = knot_rrset_rdata_next(rrset, rdata)) != NULL);
+
+ // the whole RRSet did fit in
+ assert(rrset_size <= *size);
+ assert(pos - wire == rrset_size);
+ *size = rrset_size;
+
+ fprintf(stderr, " Size after: %zu\n", *size);
+
+ *rr_count = rrs;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_compare(const knot_rrset_t *r1,
+ const knot_rrset_t *r2,
+ knot_rrset_compare_type_t cmp)
+{
+ if (cmp == KNOT_RRSET_COMPARE_PTR) {
+ return (r1 == r2);
+ }
+
+ int res = ((r1->rclass == r2->rclass)
+ && (r1->type == r2->type)
+ && (r1->ttl == r2->ttl)
+ && knot_dname_compare(r1->owner, r2->owner) == 0);
+
+ if (cmp == KNOT_RRSET_COMPARE_WHOLE && res) {
+ res = knot_rrset_compare_rdata(r1, r2);
+ if (res < 0) {
+ return 0;
+ }
+ }
+
+ return res;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to)
+{
+ if (from == NULL || to == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ int ret;
+
+ *to = (knot_rrset_t *)calloc(1, sizeof(knot_rrset_t));
+ CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
+
+ (*to)->owner = knot_dname_deep_copy(from->owner);
+ (*to)->rclass = from->rclass;
+ (*to)->ttl = from->ttl;
+ (*to)->type = from->type;
+ if (from->rrsigs != NULL) {
+ ret = knot_rrset_deep_copy(from->rrsigs, &(*to)->rrsigs);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(to, 1, 0, 0);
+ return ret;
+ }
+ }
+ assert((*to)->rrsigs == NULL || from->rrsigs != NULL);
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(from);
+
+ /*! \note Order of RDATA will be reversed. */
+ while (rdata != NULL) {
+ ret = knot_rrset_add_rdata(*to, knot_rdata_deep_copy(rdata,
+ knot_rrset_type(from)));
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(to, 1, 1, 1);
+ return ret;
+ }
+ rdata = knot_rrset_rdata_next(from, rdata);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to)
+{
+ *to = (knot_rrset_t *)malloc(sizeof(knot_rrset_t));
+ CHECK_ALLOC_LOG(*to, KNOT_ENOMEM);
+
+ memcpy(*to, from, sizeof(knot_rrset_t));
+
+ /* Retain owner. */
+ knot_dname_retain((*to)->owner);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_free(knot_rrset_t **rrset)
+{
+ if (rrset == NULL || *rrset == NULL) {
+ return;
+ }
+
+ /*! \todo Shouldn't we always release owner reference? */
+ knot_dname_release((*rrset)->owner);
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
+ int free_rdata, int free_rdata_dnames)
+{
+ if (rrset == NULL || *rrset == NULL) {
+ return;
+ }
+
+ if (free_rdata) {
+ knot_rdata_t *tmp_rdata;
+ knot_rdata_t *next_rdata;
+ tmp_rdata = (*rrset)->rdata;
+
+ while ((tmp_rdata != NULL)
+ && (tmp_rdata->next != (*rrset)->rdata)
+ && (tmp_rdata->next != NULL)) {
+ next_rdata = tmp_rdata->next;
+ knot_rdata_deep_free(&tmp_rdata, (*rrset)->type,
+ free_rdata_dnames);
+ tmp_rdata = next_rdata;
+ }
+
+ assert(tmp_rdata == NULL
+ || tmp_rdata->next == (*rrset)->rdata);
+
+ knot_rdata_deep_free(&tmp_rdata, (*rrset)->type,
+ free_rdata_dnames);
+ }
+
+ // RRSIGs should have the same owner as this RRSet, so do not delete it
+ if ((*rrset)->rrsigs != NULL) {
+ knot_rrset_deep_free(&(*rrset)->rrsigs, 0, 1,
+ free_rdata_dnames);
+ }
+
+ /*! \todo Release owner every time? */
+ //if (free_owner) {
+ knot_dname_release((*rrset)->owner);
+ //}
+
+ free(*rrset);
+ *rrset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_rrset_merge(void **r1, void **r2)
+{
+ knot_rrset_t *rrset1 = (knot_rrset_t *)(*r1);
+ knot_rrset_t *rrset2 = (knot_rrset_t *)(*r2);
+
+ if ((knot_dname_compare(rrset1->owner, rrset2->owner) != 0)
+ || rrset1->rclass != rrset2->rclass
+ || rrset1->type != rrset2->type
+ || rrset1->ttl != rrset2->ttl) {
+ return KNOT_EBADARG;
+ }
+
+ // add all RDATAs from rrset2 to rrset1 (i.e. concatenate linked lists)
+
+ // no RDATA in RRSet 1
+ assert(rrset1 && rrset2);
+ if (rrset1->rdata == NULL) {
+ rrset1->rdata = rrset2->rdata;
+ return KNOT_EOK;
+ }
+
+ knot_rdata_t *tmp_rdata = rrset1->rdata;
+
+ if (!tmp_rdata) {
+ return KNOT_EOK;
+ }
+
+ while (tmp_rdata->next != rrset1->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ tmp_rdata->next = rrset2->rdata;
+
+ tmp_rdata = rrset2->rdata; //maybe unnecessary, but is clearer
+
+ while (tmp_rdata->next != rrset2->rdata) {
+ tmp_rdata = tmp_rdata->next;
+ }
+
+ tmp_rdata->next = rrset1->rdata;
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
new file mode 100644
index 0000000..7754c7f
--- /dev/null
+++ b/src/libknot/rrset.h
@@ -0,0 +1,306 @@
+/*!
+ * \file rrset.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief RRSet 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_RRSET_H_
+#define _KNOT_RRSET_H_
+
+#include <stdint.h>
+
+#include "dname.h"
+#include "rdata.h"
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Structure for representing an RRSet.
+ *
+ * For definition of a RRSet see RFC2181, Section 5.
+ *
+ * As all RRs within a RRSet share the same OWNER, TYPE, CLASS and TTL (see
+ * Section 5.2 of RFC2181), there is no need to duplicate these data in the
+ * program. Distinct Resource Records are thus represented only as distinct
+ * RDATA sections of corresponding RRs.
+ */
+struct knot_rrset {
+ /*! \brief Domain name being the owner of the RRSet. */
+ knot_dname_t *owner;
+ uint16_t type; /*!< TYPE of the RRset. */
+ uint16_t rclass; /*!< CLASS of the RRSet. */
+ uint32_t ttl; /*!< TTL of the RRSet. */
+ /*!
+ * \brief First item in an ordered cyclic list of RDATA items.
+ *
+ * \note The fact that the list is cyclic will easily allow for
+ * possible round-robin rotation of RRSets.
+ */
+ knot_rdata_t *rdata;
+ struct knot_rrset *rrsigs; /*!< Set of RRSIGs covering this RRSet. */
+};
+
+typedef struct knot_rrset knot_rrset_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef enum {
+ KNOT_RRSET_COMPARE_PTR,
+ KNOT_RRSET_COMPARE_HEADER,
+ KNOT_RRSET_COMPARE_WHOLE
+} knot_rrset_compare_type_t;
+
+typedef enum {
+ KNOT_RRSET_DUPL_MERGE,
+ KNOT_RRSET_DUPL_REPLACE,
+ KNOT_RRSET_DUPL_SKIP
+} knot_rrset_dupl_handling_t;
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \brief Creates a new RRSet with the given properties.
+ *
+ * The created RRSet contains no RDATAs (i.e. is actually empty).
+ *
+ * \param owner OWNER of the RRSet.
+ * \param type TYPE of the RRSet.
+ * \param rclass CLASS of the RRSet.
+ * \param ttl TTL of the RRset.
+ *
+ * \return New RRSet structure with the given OWNER, TYPE, CLASS and TTL or NULL
+ * if an error occured.
+ */
+knot_rrset_t *knot_rrset_new(knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl);
+
+/*!
+ * \brief Adds the given RDATA to the RRSet.
+ *
+ * \param rrset RRSet to add the RDATA to.
+ * \param rdata RDATA to add to the RRSet.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ *
+ * \todo Provide some function for comparing RDATAs.
+ */
+int knot_rrset_add_rdata(knot_rrset_t *rrset, knot_rdata_t *rdata);
+
+knot_rdata_t * knot_rrset_remove_rdata(knot_rrset_t *rrset,
+ const knot_rdata_t *rdata);
+
+/*!
+ * \brief Adds RRSIG signatures to this RRSet.
+ *
+ * \param rrset RRSet to add the signatures into.
+ * \param rrsigs Set of RRSIGs covering this RRSet.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG
+ */
+int knot_rrset_set_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs);
+
+int knot_rrset_add_rrsigs(knot_rrset_t *rrset, knot_rrset_t *rrsigs,
+ knot_rrset_dupl_handling_t dupl);
+
+/*!
+ * \brief Returns the Owner of the RRSet.
+ *
+ * \param rrset RRSet to get the Owner of.
+ *
+ * \return Owner of the given RRSet.
+ */
+const knot_dname_t *knot_rrset_owner(const knot_rrset_t *rrset);
+
+/*!
+ * \todo Document me.
+ */
+knot_dname_t *knot_rrset_get_owner(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Set rrset owner to specified dname.
+ *
+ * Previous owner will be replaced if exist.
+ *
+ * \param rrset Specified RRSet.
+ * \param owner New owner dname.
+ */
+void knot_rrset_set_owner(knot_rrset_t *rrset, knot_dname_t* owner);
+
+/*!
+ * \brief Returns the TYPE of the RRSet.
+ *
+ * \param rrset RRSet to get the TYPE of.
+ *
+ * \return TYPE of the given RRSet.
+ */
+uint16_t knot_rrset_type(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the CLASS of the RRSet.
+ *
+ * \param rrset RRSet to get the CLASS of.
+ *
+ * \return CLASS of the given RRSet.
+ */
+uint16_t knot_rrset_class(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the TTL of the RRSet.
+ *
+ * \param rrset RRSet to get the TTL of.
+ *
+ * \return TTL of the given RRSet.
+ */
+uint32_t knot_rrset_ttl(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the first RDATA in the RRSet.
+ *
+ * RDATAs in a RRSet are stored in a ordered cyclic list.
+ *
+ * \note If later a round-robin rotation of RRSets is employed, the RDATA
+ * returned by this function may not be the first RDATA in canonical
+ * order.
+ *
+ * \param rrset RRSet to get the RDATA from.
+ *
+ * \return First RDATA in the given RRSet.
+ */
+const knot_rdata_t *knot_rrset_rdata(const knot_rrset_t *rrset);
+
+const knot_rdata_t *knot_rrset_rdata_next(const knot_rrset_t *rrset,
+ const knot_rdata_t *rdata);
+
+/*!
+ * \brief Returns the first RDATA in the RRSet (non-const version).
+ *
+ * RDATAs in a RRSet are stored in a ordered cyclic list.
+ *
+ * \note If later a round-robin rotation of RRSets is employed, the RDATA
+ * returned by this function may not be the first RDATA in canonical
+ * order.
+ *
+ * \param rrset RRSet to get the RDATA from.
+ *
+ * \return First RDATA in the given RRSet or NULL if there is none or if no
+ * rrset was provided (\a rrset is NULL).
+ */
+knot_rdata_t *knot_rrset_get_rdata(knot_rrset_t *rrset);
+
+knot_rdata_t *knot_rrset_rdata_get_next(knot_rrset_t *rrset,
+ knot_rdata_t *rdata);
+
+int knot_rrset_rdata_rr_count(const knot_rrset_t *rrset);
+
+/*!
+ * \brief Returns the set of RRSIGs covering the given RRSet.
+ *
+ * \param rrset RRSet to get the signatures for.
+ *
+ * \return Set of RRSIGs which cover the given RRSet or NULL if there is none or
+ * if no rrset was provided (\a rrset is NULL).
+ */
+const knot_rrset_t *knot_rrset_rrsigs(const knot_rrset_t *rrset);
+
+knot_rrset_t *knot_rrset_get_rrsigs(knot_rrset_t *rrset);
+
+int knot_rrset_compare_rdata(const knot_rrset_t *r1, const knot_rrset_t *r2);
+
+int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, size_t *size,
+ int *rr_count);
+
+/*!
+ * \brief Compares two RRSets.
+ *
+ * \note This function does not return 'standard' compare return values, because
+ * there is no way to define which RRSet is 'larger'.
+ *
+ * \param r1 First RRSet.
+ * \param r2 Second RRSet.
+ * \param cmp Type of comparison to perform.
+ *
+ * \retval <> 0 If RRSets are equal.
+ * \retval 0 if RRSets are not equal.
+ */
+int knot_rrset_compare(const knot_rrset_t *r1,
+ const knot_rrset_t *r2,
+ knot_rrset_compare_type_t cmp);
+
+/*! \todo Add unit test. */
+int knot_rrset_deep_copy(const knot_rrset_t *from, knot_rrset_t **to);
+
+/*! \todo Add unit test. */
+int knot_rrset_shallow_copy(const knot_rrset_t *from, knot_rrset_t **to);
+
+/*!
+ * \brief Destroys the RRSet structure.
+ *
+ * Does not destroy the OWNER domain name structure, nor the signatures, as
+ * these may be used elsewhere.
+ *
+ * Does not destroy RDATA structures neither, as they need special processing.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rrset RRset to be destroyed.
+ */
+void knot_rrset_free(knot_rrset_t **rrset);
+
+/*!
+ * \brief Destroys the RRSet structure and all its substructures.
+ *
+ * Also sets the given pointer to NULL.
+ *
+ * \param rrset RRset 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 free_rdata ***\todo DOCUMENT ME***
+ * \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_rrset_deep_free(knot_rrset_t **rrset, int free_owner,
+ int free_rdata, int free_rdata_dnames);
+
+/*!
+ * \brief Merges two RRSets.
+ *
+ * Merges \a r1 into \a r2 by concatenating the list of RDATAs in \a r2 after
+ * the list of RDATAs in \a r1. \a r2 is unaffected by this, though you must not
+ * destroy the RDATAs in \a r2 as they are now also in \a r1. (You may use
+ * function knot_rrset_free() though, as it does not touch RDATAs).
+ *
+ * \note Member \a rrsigs is preserved from the first RRSet.
+ *
+ * \param r1 Pointer to RRSet to be merged into.
+ * \param r2 Poitner to RRSet to be merged.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EBADARG if the RRSets could not be merged, because their
+ * Owner, Type, Class or TTL does not match.
+ */
+int knot_rrset_merge(void **r1, void **r2);
+
+#endif /* _KNOT_RRSET_H_ */
+
+/*! @} */
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
new file mode 100644
index 0000000..3178a23
--- /dev/null
+++ b/src/libknot/tsig-op.c
@@ -0,0 +1,1089 @@
+/* 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 <stdint.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "tsig.h"
+#include "tsig-op.h"
+#include "util/wire.h"
+#include "util/error.h"
+#include "util/debug.h"
+
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+
+static int b64rmap_initialized = 0;
+static uint8_t b64rmap[256];
+
+static const uint8_t b64rmap_special = 0xf0;
+static const uint8_t b64rmap_end = 0xfd;
+static const uint8_t b64rmap_space = 0xfe;
+static const uint8_t b64rmap_invalid = 0xff;
+
+/**
+ * Initializing the reverse map is not thread safe.
+ * Which is fine for NSD. For now...
+ **/
+void b64_initialize_rmap()
+{
+ int i;
+ char ch;
+
+ /* Null: end of string, stop parsing */
+ b64rmap[0] = b64rmap_end;
+
+ for (i = 1; i < 256; ++i) {
+ ch = (char)i;
+ /* Whitespaces */
+ if (isspace(ch)) {
+ b64rmap[i] = b64rmap_space;
+ }
+ /* Padding: stop parsing */
+ else if (ch == Pad64) {
+ b64rmap[i] = b64rmap_end;
+ }
+ /* Non-base64 char */
+ else {
+ b64rmap[i] = b64rmap_invalid;
+ }
+ }
+
+ /* Fill reverse mapping for base64 chars */
+ for (i = 0; Base64[i] != '\0'; ++i) {
+ b64rmap[(uint8_t)Base64[i]] = i;
+ }
+
+ b64rmap_initialized = 1;
+}
+
+int b64_pton_do(char const *src, uint8_t *target, size_t targsize)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] = ofs << 2;
+ state = 1;
+ break;
+ case 1:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 4;
+ target[tarindex+1] = (ofs & 0x0f)
+ << 4 ;
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if ((size_t)tarindex + 1 >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs >> 2;
+ target[tarindex+1] = (ofs & 0x03)
+ << 6;
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if ((size_t)tarindex >= targsize) {
+ return (-1);
+ }
+ target[tarindex] |= ofs;
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target[tarindex] != 0) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+
+int b64_pton_len(char const *src)
+{
+ int tarindex, state, ch;
+ uint8_t ofs;
+
+ state = 0;
+ tarindex = 0;
+
+ while (1) {
+ ch = *src++;
+ ofs = b64rmap[ch];
+
+ if (ofs >= b64rmap_special) {
+ /* Ignore whitespaces */
+ if (ofs == b64rmap_space) {
+ continue;
+ }
+ /* End of base64 characters */
+ if (ofs == b64rmap_end) {
+ break;
+ }
+ /* A non-base64 character. */
+ return (-1);
+ }
+
+ switch (state) {
+ case 0:
+ state = 1;
+ break;
+ case 1:
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ break;
+ }
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64) {
+ return (-1);
+ }
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (b64rmap[ch] != b64rmap_space) {
+ return (-1);
+ }
+
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0) {
+ return (-1);
+ }
+ }
+
+ return (tarindex);
+}
+
+int b64_pton(char const *src, uint8_t *target, size_t targsize)
+{
+ if (!b64rmap_initialized) {
+ b64_initialize_rmap();
+ }
+
+ if (target) {
+ return b64_pton_do(src, target, targsize);
+ } else {
+ return b64_pton_len(src);
+ }
+}
+
+#define B64BUFSIZE 65535 /*!< Buffer size for b64 conversion. */
+
+
+
+
+
+
+
+
+
+
+
+
+const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest
+
+
+static int knot_tsig_check_algorithm(const knot_rrset_t *tsig_rr)
+{
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ return KNOT_EMALF;
+ }
+
+ tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
+ if (alg == 0) {
+ /*!< \todo is this error OK? */
+ dbg_tsig("TSIG: unknown algorithm.\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_key(const knot_rrset_t *tsig_rr,
+ const knot_key_t *tsig_key)
+{
+ const knot_dname_t *tsig_name = knot_rrset_owner(tsig_rr);
+ if (!tsig_name) {
+ return KNOT_EMALF;
+ }
+
+ const char *name = knot_dname_to_str(tsig_name);
+ if (!name) {
+ return KNOT_EMALF;
+ }
+
+ if (knot_dname_compare(tsig_name, tsig_key->name) != 0) {
+ /*!< \todo which error. */
+ dbg_tsig("TSIG: unknown key: %s\n", name);
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_compute_digest(const uint8_t *wire, size_t wire_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!wire || !digest || !digest_len || !key) {
+ dbg_tsig("TSIG: digest: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ if (!key->name) {
+ dbg_tsig("TSIG: digest: no algorithm\n");
+ return KNOT_EMALF;
+ }
+
+ tsig_algorithm_t tsig_alg = key->algorithm;
+ if (tsig_alg == 0) {
+ dbg_tsig("TSIG: digest: unknown algorithm\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ /* Create digest, using length of the algorithm. */
+// *digest = malloc(sizeof(uint8_t) * tsig_alg_digest_length(tsig_alg));
+// if (!digest) {
+// ERR_ALLOC_FAILED;
+// return KNOT_ENOMEM;
+// }
+
+ /* Decode key from Base64. */
+ char decoded_key[B64BUFSIZE];
+
+ int decoded_key_size = b64_pton(key->secret, (uint8_t *)decoded_key,
+ B64BUFSIZE);
+ if (decoded_key_size < 0) {
+ dbg_tsig("TSIG: Could not decode Base64\n");
+ return KNOT_EMALF;
+ }
+
+ dbg_tsig("TSIG: decoded key size: %d\n", decoded_key_size);
+ dbg_tsig("TSIG: decoded key: '%*s'\n", decoded_key_size, decoded_key);
+
+ dbg_tsig("TSIG: using this wire for digest calculation\n");
+
+ //dbg_tsig_hex(wire, wire_len);
+
+ /* Compute digest. */
+ HMAC_CTX ctx;
+
+ switch (tsig_alg) {
+ case KNOT_TSIG_ALG_HMAC_MD5:
+ HMAC_Init(&ctx, decoded_key,
+ decoded_key_size, EVP_md5());
+ break;
+ default:
+ return KNOT_ENOTSUP;
+ } /* switch */
+
+ unsigned tmp_dig_len = *digest_len;
+ HMAC_Update(&ctx, (const unsigned char *)wire, wire_len);
+ HMAC_Final(&ctx, digest, &tmp_dig_len);
+ *digest_len = tmp_dig_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_time_signed(const knot_rrset_t *tsig_rr)
+{
+ if (!tsig_rr) {
+ return KNOT_EBADARG;
+ }
+
+ /* Get the time signed and fudge values. */
+ uint64_t time_signed = tsig_rdata_time_signed(tsig_rr);
+ if (time_signed == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+ uint16_t fudge = tsig_rdata_fudge(tsig_rr);
+ if (fudge == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ /* Get the current time. */
+ time_t curr_time = time(NULL);
+
+ /*!< \todo bleeding eyes. */
+ if (difftime(curr_time, (time_t)time_signed) > fudge) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_write_tsig_timers(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ // put time signed
+ knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+
+ // put fudge
+ knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_write_tsig_variables(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ /* Copy TSIG variables - starting with key name. */
+ const knot_dname_t *tsig_owner = knot_rrset_owner(tsig_rr);
+ if (!tsig_owner) {
+ dbg_tsig("TSIG: write variables: no owner.\n");
+ return KNOT_EBADARG;
+ }
+
+ int offset = 0;
+
+ memcpy(wire + offset, knot_dname_name(tsig_owner),
+ sizeof(uint8_t) * knot_dname_size(tsig_owner));
+ dbg_tsig("TSIG: write variables: written owner (tsig alg): \n");
+ /*knot_rrset_class(tsig_rr));*/
+ dbg_tsig_hex_detail(wire + offset, knot_dname_size(tsig_owner));
+ offset += knot_dname_size(tsig_owner);
+
+ /*!< \todo which order? */
+
+ /* Copy class. */
+ knot_wire_write_u16(wire + offset, knot_rrset_class(tsig_rr));
+ dbg_tsig("TSIG: write variables: written CLASS: %u - ",
+ knot_rrset_class(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset, sizeof(uint16_t));
+ offset += sizeof(uint16_t);
+
+ /* Copy TTL - always 0. */
+ knot_wire_write_u32(wire + offset, knot_rrset_ttl(tsig_rr));
+ dbg_tsig("TSIG: write variables: written TTL: %u - ",
+ knot_rrset_ttl(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset, sizeof(uint32_t));
+ offset += sizeof(uint32_t);
+
+ /* Copy alg name. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ dbg_tsig("TSIG: write variables: no algorithm name.\n");
+ return KNOT_EBADARG;
+ }
+// alg_name = knot_dname_new_from_str("HMAC-MD5.SIG-ALG.REG.INT.",
+ //strlen("HMAC-MD5.SIG-ALG.REG.INT."),
+ //NULL);
+
+ memcpy(wire + offset, knot_dname_name(alg_name),
+ sizeof(uint8_t) * knot_dname_size(alg_name));
+ offset += knot_dname_size(alg_name);
+ dbg_tsig_detail("TSIG: write variables: written alg name: %s\n",
+ knot_dname_to_str(alg_name));
+
+ /* Following data are written in network order. */
+ /* Time signed. */
+ knot_wire_write_u48(wire + offset, tsig_rdata_time_signed(tsig_rr));
+ offset += 6;
+ dbg_tsig_detail("TSIG: write variables: time signed: %llu - ",
+ tsig_rdata_time_signed(tsig_rr));
+ dbg_tsig_hex_detail(wire + offset - 6, 6);
+ /* Fudge. */
+ knot_wire_write_u16(wire + offset, tsig_rdata_fudge(tsig_rr));
+ offset += sizeof(uint16_t);
+ dbg_tsig_detail("TSIG: write variables: fudge: %hu\n",
+ tsig_rdata_fudge(tsig_rr));
+ /* TSIG error. */
+ knot_wire_write_u16(wire + offset, tsig_rdata_error(tsig_rr));
+ offset += sizeof(uint16_t);
+ /* Get other data length. */
+ uint16_t other_data_length = tsig_rdata_other_data_length(tsig_rr);
+ /* Get other data. */
+ const uint8_t *other_data = tsig_rdata_other_data(tsig_rr);
+ if (!other_data) {
+ dbg_tsig("TSIG: write variables: no other data.\n");
+ return KNOT_EBADARG;
+ }
+
+ /*
+ * We cannot write the whole other_data, as it contains its length in
+ * machine order.
+ */
+ knot_wire_write_u16(wire + offset, other_data_length);
+ offset += sizeof(uint16_t);
+
+ /* Skip the length. */
+ dbg_tsig_detail("Copying other data.\n");
+ memcpy(wire + offset, other_data, other_data_length);
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_wire_write_timers(uint8_t *wire,
+ const knot_rrset_t *tsig_rr)
+{
+ knot_wire_write_u48(wire, tsig_rdata_time_signed(tsig_rr));
+ knot_wire_write_u16(wire + 6, tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_create_sign_wire(const uint8_t *msg, size_t msg_len,
+ /*size_t msg_max_len, */const uint8_t *request_mac,
+ size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ dbg_tsig("TSIG: create wire: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+// knot_rrset_t *tmp_tsig =
+// knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+// if (!tmp_tsig) {
+// return KNOT_ENOMEM;
+// }
+
+// tsig_rdata_store_current_time(tmp_tsig);
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig varibles.
+ */
+ dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, request_mac_len,
+ tsig_rdata_tsig_variables_length(tmp_tsig));
+ size_t wire_len = sizeof(uint8_t) *
+ (msg_len + request_mac_len + ((request_mac_len > 0)
+ ? 2 : 0) +
+ tsig_rdata_tsig_variables_length(tmp_tsig));
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ uint8_t *pos = wire;
+
+ /* Copy the request MAC - should work even if NULL. */
+ if (request_mac_len > 0) {
+ dbg_tsig_detail("Copying request MAC size\n");
+ knot_wire_write_u16(pos, request_mac_len);
+ pos += 2;
+ }
+ dbg_tsig("Copying request mac.\n");
+ memcpy(pos, request_mac, sizeof(uint8_t) * request_mac_len);
+ dbg_tsig_detail("TSIG: create wire: request mac: ");
+ dbg_tsig_hex_detail(pos, request_mac_len);
+ pos += request_mac_len;
+ /* Copy the original message. */
+ dbg_tsig("Copying original message.\n");
+ memcpy(pos, msg, msg_len);
+ dbg_tsig_detail("TSIG: create wire: original message: \n");
+ //dbg_tsig_hex_detail(pos, msg_len);
+ pos += msg_len;
+ /* Copy TSIG variables. */
+ dbg_tsig("Writing TSIG variables.\n");
+ ret = knot_tsig_write_tsig_variables(pos, tmp_tsig);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to write TSIG "
+ "variables: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
+ knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+// assert(digest_tmp_len > 0);
+ free(wire);
+
+// if (digest_tmp_len > *digest_len) {
+// *digest_len = 0;
+// return KNOT_ESPACE;
+// }
+
+// knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ // everything went ok, save the digest to the output parameter
+// memcpy(digest, digest_tmp, digest_tmp_len);
+// *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_create_sign_wire_next(const uint8_t *msg, size_t msg_len,
+ const uint8_t *prev_mac,
+ size_t prev_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ dbg_tsig("TSIG: create wire: bad args.\n");
+ return KNOT_EBADARG;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig varibles.
+ */
+ dbg_tsig("Counting wire size: %zu, %zu, %zu.\n",
+ msg_len, prev_mac_len,
+ tsig_rdata_tsig_timers_length());
+ size_t wire_len = sizeof(uint8_t) *
+ (msg_len + prev_mac_len +
+ tsig_rdata_tsig_timers_length());
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ /* Copy the request MAC - should work even if NULL. */
+ dbg_tsig("Copying request mac.\n");
+ memcpy(wire, prev_mac, sizeof(uint8_t) * prev_mac_len);
+ dbg_tsig_detail("TSIG: create wire: request mac: ");
+ dbg_tsig_hex_detail(wire, prev_mac_len);
+ /* Copy the original message. */
+ dbg_tsig("Copying original message.\n");
+ memcpy(wire + prev_mac_len, msg, msg_len);
+ dbg_tsig_detail("TSIG: create wire: original message: \n");
+ //dbg_tsig_hex_detail(wire + prev_mac_len, msg_len);
+ /* Copy TSIG variables. */
+
+ dbg_tsig("Writing TSIG timers.\n");
+ ret = knot_tsig_write_tsig_timers(wire + prev_mac_len + msg_len,
+ tmp_tsig);
+// ret = knot_tsig_write_tsig_variables(wire + prev_mac_len + msg_len,
+// tmp_tsig);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to write TSIG "
+ "timers: %s\n", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: create wire: failed to compute digest: %s\n",
+ knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+ free(wire);
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len,
+ size_t msg_max_len, const uint8_t *request_mac,
+ size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ knot_dname_t *key_name_copy = knot_dname_deep_copy(key->name);
+ if (!key_name_copy) {
+ dbg_tsig_detail("TSIG: key_name_copy = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_t *tmp_tsig =
+ knot_rrset_new(key_name_copy,
+ KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ if (!tmp_tsig) {
+ dbg_tsig_detail("TSIG: tmp_tsig = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ /* Create rdata for TSIG RR. */
+ knot_rdata_t *rdata = knot_rdata_new();
+ if (!rdata) {
+ dbg_tsig_detail("TSIG: rdata = NULL\n");
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_add_rdata(tmp_tsig, rdata);
+
+ /* Create items for TSIG RR. */
+ knot_rrtype_descriptor_t *desc =
+ knot_rrtype_descriptor_by_type(KNOT_RRTYPE_TSIG);
+ assert(desc);
+
+ knot_rdata_item_t *items =
+ malloc(sizeof(knot_rdata_item_t) * desc->length);
+ if (!items) {
+ dbg_tsig_detail("TSIG: items = NULL\n");
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(items, 0, sizeof(knot_rdata_item_t) * desc->length);
+
+ int ret = knot_rdata_set_items(rdata, items, desc->length);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rdata_set_items returned %s\n", knot_strerror(ret));
+ return ret;
+ }
+ free(items);
+
+ tsig_rdata_set_alg(tmp_tsig, key->algorithm);
+ tsig_rdata_store_current_time(tmp_tsig);
+ tsig_rdata_set_fudge(tmp_tsig, 300);
+
+ /* Set original ID */
+ tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set error */
+ /*! \todo [TSIG] Set error and other data if appropriate. */
+ tsig_rdata_set_tsig_error(tmp_tsig, 0);
+
+ /* Set other len. */
+ tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+
+ dbg_tsig_detail("tmp_tsig before sign_wire():\n");
+ knot_rrset_dump(tmp_tsig, 0);
+
+ ret = knot_tsig_create_sign_wire(msg, *msg_len, /*msg_max_len,*/
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tmp_tsig, key);
+ if (ret != KNOT_EOK) {
+ dbg_tsig("TSIG: could not create wire or sign wire: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Set the digest. */
+ size_t tsig_wire_len = msg_max_len - *msg_len;
+ int rr_count = 0;
+ tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
+
+ //knot_rrset_dump(tmp_tsig, 1);
+
+ /* Write RRSet to wire */
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_len, &rr_count);
+ if (ret != KNOT_EOK) {
+ dbg_tsig_detail("TSIG: rrset_to_wire = %s\n", knot_strerror(ret));
+ *digest_len = 0;
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_len;
+
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ // everything went ok, save the digest to the output parameter
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key)
+{
+ if (!msg || !msg_len || !key || !key || !digest || !digest_len) {
+ return KNOT_EBADARG;
+ }
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+
+ /* Create tmp TSIG. */
+ knot_rrset_t *tmp_tsig =
+ knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, KNOT_CLASS_ANY, 0);
+ if (!tmp_tsig) {
+ return KNOT_ENOMEM;
+ }
+
+ tsig_rdata_store_current_time(tmp_tsig);
+
+ /* Create wire to be signed. */
+ size_t wire_len = prev_digest_len + *msg_len + KNOT_TSIG_TIMERS_LENGTH;
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+ memset(wire, 0, wire_len);
+
+ /* Write previous digest. */
+ memcpy(wire, prev_digest, sizeof(uint8_t) * prev_digest_len);
+ /* Write original message. */
+ memcpy(msg + prev_digest_len, msg, *msg_len);
+ /* Write timers. */
+ knot_tsig_wire_write_timers(msg + prev_digest_len + *msg_len, tmp_tsig);
+
+ int ret = 0;
+ ret = knot_tsig_compute_digest(wire, wire_len,
+ digest_tmp, &digest_tmp_len, key);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ return ret;
+ }
+
+ if (digest_tmp_len > *digest_len) {
+ *digest_len = 0;
+ return KNOT_ESPACE;
+ }
+
+ free(wire);
+
+ /* Set the MAC. */
+ tsig_rdata_set_mac(tmp_tsig, *digest_len, digest);
+
+ size_t tsig_wire_size = msg_max_len - *msg_len;
+ int rr_count = 0;
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ &tsig_wire_size, &rr_count);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ return ret;
+ }
+
+ knot_rrset_deep_free(&tmp_tsig, 1, 1, 1);
+
+ *msg_len += tsig_wire_size;
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int knot_tsig_check_digest(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac,
+ size_t request_mac_len,
+ const knot_key_t *tsig_key,
+ int use_times)
+{
+ if (!tsig_rr || !wire || !tsig_key) {
+ return KNOT_EBADARG;
+ }
+
+ /* Check time signed. */
+ int ret = knot_tsig_check_time_signed(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: time checked.\n");
+
+ /* Check that libknot knows the algorithm. */
+ ret = knot_tsig_check_algorithm(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: algorithm checked.\n");
+
+ /* Check that key is valid, ie. the same as given in args. */
+ ret = knot_tsig_check_key(tsig_rr, tsig_key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_tsig("TSIG: key validity checked.\n");
+
+ /* Time OK algorithm OK, key name OK - do digest. */
+ /* Calculate the size of TSIG RR. */
+ size_t tsig_len = tsig_wire_actsize(tsig_rr);
+
+ dbg_tsig_detail("TSIG: check digest: wire before strip: \n");
+ //dbg_tsig_hex_detail(wire, size);
+
+ /* Strip the TSIG. */
+ size -= tsig_len;
+
+ dbg_tsig_detail("TSIG: check digest: wire after strip (stripped %zu):\n",
+ tsig_len);
+ //dbg_tsig_hex_detail(wire, size);
+
+ uint8_t *wire_to_sign = malloc(sizeof(uint8_t) * size);
+ if (!wire_to_sign) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire_to_sign, 0, sizeof(uint8_t) * size);
+ memcpy(wire_to_sign, wire, size);
+
+ /* Decrease arcount. */
+ knot_wire_set_arcount(wire_to_sign,
+ knot_wire_get_arcount(wire_to_sign) - 1);
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+ assert(tsig_rr->rdata);
+
+ if (use_times) {
+ ret = knot_tsig_create_sign_wire_next(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ } else {
+ ret = knot_tsig_create_sign_wire(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ }
+
+ assert(tsig_rr->rdata);
+ free(wire_to_sign);
+
+ if (ret != KNOT_EOK) {
+ dbg_tsig("Failed to create wire format for checking: %s.\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+// uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+// size_t digest_tmp_len = 0;
+// ret = knot_tsig_compute_digest(wire, size, digest_tmp,
+// &digest_tmp_len, tsig_key);
+// if (ret != KNOT_EOK) {
+// dbg_tsig("TSIG: digest could not be calculated\n");
+// return ret;
+// }
+
+ dbg_tsig("TSIG: digest calculated\n");
+
+ /* Compare MAC from TSIG RR RDATA with just computed digest. */
+
+ /*!< \todo move to function. */
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig_rr);
+ tsig_algorithm_t alg = tsig_alg_from_name(alg_name);
+
+ /*! \todo [TSIG] TRUNCATION */
+ uint16_t mac_length = tsig_rdata_mac_length(tsig_rr);
+ const uint8_t *tsig_mac = tsig_rdata_mac(tsig_rr);
+
+ if (mac_length != tsig_alg_digest_length(alg)) {
+ dbg_tsig("TSIG: calculated digest length and given length do not match!\n");
+ return KNOT_TSIG_EBADSIG;
+ }
+
+// assert(tsig_alg_digest_length(alg) == mac_length);
+
+ dbg_tsig("TSIG: calc digest : ");
+ dbg_tsig_hex(digest_tmp, digest_tmp_len);
+
+ dbg_tsig("TSIG: given digest: ");
+ dbg_tsig_hex(tsig_mac, mac_length);
+
+ if (strncasecmp((char *)(tsig_mac), (char *)digest_tmp,
+ mac_length) != 0) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_key_t *tsig_key)
+{
+ dbg_tsig_verb("tsig_server_check()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0);
+}
+
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_key_t *tsig_key)
+{
+ dbg_tsig_verb("tsig_client_check()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, request_mac,
+ request_mac_len, tsig_key, 0);
+}
+
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_key_t *tsig_key)
+{
+// return knot_tsig_client_check(tsig_rr, wire, size, prev_digest,
+// prev_digest_len, tsig_key);
+ dbg_tsig_verb("tsig_client_check_next()\n");
+ return knot_tsig_check_digest(tsig_rr, wire, size, prev_digest,
+ prev_digest_len, tsig_key, 1);
+ return KNOT_ENOTSUP;
+}
diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h
new file mode 100644
index 0000000..b206dc7
--- /dev/null
+++ b/src/libknot/tsig-op.h
@@ -0,0 +1,161 @@
+/*!
+ * \file tsig-op.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief TSIG signing and validating.
+ *
+ * \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_TSIG_OP_H_
+#define _KNOT_TSIG_OP_H_
+
+#include <stdint.h>
+
+#include "tsig.h"
+#include "rrset.h"
+
+/*!
+ * \brief Generate TSIG signature of a message.
+ *
+ * This function generates TSIG digest of the given message prepended with the
+ * given Request MAC (if any) and appended with TSIG Variables. It also appends
+ * the resulting TSIG RR to the message wire format and accordingly adjusts
+ * the message size.
+ *
+ * \note This function does not save the new digest to the 'digest' parameter
+ * unless everything went OK. This allows to sent the same buffer to
+ * the 'request_mac' and 'digest' parameters.
+ *
+ * \param msg Message to be signed.
+ * \param msg_len Size of the message in bytes.
+ * \param msg_max_len Maximum size of the message in bytes.
+ * \param request_mac Request MAC. (may be NULL).
+ * \param request_mac_len Size of the request MAC in bytes.
+ * \param digest Buffer to save the digest in.
+ * \param digest_len In: size of the buffer. Out: real size of the digest saved.
+ * \param tsig_rr RRSet containing the TSIG RR to be used. Data from the RR are
+ * appended to the signed message.
+ *
+ * \retval KNOT_EOK if everything went OK.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *request_mac, size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key);
+
+/*!
+ * \brief Generate TSIG signature of a 2nd or later message in a TCP session.
+ *
+ * This function generates TSIG digest of the given message prepended with the
+ * given Request MAC (if any) and appended with TSIG Variables. It also appends
+ * the resulting TSIG RR to the message wire format and accordingly adjusts
+ * the message size.
+ *
+ * \note This function does not save the new digest to the 'digest' parameter
+ * unless everything went OK. This allows to sent the same buffer to
+ * the 'request_mac' and 'digest' parameters.
+ *
+ * \param msg Message to be signed.
+ * \param msg_len Size of the message in bytes.
+ * \param msg_max_len Maximum size of the message in bytes.
+ * \param prev_digest Previous digest sent by the server in the session.
+ * \param prev_digest_len Size of the previous digest in bytes.
+ * \param digest Buffer to save the digest in.
+ * \param digest_len In: size of the buffer. Out: real size of the digest saved.
+ * \param tsig_rr RRSet containing the TSIG RR to be used. Data from the RR are
+ * appended to the signed message.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_key_t *key);
+
+/*!
+ * \brief Checks incoming request.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_key_t *tsig_key);
+
+/*!
+ * \brief Checks incoming response.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param request_mac Request MAC. (may be NULL).
+ * \param request_mac_len Size of the request MAC in bytes.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_key_t *key);
+
+/*!
+ * \brief Checks signature of 2nd or next packet in a TCP session.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param prev_digest Previous digest sent by the server in the session.
+ * \param prev_digest_len Size of the previous digest in bytes.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_key_t *key);
+
+#endif /* _KNOT_TSIG_H_ */
+
+/*! @} */
diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c
new file mode 100644
index 0000000..432539f
--- /dev/null
+++ b/src/libknot/tsig.c
@@ -0,0 +1,618 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <time.h>
+
+#include "tsig.h"
+#include "util/error.h"
+#include "util/debug.h"
+#include "common.h"
+#include "util/utils.h"
+#include "rrset.h"
+#include "rdata.h"
+#include "dname.h"
+
+/*! \brief TSIG algorithms table. */
+#define TSIG_ALG_TABLE_SIZE 8
+static knot_lookup_table_t tsig_alg_table[TSIG_ALG_TABLE_SIZE] = {
+ { KNOT_TSIG_ALG_GSS_TSIG, "gss-tsig." },
+ { KNOT_TSIG_ALG_HMAC_MD5, "hmac-md5.sig-alg.reg.int." },
+ { KNOT_TSIG_ALG_HMAC_SHA1, "hmac-sha1." },
+ { KNOT_TSIG_ALG_HMAC_SHA224, "hmac-sha224." },
+ { KNOT_TSIG_ALG_HMAC_SHA256, "hmac-sha256." },
+ { KNOT_TSIG_ALG_HMAC_SHA384, "hmac-sha384." },
+ { KNOT_TSIG_ALG_HMAC_SHA512, "hmac-sha512." },
+ { KNOT_TSIG_ALG_NULL, NULL }
+};
+
+int tsig_rdata_init(knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ /* Initializes rdata. */
+ tsig->rdata = knot_rdata_new();
+ if (!tsig->rdata) {
+ return KNOT_ENOMEM;
+ }
+
+ tsig->rdata->items =
+ malloc(sizeof(knot_rdata_item_t) * KNOT_TSIG_ITEM_COUNT);
+ if (!tsig->rdata->items) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(tsig->rdata->items, 0,
+ sizeof(knot_rdata_item_t) * KNOT_TSIG_ITEM_COUNT);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 1);
+
+ knot_dname_t *alg_name_copy = knot_dname_deep_copy(alg_name);
+ if (!alg_name_copy) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rdata_item_set_dname(rdata, 0, alg_name_copy);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_alg(knot_rrset_t *tsig, tsig_algorithm_t alg)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 1);
+
+ const char *alg_str = tsig_alg_to_str(alg);
+ knot_dname_t *alg_name_copy = knot_dname_new_from_str(alg_str,
+ strlen(alg_str),
+ 0);
+ if (!alg_name_copy) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rdata_item_set_dname(rdata, 0, alg_name_copy);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 2);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 6 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 6. */
+ wire[0] = 6;
+ knot_wire_write_u48((uint8_t *)(wire + 1), time);
+
+ knot_rdata_item_set_raw_data(rdata, 1, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 3);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 2 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 2. */
+ wire[0] = sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), fudge);
+
+ knot_rdata_item_set_raw_data(rdata, 2, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 4);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * length + 2 * sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length. */
+ wire[0] = length + sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), length);
+ /* Copy the actual MAC. */
+ memcpy((uint8_t *)(wire + 2), mac, sizeof(uint8_t) * length);
+ knot_rdata_item_set_raw_data(rdata, 3, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 5);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 2 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 2. */
+ wire[0] = sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), id);
+
+ knot_rdata_item_set_raw_data(rdata, 4, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 6);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * 2 + sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length - 2. */
+ wire[0] = sizeof(uint16_t);
+ knot_wire_write_u16((uint8_t *)(wire + 1), tsig_error);
+
+ knot_rdata_item_set_raw_data(rdata, 5, wire);
+
+ return KNOT_EOK;
+}
+
+int tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *other_data)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+
+ knot_rdata_t *rdata = knot_rrset_get_rdata(tsig);
+ if (!rdata) {
+ return KNOT_EBADARG;
+ }
+ assert(knot_rdata_item_count(rdata) >= 6);
+
+ /* Create the wire format. */
+ uint16_t *wire = malloc(sizeof(uint8_t) * length + 2 * sizeof(uint16_t));
+ if (!wire) {
+ ERR_ALLOC_FAILED;
+ return KNOT_ENOMEM;
+ }
+
+ /* Write the length. */
+ wire[0] = length + 2;
+ knot_wire_write_u16((uint8_t *)(wire + 1), length);
+ /* Copy the actual data. */
+ memcpy(wire + 2, other_data, sizeof(uint8_t) * length);
+ knot_rdata_item_set_raw_data(rdata, 6, wire);
+
+ return KNOT_EOK;
+}
+
+const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return NULL;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ dbg_tsig("TSIG: rdata: alg name: no rdata.\n");
+ return NULL;
+ }
+
+ if (knot_rdata_item_count(rdata) < 1) {
+ dbg_tsig("TSIG: rdata: alg name: not enough items.\n");
+ return NULL;
+ }
+
+ return knot_rdata_item(rdata, 0)->dname;
+}
+
+tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig)
+{
+ /*! \todo [TSIG] Implement me. */
+ return KNOT_TSIG_ALG_HMAC_MD5;
+}
+
+uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 2) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 1)->raw_data;
+ assert(wire[0] == 6);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u48((uint8_t *)wire);
+}
+
+uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 3) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 2)->raw_data;
+ assert(wire[0] == 2);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u16((uint8_t *)wire);
+}
+
+const uint8_t *tsig_rdata_mac(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 4) {
+ return 0;
+ }
+
+ return (uint8_t*)(knot_rdata_item(rdata, 3)->raw_data + 2);
+}
+
+size_t tsig_rdata_mac_length(const knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata || knot_rdata_item_count(rdata) < 4) {
+ return 0;
+ }
+
+ return knot_wire_read_u16(
+ (uint8_t *)(knot_rdata_item(rdata, 3)->raw_data + 1));
+}
+
+uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 5) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 4)->raw_data;
+ assert(wire[0] == 2);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u16((uint8_t *)wire);
+}
+
+uint16_t tsig_rdata_error(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 6) {
+ return 0;
+ }
+
+ uint16_t *wire = knot_rdata_item(rdata, 5)->raw_data;
+ assert(wire[0] == 2);
+ /* Skip the size. */
+ wire++;
+
+ return knot_wire_read_u16((uint8_t *)wire);
+}
+
+const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 7) {
+ return 0;
+ }
+
+ return (uint8_t *)(knot_rdata_item(rdata, 6)->raw_data + 2);
+}
+
+uint16_t tsig_rdata_other_data_length(const knot_rrset_t *tsig)
+{
+ /*!< \note How about assert. Or maybe change API??? */
+ if (!tsig) {
+ return 0;
+ }
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(tsig);
+ if (!rdata) {
+ return 0;
+ }
+
+ if (knot_rdata_item_count(rdata) < 7) {
+ return 0;
+ }
+
+ return knot_wire_read_u16((uint8_t *)
+ (knot_rdata_item(rdata, 6)->raw_data + 1));
+}
+
+int tsig_alg_from_name(const knot_dname_t *alg_name)
+{
+ if (!alg_name) {
+ return 0;
+ }
+
+ char *name = knot_dname_to_str(alg_name);
+ if (!name) {
+ return 0;
+ }
+
+ knot_lookup_table_t *found =
+ knot_lookup_by_name(tsig_alg_table, name);
+
+ if (!found) {
+ dbg_tsig("Unknown algorithm: %s \n", name);
+ free(name);
+ return 0;
+ }
+
+ free(name);
+
+ return found->id;
+}
+
+uint16_t tsig_alg_digest_length(tsig_algorithm_t alg)
+{
+ switch (alg) {
+ case KNOT_TSIG_ALG_GSS_TSIG:
+ return KNOT_TSIG_ALG_DIG_LENGTH_GSS_TSIG;
+ case KNOT_TSIG_ALG_HMAC_MD5:
+ return KNOT_TSIG_ALG_DIG_LENGTH_HMAC_MD5;
+ case KNOT_TSIG_ALG_HMAC_SHA1:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA1;
+ case KNOT_TSIG_ALG_HMAC_SHA224:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA224;
+ case KNOT_TSIG_ALG_HMAC_SHA256:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA384;
+ case KNOT_TSIG_ALG_HMAC_SHA512:
+ return KNOT_TSIG_ALG_DIG_LENGTH_SHA512;
+ default:
+ return 0;
+ } /* switch(alg) */
+}
+
+size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig)
+{
+ /* Key name, Algorithm name and Other data have variable lengths. */
+ const knot_dname_t *key_name = knot_rrset_owner(tsig);
+ if (!key_name) {
+ return 0;
+ }
+
+ const knot_dname_t *alg_name = tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ return 0;
+ }
+
+// dbg_tsig_detail("key_name: %.*s (size: %u) alg_name: %.*s (size: %u)\n", knot_dname_size(key_name),
+// key_name->name, alg_name->size, alg_name->name,
+// key_name->size, alg_name->size);
+
+// dbg_tsig_hex_detail(key_name->name, key_name->size);
+// dbg_tsig_hex_detail(alg_name->name, alg_name->size);
+
+ uint16_t other_data_length = tsig_rdata_other_data_length(tsig);
+
+ return knot_dname_size(key_name) + knot_dname_size(alg_name) +
+ other_data_length + KNOT_TSIG_VARIABLES_LENGTH;
+}
+
+size_t tsig_rdata_tsig_timers_length()
+{
+ return KNOT_TSIG_TIMERS_LENGTH;
+}
+
+
+int tsig_rdata_store_current_time(knot_rrset_t *tsig)
+{
+ if (!tsig) {
+ return KNOT_EBADARG;
+ }
+ time_t curr_time = time(NULL);
+ /*!< \todo bleeding eyes. */
+ tsig_rdata_set_time_signed(tsig, (uint64_t)curr_time);
+ return KNOT_EOK;
+}
+
+const char* tsig_alg_to_str(tsig_algorithm_t alg)
+{
+ for (unsigned i = 0; i < TSIG_ALG_TABLE_SIZE; ++i) {
+ if (tsig_alg_table[i].id == alg) {
+ return tsig_alg_table[i].name;
+ }
+ }
+
+ return "";
+}
+
+size_t tsig_wire_maxsize(const knot_key_t* key)
+{
+ size_t alg_name_size = strlen(tsig_alg_to_str(key->algorithm)) + 1;
+
+ return knot_dname_size(key->name) +
+ sizeof(uint16_t) + /* TYPE */
+ sizeof(uint16_t) + /* CLASS */
+ sizeof(uint32_t) + /* TTL */
+ sizeof(uint16_t) + /* RDLENGTH */
+ alg_name_size + /* Alg. name */
+ 6 * sizeof(uint8_t) + /* Time signed */
+ sizeof(uint16_t) + /* Fudge */
+ sizeof(uint16_t) + /* MAC size */
+ tsig_alg_digest_length(key->algorithm) + /* MAC */
+ sizeof(uint16_t) + /* Original ID */
+ sizeof(uint16_t) + /* Error */
+ sizeof(uint16_t) + /* Other len */
+ 6* sizeof(uint8_t); /* uint48_t in case of BADTIME RCODE */
+}
+
+size_t tsig_wire_actsize(const knot_rrset_t *tsig)
+{
+ return knot_dname_size(knot_rrset_owner(tsig)) +
+ sizeof(uint16_t) + /* TYPE */
+ sizeof(uint16_t) + /* CLASS */
+ sizeof(uint32_t) + /* TTL */
+ sizeof(uint16_t) + /* RDLENGTH */
+ knot_dname_size(tsig_rdata_alg_name(tsig)) +
+ 6 * sizeof(uint8_t) + /* Time signed */
+ sizeof(uint16_t) + /* Fudge */
+ sizeof(uint16_t) + /* MAC size */
+ tsig_rdata_mac_length(tsig) +
+ sizeof(uint16_t) + /* Original ID */
+ sizeof(uint16_t) + /* Error */
+ sizeof(uint16_t) + /* Other len */
+ tsig_rdata_other_data_length(tsig);
+}
+
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
new file mode 100644
index 0000000..eafcfab
--- /dev/null
+++ b/src/libknot/tsig.h
@@ -0,0 +1,145 @@
+/*!
+ * \file tsig.h
+ *
+ * \author Jan Kadlec <jan.kadlec@nic.cz>
+ *
+ * \brief TSIG manipulation.
+ *
+ * \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_TSIG_H_
+#define _KNOT_TSIG_H_
+
+#include <stdint.h>
+
+#include "rrset.h"
+#include "util/utils.h"
+
+/* The assigned numbers should not begin with 0 - reserved for error. */
+enum tsig_algorithm {
+ KNOT_TSIG_ALG_NULL = 0,
+ KNOT_TSIG_ALG_GSS_TSIG = 128, /*!< \brief gss-tsig. */
+ KNOT_TSIG_ALG_HMAC_MD5, /*!< \brief HMAC-MD5.SIG-ALG.REG.INT. */
+ KNOT_TSIG_ALG_HMAC_SHA1, /*!< \brief hmac-sha1. */
+ KNOT_TSIG_ALG_HMAC_SHA224, /*!< \brief hmac-sha224. */
+ KNOT_TSIG_ALG_HMAC_SHA256, /*!< \brief hmac-sha256. */
+ KNOT_TSIG_ALG_HMAC_SHA384, /*!< \brief hmac-sha384. */
+ KNOT_TSIG_ALG_HMAC_SHA512 /*!< \brief hmac-sha512. */
+};
+
+typedef enum tsig_algorithm tsig_algorithm_t;
+
+struct knot_key {
+ knot_dname_t *name; /*!< Key name. */
+ tsig_algorithm_t algorithm; /*!< Key algorithm. */
+ char *secret; /*!< Key data. */
+ size_t secret_size; /*!< Key length. */
+};
+
+typedef struct knot_key knot_key_t;
+
+/*!< \todo FIND ALG LENGTHS */
+enum tsig_algorithm_digest_length {
+ KNOT_TSIG_ALG_DIG_LENGTH_GSS_TSIG = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_HMAC_MD5 = 16,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA1 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA224 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA256 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA384 = 0,
+ KNOT_TSIG_ALG_DIG_LENGTH_SHA512 = 0
+};
+
+enum tsig_consts {
+ KNOT_TSIG_ITEM_COUNT = 7,
+ KNOT_TSIG_VARIABLES_LENGTH = sizeof(uint16_t) // class
+ + sizeof(uint32_t) // ttl
+ + 6 // time signed
+ + sizeof(uint16_t) // fudge
+ + sizeof(uint16_t) // error
+ + sizeof(uint16_t),// other data length
+ KNOT_TSIG_TIMERS_LENGTH = sizeof(uint16_t) //fugde
+ + 6 // time signed
+};
+
+/*! TSIG errors are defined in util/error.h
+ * and present negative value of the TSIG error to
+ * comply with other parts of the library.
+ *
+ * KNOT_TSIG_EBADSIG = -16
+ * KNOT_TSIG_EBADKEY = -17
+ * KNOT_TSIG_EBADTIME = -18
+ */
+
+/*!
+ * \note Uses the given domain name, do not deallocate it!
+ */
+int tsig_rdata_set_alg_name(knot_rrset_t *tsig, knot_dname_t *alg_name);
+int tsig_rdata_set_alg(knot_rrset_t *tsig, tsig_algorithm_t alg);
+int tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time);
+int tsig_rdata_store_current_time(knot_rrset_t *tsig);
+int tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge);
+int tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *mac);
+int tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id);
+int tsig_rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error);
+int tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *other_data);
+
+const knot_dname_t *tsig_rdata_alg_name(const knot_rrset_t *tsig);
+tsig_algorithm_t tsig_rdata_alg(const knot_rrset_t *tsig);
+uint64_t tsig_rdata_time_signed(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_fudge(const knot_rrset_t *tsig);
+const uint8_t *tsig_rdata_mac(const knot_rrset_t *tsig);
+size_t tsig_rdata_mac_length(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_orig_id(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_error(const knot_rrset_t *tsig);
+const uint8_t *tsig_rdata_other_data(const knot_rrset_t *tsig);
+uint16_t tsig_rdata_other_data_length(const knot_rrset_t *tsig);
+size_t tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig);
+
+size_t tsig_rdata_tsig_timers_length();
+
+int tsig_alg_from_name(const knot_dname_t *name);
+
+/*!
+ * \brief Convert TSIG algorithm identifier to name.
+ *
+ * \param alg TSIG algorithm identifier.
+ *
+ * \retval TSIG algorithm string name.
+ * \retval Empty string if undefined.
+ */
+const char* tsig_alg_to_str(tsig_algorithm_t alg);
+
+uint16_t tsig_alg_digest_length(tsig_algorithm_t alg);
+
+/*!
+ * \brief Return TSIG RRSET maximum wire size for given algorithm.
+ *
+ * \param key Signing key descriptor.
+ *
+ * \return RRSET wire size.
+ */
+size_t tsig_wire_maxsize(const knot_key_t *key);
+size_t tsig_wire_actsize(const knot_rrset_t *tsig);
+
+#endif /* _KNOT_TSIG_H_ */
+
+/*! @} */
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
new file mode 100644
index 0000000..cf9e6a0
--- /dev/null
+++ b/src/libknot/updates/changesets.c
@@ -0,0 +1,296 @@
+/* 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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "updates/changesets.h"
+
+#include "rrset.h"
+#include "util/error.h"
+
+static const size_t KNOT_CHANGESET_COUNT = 5;
+static const size_t KNOT_CHANGESET_STEP = 5;
+static const size_t KNOT_CHANGESET_RRSET_COUNT = 5;
+static const size_t KNOT_CHANGESET_RRSET_STEP = 5;
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_changeset_check_count(knot_rrset_t ***rrsets, size_t count,
+ size_t *allocated)
+{
+ /* Check if allocated is sufficient. */
+ if (count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* How many steps is needed to content count? */
+ size_t extra = (count - *allocated) % KNOT_CHANGESET_RRSET_STEP;
+ extra = (extra + 1) * KNOT_CHANGESET_RRSET_STEP;
+
+ /* Allocate new memory block. */
+ const size_t item_len = sizeof(knot_rrset_t *);
+ const size_t new_count = *allocated + extra;
+ knot_rrset_t **rrsets_new = malloc(new_count * item_len);
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Clear old memory block and copy old data. */
+ memset(rrsets_new, 0, new_count * item_len);
+ memcpy(rrsets_new, *rrsets, (*allocated) * item_len);
+
+ /* Replace old rrsets. */
+ free(*rrsets);
+ *rrsets = rrsets_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_changeset_rrsets_match(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2)
+{
+ return knot_rrset_compare(rrset1, rrset2, KNOT_RRSET_COMPARE_HEADER)
+ && (knot_rrset_type(rrset1) != KNOT_RRTYPE_RRSIG
+ || knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrset1))
+ == knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(rrset2)));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_allocate(knot_changesets_t **changesets)
+{
+ // create new changesets
+ *changesets = (knot_changesets_t *)(malloc(sizeof(knot_changesets_t)));
+ if (*changesets == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(*changesets, 0, sizeof(knot_changesets_t));
+
+ return knot_changesets_check_size(*changesets);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocated,
+ knot_rrset_t *rrset)
+{
+ int ret = knot_changeset_check_count(rrsets, *count + 1, allocated);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ (*rrsets)[*count] = rrset;
+ *count = *count + 1;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
+ size_t *allocated, knot_rrset_t *rr)
+{
+ // try to find the RRSet in the list of RRSets, but search backwards
+ // as it is probable that the last RRSet is the one to which the RR
+ // belongs
+ int i = *count - 1;
+
+ while (i >= 0 && !knot_changeset_rrsets_match((*rrsets)[i], rr)) {
+ --i;
+ }
+
+ if (i >= 0) {
+ // found RRSet to merge the new one into
+ if (knot_rrset_merge((void **)&(*rrsets)[i],
+ (void **)&rr) != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+ // remove the RR
+ /*! \todo does this make sense? */
+ knot_rrset_free(&rr); // used to be deep free with all 1's
+
+ return KNOT_EOK;
+ } else {
+ return knot_changeset_add_rrset(rrsets, count, allocated, rr);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_new_rr(knot_changeset_t *changeset,
+ knot_rrset_t *rrset,
+ xfrin_changeset_part_t part)
+{
+ knot_rrset_t ***rrsets = NULL;
+ size_t *count = NULL;
+ size_t *allocated = NULL;
+
+ switch (part) {
+ case XFRIN_CHANGESET_ADD:
+ rrsets = &changeset->add;
+ count = &changeset->add_count;
+ allocated = &changeset->add_allocated;
+ break;
+ case XFRIN_CHANGESET_REMOVE:
+ rrsets = &changeset->remove;
+ count = &changeset->remove_count;
+ allocated = &changeset->remove_allocated;
+ break;
+ default:
+ assert(0);
+ }
+
+ assert(rrsets != NULL);
+ assert(count != NULL);
+ assert(allocated != NULL);
+
+ int ret = knot_changeset_add_rr(rrsets, count, allocated, rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_changeset_store_soa(knot_rrset_t **chg_soa,
+ uint32_t *chg_serial, knot_rrset_t *soa)
+{
+ *chg_soa = soa;
+ *chg_serial = knot_rdata_soa_serial(knot_rrset_rdata(soa));
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
+ xfrin_changeset_part_t part)
+{
+ switch (part) {
+ case XFRIN_CHANGESET_ADD:
+ knot_changeset_store_soa(&changeset->soa_to,
+ &changeset->serial_to, soa);
+ break;
+ case XFRIN_CHANGESET_REMOVE:
+ knot_changeset_store_soa(&changeset->soa_from,
+ &changeset->serial_from, soa);
+ break;
+ default:
+ assert(0);
+ }
+
+ /*! \todo Remove return value? */
+ return KNOT_EOK;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int knot_changesets_check_size(knot_changesets_t *changesets)
+{
+ /* Check if allocated is sufficient. */
+ if (changesets->count <= changesets->allocated) {
+ return KNOT_EOK;
+ }
+
+ /* How many steps is needed to content count? */
+ size_t extra = (changesets->count - changesets->allocated) % KNOT_CHANGESET_STEP;
+ extra = (extra + 1) * KNOT_CHANGESET_STEP;
+
+ /* Allocate new memory block. */
+ const size_t item_len = sizeof(knot_changeset_t);
+ size_t new_count = (changesets->allocated + extra);
+ knot_changeset_t *sets = malloc(new_count * item_len);
+ if (sets == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Clear old memory block and copy old data. */
+ memset(sets, 0, new_count * item_len);
+ memcpy(sets, changesets->sets, changesets->allocated * item_len);
+
+ /* Replace old changesets. */
+ free(changesets->sets);
+ changesets->sets = sets;
+ changesets->allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_free_changeset(knot_changeset_t **changeset)
+{
+ /* XXX XXX investigate wrong frees. */
+ assert((*changeset)->add_allocated >= (*changeset)->add_count);
+ assert((*changeset)->remove_allocated >= (*changeset)->remove_count);
+ assert((*changeset)->allocated >= (*changeset)->size);
+
+ int j;
+ for (j = 0; j < (*changeset)->add_count; ++j) {
+ knot_rrset_deep_free(&(*changeset)->add[j], 1, 1, 1);
+ }
+ free((*changeset)->add);
+
+ for (j = 0; j < (*changeset)->remove_count; ++j) {
+ knot_rrset_deep_free(&(*changeset)->remove[j], 1, 1, 1);
+ }
+ free((*changeset)->remove);
+
+ knot_rrset_deep_free(&(*changeset)->soa_from, 1, 1, 1);
+ knot_rrset_deep_free(&(*changeset)->soa_to, 1, 1, 1);
+
+ free((*changeset)->data);
+
+
+ *changeset = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_free_changesets(knot_changesets_t **changesets)
+{
+ if (changesets == NULL || *changesets == NULL) {
+ return;
+ }
+
+ assert((*changesets)->allocated >= (*changesets)->count);
+
+ for (int i = 0; i < (*changesets)->count; ++i) {
+ knot_changeset_t *ch = &(*changesets)->sets[i];
+ knot_free_changeset(&ch);
+ }
+
+ free((*changesets)->sets);
+
+ knot_rrset_deep_free(&(*changesets)->first_soa, 1, 1, 1);
+
+ free(*changesets);
+ *changesets = NULL;
+}
+
+/*---------------------------------------------------------------------------*/
+
+
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
new file mode 100644
index 0000000..e8d5e39
--- /dev/null
+++ b/src/libknot/updates/changesets.h
@@ -0,0 +1,102 @@
+/*!
+ * \file changesets.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Structure for representing IXFR/DDNS changeset and its API.
+ *
+ * \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_CHANGESETS_H_
+#define _KNOT_CHANGESETS_H_
+
+#include "rrset.h"
+
+/*! \todo Changeset must be serializable/deserializable, so
+ * all data and pointers have to be changeset-exclusive,
+ * or more advanced structure serialization scheme has to be
+ * implemented.
+ *
+ * \todo Preallocation of space for changeset.
+ */
+typedef struct {
+ knot_rrset_t *soa_from;
+ knot_rrset_t **remove;
+ size_t remove_count;
+ size_t remove_allocated;
+
+ knot_rrset_t *soa_to;
+ knot_rrset_t **add;
+ size_t add_count;
+ size_t add_allocated;
+
+ uint8_t *data;
+ size_t size;
+ size_t allocated;
+ uint32_t serial_from;
+ uint32_t serial_to;
+} knot_changeset_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct {
+ knot_changeset_t *sets;
+ size_t count;
+ size_t allocated;
+ knot_rrset_t *first_soa;
+} knot_changesets_t;
+
+/*----------------------------------------------------------------------------*/
+
+typedef enum {
+ XFRIN_CHANGESET_ADD,
+ XFRIN_CHANGESET_REMOVE
+} xfrin_changeset_part_t;
+
+/*----------------------------------------------------------------------------*/
+
+int knot_changeset_allocate(knot_changesets_t **changesets);
+
+int knot_changeset_add_rrset(knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocated,
+ knot_rrset_t *rrset);
+
+int knot_changeset_add_rr(knot_rrset_t ***rrsets, size_t *count,
+ size_t *allocated, knot_rrset_t *rr);
+
+int knot_changeset_add_new_rr(knot_changeset_t *changeset,
+ knot_rrset_t *rrset,
+ xfrin_changeset_part_t part);
+
+void knot_changeset_store_soa(knot_rrset_t **chg_soa,
+ uint32_t *chg_serial, knot_rrset_t *soa);
+
+int knot_changeset_add_soa(knot_changeset_t *changeset, knot_rrset_t *soa,
+ xfrin_changeset_part_t part);
+
+int knot_changesets_check_size(knot_changesets_t *changesets);
+
+void knot_free_changeset(knot_changeset_t **changeset);
+
+void knot_free_changesets(knot_changesets_t **changesets);
+
+#endif /* _KNOT_CHANGESETS_H_ */
+
+/*! @} */
diff --git a/src/libknot/updates/ddns.c b/src/libknot/updates/ddns.c
new file mode 100644
index 0000000..4c6ab7b
--- /dev/null
+++ b/src/libknot/updates/ddns.c
@@ -0,0 +1,638 @@
+/* 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 "updates/ddns.h"
+#include "updates/changesets.h"
+#include "util/debug.h"
+#include "packet/packet.h"
+#include "util/error.h"
+#include "consts.h"
+
+/*----------------------------------------------------------------------------*/
+// Copied from XFR - maybe extract somewhere else
+static int knot_ddns_prereq_check_rrsets(knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocated)
+{
+ int new_count = 0;
+ if (*count == *allocated) {
+ new_count = *allocated * 2;
+ }
+
+ knot_rrset_t **rrsets_new =
+ (knot_rrset_t **)calloc(new_count, sizeof(knot_rrset_t *));
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(rrsets_new, *rrsets, *count);
+ *rrsets = rrsets_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_prereq_check_dnames(knot_dname_t ***dnames,
+ size_t *count, size_t *allocated)
+{
+ int new_count = 0;
+ if (*count == *allocated) {
+ new_count = *allocated * 2;
+ }
+
+ knot_dname_t **dnames_new =
+ (knot_dname_t **)calloc(new_count, sizeof(knot_dname_t *));
+ if (dnames_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(dnames_new, *dnames, *count);
+ *dnames = dnames_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_prereq_rrset(const knot_rrset_t *rrset,
+ knot_rrset_t ***rrsets,
+ size_t *count, size_t *allocd)
+{
+ // check if such RRSet is not already there and merge if needed
+ int ret;
+ for (int i = 0; i < *count; ++i) {
+ if (knot_rrset_compare(rrset, (*rrsets)[i],
+ KNOT_RRSET_COMPARE_HEADER) == 0) {
+ ret = knot_rrset_merge((void **)&((*rrsets)[i]),
+ (void **)&rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ return KNOT_EOK;
+ }
+ }
+ }
+
+ // if we are here, the RRSet was not found
+ ret = knot_ddns_prereq_check_rrsets(rrsets, count, allocd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_rrset_t *new_rrset = NULL;
+ ret = knot_rrset_deep_copy(rrset, &new_rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ (*rrsets)[(*count)++] = new_rrset;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_prereq_dname(const knot_dname_t *dname,
+ knot_dname_t ***dnames,
+ size_t *count, size_t *allocd)
+{
+ // we do not have to check if the name is not already there
+ // if it is, we will just check it twice in the zone
+
+ int ret = knot_ddns_prereq_check_dnames(dnames, count, allocd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_dname_t *dname_new = knot_dname_deep_copy(dname);
+ if (dname_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ (*dnames)[(*count)++] = dname_new;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_prereq(knot_ddns_prereq_t *prereqs,
+ const knot_rrset_t *rrset, uint16_t qclass)
+{
+ assert(prereqs != NULL);
+ assert(rrset != NULL);
+
+ if (knot_rrset_ttl(rrset) != 0) {
+ return KNOT_EMALF;
+ }
+
+ int ret;
+
+ if (knot_rrset_class(rrset) == KNOT_CLASS_ANY) {
+ if (knot_rrset_rdata(rrset) != NULL) {
+ return KNOT_EMALF;
+ }
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_ANY) {
+ ret = knot_ddns_add_prereq_dname(
+ knot_rrset_owner(rrset), &prereqs->in_use,
+ &prereqs->in_use_count,
+ &prereqs->in_use_allocd);
+ } else {
+ ret = knot_ddns_add_prereq_rrset(rrset,
+ &prereqs->exist,
+ &prereqs->exist_count,
+ &prereqs->exist_allocd);
+ }
+ } else if (knot_rrset_class(rrset) == KNOT_CLASS_NONE) {
+ if (knot_rrset_rdata(rrset) != NULL) {
+ return KNOT_EMALF;
+ }
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_ANY) {
+ ret = knot_ddns_add_prereq_dname(
+ knot_rrset_owner(rrset), &prereqs->not_in_use,
+ &prereqs->not_in_use_count,
+ &prereqs->not_in_use_allocd);
+ } else {
+ ret = knot_ddns_add_prereq_rrset(rrset,
+ &prereqs->not_exist,
+ &prereqs->not_exist_count,
+ &prereqs->not_exist_allocd);
+ }
+ } else if (knot_rrset_class(rrset) == qclass) {
+ ret = knot_ddns_add_prereq_rrset(rrset,
+ &prereqs->exist_full,
+ &prereqs->exist_full_count,
+ &prereqs->exist_full_allocd);
+ } else {
+ return KNOT_EMALF;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_add_update(knot_changeset_t *changeset,
+ const knot_rrset_t *rrset, uint16_t qclass)
+{
+ assert(changeset != NULL);
+ assert(rrset != NULL);
+
+ int ret;
+
+ // create a copy of the RRSet
+ /*! \todo If the packet was not parsed all at once, we could save this
+ * copy.
+ */
+ knot_rrset_t *rrset_copy;
+ ret = knot_rrset_deep_copy(rrset, &rrset_copy);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /*! \todo What about the SOAs? */
+
+ if (knot_rrset_class(rrset) == qclass) {
+ // this RRSet should be added to the zone
+ ret = knot_changeset_add_rr(&changeset->add,
+ &changeset->add_count,
+ &changeset->add_allocated,
+ rrset_copy);
+ } else {
+ // this RRSet marks removal of something from zone
+ // what should be removed is distinguished when applying
+ ret = knot_changeset_add_rr(&changeset->remove,
+ &changeset->remove_count,
+ &changeset->remove_allocated,
+ rrset_copy);
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_exist(const knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(rrset != NULL);
+ assert(rcode != NULL);
+ assert(knot_rrset_rdata(rrset) == NULL);
+ assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
+ assert(knot_rrset_ttl(rrset) == 0);
+ assert(knot_rrset_class(rrset) == KNOT_CLASS_ANY);
+
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+ node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
+ if (node == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_ENONODE;
+ } else if (knot_node_rrset(node, knot_rrset_type(rrset)) == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_ENORRSET;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_exist_full(const knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(rrset != NULL);
+ assert(rcode != NULL);
+ assert(knot_rrset_rdata(rrset) == NULL);
+ assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
+ assert(knot_rrset_ttl(rrset) == 0);
+ assert(knot_rrset_class(rrset) == KNOT_CLASS_ANY);
+
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+ const knot_rrset_t *found;
+
+ node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
+ if (node == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ } else if ((found = knot_node_rrset(node, knot_rrset_type(rrset)))
+ == NULL) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ } else {
+ // do not have to compare the header, it is already done
+ assert(knot_rrset_type(found) == knot_rrset_type(rrset));
+ assert(knot_dname_compare(knot_rrset_owner(found),
+ knot_rrset_owner(rrset)) == 0);
+ if (knot_rrset_compare_rdata(found, rrset) <= 0) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_not_exist(const knot_zone_contents_t *zone,
+ const knot_rrset_t *rrset, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(rrset != NULL);
+ assert(rcode != NULL);
+ assert(knot_rrset_rdata(rrset) == NULL);
+ assert(knot_rrset_type(rrset) != KNOT_RRTYPE_ANY);
+ assert(knot_rrset_ttl(rrset) == 0);
+ assert(knot_rrset_class(rrset) == KNOT_CLASS_NONE);
+
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+ const knot_rrset_t *found;
+
+ node = knot_zone_contents_find_node(zone, knot_rrset_owner(rrset));
+ if (node == NULL) {
+ return KNOT_EOK;
+ } else if ((found = knot_node_rrset(node, knot_rrset_type(rrset)))
+ == NULL) {
+ return KNOT_EOK;
+ } else {
+ // do not have to compare the header, it is already done
+ assert(knot_rrset_type(found) == knot_rrset_type(rrset));
+ assert(knot_dname_compare(knot_rrset_owner(found),
+ knot_rrset_owner(rrset)) == 0);
+ if (knot_rrset_compare_rdata(found, rrset) <= 0) {
+ return KNOT_EOK;
+ }
+ }
+
+ *rcode = KNOT_RCODE_YXRRSET;
+ return KNOT_EPREREQ;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_in_use(const knot_zone_contents_t *zone,
+ const knot_dname_t *dname, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(dname != NULL);
+ assert(rcode != NULL);
+
+ if (!knot_dname_is_subdomain(dname,
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+
+ node = knot_zone_contents_find_node(zone, dname);
+ if (node == NULL) {
+ *rcode = KNOT_RCODE_NXDOMAIN;
+ return KNOT_EPREREQ;
+ } else if (knot_node_rrset_count(node) == 0) {
+ *rcode = KNOT_RCODE_NXDOMAIN;
+ return KNOT_EPREREQ;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_not_in_use(const knot_zone_contents_t *zone,
+ const knot_dname_t *dname, uint8_t *rcode)
+{
+ assert(zone != NULL);
+ assert(dname != NULL);
+ assert(rcode != NULL);
+
+ if (!knot_dname_is_subdomain(dname,
+ knot_node_owner(knot_zone_contents_apex(zone)))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ const knot_node_t *node;
+
+ node = knot_zone_contents_find_node(zone, dname);
+ if (node == NULL) {
+ return KNOT_EOK;
+ } else if (knot_node_rrset_count(node) == 0) {
+ return KNOT_EOK;
+ }
+
+ *rcode = KNOT_RCODE_YXDOMAIN;
+ return KNOT_EPREREQ;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
+ uint8_t *rcode)
+{
+ if (zone == NULL || query == NULL || rcode == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ if (knot_packet_qtype(query) != KNOT_RRTYPE_SOA) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+
+ if(!knot_zone_contents(zone)) {
+ *rcode = KNOT_RCODE_REFUSED;
+ return KNOT_ENOZONE;
+ }
+
+ // 1) check if the zone is master or slave
+ if (!knot_zone_is_master(zone)) {
+ return KNOT_EBADZONE;
+ }
+
+ // 2) check zone CLASS
+ if (knot_zone_contents_class(knot_zone_contents(zone)) !=
+ knot_packet_qclass(query)) {
+ *rcode = KNOT_RCODE_NOTAUTH;
+ return KNOT_ENOZONE;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_process_prereqs(knot_packet_t *query,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode)
+{
+ /*! \todo Consider not parsing the whole packet at once, but
+ * parsing one RR at a time - could save some memory and time.
+ */
+
+ if (query == NULL || prereqs == NULL || rcode == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ // allocate space for the prerequisities
+ *prereqs = (knot_ddns_prereq_t *)calloc(1, sizeof(knot_ddns_prereq_t));
+ CHECK_ALLOC_LOG(*prereqs, KNOT_ENOMEM);
+
+ int ret;
+
+ for (int i = 0; i < knot_packet_answer_rrset_count(query); ++i) {
+ // we must copy the RRSets, because all those stored in the
+ // packet will be destroyed
+ ret = knot_ddns_add_prereq(*prereqs,
+ knot_packet_answer_rrset(query, i),
+ knot_packet_qclass(query));
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add prerequisity RRSet:%s\n",
+ knot_strerror(ret));
+ *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL;
+ knot_ddns_prereqs_free(prereqs);
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode)
+{
+ int i, ret;
+
+ for (i = 0; i < (*prereqs)->exist_count; ++i) {
+ ret = knot_ddns_check_exist(zone, (*prereqs)->exist[i], rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->exist_full_count; ++i) {
+ ret = knot_ddns_check_exist_full(zone,
+ (*prereqs)->exist_full[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->not_exist_count; ++i) {
+ ret = knot_ddns_check_not_exist(zone, (*prereqs)->not_exist[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->in_use_count; ++i) {
+ ret = knot_ddns_check_in_use(zone, (*prereqs)->in_use[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < (*prereqs)->not_in_use_count; ++i) {
+ ret = knot_ddns_check_not_in_use(zone,
+ (*prereqs)->not_in_use[i],
+ rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int knot_ddns_check_update(const knot_rrset_t *rrset,
+ const knot_packet_t *query, uint8_t *rcode)
+{
+ if (!knot_dname_is_subdomain(knot_rrset_owner(rrset),
+ knot_packet_qname(query))) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EBADZONE;
+ }
+
+ if (knot_rrset_class(rrset) == knot_packet_qclass(query)) {
+ if (knot_rrtype_is_metatype(knot_rrset_type(rrset))) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else if (knot_rrset_class(rrset) == KNOT_CLASS_ANY) {
+ if (knot_rrset_rdata(rrset) != NULL
+ || (knot_rrtype_is_metatype(knot_rrset_type(rrset))
+ && knot_rrset_type(rrset) != KNOT_RRTYPE_ANY)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else if (knot_rrset_class(rrset) == KNOT_CLASS_NONE) {
+ if (knot_rrset_ttl(rrset) != 0
+ || knot_rrtype_is_metatype(knot_rrset_type(rrset))) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int knot_ddns_process_update(knot_packet_t *query,
+ knot_changeset_t **changeset, uint8_t *rcode)
+{
+ // just put all RRSets from query's Authority section
+ // it will be distinguished when applying to the zone
+
+ if (query == NULL || changeset == NULL || rcode == NULL) {
+ return KNOT_EBADARG;
+ }
+
+ *changeset = (knot_changeset_t *)calloc(1, sizeof(knot_changeset_t));
+ CHECK_ALLOC_LOG(*changeset, KNOT_ENOMEM);
+
+ int ret;
+
+ for (int i = 0; i < knot_packet_authority_rrset_count(query); ++i) {
+
+ const knot_rrset_t *rrset =
+ knot_packet_authority_rrset(query, i);
+
+ ret = knot_ddns_check_update(rrset, query, rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_ddns_add_update(*changeset, rrset,
+ knot_packet_qclass(query));
+
+ if (ret != KNOT_EOK) {
+ dbg_ddns("Failed to add update RRSet:%s\n",
+ knot_strerror(ret));
+ *rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
+ : KNOT_RCODE_SERVFAIL;
+ knot_free_changeset(changeset);
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq)
+{
+ int i;
+
+ for (i = 0; i < (*prereq)->exist_count; ++i) {
+ knot_rrset_deep_free(&(*prereq)->exist[i], 1, 1, 1);
+ }
+
+ for (i = 0; i < (*prereq)->exist_full_count; ++i) {
+ knot_rrset_deep_free(&(*prereq)->exist_full[i], 1, 1, 1);
+ }
+
+ for (i = 0; i < (*prereq)->not_exist_count; ++i) {
+ knot_rrset_deep_free(&(*prereq)->not_exist[i], 1, 1, 1);
+ }
+
+ for (i = 0; i < (*prereq)->in_use_count; ++i) {
+ knot_dname_free(&(*prereq)->in_use[i]);
+ }
+
+ for (i = 0; i < (*prereq)->not_in_use_count; ++i) {
+ knot_dname_free(&(*prereq)->not_in_use[i]);
+ }
+
+ free(*prereq);
+ *prereq = NULL;
+}
diff --git a/src/libknot/updates/ddns.h b/src/libknot/updates/ddns.h
new file mode 100644
index 0000000..dceebed
--- /dev/null
+++ b/src/libknot/updates/ddns.h
@@ -0,0 +1,74 @@
+/*!
+ * \file ddns.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Dynamic updates processing.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/* 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_DDNS_H_
+#define _KNOT_DDNS_H_
+
+#include "updates/changesets.h"
+#include "zone/zone.h"
+#include "packet/packet.h"
+#include "rrset.h"
+#include "dname.h"
+
+typedef struct knot_ddns_prereq_t {
+ knot_rrset_t **exist;
+ size_t exist_count;
+ size_t exist_allocd;
+
+ knot_rrset_t **exist_full;
+ size_t exist_full_count;
+ size_t exist_full_allocd;
+
+ knot_rrset_t **not_exist;
+ size_t not_exist_count;
+ size_t not_exist_allocd;
+
+ knot_dname_t **in_use;
+ size_t in_use_count;
+ size_t in_use_allocd;
+
+ knot_dname_t **not_in_use;
+ size_t not_in_use_count;
+ size_t not_in_use_allocd;
+} knot_ddns_prereq_t;
+
+int knot_ddns_check_zone(const knot_zone_t *zone, knot_packet_t *query,
+ uint8_t *rcode);
+
+int knot_ddns_process_prereqs(knot_packet_t *query,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode);
+
+int knot_ddns_check_prereqs(const knot_zone_contents_t *zone,
+ knot_ddns_prereq_t **prereqs, uint8_t *rcode);
+
+int knot_ddns_process_update(knot_packet_t *query,
+ knot_changeset_t **changeset, uint8_t *rcode);
+
+void knot_ddns_prereqs_free(knot_ddns_prereq_t **prereq);
+
+#endif /* _KNOT_DDNS_H_ */
+
+/*! @} */
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
new file mode 100644
index 0000000..51be430
--- /dev/null
+++ b/src/libknot/updates/xfr-in.c
@@ -0,0 +1,3013 @@
+/* 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 <urcu.h>
+
+#include "updates/xfr-in.h"
+
+#include "nameserver/name-server.h"
+#include "util/wire.h"
+#include "util/debug.h"
+// #include "knot/zone/zone-dump.h"
+// #include "knot/zone/zone-load.h"
+#include "packet/packet.h"
+#include "dname.h"
+#include "zone/zone.h"
+#include "packet/query.h"
+#include "packet/response.h"
+#include "util/error.h"
+#include "updates/changesets.h"
+#include "tsig.h"
+#include "tsig-op.h"
+
+/*----------------------------------------------------------------------------*/
+/* Non-API functions */
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_create_query(knot_dname_t *qname, uint16_t qtype,
+ uint16_t qclass, knot_ns_xfr_t *xfr, size_t *size,
+ const knot_rrset_t *soa, int use_tsig)
+{
+ knot_packet_t *pkt = knot_packet_new(KNOT_PACKET_PREALLOC_QUERY);
+ CHECK_ALLOC_LOG(pkt, KNOT_ENOMEM);
+
+ /*! \todo Get rid of the numeric constant. */
+ int rc = knot_packet_set_max_size(pkt, 512);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOT_ERROR;
+ }
+
+ rc = knot_query_init(pkt);
+ if (rc != KNOT_EOK) {
+ knot_packet_free(&pkt);
+ return KNOT_ERROR;
+ }
+
+ knot_question_t question;
+
+ /* Retain qname until the question is freed. */
+ knot_dname_retain(qname);
+
+ /* Set random query ID. */
+ knot_packet_set_random_id(pkt);
+ knot_wire_set_id(pkt->wireformat, pkt->header.id);
+
+ // this is ugly!!
+ question.qname = (knot_dname_t *)qname;
+ question.qtype = qtype;
+ question.qclass = qclass;
+
+ rc = knot_query_set_question(pkt, &question);
+ if (rc != KNOT_EOK) {
+ knot_dname_release(question.qname);
+ knot_packet_free(&pkt);
+ return KNOT_ERROR;
+ }
+
+ /* Reserve space for TSIG. */
+ if (use_tsig && xfr->tsig_key) {
+ dbg_xfrin_detail("xfrin: setting packet TSIG size to %zu\n",
+ xfr->tsig_size);
+ knot_packet_set_tsig_size(pkt, xfr->tsig_size);
+ }
+
+ /* Add SOA RR to authority section for IXFR. */
+ if (qtype == KNOT_RRTYPE_IXFR && soa) {
+ knot_query_add_rrset_authority(pkt, soa);
+ }
+
+ /*! \todo OPT RR ?? */
+
+ uint8_t *wire = NULL;
+ size_t wire_size = 0;
+ rc = knot_packet_to_wire(pkt, &wire, &wire_size);
+ if (rc != KNOT_EOK) {
+ dbg_xfrin("Failed to write packet to wire.\n");
+ knot_dname_release(question.qname);
+ knot_packet_free(&pkt);
+ return KNOT_ERROR;
+ }
+
+ if (wire_size > *size) {
+ dbg_xfrin("Not enough space provided for the wire "
+ "format of the query.\n");
+ knot_packet_free(&pkt);
+ return KNOT_ESPACE;
+ }
+
+ // wire format created, sign it with TSIG if required
+ if (use_tsig && xfr->tsig_key) {
+ char *name = knot_dname_to_str(xfr->tsig_key->name);
+ dbg_xfrin_detail("Signing XFR query with key (name %s): \n",
+ name);
+ free(name);
+ dbg_xfrin_hex_detail(xfr->tsig_key->secret,
+ xfr->tsig_key->secret_size);
+
+ xfr->digest_size = xfr->digest_max_size;
+ rc = knot_tsig_sign(wire, &wire_size, *size, NULL, 0,
+ xfr->digest, &xfr->digest_size, xfr->tsig_key);
+ if (rc != KNOT_EOK) {
+ /*! \todo [TSIG] Handle TSIG errors. */
+ knot_packet_free(&pkt);
+ return rc;
+ }
+
+ dbg_xfrin_detail("Signed XFR query, new wire size: %zu, digest:"
+ "\n", wire_size);
+ dbg_xfrin_hex_detail((const char*)xfr->digest, xfr->digest_size);
+ }
+
+ memcpy(xfr->wire, wire, wire_size);
+ *size = wire_size;
+
+ dbg_xfrin("Created query of size %zu.\n", *size);
+ knot_packet_dump(pkt);
+
+ knot_packet_free(&pkt);
+
+ /* Release qname. */
+ knot_dname_release(question.qname);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/* API functions */
+/*----------------------------------------------------------------------------*/
+
+int xfrin_create_soa_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size)
+{
+ /*! \todo [TSIG] Should TSIG apply for SOA query too? */
+ return xfrin_create_query(owner, KNOT_RRTYPE_SOA,
+ KNOT_CLASS_IN, xfr, size, 0, 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_transfer_needed(const knot_zone_contents_t *zone,
+ knot_packet_t *soa_response)
+{
+ // first, parse the rest of the packet
+ assert(!knot_packet_is_query(soa_response));
+ dbg_xfrin("Response - parsed: %zu, total wire size: %zu\n",
+ soa_response->parsed, soa_response->size);
+ int ret;
+
+ if (soa_response->parsed < soa_response->size) {
+ ret = knot_packet_parse_rest(soa_response);
+ if (ret != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+ }
+
+ /*
+ * Retrieve the local Serial
+ */
+ const knot_rrset_t *soa_rrset =
+ knot_node_rrset(knot_zone_contents_apex(zone),
+ KNOT_RRTYPE_SOA);
+ if (soa_rrset == NULL) {
+ char *name = knot_dname_to_str(knot_node_owner(
+ knot_zone_contents_apex(zone)));
+ dbg_xfrin("SOA RRSet missing in the zone %s!\n", name);
+ free(name);
+ return KNOT_ERROR;
+ }
+
+ int64_t local_serial = knot_rdata_soa_serial(
+ knot_rrset_rdata(soa_rrset));
+ if (local_serial < 0) {
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(soa_rrset));
+ dbg_xfrin("Malformed data in SOA of zone %s\n", name);
+ free(name);
+);
+ return KNOT_EMALF; // maybe some other error
+ }
+
+ /*
+ * Retrieve the remote Serial
+ */
+ // the SOA should be the first (and only) RRSet in the response
+ soa_rrset = knot_packet_answer_rrset(soa_response, 0);
+ if (soa_rrset == NULL
+ || knot_rrset_type(soa_rrset) != KNOT_RRTYPE_SOA) {
+ return KNOT_EMALF;
+ }
+
+ int64_t remote_serial = knot_rdata_soa_serial(
+ knot_rrset_rdata(soa_rrset));
+ if (remote_serial < 0) {
+ return KNOT_EMALF; // maybe some other error
+ }
+
+ return (ns_serial_compare(local_serial, remote_serial) < 0);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_create_axfr_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size, int use_tsig)
+{
+ return xfrin_create_query(owner, KNOT_RRTYPE_AXFR,
+ KNOT_CLASS_IN, xfr, size, 0, use_tsig);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_create_ixfr_query(const knot_zone_contents_t *zone,
+ knot_ns_xfr_t *xfr, size_t *size, int use_tsig)
+{
+ /*!
+ * \todo Implement properly.
+ */
+ knot_node_t *apex = knot_zone_contents_get_apex(zone);
+ const knot_rrset_t *soa = knot_node_rrset(apex, KNOT_RRTYPE_SOA);
+
+ return xfrin_create_query(knot_node_get_owner(apex), KNOT_RRTYPE_IXFR,
+ KNOT_CLASS_IN, xfr, size, soa, use_tsig);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_add_orphan_rrsig(xfrin_orphan_rrsig_t *rrsigs,
+ knot_rrset_t *rr)
+{
+ // try to find similar RRSIGs (check owner and type covered) in the list
+ assert(knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG);
+
+ int ret = 0;
+ xfrin_orphan_rrsig_t **last = &rrsigs;
+ while (*last != NULL) {
+ // check if the RRSIG is not similar to the one we want to add
+ assert((*last)->rrsig != NULL);
+ if (knot_rrset_compare((*last)->rrsig, rr,
+ KNOT_RRSET_COMPARE_HEADER) == 1
+ && knot_rdata_rrsig_type_covered(knot_rrset_rdata(
+ (*last)->rrsig))
+ == knot_rdata_rrsig_type_covered(knot_rrset_rdata(rr))) {
+ ret = knot_rrset_merge((void **)&(*last)->rrsig,
+ (void **)&rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ return 1;
+ }
+ }
+ last = &((*last)->next);
+ }
+
+ assert(*last == NULL);
+ // we did not find the right RRSIGs, add to the end
+ *last = (xfrin_orphan_rrsig_t *)malloc(sizeof(xfrin_orphan_rrsig_t));
+ CHECK_ALLOC_LOG(*last, KNOT_ENOMEM);
+
+ (*last)->rrsig = rr;
+ (*last)->next = NULL;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_process_orphan_rrsigs(knot_zone_contents_t *zone,
+ xfrin_orphan_rrsig_t *rrsigs)
+{
+ xfrin_orphan_rrsig_t **last = &rrsigs;
+ int ret = 0;
+ while (*last != NULL) {
+ knot_rrset_t *rrset = NULL;
+ knot_node_t *node = NULL;
+ ret = knot_zone_contents_add_rrsigs(zone, (*last)->rrsig,
+ &rrset, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret > 0) {
+ knot_rrset_free(&(*last)->rrsig);
+ } else if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add orphan RRSIG to zone.\n");
+ return ret;
+ } else {
+ (*last)->rrsig = NULL;
+ }
+
+ last = &((*last)->next);
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_free_orphan_rrsigs(xfrin_orphan_rrsig_t **rrsigs)
+{
+ xfrin_orphan_rrsig_t *r = *rrsigs;
+ while (r != NULL) {
+ xfrin_orphan_rrsig_t *prev = r;
+ r = r->next;
+ free(prev);
+ }
+
+ *rrsigs = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+/*! \note [TSIG] */
+static int xfrin_check_tsig(knot_packet_t *packet, knot_ns_xfr_t *xfr,
+ int tsig_req)
+{
+ assert(packet != NULL);
+ assert(xfr != NULL);
+
+ dbg_xfrin_verb("xfrin_check_tsig(): packet nr: %d, required: %d\n",
+ xfr->packet_nr, tsig_req);
+
+ /*
+ * If we are expecting it (i.e. xfr->prev_digest_size > 0)
+ * a) it should be there (first, last or each 100th packet) and it
+ * is not
+ * Then we should discard the changes and close the connection.
+ * b) it should be there and it is or it may not be there (other
+ * packets) and it is
+ * We validate the TSIG and reset packet number counting and
+ * data aggregation.
+ *
+ * If we are not expecting it (i.e. xfr->prev_digest_size <= 0) and
+ * it is there => it should probably be considered an error
+ */
+ knot_rrset_t *tsig = NULL;
+ int ret = knot_packet_parse_next_rr_additional(packet, &tsig);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (xfr->tsig_key) {
+ if (tsig_req && tsig == NULL) {
+ // TSIG missing!!
+ return KNOT_EMALF;
+ } else if (tsig != NULL) {
+ // TSIG there, either required or not, process
+ if (xfr->packet_nr == 0) {
+ ret = knot_tsig_client_check(tsig,
+ xfr->wire, xfr->wire_size,
+ xfr->digest, xfr->digest_size,
+ xfr->tsig_key);
+ } else {
+ ret = knot_tsig_client_check_next(tsig,
+ xfr->wire, xfr->wire_size,
+ xfr->digest, xfr->digest_size,
+ xfr->tsig_key);
+ }
+
+ if (ret != KNOT_EOK) {
+ /*! \note [TSIG] No need to check TSIG error
+ * here, propagate and check elsewhere.*/
+ return ret;
+ }
+
+ // and reset the data storage
+ //xfr->packet_nr = 1;
+ xfr->tsig_data_size = 0;
+
+ // Extract the digest from the TSIG RDATA and store it.
+ if (xfr->digest_max_size < tsig_rdata_mac_length(tsig)) {
+ return KNOT_ESPACE;
+ }
+ memcpy(xfr->digest, tsig_rdata_mac(tsig),
+ tsig_rdata_mac_length(tsig));
+ xfr->digest_size = tsig_rdata_mac_length(tsig);
+
+ } else { // TSIG not required and not there
+ // just append the wireformat to the TSIG data
+ assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+ >= xfr->wire_size);
+ memcpy(xfr->tsig_data + xfr->tsig_data_size,
+ xfr->wire, xfr->wire_size);
+ xfr->tsig_data_size += xfr->wire_size;
+ }
+ } else if (tsig != NULL) {
+ // TSIG where it should not be
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
+ xfrin_constructed_zone_t **constr*/
+ knot_ns_xfr_t *xfr)
+{
+ const uint8_t *pkt = xfr->wire;
+ size_t size = xfr->wire_size;
+ xfrin_constructed_zone_t **constr =
+ (xfrin_constructed_zone_t **)(&xfr->data);
+
+ if (pkt == NULL || constr == NULL) {
+ dbg_xfrin("Wrong parameters supported.\n");
+ return KNOT_EBADARG;
+ }
+
+ dbg_xfrin("Processing AXFR packet of size %zu.\n", size);
+
+ // check if the response is OK
+ if (knot_wire_get_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ return KNOT_EXFRREFUSED;
+ }
+
+ /*! \todo Should TC bit be checked? */
+
+ knot_packet_t *packet =
+ knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (packet == NULL) {
+ dbg_xfrin("Could not create packet structure.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_packet_parse_from_wire(packet, pkt, size, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse packet: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ /*! \todo [TSIG] If packet RCODE is NOTAUTH(9), process as TSIG error. */
+
+ knot_rrset_t *rr = NULL;
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse first Answer RR: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ if (rr == NULL) {
+ dbg_xfrin("No RRs in the packet.\n");
+ knot_packet_free(&packet);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ /*! \todo We should probably test whether the Question of the first
+ * message corresponds to the SOA RR.
+ */
+
+ knot_node_t *node = NULL;
+ int in_zone = 0;
+ knot_zone_contents_t *zone = NULL;
+
+ if (*constr == NULL) {
+ // this should be the first packet
+ /*! \note [TSIG] Packet number for checking TSIG validation. */
+ xfr->packet_nr = 0;
+ /*! \note [TSIG] Storing total size of data for TSIG digest. */
+ xfr->tsig_data_size = 0;
+
+ // create new zone
+ /*! \todo Ensure that the packet is the first one. */
+ if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
+ dbg_xfrin("No zone created, but the first RR in "
+ "Answer is not a SOA RR.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ if (knot_dname_compare(knot_rrset_owner(rr),
+ knot_packet_qname(packet)) != 0) {
+dbg_xfrin_exec(
+ char *rr_owner =
+ knot_dname_to_str(knot_rrset_owner(rr));
+ char *qname = knot_dname_to_str(
+ knot_packet_qname(packet));
+
+ dbg_xfrin("Owner of the first SOA RR (%s) does not"
+ " match QNAME (%s).\n", rr_owner, qname);
+
+ free(rr_owner);
+ free(qname);
+);
+ /*! \todo Cleanup. */
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_EMALF;
+ }
+
+ node = knot_node_new(rr->owner, NULL, 0);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node.\n");
+ knot_packet_free(&packet);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ // the first RR is SOA and its owner and QNAME are the same
+ // create the zone
+
+ *constr = (xfrin_constructed_zone_t *)malloc(
+ sizeof(xfrin_constructed_zone_t));
+ if (*constr == NULL) {
+ dbg_xfrin("Failed to create new constr. zone.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+
+ memset(*constr, 0, sizeof(xfrin_constructed_zone_t));
+
+ (*constr)->contents = knot_zone_contents_new(node, 0, 1, NULL);
+// assert(0);
+ if ((*constr)->contents== NULL) {
+ dbg_xfrin("Failed to create new zone.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_ENOMEM;
+ }
+
+ in_zone = 1;
+ assert(node->owner == rr->owner);
+ zone = (*constr)->contents;
+ assert(zone->apex == node);
+ assert(zone->apex->owner == rr->owner);
+ // add the RRSet to the node
+ //ret = knot_node_add_rrset(node, rr, 0);
+ ret = knot_zone_contents_add_rrset(zone, rr, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to zone node: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_ERROR;
+ } else if (ret > 0) {
+ dbg_xfrin("Merged SOA RRSet.\n");
+ // merged, free the RRSet
+ //knot_rrset_deep_free(&rr, 1, 0, 0);
+ knot_rrset_free(&rr);
+ }
+
+ // take next RR
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ } else {
+ zone = (*constr)->contents;
+ ++xfr->packet_nr;
+ }
+
+ /*! \note [TSIG] add the packet wire size to the data to be verified by
+ * TSIG
+ */
+ if (xfr->tsig_key) {
+ dbg_xfrin("Adding packet wire to TSIG data (size till now: %zu,"
+ " adding: %zu).\n", xfr->tsig_data_size,
+ xfr->wire_size);
+ assert(KNOT_NS_TSIG_DATA_MAX_SIZE - xfr->tsig_data_size
+ >= xfr->wire_size);
+ memcpy(xfr->tsig_data + xfr->tsig_data_size, xfr->wire,
+ xfr->wire_size);
+ xfr->tsig_data_size += xfr->wire_size;
+ }
+
+ assert(zone != NULL);
+
+ while (ret == KNOT_EOK && rr != NULL) {
+ // process the parsed RR
+
+ dbg_xfrin("\nNext RR:\n\n");
+ knot_rrset_dump(rr, 0);
+
+ if (node != NULL
+ && knot_dname_compare(rr->owner, node->owner) != 0) {
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_xfrin("Node owner: %s\n", name);
+ free(name);
+);
+ if (!in_zone) {
+ // this should not happen
+ assert(0);
+ // the node is not in the zone and the RR has
+ // other owner, so a new node must be created
+ // insert the old node to the zone
+ }
+
+ node = NULL;
+ }
+
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ // this must be the last SOA, do not do anything more
+ // discard the RR
+ assert(knot_zone_contents_apex((zone)) != NULL);
+ assert(knot_node_rrset(knot_zone_contents_apex((zone)),
+ KNOT_RRTYPE_SOA) != NULL);
+ dbg_xfrin("Found last SOA, transfer finished.\n");
+
+ dbg_xfrin("Verifying TSIG...\n");
+ /*! \note [TSIG] Now check if there is not a TSIG record
+ * at the end of the packet.
+ */
+ ret = xfrin_check_tsig(packet, xfr, 1);
+
+ dbg_xfrin_detail("xfrin_check_tsig() returned %d\n",
+ ret);
+
+ knot_packet_free(&packet);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+
+ if (ret != KNOT_EOK) {
+ /*! \todo [TSIG] Handle TSIG errors. */
+ return ret;
+ }
+
+ // we must now find place for all orphan RRSIGs
+ ret = xfrin_process_orphan_rrsigs(zone,
+ (*constr)->rrsigs);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to process orphan "
+ "RRSIGs\n");
+ /*! \todo Cleanup?? */
+ return ret;
+ }
+
+ xfrin_free_orphan_rrsigs(&(*constr)->rrsigs);
+
+ return 1;
+ }
+
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_RRSIG) {
+ // RRSIGs require special handling, as there are no
+ // nodes for them
+ knot_rrset_t *tmp_rrset = NULL;
+ ret = knot_zone_contents_add_rrsigs(zone, rr,
+ &tmp_rrset, &node, KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret == KNOT_ENONODE || ret == KNOT_ENORRSET) {
+ dbg_xfrin("No node or RRSet for RRSIGs\n");
+ dbg_xfrin("Saving for later insertion.\n");
+ ret = xfrin_add_orphan_rrsig((*constr)->rrsigs,
+ rr);
+ if (ret > 0) {
+ dbg_xfrin("Merged RRSIGs.\n");
+ knot_rrset_free(&rr);
+ } else if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to save orphan"
+ " RRSIGs.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return ret;
+ }
+ } else if (ret < 0) {
+ dbg_xfrin("Failed to add RRSIGs (%s).\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ERROR; /*! \todo Other error code. */
+ } else if (ret == 1) {
+ assert(node != NULL);
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_xfrin("Found node for the record in "
+ "zone: %s.\n", name);
+ free(name);
+);
+ in_zone = 1;
+ knot_rrset_deep_free(&rr, 1, 0, 0);
+ } else if (ret == 2) {
+ // should not happen
+ assert(0);
+// knot_rrset_deep_free(&rr, 1, 1, 1);
+ } else {
+ assert(node != NULL);
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(node->owner);
+ dbg_xfrin("Found node for the record in "
+ "zone: %s.\n", name);
+ free(name);
+);
+ in_zone = 1;
+ assert(tmp_rrset->rrsigs == rr);
+ }
+
+ // parse next RR
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+
+ continue;
+ }
+
+ /*! \note [TSIG] TSIG where it should not be - in Answer section.*/
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_TSIG) {
+ // not allowed here
+ dbg_xfrin(" in Answer section.\n");
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_EMALF;
+ }
+
+ knot_node_t *(*get_node)(const knot_zone_contents_t *,
+ const knot_dname_t *) = NULL;
+ int (*add_node)(knot_zone_contents_t *, knot_node_t *, int,
+ uint8_t, int) = NULL;
+
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_NSEC3) {
+ get_node = knot_zone_contents_get_nsec3_node;
+ add_node = knot_zone_contents_add_nsec3_node;
+ } else {
+ get_node = knot_zone_contents_get_node;
+ add_node = knot_zone_contents_add_node;
+ }
+
+ if (node == NULL && (node = get_node(zone,
+ knot_rrset_owner(rr))) != NULL) {
+ // the node for this RR was found in the zone
+ dbg_xfrin("Found node for the record in zone.\n");
+ in_zone = 1;
+ }
+
+ if (node == NULL) {
+ // a new node for the RR is required but it is not
+ // in the zone
+ node = knot_node_new(rr->owner, NULL, 0);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node.\n");
+ knot_packet_free(&packet);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ENOMEM;
+ }
+ dbg_xfrin("Created new node for the record.\n");
+
+ // insert the RRSet to the node
+ ret = knot_node_add_rrset(node, rr, 1);
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to node (%s"
+ ")\n", knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ERROR;
+ } else if (ret > 0) {
+ // should not happen, this is new node
+ assert(0);
+// knot_rrset_deep_free(&rr, 1, 0, 0);
+ }
+
+ // insert the node into the zone
+ ret = add_node(zone, node, 1, 0, 1);
+ assert(node != NULL);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add node to zone (%s)"
+ ".\n", knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0); // ???
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ return KNOT_ERROR;
+ }
+
+ in_zone = 1;
+ } else {
+ assert(in_zone);
+
+ ret = knot_zone_contents_add_rrset(zone, rr, &node,
+ KNOT_RRSET_DUPL_MERGE, 1);
+ if (ret < 0) {
+ dbg_xfrin("Failed to add RRSet to zone:"
+ "%s.\n", knot_strerror(ret));
+ return KNOT_ERROR;
+ } else if (ret > 0) {
+ // merged, free the RRSet
+// knot_rrset_deep_free(&rr, 1, 0, 0);
+ knot_rrset_free(&rr);
+ }
+
+ }
+
+ rr = NULL;
+
+ // parse next RR
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ }
+
+ assert(ret != KNOT_EOK || rr == NULL);
+
+ if (ret < 0) {
+ // some error in parsing
+ dbg_xfrin("Could not parse next RR: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 0, 0);
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ /*! \todo Cleanup. */
+ return KNOT_EMALF;
+ }
+
+ assert(ret == KNOT_EOK);
+ assert(rr == NULL);
+
+ // if the last node is not yet in the zone, insert
+ if (!in_zone) {
+ assert(node != NULL);
+ ret = knot_zone_contents_add_node(zone, node, 1, 0, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add last node into zone (%s)"
+ ".\n", knot_strerror(ret));
+ knot_packet_free(&packet);
+ knot_node_free(&node, 1, 0);
+ return KNOT_ERROR; /*! \todo Other error */
+ }
+ }
+
+ /*! \note [TSIG] Now check if there is not a TSIG record at the end of
+ * the packet.
+ */
+ ret = xfrin_check_tsig(packet, xfr,
+ knot_ns_tsig_required(xfr->packet_nr));
+ ++xfr->packet_nr;
+
+ knot_packet_free(&packet);
+ dbg_xfrin("Processed one AXFR packet successfully.\n");
+
+ /*! \note [TSIG] TSIG errors are propagated and reported in a standard
+ * manner, as we're in response processing, no further error response
+ * should be sent.
+ */
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_parse_first_rr(knot_packet_t **packet, const uint8_t *pkt,
+ size_t size, knot_rrset_t **rr)
+{
+ *packet = knot_packet_new(KNOT_PACKET_PREALLOC_NONE);
+ if (packet == NULL) {
+ dbg_xfrin("Could not create packet structure.\n");
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_packet_parse_from_wire(*packet, pkt, size, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse packet: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(packet);
+ return KNOT_EMALF;
+ }
+
+ // check if the TC bit is set (it must not be)
+ if (knot_wire_get_tc(pkt)) {
+ dbg_xfrin("IXFR response has TC bit set.\n");
+ knot_packet_free(packet);
+ return KNOT_EMALF;
+ }
+
+ ret = knot_packet_parse_next_rr_answer(*packet, rr);
+
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Could not parse first Answer RR: %s.\n",
+ knot_strerror(ret));
+ knot_packet_free(packet);
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_process_ixfr_packet(/*const uint8_t *pkt, size_t size,
+ knot_changesets_t **chs*/knot_ns_xfr_t *xfr)
+{
+ size_t size = xfr->wire_size;
+ const uint8_t *pkt = xfr->wire;
+ knot_changesets_t **chs = (knot_changesets_t **)(&xfr->data);
+
+ if (pkt == NULL || chs == NULL) {
+ dbg_xfrin("Wrong parameters supported.\n");
+ return KNOT_EBADARG;
+ }
+
+ // check if the response is OK
+ if (knot_wire_get_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ return KNOT_EXFRREFUSED;
+ }
+
+ knot_packet_t *packet = NULL;
+// knot_rrset_t *soa1 = NULL;
+// knot_rrset_t *soa2 = NULL;
+ knot_rrset_t *rr = NULL;
+
+ int ret;
+
+ if ((ret = xfrin_parse_first_rr(&packet, pkt, size, &rr)) != KNOT_EOK) {
+ return ret;
+ }
+
+ assert(packet != NULL);
+
+ // state of the transfer
+ // -1 .. a SOA is expected to create a new changeset
+ int state = 0;
+
+ if (rr == NULL) {
+ dbg_xfrin("No RRs in the packet.\n");
+ knot_packet_free(&packet);
+ /*! \todo Some other action??? */
+ return KNOT_EMALF;
+ }
+
+ if (*chs == NULL) {
+ dbg_xfrin("Changesets empty, creating new.\n");
+
+ ret = knot_changeset_allocate(chs);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ knot_packet_free(&packet);
+ return ret;
+ }
+
+ // the first RR must be a SOA
+ if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
+ dbg_xfrin("First RR is not a SOA RR!\n");
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ ret = KNOT_EMALF;
+ goto cleanup;
+ }
+
+ // just store the first SOA for later use
+ (*chs)->first_soa = rr;
+ state = -1;
+
+ dbg_xfrin("First SOA of IXFR saved, state set to -1.\n");
+
+ // parse the next one
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /*
+ * If there is no other records in the response than the SOA, it
+ * means one of these two cases:
+ *
+ * 1) The server does not have newer zone than ours.
+ * This is indicated by serial equal to the one of our zone.
+ * 2) The server wants to send the transfer but is unable to fit
+ * it in the packet. This is indicated by serial different
+ * (newer) from the one of our zone.
+ *
+ * The serials must be compared in other parts of the server, so
+ * just indicate that the answer contains only one SOA.
+ */
+ if (rr == NULL) {
+ dbg_xfrin("Response containing only SOA,\n");
+ knot_packet_free(&packet);
+ return XFRIN_RES_SOA_ONLY;
+ } else if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ dbg_xfrin("Fallback to AXFR.\n");
+ ret = XFRIN_RES_FALLBACK;
+ knot_free_changesets(chs);
+ xfr->data = 0;
+ return ret;
+ }
+ } else {
+ if ((*chs)->first_soa == NULL) {
+ dbg_xfrin("Changesets don't contain frist SOA!\n");
+ ret = KNOT_EBADARG;
+ goto cleanup;
+ }
+ dbg_xfrin("Changesets present.\n");
+ }
+
+ /*
+ * Process the next RR. Different requirements are in place in
+ * different cases:
+ *
+ * 1) Last changeset has both soa_from and soa_to.
+ * a) The next RR is a SOA.
+ * i) The next RR is equal to the first_soa saved in changesets.
+ * This denotes the end of the transfer. It may be dropped and
+ * the end should be signalised by returning positive value.
+ *
+ * ii) The next RR is some other SOA.
+ * This means a start of new changeset - create it and add it
+ * to the list.
+ *
+ * b) The next RR is not a SOA.
+ * Put the RR into the ADD part of the last changeset as this is
+ * not finished yet. Continue while SOA is not encountered. Then
+ * jump to 1-a.
+ *
+ * 2) Last changeset has only the soa_from and does not have soa_to.
+ * a) The next RR is a SOA.
+ * This means start of the ADD section. Put the SOA to the
+ * changeset. Continue adding RRs to the ADD section while SOA
+ * is not encountered. This is identical to 1-b.
+ *
+ * b) The next RR is not a SOA.
+ * This means the REMOVE part is not finished yet. Add the RR to
+ * the REMOVE part. Continue adding next RRs until a SOA is
+ * encountered. Then jump to 2-a.
+ */
+
+ // first, find out in what state we are
+ /*! \todo It would be more elegant to store the state in the
+ * changesets structure, or in some place persistent between
+ * calls to this function.
+ */
+ if (state != -1) {
+ dbg_xfrin("State is not -1, deciding...\n");
+ // there should be at least one started changeset right now
+ if ((*chs)->count <= 0) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ ret = KNOT_EMALF;
+ goto cleanup;
+ }
+
+ // a changeset should be created only when there is a SOA
+ assert((*chs)->sets[(*chs)->count - 1].soa_from != NULL);
+
+ if ((*chs)->sets[(*chs)->count - 1].soa_to == NULL) {
+ state = XFRIN_CHANGESET_REMOVE;
+ } else {
+ state = XFRIN_CHANGESET_ADD;
+ }
+ }
+
+ dbg_xfrin("State before the loop: %d\n", state);
+
+ /*! \todo This may be implemented with much less IFs! */
+
+ while (ret == KNOT_EOK && rr != NULL) {
+dbg_xfrin_exec(
+ dbg_xfrin("Next loop, state: %d\n", state);
+ char *name = knot_dname_to_str(knot_rrset_owner(rr));
+ dbg_xfrin("Actual RR: %s, type %s.\n", name,
+ knot_rrtype_to_string(knot_rrset_type(rr)));
+ free(name);
+);
+ switch (state) {
+ case -1:
+ // a SOA is expected
+ // this may be either a start of a changeset or the
+ // last SOA (in case the transfer was empty, but that
+ // is quite weird in fact
+ if (knot_rrset_type(rr) != KNOT_RRTYPE_SOA) {
+ dbg_xfrin("First RR is not a SOA RR!\n");
+ dbg_xfrin("RR type: %s\n",
+ knot_rrtype_to_string(knot_rrset_type(rr)));
+ ret = KNOT_EMALF;
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ if (knot_rdata_soa_serial(knot_rrset_rdata(rr))
+ == knot_rdata_soa_serial(
+ knot_rrset_rdata((*chs)->first_soa))) {
+
+ /*! \note [TSIG] Check TSIG, we're at the end of
+ * transfer.
+ */
+ ret = xfrin_check_tsig(packet, xfr, 1);
+
+ // last SOA, discard and end
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ knot_packet_free(&packet);
+
+ /*! \note [TSIG] If TSIG validates, consider
+ * transfer complete. */
+ if (ret == KNOT_EOK) {
+ ret = XFRIN_RES_COMPLETE;
+ }
+
+ return ret;
+ } else {
+ // normal SOA, start new changeset
+ (*chs)->count++;
+ if ((ret = knot_changesets_check_size(*chs))
+ != KNOT_EOK) {
+ (*chs)->count--;
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ ret = knot_changeset_add_soa(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_REMOVE);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ // change state to REMOVE
+ state = XFRIN_CHANGESET_REMOVE;
+ }
+ break;
+ case XFRIN_CHANGESET_REMOVE:
+ // if the next RR is SOA, store it and change state to
+ // ADD
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ // we should not be here if soa_from is not set
+ assert((*chs)->sets[(*chs)->count - 1].soa_from
+ != NULL);
+
+ ret = knot_changeset_add_soa(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_ADD);
+ if (ret != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+
+ state = XFRIN_CHANGESET_ADD;
+ } else {
+ // just add the RR to the REMOVE part and
+ // continue
+ if ((ret = knot_changeset_add_new_rr(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_REMOVE)) != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+ }
+ break;
+ case XFRIN_CHANGESET_ADD:
+ // if the next RR is SOA change to state -1 and do not
+ // parse next RR
+ if (knot_rrset_type(rr) == KNOT_RRTYPE_SOA) {
+ state = -1;
+ continue;
+ } else {
+
+ // just add the RR to the ADD part and continue
+ if ((ret = knot_changeset_add_new_rr(
+ &(*chs)->sets[(*chs)->count - 1], rr,
+ XFRIN_CHANGESET_ADD)) != KNOT_EOK) {
+ knot_rrset_deep_free(&rr, 1, 1, 1);
+ goto cleanup;
+ }
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ // parse the next RR
+ dbg_xfrin("Parsing next RR..\n");
+ ret = knot_packet_parse_next_rr_answer(packet, &rr);
+ dbg_xfrin("Returned %d, %p.\n", ret, rr);
+ }
+
+ /*! \note Check TSIG, we're at the end of packet. It may not be
+ * required.
+ */
+ ret = xfrin_check_tsig(packet, xfr,
+ knot_ns_tsig_required(xfr->packet_nr));
+ dbg_xfrin_detail("xfrin_check_tsig() returned %d\n", ret);
+ ++xfr->packet_nr;
+
+ /*! \note [TSIG] Cleanup and propagate error if TSIG validation fails.*/
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+
+ // here no RRs remain in the packet but the transfer is not finished
+ // yet, return EOK
+ knot_packet_free(&packet);
+ return KNOT_EOK;
+
+cleanup:
+ /* We should go here only if some error occured. */
+ assert(ret < 0);
+
+ dbg_xfrin("Cleanup after processing IXFR/IN packet.\n");
+ knot_free_changesets(chs);
+ knot_packet_free(&packet);
+ xfr->data = 0;
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+/* Applying changesets to zone */
+/*----------------------------------------------------------------------------*/
+
+typedef struct {
+ /*!
+ * Deleted (without owners and RDATA) after successful update.
+ */
+ knot_rrset_t **old_rrsets;
+ int old_rrsets_count;
+ int old_rrsets_allocated;
+
+ /*!
+ * Deleted after successful update.
+ */
+ knot_rdata_t **old_rdata;
+ uint *old_rdata_types;
+ int old_rdata_count;
+ int old_rdata_allocated;
+
+ /*!
+ * \brief Copied RRSets (i.e. modified by the update).
+ *
+ * Deleted (without owners and RDATA) after failed update.
+ */
+ knot_rrset_t **new_rrsets;
+ int new_rrsets_count;
+ int new_rrsets_allocated;
+
+ /*!
+ * Deleted (without contents) after successful update.
+ */
+ knot_node_t **old_nodes;
+ int old_nodes_count;
+ int old_nodes_allocated;
+
+ /*!
+ * Deleted (without contents) after failed update.
+ */
+ knot_node_t **new_nodes;
+ int new_nodes_count;
+ int new_nodes_allocated;
+
+ ck_hash_table_item_t **old_hash_items;
+ int old_hash_items_count;
+ int old_hash_items_allocated;
+} xfrin_changes_t;
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_changes_free(xfrin_changes_t **changes)
+{
+ free((*changes)->old_nodes);
+ free((*changes)->old_rrsets);
+ free((*changes)->old_rdata);
+ free((*changes)->old_rdata_types);
+ free((*changes)->new_rrsets);
+ free((*changes)->new_nodes);
+ free((*changes)->old_hash_items);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_rrsets(knot_rrset_t ***rrsets,
+ int *count, int *allocated, int to_add)
+{
+ /* Ensure at least requested size is allocated. */
+ int new_count = (*count + to_add);
+ assert(new_count >= 0);
+ if (new_count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* Allocate new memory block. */
+ knot_rrset_t **rrsets_new = malloc(new_count * sizeof(knot_rrset_t *));
+ if (rrsets_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Initialize new memory and copy old data. */
+ memset(rrsets_new, 0, new_count * sizeof(knot_rrset_t *));
+ memcpy(rrsets_new, *rrsets, (*allocated) * sizeof(knot_rrset_t *));
+
+ /* Free old nodes and switch pointers. */
+ free(*rrsets);
+ *rrsets = rrsets_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_nodes(knot_node_t ***nodes,
+ int *count, int *allocated)
+{
+ assert(nodes != NULL);
+ assert(count != NULL);
+ assert(allocated != 0);
+
+ /* Ensure at least count and some reserve is allocated. */
+ int new_count = *count + 2;
+ if (new_count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* Allocate new memory block. */
+ const size_t node_len = sizeof(knot_node_t *);
+ knot_node_t **nodes_new = malloc(new_count * node_len);
+ if (nodes_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Clear memory block and copy old data. */
+ memset(nodes_new, 0, new_count * node_len);
+ memcpy(nodes_new, *nodes, (*allocated) * node_len);
+
+ /* Free old nodes and switch pointers. */
+ free(*nodes);
+ *nodes = nodes_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_rdata(knot_rdata_t ***rdatas, uint **types,
+ int count, int *allocated, int to_add)
+{
+ /* Ensure at least requested size is allocated. */
+ int new_count = (count + to_add);
+ assert(new_count >= 0);
+ if (new_count <= *allocated) {
+ return KNOT_EOK;
+ }
+
+ /* Allocate new memory block. */
+ knot_rdata_t **rdatas_new = malloc(new_count * sizeof(knot_rdata_t *));
+ if (rdatas_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ uint *types_new = malloc(new_count * sizeof(uint));
+ if (types_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Initialize new memory and copy old data. */
+ memset(rdatas_new, 0, new_count * sizeof(knot_rdata_t *));
+ memcpy(rdatas_new, *rdatas, (*allocated) * sizeof(knot_rdata_t *));
+
+ memset(types_new, 0, new_count * sizeof(uint));
+ memcpy(types_new, *types, (*allocated) * sizeof(uint));
+
+ /* Free old rdatas and switch pointers. */
+ free(*rdatas);
+ free(*types);
+ *rdatas = rdatas_new;
+ *types = types_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_changes_check_hash_items(ck_hash_table_item_t ***items,
+ int *count, int *allocated)
+{
+ /* Prevent infinite loop in case of allocated = 0. */
+ int new_count = 0;
+ if (*allocated == 0) {
+ new_count = *count + 1;
+ } else {
+ if (*count == *allocated) {
+ new_count = *allocated * 2;
+ }
+ }
+
+ const size_t item_len = sizeof(ck_hash_table_item_t *);
+ ck_hash_table_item_t **items_new = malloc(new_count * item_len);
+ if (items_new == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(items_new, 0, new_count * item_len);
+ memcpy(items_new, *items, (*count) * item_len);
+ free(*items);
+ *items = items_new;
+ *allocated = new_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
+{
+ /*! \todo This should be all in some API!! */
+
+ if ((*contents)->table != NULL) {
+// ck_destroy_table(&(*contents)->table, NULL, 0);
+ ck_table_free(&(*contents)->table);
+ }
+
+ // 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);
+
+ knot_dname_table_deep_free(&(*contents)->dname_table);
+
+ free(*contents);
+ *contents = NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_rollback_update(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes)
+{
+ /*
+ * This function is called only when no references were actually set to
+ * the new nodes, just the new nodes reference other.
+ * We thus do not need to fix any references, just from the old nodes
+ * to the new ones.
+ */
+
+ // discard new nodes, but do not remove RRSets from them
+ for (int i = 0; i < changes->new_nodes_count; ++i) {
+ knot_node_free(&changes->new_nodes[i], 0, 0);
+ }
+
+ // set references from old nodes to new nodes to NULL and remove the
+ // old flag
+ for (int i = 0; i < changes->old_nodes_count; ++i) {
+ knot_node_set_new_node(changes->old_nodes[i], NULL);
+ knot_node_clear_old(changes->old_nodes[i]);
+ }
+
+ // discard new RRSets
+ for (int i = 0; i < changes->old_rrsets_count; ++i) {
+ knot_rrset_deep_free(&changes->new_rrsets[i], 0, 1, 0);
+ }
+
+ // destroy the shallow copy of zone
+ xfrin_zone_contents_free(&contents);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_rdata_t *xfrin_remove_rdata(knot_rrset_t *from,
+ const knot_rrset_t *what)
+{
+ knot_rdata_t *old = NULL;
+ knot_rdata_t *old_actual = NULL;
+
+ const knot_rdata_t *rdata = knot_rrset_rdata(what);
+
+ while (rdata != NULL) {
+ old_actual = knot_rrset_remove_rdata(from, rdata);
+ if (old_actual != NULL) {
+ old_actual->next = old;
+ old = old_actual;
+ }
+ rdata = knot_rrset_rdata_next(what, rdata);
+ }
+
+ return old;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_get_node_copy(knot_node_t **node, xfrin_changes_t *changes)
+{
+ knot_node_t *new_node =
+ knot_node_get_new_node(*node);
+ if (new_node == NULL) {
+ dbg_xfrin("Creating copy of node.\n");
+ int ret = knot_node_shallow_copy(*node, &new_node);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to create node copy.\n");
+ return KNOT_ENOMEM;
+ }
+
+ dbg_xfrin_detail("Created copy of old node %p to new node %p\n",
+ *node, new_node);
+
+ assert(changes);
+
+// changes->new_nodes_allocated = 0;
+
+ // save the copy of the node
+ ret = xfrin_changes_check_nodes(
+ &changes->new_nodes,
+ &changes->new_nodes_count,
+ &changes->new_nodes_allocated);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new node to list.\n");
+ knot_node_free(&new_node, 0, 0);
+ return ret;
+ }
+
+// changes->old_nodes_allocated = 0;
+
+ // save the old node to list of old nodes
+ ret = xfrin_changes_check_nodes(
+ &changes->old_nodes,
+ &changes->old_nodes_count,
+ &changes->old_nodes_allocated);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old node to list.\n");
+ knot_node_free(&new_node, 0, 0);
+ return ret;
+ }
+
+ assert(changes->new_nodes);
+ assert(changes->old_nodes);
+
+ changes->new_nodes[changes->new_nodes_count++] = new_node;
+ changes->old_nodes[changes->old_nodes_count++] = *node;
+
+ // mark the old node as old
+ knot_node_set_old(*node);
+
+ knot_node_set_new(new_node);
+ knot_node_set_new_node(*node, new_node);
+ }
+
+ *node = new_node;
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
+ xfrin_changes_t *changes)
+{
+ // create new RRSet by copying the old one
+ int ret = knot_rrset_shallow_copy(old, copy);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to create RRSet copy.\n");
+ return KNOT_ENOMEM;
+ }
+
+ // add the RRSet to the list of new RRSets
+ ret = xfrin_changes_check_rrsets(&changes->new_rrsets,
+ &changes->new_rrsets_count,
+ &changes->new_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new RRSet to list.\n");
+ knot_rrset_free(copy);
+ return ret;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] = *copy;
+
+ // add the old RRSet to the list of old RRSets
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RRSet to list.\n");
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = old;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_copy_rrset(knot_node_t *node, knot_rr_type_t type,
+ knot_rrset_t **rrset, xfrin_changes_t *changes)
+{
+ knot_rrset_t *old = knot_node_remove_rrset(node, type);
+
+ if (old == NULL) {
+ dbg_xfrin("RRSet not found for RR to be removed.\n");
+ return 1;
+ }
+
+ int ret = xfrin_copy_old_rrset(old, rrset, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dbg_xfrin_detail("Copied old rrset %p to new %p.\n",
+ old, *rrset);
+
+ // replace the RRSet in the node copy by the new one
+ ret = knot_node_add_rrset(node, *rrset, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet copy to node\n");
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove_rrsigs(xfrin_changes_t *changes,
+ const knot_rrset_t *remove,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(remove != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+ assert(knot_rrset_type(remove) == KNOT_RRTYPE_RRSIG);
+
+ /*! \todo These optimalizations may be useless as there may be only
+ * one RRSet of each type and owner in the changeset.
+ */
+
+ int ret;
+
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove))) {
+ // find RRSet based on the Type Covered
+ knot_rr_type_t type = knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove));
+
+ // copy the rrset
+ ret = xfrin_copy_rrset(node, type, rrset, changes);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to copy rrset from changeset.\n");
+ return ret;
+ }
+ } else {
+ // we should have the right RRSIG RRSet in *rrset
+ assert(knot_rrset_type(*rrset)
+ == knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(remove)));
+ // this RRSet should be the already copied RRSet so we may
+ // update it right away
+ }
+
+ // get the old rrsigs
+ knot_rrset_t *old = knot_rrset_get_rrsigs(*rrset);
+ if (old == NULL) {
+ return 1;
+ }
+
+ // copy the RRSIGs
+ /*! \todo This may be done unnecessarily more times. */
+ knot_rrset_t *rrsigs;
+ ret = xfrin_copy_old_rrset(old, &rrsigs, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // set the RRSIGs to the new RRSet copy
+ if (knot_rrset_set_rrsigs(*rrset, rrsigs) != KNOT_EOK) {
+ dbg_xfrin("Failed to set rrsigs.\n");
+ return KNOT_ERROR;
+ }
+
+
+
+ // now in '*rrset' we have a copy of the RRSet which holds the RRSIGs
+ // and in 'rrsigs' we have the copy of the RRSIGs
+
+ knot_rdata_t *rdata = xfrin_remove_rdata(rrsigs, remove);
+ if (rdata == NULL) {
+ dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n",
+ knot_strerror(ret));
+ return 1;
+ }
+
+ // if the RRSet is empty, remove from node and add to old RRSets
+ // check if there is no RRSIGs; if there are, leave the RRSet
+ // there; it may be eventually removed when the RRSIGs are removed
+ if (knot_rrset_rdata(rrsigs) == NULL) {
+ // remove the RRSIGs from the RRSet
+ knot_rrset_set_rrsigs(*rrset, NULL);
+
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add empty RRSet to the "
+ "list of old RRSets.");
+ // delete the RRSet right away
+ knot_rrset_free(&rrsigs);
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = rrsigs;
+
+ // now check if the RRSet is not totally empty
+ if (knot_rrset_rdata(*rrset) == NULL) {
+ assert(knot_rrset_rrsigs(*rrset) == NULL);
+
+ // remove the whole RRSet from the node
+ knot_rrset_t *tmp = knot_node_remove_rrset(node,
+ knot_rrset_type(*rrset));
+ assert(tmp == *rrset);
+
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add empty RRSet to "
+ "the list of old RRSets.");
+ // delete the RRSet right away
+ knot_rrset_free(rrset);
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] =
+ *rrset;
+ }
+ }
+
+ // connect the RDATA to the list of old RDATA
+ ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ changes->old_rdata[changes->old_rdata_count] = rdata;
+ changes->old_rdata_types[changes->old_rdata_count] =
+ knot_rrset_type(remove);
+ ++changes->old_rdata_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove_normal(xfrin_changes_t *changes,
+ const knot_rrset_t *remove,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(remove != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+
+ int ret;
+
+ dbg_xfrin_detail("Removing RRSet: \n");
+ knot_rrset_dump(remove, 0);
+
+ // now we have the copy of the node, so lets get the right RRSet
+ // check if we do not already have it
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset)
+ != knot_rrset_type(remove)) {
+ /*!
+ * \todo This may happen also with already
+ * copied RRSet. In that case it would be
+ * an unnecesary overhead but will
+ * probably not cause problems. TEST!!
+ */
+ ret = xfrin_copy_rrset(node,
+ knot_rrset_type(remove), rrset, changes);
+ dbg_xfrin_detail("Copied RRSet:\n");
+ knot_rrset_dump(*rrset, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ if (*rrset == NULL) {
+ dbg_xfrin("RRSet not found for RR to be removed.\n");
+ return 1;
+ }
+
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
+ dbg_xfrin("Updating RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ free(name);
+);
+
+ // remove the specified RRs from the RRSet (de facto difference of
+ // sets)
+ knot_rdata_t *rdata = xfrin_remove_rdata(*rrset, remove);
+ if (rdata == NULL) {
+ dbg_xfrin("Failed to remove RDATA from RRSet: %s.\n",
+ knot_strerror(ret));
+ return 1;
+ }
+
+dbg_xfrin_exec_detail(
+ dbg_xfrin_detail("Removed rdata: \n");
+ knot_rdata_t *r = rdata;
+ if (r != NULL) {
+ do {
+ dbg_xfrin_detail("pointer: %p\n", r);
+ knot_rdata_dump(r, knot_rrset_type(remove), 0);
+ r = r->next;
+ } while (r != NULL && r != rdata);
+ }
+);
+
+ // if the RRSet is empty, remove from node and add to old RRSets
+ // check if there is no RRSIGs; if there are, leave the RRSet
+ // there; it may be eventually removed when the RRSIGs are removed
+ if (knot_rrset_rdata(*rrset) == NULL
+ && knot_rrset_rrsigs(*rrset) == NULL) {
+
+ knot_rrset_t *tmp = knot_node_remove_rrset(node,
+ knot_rrset_type(*rrset));
+ dbg_xfrin_detail("Removed whole RRSet (%p).\n", tmp);
+
+ // add the removed RRSet to list of old RRSets
+
+ assert(tmp == *rrset);
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add empty RRSet to the "
+ "list of old RRSets.");
+ // delete the RRSet right away
+ knot_rrset_free(rrset);
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = *rrset;
+ }
+
+ // connect the RDATA to the list of old RDATA
+ ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ changes->old_rdata[changes->old_rdata_count] = rdata;
+ changes->old_rdata_types[changes->old_rdata_count] =
+ knot_rrset_type(remove);
+ ++changes->old_rdata_count;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove_all_rrsets(xfrin_changes_t *changes,
+ knot_node_t *node, uint16_t type)
+{
+ /*! \todo Implement. */
+ int ret;
+
+ if (type == KNOT_RRTYPE_ANY) {
+ // put all the RRSets to the changes structure
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ knot_node_rrset_count(node));
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to check changeset rrsets.\n");
+ return ret;
+ }
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+ knot_rrset_t **place = changes->old_rrsets
+ + changes->old_rrsets_count;
+ /*! \todo Test this!!! */
+ memcpy(place, rrsets, knot_node_rrset_count(node) * sizeof(knot_rrset_t *));
+
+ // remove all RRSets from the node
+ knot_node_remove_all_rrsets(node);
+ } else {
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated,
+ 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to check changeset rrsets.\n");
+ return ret;
+ }
+ // remove only RRSet with the given type
+ knot_rrset_t *rrset = knot_node_remove_rrset(node, type);
+ changes->old_rrsets[changes->old_rrsets_count++] = rrset;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_remove(knot_zone_contents_t *contents,
+ knot_changeset_t *chset,
+ xfrin_changes_t *changes)
+{
+ /*
+ * Iterate over removed RRSets, copy appropriate nodes and remove
+ * the rrsets from them. By default, the RRSet should be copied so that
+ * RDATA may be removed from it.
+ */
+ int ret = 0;
+ knot_node_t *node = NULL;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < chset->remove_count; ++i) {
+ // check if the old node is not the one we should use
+ if (!node || knot_rrset_owner(chset->remove[i])
+ != knot_node_owner(node)) {
+ node = knot_zone_contents_get_node(contents,
+ knot_rrset_owner(chset->remove[i]));
+ if (node == NULL) {
+ dbg_xfrin("Node not found for RR to be removed"
+ "!\n");
+ continue;
+ }
+ }
+
+ // create a copy of the node if not already created
+ if (!knot_node_is_new(node)) {
+ ret = xfrin_get_node_copy(&node, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ assert(node != NULL);
+ assert(knot_node_is_new(node));
+
+ // first check if all RRSets should be removed
+ if (knot_rrset_class(chset->remove[i]) == KNOT_CLASS_ANY) {
+ ret = xfrin_apply_remove_all_rrsets(
+ changes, node,
+ knot_rrset_type(chset->remove[i]));
+ } else if (knot_rrset_type(chset->remove[i])
+ == KNOT_RRTYPE_RRSIG) {
+ // this should work also for UPDATE
+ ret = xfrin_apply_remove_rrsigs(changes,
+ chset->remove[i],
+ node, &rrset);
+ } else {
+ // this should work also for UPDATE
+ ret = xfrin_apply_remove_normal(changes,
+ chset->remove[i],
+ node, &rrset);
+ }
+
+ dbg_xfrin("xfrin_apply_remove() ret = %d\n", ret);
+
+ if (ret > 0) {
+ continue;
+ } else if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static knot_node_t *xfrin_add_new_node(knot_zone_contents_t *contents,
+ knot_rrset_t *rrset)
+{
+ /*! \todo Why is the function disabled? */
+ //return NULL;
+
+ knot_node_t *node = knot_node_new(knot_rrset_get_owner(rrset),
+ NULL, KNOT_NODE_FLAGS_NEW);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create a new node.\n");
+ return NULL;
+ }
+
+ int ret = 0;
+
+ // insert the node into zone structures and create parents if
+ // necessary
+// dbg_xfrin("Adding new node to zone. From owner: %s type %s\n",
+// knot_dname_to_str(node->owner),
+// knot_rrtype_to_string(rrset->type));
+// getchar();
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3) {
+ ret = knot_zone_contents_add_nsec3_node(contents, node, 1, 0,
+ 1);
+ } else {
+ ret = knot_zone_contents_add_node(contents, node, 1,
+ KNOT_NODE_FLAGS_NEW, 1);
+ }
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add new node to zone contents.\n");
+ return NULL;
+ }
+
+ // find previous node and connect the new one to it
+ knot_node_t *prev = NULL;
+ if (knot_rrset_type(rrset) == KNOT_RRTYPE_NSEC3) {
+ prev = knot_zone_contents_get_previous_nsec3(contents,
+ knot_rrset_owner(rrset));
+ } else {
+ prev = knot_zone_contents_get_previous(contents,
+ knot_rrset_owner(rrset));
+ }
+
+ // fix prev and next pointers
+ if (prev != NULL) {
+ knot_node_set_previous(node, prev);
+ }
+
+// printf("contents owned by: %s (%p)\n",
+// knot_dname_to_str(contents->apex->owner),
+// contents);
+ assert(contents->zone != NULL);
+ knot_node_set_zone(node, contents->zone);
+
+ return node;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add_normal(xfrin_changes_t *changes,
+ knot_rrset_t *add,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(add != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+
+ int ret;
+
+ dbg_xfrin("applying rrset:\n");
+ knot_rrset_dump(add, 0);
+// getchar();
+
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset)
+ != knot_rrset_type(add)) {
+ dbg_xfrin("Removing rrset!\n");
+ *rrset = knot_node_remove_rrset(node, knot_rrset_type(add));
+ }
+
+ dbg_xfrin("Removed RRSet: \n");
+ knot_rrset_dump(*rrset, 1);
+
+ if (*rrset == NULL) {
+dbg_xfrin_exec_verb(
+ char *name = knot_dname_to_str(add->owner);
+ dbg_xfrin_verb("RRSet to be added not found in zone.\n");
+ dbg_xfrin_verb("owner: %s type: %s\n", name,
+ knot_rrtype_to_string(add->type));
+ free(name);
+);
+// getchar();
+ // add the RRSet from the changeset to the node
+ /*! \todo What about domain names?? Shouldn't we use the
+ * zone-contents' version of this function??
+ */
+ ret = knot_node_add_rrset(node, add, 0);
+// ret = knot_zone_contents_add_rrset(node->zone->contents,
+// rrset, node,
+// KNOT_RRSET_DUPL_MERGE,
+// 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+ return 1; // return 1 to indicate the add RRSet was used
+ }
+
+ knot_rrset_t *old = *rrset;
+
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
+ dbg_xfrin("Found RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ free(name);
+);
+ knot_rrset_dump(*rrset, 1);
+ ret = xfrin_copy_old_rrset(old, rrset, changes);
+ if (ret != KNOT_EOK) {
+ assert(0);
+ return ret;
+ }
+
+// dbg_xfrin("After copy: Found RRSet with owner %s, type %s\n",
+// knot_dname_to_str((*rrset)->owner),
+// knot_rrtype_to_string(knot_rrset_type(*rrset)));
+
+ // merge the changeset RRSet to the copy
+ /* What if the update fails?
+ * The changesets will be destroyed - that will destroy 'add',
+ * and the copied RRSet will be destroyed because it is in the new
+ * rrsets list.
+ *
+ * If the update is successfull, the old RRSet will be destroyed,
+ * but the one from the changeset will be not!!
+ *
+ * TODO: add the 'add' rrset to list of old RRSets?
+ */
+ dbg_xfrin("Merging RRSets with owners: %s %s types: %d %d\n",
+ (*rrset)->owner->name, add->owner->name, (*rrset)->type,
+ add->type);
+ dbg_xfrin_detail("RDATA in RRSet1: %p, RDATA in RRSet2: %p\n",
+ (*rrset)->rdata, add->rdata);
+ ret = knot_rrset_merge((void **)rrset, (void **)&add);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to merge changeset RRSet to copy.\n");
+ return KNOT_ERROR;
+ }
+ dbg_xfrin("Merge returned: %d\n", ret);
+ knot_rrset_dump(*rrset, 1);
+ ret = knot_node_add_rrset(node, *rrset, 0);
+
+ // return 2 so that the add RRSet is removed from
+ // the changeset (and thus not deleted)
+ // and put to list of new RRSets (is this ok?)
+ // and deleted
+ return 2;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add_rrsig(xfrin_changes_t *changes,
+ knot_rrset_t *add,
+ knot_node_t *node,
+ knot_rrset_t **rrset)
+{
+ assert(changes != NULL);
+ assert(add != NULL);
+ assert(node != NULL);
+ assert(rrset != NULL);
+ assert(knot_rrset_type(add) == KNOT_RRTYPE_RRSIG);
+
+ int ret;
+
+ knot_rr_type_t type = knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(add));
+
+ if (!*rrset
+ || knot_dname_compare(knot_rrset_owner(*rrset),
+ knot_node_owner(node)) != 0
+ || knot_rrset_type(*rrset) != knot_rdata_rrsig_type_covered(
+ knot_rrset_rdata(add))) {
+ // copy the rrset
+ ret = xfrin_copy_rrset(node, type, rrset, changes);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ // we should have the right RRSIG RRSet in *rrset
+ assert(knot_rrset_type(*rrset) == type);
+ // this RRSet should be the already copied RRSet so we may
+ // update it right away
+ }
+
+ if (*rrset == NULL) {
+ dbg_xfrin("RRSet to be added not found in zone.\n");
+
+ // create a new RRSet to add the RRSIGs into
+ *rrset = knot_rrset_new(knot_node_get_owner(node), type,
+ knot_rrset_class(add),
+ knot_rrset_ttl(add));
+ if (*rrset == NULL) {
+ dbg_xfrin("Failed to create new RRSet for RRSIGs.\n");
+ return KNOT_ENOMEM;
+ }
+
+ // add the RRSet from the changeset to the node
+ ret = knot_node_add_rrset(node, *rrset, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+ }
+
+dbg_xfrin_exec(
+ char *name = knot_dname_to_str(knot_rrset_owner(*rrset));
+ dbg_xfrin("Found RRSet with owner %s, type %s\n", name,
+ knot_rrtype_to_string(knot_rrset_type(*rrset)));
+ free(name);
+);
+
+ if (knot_rrset_rrsigs(*rrset) == NULL) {
+ ret = knot_rrset_set_rrsigs(*rrset, add);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSIGs to the RRSet.\n");
+ return KNOT_ERROR;
+ }
+
+ return 1;
+ } else {
+ knot_rrset_t *old = knot_rrset_get_rrsigs(*rrset);
+ assert(old != NULL);
+ knot_rrset_t *rrsig;
+
+ ret = xfrin_copy_old_rrset(old, &rrsig, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // replace the old RRSIGs with the new ones
+ knot_rrset_set_rrsigs(*rrset, rrsig);
+
+ // merge the changeset RRSet to the copy
+ /*! \todo What if the update fails?
+ *
+ */
+ ret = knot_rrset_merge((void **)&rrsig, (void **)&add);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to merge changeset RRSet to copy.\n");
+ return KNOT_ERROR;
+ }
+
+ return 2;
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_add(knot_zone_contents_t *contents,
+ knot_changeset_t *chset,
+ xfrin_changes_t *changes)
+{
+ // iterate over removed RRSets, copy appropriate nodes and remove
+ // the rrsets from them
+ int ret = 0;
+ knot_node_t *node = NULL;
+ knot_rrset_t *rrset = NULL;
+
+ for (int i = 0; i < chset->add_count; ++i) {
+ dbg_xfrin_detail("Adding RRSet:\n");
+ knot_rrset_dump(chset->add[i], 0);
+ // check if the old node is not the one we should use
+ if (!node || knot_rrset_owner(chset->add[i])
+ != knot_node_owner(node)) {
+ node = knot_zone_contents_get_node(contents,
+ knot_rrset_owner(chset->add[i]));
+ if (node == NULL) {
+ // create new node, connect it properly to the
+ // zone nodes
+ dbg_xfrin("Creating new node from.\n");
+ node = xfrin_add_new_node(contents,
+ chset->add[i]);
+ if (node == NULL) {
+ dbg_xfrin("Failed to create new node "
+ "in zone.\n");
+ return KNOT_ERROR;
+ }
+// continue; // continue with another RRSet
+ }
+ }
+
+ // create a copy of the node if not already created
+ if (!knot_node_is_new(node)) {
+ xfrin_get_node_copy(&node, changes);
+ }
+
+ assert(node != NULL);
+ assert(knot_node_is_new(node));
+
+ if (knot_rrset_type(chset->add[i]) == KNOT_RRTYPE_RRSIG) {
+ ret = xfrin_apply_add_rrsig(changes, chset->add[i],
+ node, &rrset);
+ } else {
+ ret = xfrin_apply_add_normal(changes, chset->add[i],
+ node, &rrset);
+ }
+
+ dbg_xfrin("xfrin_apply_..() returned %d, rrset: %p\n", ret,
+ rrset);
+
+ if (ret == 1) {
+ // the ADD RRSet was used, i.e. it should be removed
+ // from the changeset and saved in the list of new
+ // RRSets
+ ret = xfrin_changes_check_rrsets(
+ &changes->new_rrsets,
+ &changes->new_rrsets_count,
+ &changes->new_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RRSet to list.\n");
+ return ret;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] =
+ chset->add[i];
+
+ chset->add[i] = NULL;
+ } else if (ret == 2) {
+ // the copy of the RRSet was used, but it was already
+ // stored in the new RRSets list
+ // just delete the add RRSet, but without RDATA
+ // as these were merged to the copied RRSet
+ knot_rrset_free(&chset->add[i]);
+ } else if (ret != KNOT_EOK) {
+
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+ * \todo This must be tested!! Simulate failure somehow.
+ */
+static void xfrin_clean_changes_after_fail(xfrin_changes_t *changes)
+{
+ /* 1) Delete copies of RRSets created because they were updated.
+ * Do not delete their RDATA or owners.
+ */
+ for (int i = 0; i < changes->new_rrsets_count; ++i) {
+ knot_rrset_free(&changes->new_rrsets[i]);
+ }
+
+ /* 2) Delete copies of nodes created because they were updated.
+ * Do not delete their RRSets.
+ */
+ for (int i = 0; i < changes->new_nodes_count; ++i) {
+ knot_node_free(&changes->new_nodes[i], 0, 1);
+ }
+
+ // changesets will be deleted elsewhere
+ // so just delete the changes structure
+ xfrin_changes_free(&changes);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_replace_soa(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes,
+ knot_changeset_t *chset)
+{
+ knot_node_t *node = knot_zone_contents_get_apex(contents);
+ assert(node != NULL);
+
+ int ret = 0;
+
+ // create a copy of the node if not already created
+ if (!knot_node_is_new(node)) {
+ ret = xfrin_get_node_copy(&node, changes);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ assert(knot_node_is_new(node));
+
+ // set the node copy as the apex of the contents
+ contents->apex = node;
+
+ // remove the SOA RRSet from the apex
+ knot_rrset_t *rrset = knot_node_remove_rrset(node, KNOT_RRTYPE_SOA);
+ assert(rrset != NULL);
+
+ // add the old RRSet to the list of old RRSets
+ ret = xfrin_changes_check_rrsets(&changes->old_rrsets,
+ &changes->old_rrsets_count,
+ &changes->old_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RRSet to list.\n");
+ return ret;
+ }
+
+ // save also the SOA RDATA, because RDATA are not deleted with the
+ // RRSet
+ ret = xfrin_changes_check_rdata(&changes->old_rdata,
+ &changes->old_rdata_types,
+ changes->old_rdata_count,
+ &changes->old_rdata_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RDATA to list.\n");
+ return ret;
+ }
+
+ // save the SOA to the new RRSet, so that it is deleted if the
+ // apply fails
+ ret = xfrin_changes_check_rrsets(&changes->new_rrsets,
+ &changes->new_rrsets_count,
+ &changes->new_rrsets_allocated, 1);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add old RRSet to list.\n");
+ return ret;
+ }
+
+ changes->old_rrsets[changes->old_rrsets_count++] = rrset;
+
+ /*! \todo Maybe check if the SOA does not have more RDATA? */
+ changes->old_rdata[changes->old_rdata_count] = rrset->rdata;
+ changes->old_rdata_types[changes->old_rdata_count] = KNOT_RRTYPE_SOA;
+ ++changes->old_rdata_count;
+
+ // insert the new SOA RRSet to the node
+ dbg_xfrin_verb("Adding SOA.\n");
+ ret = knot_node_add_rrset(node, chset->soa_to, 0);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to add RRSet to node.\n");
+ return KNOT_ERROR;
+ }
+
+ changes->new_rrsets[changes->new_rrsets_count++] = chset->soa_to;
+
+ // remove the SOA from the changeset, so it will not be deleted after
+ // successful apply
+ chset->soa_to = NULL;
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_apply_changeset(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes,
+ knot_changeset_t *chset)
+{
+ /*
+ * Applies one changeset to the zone. Checks if the changeset may be
+ * applied (i.e. the origin SOA (soa_from) has the same serial as
+ * SOA in the zone apex.
+ */
+
+ // check if serial matches
+ /*! \todo Only if SOA is present? */
+ const knot_rrset_t *soa = knot_node_rrset(contents->apex,
+ KNOT_RRTYPE_SOA);
+ if (soa == NULL || knot_rdata_soa_serial(knot_rrset_rdata(soa))
+ != chset->serial_from) {
+ dbg_xfrin("SOA serials do not match!!\n");
+ return KNOT_ERROR;
+ }
+
+ int ret = xfrin_apply_remove(contents, chset, changes);
+ if (ret != KNOT_EOK) {
+ xfrin_clean_changes_after_fail(changes);
+ return ret;
+ }
+
+ ret = xfrin_apply_add(contents, chset, changes);
+ if (ret != KNOT_EOK) {
+ xfrin_clean_changes_after_fail(changes);
+ return ret;
+ }
+
+ /*! \todo Only if SOA is present? */
+ return xfrin_apply_replace_soa(contents, changes, chset);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_check_node_in_tree(knot_zone_tree_node_t *tnode, void *data)
+{
+ assert(tnode != NULL);
+ assert(data != NULL);
+ assert(tnode->node != NULL);
+
+ xfrin_changes_t *changes = (xfrin_changes_t *)data;
+
+ knot_node_t *node = knot_node_get_new_node(tnode->node);
+
+ if (node == NULL) {
+ // no RRSets were removed from this node, thus it cannot be
+ // empty
+ return;
+ }
+
+ dbg_xfrin("xfrin_check_node_in_tree: children of old node: %u, "
+ "children of new node: %u.\n",
+ knot_node_children(node),
+ knot_node_children(tnode->node));
+
+
+ // check if the node is empty and has no children
+ // to be sure, check also the count of children of the old node
+ if (knot_node_rrset_count(node) == 0
+ && knot_node_children(node) == 0
+ && knot_node_children(tnode->node) == 0) {
+ // in this case the new node copy should be removed
+ // but it cannot be deleted because if a rollback happens,
+ // the node must be in the new nodes list
+ // just add it to the old nodes list so that it is deleted
+ // after successful update
+
+ // set the new node of the old node to NULL
+ knot_node_set_new_node(tnode->node, NULL);
+
+ // if the parent has a new copy, decrease the number of
+ // children of that copy
+ if (knot_node_new_node(knot_node_parent(node, 0))) {
+ /*! \todo Replace by some API. */
+ --node->parent->new_node->children;
+ }
+
+ // put the new node to te list of old nodes
+ if (xfrin_changes_check_nodes(&changes->old_nodes,
+ &changes->old_nodes_count,
+ &changes->old_nodes_allocated)
+ != KNOT_EOK) {
+ /*! \todo Notify about the error!!! */
+ return;
+ }
+
+ changes->old_nodes[changes->old_nodes_count++] = node;
+
+ // leave the old node in the old node list, we will delete
+ // it later
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_finalize_remove_nodes(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes)
+{
+ assert(contents != NULL);
+ assert(changes != NULL);
+
+ knot_node_t *node;
+ knot_zone_tree_node_t *removed;
+ ck_hash_table_item_t *rem_hash;
+ int ret;
+
+ for (int i = 0; i < changes->old_nodes_count; ++i) {
+ node = changes->old_nodes[i];
+
+ // if the node is marked as old and has no new node copy
+ // remove it from the zone structure but do not delete it
+ // that may be done only after the grace period
+ if (knot_node_is_old(node)
+ && knot_node_new_node(node) == NULL) {
+
+ if (knot_node_rrset(node, KNOT_RRTYPE_NSEC3)
+ != NULL) {
+ ret = knot_zone_contents_remove_nsec3_node(
+ contents, node, &removed);
+ } else {
+ ret = knot_zone_contents_remove_node(
+ contents, node, &removed, &rem_hash);
+ }
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to remove node from zone"
+ "!\n");
+ return KNOT_ENONODE;
+ }
+
+ assert(removed != NULL);
+ assert(removed->node == node);
+ // delete the tree node (not needed)
+ free(removed);
+
+ if (rem_hash != NULL) {
+ // save the removed hash table item
+ ret = xfrin_changes_check_hash_items(
+ &changes->old_hash_items,
+ &changes->old_hash_items_count,
+ &changes->old_hash_items_allocated);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to save the hash"
+ " table item to list of "
+ "old items.\n");
+ return ret;
+ }
+ changes->old_hash_items[
+ changes->old_hash_items_count++]
+ = rem_hash;
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_finalize_contents(knot_zone_contents_t *contents,
+ xfrin_changes_t *changes)
+{
+ // don't know what should have been done here, except for one thing:
+ // walk through the zone and remove empty nodes (save them in the
+ // old nodes list). But only those having no children!!!
+
+ /*
+ * Walk through the zone and remove empty nodes.
+ * We must walk backwards, so that children are processed before
+ * their parents. This will allow to remove chain of parent-children
+ * nodes.
+ * We cannot remove the nodes right away as it would modify the very
+ * structure used for walking through the zone. Just put the nodes
+ * to the list of old nodes to be removed.
+ * We must also decrease the node's parent's children count now
+ * and not when deleting the node, so that the chain of parent-child
+ * nodes may be removed.
+ */
+ knot_zone_tree_t *t = knot_zone_contents_get_nodes(contents);
+ assert(t != NULL);
+
+ // walk through the zone and select nodes to be removed
+ knot_zone_tree_reverse_apply_postorder(t, xfrin_check_node_in_tree,
+ (void *)changes);
+
+ // Do the same with NSEC3 nodes.
+ t = knot_zone_contents_get_nsec3_nodes(contents);
+ assert(t != NULL);
+
+ knot_zone_tree_reverse_apply_postorder(t, xfrin_check_node_in_tree,
+ (void *)changes);
+
+ // remove the nodes one by one
+ return xfrin_finalize_remove_nodes(contents, changes);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_refs_in_node(knot_zone_tree_node_t *tnode, void *data)
+{
+ /*! \todo Passed data is always seto to NULL. */
+ assert(tnode != NULL);
+ //assert(data != NULL);
+
+ //xfrin_changes_t *changes = (xfrin_changes_t *)data;
+
+ // 1) Fix the reference to the node to the new one if there is some
+ knot_node_t *node = tnode->node;
+
+ knot_node_t *new_node = knot_node_get_new_node(node);
+ if (new_node != NULL) {
+ //assert(knot_node_rrset_count(new_node) > 0);
+ node = new_node;
+ tnode->node = new_node;
+ }
+
+ // 2) fix references from the node remaining in the zone
+ knot_node_update_refs(node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_gen_in_node(knot_zone_tree_node_t *tnode, void *data)
+{
+ /*! \todo Passed data is always seto to NULL. */
+ assert(tnode != NULL);
+
+ knot_node_t *node = tnode->node;
+
+ knot_node_set_old(node);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_hash_refs(ck_hash_table_item_t *item, void *data)
+{
+ if (item == NULL) {
+ return;
+ }
+
+ knot_node_t *new_node = knot_node_get_new_node(
+ (knot_node_t *)item->value);
+ if (new_node != NULL) {
+ assert(item->key_length
+ == knot_dname_size(knot_node_owner(new_node)));
+ assert(strncmp(item->key, (const char *)knot_dname_name(
+ knot_node_owner(new_node)), item->key_length) == 0);
+ item->value = (void *)new_node;
+ item->key = (const char *)knot_dname_name(
+ knot_node_owner(new_node));
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_fix_dname_refs(knot_dname_t *dname, void *data)
+{
+ UNUSED(data);
+ knot_dname_update_node(dname);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_fix_references(knot_zone_contents_t *contents)
+{
+ /*! \todo This function must not fail!! */
+
+ /*
+ * Now the contents are already switched, and we should update all
+ * references not updated yet, so that the old contents may be removed.
+ *
+ * Walk through the zone tree, so that each node will be checked
+ * and updated.
+ */
+ // fix references in normal nodes
+ knot_zone_tree_t *tree = knot_zone_contents_get_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_refs_in_node,
+ NULL);
+
+ // fix refereces in NSEC3 nodes
+ tree = knot_zone_contents_get_nsec3_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_refs_in_node,
+ NULL);
+
+ // fix references in hash table
+ ck_hash_table_t *table = knot_zone_contents_get_hash_table(contents);
+ ck_apply(table, xfrin_fix_hash_refs, NULL);
+
+ // fix references dname table
+ int ret = knot_zone_contents_dname_table_apply(contents,
+ xfrin_fix_dname_refs, NULL);
+ assert(ret == KNOT_EOK);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int xfrin_fix_generation(knot_zone_contents_t *contents)
+{
+ assert(contents != NULL);
+
+ knot_zone_tree_t *tree = knot_zone_contents_get_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_gen_in_node,
+ NULL);
+
+ tree = knot_zone_contents_get_nsec3_nodes(contents);
+ knot_zone_tree_forward_apply_inorder(tree, xfrin_fix_gen_in_node,
+ NULL);
+
+ return KNOT_EOK;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void xfrin_cleanup_update(xfrin_changes_t *changes)
+{
+ // free old nodes but do not destroy their RRSets
+ // remove owners also, because of reference counting
+ for (int i = 0; i < changes->old_nodes_count; ++i) {
+ dbg_xfrin_detail("Deleting old node: %p\n", changes->old_nodes[i]);
+ knot_node_dump(changes->old_nodes[i], 0);
+ knot_node_free(&changes->old_nodes[i], 1, 0);
+ }
+
+ // free old RRSets, and destroy also domain names in them
+ // because of reference counting
+
+ // check if there are not some duplicate RRSets
+// for (int i = 0; i < changes->old_rrsets_count; ++i) {
+// for (int j = i + 1; j < changes->old_rrsets_count; ++j) {
+// if (changes->old_rrsets[i] == changes->old_rrsets[j]) {
+// assert(0);
+// }
+// if (changes->old_rrsets[i]->rdata != NULL
+// && changes->old_rrsets[i]->rdata
+// == changes->old_rrsets[j]->rdata) {
+// assert(0);
+// }
+// }
+// }
+
+ for (int i = 0; i < changes->old_rrsets_count; ++i) {
+// knot_rrset_deep_free(&changes->old_rrsets[i], 1, 1, 1);
+ dbg_xfrin_detail("Deleting old RRSet: %p\n", changes->old_rrsets[i]);
+ knot_rrset_dump(changes->old_rrsets[i], 0);
+ knot_rrset_free(&changes->old_rrsets[i]);
+ }
+
+ // delete old RDATA
+ for (int i = 0; i < changes->old_rdata_count; ++i) {
+ dbg_xfrin_detail("Deleting old RDATA: %p, type: %s\n",
+ changes->old_rdata[i],
+ knot_rrtype_to_string(changes->old_rdata_types[i]));
+ knot_rdata_dump(changes->old_rdata[i], changes->old_rdata_types[i], 0);
+ knot_rdata_t *rdata = changes->old_rdata[i];
+ assert(rdata != NULL);
+ do {
+ knot_rdata_t *tmp = rdata->next;
+ knot_rdata_deep_free(&rdata,
+ changes->old_rdata_types[i], 1);
+ rdata = tmp;
+ } while (rdata != NULL && rdata != changes->old_rdata[i]);
+ changes->old_rdata[i] = NULL;
+ }
+
+ // free old hash table items, but do not touch their contents
+ for (int i = 0; i < changes->old_hash_items_count; ++i) {
+ free(changes->old_hash_items[i]);
+ }
+ free(changes->old_hash_items);
+
+ // free allocated arrays of nodes and rrsets
+ free(changes->new_nodes);
+ free(changes->old_nodes);
+ free(changes->new_rrsets);
+ free(changes->old_rrsets);
+ free(changes->old_rdata);
+ free(changes->old_rdata_types);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int xfrin_apply_changesets_to_zone(knot_zone_t *zone,
+ knot_changesets_t *chsets)
+{
+ if (zone == NULL || chsets == NULL || chsets->count == 0) {
+ return KNOT_EBADARG;
+ }
+
+ knot_zone_contents_t *old_contents = knot_zone_get_contents(zone);
+ if (!old_contents) {
+ return KNOT_EBADARG;
+ }
+
+// dbg_xfrin("\nOLD ZONE CONTENTS:\n\n");
+// knot_zone_contents_dump(old_contents, 1);
+
+ /*
+ * Ensure that the zone generation is set to 0.
+ */
+ if (!knot_zone_contents_gen_is_old(old_contents)) {
+ // this would mean that a previous update was not completed
+ // abort
+ dbg_zone("Trying to apply changesets to zone that is "
+ "being updated. Aborting.\n");
+ return KNOT_EAGAIN;
+ }
+
+ /*
+ * Create a shallow copy of the zone, so that the structures may be
+ * updated.
+ *
+ * This will create new zone contents structures (normal nodes' tree,
+ * NSEC3 tree, hash table, domain name table), but fill them with the
+ * data from the old contents.
+ */
+ knot_zone_contents_t *contents_copy = NULL;
+
+ int ret = knot_zone_contents_shallow_copy(old_contents,
+ &contents_copy);
+ if (ret != KNOT_EOK) {
+ dbg_xfrin("Failed to create shallow copy of zone: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Now, apply one changeset after another until all are applied.
+ * Changesets may be either from IXFR or from a dynamic update.
+ * Dynamic updates use special TYPE and CLASS values to distinguish
+ * requests, such as "remove all RRSets from a node", "remove all RRs
+ * with the specified type from a node", etc.
+ *
+ * When updating anything within some node (removing RR, adding RR),
+ * the node structure is copied, but the RRSets within are not.
+ *
+ * 1) When removing RRs from node, The affected RRSet is copied. This
+ * it also a 'shallow copy', i.e. the RDATA remain the exact same.
+ * The specified RRs (i.e. RDATA) are then removed from the copied
+ * RRSet.
+ * 2) When adding RRs to node, there are two cases:
+ * a) If there is a RRSet that should contain these RRs
+ * this RRSet is copied (shallow copy) and the RRs are added to
+ * it (rrset_merge()).
+ * b) If there is not such a RRSet, the whole RRSet from the
+ * changeset is added to the new node (thus this RRSet must not
+ * be deleted afterwards).
+ *
+ * A special case are RRSIG RRs. These functions assume that they
+ * are grouped together in knot_rrset_t structures according to
+ * their header (owner, type, class) AND their 'type covered', i.e.
+ * there may be more RRSIG RRSets in one changeset (while there
+ * should not be more RRSets of any other type).
+ * 3) When removing RRSIG RRs from node, the appropriate RRSet holding
+ * them must be found (according to the 'type covered' field). This
+ * RRSet is then copied (shallow copy), Its RRSIGs are also copied
+ * and the RRSIG RRs are added to the RRSIG copy.
+ * 4) When adding RRSIG RRs to node, the same process is done - the
+ * proper RRSet holding them is found, copied, its RRSIGs are
+ * copied (if there are some) and the RRs are added to the copy.
+ *
+ * When a node is copied, reference to the copy is stored within the
+ * old node (node_t.old_node). This is important, because when the
+ * zone contents are switched to the new ones, references from old nodes
+ * that should point to new nodes are not yet set (it would influence
+ * replying from the old zone contents). While all these references
+ * (such as node_t.prev, node_t.next, node_t.parent, etc.) are properly
+ * modified, the search functions use old or new nodes accordingly
+ * (old nodes while old contents are used, new nodes when new contents
+ * are used). The 'check_version' parameter turns on this behaviour in
+ * search functions.
+ *
+ * In case of error, we must remove all data created by the update, i.e.
+ * - new nodes,
+ * - new RRSets,
+ * and remove the references to the new nodes from old nodes.
+ *
+ * In case of success, the RRSet structures from the changeset
+ * structures must not be deleted, as they are either already used by
+ * the server (stored within the new zone contents) or deleted when
+ * cleaning up the temporary 'changes' structure.
+ */
+ xfrin_changes_t changes;
+ memset(&changes, 0, sizeof(xfrin_changes_t));
+
+ for (int i = 0; i < chsets->count; ++i) {
+ if ((ret = xfrin_apply_changeset(contents_copy, &changes,
+ &chsets->sets[i])) != KNOT_EOK) {
+ xfrin_rollback_update(contents_copy, &changes);
+ dbg_xfrin("Failed to apply changesets to zone: "
+ "%s\n", knot_strerror(ret));
+ return ret;
+ }
+ }
+
+ /*
+ * When all changesets are applied, set generation 1 to the copy of
+ * the zone so that new nodes are used instead of old ones.
+ */
+// knot_zone_contents_switch_generation(contents_copy);
+ //contents_copy->generation = 1;
+ knot_zone_contents_set_gen_new(contents_copy);
+
+ /*
+ * Finalize the zone contents.
+ */
+ ret = xfrin_finalize_contents(contents_copy, &changes);
+ if (ret != KNOT_EOK) {
+ xfrin_rollback_update(contents_copy, &changes);
+ dbg_xfrin("Failed to finalize new zone contents: %s\n",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Switch the zone contents
+ */
+ knot_zone_contents_t *old =
+ knot_zone_switch_contents(zone, contents_copy);
+ assert(old == old_contents);
+
+ /*
+ * From now on, the new contents of the zone are being used.
+ * References to nodes may be updated in the meantime. However, we must
+ * traverse the zone and fix all references that were not.
+ */
+ /*! \todo This operation must not fail!!! .*/
+ ret = xfrin_fix_references(contents_copy);
+ assert(ret == KNOT_EOK);
+
+ // set generation to finished
+ knot_zone_contents_set_gen_new_finished(contents_copy);
+
+ // set generation of all nodes to the old one
+ // now it is safe (no old nodes should be referenced)
+ ret = xfrin_fix_generation(contents_copy);
+ assert(ret == KNOT_EOK);
+
+ /*
+ * Now we may also set the generation back to 0 so that another
+ * update is possible.
+ */
+ knot_zone_contents_set_gen_old(contents_copy);
+
+ /*
+ * Wait until all readers finish reading
+ */
+ synchronize_rcu();
+
+ /*
+ * Delete all old and unused data.
+ */
+ xfrin_zone_contents_free(&old_contents);
+ xfrin_cleanup_update(&changes);
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
new file mode 100644
index 0000000..8a7c64b
--- /dev/null
+++ b/src/libknot/updates/xfr-in.h
@@ -0,0 +1,184 @@
+/*!
+ * \file xfr-in.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief XFR client API.
+ *
+ * \addtogroup query_processing
+ * @{
+ */
+/* 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_XFR_IN_H_
+#define _KNOT_XFR_IN_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include "dname.h"
+#include "zone/zone.h"
+#include "packet/packet.h"
+#include "nameserver/name-server.h"
+#include "updates/changesets.h"
+
+/*----------------------------------------------------------------------------*/
+
+typedef struct xfrin_orphan_rrsig {
+ knot_rrset_t *rrsig;
+ struct xfrin_orphan_rrsig *next;
+} xfrin_orphan_rrsig_t;
+
+typedef struct xfrin_constructed_zone {
+ knot_zone_contents_t *contents;
+ xfrin_orphan_rrsig_t *rrsigs;
+} xfrin_constructed_zone_t;
+
+typedef enum xfrin_transfer_result {
+ XFRIN_RES_COMPLETE = 1,
+ XFRIN_RES_SOA_ONLY = 2,
+ XFRIN_RES_FALLBACK = 3
+} xfrin_transfer_result_t;
+
+/*----------------------------------------------------------------------------*/
+
+/*!
+ * \brief Creates normal query for the given zone name and the SOA type.
+ *
+ * \param owner Zone owner.
+ * \param buffer Buffer to fill the message in.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ESPACE
+ * \retval KNOT_ERROR
+ */
+int xfrin_create_soa_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size);
+
+/*!
+ * \brief Checks if a zone transfer is required by comparing the zone's SOA with
+ * the one received from master server.
+ *
+ * \param zone Zone to check.
+ * \param soa_response Response to SOA query received from master server.
+ *
+ * \retval < 0 if an error occured.
+ * \retval 1 if the transfer is needed.
+ * \retval 0 if the transfer is not needed.
+ */
+int xfrin_transfer_needed(const knot_zone_contents_t *zone,
+ knot_packet_t *soa_response);
+
+/*!
+ * \brief Creates normal query for the given zone name and the AXFR type.
+ *
+ * \param owner Zone owner.
+ * \param xfr Data structure holding important data for the query, namely
+ * pointer to the buffer for wireformat and TSIG data.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ * \param use_tsig If TSIG should be used.
+ *
+ * \todo Parameter use_tsig probably not needed.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ESPACE
+ * \retval KNOT_ERROR
+ */
+int xfrin_create_axfr_query(knot_dname_t *owner, knot_ns_xfr_t *xfr,
+ size_t *size, int use_tsig);
+
+/*!
+ * \brief Creates normal query for the given zone name and the IXFR type.
+ *
+ * \param zone Zone contents.
+ * \param buffer Buffer to fill the message in.
+ * \param size In: available space in the buffer. Out: actual size of the
+ * message in bytes.
+ * \param use_tsig If TSIG should be used.
+ *
+ * \todo Parameter use_tsig probably not needed.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ESPACE
+ * \retval KNOT_ERROR
+ */
+int xfrin_create_ixfr_query(const knot_zone_contents_t *zone,
+ knot_ns_xfr_t *xfr, size_t *size, int use_tsig);
+
+/*!
+ * \brief Processes the newly created transferred zone.
+ *
+ * \param nameserver Name server to update.
+ * \param zone Zone build from transfer.
+ *
+ * \retval KNOT_ENOTSUP
+ */
+int xfrin_zone_transferred(knot_nameserver_t *nameserver,
+ knot_zone_contents_t *zone);
+
+/*!
+ * \brief Processes one incoming packet of AXFR transfer by updating the given
+ * zone.
+ *
+ * \param pkt Incoming packet in wire format.
+ * \param size Size of the packet in bytes.
+ * \param zone Zone being built. If there is no such zone (i.e. this is the
+ * first packet, \a *zone may be set to NULL, in which case a new
+ * zone structure is created).
+ *
+ * \retval KNOT_EOK
+ *
+ * \todo Refactor!!!
+ */
+int xfrin_process_axfr_packet(/*const uint8_t *pkt, size_t size,
+ xfrin_constructed_zone_t **zone*/
+ knot_ns_xfr_t *xfr);
+
+/*!
+ * \brief Destroys the whole changesets structure.
+ *
+ * Frees all RRSets present in the changesets and all their data. Also frees
+ * the changesets structure and sets the parameter to NULL.
+ *
+ * \param changesets Changesets to destroy.
+ */
+void xfrin_free_changesets(knot_changesets_t **changesets);
+
+/*!
+ * \brief Parses IXFR reply packet and fills in the changesets structure.
+ *
+ * \param pkt Packet containing the IXFR reply in wire format.
+ * \param size Size of the packet in bytes.
+ * \param changesets Changesets to be filled in.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ * \retval KNOT_EMALF
+ * \retval KNOT_ENOMEM
+ */
+int xfrin_process_ixfr_packet(knot_ns_xfr_t *xfr/*const uint8_t *pkt, size_t size,
+ knot_changesets_t **changesets*/);
+
+int xfrin_apply_changesets_to_zone(knot_zone_t *zone,
+ knot_changesets_t *chsets);
+
+#endif /* _KNOTXFR_IN_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/debug.c b/src/libknot/util/debug.c
new file mode 100644
index 0000000..0ca67c9
--- /dev/null
+++ b/src/libknot/util/debug.c
@@ -0,0 +1,233 @@
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "util/utils.h"
+#include "util/debug.h"
+#include "libknot.h"
+#include "common/print.h"
+
+void knot_rdata_dump(knot_rdata_t *rdata, uint32_t type, char loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG) || defined(KNOT_RDATA_DEBUG)
+ fprintf(stderr, " ------- RDATA -------\n");
+ if (rdata == NULL) {
+ fprintf(stderr, " There are no rdata in this RRset!\n");
+ fprintf(stderr, " ------- RDATA -------\n");
+ return;
+ }
+ knot_rrtype_descriptor_t *desc = knot_rrtype_descriptor_by_type(type);
+ assert(desc != NULL);
+ char *name;
+
+ for (int i = 0; i < rdata->count; i++) {
+ if (rdata->items[i].raw_data == NULL) {
+ continue;
+ }
+ 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 ) {
+ assert(rdata->items[i].dname != NULL);
+ name = knot_dname_to_str(rdata->items[i].dname);
+ fprintf(stderr, " DNAME: %d: %s\n",
+ i, name);
+ free(name);
+ if (loaded_zone) {
+ if (rdata->items[i].dname->node) {
+ name =
+ knot_dname_to_str(rdata->items[i].dname->node->owner);
+ fprintf(stderr, " Has node owner: %s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, " No node set\n");
+ }
+ }
+ fprintf(stderr, " labels: ");
+ hex_print((char *)rdata->items[i].dname->labels,
+ rdata->items[i].dname->label_count);
+
+ } else {
+ assert(rdata->items[i].raw_data != NULL);
+ fprintf(stderr, " %d: raw_data: length: %d\n", i,
+ *(rdata->items[i].raw_data));
+ fprintf(stderr, " ");
+ hex_print(((char *)(
+ rdata->items[i].raw_data + 1)),
+ rdata->items[i].raw_data[0]);
+ }
+ }
+ fprintf(stderr, " ------- RDATA -------\n");
+#endif
+}
+
+void knot_rrset_dump(const knot_rrset_t *rrset, char loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG) || defined(KNOT_RRSET_DEBUG)
+ fprintf(stderr, " ------- RRSET -------\n");
+ fprintf(stderr, " %p\n", rrset);
+ if (!rrset) {
+ return;
+ }
+ char *name = knot_dname_to_str(rrset->owner);
+ fprintf(stderr, " owner: %s\n", name);
+ free(name);
+ fprintf(stderr, " type: %s\n", knot_rrtype_to_string(rrset->type));
+ fprintf(stderr, " class: %d\n", rrset->rclass);
+ fprintf(stderr, " ttl: %d\n", rrset->ttl);
+
+ fprintf(stderr, " RRSIGs:\n");
+ if (rrset->rrsigs != NULL) {
+ knot_rrset_dump(rrset->rrsigs, loaded_zone);
+ } else {
+ fprintf(stderr, " none\n");
+ }
+
+ if (rrset->rdata == NULL) {
+ fprintf(stderr, " NO RDATA!\n");
+ fprintf(stderr, " ------- RRSET -------\n");
+ return;
+ }
+
+ fprintf(stderr, " rdata count: %d\n", rrset->rdata->count);
+ knot_rdata_t *tmp = rrset->rdata;
+
+ while (tmp->next != rrset->rdata && tmp->next != NULL) {
+ knot_rdata_dump(tmp, rrset->type, loaded_zone);
+ tmp = tmp->next;
+ }
+
+ knot_rdata_dump(tmp, rrset->type, loaded_zone);
+
+ fprintf(stderr, " ------- RRSET -------\n");
+#endif
+}
+
+void knot_node_dump(knot_node_t *node, void *loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG) || defined(KNOT_NODE_DEBUG)
+ //char loaded_zone = *((char*) data);
+ char *name;
+
+ fprintf(stderr, "------- NODE --------\n");
+ name = knot_dname_to_str(node->owner);
+ fprintf(stderr, "owner: %s\n", name);
+ free(name);
+ fprintf(stderr, "labels: ");
+ hex_print((char *)node->owner->labels, node->owner->label_count);
+ fprintf(stderr, "node: %p\n", node);
+ fprintf(stderr, "node (in node's owner): %p\n", node->owner->node);
+ if (loaded_zone && node->prev != NULL) {
+ name = knot_dname_to_str(node->prev->owner);
+ fprintf(stderr, "previous node: %s\n", name);
+ free(name);
+ }
+
+ if (knot_node_is_deleg_point(node)) {
+ fprintf(stderr, "delegation point\n");
+ }
+
+ if (knot_node_is_non_auth(node)) {
+ fprintf(stderr, "non-authoritative node\n");
+ }
+
+ if (node->parent != NULL) {
+ /*! \todo This causes segfault when parent was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->parent->owner);
+ fprintf(stderr, "parent: %s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "no parent\n");
+ }
+
+ if (node->prev != NULL) {
+ fprintf(stderr, "previous node: %p\n", node->prev);
+ /*! \todo This causes segfault when prev was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->prev->owner);
+ fprintf(stderr, "previous node: %s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "previous node: none\n");
+ }
+
+ knot_rrset_t **rrsets = knot_node_get_rrsets(node);
+
+ fprintf(stderr, "Wildcard child: ");
+
+ if (node->wildcard_child != NULL) {
+ /*! \todo This causes segfault when wildcard child was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->wildcard_child->owner);
+ fprintf(stderr, "%s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "none\n");
+ }
+
+ fprintf(stderr, "NSEC3 node: ");
+
+ if (node->nsec3_node != NULL) {
+ /*! \todo This causes segfault when nsec3_node was free'd,
+ * e.g. when applying changesets.
+ */
+ name = knot_dname_to_str(node->nsec3_node->owner);
+ fprintf(stderr, "%s\n", name);
+ free(name);
+ } else {
+ fprintf(stderr, "none\n");
+ }
+
+ fprintf(stderr, "RRSet count: %d\n", node->rrset_count);
+
+ for (int i = 0; i < node->rrset_count; i++) {
+ knot_rrset_dump(rrsets[i], (int) loaded_zone);
+ }
+ free(rrsets);
+ //assert(node->owner->node == node);
+ fprintf(stderr, "------- NODE --------\n");
+#endif
+}
+
+void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone)
+{
+#if defined(KNOT_ZONE_DEBUG)
+ if (!zone) {
+ fprintf(stderr, "------- STUB ZONE --------\n");
+ return;
+ }
+
+ fprintf(stderr, "------- ZONE --------\n");
+
+ knot_zone_contents_tree_apply_inorder(zone, knot_node_dump, (void *)&loaded_zone);
+
+ fprintf(stderr, "------- ZONE --------\n");
+
+ fprintf(stderr, "------- NSEC 3 tree -\n");
+
+ knot_zone_contents_nsec3_apply_inorder(zone, knot_node_dump, (void *)&loaded_zone);
+
+ fprintf(stderr, "------- NSEC 3 tree -\n");
+#endif
+}
diff --git a/src/libknot/util/debug.h b/src/libknot/util/debug.h
new file mode 100644
index 0000000..2f9f5fd
--- /dev/null
+++ b/src/libknot/util/debug.h
@@ -0,0 +1,755 @@
+/*!
+ * \file debug.h
+ *
+ * \author Jan Kadlec <jan.kadlec.@nic.cz>
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Functions for debug output of 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_DEBUG_H_
+#define _KNOT_DEBUG_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config.h" /* autoconf generated */
+
+#include "rdata.h"
+#include "rrset.h"
+#include "zone/node.h"
+#include "zone/zone.h"
+#include "util/utils.h"
+#include "common/print.h"
+
+/*
+ * Debug macros
+ */
+/*! \todo Set these during configure. */
+//#define KNOT_ZONE_DEBUG
+//#define KNOT_RESPONSE_DEBUG
+//#define KNOT_ZONEDB_DEBUG
+//#define KNOT_DNAME_DEBUG
+//#define KNOT_NODE_DEBUG
+//#define KNOT_PACKET_DEBUG
+//#define KNOT_EDNS_DEBUG
+//#define KNOT_RRSET_DEBUG
+//#define KNOT_RDATA_DEBUG
+//#define KNOT_NSEC3_DEBUG
+//#define CUCKOO_DEBUG
+//#define CUCKOO_DEBUG_HASH
+//#define KNOT_NS_DEBUG
+//#define KNOT_XFRIN_DEBUG
+//#define KNOT_DDNS_DEBUG
+//#define KNOT_TSIG_DEBUG
+
+/*!
+ * \brief Dumps RDATA of the given type.
+ *
+ * This function is empty if neither KNOT_ZONE_DEBUG nor KNOT_RDATA_DEBUG
+ * is defined.
+ *
+ * \param rdata RDATA to dump.
+ * \param type Type of the RDATA (needed to properly parse the RDATA).
+ * \param loaded_zone Set to <> 0 if the RDATA is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_rdata_dump(knot_rdata_t *rdata, uint32_t type, char loaded_zone);
+
+/*!
+ * \brief Dumps RRSet.
+ *
+ * This function is empty if neither KNOT_ZONE_DEBUG nor KNOT_RRSET_DEBUG
+ * is defined.
+ *
+ * \param rrset RRSet to dump.
+ * \param loaded_zone Set to <> 0 if the RRSet is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_rrset_dump(const knot_rrset_t *rrset, char loaded_zone);
+
+/*!
+ * \brief Dumps zone node.
+ *
+ * This function is empty if neither KNOT_ZONE_DEBUG nor KNOT_NODE_DEBUG
+ * is defined.
+ *
+ * \param node Node to dump.
+ * \param loaded_zone Set to <> 0 if the node is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_node_dump(knot_node_t *node, void *loaded_zone);
+
+/*!
+ * \brief Dumps the whole zone.
+ *
+ * This function is empty if KNOT_ZONE_DEBUG is not defined.
+ *
+ * \param zone Zone to dump.
+ * \param loaded_zone Set to <> 0 if the node is part of a zone loaded into
+ * the server. Set to 0 otherwise.
+ */
+void knot_zone_contents_dump(knot_zone_contents_t *zone, char loaded_zone);
+
+/******************************************************************************/
+
+#ifdef KNOT_NS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ns(msg...) fprintf(stderr, msg)
+#define dbg_ns_hex(data, len) hex_print((data), (len))
+#define dbg_ns_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_ns(msg...)
+#define dbg_ns_hex(data, len)
+#define dbg_ns_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ns_verb(msg...) fprintf(stderr, msg)
+#define dbg_ns_hex_verb(data, len) hex_print((data), (len))
+#define dbg_ns_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_ns_verb(msg...)
+#define dbg_ns_hex_verb(data, len)
+#define dbg_ns_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ns_detail(msg...) fprintf(stderr, msg)
+#define dbg_ns_hex_detail(data, len) hex_print((data), (len))
+#define dbg_ns_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_ns_detail(msg...)
+#define dbg_ns_hex_detail(data, len)
+#define dbg_ns_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ns(msg...)
+#define dbg_ns_hex(data, len)
+#define dbg_ns_exec(cmds)
+#define dbg_ns_verb(msg...)
+#define dbg_ns_hex_verb(data, len)
+#define dbg_ns_exec_verb(cmds)
+#define dbg_ns_detail(msg...)
+#define dbg_ns_hex_detail(data, len)
+#define dbg_ns_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_DNAME_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_dname(msg...) fprintf(stderr, msg)
+#define dbg_dname_hex(data, len) hex_print((data), (len))
+#define dbg_dname_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_dname(msg...)
+#define dbg_dname_hex(data, len)
+#define dbg_dname_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_dname_verb(msg...) fprintf(stderr, msg)
+#define dbg_dname_hex_verb(data, len) hex_print((data), (len))
+#define dbg_dname_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_dname_verb(msg...)
+#define dbg_dname_hex_verb(data, len)
+#define dbg_dname_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_dname_detail(msg...) fprintf(stderr, msg)
+#define dbg_dname_hex_detail(data, len) hex_print((data), (len))
+#define dbg_dname_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_dname_detail(msg...)
+#define dbg_dname_hex_detail(data, len)
+#define dbg_dname_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_dname(msg...)
+#define dbg_dname_hex(data, len)
+#define dbg_dname_exec(cmds)
+#define dbg_dname_verb(msg...)
+#define dbg_dname_hex_verb(data, len)
+#define dbg_dname_exec_verb(cmds)
+#define dbg_dname_detail(msg...)
+#define dbg_dname_hex_detail(data, len)
+#define dbg_dname_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_NODE_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_node(msg...) fprintf(stderr, msg)
+#define dbg_node_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_node(msg...)
+#define dbg_node_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_node_verb(msg...) fprintf(stderr, msg)
+#define dbg_node_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_node_verb(msg...)
+#define dbg_node_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_node_detail(msg...) fprintf(stderr, msg)
+#define dbg_node_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_node_detail(msg...)
+#define dbg_node_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_node(msg...)
+#define dbg_node_hex(data, len)
+#define dbg_node_verb(msg...)
+#define dbg_node_hex_verb(data, len)
+#define dbg_node_detail(msg...)
+#define dbg_node_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_ZONE_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zone(msg...) fprintf(stderr, msg)
+#define dbg_zone_hex(data, len) hex_print((data), (len))
+#define dbg_zone_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_zone(msg...)
+#define dbg_zone_hex(data, len)
+#define dbg_zone_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zone_verb(msg...) fprintf(stderr, msg)
+#define dbg_zone_hex_verb(data, len) hex_print((data), (len))
+#define dbg_zone_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_zone_verb(msg...)
+#define dbg_zone_hex_verb(data, len)
+#define dbg_zone_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zone_detail(msg...) fprintf(stderr, msg)
+#define dbg_zone_hex_detail(data, len) hex_print((data), (len))
+#define dbg_zone_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_zone_detail(msg...)
+#define dbg_zone_hex_detail(data, len)
+#define dbg_zone_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zone(msg...)
+#define dbg_zone_hex(data, len)
+#define dbg_zone_exec(cmds)
+#define dbg_zone_verb(msg...)
+#define dbg_zone_hex_verb(data, len)
+#define dbg_zone_exec_verb(cmds)
+#define dbg_zone_detail(msg...)
+#define dbg_zone_hex_detail(data, len)
+#define dbg_zone_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_ZONEDB_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_zonedb(msg...) fprintf(stderr, msg)
+#define dbg_zonedb_hex(data, len) hex_print((data), (len))
+#define dbg_zonedb_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_zonedb(msg...)
+#define dbg_zonedb_hex(data, len)
+#define dbg_zonedb_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_zonedb_verb(msg...) fprintf(stderr, msg)
+#define dbg_zonedb_hex_verb(data, len) hex_print((data), (len))
+#define dbg_zonedb_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_zonedb_verb(msg...)
+#define dbg_zonedb_hex_verb(data, len)
+#define dbg_zonedb_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_zonedb_detail(msg...) fprintf(stderr, msg)
+#define dbg_zonedb_hex_detail(data, len) hex_print((data), (len))
+#define dbg_zonedb_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_zonedb_detail(msg...)
+#define dbg_zonedb_hex_detail(data, len)
+#define dbg_zonedb_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_zonedb(msg...)
+#define dbg_zonedb_hex(data, len)
+#define dbg_zonedb_exec(cmds)
+#define dbg_zonedb_verb(msg...)
+#define dbg_zonedb_hex_verb(data, len)
+#define dbg_zonedb_exec_verb(cmds)
+#define dbg_zonedb_detail(msg...)
+#define dbg_zonedb_hex_detail(data, len)
+#define dbg_zonedb_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_RESPONSE_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_response(msg...) fprintf(stderr, msg)
+#define dbg_response_hex(data, len) hex_print((data), (len))
+#define dbg_response_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_response(msg...)
+#define dbg_response_hex(data, len)
+#define dbg_response_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_response_verb(msg...) fprintf(stderr, msg)
+#define dbg_response_hex_verb(data, len) hex_print((data), (len))
+#define dbg_response_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_response_verb(msg...)
+#define dbg_response_hex_verb(data, len)
+#define dbg_response_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_response_detail(msg...) fprintf(stderr, msg)
+#define dbg_response_hex_detail(data, len) hex_print((data), (len))
+#define dbg_response_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_response_detail(msg...)
+#define dbg_response_hex_detail(data, len)
+#define dbg_response_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_response(msg...)
+#define dbg_response_hex(data, len)
+#define dbg_response_exec(cmds)
+#define dbg_response_verb(msg...)
+#define dbg_response_hex_verb(data, len)
+#define dbg_response_exec_verb(cmds)
+#define dbg_response_detail(msg...)
+#define dbg_response_hex_detail(data, len)
+#define dbg_response_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_PACKET_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_packet(msg...) fprintf(stderr, msg)
+#define dbg_packet_hex(data, len) hex_print((data), (len))
+#define dbg_packet_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_packet(msg...)
+#define dbg_packet_hex(data, len)
+#define dbg_packet_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_packet_verb(msg...) fprintf(stderr, msg)
+#define dbg_packet_hex_verb(data, len) hex_print((data), (len))
+#define dbg_packet_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_packet_verb(msg...)
+#define dbg_packet_hex_verb(data, len)
+#define dbg_packet_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_packet_detail(msg...) fprintf(stderr, msg)
+#define dbg_packet_hex_detail(data, len) hex_print((data), (len))
+#define dbg_packet_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_packet_detail(msg...)
+#define dbg_packet_hex_detail(data, len)
+#define dbg_packet_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_packet(msg...)
+#define dbg_packet_hex(data, len)
+#define dbg_packet_exec(cmds)
+#define dbg_packet_verb(msg...)
+#define dbg_packet_hex_verb(data, len)
+#define dbg_packet_exec_verb(cmds)
+#define dbg_packet_detail(msg...)
+#define dbg_packet_hex_detail(data, len)
+#define dbg_packet_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_EDNS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_edns(msg...) fprintf(stderr, msg)
+#define dbg_edns_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_edns(msg...)
+#define dbg_edns_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_edns_verb(msg...) fprintf(stderr, msg)
+#define dbg_edns_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_edns_verb(msg...)
+#define dbg_edns_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_edns_detail(msg...) fprintf(stderr, msg)
+#define dbg_edns_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_edns_detail(msg...)
+#define dbg_edns_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_edns(msg...)
+#define dbg_edns_hex(data, len)
+#define dbg_edns_verb(msg...)
+#define dbg_edns_hex_verb(data, len)
+#define dbg_edns_detail(msg...)
+#define dbg_edns_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_NSEC3_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_nsec3(msg...) fprintf(stderr, msg)
+#define dbg_nsec3_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_nsec3(msg...)
+#define dbg_nsec3_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_nsec3_verb(msg...) fprintf(stderr, msg)
+#define dbg_nsec3_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_nsec3_verb(msg...)
+#define dbg_nsec3_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_nsec3_detail(msg...) fprintf(stderr, msg)
+#define dbg_nsec3_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_nsec3_detail(msg...)
+#define dbg_nsec3_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_nsec3(msg...)
+#define dbg_nsec3_hex(data, len)
+#define dbg_nsec3_verb(msg...)
+#define dbg_nsec3_hex_verb(data, len)
+#define dbg_nsec3_detail(msg...)
+#define dbg_nsec3_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef CUCKOO_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ck(msg...) fprintf(stderr, msg)
+#define dbg_ck_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_ck(msg...)
+#define dbg_ck_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ck_verb(msg...) fprintf(stderr, msg)
+#define dbg_ck_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_verb(msg...)
+#define dbg_ck_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ck_detail(msg...) fprintf(stderr, msg)
+#define dbg_ck_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_detail(msg...)
+#define dbg_ck_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ck(msg...)
+#define dbg_ck_hex(data, len)
+#define dbg_ck_verb(msg...)
+#define dbg_ck_hex_verb(data, len)
+#define dbg_ck_detail(msg...)
+#define dbg_ck_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef CUCKOO_DEBUG_HASH
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ck_hash(msg...) fprintf(stderr, msg)
+#define dbg_ck_rehash(msg...) fprintf(stderr, msg)
+#define dbg_ck_hash_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_hash(msg...)
+#define dbg_ck_rehash(msg...)
+#define dbg_ck_hash_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ck_hash_verb(msg...) fprintf(stderr, msg)
+#define dbg_ck_hash_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_hash_verb(msg...)
+#define dbg_ck_hash_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ck_hash_detail(msg...) fprintf(stderr, msg)
+#define dbg_ck_hash_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_ck_hash_detail(msg...)
+#define dbg_ck_hash_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ck_hash(msg...)
+#define dbg_ck_rehash(msg...)
+#define dbg_ck_hash_hex(data, len)
+#define dbg_ck_hash_verb(msg...)
+#define dbg_ck_hash_hex_verb(data, len)
+#define dbg_ck_hash_detail(msg...)
+#define dbg_ck_hash_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_XFRIN_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_xfrin(msg...) fprintf(stderr, msg)
+#define dbg_xfrin_hex(data, len) hex_print((data), (len))
+#define dbg_xfrin_exec(cmds) do { cmds } while (0)
+#else
+#define dbg_xfrin(msg...)
+#define dbg_xfrin_hex(data, len)
+#define dbg_xfrin_exec(cmds)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_xfrin_verb(msg...) fprintf(stderr, msg)
+#define dbg_xfrin_hex_verb(data, len) hex_print((data), (len))
+#define dbg_xfrin_exec_verb(cmds) do { cmds } while (0)
+#else
+#define dbg_xfrin_verb(msg...)
+#define dbg_xfrin_hex_verb(data, len)
+#define dbg_xfrin_exec_verb(cmds)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_xfrin_detail(msg...) fprintf(stderr, msg)
+#define dbg_xfrin_hex_detail(data, len) hex_print((data), (len))
+#define dbg_xfrin_exec_detail(cmds) do { cmds } while (0)
+#else
+#define dbg_xfrin_detail(msg...)
+#define dbg_xfrin_hex_detail(data, len)
+#define dbg_xfrin_exec_detail(cmds)
+#endif
+
+/* No messages. */
+#else
+#define dbg_xfrin(msg...)
+#define dbg_xfrin_hex(data, len)
+#define dbg_xfrin_exec(cmds)
+#define dbg_xfrin_verb(msg...)
+#define dbg_xfrin_hex_verb(data, len)
+#define dbg_xfrin_exec_verb(cmds)
+#define dbg_xfrin_detail(msg...)
+#define dbg_xfrin_hex_detail(data, len)
+#define dbg_xfrin_exec_detail(cmds)
+#endif
+
+/******************************************************************************/
+
+#ifdef KNOT_DDNS_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_ddns(msg...) fprintf(stderr, msg)
+#define dbg_ddns_hex(data, len) hex_print((data), (len))
+#else
+#define dbg_ddns(msg...)
+#define dbg_ddns_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_ddns_verb(msg...) fprintf(stderr, msg)
+#define dbg_ddns_hex_verb(data, len) hex_print((data), (len))
+#else
+#define dbg_ddns_verb(msg...)
+#define dbg_ddns_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_ddns_detail(msg...) fprintf(stderr, msg)
+#define dbg_ddns_hex_detail(data, len) hex_print((data), (len))
+#else
+#define dbg_ddns_detail(msg...)
+#define dbg_ddns_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_ddns(msg...)
+#define dbg_ddns_hex(data, len)
+#define dbg_ddns_verb(msg...)
+#define dbg_ddns_hex_verb(data, len)
+#define dbg_ddns_detail(msg...)
+#define dbg_ddns_hex_detail(data, len)
+#endif
+
+#ifdef KNOT_TSIG_DEBUG
+
+/* Brief messages. */
+#ifdef DEBUG_ENABLE_BRIEF
+#define dbg_tsig(msg...) fprintf(stderr, msg)
+#define dbg_tsig_hex(data, len) hex_print((const char*)(data), (len))
+#else
+#define dbg_tsig(msg...)
+#define dbg_tsig_hex(data, len)
+#endif
+
+/* Verbose messages. */
+#ifdef DEBUG_ENABLE_VERBOSE
+#define dbg_tsig_verb(msg...) fprintf(stderr, msg)
+#define dbg_tsig_hex_verb(data, len) hex_print((const char*)(data), (len))
+#else
+#define dbg_tsig_verb(msg...)
+#define dbg_tsig_hex_verb(data, len)
+#endif
+
+/* Detail messages. */
+#ifdef DEBUG_ENABLE_DETAILS
+#define dbg_tsig_detail(msg...) fprintf(stderr, msg)
+#define dbg_tsig_hex_detail(data, len) hex_print((const char*)(data), (len))
+#else
+#define dbg_tsig_detail(msg...)
+#define dbg_tsig_hex_detail(data, len)
+#endif
+
+/* No messages. */
+#else
+#define dbg_tsig(msg...)
+#define dbg_tsig_hex(data, len)
+#define dbg_tsig_verb(msg...)
+#define dbg_tsig_hex_verb(data, len)
+#define dbg_tsig_detail(msg...)
+#define dbg_tsig_hex_detail(data, len)
+#endif
+
+/******************************************************************************/
+
+#endif /* _KNOT_DEBUG_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/descriptor.c b/src/libknot/util/descriptor.c
new file mode 100644
index 0000000..fa94c24
--- /dev/null
+++ b/src/libknot/util/descriptor.c
@@ -0,0 +1,501 @@
+/*!
+ * \file descriptor.c
+ *
+ * \author Modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the work by NLnet labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \note Most of the constants and functions were taken from NSD's dns.c.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "libknot.h"
+
+enum desclen { KNOT_RRTYPE_DESCRIPTORS_LENGTH = 32770 }; // used to be 101
+
+/*!
+ * \brief Table for linking RR class constants to their textual representation.
+ */
+static knot_lookup_table_t dns_rrclasses[] = {
+ { KNOT_CLASS_IN, "IN" }, /* the Internet */
+ { KNOT_CLASS_CS, "CS" }, /* the CSNET class (Obsolete) */
+ { KNOT_CLASS_CH, "CH" }, /* the CHAOS class */
+ { KNOT_CLASS_HS, "HS" }, /* Hesiod */
+ { 0, NULL }
+};
+
+/*! \brief RR type descriptors. */
+static knot_rrtype_descriptor_t
+ knot_rrtype_descriptors[KNOT_RRTYPE_DESCRIPTORS_LENGTH] = {
+ /* 0 */
+ { 0, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 1 */
+ { KNOT_RRTYPE_A, "A", 1, { KNOT_RDATA_WF_A }, { KNOT_RDATA_ZF_A }, true },
+ /* 2 */
+ { KNOT_RRTYPE_NS, "NS", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 3 */
+ { KNOT_RRTYPE_MD, "MD", 1,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 4 */
+ { KNOT_RRTYPE_MF, "MF", 1,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 5 */
+ { KNOT_RRTYPE_CNAME, "CNAME", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 6 */
+ { KNOT_RRTYPE_SOA, "SOA", 7,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME, KNOT_RDATA_WF_COMPRESSED_DNAME,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_PERIOD, KNOT_RDATA_ZF_PERIOD,
+ KNOT_RDATA_ZF_PERIOD, KNOT_RDATA_ZF_PERIOD, KNOT_RDATA_ZF_PERIOD },
+ true },
+ /* 7 */
+ { KNOT_RRTYPE_MB, "MB", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 8 */
+ { KNOT_RRTYPE_MG, "MG", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 9 */
+ { KNOT_RRTYPE_MR, "MR", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME }, { KNOT_RDATA_ZF_DNAME }, true },
+ /* 10 */
+ { KNOT_RRTYPE_NULL, NULL, 1,
+ { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 11 */
+ { KNOT_RRTYPE_WKS, "WKS", 2,
+ { KNOT_RDATA_WF_A, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_A, KNOT_RDATA_ZF_SERVICES }, true },
+ /* 12 */
+ { KNOT_RRTYPE_PTR, "PTR", 1,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME }, true },
+ /* 13 */
+ { KNOT_RRTYPE_HINFO, "HINFO", 2,
+ { KNOT_RDATA_WF_TEXT_SINGLE, KNOT_RDATA_WF_TEXT_SINGLE },
+ { KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_TEXT }, true },
+ /* 14 */
+ { KNOT_RRTYPE_MINFO, "MINFO", 2,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME,
+ KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME }, true },
+ /* 15 */
+ { KNOT_RRTYPE_MX, "MX", 2,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 16 */ /* This is obscure, but I guess there's no other way */
+ { KNOT_RRTYPE_TXT, "TXT", 1,
+ { KNOT_RDATA_WF_TEXT },
+ { KNOT_RDATA_ZF_TEXT },
+ false },
+ /* 17 */
+ { KNOT_RRTYPE_RP, "RP", 2,
+ { KNOT_RDATA_WF_COMPRESSED_DNAME,
+ KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME }, true },
+ /* 18 */
+ { KNOT_RRTYPE_AFSDB, "AFSDB", 2,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 19 */
+ { KNOT_RRTYPE_X25, "X25", 1,
+ { KNOT_RDATA_WF_TEXT_SINGLE },
+ { KNOT_RDATA_ZF_TEXT }, true },
+ /* 20 */
+ { KNOT_RRTYPE_ISDN, "ISDN", 2,
+ { KNOT_RDATA_WF_TEXT_SINGLE, KNOT_RDATA_WF_TEXT_SINGLE },
+ { KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_TEXT }, false },
+ /* 21 */
+ { KNOT_RRTYPE_RT, "RT", 2,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_COMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 22 */
+ { KNOT_RRTYPE_NSAP, "NSAP", 1,
+ { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_NSAP }, true },
+ /* 23 */
+ { 23, NULL, 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 24 */
+ { KNOT_RRTYPE_SIG, "SIG", 9,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_SHORT,KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_RRTYPE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_PERIOD,
+ KNOT_RDATA_ZF_TIME, KNOT_RDATA_ZF_TIME, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME,
+ KNOT_RDATA_ZF_BASE64 },
+ true },
+ /* 25 */
+ { KNOT_RRTYPE_KEY, "KEY", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_ALGORITHM,
+ KNOT_RDATA_ZF_BASE64 }, true },
+ /* 26 */
+ { KNOT_RRTYPE_PX, "PX", 3,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_DNAME }, true },
+ /* 27 */
+ { 27, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 28 */
+ { KNOT_RRTYPE_AAAA, "AAAA", 1,
+ { KNOT_RDATA_WF_AAAA },
+ { KNOT_RDATA_ZF_AAAA }, true },
+ /* 29 */
+ { KNOT_RRTYPE_LOC, "LOC", 1,
+ { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_LOC }, true },
+ /* 30 */
+ { KNOT_RRTYPE_NXT, "NXT", 2,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_DNAME, KNOT_RDATA_ZF_NXT }, true },
+ /* 31 */
+ { 31, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 32 */
+ { 32, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 33 */
+ { KNOT_RRTYPE_SRV, "SRV", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME },
+ true },
+ /* 34 */
+ { 34, NULL, 1, { KNOT_RDATA_WF_BINARY }, { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 35 */
+ { KNOT_RRTYPE_NAPTR, "NAPTR", 6,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_TEXT_SINGLE,
+ KNOT_RDATA_WF_TEXT_SINGLE, KNOT_RDATA_WF_TEXT_SINGLE,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_TEXT,
+ KNOT_RDATA_ZF_TEXT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 36 */
+ { KNOT_RRTYPE_KX, "KX", 2,
+ { KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_DNAME }, true },
+ /* 37 */
+ { KNOT_RRTYPE_CERT, "CERT", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_CERTIFICATE_TYPE, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_ALGORITHM,
+ KNOT_RDATA_ZF_BASE64 }, true },
+ /* 38 */
+ { KNOT_RRTYPE_A6, NULL, 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 39 */
+ { KNOT_RRTYPE_DNAME, "DNAME", 1,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME },
+ { KNOT_RDATA_ZF_DNAME }, true },
+ /* 40 */
+ { 40, NULL, 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 41 */
+ /* OPT has its parser token, but should never be in zone file... */
+ { KNOT_RRTYPE_OPT, "OPT", 1,
+ { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_UNKNOWN }, true },
+ /* 42 */
+ { KNOT_RRTYPE_APL, "APL", 1,
+ { KNOT_RDATA_WF_APL },
+ { KNOT_RDATA_ZF_APL }, false },
+ /* 43 */
+ { KNOT_RRTYPE_DS, "DS", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_ALGORITHM, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_HEX }, true },
+ /* 44 */
+ { KNOT_RRTYPE_SSHFP, "SSHFP", 3,
+ { KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_HEX },
+ true },
+ /* 45 */
+ { KNOT_RRTYPE_IPSECKEY, "IPSECKEY", 5,
+ { KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_IPSECGATEWAY,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_IPSECGATEWAY,
+ KNOT_RDATA_ZF_BASE64 }, false },
+ /* 46 */
+ { KNOT_RRTYPE_RRSIG, "RRSIG", 9,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_LONG, KNOT_RDATA_WF_LONG,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_LITERAL_DNAME,
+ KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_RRTYPE, KNOT_RDATA_ZF_ALGORITHM,
+ KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_PERIOD,
+ KNOT_RDATA_ZF_TIME, KNOT_RDATA_ZF_TIME,
+ KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_LITERAL_DNAME,
+ KNOT_RDATA_ZF_BASE64 }, true },
+ /* 47 */
+ { KNOT_RRTYPE_NSEC, "NSEC", 2,
+ { KNOT_RDATA_WF_LITERAL_DNAME, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_LITERAL_DNAME, KNOT_RDATA_ZF_NSEC },
+ true },
+ /* 48 */
+ { KNOT_RRTYPE_DNSKEY, "DNSKEY", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_BYTE,
+ KNOT_RDATA_ZF_ALGORITHM, KNOT_RDATA_ZF_BASE64 }, true },
+ /* 49 */
+ { KNOT_RRTYPE_DHCID, "DHCID", 1, { KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_BASE64 }, true },
+ /* 50 */
+ { KNOT_RRTYPE_NSEC3, "NSEC3", 6,
+ { KNOT_RDATA_WF_BYTE, /* hash type */
+ KNOT_RDATA_WF_BYTE, /* flags */
+ KNOT_RDATA_WF_SHORT, /* iterations */
+ KNOT_RDATA_WF_BINARYWITHLENGTH, /* salt */
+ KNOT_RDATA_WF_BINARYWITHLENGTH, /* next hashed name */
+ KNOT_RDATA_WF_BINARY /* type bitmap */ },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_HEX_LEN,
+ KNOT_RDATA_ZF_BASE32, KNOT_RDATA_ZF_NSEC },
+ true },
+ /* 51 */
+ { KNOT_RRTYPE_NSEC3PARAM, "NSEC3PARAM", 4,
+ { KNOT_RDATA_WF_BYTE, /* hash type */
+ KNOT_RDATA_WF_BYTE, /* flags */
+ KNOT_RDATA_WF_SHORT, /* iterations */
+ KNOT_RDATA_WF_BINARYWITHLENGTH /* salt */ },
+ { KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_BYTE,
+ KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_HEX_LEN }, true },
+ /* 52 */
+
+
+ /* In NSD they have indices between 52 and 99 filled with
+ unknown types. TODO add here if it's really needed? */
+ /* it is indeed needed, in rrtype_from_string */
+
+ /* There's a GNU extension that works like this: [first ... last] = value */
+
+ /* 99 */
+ [99] = { KNOT_RRTYPE_SPF, "SPF", 1,
+ { KNOT_RDATA_WF_TEXT },
+ { KNOT_RDATA_ZF_TEXT }, false },
+ /* TSIG pseudo RR. */
+ [250] = { KNOT_RRTYPE_TSIG, "TSIG", 7,
+ { KNOT_RDATA_WF_UNCOMPRESSED_DNAME, KNOT_RDATA_WF_UINT48,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BINARYWITHSHORT,
+ KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_SHORT,
+ KNOT_RDATA_WF_BINARYWITHSHORT },
+ /* Zoneformat not needed. */
+ {0, 0, 0, 0, 0}, true },
+ /* 32769 */
+ [32769] = { KNOT_RRTYPE_DLV, "DLV", 4,
+ { KNOT_RDATA_WF_SHORT, KNOT_RDATA_WF_BYTE,
+ KNOT_RDATA_WF_BYTE, KNOT_RDATA_WF_BINARY },
+ { KNOT_RDATA_ZF_SHORT, KNOT_RDATA_ZF_ALGORITHM, KNOT_RDATA_ZF_BYTE, KNOT_RDATA_ZF_HEX },
+ true },
+};
+
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_type(uint16_t type)
+{
+ if (type < KNOT_RRTYPE_LAST + 1) {
+ return &knot_rrtype_descriptors[type];
+ } else if (type == KNOT_RRTYPE_DLV) {
+ return &knot_rrtype_descriptors[KNOT_RRTYPE_DLV];
+ }
+ return &knot_rrtype_descriptors[0];
+}
+
+/* I see a lot of potential here to speed up zone parsing - this is O(n) *
+ * could be better */
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < KNOT_RRTYPE_DLV + 1; ++i) {
+ if (knot_rrtype_descriptors[i].name &&
+ strcasecmp(knot_rrtype_descriptors[i].name, name) == 0) {
+ return &knot_rrtype_descriptors[i];
+ }
+ }
+
+ if (knot_rrtype_descriptors[KNOT_RRTYPE_DLV].name &&
+ strcasecmp(knot_rrtype_descriptors[KNOT_RRTYPE_DLV].name,
+ name) == 0) {
+ return &knot_rrtype_descriptors[KNOT_RRTYPE_DLV];
+ }
+
+ return NULL;
+}
+
+const char *knot_rrtype_to_string(uint16_t rrtype)
+{
+ static char buf[20];
+ knot_rrtype_descriptor_t *descriptor =
+ knot_rrtype_descriptor_by_type(rrtype);
+ if (descriptor->name) {
+ return descriptor->name;
+ } else {
+ snprintf(buf, sizeof(buf), "TYPE%d", (int) rrtype);
+ return buf;
+ }
+}
+
+uint16_t knot_rrtype_from_string(const char *name)
+{
+ char *end;
+ long rrtype;
+ knot_rrtype_descriptor_t *entry;
+
+ entry = knot_rrtype_descriptor_by_name(name);
+ if (entry) {
+ return entry->type;
+ }
+
+ if (strlen(name) < 5) {
+ return 0;
+ }
+
+ if (strncasecmp(name, "TYPE", 4) != 0) {
+ return 0;
+ }
+
+ if (!isdigit((int)name[4])) {
+ return 0;
+ }
+
+ /* The rest from the string must be a number. */
+ rrtype = strtol(name + 4, &end, 10);
+ if (*end != '\0') {
+ return 0;
+ }
+ if (rrtype < 0 || rrtype > 65535L) {
+ return 0;
+ }
+
+ return (uint16_t) rrtype;
+}
+
+const char *knot_rrclass_to_string(uint16_t rrclass)
+{
+ static char buf[20];
+ knot_lookup_table_t *entry = knot_lookup_by_id(dns_rrclasses,
+ rrclass);
+ if (entry) {
+ assert(strlen(entry->name) < sizeof(buf));
+ knot_strlcpy(buf, entry->name, sizeof(buf));
+ } else {
+ snprintf(buf, sizeof(buf), "CLASS%d", (int) rrclass);
+ }
+ return buf;
+}
+
+uint16_t knot_rrclass_from_string(const char *name)
+{
+ char *end;
+ long rrclass;
+ knot_lookup_table_t *entry;
+
+ entry = knot_lookup_by_name(dns_rrclasses, name);
+ if (entry) {
+ return (uint16_t) entry->id;
+ }
+
+ if (strlen(name) < 6) {
+ return 0;
+ }
+
+ if (strncasecmp(name, "CLASS", 5) != 0) {
+ return 0;
+ }
+
+ if (!isdigit((int)name[5])) {
+ return 0;
+ }
+
+ // The rest from the string must be a number.
+ rrclass = strtol(name + 5, &end, 10);
+ if (*end != '\0') {
+ return 0;
+ }
+ if (rrclass < 0 || rrclass > 65535L) {
+ return 0;
+ }
+
+ return (uint16_t) rrclass;
+}
+
+size_t knot_wireformat_size(unsigned int wire_type)
+{
+ switch(wire_type) {
+ case KNOT_RDATA_WF_BYTE:
+ return 1;
+ break;
+ case KNOT_RDATA_WF_SHORT:
+ return 2;
+ break;
+ case KNOT_RDATA_WF_LONG:
+ return 4;
+ break;
+ case KNOT_RDATA_WF_A:
+ return 4;
+ break;
+ default: /* unknown size */
+ return 0;
+ break;
+ } /* switch */
+}
+
+int knot_rrtype_is_metatype(uint16_t type)
+{
+ /*! \todo Check if there are some other metatypes. */
+ return (type == KNOT_RRTYPE_ANY
+ || type == KNOT_RRTYPE_AXFR
+ || type == KNOT_RRTYPE_IXFR
+ || type == KNOT_RRTYPE_MAILA
+ || type == KNOT_RRTYPE_MAILB
+ || type == KNOT_RRTYPE_OPT);
+}
+
diff --git a/src/libknot/util/descriptor.h b/src/libknot/util/descriptor.h
new file mode 100644
index 0000000..10d3d20
--- /dev/null
+++ b/src/libknot/util/descriptor.h
@@ -0,0 +1,332 @@
+/*!
+ * \file descriptor.h
+ *
+ * \author Modifications by Jan Kadlec <jan.kadlec@nic.cz>,
+ * most of the work by NLnet Labs.
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * \note Most of the constants and functions were taken from NSD's dns.h.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+/*
+ * Copyright (c) 2001-2011, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _KNOT_DESCRIPTOR_H_
+#define _KNOT_DESCRIPTOR_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+enum knot_mxrdtln {
+ /*! \brief Maximum items in RDATA wireformat. */
+ KNOT_MAX_RDATA_ITEMS = 64,
+ /*! \brief Maximum size of one item in RDATA wireformat. */
+ KNOT_MAX_RDATA_ITEM_SIZE = 65534,
+ /*! \brief Maximum wire size of one RDATA. */
+ KNOT_MAX_RDATA_WIRE_SIZE =
+ KNOT_MAX_RDATA_ITEMS * KNOT_MAX_RDATA_ITEM_SIZE
+};
+
+typedef enum knot_mxrdtln knot_mxrdtln_t;
+//#define MAXRDATALEN 64
+
+/* 64 is in NSD. Seems a little too much, but I'd say it's not a real issue. */
+
+/*!
+ * \brief Resource record class codes.
+ */
+enum knot_rr_class {
+ KNOT_CLASS_IN = 1,
+ KNOT_CLASS_CS,
+ KNOT_CLASS_CH,
+ KNOT_CLASS_HS,
+ KNOT_CLASS_NONE = 254,
+ KNOT_CLASS_ANY = 255
+};
+
+typedef enum knot_rr_class knot_rr_class_t;
+
+/*!
+ * \brief Resource record type constants.
+ * \todo Not all indices can be used for indexing.
+ */
+enum knot_rr_type {
+ KNOT_RRTYPE_UNKNOWN, /*!< 0 - an unknown type */
+ KNOT_RRTYPE_A, /*!< 1 - a host address */
+ KNOT_RRTYPE_NS, /*!< 2 - an authoritative name server */
+ KNOT_RRTYPE_MD, /*!< 3 - a mail destination (Obsolete - use MX) */
+ KNOT_RRTYPE_MF, /*!< 4 - a mail forwarder (Obsolete - use MX) */
+ KNOT_RRTYPE_CNAME, /*!< 5 - the canonical name for an alias */
+ KNOT_RRTYPE_SOA, /*!< 6 - marks the start of a zone of authority */
+ KNOT_RRTYPE_MB, /*!< 7 - a mailbox domain name (EXPERIMENTAL) */
+ KNOT_RRTYPE_MG, /*!< 8 - a mail group member (EXPERIMENTAL) */
+ KNOT_RRTYPE_MR, /*!< 9 - a mail rename domain name (EXPERIMENTAL) */
+ KNOT_RRTYPE_NULL, /*!< 10 - a null RR (EXPERIMENTAL) */
+ KNOT_RRTYPE_WKS, /*!< 11 - a well known service description */
+ KNOT_RRTYPE_PTR, /*!< 12 - a domain name pointer */
+ KNOT_RRTYPE_HINFO, /*!< 13 - host information */
+ KNOT_RRTYPE_MINFO, /*!< 14 - mailbox or mail list information */
+ KNOT_RRTYPE_MX, /*!< 15 - mail exchange */
+ KNOT_RRTYPE_TXT, /*!< 16 - text strings */
+ KNOT_RRTYPE_RP, /*!< 17 - RFC1183 */
+ KNOT_RRTYPE_AFSDB, /*!< 18 - RFC1183 */
+ KNOT_RRTYPE_X25, /*!< 19 - RFC1183 */
+ KNOT_RRTYPE_ISDN, /*!< 20 - RFC1183 */
+ KNOT_RRTYPE_RT, /*!< 21 - RFC1183 */
+ KNOT_RRTYPE_NSAP, /*!< 22 - RFC1706 */
+
+ KNOT_RRTYPE_SIG = 24, /*!< 24 - 2535typecode */
+ KNOT_RRTYPE_KEY, /*!< 25 - 2535typecode */
+ KNOT_RRTYPE_PX, /*!< 26 - RFC2163 */
+
+ KNOT_RRTYPE_AAAA = 28, /*!< 28 - ipv6 address */
+ KNOT_RRTYPE_LOC, /*!< 29 - LOC record RFC1876 */
+ KNOT_RRTYPE_NXT, /*!< 30 - 2535typecode */
+
+ KNOT_RRTYPE_SRV = 33, /*!< 33 - SRV record RFC2782 */
+
+ KNOT_RRTYPE_NAPTR = 35, /*!< 35 - RFC2915 */
+ KNOT_RRTYPE_KX, /*!< 36 - RFC2230 Key Exchange Delegation Record */
+ KNOT_RRTYPE_CERT, /*!< 37 - RFC2538 */
+ KNOT_RRTYPE_A6, /*!< 38 - RFC2874 */
+ KNOT_RRTYPE_DNAME, /*!< 39 - RFC2672 */
+
+ KNOT_RRTYPE_OPT = 41, /*!< 41 - Pseudo OPT record... */
+ KNOT_RRTYPE_APL, /*!< 42 - RFC3123 */
+ KNOT_RRTYPE_DS, /*!< 43 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_SSHFP, /*!< 44 - SSH Key Fingerprint */
+ KNOT_RRTYPE_IPSECKEY, /*!< 45 - public key for ipsec use. RFC 4025 */
+ KNOT_RRTYPE_RRSIG, /*!< 46 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_NSEC, /*!< 47 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_DNSKEY, /*!< 48 - RFC 4033, 4034, and 4035 */
+ KNOT_RRTYPE_DHCID, /*!< 49 - RFC4701 DHCP information */
+ /*!
+ * \brief 50 - NSEC3, secure denial, prevents zonewalking
+ */
+ KNOT_RRTYPE_NSEC3,
+ /*!
+ * \brief 51 - NSEC3PARAM at zone apex nsec3 parameters
+ */
+ KNOT_RRTYPE_NSEC3PARAM,
+
+ /* TODO consider some better way of doing this, indices too high */
+
+ KNOT_RRTYPE_SPF = 99, /*!< RFC 4408 */
+
+ // not designating any RRs
+ KNOT_RRTYPE_TSIG = 250, /*!< TSIG - RFC2845. */
+ KNOT_RRTYPE_IXFR = 251, /*!< IXFR (not an actual RR). */
+ KNOT_RRTYPE_AXFR = 252, /*!< AXFR (not an actual RR). */
+ /*!
+ * \brief A request for mailbox-related records (MB, MG or MR)
+ */
+ KNOT_RRTYPE_MAILB = 253,
+ /*!
+ * \brief A request for mail agent RRs (Obsolete - see MX)
+ */
+ KNOT_RRTYPE_MAILA = 254,
+ KNOT_RRTYPE_ANY = 255, /*!< any type (wildcard) */
+
+ // totally weird numbers (cannot use for indexing)
+ KNOT_RRTYPE_TA = 32768, /*!< DNSSEC Trust Authorities */
+ KNOT_RRTYPE_DLV = 32769, /*!< RFC 4431 */
+
+ /*! \brief Last normal RR type. */
+ KNOT_RRTYPE_LAST = KNOT_RRTYPE_TSIG
+ /*! \todo [TSIG] Is it allright to include all <= RR TSIG?
+ * Because TSIG is normal RR type. */
+};
+
+typedef enum knot_rr_type knot_rr_type_t;
+
+/*! \brief Constants characterising the wire format of RDATA items. */
+enum knot_rdata_wireformat {
+ /*!
+ * \brief Possibly compressed domain name.
+ */
+ KNOT_RDATA_WF_COMPRESSED_DNAME = 50,
+ KNOT_RDATA_WF_UNCOMPRESSED_DNAME = 51, /*!< Uncompressed domain name. */
+ KNOT_RDATA_WF_LITERAL_DNAME = 52, /*!< Literal (not downcased) dname. */
+ KNOT_RDATA_WF_BYTE = 1, /*!< 8-bit integer. */
+ KNOT_RDATA_WF_SHORT = 2, /*!< 16-bit integer. */
+ KNOT_RDATA_WF_LONG = 4, /*!< 32-bit integer. */
+ KNOT_RDATA_WF_UINT48 = 8, /*!< 48-bit integer. */
+ KNOT_RDATA_WF_TEXT = 53, /*!< Text string. */
+ KNOT_RDATA_WF_A = 58, /*!< 32-bit IPv4 address. */
+ KNOT_RDATA_WF_AAAA = 16, /*!< 128-bit IPv6 address. */
+ KNOT_RDATA_WF_BINARY = 54, /*!< Binary data (unknown length). */
+ /*!
+ * \brief Binary data preceded by 1 byte length
+ */
+ KNOT_RDATA_WF_BINARYWITHLENGTH = 55,
+ KNOT_RDATA_WF_APL = 56, /*!< APL data. */
+ KNOT_RDATA_WF_IPSECGATEWAY = 57, /*!< IPSECKEY gateway ip4, ip6 or dname. */
+ KNOT_RDATA_WF_BINARYWITHSHORT = 59,
+ KNOT_RDATA_WF_TEXT_SINGLE = 60 /*!< Text string. */
+};
+
+/*! \brief Constants characterising the format of RDATA items in zone file. */
+enum knot_rdata_zoneformat
+{
+ KNOT_RDATA_ZF_DNAME, /* Domain name. */
+ KNOT_RDATA_ZF_LITERAL_DNAME, /* DNS name (not lowercased domain name). */
+ KNOT_RDATA_ZF_TEXT, /* Text string. */
+ KNOT_RDATA_ZF_BYTE, /* 8-bit integer. */
+ KNOT_RDATA_ZF_SHORT, /* 16-bit integer. */
+ KNOT_RDATA_ZF_LONG, /* 32-bit integer. */
+ KNOT_RDATA_ZF_A, /* 32-bit IPv4 address. */
+ KNOT_RDATA_ZF_AAAA, /* 128-bit IPv6 address. */
+ KNOT_RDATA_ZF_RRTYPE, /* RR type. */
+ KNOT_RDATA_ZF_ALGORITHM, /* Cryptographic algorithm. */
+ KNOT_RDATA_ZF_CERTIFICATE_TYPE,
+ KNOT_RDATA_ZF_PERIOD, /* Time period. */
+ KNOT_RDATA_ZF_TIME,
+ KNOT_RDATA_ZF_BASE64, /* Base-64 binary data. */
+ KNOT_RDATA_ZF_BASE32, /* Base-32 binary data. */
+ KNOT_RDATA_ZF_HEX, /* Hexadecimal binary data. */
+ KNOT_RDATA_ZF_HEX_LEN, /* Hexadecimal binary data. Skip initial length byte. */
+ KNOT_RDATA_ZF_NSAP, /* NSAP. */
+ KNOT_RDATA_ZF_APL, /* APL. */
+ KNOT_RDATA_ZF_IPSECGATEWAY, /* IPSECKEY gateway ip4, ip6 or dname. */
+ KNOT_RDATA_ZF_SERVICES, /* Protocol and port number bitmap. */
+ KNOT_RDATA_ZF_NXT, /* NXT type bitmap. */
+ KNOT_RDATA_ZF_NSEC, /* NSEC type bitmap. */
+ KNOT_RDATA_ZF_LOC, /* Location data. */
+ KNOT_RDATA_ZF_UNKNOWN /* Unknown data. */
+};
+
+/*! \brief Constants characterising the wire format of RDATA items. */
+typedef enum knot_rdata_zoneformat knot_rdata_zoneformat_t;
+
+/*! \brief Enum containing wireformat codes. */
+typedef enum knot_rdatawireformat knot_rdata_wireformat_t;
+
+/*! \brief Structure holding RR descriptor. */
+struct knot_rrtype_descriptor {
+ uint16_t type; /*!< RR type */
+ const char *name; /*!< Textual name. */
+ uint8_t length; /*!< Maximum number of RDATA items. */
+
+ /*! \brief Wire format specification for the RDATA. */
+ uint8_t wireformat[KNOT_MAX_RDATA_ITEMS];
+
+ /*! \brief Zone file format specification for the RDATA. */
+ uint8_t zoneformat[KNOT_MAX_RDATA_ITEMS];
+
+ bool fixed_items; /*!< Has fixed number of RDATA items? */
+};
+
+/*! \brief Structure holding RR descriptor. */
+typedef struct knot_rrtype_descriptor knot_rrtype_descriptor_t;
+
+/*!
+ * \brief Gets RR descriptor for given RR type.
+ *
+ * \param type Code of RR type whose descriptor should be returned.
+ *
+ * \return RR descriptor for given type code, NULL descriptor if
+ * unknown type.
+ *
+ * \todo Change return value to const.
+ */
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_type(uint16_t type);
+
+/*!
+ * \brief Gets RR descriptor for given RR name.
+ *
+ * \param name Mnemonic of RR type whose descriptor should be returned.
+ *
+ * \return RR descriptor for given name, NULL descriptor if
+ * unknown type.
+ *
+ * \todo Change return value to const.
+ */
+knot_rrtype_descriptor_t *knot_rrtype_descriptor_by_name(const char *name);
+
+/*!
+ * \brief Converts numeric type representation to mnemonic string.
+ *
+ * \param rrtype Type RR type code to be converted.
+ *
+ * \return Mnemonic string if found, str(TYPE[rrtype]) otherwise.
+ */
+const char *knot_rrtype_to_string(uint16_t rrtype);
+
+/*!
+ * \brief Converts mnemonic string representation of a type to numeric one.
+ *
+ * \param name Mnemonic string to be converted.
+ *
+ * \return Correct code if found, 0 otherwise.
+ */
+uint16_t knot_rrtype_from_string(const char *name);
+
+/*!
+ * \brief Converts numeric class representation to string one.
+ *
+ * \param rrclass Class code to be converted.
+ *
+ * \return String represenation of class if found,
+ * str(CLASS[rrclass]) otherwise.
+ */
+const char *knot_rrclass_to_string(uint16_t rrclass);
+
+/*!
+ * \brief Converts string representation of a class to numeric one.
+ *
+ * \param name Class string to be converted.
+ *
+ * \return Correct code if found, 0 otherwise.
+ */
+uint16_t knot_rrclass_from_string(const char *name);
+
+/*!
+ * \brief Returns size of wireformat type in bytes.
+ *
+ * \param wire_type Wireformat type.
+ *
+ * \retval Size of given type on success.
+ * \retval 0 on unknown type or type that has no length.
+ */
+size_t knot_wireformat_size(unsigned int wire_type);
+
+int knot_rrtype_is_metatype(uint16_t type);
+
+#endif /* _KNOT_DESCRIPTOR_H_ */
+
+/*! @} */
+
diff --git a/src/libknot/util/error.h b/src/libknot/util/error.h
new file mode 100644
index 0000000..da45151
--- /dev/null
+++ b/src/libknot/util/error.h
@@ -0,0 +1,87 @@
+/*!
+ * \file error.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Error codes and function for getting error message.
+ *
+ * \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_ERROR_H_
+#define _KNOT_ERROR_H_
+
+#include "common/errors.h"
+
+/*! \brief Error codes used in the library. */
+enum knot_error {
+ KNOT_EOK = 0, /*!< OK */
+
+ /* TSIG errors. */
+ KNOT_TSIG_EBADSIG = -16, /*!< Failed to verify TSIG MAC. */
+ KNOT_TSIG_EBADKEY = -17, /*!< TSIG key not recognized or invalid. */
+ KNOT_TSIG_EBADTIME = -18,/*!< TSIG signing time out of range. */
+
+ /* General errors. */
+ KNOT_ERROR = -10000, /*!< General error. */
+ KNOT_ENOMEM, /*!< Not enough memory. */
+ KNOT_ENOTSUP, /*!< Operation not supported. */
+ KNOT_EAGAIN, /*!< OS lacked necessary resources. */
+ KNOT_ERANGE, /*!< Value is out of range. */
+ KNOT_EBADARG, /*!< Wrong argument supported. */
+ KNOT_EFEWDATA, /*!< Not enough data to parse. */
+ KNOT_ESPACE, /*!< Not enough space provided. */
+ KNOT_EMALF, /*!< Malformed data. */
+ KNOT_ECRYPTO, /*!< Error in crypto library. */
+ KNOT_ENSEC3PAR, /*!< Missing or wrong NSEC3PARAM record. */
+ KNOT_EBADZONE, /*!< Domain name does not belong to the zone. */
+ KNOT_EHASH, /*!< Error in hash table. */
+ KNOT_EZONEIN, /*!< Error inserting zone. */
+ KNOT_ENOZONE, /*!< No such zone found. */
+ KNOT_ENONODE, /*!< No such node in zone found. */
+ KNOT_ENORRSET, /*!< No such RRSet found. */
+ KNOT_EDNAMEPTR, /*!< Domain name pointer larger than allowed. */
+ KNOT_EPAYLOAD, /*!< Payload in OPT RR larger than max wire size. */
+ KNOT_ECRC, /*!< Wrong dump CRC. */
+ KNOT_EPREREQ, /*!< UPDATE prerequisity not met. */
+ KNOT_ENOXFR, /*!< Transfer was not sent. */
+ KNOT_ENOIXFR, /*!< Transfer is not IXFR (is in AXFR format). */
+ KNOT_EXFRREFUSED, /*!< Zone transfer refused by the server. */
+ KNOT_ECONN, /*!< Connection reset. */
+ KNOT_ERROR_COUNT = 30
+};
+
+/*! \brief Table linking error messages to error codes. */
+extern const error_table_t knot_error_msgs[KNOT_ERROR_COUNT];
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+static inline const char *knot_strerror(int code)
+{
+ return error_to_str((const error_table_t*)knot_error_msgs, code);
+}
+
+#endif /* _KNOT_ERROR_H_ */
+
+/*! @} */
diff --git a/src/libknot/util/libknot_error.c b/src/libknot/util/libknot_error.c
new file mode 100644
index 0000000..bc2bed2
--- /dev/null
+++ b/src/libknot/util/libknot_error.c
@@ -0,0 +1,53 @@
+/* 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 "util/error.h"
+#include "util/utils.h"
+
+#include "common/errors.h"
+
+const error_table_t knot_error_msgs[KNOT_ERROR_COUNT] = {
+ {KNOT_EOK, "OK"},
+ {KNOT_ERROR, "General error."},
+ {KNOT_ENOMEM, "Not enough memory."},
+ {KNOT_ENOTSUP, "Operation not supported."},
+ {KNOT_EAGAIN, "OS lacked necessary resources."},
+ {KNOT_ERANGE, "Value is out of range."},
+ {KNOT_EBADARG, "Wrong argument supported."},
+ {KNOT_EFEWDATA, "Not enough data to parse."},
+ {KNOT_ESPACE, "Not enough space provided."},
+ {KNOT_EMALF, "Malformed data."},
+ {KNOT_ECRYPTO, "Error in crypto library."},
+ {KNOT_ENSEC3PAR, "Missing or wrong NSEC3PARAM record."},
+ {KNOT_EBADZONE, "Domain name does not belong to the given zone."},
+ {KNOT_EHASH, "Error in hash table."},
+ {KNOT_EZONEIN, "Error inserting zone."},
+ {KNOT_ENOZONE, "No such zone found."},
+ {KNOT_ENONODE, "No such node in zone found."},
+ {KNOT_ENORRSET, "No such RRSet found."},
+ {KNOT_EDNAMEPTR, "Domain name pointer larger than allowed."},
+ {KNOT_EPAYLOAD, "Payload in OPT RR larger than max wire size."},
+ {KNOT_ECRC, "CRC check failed."},
+ {KNOT_EPREREQ, "UPDATE prerequisity not met."},
+ {KNOT_ENOXFR, "Transfer was not sent."},
+ {KNOT_ENOIXFR, "Transfer is not IXFR (is in AXFR format)."},
+ {KNOT_EXFRREFUSED, "Zone transfer refused by the server."},
+ {KNOT_TSIG_EBADSIG, "Failed to verify TSIG MAC." },
+ {KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid." },
+ {KNOT_TSIG_EBADTIME, "TSIG signing time out of range." },
+ {KNOT_ECONN, "Connection reset."},
+ {KNOT_ERROR, 0}
+};
diff --git a/src/libknot/util/tolower.c b/src/libknot/util/tolower.c
new file mode 100644
index 0000000..d71c467
--- /dev/null
+++ b/src/libknot/util/tolower.c
@@ -0,0 +1,276 @@
+/* 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 "util/tolower.h"
+
+const uint8_t char_table[CHAR_TABLE_SIZE] = {
+ '\x00',
+ '\x01',
+ '\x02',
+ '\x03',
+ '\x04',
+ '\x05',
+ '\x06',
+ '\x07',
+ '\x08',
+ '\x09',
+ '\x0A',
+ '\x0B',
+ '\x0C',
+ '\x0D',
+ '\x0E',
+ '\x0F',
+ '\x10',
+ '\x11',
+ '\x12',
+ '\x13',
+ '\x14',
+ '\x15',
+ '\x16',
+ '\x17',
+ '\x18',
+ '\x19',
+ '\x1A',
+ '\x1B',
+ '\x1C',
+ '\x1D',
+ '\x1E',
+ '\x1F',
+ '\x20',
+ '\x21', /* ! */
+ '\x22', /* " */
+ '\x23', /* # */
+ '\x24', /* $ */
+ '\x25', /* % */
+ '\x26', /* & */
+ '\x27', /* ' */
+ '\x28', /* ( */
+ '\x29', /* ) */
+ '\x2A', /* * */
+ '\x2B', /* + */
+ '\x2C', /* , */
+ '\x2D', /* - */
+ '\x2E', /* . */
+ '\x2F', /* / */
+ '\x30', /* 0 */
+ '\x31', /* 1 */
+ '\x32', /* 2 */
+ '\x33', /* 3 */
+ '\x34', /* 4 */
+ '\x35', /* 5 */
+ '\x36', /* 6 */
+ '\x37', /* 7 */
+ '\x38', /* 8 */
+ '\x39', /* 9 */
+ '\x3A', /* : */
+ '\x3B', /* ; */
+ '\x3C', /* < */
+ '\x3D', /* = */
+ '\x3E', /* > */
+ '\x3F', /* ? */
+ '\x40', /* @ */
+ '\x61', /* A */
+ '\x62', /* B */
+ '\x63', /* C */
+ '\x64', /* D */
+ '\x65', /* E */
+ '\x66', /* F */
+ '\x67', /* G */
+ '\x68', /* H */
+ '\x69', /* I */
+ '\x6A', /* J */
+ '\x6B', /* K */
+ '\x6C', /* L */
+ '\x6D', /* M */
+ '\x6E', /* N */
+ '\x6F', /* O */
+ '\x70', /* P */
+ '\x71', /* Q */
+ '\x72', /* R */
+ '\x73', /* S */
+ '\x74', /* T */
+ '\x75', /* U */
+ '\x76', /* V */
+ '\x77', /* W */
+ '\x78', /* X */
+ '\x79', /* Y */
+ '\x7A', /* Z */
+ '\x5B', /* [ */
+ '\x5C', /* \ */
+ '\x5D', /* ] */
+ '\x5E', /* ^ */
+ '\x5F', /* _ */
+ '\x60', /* ` */
+ '\x61', /* a */
+ '\x62', /* b */
+ '\x63', /* c */
+ '\x64', /* d */
+ '\x65', /* e */
+ '\x66', /* f */
+ '\x67', /* g */
+ '\x68', /* h */
+ '\x69', /* i */
+ '\x6A', /* j */
+ '\x6B', /* k */
+ '\x6C', /* l */
+ '\x6D', /* m */
+ '\x6E', /* n */
+ '\x6F', /* o */
+ '\x70', /* p */
+ '\x71', /* q */
+ '\x72', /* r */
+ '\x73', /* s */
+ '\x74', /* t */
+ '\x75', /* u */
+ '\x76', /* v */
+ '\x77', /* w */
+ '\x78', /* x */
+ '\x79', /* y */
+ '\x7A', /* z */
+ '\x7B', /* { */
+ '\x7C', /* | */
+ '\x7D', /* } */
+ '\x7E', /* ~ */
+ '\x7F',
+ '\x80',
+ '\x81',
+ '\x82',
+ '\x83',
+ '\x84',
+ '\x85',
+ '\x86',
+ '\x87',
+ '\x88',
+ '\x89',
+ '\x8A',
+ '\x8B',
+ '\x8C',
+ '\x8D',
+ '\x8E',
+ '\x8F',
+ '\x90',
+ '\x91',
+ '\x92',
+ '\x93',
+ '\x94',
+ '\x95',
+ '\x96',
+ '\x97',
+ '\x98',
+ '\x99',
+ '\x9A',
+ '\x9B',
+ '\x9C',
+ '\x9D',
+ '\x9E',
+ '\x9F',
+ '\xA0',
+ '\xA1',
+ '\xA2',
+ '\xA3',
+ '\xA4',
+ '\xA5',
+ '\xA6',
+ '\xA7',
+ '\xA8',
+ '\xA9',
+ '\xAA',
+ '\xAB',
+ '\xAC',
+ '\xAD',
+ '\xAE',
+ '\xAF',
+ '\xB0',
+ '\xB1',
+ '\xB2',
+ '\xB3',
+ '\xB4',
+ '\xB5',
+ '\xB6',
+ '\xB7',
+ '\xB8',
+ '\xB9',
+ '\xBA',
+ '\xBB',
+ '\xBC',
+ '\xBD',
+ '\xBE',
+ '\xBF',
+ '\xC0',
+ '\xC1',
+ '\xC2',
+ '\xC3',
+ '\xC4',
+ '\xC5',
+ '\xC6',
+ '\xC7',
+ '\xC8',
+ '\xC9',
+ '\xCA',
+ '\xCB',
+ '\xCC',
+ '\xCD',
+ '\xCE',
+ '\xCF',
+ '\xD0',
+ '\xD1',
+ '\xD2',
+ '\xD3',
+ '\xD4',
+ '\xD5',
+ '\xD6',
+ '\xD7',
+ '\xD8',
+ '\xD9',
+ '\xDA',
+ '\xDB',
+ '\xDC',
+ '\xDD',
+ '\xDE',
+ '\xDF',
+ '\xE0',
+ '\xE1',
+ '\xE2',
+ '\xE3',
+ '\xE4',
+ '\xE5',
+ '\xE6',
+ '\xE7',
+ '\xE8',
+ '\xE9',
+ '\xEA',
+ '\xEB',
+ '\xEC',
+ '\xED',
+ '\xEE',
+ '\xEF',
+ '\xF0',
+ '\xF1',
+ '\xF2',
+ '\xF3',
+ '\xF4',
+ '\xF5',
+ '\xF6',
+ '\xF7',
+ '\xF8',
+ '\xF9',
+ '\xFA',
+ '\xFB',
+ '\xFC',
+ '\xFD',
+ '\xFE',
+ '\xFF',
+};
diff --git a/src/libknot/util/tolower.h b/src/libknot/util/tolower.h
new file mode 100644
index 0000000..6b9e98c
--- /dev/null
+++ b/src/libknot/util/tolower.h
@@ -0,0 +1,57 @@
+/*!
+ * \file tolower.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Table for converting ASCII characters to lowercase.
+ *
+ * \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_TOLOWER_H_
+#define _KNOT_TOLOWER_H_
+
+#include <stdint.h>
+
+/*! \brief Size of the character conversion table. */
+#define KNOT_CHAR_TABLE_SIZE 256
+
+enum {
+ /*! \brief Size of the character conversion table. */
+ CHAR_TABLE_SIZE = KNOT_CHAR_TABLE_SIZE
+};
+
+/*! \brief Character table mapping uppercase letters to lowercase. */
+extern const uint8_t char_table[CHAR_TABLE_SIZE];
+
+/*!
+ * \brief Converts ASCII character to lowercase.
+ *
+ * \param c ASCII character code.
+ *
+ * \return \a c converted to lowercase (or \a c if not applicable).
+ */
+static inline uint8_t knot_tolower(uint8_t c) {
+#if KNOT_CHAR_TABLE_SIZE < 256
+ assert(c < CHAR_TABLE_SIZE);
+#endif
+ return char_table[c];
+}
+
+#endif /* _KNOT_TOLOWER_H_ */
diff --git a/src/libknot/util/utils.c b/src/libknot/util/utils.c
new file mode 100644
index 0000000..17b33a7
--- /dev/null
+++ b/src/libknot/util/utils.c
@@ -0,0 +1,127 @@
+/* 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 <string.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util/utils.h"
+#include "common/WELL1024a.h"
+
+/*----------------------------------------------------------------------------*/
+
+knot_lookup_table_t *knot_lookup_by_name(knot_lookup_table_t *table,
+ const char *name)
+{
+ while (table->name != NULL) {
+ if (strcasecmp(name, table->name) == 0) {
+ return table;
+ }
+ table++;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+knot_lookup_table_t *knot_lookup_by_id(knot_lookup_table_t *table,
+ int id)
+{
+ while (table->name != NULL) {
+ if (table->id == id) {
+ return table;
+ }
+ table++;
+ }
+
+ return NULL;
+}
+
+/*----------------------------------------------------------------------------*/
+
+size_t knot_strlcpy(char *dst, const char *src, size_t size)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = size;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0) {
+ break;
+ }
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (size != 0) {
+ *d = '\0'; /* NUL-terminate dst */
+ }
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
+/*! \brief TLS key for rand seed. */
+static pthread_key_t _qr_key;
+static pthread_once_t _qr_once = PTHREAD_ONCE_INIT;
+
+/*! \brief TLS key initializer. */
+static void _qr_init()
+{
+ (void) pthread_key_create(&_qr_key, NULL);
+ (void) pthread_setspecific(_qr_key, (void*)time(0));
+}
+
+size_t knot_quick_rand()
+{
+ (void) pthread_once(&_qr_once, _qr_init);
+ size_t x = (size_t)pthread_getspecific(_qr_key);
+
+ /* Numerical Recipes in C.
+ * The Art of Scientific Computing, 2nd Edition,
+ * 1992, ISBN 0-521-43108-5.
+ * Page 284.
+ */
+ x = 1664525L * x + 1013904223L;
+ (void) pthread_setspecific(_qr_key, (void*)x);
+ return x;
+}
+
+uint16_t knot_random_id()
+{
+ return (uint16_t)(tls_rand() * ((uint16_t)~0));
+}
+
+struct flock* knot_file_lock(short type, short whence)
+{
+ static struct flock ret;
+ ret.l_type = type;
+ ret.l_start = 0;
+ ret.l_whence = whence;
+ ret.l_len = 0;
+ ret.l_pid = getpid();
+ return &ret;
+}
+
diff --git a/src/libknot/util/utils.h b/src/libknot/util/utils.h
new file mode 100644
index 0000000..f43b8f0
--- /dev/null
+++ b/src/libknot/util/utils.h
@@ -0,0 +1,196 @@
+/*!
+ * \file utils.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Various utilities.
+ *
+ * \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_UTILS_H_
+#define _KNOT_UTILS_H_
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*!
+ * \brief A general purpose lookup table.
+ *
+ * \note Taken from NSD.
+ */
+struct knot_lookup_table {
+ int id;
+ const char *name;
+};
+
+typedef struct knot_lookup_table knot_lookup_table_t;
+
+/*!
+ * \brief Looks up the given name in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param name Name to look up.
+ *
+ * \return Item in the lookup table with the given name or NULL if no such is
+ * present.
+ */
+knot_lookup_table_t *knot_lookup_by_name(knot_lookup_table_t *table,
+ const char *name);
+
+/*!
+ * \brief Looks up the given id in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param id ID to look up.
+ *
+ * \return Item in the lookup table with the given id or NULL if no such is
+ * present.
+ */
+knot_lookup_table_t *knot_lookup_by_id(knot_lookup_table_t *table,
+ int id);
+
+/*!
+ * \brief Strlcpy - safe string copy function, based on FreeBSD implementation.
+ *
+ * http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/
+ *
+ * \param dst Destination string.
+ * \param src Source string.
+ * \param size How many characters to copy - 1.
+ *
+ * \return strlen(src), if retval >= siz, truncation occurred.
+ */
+size_t knot_strlcpy(char *dst, const char *src, size_t size);
+
+/*
+ * Writing / reading arbitrary data to / from wireformat.
+ */
+
+/*!
+ * \brief Reads 2 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 2 bytes from.
+ *
+ * \return The 2 bytes read, in inverse endian.
+ */
+static inline uint16_t knot_wire_read_u16(const uint8_t *pos)
+{
+ return (pos[0] << 8) | pos[1];
+}
+
+/*!
+ * \brief Reads 4 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 4 bytes from.
+ *
+ * \return The 4 bytes read, in inverse endian.
+ */
+static inline uint32_t knot_wire_read_u32(const uint8_t *pos)
+{
+ return (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3];
+}
+
+/*!
+ * \brief Reads 6 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 6 bytes from.
+ *
+ * \return The 6 bytes read, in inverse endian.
+ */
+static inline uint64_t knot_wire_read_u48(const uint8_t *pos)
+{
+ return ((uint64_t)(pos[0]) << 40) | ((uint64_t)(pos[1]) << 32) | (pos[2] << 24) |
+ (pos[3] << 16) | (pos[4] << 8) | pos[5];
+}
+
+/*!
+ * \brief Writes 2 bytes in wireformat.
+ *
+ * The endian of the data is inverted.
+ *
+ * \param pos Position where to put the 2 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u16(uint8_t *pos, uint16_t data)
+{
+ pos[0] = (uint8_t)((data >> 8) & 0xff);
+ pos[1] = (uint8_t)(data & 0xff);
+}
+
+/*!
+ * \brief Writes 4 bytes in wireformat.
+ *
+ * The endian of the data is inverted.
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u32(uint8_t *pos, uint32_t data)
+{
+ pos[0] = (uint8_t)((data >> 24) & 0xff);
+ pos[1] = (uint8_t)((data >> 16) & 0xff);
+ pos[2] = (uint8_t)((data >> 8) & 0xff);
+ pos[3] = (uint8_t)(data & 0xff);
+}
+
+/*!
+ * \brief Writes 6 bytes in wireformat.
+ *
+ * The endian of the data is inverted.
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+static inline void knot_wire_write_u48(uint8_t *pos, uint64_t data)
+{
+ pos[0] = (uint8_t)((data >> 40) & 0xff);
+ pos[1] = (uint8_t)((data >> 32) & 0xff);
+ pos[2] = (uint8_t)((data >> 24) & 0xff);
+ pos[3] = (uint8_t)((data >> 16) & 0xff);
+ pos[4] = (uint8_t)((data >> 8) & 0xff);
+ pos[5] = (uint8_t)(data & 0xff);
+}
+
+/*!
+ * \brief Linear congruential generator.
+ *
+ * Simple pseudorandom generator for general purpose.
+ * \warning Do not use for cryptography.
+ * \return Random number <0, (size_t)~0>
+ */
+size_t knot_quick_rand();
+
+uint16_t knot_random_id();
+
+/*!
+ * \brief Helper function for simple locking.
+ *
+ * \param type Type of lock.
+ * \param type Starting position of lock.
+ *
+ * \return Locking structure.
+ */
+struct flock* knot_file_lock(short type, short whence);
+
+#endif /* _KNOT_UTILS_H_ */
+
+/*! @} */
+
diff --git a/src/libknot/util/wire.h b/src/libknot/util/wire.h
new file mode 100644
index 0000000..0a24ff1
--- /dev/null
+++ b/src/libknot/util/wire.h
@@ -0,0 +1,926 @@
+/*!
+ * \file wire.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Functions for manipulating and parsing raw data in DNS packets.
+ *
+ * \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_WIRE_H_
+#define _KNOT_WIRE_H_
+
+#include <stdint.h>
+#include <assert.h>
+
+#include "util/utils.h"
+
+/*! \brief Offset of DNS header fields in wireformat. */
+enum knot_wire_offsets {
+ KNOT_WIRE_OFFSET_ID = 0,
+ KNOT_WIRE_OFFSET_FLAGS1 = 2,
+ KNOT_WIRE_OFFSET_FLAGS2 = 3,
+ KNOT_WIRE_OFFSET_QDCOUNT = 4,
+ KNOT_WIRE_OFFSET_ANCOUNT = 6,
+ KNOT_WIRE_OFFSET_NSCOUNT = 8,
+ KNOT_WIRE_OFFSET_ARCOUNT = 10
+};
+
+/*! \brief Minimum size for some parts of the DNS packet. */
+enum knot_wire_sizes {
+ KNOT_WIRE_HEADER_SIZE = 12,
+ KNOT_WIRE_QUESTION_MIN_SIZE = 5,
+ KNOT_WIRE_RR_MIN_SIZE = 11
+};
+
+/*
+ * Packet header manipulation functions.
+ */
+
+/*!
+ * \brief Returns the ID from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return DNS packet ID.
+ */
+static inline uint16_t knot_wire_get_id(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ID);
+}
+
+/*!
+ * \brief Sets the ID to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param id DNS packet ID.
+ */
+static inline void knot_wire_set_id(uint8_t *packet, uint16_t id)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ID, id);
+}
+
+/*!
+ * \brief Returns the first byte of flags from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return First byte of DNS flags.
+ */
+static inline uint8_t knot_wire_get_flags1(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1);
+}
+
+/*!
+ * \brief Sets the first byte of flags to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param flags1 First byte of the DNS flags.
+ */
+static inline uint8_t knot_wire_set_flags1(uint8_t *packet, uint8_t flags1)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) = flags1;
+}
+
+/*!
+ * \brief Returns the second byte of flags from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Second byte of DNS flags.
+ */
+static inline uint8_t knot_wire_get_flags2(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2);
+}
+
+/*!
+ * \brief Sets the second byte of flags to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param flags2 Second byte of the DNS flags.
+ */
+static inline uint8_t knot_wire_set_flags2(uint8_t *packet, uint8_t flags2)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) = flags2;
+}
+
+/*!
+ * \brief Returns the QDCOUNT (count of Question entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return QDCOUNT (count of Question entries in the packet).
+ */
+static inline uint16_t knot_wire_get_qdcount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT);
+}
+
+/*!
+ * \brief Sets the QDCOUNT (count of Question entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param qdcount QDCOUNT (count of Question entries in the packet).
+ */
+static inline void knot_wire_set_qdcount(uint8_t *packet, uint16_t qdcount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT, qdcount);
+}
+
+/*!
+ * \brief Returns the ANCOUNT (count of Answer entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return ANCOUNT (count of Answer entries in the packet).
+ */
+static inline uint16_t knot_wire_get_ancount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT);
+}
+
+/*!
+ * \brief Sets the ANCOUNT (count of Answer entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param ancount ANCOUNT (count of Answer entries in the packet).
+ */
+static inline void knot_wire_set_ancount(uint8_t *packet, uint16_t ancount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT, ancount);
+}
+
+/*!
+ * \brief Returns the NSCOUNT (count of Authority entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return NSCOUNT (count of Authority entries in the packet).
+ */
+static inline uint16_t knot_wire_get_nscount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT);
+}
+
+/*!
+ * \brief Sets the NSCOUNT (count of Authority entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param nscount NSCOUNT (count of Authority entries in the packet).
+ */
+static inline void knot_wire_set_nscount(uint8_t *packet, uint16_t nscount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT, nscount);
+}
+
+/*!
+ * \brief Returns the ARCOUNT (count of Additional entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return ARCOUNT (count of Additional entries in the packet).
+ */
+static inline uint16_t knot_wire_get_arcount(const uint8_t *packet)
+{
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT);
+}
+
+/*!
+ * \brief Sets the ARCOUNT (count of Additional entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param arcount ARCOUNT (count of Additional entries in the packet).
+ */
+static inline void knot_wire_set_arcount(uint8_t *packet, uint16_t arcount)
+{
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT, arcount);
+}
+
+/*
+ * Packet header flags manipulation functions.
+ */
+/*! \brief Constants for DNS header flags in the first flags byte. */
+enum knot_wire_flags1_consts {
+ KNOT_WIRE_RD_MASK = (uint8_t)0x01U, /*!< RD bit mask. */
+ KNOT_WIRE_RD_SHIFT = 0, /*!< RD bit shift. */
+ KNOT_WIRE_TC_MASK = (uint8_t)0x02U, /*!< TC bit mask. */
+ KNOT_WIRE_TC_SHIFT = 1, /*!< TC bit shift. */
+ KNOT_WIRE_AA_MASK = (uint8_t)0x04U, /*!< AA bit mask. */
+ KNOT_WIRE_AA_SHIFT = 2, /*!< AA bit shift. */
+ KNOT_WIRE_OPCODE_MASK = (uint8_t)0x78U, /*!< OPCODE mask. */
+ KNOT_WIRE_OPCODE_SHIFT = 3, /*!< OPCODE shift. */
+ KNOT_WIRE_QR_MASK = (uint8_t)0x80U, /*!< QR bit mask. */
+ KNOT_WIRE_QR_SHIFT = 7 /*!< QR bit shift. */
+};
+
+/*! \brief Constants for DNS header flags in the second flags byte. */
+enum knot_wire_flags2_consts {
+ KNOT_WIRE_RCODE_MASK = (uint8_t)0x0fU, /*!< RCODE mask. */
+ KNOT_WIRE_RCODE_SHIFT = 0, /*!< RCODE shift. */
+ KNOT_WIRE_CD_MASK = (uint8_t)0x10U, /*!< CD bit mask. */
+ KNOT_WIRE_CD_SHIFT = 4, /*!< CD bit shift. */
+ KNOT_WIRE_AD_MASK = (uint8_t)0x20U, /*!< AD bit mask. */
+ KNOT_WIRE_AD_SHIFT = 5, /*!< AD bit shift. */
+ KNOT_WIRE_Z_MASK = (uint8_t)0x40U, /*!< Zero bit mask. */
+ KNOT_WIRE_Z_SHIFT = 6, /*!< Zero bit shift. */
+ KNOT_WIRE_RA_MASK = (uint8_t)0x80U, /*!< RA bit mask. */
+ KNOT_WIRE_RA_SHIFT = 7 /*!< RA bit shift. */
+};
+
+/*
+ * Functions for getting / setting / clearing flags and codes directly in packet
+ */
+
+/*!
+ * \brief Returns the RD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the RD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_rd(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Sets the RD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_rd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Clears the RD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_flags_clear_rd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Returns the TC bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the TC bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_tc(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Sets the TC bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_tc(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Clears the TC bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_tc(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Returns the AA bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the AA bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_aa(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Sets the AA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_aa(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Clears the AA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_aa(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Returns the OPCODE from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return OPCODE of the packet.
+ */
+static inline uint8_t knot_wire_get_opcode(const uint8_t *packet)
+{
+ return (*(packet + KNOT_WIRE_OFFSET_FLAGS1)
+ & KNOT_WIRE_OPCODE_MASK) >> KNOT_WIRE_OPCODE_SHIFT;
+}
+
+/*!
+ * \brief Sets the OPCODE in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param opcode OPCODE to set.
+ */
+static inline void knot_wire_set_opcode(uint8_t *packet, short opcode)
+{
+ uint8_t *flags1 = packet + KNOT_WIRE_OFFSET_FLAGS1;
+ *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK)
+ | ((opcode) << KNOT_WIRE_OPCODE_SHIFT);
+}
+
+/*!
+ * \brief Returns the QR bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the QR bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_qr(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Sets the QR bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_qr(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Clears the QR bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_qr(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Returns the RCODE from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return RCODE of the packet.
+ */
+static inline uint8_t knot_wire_get_rcode(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2)
+ & KNOT_WIRE_RCODE_MASK;
+}
+
+/*!
+ * \brief Sets the RCODE in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param rcode RCODE to set.
+ */
+static inline void knot_wire_set_rcode(uint8_t *packet, short rcode)
+{
+ uint8_t *flags2 = packet + KNOT_WIRE_OFFSET_FLAGS2;
+ *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode);
+}
+
+/*!
+ * \brief Returns the CD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the CD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_cd(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Sets the CD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_cd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Clears the CD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_cd(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Returns the AD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the AD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_ad(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Sets the AD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_ad(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Clears the AD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_ad(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Returns the Zero bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the Zero bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_z(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Sets the Zero bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_z(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Clears the Zero bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_z(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Returns the RA bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the RA bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_ra(const uint8_t *packet)
+{
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Sets the RA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_ra(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Clears the RA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_ra(uint8_t *packet)
+{
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_RA_MASK;
+}
+
+/*
+ * Functions for getting / setting / clearing flags in flags variable
+ */
+
+/*!
+ * \brief Returns the RD bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the RD bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_rd(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Sets the RD bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_rd(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Clears the RD bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_flags_clear_rd(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Returns the TC bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the TC bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_tc(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Sets the TC bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_tc(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Clears the TC bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_tc(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Returns the AA bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the AA bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_aa(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Sets the AA bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_aa(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Clears the AA bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_aa(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Returns the OPCODE from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return OPCODE
+ */
+static inline uint8_t knot_wire_flags_get_opcode(uint8_t flags1)
+{
+ return (flags1 & KNOT_WIRE_OPCODE_MASK)
+ >> KNOT_WIRE_OPCODE_SHIFT;
+}
+
+/*!
+ * \brief Sets the OPCODE in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ * \param opcode OPCODE to set.
+ */
+static inline void knot_wire_flags_set_opcode(uint8_t *flags1, short opcode)
+{
+ *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK)
+ | ((opcode) << KNOT_WIRE_OPCODE_SHIFT);
+}
+
+/*!
+ * \brief Returns the QR bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the QR bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_qr(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Sets the QR bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_qr(uint8_t *flags1)
+{
+ *flags1 |= KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Clears the QR bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_qr(uint8_t *flags1)
+{
+ *flags1 &= ~KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Returns the RCODE from the second byte of flags.
+ *
+ * \param flags2 First byte of DNS header flags.
+ *
+ * \return RCODE
+ */
+static inline uint8_t knot_wire_flags_get_rcode(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_RCODE_MASK;
+}
+
+/*!
+ * \brief Sets the RCODE in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ * \param rcode RCODE to set.
+ */
+static inline void knot_wire_flags_set_rcode(uint8_t *flags2, short rcode)
+{
+ *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode);
+}
+
+/*!
+ * \brief Returns the CD bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the CD bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_cd(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Sets the CD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_cd(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Clears the CD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_cd(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Returns the AD bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the AD bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_ad(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Sets the AD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_ad(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Clears the AD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_ad(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Returns the Zero bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the Zero bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_z(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Sets the Zero bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_z(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Clears the Zero bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_z(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Returns the RA bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the RA bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_ra(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Sets the RA bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_ra(uint8_t *flags2)
+{
+ *flags2 |= KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Clears the RA bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_ra(uint8_t *flags2)
+{
+ *flags2 &= ~KNOT_WIRE_RA_MASK;
+}
+
+/*
+ * Pointer manipulation
+ */
+
+enum knot_wire_pointer_consts {
+ /*! \brief DNS packet pointer designation (first two bits set to 1). */
+ KNOT_WIRE_PTR = (uint8_t)0xc0U
+};
+
+/*!
+ * \brief Creates a DNS packet pointer and stores it in wire format.
+ *
+ * \param pos Position where tu put the pointer.
+ * \param ptr Relative position of the item to which the pointer should point in
+ * the wire format of the packet.
+ */
+static inline void knot_wire_put_pointer(uint8_t *pos, size_t ptr)
+{
+ uint16_t p = ptr;
+ knot_wire_write_u16(pos, p);
+ assert((pos[0] & KNOT_WIRE_PTR) == 0);
+ pos[0] |= KNOT_WIRE_PTR;
+}
+
+static inline int knot_wire_is_pointer(const uint8_t *pos)
+{
+ return ((pos[0] & KNOT_WIRE_PTR) != 0);
+}
+
+static inline size_t knot_wire_get_pointer(const uint8_t *pos)
+{
+ /*! \todo memcpy() is not needed, may be directly assigned. */
+ uint16_t p = 0;
+ memcpy(&p, pos, 2);
+ p &= ~KNOT_WIRE_PTR;
+
+ uint16_t p2 = knot_wire_read_u16((uint8_t *)&p);
+ return p2;
+}
+
+#endif /* _KNOT_WIRE_H_ */
+
+/*! @} */
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_ */
+
+/*! @} */