bool ixfr_by_one; //!< Allow only single changeset within IXFR.
bool ixfr_from_axfr; //!< Diff computation of incremental update from AXFR allowed.
bool reverse_or_include; //!< Auto reverse generation or subzone inclusion configured.
+ bool ignore_zonemd; //!< Ignore apex ZONEMD in incomming IXFR as it is overwritten anyway.
uint32_t expire_timer; //!< Result: expire timer from answer EDNS.
// internal state, initialize with zeroes:
case IXFR_SOA_DEL:
return ixfr_solve_soa_del(rr, data);
case IXFR_DEL:
+ if (data->ignore_zonemd && rr->type == KNOT_RRTYPE_ZONEMD &&
+ knot_dname_is_equal(rr->owner, data->zone->name)) {
+ return KNOT_EOK;
+ }
return ixfr_solve_del(rr, change, data->mm);
case IXFR_SOA_ADD:
return ixfr_solve_soa_add(rr, change, data->mm);
bool ixfr_from_axfr;
bool reverse_or_include;
bool more_xfr;
+ bool ignore_zonemd;
} try_refresh_ctx_t;
static int try_refresh(conf_t *conf, zone_t *zone, const conf_remote_t *master,
.ixfr_by_one = trctx->ixfr_by_one,
.ixfr_from_axfr = trctx->ixfr_from_axfr,
.reverse_or_include = trctx->reverse_or_include,
+ .ignore_zonemd = trctx->ignore_zonemd,
+ // TODO refactor refresh_data and try_refresh_ctx_t so that this cross-assignment is not necessary for simple fields
};
knot_requestor_t requestor;
conf_val_t val = conf_zone_get(conf, C_IXFR_BY_ONE, zone->name);
trctx.ixfr_by_one = conf_bool(&val);
+ val = conf_zone_get(conf, C_ZONEMD_GENERATE, zone->name);
+ trctx.ignore_zonemd = (conf_opt(&val) != ZONE_DIGEST_NONE);
val = conf_zone_get(conf, C_IXFR_FROM_AXFR, zone->name);
trctx.ixfr_from_axfr = conf_bool(&val);
--- /dev/null
+#!/usr/bin/env python3
+
+"""
+Test of bump-in-the-wire signer receiving ZONEMD in unsigned version of the zone.
+"""
+
+from dnstest.utils import *
+from dnstest.test import Test
+import random
+import threading
+import time
+
+t = Test()
+
+master = t.server("knot")
+signer = t.server("knot")
+slave = t.server("knot")
+ZONE = "example."
+zones = t.zone(ZONE)
+
+t.link(zones, master, signer)
+t.link(zones, signer, slave)
+
+master.conf_zone(zones).zonemd_generate = "zonemd-sha384"
+signer.conf_zone(zones).zonemd_verify = True
+
+signer.dnssec(zones).enable = True
+signer.conf_zone(zones).zonemd_generate = "zonemd-sha384"
+
+slave.conf_zone(zones).dnssec_validation = True
+slave.conf_zone(zones).zonemd_verify = True
+
+t.start()
+serials = slave.zones_wait(zones)
+
+master.random_ddns(zones, allow_empty=False)
+t.sleep(4)
+slave.zones_wait(zones, serials, equal=True, greater=False)
+signer.ctl("zone-retransfer")
+serials = slave.zones_wait(zones, serials)
+
+signer.conf_zone(zones).zonemd_verify = False
+signer.gen_confile()
+signer.reload()
+
+master.random_ddns(zones, allow_empty=False)
+serials = slave.zones_wait(zones, serials)
+if signer.log_search_count("fallback to AXFR ") > 0: # NOTE without the trailing space the message can appear for outgoing IXFR as well, which it actually should
+ set_err("AXFR fallback")
+
+t.end()