From: Libor Peltan Date: Sun, 3 Aug 2025 13:35:54 +0000 (+0200) Subject: zonedb/reverse: bugfix: removal/modification of zone: X-Git-Tag: v3.5.0~35^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=151d0399b9b1ee06da4043a2eeba6ef509273c48;p=thirdparty%2Fknot-dns.git zonedb/reverse: bugfix: removal/modification of zone: * removal of forward zone unlinks all dependent reverses * rem/modification of reverse zone first unlinks all to avoid leftovers+duplicates --- diff --git a/src/contrib/ucw/lists.c b/src/contrib/ucw/lists.c index 01af28f311..4d93009dc6 100644 --- a/src/contrib/ucw/lists.c +++ b/src/contrib/ucw/lists.c @@ -240,6 +240,22 @@ void ptrlist_rem(ptrnode_t *node, knot_mm_t *mm) mm_free(mm, node); } +/** + * ptrlist_rem - remove all nodes with given pointer + * \p list: ptrlist + * \p val: pointer to remove + * \p mm: memory context + */ +void ptrlist_find_rem(list_t *list, void *val, knot_mm_t *mm) +{ + ptrnode_t *n, *nxt; + WALK_LIST_DELSAFE(n, nxt, *list) { + if (n->d == val) { + ptrlist_rem(n, mm); + } + } +} + /** * ptrlist_deep_free - free all nodes incl referenced data * \p list: list nodes diff --git a/src/contrib/ucw/lists.h b/src/contrib/ucw/lists.h index 1a3ca955e6..d8ed449ab6 100644 --- a/src/contrib/ucw/lists.h +++ b/src/contrib/ucw/lists.h @@ -68,6 +68,7 @@ typedef struct ptrnode { ptrnode_t *ptrlist_add(list_t *, void *, knot_mm_t *); void ptrlist_free(list_t *, knot_mm_t *); void ptrlist_rem(ptrnode_t *node, knot_mm_t *mm); +void ptrlist_find_rem(list_t *list, void *val, knot_mm_t *mm); void ptrlist_deep_free(list_t *, knot_mm_t *); typedef void (*ptrlist_free_cb)(void *); diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index 3cf34bb2e2..ba11aa1134 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -811,6 +811,11 @@ void zone_local_notify_subscribe(zone_t *zone, zone_t *subscribe) ptrlist_add(&zone->internal_notify, subscribe, NULL); } +void zone_local_notify_unsubscribe(zone_t *zone, zone_t *subscribe) +{ + ptrlist_find_rem(&zone->internal_notify, subscribe, NULL); +} + void zone_local_notify(zone_t *zone) { ptrnode_t *n; diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index ff2d2747e3..ef861f2a39 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -284,6 +284,7 @@ int zone_master_try(conf_t *conf, zone_t *zone, zone_master_cb callback, int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir); void zone_local_notify_subscribe(zone_t *zone, zone_t *subscribe); +void zone_local_notify_unsubscribe(zone_t *zone, zone_t *subscribe); void zone_local_notify(zone_t *zone); int zone_set_master_serial(zone_t *zone, uint32_t serial); diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index e7dc1658ab..7858201165 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -389,6 +389,12 @@ static void reg_reverse(conf_t *conf, knot_zonedb_t *db_new, zone_t *zone) return; } + ptrnode_t *n; + WALK_LIST(n, zone->reverse_from) { + zone_local_notify_unsubscribe(n->d, zone); + } + ptrlist_free(&zone->reverse_from, NULL); + conf_val_t val = conf_zone_get(conf, C_REVERSE_GEN, zone->name); while (val.code == KNOT_EOK) { const knot_dname_t *forw_name = conf_dname(&val); @@ -406,6 +412,21 @@ static void reg_reverse(conf_t *conf, knot_zonedb_t *db_new, zone_t *zone) } } +static void unreg_reverse(zone_t *zone) +{ + ptrnode_t *n; + WALK_LIST(n, zone->reverse_from) { + zone_local_notify_unsubscribe(n->d, zone); + } + ptrlist_free(&zone->reverse_from, NULL); + + WALK_LIST(n, zone->internal_notify) { + zone_t *reverse = n->d; + ptrlist_find_rem(&reverse->reverse_from, zone, NULL); + } + ptrlist_free(&zone->internal_notify, NULL); +} + static bool same_group(zone_t *old_z, zone_t *new_z) { if (old_z->catalog_group == NULL || new_z->catalog_group == NULL) { @@ -450,6 +471,7 @@ static knot_zonedb_t *create_zonedb_commit(conf_t *conf, server_t *server) reg_reverse(conf, db_new, zone); } else if (type & CONF_IO_TUNSET) { zone_t *zone = knot_zonedb_find(db_new, name); + unreg_reverse(zone); knot_zonedb_del(db_new, name); catalog_generate_rem(zone, db_new); } else { @@ -510,6 +532,7 @@ static knot_zonedb_t *create_zonedb_catalog(conf_t *conf, server_t *server, break; case CAT_UPD_REM: zone = knot_zonedb_find(db_new, upd->member); + unreg_reverse(zone); knot_zonedb_del(db_new, upd->member); catalog_generate_rem(zone, db_new); break; diff --git a/tests-extra/tests/zone/reverse/test.py b/tests-extra/tests/zone/reverse/test.py index 81a5c1a230..08ad7e3bf8 100644 --- a/tests-extra/tests/zone/reverse/test.py +++ b/tests-extra/tests/zone/reverse/test.py @@ -6,8 +6,11 @@ Test of autogenerating reverse zone. from dnstest.utils import * from dnstest.test import Test +from dnstest.libknot import libknot import random +DYNAMIC = random.choice([False, True]) + t = Test() knot = t.server("knot") @@ -70,9 +73,21 @@ knot.zones_wait(zones, serials, equal=True, greater=False) # check that sole rel r = knot.dig("8.2.0.192.in-addr.arpa.", "PTR") r.check(rcode="NOERROR", rdata="dns1.example2.com.") -knot.zones.pop(zones[2].name) -knot.gen_confile() -knot.reload() +if DYNAMIC: + ctl = libknot.control.KnotCtl() + ctl.connect(os.path.join(knot.dir, "knot.sock")) + ctl.send_block(cmd="conf-begin") + resp = ctl.receive_block() + ctl.send_block(cmd="conf-unset", section="zone", item="domain", data=zones[2].name) + resp = ctl.receive_block() + ctl.send_block(cmd="conf-commit") + resp = ctl.receive_block() + ctl.send(libknot.control.KnotCtlType.END) + ctl.close() +else: + knot.zones.pop(zones[2].name) + knot.gen_confile() + knot.reload() t.sleep(5) knot.update_zonefile(zones[0], version=2) @@ -85,9 +100,21 @@ r.check(rcode="REFUSED", nordata="dns2.example.com.") r = knot.dig("5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.1.0.0.2.ip6.arpa.", "PTR") r.check(rcode="NOERROR", rdata="added2.example.com.") -knot.zones.pop(zones[0].name) -knot.gen_confile() -knot.reload() +if DYNAMIC: + ctl = libknot.control.KnotCtl() + ctl.connect(os.path.join(knot.dir, "knot.sock")) + ctl.send_block(cmd="conf-begin") + resp = ctl.receive_block() + ctl.send_block(cmd="conf-unset", section="zone", item="domain", data=zones[0].name) + resp = ctl.receive_block() + ctl.send_block(cmd="conf-commit") + resp = ctl.receive_block() + ctl.send(libknot.control.KnotCtlType.END) + ctl.close() +else: + knot.zones.pop(zones[0].name) + knot.gen_confile() + knot.reload() t.sleep(5) r = knot.dig("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.1.0.0.2.ip6.arpa.", "PTR")