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));
}
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) {
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) {
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 {
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) {
}
}
+ // 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);
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)
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
/*!
* \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);
*/
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.
*
#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;
}
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;
/* 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);
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;
}
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);
}
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);
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
* \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.
*/
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.
*
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)
{
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;
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;
*/
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);
* 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++) {
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)) {
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. */
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.
}
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;
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;
}
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
* \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.