]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
ctl: zone-diff works with UPDATE_NO_CHSET
authorLibor Peltan <libor.peltan@nic.cz>
Thu, 12 Jun 2025 09:39:46 +0000 (11:39 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Thu, 31 Jul 2025 14:42:14 +0000 (16:42 +0200)
src/knot/ctl/commands.c
src/knot/updates/zone-update.c
src/knot/updates/zone-update.h
tests-extra/tests/zone/external_vldt/test.py

index 25311db9674f8f244e9f8712e6077d6dc8f8e516..ffb0b07b9f275e51962f7dbb4014262a2dc5396b 100644 (file)
@@ -945,10 +945,7 @@ static int zone_txn_commit_l(zone_t *zone, _unused_ ctl_args_t *args)
        }
 
        // NOOP if empty changeset/contents.
-       if (((zone->control_update->flags & UPDATE_INCREMENTAL) &&
-            changeset_empty(&zone->control_update->change)) ||
-           ((zone->control_update->flags & UPDATE_FULL) &&
-            zone_contents_is_empty(zone->control_update->new_cont))) {
+       if (zone_update_no_change(zone->control_update)) {
                zone_control_clear(zone);
                return KNOT_EOK;
        }
@@ -1061,7 +1058,7 @@ static int init_send_ctx(send_ctx_t *ctx, const knot_dname_t *zone_name,
        return KNOT_EOK;
 }
 
-static int send_rrset(knot_rrset_t *rrset, send_ctx_t *ctx)
+static int send_rrset(const knot_rrset_t *rrset, send_ctx_t *ctx)
 {
        if (rrset->type != KNOT_RRTYPE_RRSIG) {
                int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u", rrset->ttl);
@@ -1098,6 +1095,16 @@ static int send_rrset(knot_rrset_t *rrset, send_ctx_t *ctx)
        return KNOT_EOK;
 }
 
+static int send_rrset_callback(const knot_rrset_t *rrset, void *ctx_void)
+{
+       send_ctx_t *ctx = ctx_void;
+       char *owner = knot_dname_to_str(ctx->owner, rrset->owner, sizeof(ctx->owner));
+       if (owner == NULL) {
+               return KNOT_EINVAL;
+       }
+       return send_rrset(rrset, ctx);
+}
+
 static int send_node(zone_node_t *node, void *ctx_void)
 {
        send_ctx_t *ctx = ctx_void;
@@ -1253,66 +1260,6 @@ static int zone_txn_get(zone_t *zone, ctl_args_t *args)
        return ret;
 }
 
-static int send_changeset_part(changeset_t *ch, send_ctx_t *ctx, bool from)
-{
-       ctx->data[KNOT_CTL_IDX_FILTERS] = from ? CTL_FILTER_DIFF_REM_R : CTL_FILTER_DIFF_ADD_R;
-
-       // Send SOA only if explicitly changed.
-       if (ch->soa_to != NULL) {
-               knot_rrset_t *soa = from ? ch->soa_from : ch->soa_to;
-               assert(soa);
-
-               char *owner = knot_dname_to_str(ctx->owner, soa->owner, sizeof(ctx->owner));
-               if (owner == NULL) {
-                       return KNOT_EINVAL;
-               }
-
-               int ret = send_rrset(soa, ctx);
-               if (ret != KNOT_EOK) {
-                       return ret;
-               }
-       }
-
-       // Send other records.
-       changeset_iter_t it;
-       int ret = from ? changeset_iter_rem(&it, ch) : changeset_iter_add(&it, ch);
-       if (ret != KNOT_EOK) {
-               return ret;
-       }
-
-       knot_rrset_t rrset = changeset_iter_next(&it);
-       while (!knot_rrset_empty(&rrset)) {
-               char *owner = knot_dname_to_str(ctx->owner, rrset.owner, sizeof(ctx->owner));
-               if (owner == NULL) {
-                       changeset_iter_clear(&it);
-                       return KNOT_EINVAL;
-               }
-
-               ret = send_rrset(&rrset, ctx);
-               if (ret != KNOT_EOK) {
-                       changeset_iter_clear(&it);
-                       return ret;
-               }
-
-               rrset = changeset_iter_next(&it);
-       }
-       changeset_iter_clear(&it);
-
-       return KNOT_EOK;
-}
-
-static int send_changeset(changeset_t *ch, send_ctx_t *ctx)
-{
-       // First send 'from' changeset part.
-       int ret = send_changeset_part(ch, ctx, true);
-       if (ret != KNOT_EOK) {
-               return ret;
-       }
-
-       // Second send 'to' changeset part.
-       return send_changeset_part(ch, ctx, false);
-}
-
 static int zone_txn_diff_l(zone_t *zone, ctl_args_t *args)
 {
        if (zone->control_update == NULL) {
@@ -1327,11 +1274,16 @@ static int zone_txn_diff_l(zone_t *zone, ctl_args_t *args)
 
        send_ctx_t *ctx = &ctl_globals[args->thread_idx].send_ctx;
        int ret = init_send_ctx(ctx, zone->name, args);
-       if (ret != KNOT_EOK) {
-               return ret;
+       if (ret == KNOT_EOK) {
+               ctx->data[KNOT_CTL_IDX_FILTERS] = CTL_FILTER_DIFF_REM_R;
+               ret = zone_update_foreach(zone->control_update, false, send_rrset_callback, ctx);
+       }
+       if (ret == KNOT_EOK) {
+               ctx->data[KNOT_CTL_IDX_FILTERS] = CTL_FILTER_DIFF_ADD_R;
+               ret = zone_update_foreach(zone->control_update, true, send_rrset_callback, ctx);
        }
 
-       return send_changeset(&zone->control_update->change, ctx);
+       return ret;
 }
 
 static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
index e020e433738e1b33058dc0b377c691f392da760d..0170333278a96df8b10b4c77638fc469e17113ba 100644 (file)
@@ -1101,9 +1101,88 @@ bool zone_update_no_change(zone_update_t *update)
        } else if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
                return changeset_empty(&update->change);
        } else {
-               /* This branch does not make much sense and FULL update will most likely
-                * be a change every time anyway, just return false. */
-               return false;
+               return zone_contents_is_empty(update->new_cont);
+       }
+}
+
+static int rrset_foreach(zone_node_t *n, bool subtract_counterpart,
+                         int idx, uint16_t rrtype, // two exclusive possibilities how to define which rrset in the node
+                         rrset_cb_t cb, void *ctx)
+{
+       knot_rrset_t rr = (rrtype == KNOT_RRTYPE_ANY) ? node_rrset_at(n, idx) : node_rrset(n, rrtype);
+       knot_rrset_t rrc = subtract_counterpart ? node_rrset(binode_counterpart(n), rr.type) : (knot_rrset_t){ 0 };
+       if (rrtype == KNOT_RRTYPE_ANY && rr.type == KNOT_RRTYPE_SOA) {
+               return KNOT_EOK; // ignore SOA if rrset specified by idx
+       } else if (knot_rrset_empty(&rrc)) {
+               return cb(&rr, ctx);
+       } else if (knot_rdataset_subset(&rr.rrs, &rrc.rrs)) {
+               return KNOT_EOK;
+       } else {
+               knot_rdataset_t rd_copy = { 0 };
+               int ret = knot_rdataset_copy(&rd_copy, &rr.rrs, NULL);
+               if (ret == KNOT_EOK) {
+                       ret = knot_rdataset_subtract(&rd_copy, &rrc.rrs, NULL);
+               }
+               if (ret == KNOT_EOK) {
+                       rr.rrs = rd_copy;
+                       ret = cb(&rr, ctx);
+               }
+               knot_rdataset_clear(&rd_copy, NULL);
+               return ret;
+       }
+}
+
+static int trees_foreach(zone_tree_t *nodes, zone_tree_t *nsec3_nodes, bool subtract_counterparts,
+                         rrset_cb_t cb, void *ctx)
+{
+       zone_tree_it_t it = { 0 };
+       int ret = zone_tree_it_double_begin(nodes, nsec3_nodes, &it);
+       while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+               zone_node_t *n = zone_tree_it_val(&it);
+               for (int i = 0; i < n->rrset_count && ret == KNOT_EOK; i++) {
+                       ret = rrset_foreach(n, subtract_counterparts, i, KNOT_RRTYPE_ANY, cb, ctx);
+               }
+               zone_tree_it_next(&it);
+       }
+       zone_tree_it_free(&it);
+       return ret;
+}
+
+int zone_update_foreach(zone_update_t *update, bool additions, rrset_cb_t cb, void *ctx)
+{
+       if (update == NULL) {
+               return KNOT_EINVAL;
+       }
+
+       if (update->flags & UPDATE_NO_CHSET) {
+               zone_diff_t diff;
+               get_zone_diff(&diff, update);
+               if (!additions) {
+                       zone_diff_reverse(&diff);
+               }
+
+               int ret = rrset_foreach(diff.apex, true, 0, KNOT_RRTYPE_SOA, cb, ctx);
+               if (ret == KNOT_EOK) {
+                       ret = trees_foreach(&diff.nodes, &diff.nsec3s, true, cb, ctx);
+               }
+               return ret;
+       } else if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+               knot_rrset_t *soa = additions ? update->change.soa_to : update->change.soa_from;
+               zone_contents_t *c = additions ? update->change.add : update->change.remove;
+               int ret = (soa == NULL) ? KNOT_EOK : cb(soa, ctx);
+               if (ret == KNOT_EOK) {
+                       ret = trees_foreach(c->nodes, c->nsec3_nodes, false, cb, ctx);
+               }
+               return ret;
+       } else if (additions) {
+               knot_rrset_t soa = node_rrset(update->new_cont->apex, KNOT_RRTYPE_SOA);
+               int ret = knot_rrset_empty(&soa) ? KNOT_EOK : cb(&soa, ctx);
+               if (ret == KNOT_EOK) {
+                       ret = trees_foreach(update->new_cont->nodes, update->new_cont->nsec3_nodes, false, cb, ctx);
+               }
+               return ret;
+       } else {
+               return KNOT_EOK;
        }
 }
 
