diff --git a/Knot.files b/Knot.files index bb93e4d13d9abd1129ec73c4d1a68bccb3a298e4..353c15aa79c6f73974eab1f953fde137563e1408 100644 --- a/Knot.files +++ b/Knot.files @@ -221,6 +221,8 @@ src/knot/worker/pool.c 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 diff --git a/src/knot/Makefile.inc b/src/knot/Makefile.inc index b8a70a4c2f48e11750001d6e2ac06b343e9b8944..dc916af28d65de3854434e226e758e2a47933451 100644 --- a/src/knot/Makefile.inc +++ b/src/knot/Makefile.inc @@ -153,6 +153,8 @@ libknotd_la_SOURCES = \ 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 \ diff --git a/src/knot/zone/adds_tree.c b/src/knot/zone/adds_tree.c new file mode 100644 index 0000000000000000000000000000000000000000..4dc25a0415cd015c90aa77733c06e65ba01cca23 --- /dev/null +++ b/src/knot/zone/adds_tree.c @@ -0,0 +1,232 @@ +/* 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; +} diff --git a/src/knot/zone/adds_tree.h b/src/knot/zone/adds_tree.h new file mode 100644 index 0000000000000000000000000000000000000000..7ec4252472714301be732201b58fa785445402a8 --- /dev/null +++ b/src/knot/zone/adds_tree.h @@ -0,0 +1,107 @@ +/* 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); + diff --git a/src/knot/zone/adjust.c b/src/knot/zone/adjust.c index 581c1cba61c8029a1ea65ba8bc9957b754654775..a504c8c2869e106d8dbbd89f4825667000429861 100644 --- a/src/knot/zone/adjust.c +++ b/src/knot/zone/adjust.c @@ -20,6 +20,7 @@ #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) { @@ -256,15 +257,6 @@ int adjust_cb_nsec3_and_additionals(zone_node_t *node, const zone_contents_t *zo 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); @@ -394,17 +386,43 @@ int zone_adjust_full(zone_contents_t *zone) 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; } diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c index 78cf779e688ac7bd9c0e6da7155dbae037b0a1b6..915110da58c768322bb97b10bd4ca6c739f35334 100644 --- a/src/knot/zone/contents.c +++ b/src/knot/zone/contents.c @@ -17,6 +17,7 @@ #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" @@ -495,6 +496,7 @@ int zone_contents_shallow_copy(const zone_contents_t *from, zone_contents_t **to } else { contents->nsec3_nodes = NULL; } + contents->adds_tree = from->adds_tree; *to = contents; return KNOT_EOK; @@ -511,6 +513,7 @@ void zone_contents_free(zone_contents_t *contents) zone_tree_free(&contents->nsec3_nodes); dnssec_nsec3_params_free(&contents->nsec3_params); + additionals_tree_free(contents->adds_tree); free(contents); } diff --git a/src/knot/zone/contents.h b/src/knot/zone/contents.h index 6ec3fed86f10cf2c35ca80be7f926e77cdcc6100..c1ed7b3724cf1ecb3d332e07a448125219f4836b 100644 --- a/src/knot/zone/contents.h +++ b/src/knot/zone/contents.h @@ -32,6 +32,8 @@ typedef struct zone_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; diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c index b90fd9d2663eb7614ce8569c5c2eb5f5c6fc0940..474c6b75aa9d1520400286401df967ab3092ddbc 100644 --- a/src/knot/zone/node.c +++ b/src/knot/zone/node.c @@ -130,10 +130,7 @@ zone_node_t *node_new(const knot_dname_t *owner, bool binode, bool second, knot_ 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; diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h index e278a27f2d74f698ca43447956721f3b3ba9e9dd..4ca4f1933cd7b294d63001ecaf75c34d4c3b6a13 100644 --- a/src/knot/zone/node.h +++ b/src/knot/zone/node.h @@ -150,6 +150,15 @@ inline static zone_node_t *binode_node(zone_node_t *node, bool second) 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. */ diff --git a/tests-extra/tests/ddns/basic/test.py b/tests-extra/tests/ddns/basic/test.py index 7b8123998db160f75e82b07681f8a68b24a15a21..f6b53148d3f7bcccd5ccde7c82e6454635c6dcce 100644 --- a/tests-extra/tests/ddns/basic/test.py +++ b/tests-extra/tests/ddns/basic/test.py @@ -124,6 +124,26 @@ def do_normal_tests(master, zone, dnssec=False): 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)