From 7b2442cc0a58a9aa1e45e155c5d669f4b53bc36b Mon Sep 17 00:00:00 2001 From: Libor Peltan Date: Wed, 3 Dec 2025 11:17:57 +0100 Subject: [PATCH] xfr: finalize timers and master serial only after commit --- src/knot/dnssec/zone-events.h | 1 + src/knot/events/handlers/dnssec.c | 12 ++--- src/knot/events/handlers/refresh.c | 74 +++++++++++++++--------------- src/knot/updates/zone-update.c | 10 ++++ src/knot/updates/zone-update.h | 13 ++++++ 5 files changed, 66 insertions(+), 44 deletions(-) diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h index 6bad0b7169..b604925d05 100644 --- a/src/knot/dnssec/zone-events.h +++ b/src/knot/dnssec/zone-events.h @@ -37,6 +37,7 @@ typedef struct { bool keys_changed; bool plan_ds_check; bool plan_dnskey_sync; + bool zone_changed; } zone_sign_reschedule_t; typedef struct { diff --git a/src/knot/events/handlers/dnssec.c b/src/knot/events/handlers/dnssec.c index d669d1e237..ba52ddb4aa 100644 --- a/src/knot/events/handlers/dnssec.c +++ b/src/knot/events/handlers/dnssec.c @@ -28,7 +28,7 @@ static void log_dnssec_next(const knot_dname_t *zone, knot_time_t refresh_at) } void event_dnssec_reschedule(conf_t *conf, zone_t *zone, - const zone_sign_reschedule_t *refresh, bool zone_changed) + const zone_sign_reschedule_t *refresh) { time_t now = time(NULL); time_t ignore = -1; @@ -49,7 +49,7 @@ void event_dnssec_reschedule(conf_t *conf, zone_t *zone, ZONE_EVENT_DS_CHECK, refresh->plan_ds_check ? now : ignore, ZONE_EVENT_DNSKEY_SYNC, refresh->plan_dnskey_sync ? now + jitter : ignore ); - if (zone_changed) { + if (refresh->zone_changed) { zone_schedule_notify(zone, 0); } } @@ -61,7 +61,6 @@ int event_dnssec(conf_t *conf, zone_t *zone) zone_sign_reschedule_t resch = { 0 }; zone_sign_roll_flags_t r_flags = KEY_ROLL_ALLOW_ALL; int sign_flags = 0; - bool zone_changed = false; if (zone_get_flag(zone, ZONE_FORCE_RESIGN, true)) { log_zone_info(zone->name, "DNSSEC, dropping previous " @@ -90,7 +89,9 @@ int event_dnssec(conf_t *conf, zone_t *zone) goto done; } - zone_changed = !zone_update_no_change(&up); + resch.zone_changed = !zone_update_no_change(&up); + + zone_update_set_post_commit(&up, (zone_update_commit_cb_t)event_dnssec_reschedule, &resch); ret = zone_update_commit(conf, &up); if (ret != KNOT_EOK) { @@ -98,9 +99,6 @@ int event_dnssec(conf_t *conf, zone_t *zone) } done: - // Schedule dependent events - event_dnssec_reschedule(conf, zone, &resch, zone_changed); - if (ret != KNOT_EOK) { zone_update_clear(&up); } diff --git a/src/knot/events/handlers/refresh.c b/src/knot/events/handlers/refresh.c index 745200e816..bf7b8bf401 100644 --- a/src/knot/events/handlers/refresh.c +++ b/src/knot/events/handlers/refresh.c @@ -114,10 +114,13 @@ struct refresh_data { enum state state; //!< Event processing state. enum xfr_type xfr_type; //!< Transfer type (mostly IXFR versus AXFR). bool axfr_style_ixfr; //!< Master responded with AXFR-style-IXFR. + bool axfr_bootstrap; knot_rrset_t *initial_soa_copy; //!< Copy of the received initial SOA. struct xfr_stats stats; //!< Transfer statistics. struct timespec started; //!< When refresh started. size_t change_size; //!< Size of added and removed RRs. + uint32_t old_serial; + uint32_t master_serial; struct { zone_contents_t *zone; //!< AXFR result, new zone. @@ -330,19 +333,44 @@ static void axfr_slave_sign_serial(zone_contents_t *new_contents, zone_t *zone, zone_contents_set_soa_serial(new_contents, new_serial); } +static void post_commit(conf_t *conf, zone_t *zone, void *ctx) +{ + struct refresh_data *data = ctx; + conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name); + bool dnssec_enable = conf_bool(&val); + + if (dnssec_enable && (data->xfr_type == XFR_TYPE_AXFR || !EMPTY_LIST(data->ixfr.changesets))) { + int ret = zone_set_master_serial(data->zone, data->master_serial); + if (ret != KNOT_EOK) { + log_zone_warning(data->zone->name, + "unable to save master serial, future transfers might be broken"); + } + } + + finalize_timers(data); + xfr_log_publish(data, data->old_serial, zone_contents_serial(data->zone->contents), + data->master_serial, dnssec_enable, data->axfr_bootstrap); + + + if (data->xfr_type == XFR_TYPE_AXFR || data->old_serial != zone_contents_serial(data->zone->contents)) { + data->fallback->remote = false; + zone_set_last_master(data->zone, (const struct sockaddr_storage *)data->remote); + } +} + static int axfr_finalize(struct refresh_data *data) { zone_contents_t *new_zone = data->axfr.zone; conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name); bool dnssec_enable = conf_bool(&val); - uint32_t old_serial = zone_contents_serial(data->zone->contents), master_serial = 0; - bool bootstrap = (data->zone->contents == NULL); + data->old_serial = zone_contents_serial(data->zone->contents); + data->axfr_bootstrap = (data->zone->contents == NULL); zone_skip_t skip = { 0 }; int ret = KNOT_EOK; if (dnssec_enable) { - axfr_slave_sign_serial(new_zone, data->zone, data->conf, &master_serial); + axfr_slave_sign_serial(new_zone, data->zone, data->conf, &data->master_serial); ret = zone_skip_add_dnssec_diff(&skip); assert(ret == KNOT_EOK); // static size of zone_skip is enough to cover dnssec types } @@ -388,6 +416,8 @@ static int axfr_finalize(struct refresh_data *data) return ret; } + zone_update_set_post_commit(&up, post_commit, data); + ret = zone_update_commit(data->conf, &up); if (ret != KNOT_EOK) { zone_update_clear(&up); @@ -397,21 +427,6 @@ static int axfr_finalize(struct refresh_data *data) return ret; } - if (dnssec_enable) { - ret = zone_set_master_serial(data->zone, master_serial); - if (ret != KNOT_EOK) { - log_zone_warning(data->zone->name, - "unable to save master serial, future transfers might be broken"); - } - } - - finalize_timers(data); - xfr_log_publish(data, old_serial, zone_contents_serial(data->zone->contents), - master_serial, dnssec_enable, bootstrap); - - data->fallback->remote = false; - zone_set_last_master(data->zone, (const struct sockaddr_storage *)data->remote); - return KNOT_EOK; } @@ -595,10 +610,10 @@ static int ixfr_finalize(struct refresh_data *data) { conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name); bool dnssec_enable = conf_bool(&val); - uint32_t master_serial = 0, old_serial = zone_contents_serial(data->zone->contents); + data->old_serial = zone_contents_serial(data->zone->contents); if (dnssec_enable) { - int ret = ixfr_slave_sign_serial(&data->ixfr.changesets, data->zone, data->conf, &master_serial); + int ret = ixfr_slave_sign_serial(&data->ixfr.changesets, data->zone, data->conf, &data->master_serial); if (ret != KNOT_EOK) { IXFRIN_LOG(LOG_WARNING, data, "failed to adjust SOA serials from unsigned remote (%s)", @@ -664,6 +679,8 @@ static int ixfr_finalize(struct refresh_data *data) return ret; } + zone_update_set_post_commit(&up, post_commit, data); + ret = zone_update_commit(data->conf, &up); if (ret != KNOT_EOK) { zone_update_clear(&up); @@ -672,23 +689,6 @@ static int ixfr_finalize(struct refresh_data *data) return ret; } - if (dnssec_enable && !EMPTY_LIST(data->ixfr.changesets)) { - ret = zone_set_master_serial(data->zone, master_serial); - if (ret != KNOT_EOK) { - log_zone_warning(data->zone->name, - "unable to save master serial, future transfers might be broken"); - } - } - - finalize_timers(data); - xfr_log_publish(data, old_serial, zone_contents_serial(data->zone->contents), - master_serial, dnssec_enable, false); - - if (old_serial != zone_contents_serial(data->zone->contents)) { - data->fallback->remote = false; - zone_set_last_master(data->zone, (const struct sockaddr_storage *)data->remote); - } - return KNOT_EOK; } diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c index b936643ee8..13be9baaff 100644 --- a/src/knot/updates/zone-update.c +++ b/src/knot/updates/zone-update.c @@ -1038,11 +1038,21 @@ int zone_update_commit(conf_t *conf, zone_update_t *update) zone_contents_serial(update->zone->contents)); } + if (update->post_commit_cb != NULL) { + update->post_commit_cb(conf, update->zone, update->post_commit_cb_ctx); + } + memset(update, 0, sizeof(*update)); return KNOT_EOK; } +void zone_update_set_post_commit(zone_update_t *update, zone_update_commit_cb_t cb, void *cb_ctx) +{ + update->post_commit_cb = cb; + update->post_commit_cb_ctx = cb_ctx; +} + bool zone_update_no_change(zone_update_t *update) { if (update == NULL) { diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h index f4f6ca94ef..c7b7938fc6 100644 --- a/src/knot/updates/zone-update.h +++ b/src/knot/updates/zone-update.h @@ -20,6 +20,8 @@ typedef struct { int warning; } dnssec_validation_hint_t; +typedef void (*zone_update_commit_cb_t)(conf_t *, zone_t *, void *); + /*! \brief Structure for zone contents updating / querying. */ typedef struct zone_update { zone_t *zone; /*!< Zone being updated. */ @@ -29,6 +31,8 @@ typedef struct zone_update { changeset_t extra_ch; /*!< Extra changeset to store just diff btwn zonefile and result. */ apply_ctx_t *a_ctx; /*!< Context for applying changesets. */ uint32_t flags; /*!< Zone update flags. */ + void *post_commit_cb_ctx; + zone_update_commit_cb_t post_commit_cb; dnssec_validation_hint_t validation_hint; } zone_update_t; @@ -278,6 +282,15 @@ int zone_update_verify_digest(conf_t *conf, zone_update_t *update); */ int zone_update_commit(conf_t *conf, zone_update_t *update); +/*! + * \brief Set a callback to be called after successful zone_update_commit. + * + * \param update Zone update. + * \param cb Callback. + * \param cb_ctx Arbitrary context. + */ +void zone_update_set_post_commit(zone_update_t *update, zone_update_commit_cb_t cb, void *cb_ctx); + /*! * \brief Returns bool whether there are any changes at all. * -- 2.47.3