Commit 8dcef6ca authored by Jan Kadlec's avatar Jan Kadlec
Browse files

Removed chain fix code for both NSEC and NSEC3.

- A lot of bugs were uncovered with new tests and fixes got out of hand - the code was no longer readable (not that it was readable before)
- Totally unfeasible to fix before new zone API is done. I've fixed the code, but the result was too complex, unmaintable.
parent ef9d258f
......@@ -23,110 +23,6 @@
#include "knot/dnssec/zone-sign.h"
#include "knot/dnssec/zone-nsec.h"
/* - Forward declarations --------------------------------------------------- */
static knot_rrset_t *create_nsec_rrset(const knot_node_t *,
const knot_node_t *,
uint32_t);
/* - Helper functions ------------------------------------------------------- */
/*!
* \brief Returns true if NSEC is only RRSet in node.
*/
static bool only_nsec_in_node(const knot_node_t *n)
{
assert(n);
return n->rrset_count <= 2 && (knot_node_rrset(n, KNOT_RRTYPE_NSEC)
&& knot_node_rrset(n, KNOT_RRTYPE_RRSIG));
}
/*!
* \brief Updates last used node and DNAME.
*
* \param data Data to be updated.
* \param d DNAME to be set.
* \param n Node to be set.
*/
static void update_last_used(chain_fix_data_t *data, const knot_dname_t *d,
const knot_node_t *n)
{
assert(data && d);
data->last_used_dname = d;
data->last_used_node = n;
}
/*!
* \brief Checks whether NSEC in zone is valid and updates it if needed.
*
* \param from Start node for NSEC link.
* \param to End node for NSEC link.
* \param out_ch Changes are stored here.
* \param soa_min TTL to use for NSEC RRs.
*
* \return KNOT_E*
*/
static int update_nsec(const knot_node_t *from, const knot_node_t *to,
knot_changeset_t *out_ch, uint32_t soa_min)
{
assert(from && to && out_ch);
const knot_rrset_t *nsec_rrset = knot_node_rrset(from,
KNOT_RRTYPE_NSEC);
// Create new NSEC
knot_rrset_t *new_nsec;
if (only_nsec_in_node(from)) {
// Just NSEC present, it has to be dropped
new_nsec = NULL;
} else {
new_nsec = create_nsec_rrset(from, to, soa_min);
if (new_nsec == NULL) {
return KNOT_ERROR;
}
}
// If node in zone has NSEC record, drop it if needed
if (nsec_rrset && new_nsec) {
if (!knot_rrset_equal(new_nsec, nsec_rrset,
KNOT_RRSET_COMPARE_WHOLE)) {
dbg_dnssec_detail("Creating new NSEC for %s\n",
knot_dname_to_str(new_nsec->owner));
// Drop old
int ret = knot_nsec_changeset_remove(from, out_ch);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&new_nsec, 1, NULL);
return ret;
}
// Add new
ret = knot_changeset_add_rrset(out_ch, new_nsec,
KNOT_CHANGESET_ADD);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&new_nsec, 1, NULL);
return ret;
}
} else {
// All good, no need to update
knot_rrset_deep_free(&new_nsec, 1, NULL);
return KNOT_EOK;
}
} else if (new_nsec) {
// Add new NSEC record
int ret = knot_changeset_add_rrset(out_ch, new_nsec,
KNOT_CHANGESET_ADD);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&new_nsec, 1, NULL);
return ret;
}
} else {
// Drop old, no longer needed
int ret = knot_nsec_changeset_remove(from, out_ch);
if (ret != KNOT_EOK) {
knot_rrset_deep_free(&new_nsec, 1, NULL);
return ret;
}
}
return KNOT_EOK;
}
/* - NSEC chain construction ------------------------------------------------ */
/*!
......@@ -253,245 +149,6 @@ static int connect_nsec_nodes(knot_node_t *a, knot_node_t *b,
KNOT_CHANGESET_ADD);
}
/* - NSEC chain fix --------------------------------------------------------- */
/*!
* \brief Handles node that has been deleted by DDNS/reload.
*
* \param node Deleted node
* \param fix_data Chain fix data.
*
* \return KNOT_E*, NSEC_NODE_SKIP
*/
static int handle_deleted_node(const knot_node_t *node,
chain_fix_data_t *fix_data)
{
if (node == NULL) {
// This node was deleted and used to be non-auth
assert(knot_node_is_non_auth(node));
return NSEC_NODE_SKIP;
}
int ret = knot_nsec_changeset_remove(node, fix_data->out_ch);
if (ret != KNOT_EOK) {
return ret;
}
/*!
* This node should be ignored, but we might need the next dname from
* previous node.
*/
if (fix_data->next_dname == NULL) {
const knot_rrset_t *old_nsec =
knot_node_rrset(node, KNOT_RRTYPE_NSEC);
assert(old_nsec);
fix_data->next_dname =
(knot_dname_t *)knot_rdata_nsec_next(old_nsec);
assert(fix_data->next_dname);
}
return NSEC_NODE_SKIP;
}
/*!
* \brief Fixes 'gaps' between old and new NSEC chain.
*
* \param fix_data Chain fix data.
* \param a Dname that should be connected to old chain.
* \param a_node Node that should be connected to old chain.
*
* \return KNOT_E*, or NSEC_NODE_RESET if needed.
*/
static int handle_nsec_next_dname(chain_fix_data_t *fix_data,
const knot_dname_t *a,
const knot_node_t *a_node)
{
assert(fix_data && fix_data->next_dname && a && a_node);
int ret = KNOT_EOK;
if (knot_dname_is_equal(fix_data->next_dname, a)) {
// We cannot point to the same record here, extract next->next
const knot_rrset_t *nsec_rrset =
knot_node_rrset(a_node, KNOT_RRTYPE_NSEC);
assert(nsec_rrset);
const knot_node_t *next_node =
knot_zone_contents_find_node(fix_data->zone,
knot_rdata_nsec_next(nsec_rrset));
assert(next_node);
update_last_used(fix_data, next_node->owner, next_node);
ret = update_nsec(a_node, next_node, fix_data->out_ch,
fix_data->ttl);
} else {
// We have no immediate previous node, connect broken chain
const knot_node_t *next_node =
knot_zone_contents_find_node(fix_data->zone,
fix_data->next_dname);
assert(next_node);
update_last_used(fix_data, next_node->owner, next_node);
ret = update_nsec(a_node, next_node, fix_data->out_ch,
fix_data->ttl);
}
fix_data->next_dname = NULL;
return ret == KNOT_EOK ? NSEC_NODE_RESET : ret;
}
/*!
* \brief Finds previous usable NSEC node in zone.
*
* \param z Zone to be searched.
* \param d DNAME to search for.
*
* \return Previous NSEC node for 'd'.
*/
static const knot_node_t *find_prev_nsec_node(const knot_zone_contents_t *z,
const knot_dname_t *d)
{
// Find previous node for the dname, return node that will be used later
const knot_node_t *prev_zone_node = knot_zone_contents_find_previous(z,
d);
bool nsec_node_found = !knot_node_is_non_auth(prev_zone_node) &&
!only_nsec_in_node(prev_zone_node);
while (!nsec_node_found) {
// Get previous node from zone tree
prev_zone_node =
knot_zone_contents_find_previous(z,
prev_zone_node->owner);
assert(prev_zone_node);
// Infinite loop check
if (knot_dname_is_equal(d, prev_zone_node->owner)) {
return prev_zone_node;
}
nsec_node_found = !knot_node_is_non_auth(prev_zone_node) &&
!only_nsec_in_node(prev_zone_node);
}
assert(nsec_node_found);
return prev_zone_node;
}
/*!
* \brief Fixes NSEC chain for 'a' and 'b'. 'a' is always < 'b'.
*
* \param a First DNAME from changeset.
* \param b Second DNAME from changeset.
* \param fix_data Chain fix data.
*
* \return KNOT_E*, NSEC_NODE_SKIP, NSEC_NODE_RESET if needed.
*/
static int fix_nsec_chain(knot_dname_t *a, knot_dname_t *b,
chain_fix_data_t *fix_data)
{
assert(b);
assert(fix_data);
// Get changed nodes from zone
const knot_node_t *b_node = knot_zone_contents_find_node(fix_data->zone,
b);
assert(b_node);
if (knot_node_is_non_auth(b_node)) {
// Nothing to fix in this node
return NSEC_NODE_SKIP;
}
const knot_node_t *a_node = knot_zone_contents_find_node(fix_data->zone,
a);
// Find previous node in zone
const knot_node_t *prev_zone_node = find_prev_nsec_node(fix_data->zone,
b);
if (prev_zone_node == NULL) {
return KNOT_ERROR;
}
// Handle removals
bool node_deleted = only_nsec_in_node(b_node);
if (node_deleted) {
/*!
* If DDNS only contains removals, we need at least
* one 'last_used_dname'.
*/
if (fix_data->last_used_dname == NULL) {
assert(fix_data->last_used_node == NULL);
update_last_used(fix_data, prev_zone_node->owner,
prev_zone_node);
}
return handle_deleted_node(b_node, fix_data);
}
// Find out whether the previous node is also part of the changeset.
bool dname_equal =
a && knot_dname_is_equal(prev_zone_node->owner, a);
if (dname_equal) {
// No valid data for the previous node, create the forward link
update_last_used(fix_data, b_node->owner, b_node);
return update_nsec(a_node, b_node, fix_data->out_ch,
fix_data->ttl);
} else {
// Use data from zone or next_dname
if (fix_data->next_dname) {
return handle_nsec_next_dname(fix_data, a, a_node);
}
// Previous node was not changed in DDNS, it has to have NSEC
const knot_rrset_t *nsec_rrset =
knot_node_rrset(prev_zone_node, KNOT_RRTYPE_NSEC);
assert(nsec_rrset);
const knot_node_t *next_node = b_node;
// Store next node for next iterations
fix_data->next_dname =
(knot_dname_t *)knot_rdata_nsec_next(nsec_rrset);
update_last_used(fix_data, next_node->owner, next_node);
// Fix NSEC
return update_nsec(prev_zone_node, next_node, fix_data->out_ch,
fix_data->ttl);
}
return KNOT_EOK;
}
/*!
* \brief Wrapper for iteration function to be used with NSEC,
* shortens the code a bit.
*/
static int fix_nsec_chain_wrap(knot_dname_t *a, knot_dname_t *a_hash,
knot_dname_t *b, knot_dname_t *b_hash,
chain_fix_data_t *d)
{
UNUSED(a_hash);
UNUSED(b_hash);
return fix_nsec_chain(a, b, d);
}
/*!
* \brief Finalizes NSEC chain.
*
* \param d Fix data.
*
* \return KNOT_E*
*/
static int chain_finalize_nsec(chain_fix_data_t *fix_data)
{
assert(fix_data);
assert(fix_data->last_used_dname && fix_data->next_dname);
const knot_node_t *from = fix_data->last_used_node;
assert(from);
const knot_node_t *to = NULL;
if (knot_dname_is_equal(fix_data->last_used_dname,
fix_data->zone->apex->owner)) {
// Everything but the apex deleted
to = fix_data->zone->apex;
} else if (knot_dname_is_equal(fix_data->last_used_dname,
fix_data->next_dname)) {
// NSEC cannot point to itself (except for the case above)
const knot_rrset_t *nsec_rrset =
knot_node_rrset(from, KNOT_RRTYPE_NSEC);
to = knot_zone_contents_find_node(fix_data->zone,
knot_rdata_nsec_next(nsec_rrset));
} else {
// Normal case
to = knot_zone_contents_find_node(fix_data->zone,
fix_data->next_dname);
}
assert(to);
return update_nsec(from, to, fix_data->out_ch, fix_data->ttl);
}
/* - API - iterations ------------------------------------------------------- */
/*!
......@@ -545,68 +202,6 @@ int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes,
callback(current, first, data);
}
/*!
* \brief Iterates sorted changeset and calls callback function - works for
* NSEC and NSEC3 chain.
*/
int knot_nsec_chain_iterate_fix(hattrie_t *nodes, chain_iterate_fix_cb callback,
chain_finalize_cb finalize,
chain_fix_data_t *data)
{
assert(nodes);
assert(callback);
bool sorted = true;
hattrie_iter_t *it = hattrie_iter_begin(nodes, sorted);
if (!it) {
return KNOT_ENOMEM;
}
if (hattrie_iter_finished(it)) {
hattrie_iter_free(it);
return KNOT_EINVAL;
}
knot_dname_t *previous_original = NULL;
knot_dname_t *previous_hashed = NULL;
knot_dname_t *current_original = NULL;
knot_dname_t *current_hashed = NULL;
int result = KNOT_EOK;
while (!hattrie_iter_finished(it)) {
signed_info_t *val = (signed_info_t *)(*hattrie_iter_val(it));
current_original = val->dname;
current_hashed = val->hashed_dname;
result = callback(previous_original, previous_hashed,
current_original, current_hashed, data);
if (result == NSEC_NODE_SKIP) {
// No NSEC should be created for 'current' node, skip
hattrie_iter_next(it);
} else if (result == NSEC_NODE_RESET) {
/*!
* Used previous node, call once again so that
* we don't lose this current node.
*/
previous_original = NULL;
previous_hashed = NULL;
} else if (result == KNOT_EOK) {
previous_original = current_original;
previous_hashed = current_hashed;
hattrie_iter_next(it);
} else {
hattrie_iter_free(it);
return result;
}
}
hattrie_iter_free(it);
return finalize(data);
}
/* - API - utility functions ------------------------------------------------ */
/*!
......@@ -696,7 +291,7 @@ bool knot_nsec_only_nsec_and_rrsigs_in_node(const knot_node_t *n)
return true;
}
/* - API - Chain creation and fix ------------------------------------------- */
/* - API - Chain creation --------------------------------------------------- */
/*!
* \brief Create new NSEC chain, add differences from current into a changeset.
......@@ -714,17 +309,3 @@ int knot_nsec_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
connect_nsec_nodes, &data);
}
/*!
* \brief Fixes NSEC chain after DDNS/reload
*/
int knot_nsec_fix_chain(hattrie_t *sorted_changes,
chain_fix_data_t *fix_data)
{
if (sorted_changes == NULL || fix_data == NULL) {
return KNOT_EINVAL;
}
hattrie_build_index(sorted_changes);
return knot_nsec_chain_iterate_fix(sorted_changes, fix_nsec_chain_wrap,
chain_finalize_nsec, fix_data);
}
......@@ -17,7 +17,6 @@
* \file nsec-chain.h
*
* \author Jan Vcelak <jan.vcelak@nic.cz> (chain creation)
* \author Jan Kadlec <jan.kadlec@nic.cz> (chain fix)
*
* \brief NSEC chain fix and creation.
*
......@@ -35,21 +34,6 @@
#include "knot/updates/changesets.h"
#include "libknot/dnssec/bitmap.h"
/*!
* \brief Parameters to be used when fixing NSEC(3) chain.
*/
typedef struct chain_fix_data {
const knot_zone_contents_t *zone; // Zone to fix
knot_changeset_t *out_ch; // Outgoing changes
const knot_dname_t *chain_start; // Possible new starting node
bool old_connected; // Marks old start connection
const knot_dname_t *last_used_dname; // Last dname used in chain
const knot_node_t *last_used_node; // Last covered node used in chain
knot_dname_t *next_dname; // Used to reconnect broken chain
const hattrie_t *sorted_changes; // Iterated trie
uint32_t ttl; // TTL for NSEC(3) records
} chain_fix_data_t;
/*!
* \brief Parameters to be used in connect_nsec_nodes callback.
*/
......@@ -64,18 +48,7 @@ typedef struct {
*/
enum {
NSEC_NODE_SKIP = 1,
NSEC_NODE_RESET = 2
};
/*!
* \brief Callback used when fixing NSEC chains.
*/
typedef int (*chain_iterate_fix_cb)(knot_dname_t *, knot_dname_t *,
knot_dname_t *, knot_dname_t *,
chain_fix_data_t *);
/*!
* \brief Callback used when finalizing NSEC chains.
*/
typedef int (*chain_finalize_cb)(chain_fix_data_t *);
/*!
* \brief Callback used when creating NSEC chains.
......@@ -116,25 +89,6 @@ int knot_nsec_chain_iterate_create(knot_zone_tree_t *nodes,
chain_iterate_create_cb callback,
nsec_chain_iterate_data_t *data);
/*!
* \brief Iterates sorted changeset and calls callback function - works for
* NSEC and NSEC3 chain.
*
* \note If the callback function returns anything other than KNOT_EOK, the
* iteration is terminated and the error code is propagated.
*
* \param nodes Tree to fix.
* \param callback Callback to call.
* \param finalize Finalization callback.
* \param data Data needed for fixing.
*
* \return KNOT_E*
*/
int knot_nsec_chain_iterate_fix(hattrie_t *nodes,
chain_iterate_fix_cb callback,
chain_finalize_cb finalize,
chain_fix_data_t *data);
/*!
* \brief Add entry for removed NSEC(3) and its RRSIG to the changeset.
*
......@@ -169,16 +123,4 @@ bool knot_nsec_only_nsec_and_rrsigs_in_node(const knot_node_t *n);
int knot_nsec_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
knot_changeset_t *changeset);
/*!
* \brief Fixes NSEC chain after DDNS/reload
*
* \param sorted_changes Sorted changes created by changeset sign function.
* \param fix_data Chain fix data.
*
* \return KNOT_E*
*/
int knot_nsec_fix_chain(hattrie_t *sorted_changes,
chain_fix_data_t *fix_data);
#endif // _KNOT_DNSSEC_NSEC_CHAIN_FIX_H_
This diff is collapsed.
......@@ -16,10 +16,9 @@
/*!
* \file nsec3-chain-fix.h
*
* \author Jan Kadlec <jan.kadlec@nic.cz> (chain fix)
* \author Jan Vcelak <jan.vcelak@nic.cz> (chain creation)
*
* \brief NSEC3 chain fix and creation.
* \brief NSEC3 chain creation.
*
* \addtogroup dnssec
* @{
......@@ -43,14 +42,4 @@
int knot_nsec3_create_chain(const knot_zone_contents_t *zone, uint32_t ttl,
knot_changeset_t *changeset);
/*!
* \brief Fixes NSEC3 chain after DDNS/reload.
*
* \param sorted_changes Sorted changes created by changeset sign function.
* \param fix_data Chain fix data.
*
* \return KNOT_E*
*/
int knot_nsec3_fix_chain(hattrie_t *sorted_changes, chain_fix_data_t *fix_data);
#endif // _KNOT_DNSSEC_NSEC3_CHAIN_FIX_H_
......@@ -182,10 +182,9 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
knot_changeset_t *out_ch,
knot_update_serial_t soa_up,
uint32_t *refresh_at,
uint32_t new_serial,
hattrie_t **sorted_changes)
uint32_t new_serial)
{
if (!refresh_at || !sorted_changes) {
if (!refresh_at) {
return KNOT_EINVAL;
}
......@@ -210,7 +209,7 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
}
// Sign added and removed RRSets in changeset
ret = knot_zone_sign_changeset(zone, in_ch, out_ch, sorted_changes,
ret = knot_zone_sign_changeset(zone, in_ch, out_ch,
&zone_keys, &policy);
if (ret != KNOT_EOK) {
log_zone_error("%s Failed to sign changeset (%s)\n", msgpref,
......@@ -220,13 +219,10 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
return ret;
}
assert(sorted_changes);
// Fix NSEC(3) chain
ret = knot_zone_fix_nsec_chain(zone,
*sorted_changes, out_ch,
&zone_keys, &policy);
// Create NSEC(3) chain
ret = knot_zone_create_nsec_chain(zone, out_ch, &zone_keys, &policy);
if (ret != KNOT_EOK) {
log_zone_error("%s Failed to fix NSEC(3) chain (%s)\n",
log_zone_error("%s Failed to create NSEC(3) chain (%s)\n",
msgpref, knot_strerror(ret));
knot_free_zone_keys(&zone_keys);
free(msgpref);
......
......@@ -75,7 +75,6 @@ int knot_dnssec_zone_sign_force(knot_zone_contents_t *zone, conf_zone_t *zone_co
* \param soa_up SOA serial update policy.
* \param refresh_at Signature refresh time of the new signatures.
* \param new_serial New SOA serial.
* \param sorted_changes Info about made changes, used for partial adjustment.
*
* \return Error code, KNOT_EOK if successful.
*/
......@@ -84,8 +83,7 @@ int knot_dnssec_sign_changeset(const knot_zone_contents_t *zone,
const knot_changeset_t *in_ch,
knot_changeset_t *out_ch,
knot_update_serial_t soa_up,