diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in index a3cf08ac5..3f0c3c521 100644 --- a/doc/man/knot.conf.5in +++ b/doc/man/knot.conf.5in @@ -982,7 +982,7 @@ zone: semantic\-checks: BOOL disable\-any: BOOL zonefile\-sync: TIME - zonefile\-load: none | difference | whole + zonefile\-load: none | difference | difference\-no\-serial | whole journal\-content: none | changes | all max\-journal\-usage: SIZE max\-journal\-depth: INT @@ -1141,8 +1141,11 @@ Possible values: .IP \(bu 2 \fBnone\fP – The zonefile is not used at all. .IP \(bu 2 -\fBdifference\fP – If the zone contents are available during server start or reload, -the difference is computed between them and the zonefile, checked and applied afterwards. +\fBdifference\fP – If the zone contents is available during server start or reload, +the difference is computed between them and the zonefile, checked, and applied afterwards. +.IP \(bu 2 +\fBdifference\-no\-serial\fP – Same as \fBdifference\fP, but the SOA serial in the zonefile is +ignored, the server takes care of incrementing the serial automatically. .IP \(bu 2 \fBwhole\fP – Zone contents are loaded from zonefile. .UNINDENT diff --git a/doc/reference.rst b/doc/reference.rst index 5cf31b5ad..dd6da553c 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -1113,7 +1113,7 @@ Definition of zones served by the server. semantic-checks: BOOL disable-any: BOOL zonefile-sync: TIME - zonefile-load: none | difference | whole + zonefile-load: none | difference | difference-no-serial | whole journal-content: none | changes | all max-journal-usage: SIZE max-journal-depth: INT @@ -1288,8 +1288,10 @@ Selects how the zonefile contents are applied during zone load. Possible values: - ``none`` – The zonefile is not used at all. -- ``difference`` – If the zone contents are available during server start or reload, - the difference is computed between them and the zonefile, checked and applied afterwards. +- ``difference`` – If the zone contents is available during server start or reload, + the difference is computed between them and the zonefile, checked, and applied afterwards. +- ``difference-no-serial`` – Same as ``difference``, but the SOA serial in the zonefile is + ignored, the server takes care of incrementing the serial automatically. - ``whole`` – Zone contents are loaded from zonefile. When ``difference`` is configured and there are no zone contents yet (cold start of Knot diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c index d8391cd18..34743571a 100644 --- a/src/knot/conf/schema.c +++ b/src/knot/conf/schema.c @@ -103,6 +103,7 @@ static const knot_lookup_t journal_content[] = { static const knot_lookup_t zonefile_load[] = { { ZONEFILE_LOAD_NONE, "none" }, { ZONEFILE_LOAD_DIFF, "difference" }, + { ZONEFILE_LOAD_DIFSE, "difference-no-serial" }, { ZONEFILE_LOAD_WHOLE, "whole" }, { 0, NULL } }; diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h index 16ee36321..533215cb4 100644 --- a/src/knot/conf/schema.h +++ b/src/knot/conf/schema.h @@ -145,6 +145,7 @@ enum { ZONEFILE_LOAD_NONE = 0, ZONEFILE_LOAD_DIFF = 1, ZONEFILE_LOAD_WHOLE = 2, + ZONEFILE_LOAD_DIFSE = 3, }; extern const knot_lookup_t acl_actions[]; diff --git a/src/knot/events/handlers/load.c b/src/knot/events/handlers/load.c index 7fdb79977..b02b1cd53 100644 --- a/src/knot/events/handlers/load.c +++ b/src/knot/events/handlers/load.c @@ -95,6 +95,17 @@ int event_load(conf_t *conf, zone_t *zone) } goto cleanup; } + + // If configured and possible, fix the SOA serial of zonefile. + if (zf_conts != NULL && zf_from == ZONEFILE_LOAD_DIFSE) { + zone_contents_t *relevant = (zone->contents != NULL ? zone->contents : journal_conts); + if (relevant != NULL) { + uint32_t serial = zone_contents_serial(relevant); + conf_val_t policy = conf_zone_get(conf, C_SERIAL_POLICY, zone->name); + zone_contents_set_soa_serial(zf_conts, serial_next(serial, conf_opt(&policy))); + } + } + // If configured and appliable to zonefile, load journal changes. zone->zonefile.serial = zone_contents_serial(zf_conts); zone->zonefile.exists = (zf_conts != NULL); @@ -201,6 +212,17 @@ int event_load(conf_t *conf, zone_t *zone) zf_conts = NULL; journal_conts = NULL; + // If the change is only automatically incremented SOA serial, make it no change. + if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & UPDATE_INCREMENTAL) && + changeset_differs_just_serial(&up.change)) { + uint32_t orig_ser = knot_soa_serial(&up.change.soa_from->rrs); + ret = changeset_remove_addition(&up.change, up.change.soa_to); + zone_contents_set_soa_serial(up.new_cont, orig_ser); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + // Sign zone using DNSSEC if configured. zone_sign_reschedule_t dnssec_refresh = { .allow_rollover = true, .allow_nsec3resalt = true, }; if (dnssec_enable) { diff --git a/src/knot/updates/changesets.c b/src/knot/updates/changesets.c index 26a048d9a..b19e69fdc 100644 --- a/src/knot/updates/changesets.c +++ b/src/knot/updates/changesets.c @@ -531,6 +531,25 @@ int changeset_cancelout(changeset_t *ch) return ret; } +bool changeset_differs_just_serial(const changeset_t *ch) +{ + if (ch == NULL || ch->soa_from == NULL || ch->soa_to == NULL) { + return false; + } + + if (!zone_contents_is_empty(ch->remove) || !zone_contents_is_empty(ch->add)) { + return false; + } + + knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL); + knot_soa_serial_set(&soa_to_cpy->rrs, knot_soa_serial(&ch->soa_from->rrs)); + + bool res = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE); + knot_rrset_free(soa_to_cpy, NULL); + + return res; +} + int changeset_to_contents(changeset_t *ch, zone_contents_t **out) { assert(ch->soa_from == NULL); diff --git a/src/knot/updates/changesets.h b/src/knot/updates/changesets.h index 47be66458..4aa345b33 100644 --- a/src/knot/updates/changesets.h +++ b/src/knot/updates/changesets.h @@ -158,6 +158,16 @@ int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *ch); */ int changeset_cancelout(changeset_t *ch); +/*! + * \brief Check the changes and SOA, ignoring possibly updated SOA serial. + * + * \param ch Changeset in question. + * + * \return false if the changeset changes other records than SOA, or some SOA field other than serial changed + * true otherwise + */ +bool changeset_differs_just_serial(const changeset_t *ch); + /*! * \brief Loads zone contents from botstrap changeset. *