src/knot/worker/pool.h
src/knot/worker/queue.c
src/knot/worker/queue.h
+src/knot/zone/adds_tree.c
+src/knot/zone/adds_tree.h
src/knot/zone/adjust.c
src/knot/zone/adjust.h
src/knot/zone/contents.c
knot/worker/pool.h \
knot/worker/queue.c \
knot/worker/queue.h \
+ knot/zone/adds_tree.c \
+ knot/zone/adds_tree.h \
knot/zone/adjust.c \
knot/zone/adjust.h \
knot/zone/contents.c \
--- /dev/null
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "knot/zone/adds_tree.h"
+
+#include "libknot/error.h"
+#include "libknot/rrtype/rdname.h"
+
+static bool same_node(const zone_node_t *a, const zone_node_t *b)
+{
+ return (a == b || binode_counterpart((zone_node_t *)a) == b);
+}
+
+static int free_a_t_node(trie_val_t *val, void *null)
+{
+ assert(null == NULL);
+ list_t *nodes = *(list_t **)val;
+ ptrlist_free(nodes, NULL);
+ free(nodes);
+ return 0;
+}
+
+void additionals_tree_free(additionals_tree_t *a_t)
+{
+ if (a_t != NULL) {
+ trie_apply(a_t, free_a_t_node, NULL);
+ trie_free(a_t);
+ }
+}
+
+int zone_node_additionals_foreach(const zone_node_t *node, const knot_dname_t *zone_apex,
+ zone_node_additionals_cb_t cb, void *ctx)
+{
+ int ret = KNOT_EOK;
+ for (int i = 0; ret == KNOT_EOK && i < node->rrset_count; i++) {
+ struct rr_data *rr_data = &node->rrs[i];
+ for (int j = 0; ret == KNOT_EOK && j < rr_data->rrs.count; j++) {
+ knot_rdata_t *rdata = knot_rdataset_at(&rr_data->rrs, j);
+ const knot_dname_t *name = knot_rdata_name(rdata, rr_data->type);
+
+ if (knot_dname_in_bailiwick(name, zone_apex) > 0) {
+ ret = cb(name, ctx);
+ }
+ }
+ }
+ return ret;
+}
+
+typedef struct {
+ additionals_tree_t *a_t;
+ zone_node_t *node;
+} a_t_node_ctx_t;
+
+static int remove_node_from_a_t(const knot_dname_t *name, void *a_ctx)
+{
+ a_t_node_ctx_t *ctx = a_ctx;
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(name, lf_storage);
+
+ trie_val_t *val = trie_get_try(ctx->a_t, lf + 1, *lf);
+ if (val == NULL) {
+ return KNOT_EOK;
+ }
+
+ list_t *nodes = *(list_t **)val;
+ if (nodes == NULL) {
+ goto rem_empty;
+ }
+
+ ptrnode_t *node_in_list = NULL, *next = NULL;
+ WALK_LIST_DELSAFE(node_in_list, next, *nodes) {
+ if (same_node(node_in_list->d, ctx->node)) {
+ rem_node(&node_in_list->n);
+ free(node_in_list);
+ }
+ }
+
+ if (EMPTY_LIST(*nodes)) {
+ free(nodes);
+rem_empty:
+ trie_del(ctx->a_t, lf + 1, *lf, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int add_node_to_a_t(const knot_dname_t *name, void *a_ctx)
+{
+ a_t_node_ctx_t *ctx = a_ctx;
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(name, lf_storage);
+
+ trie_val_t *val = trie_get_ins(ctx->a_t, lf + 1, *lf);
+ if (*val == NULL) {
+ *val = malloc(sizeof(list_t));
+ if (*val == NULL) {
+ return KNOT_ENOMEM;
+ }
+ init_list(*(list_t **)val);
+ }
+
+ list_t *nodes = *(list_t **)val;
+
+ ptrnode_t *node_in_list = NULL;
+ // TODO optimize more
+ WALK_LIST(node_in_list, *nodes) {
+ if (node_in_list->d == ctx->node) { // optimization: yes a bi-node can be stored twice, but solving it is too slow
+ return KNOT_EOK;
+ }
+ }
+
+ ptrlist_add(nodes, ctx->node, NULL);
+ return KNOT_EOK;
+}
+
+int additionals_tree_update_node(additionals_tree_t *a_t, const knot_dname_t *zone_apex,
+ zone_node_t *old_node, zone_node_t *new_node)
+{
+ a_t_node_ctx_t ctx = { a_t, 0 };
+ int ret = KNOT_EOK;
+
+ if (a_t == NULL || zone_apex == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // for every additional in old_node rrsets, remove mentioning of this node in tree
+ if (old_node != NULL && !(old_node->flags & NODE_FLAGS_DELETED)) {
+ ctx.node = old_node;
+ ret = zone_node_additionals_foreach(old_node, zone_apex, remove_node_from_a_t, &ctx);
+ }
+
+ // for every additional in new_node rrsets, add reverse link into the tree
+ if (new_node != NULL && !(new_node->flags & NODE_FLAGS_DELETED) && ret == KNOT_EOK) {
+ ctx.node = new_node;
+ ret = zone_node_additionals_foreach(new_node, zone_apex, add_node_to_a_t, &ctx);
+ }
+ return ret;
+}
+
+int additionals_tree_from_zone(additionals_tree_t **a_t, const zone_contents_t *zone)
+{
+ *a_t = additionals_tree_new();
+ if (*a_t == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin(zone->nodes, &it);
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ ret = additionals_tree_update_node(*a_t, zone->apex->owner, NULL, zone_tree_it_val(&it));
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+
+ if (ret != KNOT_EOK) {
+ additionals_tree_free(*a_t);
+ *a_t = NULL;
+ }
+ return ret;
+}
+
+int additionals_tree_update_from_binodes(additionals_tree_t *a_t, const zone_tree_t *tree,
+ const knot_dname_t *zone_apex)
+{
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin((zone_tree_t *)tree, &it);
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ zone_node_t *node = zone_tree_it_val(&it);
+ ret = additionals_tree_update_node(a_t, zone_apex, binode_counterpart(node), node);
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ return ret;
+}
+
+int additionals_reverse_apply(additionals_tree_t *a_t, const knot_dname_t *name,
+ node_apply_cb_t cb, void *ctx)
+{
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(name, lf_storage);
+
+ trie_val_t *val = trie_get_try(a_t, lf + 1, *lf);
+ if (val == NULL) {
+ return KNOT_EOK;
+ }
+
+ list_t *nodes = *(list_t **)val;
+ if (nodes == NULL) {
+ return KNOT_EOK;
+ }
+
+ ptrnode_t *node_in_list = NULL;
+ WALK_LIST(node_in_list, *nodes) {
+ int ret = cb(node_in_list->d, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int additionals_reverse_apply_multi(additionals_tree_t *a_t, const zone_tree_t *tree,
+ node_apply_cb_t cb, void *ctx)
+{
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin((zone_tree_t *)tree, &it);
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ ret = additionals_reverse_apply(a_t, zone_tree_it_val(&it)->owner, cb, ctx);
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ return ret;
+}
--- /dev/null
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/ucw/lists.h"
+#include "knot/zone/contents.h"
+
+typedef trie_t additionals_tree_t;
+
+inline static additionals_tree_t *additionals_tree_new(void) { return trie_create(NULL); }
+void additionals_tree_free(additionals_tree_t *a_t);
+
+/*!
+ * \brief Foreach additional in all node RRSets, do sth.
+ *
+ * \note This is not too related to additionals_tree, might be moved.
+ *
+ * \param node Zone node with possibly NS, MX, etc rrsets.
+ * \param zone_apex Name of the zone apex.
+ * \param cb Callback to be performed.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+typedef int (*zone_node_additionals_cb_t)(const knot_dname_t *additional, void *ctx);
+int zone_node_additionals_foreach(const zone_node_t *node, const knot_dname_t *zone_apex,
+ zone_node_additionals_cb_t cb, void *ctx);
+
+/*!
+ * \brief Update additionals tree according to changed RRsets in a zone node.
+ *
+ * \param a_t Additionals tree to be updated.
+ * \param zone_apex Name of the zone.
+ * \param old_node Old state of the node (additionals will be removed).
+ * \param new_node New state of the node (additionals will be added)d
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_update_node(additionals_tree_t *a_t, const knot_dname_t *zone_apex,
+ zone_node_t *old_node, zone_node_t *new_node);
+
+/*!
+ * \brief Create additionals tree from a zone (by scanning all additionals in zone RRsets).
+ *
+ * \param a_t Out: additionals tree to be created (NULL if error).
+ * \param zone Zone contents.
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_from_zone(additionals_tree_t **a_t, const zone_contents_t *zone);
+
+/*!
+ * \brief Update attionals tree according to changed RRsets in all nodes in a zone tree.
+ *
+ * \param a_t Additionals tree to be updated.
+ * \param tree Zone tree containing updated nodes as bi-nodes.
+ * \param zone_apex Name of the zone.
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_update_from_binodes(additionals_tree_t *a_t, const zone_tree_t *tree,
+ const knot_dname_t *zone_apex);
+
+/*!
+ * \brief Foreach node that has specified name in its additionals, do sth.
+ *
+ * \note The node passed to the callback might not be correct part of bi-node!
+ *
+ * \param a_t Additionals reverse tree.
+ * \param name Name to be looked up in the additionals.
+ * \param cb Callback to be called.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+typedef int (*node_apply_cb_t)(zone_node_t *node, void *ctx);
+int additionals_reverse_apply(additionals_tree_t *a_t, const knot_dname_t *name,
+ node_apply_cb_t cb, void *ctx);
+
+/*!
+ * \brief Call additionals_reverse_apply() for every name in specified tree.
+ *
+ * \param a_t Additionals reverse tree.
+ * \param tree Zone tree with names to be looked up in additionals.
+ * \param cb Callback to be called for each affected node.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+int additionals_reverse_apply_multi(additionals_tree_t *a_t, const zone_tree_t *tree,
+ node_apply_cb_t cb, void *ctx);
+
#include "contrib/macros.h"
#include "knot/common/log.h"
#include "knot/dnssec/zone-nsec.h"
+#include "knot/zone/adds_tree.h"
int adjust_cb_flags(zone_node_t *node, const zone_contents_t *zone)
{
return ret;
}
-static int adjust_cb_nsec3_and_additionals2(zone_node_t *node, const zone_contents_t *zone)
-{
- int ret = adjust_cb_point_to_nsec3(node, zone);
- if (ret == KNOT_EOK) {
- ret = adjust_cb_additionals(node, zone);
- }
- return ret;
-}
-
int adjust_cb_void(zone_node_t *node, const zone_contents_t *zone)
{
UNUSED(node);
if (ret == KNOT_EOK) {
ret = zone_adjust_contents(zone, adjust_cb_nsec3_and_additionals, NULL, false);
}
+ if (ret == KNOT_EOK) {
+ additionals_tree_free(zone->adds_tree);
+ ret = additionals_tree_from_zone(&zone->adds_tree, zone);
+ }
return ret;
}
+static int adjust_additionals_cb(zone_node_t *node, void *ctx)
+{
+ const zone_contents_t *zone = ctx;
+ zone_node_t *real_node = binode_node(node, (zone->nodes->flags & ZONE_TREE_BINO_SECOND));
+ return adjust_cb_additionals(real_node, zone);
+}
+
int zone_adjust_incremental_update(zone_update_t *update)
{
int ret = zone_adjust_contents(update->new_cont, adjust_cb_flags, adjust_cb_nsec3_flags, true);
if (ret == KNOT_EOK) {
- ret = zone_adjust_contents(update->new_cont, adjust_cb_nsec3_and_additionals2, NULL, false);
+ ret = zone_adjust_contents(update->new_cont, adjust_cb_point_to_nsec3, NULL, false);
}
if (ret == KNOT_EOK) {
ret = zone_adjust_update(update, adjust_cb_wildcard_nsec3, NULL);
}
+ if (ret == KNOT_EOK) {
+ ret = additionals_tree_update_from_binodes(
+ update->new_cont->adds_tree,
+ update->a_ctx->node_ptrs,
+ update->new_cont->apex->owner
+ );
+ }
+ if (ret == KNOT_EOK) {
+ ret = additionals_reverse_apply_multi(
+ update->new_cont->adds_tree,
+ update->a_ctx->node_ptrs,
+ adjust_additionals_cb,
+ update->new_cont
+ );
+ }
return ret;
}
#include <assert.h>
#include "libdnssec/error.h"
+#include "knot/zone/adds_tree.h"
#include "knot/zone/adjust.h"
#include "knot/zone/contents.h"
#include "knot/common/log.h"
} else {
contents->nsec3_nodes = NULL;
}
+ contents->adds_tree = from->adds_tree;
*to = contents;
return KNOT_EOK;
zone_tree_free(&contents->nsec3_nodes);
dnssec_nsec3_params_free(&contents->nsec3_params);
+ additionals_tree_free(contents->adds_tree);
free(contents);
}
zone_tree_t *nodes;
zone_tree_t *nsec3_nodes;
+ trie_t *adds_tree; // "additionals tree" for reverse lookup of nodes affected by additionals
+
dnssec_nsec3_params_t nsec3_params;
size_t size;
uint32_t max_ttl;
return ret;
}
-/*!
- * \brief Return the other part of a bi-node.
- */
-static zone_node_t *binode_counterpart(zone_node_t *node)
+zone_node_t *binode_counterpart(zone_node_t *node)
{
zone_node_t *counterpart = NULL;
return node + (second - (int)((node->flags & NODE_FLAGS_SECOND) >> 9));
}
+/*!
+ * \brief Return the other node from a bi-node.
+ *
+ * \param node A node in a bi-node.
+ *
+ * \return The counterpart node in the smae bi-node.
+ */
+zone_node_t *binode_counterpart(zone_node_t *node);
+
/*!
* \brief Return true if the rdataset of specified type is shared (shallow-copied) among both parts of bi-node.
*/
resp.check_record(section="additional", rtype="A", rdata="1.2.3.4")
verify(master, zone, dnssec)
+ # add delegation w/o glue
+ check_log("Delegation w/o glue")
+ up = master.update(zone)
+ up.add("deleglue.ddns.", 3600, "NS", "a.deleglue.ddns.")
+ up.send("NOERROR")
+ resp = master.dig("deleglue.ddns.", "NS")
+ resp.check_record(section="authority", rtype="NS", rdata="a.deleglue.ddns.")
+ resp.check_no_rr(section="additional", rname="a.deleglue.ddns.", rtype="A")
+ verify(master, zone, dnssec)
+
+ # add glue to delegation
+ check_log("Glue for existing delegation")
+ up = master.update(zone)
+ up.add("a.deleglue.ddns.", 3600, "A", "10.20.30.40")
+ up.send("NOERROR")
+ resp = master.dig("deleglue.ddns.", "NS")
+ resp.check_record(section="authority", rtype="NS", rdata="a.deleglue.ddns.")
+ resp.check_record(section="additional", rtype="A", rdata="10.20.30.40")
+ verify(master, zone, dnssec)
+
# add CNAME to node with A records, should be ignored
check_log("Add CNAME to A node")
up = master.update(zone)