conf: allow empty value for some zone items (references to remotes)

This allows overriding of corresponding non-empty template items.
This commit is contained in:
Daniel Salzman 2024-05-09 10:55:06 +02:00
parent 4930ccf99e
commit 4784c4c601
6 changed files with 82 additions and 4 deletions

View file

@ -2604,6 +2604,7 @@ master
An ordered list of references :ref:`remote<remote_id>` and
:ref:`remotes<remotes_id>` to zone primary servers
(formerly known as master servers).
Empty value is allowed for template value overriding.
*Default:* not set
@ -2630,6 +2631,7 @@ notify
An ordered list of references :ref:`remote<remote_id>` and
:ref:`remotes<remotes_id>` to secondary servers to which notify
message is sent if the zone changes.
Empty value is allowed for template value overriding.
*Default:* not set
@ -2933,7 +2935,7 @@ ds-push
-------
Per zone configuration of :ref:`policy_ds-push`. This option overrides possible
per policy option.
per policy option. Empty value is allowed for template value overriding.
*Default:* not set

View file

@ -191,6 +191,10 @@ conf_val_t conf_zone_get_txn(
conf_db_get(conf, txn, C_ZONE, key1_name, dname, dname_size, &val);
switch (val.code) {
case KNOT_EOK:
if (val.blob_len == 1 && (val.item->flags & CONF_REF_EMPTY)) {
static const conf_val_t empty = { .code = KNOT_ENOENT };
return empty;
}
return val;
default:
CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",

View file

@ -32,6 +32,7 @@
#define CONF_IO_FRLD_MOD YP_FUSR8 /*!< Reload global modules. */
#define CONF_IO_FRLD_ZONE YP_FUSR9 /*!< Reload a specific zone. */
#define CONF_IO_FRLD_ZONES YP_FUSR10 /*!< Reload all zones. */
#define CONF_REF_EMPTY YP_FUSR11 /*!< Allow empty reference value for zone item. */
#define CONF_IO_FRLD_ALL (CONF_IO_FRLD_SRV | CONF_IO_FRLD_LOG | \
CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES)

View file

@ -454,9 +454,11 @@ static const yp_item_t desc_policy[] = {
#define ZONE_ITEMS(FLAGS) \
{ C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR }, FLAGS }, \
{ C_FILE, YP_TSTR, YP_VNONE, FLAGS }, \
{ C_MASTER, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } }, \
{ C_MASTER, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | CONF_REF_EMPTY, \
{ check_ref } }, \
{ C_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref_empty } }, \
{ C_NOTIFY, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } }, \
{ C_NOTIFY, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | CONF_REF_EMPTY, \
{ check_ref } }, \
{ C_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \
{ C_MASTER_PIN_TOL, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME } }, \
{ C_PROVIDE_IXFR, YP_TBOOL, YP_VBOOL = { true } }, \
@ -474,7 +476,7 @@ static const yp_item_t desc_policy[] = {
{ C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE, FLAGS }, \
{ C_DNSSEC_VALIDATION, YP_TBOOL, YP_VNONE, FLAGS }, \
{ C_DNSSEC_POLICY, YP_TREF, YP_VREF = { C_POLICY }, FLAGS, { check_ref_dflt } }, \
{ C_DS_PUSH, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | FLAGS, \
{ C_DS_PUSH, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | CONF_REF_EMPTY | FLAGS, \
{ check_ref } }, \
{ C_REVERSE_GEN, YP_TDNAME,YP_VNONE, FLAGS | CONF_IO_FRLD_ZONES }, \
{ C_SERIAL_POLICY, YP_TOPT, YP_VOPT = { serial_policies, SERIAL_POLICY_INCREMENT } }, \

View file

@ -236,6 +236,13 @@ int check_ref(
bool found1 = false, found2 = false;
// Check if allowed empty value for specific zone items.
if (args->data_len == 1 && (args->item->flags & CONF_REF_EMPTY) &&
args->item->parent->name[0] == C_ZONE[0] &&
memcmp(&args->item->parent->name[1], &C_ZONE[1], C_ZONE[0]) == 0) {
return KNOT_EOK;
}
// Try to find the id in the first section.
found1 = conf_rawid_exists_txn(args->extra->conf, args->extra->txn,
ref->name, args->data, args->data_len);

View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
'''Test for configuration zone templates'''
import os
from dnstest.libknot import libknot
from dnstest.test import Test
t = Test()
master = t.server("knot")
slave = t.server("knot")
zones = t.zone_rnd(2, records=5, dnssec=False)
for z in zones:
z.name = z.name.lower()
t.link(zones, master, slave, ixfr=True)
ctl = libknot.control.KnotCtl()
t.start()
serials_init = master.zones_wait(zones)
slave.zones_wait(zones)
ctl.connect(os.path.join(master.dir, "knot.sock"))
ctl.send_block(cmd="conf-begin")
resp = ctl.receive_block()
# Move notify setting from zones to the default template.
ctl.send_block(cmd="conf-get", section="zone", item="notify", identifier=zones[0].name)
resp = ctl.receive_block()
for val in resp['zone'][zones[0].name]['notify']:
ctl.send_block(cmd="conf-set", section="template", identifier="default", item="notify", data=val)
resp = ctl.receive_block()
ctl.send_block(cmd="conf-unset", section="zone", item="notify")
resp = ctl.receive_block()
# Override template setting with the default (none) for the first zone.
ctl.send_block(cmd="conf-set", section="zone", item="notify", identifier=zones[0].name, data="")
resp = ctl.receive_block()
ctl.send_block(cmd="conf-commit")
resp = ctl.receive_block()
ctl.send(libknot.control.KnotCtlType.END)
ctl.close()
# Modify the zones and check that notify doesn't work for the first zone.
serials_prev = serials_init
serial1 = serials_init[zones[0].name]
for i in range(2):
for zone in zones:
master.update_zonefile(zone, random=True)
master.ctl('zone-reload')
serials_prev = master.zones_wait(zones, serials_prev)
slave.zone_wait(zones[1], serials_prev[zones[1].name], equal=True, greater=False)
t.sleep(1)
slave.zone_wait(zones[0], serial1, equal=True, greater=False)
t.end()