summaryrefslogtreecommitdiffstats
path: root/src/knot/zone/zone-load.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/knot/zone/zone-load.c')
-rw-r--r--src/knot/zone/zone-load.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c
new file mode 100644
index 0000000..11cba83
--- /dev/null
+++ b/src/knot/zone/zone-load.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 2023 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/common/log.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/journal/journal_read.h"
+#include "knot/zone/zone-diff.h"
+#include "knot/zone/zone-load.h"
+#include "knot/zone/zonefile.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/zone-events.h"
+#include "libknot/libknot.h"
+
+int zone_load_contents(conf_t *conf, const knot_dname_t *zone_name,
+ zone_contents_t **contents, semcheck_optional_t semcheck_mode,
+ bool fail_on_warning)
+{
+ if (conf == NULL || zone_name == NULL || contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ char *zonefile = conf_zonefile(conf, zone_name);
+
+ zloader_t zl;
+ int ret = zonefile_open(&zl, zonefile, zone_name, semcheck_mode, time(NULL));
+ free(zonefile);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ sem_handler_t handler = {
+ .cb = err_handler_logger
+ };
+
+ zl.err_handler = &handler;
+ zl.creator->master = !zone_load_can_bootstrap(conf, zone_name);
+
+ *contents = zonefile_load(&zl);
+ zonefile_close(&zl);
+ if (*contents == NULL) {
+ return KNOT_ERROR;
+ }
+ if (handler.warning && fail_on_warning) {
+ zone_contents_deep_free(*contents);
+ *contents = NULL;
+ return KNOT_ESEMCHECK;
+ }
+
+ return KNOT_EOK;
+}
+
+static int apply_one_cb(bool remove, const knot_rrset_t *rr, void *ctx)
+{
+ zone_node_t *unused = NULL;
+ zone_contents_t *contents = ctx;
+ int ret = remove ? zone_contents_remove_rr(contents, rr, &unused)
+ : zone_contents_add_rr(contents, rr, &unused);
+ if (ret == KNOT_ENOENT && remove && knot_rrtype_is_dnssec(rr->type)) {
+ // Compatibility with imperfect journal contents (versions < 2.9) if
+ // 'zonefile-load: difference' and 'dnssec-signing: on`.
+ // Journal history can contain a changeset with removed DNSSEC records
+ // which are not present in the zonefile.
+ return KNOT_EOK;
+ } else {
+ return ret;
+ }
+}
+
+int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents)
+{
+ if (conf == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Check if journal is used (later in zone_changes_load() and zone is not empty.
+ if (zone_contents_is_empty(contents)) {
+ return KNOT_EOK;
+ }
+ uint32_t serial = zone_contents_serial(contents);
+
+ journal_read_t *read = NULL;
+ int ret = journal_read_begin(zone_journal(zone), false, serial, &read);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return ret;
+ }
+
+ ret = journal_read_rrsets(read, apply_one_cb, contents);
+ if (ret == KNOT_EOK) {
+ log_zone_info(zone->name, "changes from journal applied, serial %u -> %u",
+ serial, zone_contents_serial(contents));
+ } else {
+ log_zone_error(zone->name, "failed to apply journal changes, serial %u -> %u (%s)",
+ serial, zone_contents_serial(contents),
+ knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+int zone_load_from_journal(conf_t *conf, zone_t *zone, zone_contents_t **contents)
+{
+ if (conf == NULL || zone == NULL || contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ *contents = zone_contents_new(zone->name, true);
+ if (*contents == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ journal_read_t *read = NULL;
+ int ret = journal_read_begin(zone_journal(zone), true, 0, &read);
+ if (ret == KNOT_ENOENT) {
+ zone_contents_deep_free(*contents);
+ *contents = NULL;
+ return ret;
+ }
+
+ knot_rrset_t rr = { 0 };
+ while (ret == KNOT_EOK && journal_read_rrset(read, &rr, false)) {
+ zone_node_t *unused = NULL;
+ ret = zone_contents_add_rr(*contents, &rr, &unused);
+ journal_read_clear_rrset(&rr);
+ }
+
+ if (ret == KNOT_EOK) {
+ ret = journal_read_rrsets(read, apply_one_cb, *contents);
+ } else {
+ journal_read_end(read);
+ }
+
+ if (ret == KNOT_EOK) {
+ log_zone_info(zone->name, "zone loaded from journal, serial %u",
+ zone_contents_serial(*contents));
+ } else {
+ log_zone_error(zone->name, "failed to load zone from journal, serial %u (%s)",
+ zone_contents_serial(*contents), knot_strerror(ret));
+ zone_contents_deep_free(*contents);
+ *contents = NULL;
+ }
+
+ return ret;
+}
+
+bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name)
+{
+ if (conf == NULL || zone_name == NULL) {
+ return false;
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_MASTER, zone_name);
+ size_t count = conf_val_count(&val);
+
+ return count > 0;
+}