diff --git a/bin/named/config.c b/bin/named/config.c index c72ca9fdff..62acda6b70 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -242,6 +242,7 @@ options {\n\ min-transfer-rate-in 10240 5;\n\ multi-master no;\n\ notify yes;\n\ + notify-defer 0;\n\ notify-delay 5;\n\ notify-to-soa no;\n\ serial-update-method increment;\n\ diff --git a/bin/named/server.c b/bin/named/server.c index f5f449decc..8a73d1aca6 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -11245,7 +11245,7 @@ named_server_notifycommand(named_server_t *server, isc_lex_t *lex, return ISC_R_UNEXPECTEDEND; } - dns_zone_notify(zone); + dns_zone_notify(zone, true); dns_zone_detach(&zone); (void)putstr(text, msg); (void)putnull(text); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index fc650690a7..ad0da4644d 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1449,6 +1449,11 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(result == ISC_R_SUCCESS && obj != NULL); dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj)); + obj = NULL; + result = named_config_get(maps, "notify-defer", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setnotifydefer(zone, cfg_obj_asuint32(obj)); + obj = NULL; result = named_config_get(maps, "check-sibling", &obj); INSIST(result == ISC_R_SUCCESS && obj != NULL); diff --git a/bin/tests/system/catz/ns1/named.conf.in b/bin/tests/system/catz/ns1/named.conf.in index fdf8041f31..2c04668280 100644 --- a/bin/tests/system/catz/ns1/named.conf.in +++ b/bin/tests/system/catz/ns1/named.conf.in @@ -69,6 +69,16 @@ view "default" { /* catalog5 is missing on purpose */ + zone "catalog6.example" { + type primary; + file "catalog6.example.db"; + allow-transfer { any; }; + allow-update { any; }; + also-notify { 10.53.0.2; }; + notify explicit; + notify-defer 5; + }; + # No "version" property zone "catalog-bad1.example" { type primary; diff --git a/bin/tests/system/catz/ns2/named1.conf.in b/bin/tests/system/catz/ns2/named1.conf.in index e16416d22d..2b8d05217a 100644 --- a/bin/tests/system/catz/ns2/named1.conf.in +++ b/bin/tests/system/catz/ns2/named1.conf.in @@ -62,6 +62,9 @@ view "default" { #T2 zone "catalog5.example" #T2 min-update-interval 1s #T2 default-primaries { 10.53.0.1; }; + zone "catalog6.example" + min-update-interval 1s + default-primaries { 10.53.0.1; }; zone "catalog-bad1.example" default-masters { 10.53.0.1; } min-update-interval 1s @@ -129,6 +132,12 @@ view "default" { primaries { 10.53.0.1; }; }; + zone "catalog6.example" { + type secondary; + file "catalog6.example.db"; + primaries { 10.53.0.1; }; + }; + # When the following zone configuration is enabled, "dom3.example" should # already exist as a member of "catalog1.example", and named should be able # to deal with that situation (see GL #3911). Make sure that this duplicate diff --git a/bin/tests/system/catz/ns2/named2.conf.in b/bin/tests/system/catz/ns2/named2.conf.in index ead45a85af..2255c6625e 100644 --- a/bin/tests/system/catz/ns2/named2.conf.in +++ b/bin/tests/system/catz/ns2/named2.conf.in @@ -79,6 +79,12 @@ view "default" { primaries { 10.53.0.1; }; }; + zone "catalog6.example" { + type secondary; + file "catalog6.example.db"; + primaries { 10.53.0.1; }; + }; + # No "version" property zone "catalog-bad1.example" { type secondary; diff --git a/bin/tests/system/catz/setup.sh b/bin/tests/system/catz/setup.sh index c33ce7f1c1..c7e24fa1de 100644 --- a/bin/tests/system/catz/setup.sh +++ b/bin/tests/system/catz/setup.sh @@ -22,6 +22,8 @@ cp -f ns1/catalog.example.db.in ns1/catalog1.example.db cp -f ns3/catalog.example.db.in ns3/catalog2.example.db cp -f ns1/catalog.example.db.in ns1/catalog3.example.db cp -f ns1/catalog.example.db.in ns1/catalog4.example.db +# catalog5 is missing on purpose +cp -f ns1/catalog.example.db.in ns1/catalog6.example.db cp -f ns1/catalog.example.db.in ns1/catalog-tls.example.db cp -f ns4/catalog.example.db.in ns4/catalog-self.example.db diff --git a/bin/tests/system/catz/tests.sh b/bin/tests/system/catz/tests.sh index 2dd89ff7f8..5d8c7f3622 100644 --- a/bin/tests/system/catz/tests.sh +++ b/bin/tests/system/catz/tests.sh @@ -2669,6 +2669,133 @@ rndccmd 10.53.0.4 reload || ret=1 if [ $ret -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) +########################################################################## + +nextpart ns2/named.run >/dev/null + +n=$((n + 1)) +echo_i "Adding a domain dom20.example. to primary via RNDC ($n)" +ret=0 +# enough initial content for IXFR response when TXT record is added below +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" >ns1/dom20.example.db +echo "@ 3600 IN NS invalid." >>ns1/dom20.example.db +echo "foo 3600 IN TXT some content here" >>ns1/dom20.example.db +echo "bar 3600 IN TXT some content here" >>ns1/dom20.example.db +echo "xxx 3600 IN TXT some content here" >>ns1/dom20.example.db +echo "yyy 3600 IN TXT some content here" >>ns1/dom20.example.db +rndccmd 10.53.0.1 addzone dom20.example. in default '{ type primary; file "dom20.example.db"; allow-update { any; }; notify explicit; also-notify { 10.53.0.2; }; };' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom20.example. is now served by primary ($n)" +ret=0 +wait_for_soa @10.53.0.1 dom20.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "Adding domain dom20.example. to catalog6 zone ($n)" +ret=0 +$NSUPDATE -d <>nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add dom20.zones.catalog6.example. 3600 IN PTR dom20.example. + send +END +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "waiting for secondary to sync up ($n)" +ret=0 +start=$(date +%s) +wait_for_message ns2/named.run "catz: adding zone 'dom20.example' from catalog 'catalog6.example'" \ + && wait_for_message ns2/named.run "transfer of 'dom20.example/IN/default' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1 +end=$(date +%s) +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that 'notify-defer 5;' worked ($n)" +ret=0 +elapsed=$(($end - $start)) +[ $elapsed -ge 5 ] || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom20.example. is served by secondary ($n)" +ret=0 +wait_for_soa @10.53.0.2 dom20.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +nextpart ns2/named.run >/dev/null + +n=$((n + 1)) +echo_i "Adding a domain dom21.example. to primary via RNDC ($n)" +ret=0 +# enough initial content for IXFR response when TXT record is added below +echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" >ns1/dom21.example.db +echo "@ 3600 IN NS invalid." >>ns1/dom21.example.db +echo "foo 3600 IN TXT some content here" >>ns1/dom21.example.db +echo "bar 3600 IN TXT some content here" >>ns1/dom21.example.db +echo "xxx 3600 IN TXT some content here" >>ns1/dom21.example.db +echo "yyy 3600 IN TXT some content here" >>ns1/dom21.example.db +rndccmd 10.53.0.1 addzone dom21.example. in default '{ type primary; file "dom21.example.db"; allow-update { any; }; notify explicit; also-notify { 10.53.0.2; }; };' || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom21.example. is now served by primary ($n)" +ret=0 +wait_for_soa @10.53.0.1 dom21.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "Adding domain dom21.example. to catalog6 zone ($n)" +ret=0 +$NSUPDATE -d <>nsupdate.out.test$n 2>&1 || ret=1 + server 10.53.0.1 ${PORT} + update add dom21.zones.catalog6.example. 3600 IN PTR dom21.example. + send +END +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "Sending 'rndc notify catalog6.example.' to primary via RNDC ($n)" +ret=0 +rndccmd 10.53.0.1 notify catalog6.example. || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "waiting for secondary to sync up ($n)" +ret=0 +start=$(date +%s) +wait_for_message ns2/named.run "catz: adding zone 'dom21.example' from catalog 'catalog6.example'" \ + && wait_for_message ns2/named.run "transfer of 'dom21.example/IN/default' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1 +end=$(date +%s) +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that 'notify-defer 5;' was ignored because of implicit notify command ($n)" +ret=0 +elapsed=$(($end - $start)) +[ $elapsed -lt 5 ] || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking that dom21.example. is served by secondary ($n)" +ret=0 +wait_for_soa @10.53.0.2 dom21.example. dig.out.test$n || ret=1 +if [ $ret -ne 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + ########################################################################## echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/dyndb/driver/zone.c b/bin/tests/system/dyndb/driver/zone.c index d8c702236f..ec9403ba64 100644 --- a/bin/tests/system/dyndb/driver/zone.c +++ b/bin/tests/system/dyndb/driver/zone.c @@ -219,7 +219,7 @@ load_zone(dns_zone_t *zone) { dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u", serial); if (zone_dynamic) { - dns_zone_notify(zone); + dns_zone_notify(zone, false); } cleanup: diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 75a46f0bff..17b3b6a177 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -4670,6 +4670,29 @@ Tuning when resolving a client query, before terminating the query to avoid a CNAME loop. Valid values are 1 to 255. The default is 11. +.. namedconf:statement:: notify-defer + :tags: transfer, zone + :short: Sets the defer time (in seconds) before sending NOTIFY messages for a zone. + + This sets the delay, in seconds, to wait before sending a set of NOTIFY + messages for a zone. Whenever a NOTIFY message is ready to be sent, sending + will be deferred for this duration. This can be useful, for example, when + for some operation needs a catalog zone is updated with new member zones + before these member zones are actually ready to be tranferred. The delay can + be tuned for the catalog zone to an amount of time after which the member + zones are usually known to become ready. The default is 0 seconds. + + .. warning:: + This option is not to be confused with the :any:`notify-delay` option. + + .. note:: + An implicit :option:`rndc notify` command for a zone overrides the + effects of this option. + + .. note:: + This options is ignored for notifies sent during the :any:`dialup` + process. + .. namedconf:statement:: notify-delay :tags: transfer, zone :short: Sets the delay (in seconds) between sending sets of NOTIFY messages for a zone. @@ -4683,6 +4706,9 @@ Tuning The overall rate at which NOTIFY messages are sent for all zones is controlled by :any:`notify-rate`. + .. warning:: + This option is not to be confused with the :any:`notify-defer` option. + .. namedconf:statement:: max-rsa-exponent-size :tags: dnssec, query :short: Sets the maximum RSA exponent size (in bits) when validating. diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 45a4d36d5c..4528338e20 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -29,6 +29,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/doc/misc/options b/doc/misc/options index c57d32446c..ed925c83b0 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -217,6 +217,7 @@ options { no-case-compress { ; ... }; nocookie-udp-size ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-rate ; notify-source ( | * ); @@ -507,6 +508,7 @@ view [ ] { no-case-compress { ; ... }; nocookie-udp-size ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index edb25741a9..02a178fd7e 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -43,6 +43,7 @@ zone [ ] { max-types-per-name ; max-zone-ttl ( unlimited | ); // deprecated notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index efae9ca461..fc9a24dde9 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -41,6 +41,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-defer ; notify-delay ; notify-source ( | * ); notify-source-v6 ( | * ); diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index e7e3d909a8..57ff194c2b 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1375,9 +1375,10 @@ dns_zone_getredirecttype(dns_zone_t *zone); */ void -dns_zone_notify(dns_zone_t *zone); +dns_zone_notify(dns_zone_t *zone, bool nodefer); /*%< - * Generate notify events for this zone. + * Generate notify events for this zone. If 'nodefer' is true, the + * 'notify-defer' configuration option is ingored. * * Requires: *\li 'zone' to be a valid zone. @@ -2233,6 +2234,16 @@ dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns); * 'zone' to be a valid zone. */ +void +dns_zone_setnotifydefer(dns_zone_t *zone, uint32_t defer); +/*%< + * Set the wait/defer time (in seconds) before notify messages are sent when + * they are ready. + * + * Requires: + * 'zone' to be valid. + */ + void dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay); /*%< diff --git a/lib/dns/zone.c b/lib/dns/zone.c index fce460d88e..d539d4695b 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -401,6 +401,7 @@ struct dns_zone { dns_stats_t *rcvquerystats; dns_stats_t *dnssecsignstats; uint32_t notifydelay; + uint32_t notifydefer; dns_isselffunc_t isself; void *isselfarg; @@ -568,7 +569,12 @@ typedef enum { * notify due to the zone * just being loaded for * the first time. */ - DNS_ZONEFLG_FIRSTREFRESH = 0x100000000U, /*%< First refresh pending */ + DNS_ZONEFLG_NOTIFYNODEFER = 0x100000000U, /*%< ignore the + * notify-defer option. */ + DNS_ZONEFLG_NOTIFYDEFERRED = 0x200000000U, /*%< notify was deferred + * according to the + * notify-defer option. */ + DNS_ZONEFLG_FIRSTREFRESH = 0x400000000U, /*%< First refresh pending */ DNS_ZONEFLG___MAX = UINT64_MAX, /* trick to make the ENUM 64-bit wide */ } dns_zoneflg_t; @@ -1055,6 +1061,19 @@ static const char *dbargv_default[] = { ZONEDB_DEFAULT }; } \ } while (0) +#define DNS_ZONE_TIME_SUBTRACT(a, b, c) \ + do { \ + isc_interval_t _i; \ + isc_interval_set(&_i, (b), 0); \ + if (isc_time_subtract((a), &_i, (c)) != ISC_R_SUCCESS) { \ + dns_zone_log(zone, ISC_LOG_WARNING, \ + "epoch approaching: upgrade required: " \ + "isc_time_subtract() failed"); \ + isc_interval_set(&_i, (b) / 2, 0); \ + (void)isc_time_subtract((a), &_i, (c)); \ + } \ + } while (0) + typedef struct nsec3param nsec3param_t; struct nsec3param { dns_rdata_nsec3param_t rdata; @@ -11167,6 +11186,17 @@ zone_maintenance(dns_zone_t *zone) { * primaries after. */ LOCK_ZONE(zone); + if (zone->notifydefer != 0 && + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYNODEFER) && + !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED)) + { + if (isc_time_compare(&now, &zone->notifytime) > 0) { + zone->notifytime = now; + } + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED); + DNS_ZONE_TIME_ADD(&zone->notifytime, zone->notifydefer, + &zone->notifytime); + } notify = (zone->type == dns_zone_secondary || zone->type == dns_zone_mirror) && (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) || @@ -12694,14 +12724,27 @@ cleanup: } void -dns_zone_notify(dns_zone_t *zone) { +dns_zone_notify(dns_zone_t *zone, bool nodefer) { isc_time_t now; REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); - + if (nodefer) { + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED)) { + /* + * We have previously deferred the notify, but we have a + * new request not to defer it. Reverse the deferring + * operation. + */ + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOTIFYDEFERRED); + DNS_ZONE_TIME_SUBTRACT(&zone->notifytime, + zone->notifydefer, + &zone->notifytime); + } + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOTIFYNODEFER); + } now = isc_time_now(); zone_settimer(zone, &now); UNLOCK_ZONE(zone); @@ -12733,8 +12776,10 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { LOCK_ZONE(zone); startup = !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); - DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); - DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY | + DNS_ZONEFLG_NEEDSTARTUPNOTIFY | + DNS_ZONEFLG_NOTIFYNODEFER | + DNS_ZONEFLG_NOTIFYDEFERRED); notifytype = zone->notifytype; DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime); UNLOCK_ZONE(zone); @@ -19833,7 +19878,7 @@ dns_zone_dialup(dns_zone_t *zone) { DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH)); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY)) { - dns_zone_notify(zone); + dns_zone_notify(zone, true); } if (zone->type != dns_zone_primary && dns_remote_addresses(&zone->primaries) != NULL && @@ -20170,6 +20215,15 @@ dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) { UNLOCK_ZONE(zone); } +void +dns_zone_setnotifydefer(dns_zone_t *zone, uint32_t defer) { + REQUIRE(DNS_ZONE_VALID(zone)); + + LOCK_ZONE(zone); + zone->notifydefer = defer; + UNLOCK_ZONE(zone); +} + void dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay) { REQUIRE(DNS_ZONE_VALID(zone)); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 3daaeae911..ff0cc9da42 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2496,6 +2496,8 @@ static cfg_clausedef_t zone_clauses[] = { CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB }, { "notify", &cfg_type_notifytype, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, + { "notify-defer", &cfg_type_uint32, + CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "notify-delay", &cfg_type_uint32, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "notify-source", &cfg_type_sockaddr4wild, diff --git a/lib/ns/update.c b/lib/ns/update.c index 51d7f2fc04..f53255f66a 100644 --- a/lib/ns/update.c +++ b/lib/ns/update.c @@ -3431,7 +3431,7 @@ update_action(void *arg) { /* * Notify secondaries of the change we just made. */ - dns_zone_notify(zone); + dns_zone_notify(zone, false); } else { update_log(client, zone, LOGLEVEL_DEBUG, "redundant request"); dns_db_closeversion(db, &ver, true);