diff options
Diffstat (limited to '')
-rw-r--r-- | src/knot/dnssec/kasp/kasp_db.c | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/src/knot/dnssec/kasp/kasp_db.c b/src/knot/dnssec/kasp/kasp_db.c new file mode 100644 index 0000000..29c6a7d --- /dev/null +++ b/src/knot/dnssec/kasp/kasp_db.c @@ -0,0 +1,610 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "knot/dnssec/kasp/kasp_db.h" + +#include <inttypes.h> +#include <stdio.h> + +#include "contrib/strtonum.h" +#include "contrib/wire_ctx.h" +#include "knot/dnssec/key_records.h" + +typedef enum { + KASPDBKEY_PARAMS = 0x1, + KASPDBKEY_POLICYLAST = 0x2, + KASPDBKEY_NSEC3SALT = 0x3, + KASPDBKEY_NSEC3TIME = 0x4, + KASPDBKEY_MASTERSERIAL = 0x5, + KASPDBKEY_LASTSIGNEDSERIAL = 0x6, + KASPDBKEY_OFFLINE_RECORDS = 0x7, + KASPDBKEY_SAVED_TTLS = 0x8, +} keyclass_t; + +static const keyclass_t zone_related_classes[] = { + KASPDBKEY_PARAMS, + KASPDBKEY_NSEC3SALT, + KASPDBKEY_NSEC3TIME, + KASPDBKEY_MASTERSERIAL, + KASPDBKEY_LASTSIGNEDSERIAL, + KASPDBKEY_OFFLINE_RECORDS, + KASPDBKEY_SAVED_TTLS, +}; +static const size_t zone_related_classes_size = sizeof(zone_related_classes) / sizeof(*zone_related_classes); + +static bool is_zone_related_class(uint8_t class) +{ + for (size_t i = 0; i < zone_related_classes_size; i++) { + if (zone_related_classes[i] == class) { + return true; + } + } + return false; +} + +static bool is_zone_related(const MDB_val *key) +{ + return is_zone_related_class(*(uint8_t *)key->mv_data); +} + +static MDB_val make_key_str(keyclass_t kclass, const knot_dname_t *dname, const char *str) +{ + switch (kclass) { + case KASPDBKEY_POLICYLAST: + assert(dname == NULL && str != NULL); + return knot_lmdb_make_key("BS", (int)kclass, str); + case KASPDBKEY_NSEC3SALT: + case KASPDBKEY_NSEC3TIME: + case KASPDBKEY_LASTSIGNEDSERIAL: + case KASPDBKEY_MASTERSERIAL: + case KASPDBKEY_SAVED_TTLS: + assert(dname != NULL && str == NULL); + return knot_lmdb_make_key("BN", (int)kclass, dname); + case KASPDBKEY_PARAMS: + case KASPDBKEY_OFFLINE_RECORDS: + assert(dname != NULL); + if (str == NULL) { + return knot_lmdb_make_key("BN", (int)kclass, dname); + } else { + return knot_lmdb_make_key("BNS", (int)kclass, dname, str); + } + default: + assert(0); + MDB_val empty = { 0 }; + return empty; + } +} + +static MDB_val make_key_time(keyclass_t kclass, const knot_dname_t *dname, knot_time_t time) +{ + char tmp[21]; + (void)snprintf(tmp, sizeof(tmp), "%0*"PRIu64, (int)(sizeof(tmp) - 1), time); + return make_key_str(kclass, dname, tmp); +} + +static bool unmake_key_str(const MDB_val *keyv, char **str) +{ + uint8_t kclass; + const knot_dname_t *dname; + const char *s; + return (knot_lmdb_unmake_key(keyv->mv_data, keyv->mv_size, "BNS", &kclass, &dname, &s) && + ((*str = strdup(s)) != NULL)); +} + +static bool unmake_key_time(const MDB_val *keyv, knot_time_t *time) +{ + uint8_t kclass; + const knot_dname_t *dname; + const char *s; + return (knot_lmdb_unmake_key(keyv->mv_data, keyv->mv_size, "BNS", &kclass, &dname, &s) && + str_to_u64(s, time) == KNOT_EOK); +} + +static MDB_val params_serialize(const key_params_t *params) +{ + uint8_t flags = 0x02; + flags |= (params->is_ksk ? 0x01 : 0); + flags |= (params->is_pub_only ? 0x04 : 0); + flags |= (params->is_csk ? 0x08 : 0); + + return knot_lmdb_make_key("LLHBBLLLLLLLLLDL", (uint64_t)params->public_key.size, + (uint64_t)sizeof(params->timing.revoke), params->keytag, params->algorithm, flags, + params->timing.created, params->timing.pre_active, params->timing.publish, + params->timing.ready, params->timing.active, params->timing.retire_active, + params->timing.retire, params->timing.post_active, params->timing.remove, + params->public_key.data, params->public_key.size, params->timing.revoke); +} + +// this is no longer compatible with keys created by Knot 2.5.x (and unmodified since) +static bool params_deserialize(const MDB_val *val, key_params_t *params) +{ + if (val->mv_size < 2 * sizeof(uint64_t)) { + return false; + } + uint64_t keylen = knot_wire_read_u64(val->mv_data); + uint64_t future = knot_wire_read_u64(val->mv_data + sizeof(keylen)); + uint8_t flags; + + if ((params->public_key.data = malloc(keylen)) == NULL) { + return false; + } + + if (knot_lmdb_unmake_key(val->mv_data, val->mv_size - future, "LLHBBLLLLLLLLLD", + &keylen, &future, ¶ms->keytag, ¶ms->algorithm, &flags, + ¶ms->timing.created, ¶ms->timing.pre_active, ¶ms->timing.publish, + ¶ms->timing.ready, ¶ms->timing.active, ¶ms->timing.retire_active, + ¶ms->timing.retire, ¶ms->timing.post_active, ¶ms->timing.remove, + params->public_key.data, (size_t)keylen)) { + + params->public_key.size = keylen; + params->is_ksk = ((flags & 0x01) ? true : false); + params->is_pub_only = ((flags & 0x04) ? true : false); + params->is_csk = ((flags & 0x08) ? true : false); + + if (future > 0) { + if (future < sizeof(params->timing.revoke)) { + free(params->public_key.data); + params->public_key.data = NULL; + return false; + } + // 'revoked' timer is part of 'future' section since it was added later + params->timing.revoke = knot_wire_read_u64(val->mv_data + val->mv_size - future); + } + + if ((flags & 0x02) && (params->is_ksk || !params->is_csk)) { + return true; + } + } + free(params->public_key.data); + params->public_key.data = NULL; + return false; +} + +static key_params_t *txn2params(knot_lmdb_txn_t *txn) +{ + key_params_t *p = calloc(1, sizeof(*p)); + if (p == NULL) { + txn->ret = KNOT_ENOMEM; + } else { + if (!params_deserialize(&txn->cur_val, p) || + !unmake_key_str(&txn->cur_key, &p->id)) { + txn->ret = KNOT_EMALF; + free(p); + p = NULL; + } + } + return p; +} + +int kasp_db_list_keys(knot_lmdb_db_t *db, const knot_dname_t *zone_name, list_t *dst) +{ + init_list(dst); + knot_lmdb_txn_t txn = { 0 }; + MDB_val prefix = make_key_str(KASPDBKEY_PARAMS, zone_name, NULL); + knot_lmdb_begin(db, &txn, false); + knot_lmdb_foreach(&txn, &prefix) { + key_params_t *p = txn2params(&txn); + if (p != NULL) { + ptrlist_add(dst, p, NULL); + } + } + knot_lmdb_abort(&txn); + free(prefix.mv_data); + if (txn.ret != KNOT_EOK) { + ptrlist_deep_free(dst, NULL); + return txn.ret; + } + return (EMPTY_LIST(*dst) ? KNOT_ENOENT : KNOT_EOK); +} + +int kasp_db_get_key_algorithm(knot_lmdb_db_t *db, const knot_dname_t *zone_name, + const char *key_id) +{ + knot_lmdb_txn_t txn = { 0 }; + MDB_val search = make_key_str(KASPDBKEY_PARAMS, zone_name, key_id); + knot_lmdb_begin(db, &txn, false); + int ret = txn.ret == KNOT_EOK ? KNOT_ENOENT : txn.ret; + if (knot_lmdb_find(&txn, &search, KNOT_LMDB_EXACT)) { + key_params_t p = { 0 }; + ret = params_deserialize(&txn.cur_val, &p) ? p.algorithm : KNOT_EMALF; + free(p.public_key.data); + } + knot_lmdb_abort(&txn); + free(search.mv_data); + return ret; +} + +static bool keyid_inuse(knot_lmdb_txn_t *txn, const char *key_id, key_params_t **params) +{ + uint8_t pf = KASPDBKEY_PARAMS; + MDB_val prefix = { sizeof(pf), &pf }; + knot_lmdb_foreach(txn, &prefix) { + char *found_id = NULL; + if (unmake_key_str(&txn->cur_key, &found_id) && + strcmp(found_id, key_id) == 0) { + if (params != NULL) { + *params = txn2params(txn); + } + free(found_id); + return true; + } + free(found_id); + } + return false; +} + + +int kasp_db_delete_key(knot_lmdb_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used) +{ + MDB_val search = make_key_str(KASPDBKEY_PARAMS, zone_name, key_id); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + knot_lmdb_del_prefix(&txn, &search); + if (still_used != NULL) { + *still_used = keyid_inuse(&txn, key_id, NULL); + } + knot_lmdb_commit(&txn); + free(search.mv_data); + return txn.ret; +} + + +int kasp_db_delete_all(knot_lmdb_db_t *db, const knot_dname_t *zone) +{ + MDB_val prefix = make_key_str(KASPDBKEY_PARAMS, zone, NULL); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + for (size_t i = 0; i < zone_related_classes_size && prefix.mv_data != NULL; i++) { + *(uint8_t *)prefix.mv_data = zone_related_classes[i]; + knot_lmdb_del_prefix(&txn, &prefix); + } + knot_lmdb_commit(&txn); + free(prefix.mv_data); + return txn.ret; +} + +int kasp_db_sweep(knot_lmdb_db_t *db, sweep_cb keep_zone, void *cb_data) +{ + if (knot_lmdb_exists(db) == KNOT_ENODB) { + return KNOT_EOK; + } + int ret = knot_lmdb_open(db); + if (ret != KNOT_EOK) { + return ret; + } + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + knot_lmdb_forwhole(&txn) { + if (is_zone_related(&txn.cur_key) && + !keep_zone((const knot_dname_t *)txn.cur_key.mv_data + 1, cb_data)) { + knot_lmdb_del_cur(&txn); + } + } + knot_lmdb_commit(&txn); + return txn.ret; +} + +int kasp_db_list_zones(knot_lmdb_db_t *db, list_t *zones) +{ + if (knot_lmdb_exists(db) == KNOT_ENODB) { + return KNOT_EOK; + } + int ret = knot_lmdb_open(db); + if (ret != KNOT_EOK) { + return ret; + } + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, false); + + uint8_t prefix_data = KASPDBKEY_PARAMS; + MDB_val prefix = { sizeof(prefix_data), &prefix_data }; + knot_lmdb_foreach(&txn, &prefix) { + const knot_dname_t *found = txn.cur_key.mv_data + sizeof(prefix_data); + if (!knot_dname_is_equal(found, ((ptrnode_t *)TAIL(*zones))->d)) { + knot_dname_t *copy = knot_dname_copy(found, NULL); + if (copy == NULL || ptrlist_add(zones, copy, NULL) == NULL) { + free(copy); + ptrlist_deep_free(zones, NULL); + return KNOT_ENOMEM; + } + } + } + knot_lmdb_abort(&txn); + if (txn.ret != KNOT_EOK) { + ptrlist_deep_free(zones, NULL); + } + return txn.ret; +} + +int kasp_db_add_key(knot_lmdb_db_t *db, const knot_dname_t *zone_name, const key_params_t *params) +{ + MDB_val v = params_serialize(params); + MDB_val k = make_key_str(KASPDBKEY_PARAMS, zone_name, params->id); + return knot_lmdb_quick_insert(db, k, v); +} + +int kasp_db_share_key(knot_lmdb_db_t *db, const knot_dname_t *zone_from, + const knot_dname_t *zone_to, const char *key_id) +{ + MDB_val from = make_key_str(KASPDBKEY_PARAMS, zone_from, key_id); + MDB_val to = make_key_str(KASPDBKEY_PARAMS, zone_to, key_id); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + if (knot_lmdb_find(&txn, &from, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) { + knot_lmdb_insert(&txn, &to, &txn.cur_val); + } + knot_lmdb_commit(&txn); + free(from.mv_data); + free(to.mv_data); + return txn.ret; +} + +int kasp_db_store_nsec3salt(knot_lmdb_db_t *db, const knot_dname_t *zone_name, + const dnssec_binary_t *nsec3salt, knot_time_t salt_created) +{ + MDB_val key = make_key_str(KASPDBKEY_NSEC3SALT, zone_name, NULL); + MDB_val val1 = { nsec3salt->size, nsec3salt->data }; + uint64_t tmp = htobe64(salt_created); + MDB_val val2 = { sizeof(tmp), &tmp }; + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + knot_lmdb_insert(&txn, &key, &val1); + if (key.mv_data != NULL) { + *(uint8_t *)key.mv_data = KASPDBKEY_NSEC3TIME; + } + knot_lmdb_insert(&txn, &key, &val2); + knot_lmdb_commit(&txn); + free(key.mv_data); + return txn.ret; +} + +int kasp_db_load_nsec3salt(knot_lmdb_db_t *db, const knot_dname_t *zone_name, + dnssec_binary_t *nsec3salt, knot_time_t *salt_created) +{ + MDB_val key = make_key_str(KASPDBKEY_NSEC3SALT, zone_name, NULL); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, false); + if (nsec3salt != NULL) { + memset(nsec3salt, 0, sizeof(*nsec3salt)); + if (knot_lmdb_find(&txn, &key, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) { + nsec3salt->size = txn.cur_val.mv_size; + nsec3salt->data = malloc(txn.cur_val.mv_size + 1); // +1 because it can be zero + if (nsec3salt->data == NULL) { + txn.ret = KNOT_ENOMEM; + } else { + memcpy(nsec3salt->data, txn.cur_val.mv_data, txn.cur_val.mv_size); + } + } + } + *(uint8_t *)key.mv_data = KASPDBKEY_NSEC3TIME; + if (knot_lmdb_find(&txn, &key, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) { + knot_lmdb_unmake_curval(&txn, "L", salt_created); + } + knot_lmdb_abort(&txn); + free(key.mv_data); + if (txn.ret != KNOT_EOK && nsec3salt != NULL) { + free(nsec3salt->data); + } + return txn.ret; +} + +int kasp_db_store_serial(knot_lmdb_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t serial) +{ + int ret = knot_lmdb_open(db); + if (ret != KNOT_EOK) { + return ret; + } + MDB_val k = make_key_str((keyclass_t)serial_type, zone_name, NULL); + MDB_val v = knot_lmdb_make_key("I", serial); + return knot_lmdb_quick_insert(db, k, v); +} + +int kasp_db_load_serial(knot_lmdb_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t *serial) +{ + if (knot_lmdb_exists(db) == KNOT_ENODB) { + return KNOT_ENOENT; + } + int ret = knot_lmdb_open(db); + if (ret != KNOT_EOK) { + return ret; + } + MDB_val k = make_key_str((keyclass_t)serial_type, zone_name, NULL); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, false); + if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) { + knot_lmdb_unmake_curval(&txn, "I", serial); + } + knot_lmdb_abort(&txn); + free(k.mv_data); + return txn.ret; +} + +int kasp_db_get_policy_last(knot_lmdb_db_t *db, const char *policy_string, + knot_dname_t **lp_zone, char **lp_keyid) +{ + MDB_val k = make_key_str(KASPDBKEY_POLICYLAST, NULL, policy_string); + uint8_t kclass = 0; + knot_lmdb_txn_t txn = { 0 }; + *lp_zone = NULL; + *lp_keyid = NULL; + knot_lmdb_begin(db, &txn, false); + if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE) && + knot_lmdb_unmake_curval(&txn, "BNS", &kclass, lp_zone, lp_keyid)) { + assert(*lp_zone != NULL && *lp_keyid != NULL); + *lp_zone = knot_dname_copy(*lp_zone, NULL); + *lp_keyid = strdup(*lp_keyid); + if (kclass != KASPDBKEY_PARAMS) { + txn.ret = KNOT_EMALF; + } else if (*lp_keyid == NULL || *lp_zone == NULL) { + txn.ret = KNOT_ENOMEM; + } else { + // check that the referenced key really exists + knot_lmdb_find(&txn, &txn.cur_val, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE); + } + } + knot_lmdb_abort(&txn); + free(k.mv_data); + + return txn.ret; +} + +int kasp_db_set_policy_last(knot_lmdb_db_t *db, const char *policy_string, const char *last_lp_keyid, + const knot_dname_t *new_lp_zone, const char *new_lp_keyid) +{ + MDB_val k = make_key_str(KASPDBKEY_POLICYLAST, NULL, policy_string); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT)) { + // check that the last_lp_keyid matches + uint8_t unuse1, *unuse2; + const char *real_last_keyid; + if (knot_lmdb_unmake_curval(&txn, "BNS", &unuse1, &unuse2, &real_last_keyid) && + (last_lp_keyid == NULL || strcmp(last_lp_keyid, real_last_keyid) != 0)) { + txn.ret = KNOT_ESEMCHECK; + } + } + MDB_val v = make_key_str(KASPDBKEY_PARAMS, new_lp_zone, new_lp_keyid); + knot_lmdb_insert(&txn, &k, &v); + free(k.mv_data); + free(v.mv_data); + knot_lmdb_commit(&txn); + return txn.ret; +} + +int kasp_db_store_offline_records(knot_lmdb_db_t *db, knot_time_t for_time, const key_records_t *r) +{ + MDB_val k = make_key_time(KASPDBKEY_OFFLINE_RECORDS, r->rrsig.owner, for_time); + MDB_val v = { key_records_serialized_size(r), NULL }; + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + if (knot_lmdb_insert(&txn, &k, &v)) { + wire_ctx_t wire = wire_ctx_init(v.mv_data, v.mv_size); + txn.ret = key_records_serialize(&wire, r); + } + knot_lmdb_commit(&txn); + free(k.mv_data); + return txn.ret; +} + +int kasp_db_load_offline_records(knot_lmdb_db_t *db, const knot_dname_t *for_dname, + knot_time_t *for_time, knot_time_t *next_time, + key_records_t *r) +{ + MDB_val prefix = make_key_str(KASPDBKEY_OFFLINE_RECORDS, for_dname, NULL); + if (prefix.mv_data == NULL) { + return KNOT_ENOMEM; + } + unsigned operator = KNOT_LMDB_GEQ; + MDB_val search = prefix; + bool zero_for_time = (*for_time == 0); + if (!zero_for_time) { + operator = KNOT_LMDB_LEQ; + search = make_key_time(KASPDBKEY_OFFLINE_RECORDS, for_dname, *for_time); + } + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, false); + if (knot_lmdb_find(&txn, &search, operator) && + knot_lmdb_is_prefix_of(&prefix, &txn.cur_key)) { + wire_ctx_t wire = wire_ctx_init(txn.cur_val.mv_data, txn.cur_val.mv_size); + txn.ret = key_records_deserialize(&wire, r); + if (zero_for_time) { + unmake_key_time(&txn.cur_key, for_time); + } + if (!knot_lmdb_next(&txn) || !knot_lmdb_is_prefix_of(&prefix, &txn.cur_key) || + !unmake_key_time(&txn.cur_key, next_time)) { + *next_time = 0; + } + } else if (txn.ret == KNOT_EOK) { + txn.ret = KNOT_ENOENT; + } + knot_lmdb_abort(&txn); + if (!zero_for_time) { + free(search.mv_data); + } + free(prefix.mv_data); + return txn.ret; +} + +int kasp_db_delete_offline_records(knot_lmdb_db_t *db, const knot_dname_t *zone, + knot_time_t from_time, knot_time_t to_time) +{ + MDB_val prefix = make_key_str(KASPDBKEY_OFFLINE_RECORDS, zone, NULL); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + knot_lmdb_foreach(&txn, &prefix) { + knot_time_t found; + if (unmake_key_time(&txn.cur_key, &found) && + knot_time_cmp(found, from_time) >= 0 && + knot_time_cmp(found, to_time) <= 0) { + knot_lmdb_del_cur(&txn); + } + } + knot_lmdb_commit(&txn); + free(prefix.mv_data); + return txn.ret; +} + +int kasp_db_get_saved_ttls(knot_lmdb_db_t *db, const knot_dname_t *zone, + uint32_t *max_ttl, uint32_t *key_ttl) +{ + MDB_val key = make_key_str(KASPDBKEY_SAVED_TTLS, zone, NULL); + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, false); + if (knot_lmdb_find(&txn, &key, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) { + knot_lmdb_unmake_curval(&txn, "II", max_ttl, key_ttl); + } + knot_lmdb_abort(&txn); + free(key.mv_data); + return txn.ret; +} + +int kasp_db_set_saved_ttls(knot_lmdb_db_t *db, const knot_dname_t *zone, + uint32_t max_ttl, uint32_t key_ttl) +{ + MDB_val key = make_key_str(KASPDBKEY_SAVED_TTLS, zone, NULL); + MDB_val val = knot_lmdb_make_key("II", max_ttl, key_ttl); + return knot_lmdb_quick_insert(db, key, val); +} + +void kasp_db_ensure_init(knot_lmdb_db_t *db, conf_t *conf) +{ + if (db->path == NULL) { + char *kasp_dir = conf_db(conf, C_KASP_DB); + conf_val_t kasp_size = conf_db_param(conf, C_KASP_DB_MAX_SIZE); + knot_lmdb_init(db, kasp_dir, conf_int(&kasp_size), 0, "keys_db"); + free(kasp_dir); + assert(db->path != NULL); + } +} + +int kasp_db_backup(const knot_dname_t *zone, knot_lmdb_db_t *db, knot_lmdb_db_t *backup_db) +{ + size_t n_prefs = zone_related_classes_size + 1; // NOTE: this and following must match number of record types + MDB_val prefixes[n_prefs]; + prefixes[0] = knot_lmdb_make_key("B", KASPDBKEY_POLICYLAST); // we copy all policy-last records, that doesn't harm + for (size_t i = 1; i < n_prefs; i++) { + prefixes[i] = make_key_str(zone_related_classes[i - 1], zone, NULL); + } + + int ret = knot_lmdb_copy_prefixes(db, backup_db, prefixes, n_prefs); + + for (int i = 0; i < n_prefs; i++) { + free(prefixes[i].mv_data); + } + return ret; +} |