]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
zonemd: create hash and bump serial when turned on
authorLibor Peltan <libor.peltan@nic.cz>
Fri, 20 Aug 2021 13:17:09 +0000 (15:17 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Tue, 7 Sep 2021 13:38:52 +0000 (15:38 +0200)
src/knot/updates/zone-update.c
src/knot/zone/digest.c
src/knot/zone/digest.h
tests-extra/tests/zone/zonemd_flush/test.py [new file with mode: 0644]

index 4636b1bcd794eda77110551d77983c309f517760..fac41fb465ffea290537afab79ec6d5544ed88ee 100644 (file)
@@ -884,8 +884,16 @@ int zone_update_commit(conf_t *conf, zone_update_t *update)
        bool dnssec = conf_bool(&val);
        val = conf_zone_get(conf, C_ZONEMD_GENERATE, update->zone->name);
        unsigned digest_alg = conf_opt(&val);
-       if (digest_alg != ZONE_DIGEST_NONE && !dnssec && // in case of DNSSEC, digest is part of signing routine
-           ((update->flags & UPDATE_FULL) || zone_update_to(update) != NULL)) {
+       bool do_digest = (digest_alg != ZONE_DIGEST_NONE && !dnssec); // in case of DNSSEC, digest is part of signing routine
+       if (do_digest && !(update->flags & UPDATE_FULL) && zone_update_to(update) == NULL) {
+               // cold start, decide if (digest & bump SOA) or NOOP
+               if (zone_contents_digest_exists(update->new_cont, digest_alg)) { // yes, computing hash twice, but in rare situation: cold start & exists & invalid
+                       do_digest = false;
+               } else {
+                       ret = zone_update_increment_soa(update, conf);
+               }
+       }
+       if (do_digest && ret == KNOT_EOK) {
                ret = zone_update_add_digest(update, digest_alg, false);
        }
        if (ret != KNOT_EOK) {
index 8587c8be4f96d16823325ced13ea567ab0951124..81f44e024025c38cd6d3d37c51be2ed8bb6848e2 100644 (file)
@@ -173,6 +173,16 @@ static int verify_zonemd(const knot_rdata_t *zonemd, const zone_contents_t *cont
        return ret;
 }
 
+bool zone_contents_digest_exists(const zone_contents_t *contents, uint8_t alg)
+{
+       knot_rdataset_t *zonemd = node_rdataset(contents->apex, KNOT_RRTYPE_ZONEMD);
+       if (zonemd == NULL || zonemd->count != 1 || knot_zonemd_algorithm(zonemd->rdata) != alg) {
+               return false;
+       }
+
+       return verify_zonemd(zonemd->rdata, contents) == KNOT_EOK;
+}
+
 static bool check_duplicate_schalg(const knot_rdataset_t *zonemd, int check_upto,
                                    uint8_t scheme, uint8_t alg)
 {
index f136476ae547a1e98585a7d4d7770f4f2f49dd2b..64f33acbda3583d1f0964f4d574499a71bbcbb1d 100644 (file)
 int zone_contents_digest(const zone_contents_t *contents, int algorithm,
                          uint8_t **out_digest, size_t *out_size);
 
+/*!
+ * \brief Check whether exactly one ZONEMD exists in the zone, is valid and matches given algorithm.
+ *
+ * \param contents   Zone contents to be verified.
+ * \param alg        Required algorithm of the ZONEMD.
+ */
+bool zone_contents_digest_exists(const zone_contents_t *contents, uint8_t alg);
+
 /*!
  * \brief Verify zone dgest in ZONEMD record.
  *
diff --git a/tests-extra/tests/zone/zonemd_flush/test.py b/tests-extra/tests/zone/zonemd_flush/test.py
new file mode 100644 (file)
index 0000000..210c7b3
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+'''Flushing the zone after ZONEMD generation.'''
+
+from dnstest.test import Test
+from dnstest.utils import *
+
+def has_zonemd(server, zone, alg):
+    zfn = server.zones[zone.name].zfile.path
+    with open(zfn) as zf:
+        for line in zf:
+            rr = line.split()
+            if rr[0].lower() == zone.name.lower() and rr[2] == "ZONEMD" and rr[5] == alg:
+                return True
+    return False
+
+def check_zonemd(server, zone, alg):
+    for z in zone:
+        if not has_zonemd(server, z, alg):
+            set_err("NO ZONEMD in %s" % z.name)
+
+t = Test()
+
+master = t.server("knot")
+
+zone = t.zone_rnd(2, dnssec=False, records=10)
+t.link(zone, master)
+
+master.zonefile_sync = 0
+master.zonemd_generate = "zonemd-sha384"
+
+t.start()
+
+master.zones_wait(zone)
+t.sleep(4)
+check_zonemd(master, zone, "1")
+
+t.end()