diff --git a/src/knot/catalog/interpret.c b/src/knot/catalog/interpret.c index b70bd65fc51210989021c2150170891110aedb3c..622b4f0c6deca5b4ccdfe04a2f9f46a000afcefa 100644 --- a/src/knot/catalog/interpret.c +++ b/src/knot/catalog/interpret.c @@ -28,6 +28,12 @@ typedef struct { catalog_t *check; } cat_upd_ctx_t; +static bool label_eq(const knot_dname_t *a, const char *_b) +{ + const knot_dname_t *b = (const knot_dname_t *)_b; + return a[0] == b[0] && memcmp(a + 1, b + 1, a[0]) == 0; +} + static bool check_zone_version(const zone_contents_t *zone) { size_t zone_size = knot_dname_size(zone->apex->owner); @@ -126,8 +132,7 @@ static int cat_update_add_node(zone_node_t *node, void *data) return cat_update_add_memb(node->owner, ptr, ctx); } if (labels_diff == 1 && txt != NULL && txt->count == 1 && - node->owner[0] == CATALOG_GROUP_LABEL[0] && - memcmp(node->owner, CATALOG_GROUP_LABEL, CATALOG_GROUP_LABEL[0] + 1) == 0) { + label_eq(node->owner, CATALOG_GROUP_LABEL)) { const knot_dname_t *own = NULL; const knot_dname_t *memb = property_get_member(node, ctx->complete_conts, &own); return cat_update_add_grp(memb, own, txt, ctx); @@ -137,13 +142,8 @@ static int cat_update_add_node(zone_node_t *node, void *data) int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone, const struct zone_contents *complete_contents, - bool remove, bool check_ver, catalog_t *check, - ssize_t *upd_count) + bool remove, catalog_t *check, ssize_t *upd_count) { - if (check_ver && !check_zone_version(zone)) { - return KNOT_EZONEINVAL; - } - knot_dname_storage_t sub; if (knot_dname_store(sub, (uint8_t *)CATALOG_ZONES_LABEL) == 0 || catalog_dname_append(sub, zone->apex->owner) == 0) { @@ -163,3 +163,46 @@ int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone, pthread_mutex_unlock(&u->mutex); return ret; } + +static int rr_count(const zone_node_t *node, uint16_t type) +{ + const knot_rdataset_t *rd = node_rdataset(node, type); + return rd == NULL ? 0 : rd->count; +} + +static int cat_node_verify(zone_node_t *node, void *data) +{ + cat_upd_ctx_t *ctx = data; + int labels_diff = knot_dname_labels(node->owner, NULL) - ctx->apex_labels - 2; + + if (labels_diff == 0 && rr_count(node, KNOT_RRTYPE_PTR) > 1) { + return KNOT_EISRECORD; + } + + if (labels_diff == 1 && label_eq(node->owner, CATALOG_GROUP_LABEL) && + rr_count(node, KNOT_RRTYPE_TXT) > 1) { + return KNOT_EISRECORD; + } + + return KNOT_EOK; +} + +int catalog_zone_verify(const struct zone_contents *zone) +{ + if (!check_zone_version(zone)) { + return KNOT_EZONEINVAL; + } + + knot_dname_storage_t sub; + if (knot_dname_store(sub, (uint8_t *)CATALOG_ZONES_LABEL) == 0 || + catalog_dname_append(sub, zone->apex->owner) == 0) { + return KNOT_EINVAL; + } + + if (zone_contents_find_node(zone, sub) == NULL) { + return KNOT_EOK; + } + + cat_upd_ctx_t ctx = { NULL, zone, knot_dname_labels(zone->apex->owner, NULL), false, NULL }; + return zone_tree_sub_apply(zone->nodes, sub, true, cat_node_verify, &ctx); +} diff --git a/src/knot/catalog/interpret.h b/src/knot/catalog/interpret.h index 6d640312f823f37decb9d4a3ca5f45cd76802979..76ea1386e769ce818d99ad1ac90712a024e86641 100644 --- a/src/knot/catalog/interpret.h +++ b/src/knot/catalog/interpret.h @@ -20,6 +20,17 @@ struct zone_contents; +/*! + * \brief Validate if given zone is valid catalog. + * + * \param zone Catalog zone in question. + * + * \retval KNOT_EZONEINVAL Invalid varsion record. + * \retval KNOT_EISRECORD Some of single-record RRSets has multiple RRs. + * \return KNOT_EOK All OK. + */ +int catalog_zone_verify(const struct zone_contents *zone); + /*! * \brief Iterate over PTR records in given zone contents and add members to catalog update. * @@ -27,7 +38,6 @@ struct zone_contents; * \param zone Zone contents to be searched for member PTR records. * \param complete_contents Complete zone contents (zone might be from a changeset). * \param remove Add removals of found member zones. - * \param check_ver Do check catalog zone version record first. * \param check Optional: existing catalog database to be checked for existence * of such record (useful for removals). * \param upd_count Output: number of resulting updates to catalog database. @@ -36,5 +46,4 @@ struct zone_contents; */ int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone, const struct zone_contents *complete_contents, - bool remove, bool check_ver, catalog_t *check, - ssize_t *upd_count); + bool remove, catalog_t *check, ssize_t *upd_count); diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c index 31ae6073117620661a8cffe3d3903dd5cabee9e3..2e51fc3f03e7cff2cb024cd0a681c143853043bf 100644 --- a/src/knot/updates/zone-update.c +++ b/src/knot/updates/zone-update.c @@ -719,16 +719,20 @@ static int update_catalog(conf_t *conf, zone_update_t *update) zone_set_flag(update->zone, ZONE_IS_CATALOG); - int ret = KNOT_EOK; + int ret = catalog_zone_verify(update->new_cont); + if (ret != KNOT_EOK) { + return ret; + } + ssize_t upd_count = 0; if ((update->flags & UPDATE_INCREMENTAL)) { ret = catalog_update_from_zone(zone_catalog_upd(update->zone), update->change.remove, update->new_cont, - true, false, zone_catalog(update->zone), &upd_count); + true, zone_catalog(update->zone), &upd_count); if (ret == KNOT_EOK) { ret = catalog_update_from_zone(zone_catalog_upd(update->zone), update->change.add, update->new_cont, - false, false, NULL, &upd_count); + false, NULL, &upd_count); } } else { ret = catalog_update_del_all(zone_catalog_upd(update->zone), @@ -737,7 +741,7 @@ static int update_catalog(conf_t *conf, zone_update_t *update) if (ret == KNOT_EOK) { ret = catalog_update_from_zone(zone_catalog_upd(update->zone), update->new_cont, update->new_cont, - false, true, NULL, &upd_count); + false, NULL, &upd_count); } } @@ -746,6 +750,11 @@ static int update_catalog(conf_t *conf, zone_update_t *update) if (kill(getpid(), SIGUSR1) != 0) { ret = knot_map_errno(); } + } else { + // this cant normally happen, just some ENOMEM or so + (void)catalog_update_del_all(zone_catalog_upd(update->zone), + zone_catalog(update->zone), + update->zone->name, &upd_count); } return ret; diff --git a/tests-extra/tests/catalog/basic/test.py b/tests-extra/tests/catalog/basic/test.py index 80f6abbf31ffd77ea78d2d7fd269bda298676542..74f1760ffd5227ef10c6d7db8c070c26adb5c1ac 100644 --- a/tests-extra/tests/catalog/basic/test.py +++ b/tests-extra/tests/catalog/basic/test.py @@ -156,6 +156,15 @@ resp = slave.dig("cataloged2.", "SOA", dnssec=True) resp.check(rcode="NOERROR") check_keys(slave, "cataloged2", 2) +# Check adding two-RR member PTR +up = master.update(zone[1]) +up.add("bar2.zones.catalog1.", 0, "PTR", "catalogedx.") +up.add("bar4.zones.catalog1.", 0, "PTR", "catalogedy.") +up.send("SERVFAIL") +t.sleep(6) +resp = master.dig("catalogedy.", "SOA") +resp.check(rcode="REFUSED") + master.ctl("zone-backup +journal +backupdir %s/backup %s" % (master.dir, zone[1].name)) # Check removing cataloged zone up = master.update(zone[1])