index 3229eed62519a3bbc6f28244c1e1e5db09aec212..e1560e18437707579066e7be2f19b92128bbca38 100644 (file)
@@ -300,6 +300,19 @@ int zone_update_commit(conf_t *conf, zone_update_t *update);
  */
 bool zone_update_no_change(zone_update_t *update);
 
+typedef int (*rrset_cb_t)(const knot_rrset_t *, void *);
+/*!
+ * \brief Run callback for every removed/added RRset in this update.
+ *
+ * \param update       Zone update in question.
+ * \param additions    Apply on addition (removals otherwise).
+ * \param cb           Callback to call for each changed RRset.
+ * \param ctx          Arbitrary context for the callback.
+ *
+ * \return KNOT_E* emitted by the callback or error in iteration.
+ */
+int zone_update_foreach(zone_update_t *update, bool additions, rrset_cb_t cb, void *ctx);
+
 /*!
  * \brief Return whether apex DNSKEY, CDNSKEY, or CDS is updated.
  */
index 1a90f84ae09febeb21efc922e9db4a109d9f4fd5..3f8e2a7879c01b6c2054a095be2ff9f905a7a1df 100644 (file)
@@ -38,6 +38,7 @@ resp = slave.dig(ZONE, "SOA")
 resp.check(rcode="SERVFAIL")
 resp.check_count(0, "SOA")
 
+slave.ctl("zone-diff " + ZONE)
 slave.ctl("zone-commit " + ZONE)
 t.sleep(2)
 resp = slave.dig(ZONE, "SOA")
@@ -58,6 +59,8 @@ serial = master.zone_wait(zone, serial)
 
 t.sleep(2)
 log_count_expect(slave, LOG, 3)
+slave.ctl("zone-diff " + ZONE)
+
 slave.ctl("zone-commit " + ZONE)
 t.sleep(2)
 resp = slave.dig(ZONE, "SOA")
@@ -78,6 +81,7 @@ serial = master.zone_wait(zone, serial)
 
 t.sleep(2)
 log_count_expect(slave, LOG, 5)
+slave.ctl("zone-diff " + ZONE)
 slave.ctl("zone-commit " + ZONE)
 t.sleep(2)
 resp = slave.dig(ZONE, "SOA")