From f449f278dd3c70e479a035f50a9bb817a9b433ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:24:08 +0200 Subject: Adding upstream version 3.2.6. Signed-off-by: Daniel Baumann --- src/knot/zone/timers.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/knot/zone/timers.c (limited to 'src/knot/zone/timers.c') diff --git a/src/knot/zone/timers.c b/src/knot/zone/timers.c new file mode 100644 index 0000000..32c22b3 --- /dev/null +++ b/src/knot/zone/timers.c @@ -0,0 +1,228 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. + + 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 . + */ + +#include "knot/zone/timers.h" + +#include "contrib/wire_ctx.h" +#include "knot/zone/zonedb.h" + +/* + * # Timer database + * + * Timer database stores timestamps of events which need to be retained + * across server restarts. The key in the database is the zone name in + * wire format. The value contains serialized timers. + * + * # Serialization format + * + * The value is a sequence of timers. Each timer consists of the timer + * identifier (1 byte, unsigned integer) and timer value (8 bytes, unsigned + * integer, network order). + * + * For example, the following byte sequence: + * + * 81 00 00 00 00 57 e3 e8 0a 82 00 00 00 00 57 e3 e9 a1 + * + * Encodes the following timers: + * + * last_flush = 1474553866 + * last_refresh = 1474554273 + */ + +/*! + * \brief Timer database fields identifiers. + * + * Valid ID starts with '1' in MSB to avoid conflicts with "old timers". + */ +enum timer_id { + TIMER_INVALID = 0, + TIMER_SOA_EXPIRE = 0x80, // DEPRECATED + TIMER_LAST_FLUSH = 0x81, + TIMER_LAST_REFRESH = 0x82, // DEPRECATED + TIMER_NEXT_REFRESH = 0x83, + TIMER_NEXT_DS_CHECK = 0x85, + TIMER_NEXT_DS_PUSH = 0x86, + TIMER_CATALOG_MEMBER = 0x87, + TIMER_LAST_NOTIFIED = 0x88, + TIMER_LAST_REFR_OK = 0x89, + TIMER_NEXT_EXPIRE = 0x8a, +}; + +#define TIMER_SIZE (sizeof(uint8_t) + sizeof(uint64_t)) + +/*! + * \brief Deserialize timers from a binary buffer. + * + * \note Unknown timers are ignored. + */ +static int deserialize_timers(zone_timers_t *timers_ptr, + const uint8_t *data, size_t size) +{ + if (!timers_ptr || !data) { + return KNOT_EINVAL; + } + + zone_timers_t timers = { 0 }; + + wire_ctx_t wire = wire_ctx_init_const(data, size); + while (wire_ctx_available(&wire) >= TIMER_SIZE) { + uint8_t id = wire_ctx_read_u8(&wire); + uint64_t value = wire_ctx_read_u64(&wire); + switch (id) { + case TIMER_SOA_EXPIRE: timers.soa_expire = value; break; + case TIMER_LAST_FLUSH: timers.last_flush = value; break; + case TIMER_LAST_REFRESH: timers.last_refresh = value; break; + case TIMER_NEXT_REFRESH: timers.next_refresh = value; break; + case TIMER_LAST_REFR_OK: timers.last_refresh_ok = value; break; + case TIMER_LAST_NOTIFIED: timers.last_notified_serial = value; break; + case TIMER_NEXT_DS_CHECK: timers.next_ds_check = value; break; + case TIMER_NEXT_DS_PUSH: timers.next_ds_push = value; break; + case TIMER_CATALOG_MEMBER: timers.catalog_member = value; break; + case TIMER_NEXT_EXPIRE: timers.next_expire = value; break; + default: break; // ignore + } + } + + if (wire_ctx_available(&wire) != 0) { + return KNOT_EMALF; + } + + assert(wire.error == KNOT_EOK); + + *timers_ptr = timers; + return KNOT_EOK; +} + +static void txn_write_timers(knot_lmdb_txn_t *txn, const knot_dname_t *zone, + const zone_timers_t *timers) +{ + MDB_val k = { knot_dname_size(zone), (void *)zone }; + MDB_val v = knot_lmdb_make_key("BLBLBLBLBLBLBLBL", + TIMER_LAST_FLUSH, (uint64_t)timers->last_flush, + TIMER_NEXT_REFRESH, (uint64_t)timers->next_refresh, + TIMER_LAST_REFR_OK, (uint64_t)timers->last_refresh_ok, + TIMER_LAST_NOTIFIED, timers->last_notified_serial, + TIMER_NEXT_DS_CHECK, (uint64_t)timers->next_ds_check, + TIMER_NEXT_DS_PUSH, (uint64_t)timers->next_ds_push, + TIMER_CATALOG_MEMBER,(uint64_t)timers->catalog_member, + TIMER_NEXT_EXPIRE, (uint64_t)timers->next_expire); + knot_lmdb_insert(txn, &k, &v); + free(v.mv_data); +} + + +int zone_timers_open(const char *path, knot_db_t **db, size_t mapsize) +{ + if (path == NULL || db == NULL) { + return KNOT_EINVAL; + } + + struct knot_db_lmdb_opts opts = KNOT_DB_LMDB_OPTS_INITIALIZER; + opts.mapsize = mapsize; + opts.path = path; + + return knot_db_lmdb_api()->init(db, NULL, &opts); +} + +void zone_timers_close(knot_db_t *db) +{ + if (db == NULL) { + return; + } + + knot_db_lmdb_api()->deinit(db); +} + +int zone_timers_read(knot_lmdb_db_t *db, const knot_dname_t *zone, + zone_timers_t *timers) +{ + if (knot_lmdb_exists(db) == KNOT_ENODB) { + return KNOT_ENODB; + } + int ret = knot_lmdb_open(db); + if (ret != KNOT_EOK) { + return ret; + } + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, false); + MDB_val k = { knot_dname_size(zone), (void *)zone }; + if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) { + deserialize_timers(timers, txn.cur_val.mv_data, txn.cur_val.mv_size); + } + knot_lmdb_abort(&txn); + + // backward compatibility + // For catalog zones, next_expire is cleaned up later by zone_timers_sanitize(). + if (timers->next_expire == 0 && timers->last_refresh > 0) { + timers->next_expire = timers->last_refresh + timers->soa_expire; + } + + return txn.ret; +} + +int zone_timers_write(knot_lmdb_db_t *db, const knot_dname_t *zone, + const zone_timers_t *timers) +{ + knot_lmdb_txn_t txn = { 0 }; + knot_lmdb_begin(db, &txn, true); + txn_write_timers(&txn, zone, timers); + knot_lmdb_commit(&txn); + return txn.ret; +} + +static void txn_zone_write(zone_t *z, knot_lmdb_txn_t *txn) +{ + txn_write_timers(txn, z->name, &z->timers); +} + +int zone_timers_write_all(knot_lmdb_db_t *db, knot_zonedb_t *zonedb) +{ + 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_zonedb_foreach(zonedb, txn_zone_write, &txn); + knot_lmdb_commit(&txn); + return txn.ret; +} + +int zone_timers_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 (!keep_zone((const knot_dname_t *)txn.cur_key.mv_data, cb_data)) { + knot_lmdb_del_cur(&txn); + } + } + knot_lmdb_commit(&txn); + return txn.ret; +} + +bool zone_timers_serial_notified(const zone_timers_t *timers, uint32_t serial) +{ + return (timers->last_notified_serial & LAST_NOTIFIED_SERIAL_VALID) && + ((uint32_t)timers->last_notified_serial == serial); +} -- cgit v1.2.3