xfr: finalize timers and master serial only after commit

This commit is contained in:
Libor Peltan 2025-12-03 11:17:57 +01:00
parent 4c9feb2572
commit 7b2442cc0a
5 changed files with 66 additions and 44 deletions

View file

@ -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 {

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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.
*