diff options
Diffstat (limited to 'src/knot/dnssec/kasp/kasp_db.c')
-rw-r--r-- | src/knot/dnssec/kasp/kasp_db.c | 751 |
1 files changed, 751 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..7b48115 --- /dev/null +++ b/src/knot/dnssec/kasp/kasp_db.c @@ -0,0 +1,751 @@ +/* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>. +*/ + +#include "knot/dnssec/kasp/kasp_db.h" + +#include <stdarg.h> // just for va_free() +#include <pthread.h> +#include <sys/stat.h> + +#include "contrib/files.h" +#include "contrib/wire_ctx.h" + +struct kasp_db { + knot_db_t *keys_db; + char *db_path; + size_t db_mapsize; + pthread_mutex_t opening_mutex; +}; + +typedef enum { + KASPDBKEY_PARAMS = 0x1, + KASPDBKEY_POLICYLAST = 0x2, + KASPDBKEY_NSEC3SALT = 0x3, + KASPDBKEY_NSEC3TIME = 0x4, + KASPDBKEY_MASTERSERIAL = 0x5, + KASPDBKEY_LASTSIGNEDSERIAL = 0x6, +} keyclass_t; + +static const knot_db_api_t *db_api = NULL; + +static kasp_db_t *global_kasp_db = NULL; + +kasp_db_t **kaspdb(void) +{ + return &global_kasp_db; +} + +int kasp_db_init(kasp_db_t **db, const char *path, size_t mapsize) +{ + if (db == NULL || path == NULL || *db != NULL) { + return KNOT_EINVAL; + } + + db_api = knot_db_lmdb_api(); + + *db = calloc(1, sizeof(**db)); + if (*db == NULL) { + return KNOT_ENOMEM; + } + + (*db)->db_path = strdup(path); + if ((*db)->db_path == NULL) { + free(*db); + return KNOT_ENOMEM; + } + + (*db)->db_mapsize = mapsize; + + pthread_mutex_init(&(*db)->opening_mutex, NULL); + return KNOT_EOK; +} + +int kasp_db_reconfigure(kasp_db_t **db, const char *new_path, size_t new_mapsize) +{ + if (db == NULL || new_path == NULL || *db == NULL || (*db)->db_path == NULL) { + return KNOT_EINVAL; + } + + pthread_mutex_lock(&(*db)->opening_mutex); + + bool changed_path = (strcmp(new_path, (*db)->db_path) != 0); + bool changed_mapsize = (new_mapsize != (*db)->db_mapsize); + + if ((*db)->keys_db != NULL) { + pthread_mutex_unlock(&(*db)->opening_mutex); + if (changed_path) { + return KNOT_EBUSY; + } else if (changed_mapsize) { + return KNOT_EEXIST; + } else { + return KNOT_ENODIFF; + } + } + + free((*db)->db_path); + (*db)->db_path = strdup(new_path); + if ((*db)->db_path == NULL) { + pthread_mutex_unlock(&(*db)->opening_mutex); + return KNOT_ENOMEM; + } + (*db)->db_mapsize = new_mapsize; + + pthread_mutex_unlock(&(*db)->opening_mutex); + return KNOT_EOK; +} + +bool kasp_db_exists(kasp_db_t *db) +{ + if (db->keys_db == NULL) { + struct stat st; + if (stat(db->db_path, &st) != 0) { + return false; + } + } + return true; +} + +int kasp_db_open(kasp_db_t *db) +{ + if (db == NULL || db->db_path == NULL) { + return KNOT_EINVAL; + } + + pthread_mutex_lock(&db->opening_mutex); + + if (db->keys_db != NULL) { + pthread_mutex_unlock(&db->opening_mutex); + return KNOT_EOK; // already open + } + + int ret = make_dir(db->db_path, S_IRWXU | S_IRGRP | S_IXGRP, true); + if (ret != KNOT_EOK) { + pthread_mutex_unlock(&db->opening_mutex); + return ret; + } + + struct knot_db_lmdb_opts opts = KNOT_DB_LMDB_OPTS_INITIALIZER; + opts.path = db->db_path; + opts.mapsize = db->db_mapsize; + opts.maxdbs = 1; + opts.dbname = "keys_db"; + + ret = db_api->init(&db->keys_db, NULL, &opts); + if (ret != KNOT_EOK) { + pthread_mutex_unlock(&db->opening_mutex); + return ret; + } + + pthread_mutex_unlock(&db->opening_mutex); + + return ret; +} + +void kasp_db_close(kasp_db_t **db) +{ + if (db != NULL && *db != NULL) { + pthread_mutex_lock(&(*db)->opening_mutex); + db_api->deinit((*db)->keys_db); + (*db)->keys_db = NULL; + pthread_mutex_unlock(&(*db)->opening_mutex); + free((*db)->db_path); + pthread_mutex_destroy(&(*db)->opening_mutex); + free(*db); + *db = NULL; + } +} + +static knot_db_val_t make_key(keyclass_t kclass, const knot_dname_t *dname, const char *str) +{ + size_t dnlen = knot_dname_size(dname); + size_t slen = (str == NULL ? 0 : strlen(str) + 1); + knot_db_val_t res = { .len = 1 + dnlen + slen, .data = malloc(1 + dnlen + slen) }; + if (res.data != NULL) { + wire_ctx_t wire = wire_ctx_init(res.data, res.len); + wire_ctx_write_u8(&wire, (uint8_t)kclass); + wire_ctx_write(&wire, dname, dnlen); + wire_ctx_write(&wire, str, slen); + } else { + res.len = 0; + } + return res; +} + +static void free_key(knot_db_val_t *key) +{ + free(key->data); + memset(key, 0, sizeof(*key)); +} + +static char *keyid_fromkey(const knot_db_val_t *key) +{ + if (key->len < 2 || *(uint8_t *)key->data != KASPDBKEY_PARAMS) { + return NULL; + } + size_t skip = knot_dname_size((const uint8_t *)key->data + 1); + return (key->len < skip + 2 ? NULL : strdup(key->data + skip + 1)); +} + +static bool check_key_zone(const knot_db_val_t *key, const knot_dname_t *zone_name) +{ + if (key->len < 2 || *(uint8_t *)key->data == KASPDBKEY_POLICYLAST) { + return false; + } + return knot_dname_is_equal(key->data + 1, zone_name); +} + +static int serialize_key_params(const key_params_t *params, const knot_dname_t *dname, knot_db_val_t *key, knot_db_val_t *val) +{ + assert(params != NULL); + assert(dname != NULL); + assert(key != NULL); + assert(val != NULL); + + *key = make_key(KASPDBKEY_PARAMS, dname, params->id); + val->len = sizeof(uint16_t) + 2 * sizeof(uint8_t) + 11 * sizeof(uint64_t) + + params->public_key.size; + val->data = malloc(val->len); + if (val->data == NULL) { + free(key->data); + key->data = NULL; + key->len = 0; + return KNOT_ENOMEM; + } + wire_ctx_t wire = wire_ctx_init(val->data, val->len); + + wire_ctx_write_u64(&wire, params->public_key.size); + wire_ctx_write_u64(&wire, 0); // length of Unused-future block at the end + wire_ctx_write_u16(&wire, params->keytag); + wire_ctx_write_u8(&wire, params->algorithm); + uint8_t flags = 0x02; + flags |= (params->is_ksk ? 0x01 : 0); + flags |= (params->is_pub_only ? 0x04 : 0); + flags |= (params->is_csk ? 0x08 : 0); + wire_ctx_write_u8(&wire, flags); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.created); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.pre_active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.publish); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.ready); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.retire_active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.retire); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.post_active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.remove); + wire_ctx_write(&wire, params->public_key.data, params->public_key.size); + + if (wire.error != KNOT_EOK) { + free(key->data); + free(val->data); + key->data = NULL; + key->len = 0; + val->data = NULL; + val->len = 0; + return KNOT_ERROR; + } + return KNOT_EOK; +} + +static int deserialize_key_params(key_params_t *params, const knot_db_val_t *key, const knot_db_val_t *val) +{ + assert(params != NULL); + assert(key != NULL); + assert(val != NULL); + assert(key->data != NULL); + assert(val->data != NULL); + assert(val->len >= sizeof(uint64_t)); + + wire_ctx_t wire = wire_ctx_init_const(val->data, val->len); + params->public_key.size = wire_ctx_read_u64(&wire); + uint64_t unused_future_length = wire_ctx_read_u64(&wire); + params->keytag = wire_ctx_read_u16(&wire); + params->algorithm = wire_ctx_read_u8(&wire); + uint8_t isksk_plus_flags = wire_ctx_read_u8(&wire); + params->is_ksk = ((isksk_plus_flags & (uint8_t)0x01) != (uint8_t)0x00); + params->is_pub_only = ((isksk_plus_flags & (uint8_t)0x04) != (uint8_t)0x00); + params->is_csk = ((isksk_plus_flags & (uint8_t)0x08) != (uint8_t)0x00); + if (params->is_csk && !params->is_ksk) { + return KNOT_EMALF; + } + if ((isksk_plus_flags & (uint8_t)0x02) != (uint8_t)0x00) { + params->timing.created = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.pre_active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.publish = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.ready = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.retire_active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.retire = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.post_active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.remove = (knot_time_t)wire_ctx_read_u64(&wire); + } else { + // import of old kasp db format missing some timers + params->timing.created = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.pre_active = 0; + params->timing.publish = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.ready = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.retire_active = 0; + params->timing.retire = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.post_active = 0; + params->timing.remove = (knot_time_t)wire_ctx_read_u64(&wire); + } + if (wire.error != KNOT_EOK) { + return KNOT_ERROR; + } + + free(params->public_key.data); + params->public_key.data = malloc(params->public_key.size); + if (params->public_key.data == NULL) { + return KNOT_ENOMEM; + } + wire_ctx_read(&wire, params->public_key.data, params->public_key.size); + + free(params->id); + params->id = keyid_fromkey(key); + if (params->id == NULL) { + wire.error = KNOT_EMALF; + } + + if (wire.error != KNOT_EOK || wire_ctx_available(&wire) != unused_future_length) { + free(params->id); + free(params->public_key.data); + params->id = NULL; + params->public_key.data = NULL; + params->public_key.size = 0; + return KNOT_EMALF; + } + return KNOT_EOK; +} + +static key_params_t *keyval2params(const knot_db_val_t *key, const knot_db_val_t *val) +{ + key_params_t *res = calloc(1, sizeof(*res)); + if (res != NULL) { + if (deserialize_key_params(res, key, val) != KNOT_EOK) { + free(res); + return NULL; + } + } + return res; +} + +#define txn_check(...) \ + if (ret != KNOT_EOK) { \ + db_api->txn_abort(txn); \ + va_free(NULL, __VA_ARGS__); \ + return ret; \ + } \ + +#define with_txn(what, ...) \ + int ret = KNOT_EOK; \ + knot_db_txn_t local_txn, *txn = &local_txn; \ + ret = db_api->txn_begin(db->keys_db, txn, (what & 0x1) ? 0 : KNOT_DB_RDONLY); \ + txn_check(__VA_ARGS__); \ + +#define with_txn_end(...) \ + txn_check(__VA_ARGS__); \ + ret = db_api->txn_commit(txn); \ + if (ret != KNOT_EOK) { \ + db_api->txn_abort(txn); \ + } \ + +#define KEYS_RO 0x0 +#define KEYS_RW 0x1 + +// TODO move elsewhere +static void va_free(void *p, ...) +{ + va_list args; + va_start(args, p); + for (void *f = p; f != NULL; f = va_arg(args, void *)) { + free(f); + } + va_end(args); +} + +int kasp_db_list_keys(kasp_db_t *db, const knot_dname_t *zone_name, list_t *dst) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || dst == NULL) { + return KNOT_ENOENT; + } + + knot_db_val_t key = make_key(KASPDBKEY_PARAMS, zone_name, NULL), val = { 0 }; + + with_txn(KEYS_RO, NULL); + knot_db_iter_t *iter = db_api->iter_begin(txn, KNOT_DB_NOOP); + if (iter != NULL) { + iter = db_api->iter_seek(iter, &key, KNOT_DB_GEQ); + } + free_key(&key); + + init_list(dst); + while (iter != NULL && ret == KNOT_EOK) { + ret = db_api->iter_key(iter, &key); + if (ret != KNOT_EOK || *(uint8_t *)key.data != KASPDBKEY_PARAMS || !check_key_zone(&key, zone_name)) { + break; + } + ret = db_api->iter_val(iter, &val); + if (ret == KNOT_EOK) { + key_params_t *parm = keyval2params(&key, &val); + if (parm != NULL) { + ptrlist_add(dst, parm, NULL); + } + iter = db_api->iter_next(iter); + } + } + db_api->iter_finish(iter); + db_api->txn_abort(txn); + + if (ret != KNOT_EOK) { + ptrlist_deep_free(dst, NULL); + return ret; + } + return (EMPTY_LIST(*dst) ? KNOT_ENOENT : KNOT_EOK); +} + +static bool keyid_inuse(knot_db_txn_t *txn, const char *key_id, key_params_t **optional) +{ + knot_db_iter_t *iter = db_api->iter_begin(txn, KNOT_DB_FIRST); + while (iter != NULL) { + knot_db_val_t key, val; + if (db_api->iter_key(iter, &key) == KNOT_EOK && *(uint8_t *)key.data == KASPDBKEY_PARAMS) { + char *keyid = keyid_fromkey(&key); + if (keyid != NULL && strcmp(keyid, key_id) == 0) { + if (optional != NULL && db_api->iter_val(iter, &val) == KNOT_EOK) { + *optional = keyval2params(&key, &val); + } + db_api->iter_finish(iter); + free(keyid); + return true; + } + free(keyid); + } + iter = db_api->iter_next(iter); + } + db_api->iter_finish(iter); + return false; +} + +int kasp_db_delete_key(kasp_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || key_id == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key = make_key(KASPDBKEY_PARAMS, zone_name, key_id); + + with_txn(KEYS_RW, key.data, NULL); + ret = db_api->del(txn, &key); + free_key(&key); + if (still_used != NULL) { + *still_used = keyid_inuse(txn, key_id, NULL); + } + with_txn_end(NULL, NULL); + return ret; +} + +int kasp_db_delete_all(kasp_db_t *db, const knot_dname_t *zone_name) +{ + list_t allkeys; + init_list(&allkeys); + int r = kasp_db_list_keys(db, zone_name, &allkeys); + if (r != KNOT_EOK) { + return r; + } + + with_txn(KEYS_RW, NULL); + + ptrnode_t *n; + WALK_LIST(n, allkeys) { + key_params_t *parm = n->d; + knot_db_val_t key = make_key(KASPDBKEY_PARAMS, zone_name, parm->id); + (void)db_api->del(txn, &key); + free_key(&key); + free(parm->id); + free(parm->public_key.data); + memset(parm, 0, sizeof(*parm)); + } + ptrlist_deep_free(&allkeys, NULL); + + for (keyclass_t keyclass = KASPDBKEY_NSEC3SALT; keyclass <= KASPDBKEY_LASTSIGNEDSERIAL; keyclass++) { + knot_db_val_t key = make_key(keyclass, zone_name, NULL); + (void)db_api->del(txn, &key); + free_key(&key); + } + + with_txn_end(NULL, NULL); + return ret; +} + +int kasp_db_add_key(kasp_db_t *db, const knot_dname_t *zone_name, const key_params_t *params) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || params == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key = { 0 }, val = { 0 }; + + with_txn(KEYS_RW, NULL); + ret = serialize_key_params(params, zone_name, &key, &val); + txn_check(NULL); + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + free_key(&val); + with_txn_end(NULL, NULL); + return ret; +} + +int kasp_db_share_key(kasp_db_t *db, const knot_dname_t *zone_from, const knot_dname_t *zone_to, const char *key_id) +{ + if (db == NULL || db->keys_db == NULL || zone_from == NULL || + zone_to == NULL || key_id == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key_from = make_key(KASPDBKEY_PARAMS, zone_from, key_id), + key_to = make_key(KASPDBKEY_PARAMS, zone_to, key_id), val = { 0 }; + + with_txn(KEYS_RW, NULL); + ret = db_api->find(txn, &key_from, &val, 0); + txn_check(txn, key_from.data, key_to.data, NULL); + ret = db_api->insert(txn, &key_to, &val, 0); + free_key(&key_from); + free_key(&key_to); + with_txn_end(NULL); + return ret; +} + +int kasp_db_store_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name, + const dnssec_binary_t *nsec3salt, knot_time_t salt_created) +{ + if (db == NULL || db->keys_db == NULL || + zone_name == NULL || nsec3salt == NULL || salt_created <= 0) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key(KASPDBKEY_NSEC3SALT, zone_name, NULL); + knot_db_val_t val = { .len = nsec3salt->size, .data = nsec3salt->data }; + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + txn_check(NULL); + key = make_key(KASPDBKEY_NSEC3TIME, zone_name, NULL); + uint64_t tmp = htobe64((uint64_t)salt_created); + val.len = sizeof(tmp); + val.data = &tmp; + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + with_txn_end(NULL); + return ret; +} + +int kasp_db_load_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name, + dnssec_binary_t *nsec3salt, knot_time_t *salt_created) +{ + if (db == NULL || db->keys_db == NULL || + zone_name == NULL || nsec3salt == NULL || salt_created == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key(KASPDBKEY_NSEC3TIME, zone_name, NULL), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + if (val.len == sizeof(uint64_t)) { + *salt_created = (knot_time_t)be64toh(*(uint64_t *)val.data); + } + else { + ret = KNOT_EMALF; + } + } + txn_check(NULL); + key = make_key(KASPDBKEY_NSEC3SALT, zone_name, NULL); + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + nsec3salt->data = malloc(val.len); + if (nsec3salt->data == NULL) { + ret = KNOT_ENOMEM; + } else { + nsec3salt->size = val.len; + memcpy(nsec3salt->data, val.data, val.len); + } + } + with_txn_end(NULL); + return ret; +} + +int kasp_db_store_serial(kasp_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t serial) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL) { + return KNOT_EINVAL; + } + + uint32_t be = htobe32(serial); + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key((keyclass_t)serial_type, zone_name, NULL); + knot_db_val_t val = { .len = sizeof(uint32_t), .data = &be }; + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + with_txn_end(NULL); + return ret; +} + +int kasp_db_load_serial(kasp_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t *serial) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || serial == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RO, NULL); + knot_db_val_t key = make_key((keyclass_t)serial_type, zone_name, NULL), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + if (val.len == sizeof(uint32_t)) { + *serial = be32toh(*(uint32_t *)val.data); + } else { + ret = KNOT_EMALF; + } + } + with_txn_end(NULL); + return ret; +} + +int kasp_db_get_policy_last(kasp_db_t *db, const char *policy_string, knot_dname_t **lp_zone, + char **lp_keyid) +{ + if (db == NULL || db->keys_db == NULL || policy_string == NULL || + lp_zone == NULL || lp_keyid == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RO, NULL); + knot_db_val_t key = make_key(KASPDBKEY_POLICYLAST, NULL, policy_string), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + if (*(uint8_t *)val.data != KASPDBKEY_PARAMS) { + ret = KNOT_EMALF; + } else { + *lp_zone = knot_dname_copy((knot_dname_t *)(val.data + 1), NULL); + *lp_keyid = keyid_fromkey(&val); + if (*lp_zone == NULL || *lp_keyid == NULL) { + free(*lp_zone); + free(*lp_keyid); + ret = KNOT_ENOMEM; + } else { + // check that the shared key ID really exists + key = make_key(KASPDBKEY_PARAMS, *lp_zone, *lp_keyid); + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret != KNOT_EOK) { + free(*lp_zone); + free(*lp_keyid); + } + } + } + } + with_txn_end(NULL); + return ret; +} + +int kasp_db_set_policy_last(kasp_db_t *db, const char *policy_string, const char *last_lp_keyid, + const knot_dname_t *new_lp_zone, const char *new_lp_keyid) +{ + if (db == NULL || db->keys_db == NULL || + new_lp_zone == NULL || new_lp_keyid == NULL) { + return KNOT_EINVAL; + } + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key(KASPDBKEY_POLICYLAST, NULL, policy_string), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + switch (ret) { + case KNOT_EOK: + if (*(uint8_t *)val.data != KASPDBKEY_PARAMS) { + ret = KNOT_EMALF; + } else { + char *real_last = keyid_fromkey(&val); + if (real_last == NULL) { + ret = KNOT_ENOMEM; + } else { + if (last_lp_keyid == NULL || strcmp(real_last, last_lp_keyid) != 0) { + ret = KNOT_ESEMCHECK; + } + free(real_last); + } + } + break; + case KNOT_ENOENT: + ret = KNOT_EOK; + break; + } + if (ret == KNOT_EOK) { + val = make_key(KASPDBKEY_PARAMS, new_lp_zone, new_lp_keyid); + ret = db_api->insert(txn, &key, &val, 0); + free(val.data); + } + free(key.data); + with_txn_end(NULL); + return ret; +} + +int kasp_db_list_zones(kasp_db_t *db, list_t *dst) +{ + if (db == NULL || db->keys_db == NULL || dst == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RO, NULL); + knot_db_iter_t *iter = db_api->iter_begin(txn, KNOT_DB_FIRST); + while (iter != NULL) { + knot_db_val_t key; + if (db_api->iter_key(iter, &key) == KNOT_EOK && key.len > 1 && + *(uint8_t *)key.data != KASPDBKEY_POLICYLAST) { + // obtain a domain name of a record in KASP db + knot_dname_t *key_dn = (knot_dname_t *)(key.data + 1); + // check if not already in dst + ptrnode_t *n; + WALK_LIST(n, *dst) { + knot_dname_t *exist_dn = (knot_dname_t *)n->d; + if (knot_dname_is_equal(key_dn, exist_dn)) { + key_dn = NULL; + break; + } + } + // copy it from txn and add to dst + if (key_dn != NULL) { + knot_dname_t *add_dn = knot_dname_copy(key_dn, NULL); + if (add_dn == NULL) { + ret = KNOT_ENOMEM; + break; + } + ptrlist_add(dst, add_dn, NULL); + } + } + iter = db_api->iter_next(iter); + } + db_api->iter_finish(iter); + db_api->txn_abort(txn); + + if (ret != KNOT_EOK) { + ptrlist_deep_free(dst, NULL); + return ret; + } + return (EMPTY_LIST(*dst) ? KNOT_ENOENT : KNOT_EOK); +} |