Commit 77f8a9ee authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

load: ignore dnssec-related records if load difference and signing enabled

The difference ignores RRSIGs, NSECs, and NSEC3s. This is motivated by users
willing to have zonefile w/o DNSSEC, but signed zone.

If the final update consists only from changed SOA serial and RRSIG-SOA, clear it.
This happens after automatic serial icrement, we avoid increment on simple reload.
parent f8cd4fec
......@@ -119,6 +119,7 @@ int event_load(conf_t *conf, zone_t *zone)
ret = zone_load_journal(conf, zone, zf_conts);
if (ret != KNOT_EOK) {
zone_contents_deep_free(zf_conts);
zf_conts = NULL;
log_zone_warning(zone->name, "failed to load journal (%s)",
knot_strerror(ret));
}
......@@ -138,6 +139,8 @@ int event_load(conf_t *conf, zone_t *zone)
val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
bool dnssec_enable = conf_bool(&val), zu_from_zf_conts = false;
zone_update_t up = { 0 };
bool ignore_dnssec = ((zf_from == ZONEFILE_LOAD_DIFF || zf_from == ZONEFILE_LOAD_DIFSE)
&& dnssec_enable);
// Create zone_update structure according to current state.
if (old_contents_exist) {
......@@ -153,7 +156,9 @@ int event_load(conf_t *conf, zone_t *zone)
zu_from_zf_conts = true;
} else {
// compute ZF diff and if success, apply it
ret = zone_update_from_differences(&up, zone, zone->contents, zf_conts, UPDATE_INCREMENTAL);
ret = zone_update_from_differences(&up, zone, zone->contents, zf_conts, UPDATE_INCREMENTAL, ignore_dnssec);
zone_contents_deep_free(zf_conts);
zf_conts = NULL;
}
} else {
if (journal_conts != NULL && zf_from != ZONEFILE_LOAD_WHOLE) {
......@@ -162,16 +167,18 @@ int event_load(conf_t *conf, zone_t *zone)
ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL);
} else {
// load zone-in-journal, compute ZF diff and if success, apply it
ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts, UPDATE_INCREMENTAL);
ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts, UPDATE_INCREMENTAL, ignore_dnssec);
zone_contents_deep_free(zf_conts);
zf_conts = NULL;
if (ret == KNOT_ESEMCHECK || ret == KNOT_ERANGE) {
log_zone_warning(zone->name,
"zone file changed with SOA serial %s, "
"ignoring zone file and loading from journal",
(ret == KNOT_ESEMCHECK ? "unupdated" : "decreased"));
zone_contents_deep_free(zf_conts);
ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL);
} else {
zone_contents_deep_free(journal_conts);
journal_conts = NULL;
}
}
} else {
......@@ -212,17 +219,6 @@ int event_load(conf_t *conf, zone_t *zone)
zf_conts = NULL;
journal_conts = NULL;
// If the change is only automatically incremented SOA serial, make it no change.
if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & UPDATE_INCREMENTAL) &&
changeset_differs_just_serial(&up.change)) {
uint32_t orig_ser = knot_soa_serial(&up.change.soa_from->rrs);
ret = changeset_remove_addition(&up.change, up.change.soa_to);
zone_contents_set_soa_serial(up.new_cont, orig_ser);
if (ret != KNOT_EOK) {
goto cleanup;
}
}
// Sign zone using DNSSEC if configured.
zone_sign_reschedule_t dnssec_refresh = { .allow_rollover = true, .allow_nsec3resalt = true, };
if (dnssec_enable) {
......@@ -239,6 +235,24 @@ int event_load(conf_t *conf, zone_t *zone)
}
}
// If the change is only automatically incremented SOA serial, make it no change.
if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & UPDATE_INCREMENTAL) &&
changeset_differs_just_serial(&up.change)) {
changeset_t *cpy = changeset_clone(&up.change);
if (cpy == NULL) {
ret = KNOT_ENOMEM;
goto cleanup;
}
ret = zone_update_apply_changeset_reverse(&up, cpy);
changeset_free(cpy);
if (ret == KNOT_EOK) {
ret = changeset_remove_addition(&up.change, up.change.soa_to);
}
if (ret != KNOT_EOK) {
goto cleanup;
}
}
// Commit zone_update back to zone (including journal update, rcu,...).
ret = zone_update_commit(conf, &up);
zone_update_clear(&up);
......
......@@ -537,17 +537,26 @@ bool changeset_differs_just_serial(const changeset_t *ch)
return false;
}
if (!zone_contents_is_empty(ch->remove) || !zone_contents_is_empty(ch->add)) {
return false;
}
knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL);
knot_soa_serial_set(&soa_to_cpy->rrs, knot_soa_serial(&ch->soa_from->rrs));
knot_soa_serial_set(soa_to_cpy->rrs.rdata, knot_soa_serial(ch->soa_from->rrs.rdata));
bool res = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE);
bool ret = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE);
knot_rrset_free(soa_to_cpy, NULL);
return res;
changeset_iter_t itt;
changeset_iter_all(&itt, ch);
knot_rrset_t rrset = changeset_iter_next(&itt);
while (!knot_rrset_empty(&rrset) && ret) {
if (rrset.type != KNOT_RRTYPE_RRSIG || rrset.rrs.count != 1 ||
knot_rrsig_type_covered(rrset.rrs.rdata) != KNOT_RRTYPE_SOA) {
ret = false;
}
rrset = changeset_iter_next(&itt);
}
changeset_iter_clear(&itt);
return ret;
}
int changeset_to_contents(changeset_t *ch, zone_contents_t **out)
......@@ -648,6 +657,49 @@ void changeset_clear(changeset_t *ch)
free(ch->data);
}
changeset_t *changeset_clone(const changeset_t *ch)
{
if (ch == NULL) {
return NULL;
}
changeset_t *res = changeset_new(ch->soa_from->owner);
if (res == NULL) {
return NULL;
}
res->soa_from = knot_rrset_copy(ch->soa_from, NULL);
res->soa_to = knot_rrset_copy(ch->soa_to, NULL);
int ret = KNOT_EOK;
changeset_iter_t itt;
changeset_iter_rem(&itt, ch);
knot_rrset_t rr = changeset_iter_next(&itt);
while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
ret = changeset_add_removal(res, &rr, 0);
rr = changeset_iter_next(&itt);
}
changeset_iter_clear(&itt);
changeset_iter_add(&itt, ch);
rr = changeset_iter_next(&itt);
while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
ret = changeset_add_addition(res, &rr, 0);
rr = changeset_iter_next(&itt);
}
changeset_iter_clear(&itt);
if ((ch->soa_from != NULL && res->soa_from == NULL) ||
(ch->soa_to != NULL && res->soa_to == NULL) ||
ret != KNOT_EOK) {
changeset_free(res);
return NULL;
}
return res;
}
void changeset_free(changeset_t *ch)
{
changeset_clear(ch);
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2018 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
......@@ -161,10 +161,13 @@ int changeset_cancelout(changeset_t *ch);
/*!
* \brief Check the changes and SOA, ignoring possibly updated SOA serial.
*
* \note Also tolerates changed RRSIG of SOA.
*
* \param ch Changeset in question.
*
* \return false if the changeset changes other records than SOA, or some SOA field other than serial changed
* true otherwise
* \retval false If the changeset changes other records than SOA, or some SOA field
* other than serial changed.
* \retval true Otherwise.
*/
bool changeset_differs_just_serial(const changeset_t *ch);
......@@ -215,6 +218,15 @@ void changesets_free(list_t *chgs);
*/
void changeset_clear(changeset_t *ch);
/*!
* \brief Copy changeset to newly allocated space, all rrsigs are copied.
*
* \param ch Changeset to be copied.
*
* \return a copy, or NULL if error.
*/
changeset_t *changeset_clone(const changeset_t *ch);
/*!
* \brief Frees single changeset.
*
......
......@@ -26,9 +26,9 @@
#include <urcu.h>
static int init_incremental(zone_update_t *update, zone_t *zone)
static int init_incremental(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents)
{
if (zone->contents == NULL) {
if (old_contents == NULL) {
return KNOT_EINVAL;
}
......@@ -39,7 +39,7 @@ static int init_incremental(zone_update_t *update, zone_t *zone)
update->new_cont_deep_copy = false;
ret = apply_prepare_zone_copy(zone->contents, &update->new_cont);
ret = apply_prepare_zone_copy(old_contents, &update->new_cont);
if (ret != KNOT_EOK) {
changeset_clear(&update->change);
return ret;
......@@ -50,7 +50,7 @@ static int init_incremental(zone_update_t *update, zone_t *zone)
/* Copy base SOA RR. */
update->change.soa_from =
node_create_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
node_create_rrset(old_contents->apex, KNOT_RRTYPE_SOA);
if (update->change.soa_from == NULL) {
zone_contents_free(update->new_cont);
changeset_clear(&update->change);
......@@ -96,11 +96,10 @@ static int replace_soa(zone_contents_t *contents, const knot_rrset_t *rr)
return ret;
}
/* ------------------------------- API -------------------------------------- */
int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags)
int init_base(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents,
zone_update_flags_t flags)
{
if (update == NULL || zone == NULL) {
if (update == NULL || zone == NULL || (old_contents == NULL && (flags & UPDATE_INCREMENTAL))) {
return KNOT_EINVAL;
}
......@@ -117,7 +116,7 @@ int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t fl
int ret = KNOT_EINVAL;
if (flags & UPDATE_INCREMENTAL) {
ret = init_incremental(update, zone);
ret = init_incremental(update, zone, old_contents);
} else if (flags & UPDATE_FULL) {
ret = init_full(update, zone);
}
......@@ -128,45 +127,49 @@ int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t fl
return ret;
}
/* ------------------------------- API -------------------------------------- */
int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags)
{
return init_base(update, zone, zone->contents, flags);
}
int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont,
zone_contents_t *new_cont, zone_update_flags_t flags)
zone_contents_t *new_cont, zone_update_flags_t flags, bool ignore_dnssec)
{
if (update == NULL || zone == NULL || new_cont == NULL ||
!(flags & UPDATE_INCREMENTAL) || (flags & UPDATE_FULL)) {
return KNOT_EINVAL;
}
memset(update, 0, sizeof(*update));
update->zone = zone;
mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE);
update->flags = flags;
update->new_cont = new_cont;
update->new_cont_deep_copy = true;
changeset_t diff;
int ret = changeset_init(&diff, zone->name);
if (ret != KNOT_EOK) {
return ret;
}
update->a_ctx = calloc(1, sizeof(*update->a_ctx));
if (update->a_ctx == NULL) {
return KNOT_ENOMEM;
ret = zone_contents_diff(old_cont, new_cont, &diff, ignore_dnssec);
if (ret != KNOT_EOK && ret != KNOT_ENODIFF && ret != KNOT_ESEMCHECK) {
changeset_clear(&diff);
return ret;
}
int ret = changeset_init(&update->change, zone->name);
ret = init_base(update, zone, old_cont, flags);
if (ret != KNOT_EOK) {
free(update->a_ctx);
changeset_clear(&diff);
return ret;
}
ret = zone_contents_diff(old_cont, new_cont, &update->change);
if (ret != KNOT_EOK && ret != KNOT_ENODIFF && ret != KNOT_ESEMCHECK) {
free(update->a_ctx);
changeset_clear(&update->change);
bool diff_semcheck = (ret == KNOT_ESEMCHECK);
ret = zone_update_apply_changeset(update, &diff);
changeset_clear(&diff);
if (ret != KNOT_EOK) {
zone_update_clear(update);
return ret;
}
uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0;
apply_init_ctx(update->a_ctx, update->new_cont, apply_flags);
if (ret == KNOT_ESEMCHECK) {
if (diff_semcheck) {
ret = zone_update_increment_soa(update, conf());
if (ret != KNOT_EOK) {
zone_update_clear(update);
......@@ -522,6 +525,16 @@ int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes)
return ret;
}
int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes)
{
changeset_t reverse;
reverse.remove = changes->add;
reverse.add = changes->remove;
reverse.soa_from = changes->soa_to;
reverse.soa_to = changes->soa_from;
return zone_update_apply_changeset(update, &reverse);
}
static int set_new_soa(zone_update_t *update, unsigned serial_policy)
{
assert(update);
......
/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2018 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
......@@ -76,7 +76,7 @@ int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t fl
* \return KNOT_E*
*/
int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont,
zone_contents_t *new_cont, zone_update_flags_t flags);
zone_contents_t *new_cont, zone_update_flags_t flags, bool ignore_dnssec);
/*!
* \brief Inits a zone update based on new zone contents.
......@@ -228,6 +228,16 @@ int zone_update_apply_changeset(zone_update_t *update, const changeset_t *change
*/
int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes);
/*!
* \brief Applies the changeset in reverse, rsets from REM section are added and from ADD section removed.
*
* \param update Zone update.
* \param changes Changes to be un-done.
*
* \return KNOT_E*
*/
int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes);
/*!
* \brief Increment SOA serial (according to cofigured policy) in the update.
*
......
......@@ -25,8 +25,21 @@
struct zone_diff_param {
zone_tree_t *nodes;
changeset_t *changeset;
bool ignore_dnssec;
};
static bool rrset_is_dnssec(const knot_rrset_t *rrset)
{
switch (rrset->type) {
case KNOT_RRTYPE_RRSIG:
case KNOT_RRTYPE_NSEC:
case KNOT_RRTYPE_NSEC3:
return true;
default:
return false;
}
}
static int load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2,
changeset_t *changeset)
{
......@@ -75,11 +88,16 @@ static int load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2,
return KNOT_EOK;
}
static int add_node(const zone_node_t *node, changeset_t *changeset)
static int add_node(const zone_node_t *node, changeset_t *changeset, bool ignore_dnssec)
{
/* Add all rrsets from node. */
for (unsigned i = 0; i < node->rrset_count; i++) {
knot_rrset_t rrset = node_rrset_at(node, i);
if (ignore_dnssec && rrset_is_dnssec(&rrset)) {
continue;
}
int ret = changeset_add_addition(changeset, &rrset, 0);
if (ret != KNOT_EOK) {
return ret;
......@@ -89,11 +107,16 @@ static int add_node(const zone_node_t *node, changeset_t *changeset)
return KNOT_EOK;
}
static int remove_node(const zone_node_t *node, changeset_t *changeset)
static int remove_node(const zone_node_t *node, changeset_t *changeset, bool ignore_dnssec)
{
/* Remove all the RRSets of the node. */
for (unsigned i = 0; i < node->rrset_count; i++) {
knot_rrset_t rrset = node_rrset_at(node, i);
if (ignore_dnssec && rrset_is_dnssec(&rrset)) {
continue;
}
int ret = changeset_add_removal(changeset, &rrset, 0);
if (ret != KNOT_EOK) {
return ret;
......@@ -204,7 +227,7 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data)
*/
zone_node_t *node_in_second_tree = zone_tree_get(param->nodes, node->owner);
if (node_in_second_tree == NULL) {
return remove_node(node, param->changeset);
return remove_node(node, param->changeset, param->ignore_dnssec);
}
assert(node_in_second_tree != node);
......@@ -215,7 +238,7 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data)
* If there are no RRs in the first tree, all of the RRs
* in the second tree will have to be inserted to ADD section.
*/
return add_node(node_in_second_tree, param->changeset);
return add_node(node_in_second_tree, param->changeset, param->ignore_dnssec);
}
for (unsigned i = 0; i < node->rrset_count; i++) {
......@@ -227,6 +250,10 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data)
continue;
}
if (param->ignore_dnssec && rrset_is_dnssec(&rrset)) {
continue;
}
knot_rrset_t rrset_from_second_node =
node_rrset(node_in_second_tree, rrset.type);
if (knot_rrset_empty(&rrset_from_second_node)) {
......@@ -255,6 +282,10 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data)
continue;
}
if (param->ignore_dnssec && rrset_is_dnssec(&rrset)) {
continue;
}
knot_rrset_t rrset_from_first_node = node_rrset(node, rrset.type);
if (knot_rrset_empty(&rrset_from_first_node)) {
/* RRSet has been added. Make a copy and add. */
......@@ -291,19 +322,20 @@ static int add_new_nodes(zone_node_t **node_ptr, void *data)
zone_node_t *new_node = zone_tree_get(param->nodes, node->owner);
if (new_node == NULL) {
assert(node);
return add_node(node, param->changeset);
return add_node(node, param->changeset, param->ignore_dnssec);
}
return KNOT_EOK;
}
static int load_trees(zone_tree_t *nodes1, zone_tree_t *nodes2,
changeset_t *changeset)
changeset_t *changeset, bool ignore_dnssec)
{
assert(changeset);
struct zone_diff_param param = {
.changeset = changeset
.changeset = changeset,
.ignore_dnssec = ignore_dnssec,
};
// Traverse one tree, compare every node, each RRSet with its rdata.
......@@ -319,7 +351,7 @@ static int load_trees(zone_tree_t *nodes1, zone_tree_t *nodes2,
}
int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2,
changeset_t *changeset)
changeset_t *changeset, bool ignore_dnssec)
{
if (zone1 == NULL || zone2 == NULL || changeset == NULL) {
return KNOT_EINVAL;
......@@ -330,12 +362,12 @@ int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone
return ret_soa;
}
int ret = load_trees(zone1->nodes, zone2->nodes, changeset);
int ret = load_trees(zone1->nodes, zone2->nodes, changeset, ignore_dnssec);
if (ret != KNOT_EOK) {
return ret;
}
ret = load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes, changeset);
ret = load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes, changeset, ignore_dnssec);
if (ret != KNOT_EOK) {
return ret;
}
......@@ -353,5 +385,5 @@ int zone_tree_add_diff(zone_tree_t *t1, zone_tree_t *t2, changeset_t *changeset)
return KNOT_EINVAL;
}
return load_trees(t1, t2, changeset);
return load_trees(t1, t2, changeset, false);
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2018 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
......@@ -23,7 +23,7 @@
* \brief Create diff between two zone trees.
* */
int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2,
changeset_t *changeset);
changeset_t *changeset, bool ignore_dnssec);
/*!
* \brief Add diff between two zone trees into the changeset.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment