From 10c12aa5493f34920585164c5fb54a7ac9109fbd Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Mon, 29 Sep 2014 10:01:08 +1000 Subject: [PATCH] 3956. [func] Notify messages are now rate limited by notify-rate and startup-notify-rate instead of serial-query-rate. [RT #24454] 3955. [bug] Notify messages due to changes are no longer queued behind startup notify messages. [RT #24454] --- CHANGES | 7 + README | 2 + bin/named/config.c | 2 + bin/named/server.c | 10 ++ bin/tests/system/notify/clean.sh | 3 +- bin/tests/system/notify/ns2/generic.db | 28 ++++ bin/tests/system/notify/ns2/named.conf | 24 ++- bin/tests/system/notify/tests.sh | 16 +- doc/arm/Bv9ARM-book.xml | 36 ++++- lib/dns/include/dns/zone.h | 36 +++++ lib/dns/win32/libdns.def.in | 4 + lib/dns/zone.c | 203 +++++++++++++++++++------ lib/isc/include/isc/ratelimiter.h | 10 ++ lib/isc/ratelimiter.c | 42 ++++- lib/isc/win32/libisc.def.in | 1 + lib/isccfg/namedconf.c | 2 + 16 files changed, 367 insertions(+), 59 deletions(-) create mode 100644 bin/tests/system/notify/ns2/generic.db diff --git a/CHANGES b/CHANGES index 8fff791141..cfdbcae7bd 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +3956. [func] Notify messages are now rate limited by notify-rate and + startup-notify-rate instead of serial-query-rate. + [RT #24454] + +3955. [bug] Notify messages due to changes are no longer queued + behind startup notify messages. [RT #24454] + 3954. [bug] Unchecked mutex init in dlz_dlopen_driver.c [RT #37112] 3953. [bug] Don't escape semi-colon in TXT fields. [RT #37159] diff --git a/README b/README index 1fa55f1a94..14b6a15d48 100644 --- a/README +++ b/README @@ -95,6 +95,8 @@ BIND 9.11.0 the specified file by default instead of to the system log. - dig can now set arbitary EDNS options on requests (+ednsopt). - dig can now set yet-to-be-defined EDNS flags on requests (+ednsflags). + - serial-query-rate no longer covers NOTIFY messages. These are + seperately controlled by notity-rate and startup-notify-rate. This release addresses the security flaw described in CVE-2014-3214 and CVE-2014-3859. diff --git a/bin/named/config.c b/bin/named/config.c index ef79a111da..dbc156aba8 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -81,6 +81,7 @@ options {\n\ # named-xfer ;\n\ nta-lifetime 3600;\n\ nta-recheck 300;\n\ + notify-rate 20;\n\ # pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\ port 53;\n\ prefetch 2 9;\n\ @@ -99,6 +100,7 @@ options {\n\ serial-queries 20;\n\ serial-query-rate 20;\n\ server-id none;\n\ + startup-notify-rate 20;\n\ statistics-file \"named.stats\";\n\ statistics-interval 60;\n\ tcp-clients 100;\n\ diff --git a/bin/named/server.c b/bin/named/server.c index 443a050e1e..87896940aa 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -5701,6 +5701,16 @@ load_configuration(const char *filename, ns_server_t *server, INSIST(result == ISC_R_SUCCESS); dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj)); + obj = NULL; + result = ns_config_get(maps, "notify-rate", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_setnotifyrate(server->zonemgr, cfg_obj_asuint32(obj)); + + obj = NULL; + result = ns_config_get(maps, "startup-notify-rate", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_setstartupnotifyrate(server->zonemgr, cfg_obj_asuint32(obj)); + obj = NULL; result = ns_config_get(maps, "serial-query-rate", &obj); INSIST(result == ISC_R_SUCCESS); diff --git a/bin/tests/system/notify/clean.sh b/bin/tests/system/notify/clean.sh index 55569f36d3..fc2982b421 100644 --- a/bin/tests/system/notify/clean.sh +++ b/bin/tests/system/notify/clean.sh @@ -15,12 +15,11 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: clean.sh,v 1.14 2011/10/17 23:46:33 tbox Exp $ - # # Clean up after zone transfer tests. # rm -f ns3/example.bk dig.out.ns2.test* dig.out.ns3.test* rm -f ns2/example.db +rm -f log.out rm -f */named.memstats diff --git a/bin/tests/system/notify/ns2/generic.db b/bin/tests/system/notify/ns2/generic.db new file mode 100644 index 0000000000..1fb7e8ae96 --- /dev/null +++ b/bin/tests/system/notify/ns2/generic.db @@ -0,0 +1,28 @@ +; Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 1 ; serial + 300 ; refresh (300 seconds) + 300 ; retry (300 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns2 + NS ns3 +ns2 A 10.53.0.2 +ns3 A 10.53.0.3 + +a A 10.0.0.1 diff --git a/bin/tests/system/notify/ns2/named.conf b/bin/tests/system/notify/ns2/named.conf index 139114639e..8727b73187 100644 --- a/bin/tests/system/notify/ns2/named.conf +++ b/bin/tests/system/notify/ns2/named.conf @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named.conf,v 1.23 2011/12/20 00:06:54 marka Exp $ */ - controls { /* empty */ }; options { @@ -29,6 +27,7 @@ options { listen-on-v6 { none; }; recursion no; notify yes; + startup-notify-rate 5; }; zone "." { @@ -42,3 +41,24 @@ zone "example" { // Check that named can handle a empty also-notify. also-notify { /* empty */ }; }; + +zone x1 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x2 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x3 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x4 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x5 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x6 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x7 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x8 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x9 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x10 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x11 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x12 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x13 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x14 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x15 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x16 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x17 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x18 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x19 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; +zone x20 { type master; file "generic.db"; also-notify { 10.53.0.3; }; }; diff --git a/bin/tests/system/notify/tests.sh b/bin/tests/system/notify/tests.sh index d3096741db..74bd2be4b0 100644 --- a/bin/tests/system/notify/tests.sh +++ b/bin/tests/system/notify/tests.sh @@ -15,8 +15,6 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: tests.sh,v 1.36 2011/10/17 01:33:27 marka Exp $ - SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh @@ -35,6 +33,8 @@ do $DIG +tcp example @10.53.0.3 soa -p 5300 > dig.out.ns3.test$n || ret=1 grep "status: NOERROR" dig.out.ns3.test$n > /dev/null || ret=1 grep "flags:.* aa[ ;]" dig.out.ns3.test$n > /dev/null || ret=1 + nr=`grep 'x[0-9].*sending notify to' ns2/named.run | wc -l` + [ $nr -eq 20 ] || ret=1 [ $ret = 0 ] && break sleep 1 done @@ -55,6 +55,18 @@ $PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns3.test$n || ret=1 [ $ret = 0 ] || echo "I:failed" status=`expr $ret + $status` +n=`expr $n + 1` +echo "I:checking startup notify rate limit ($n)" +ret=0 +grep 'x[0-9].*sending notify to' ns2/named.run | + sed 's/.*:\([0-9][0-9]\)\..*/\1/' | uniq -c | awk '{print $1}' > log.out +# the notifies should span at least 4 seconds +wc -l log.out | awk '$1 < 4 { exit(1) }' || ret=1 +# ... with no more than 5 in any one second +awk '$1 > 5 { exit(1) }' log.out || ret=1 +[ $ret = 0 ] || echo "I:failed" +status=`expr $ret + $status` + echo "I:reloading with example2 using HUP and waiting 45 seconds" sleep 1 # make sure filesystem time stamp is newer for reload. rm -f ns2/example.db diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 5f4e0859f7..d1e3d5aba4 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4835,6 +4835,8 @@ badresp:1,adberr:0,findfail:0,valfail:0] tcp-clients number; reserved-sockets number; recursive-clients number; + notify-rate number; + startup-notify-rate number; serial-query-rate number; serial-queries number; tcp-listen-queue number; @@ -7783,6 +7785,31 @@ avoid-v6-udp-ports {}; + + notify-rate + + + The rate at which NOTIFY requests will be sent + during normal zone maintenance operations. (NOTIFY + requests due to initial zone loading are subject + to a separate rate limit; see below.) The default is + 20 per second. + + + + + + startup-notify-rate + + + The rate at which NOTIFY requests will be sent + when the name server is first starting up, or when + zones have been newly added to the nameserver. + The default is 20 per second. + + + + serial-query-rate @@ -7795,14 +7822,7 @@ avoid-v6-udp-ports {}; rate at which queries are sent. The value of the serial-query-rate option, an integer, is the maximum number of queries sent - per second. The default is 20. - - - In addition to controlling the rate SOA refresh - queries are issued at - serial-query-rate also controls - the rate at which NOTIFY messages are sent from - both master and slave zones. + per second. The default is 20 per second. diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 3c5e2fd78b..d6ab7cac7f 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -1767,6 +1767,24 @@ dns_zonemgr_getiolimit(dns_zonemgr_t *zmgr); *\li 'zmgr' to be a valid zone manager. */ +void +dns_zonemgr_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value); +/*%< + * Set the number of NOTIFY requests sent per second. + * + * Requires: + *\li 'zmgr' to be a valid zone manager + */ + +void +dns_zonemgr_setstartupnotifyrate(dns_zonemgr_t *zmgr, unsigned int value); +/*%< + * Set the number of startup NOTIFY requests sent per second. + * + * Requires: + *\li 'zmgr' to be a valid zone manager + */ + void dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value); /*%< @@ -1776,6 +1794,24 @@ dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value); *\li 'zmgr' to be a valid zone manager */ +unsigned int +dns_zonemgr_getnotifyrate(dns_zonemgr_t *zmgr); +/*%< + * Return the number of NOTIFY requests sent per second. + * + * Requires: + *\li 'zmgr' to be a valid zone manager. + */ + +unsigned int +dns_zonemgr_getstartupnotifyrate(dns_zonemgr_t *zmgr); +/*%< + * Return the number of startup NOTIFY requests sent per second. + * + * Requires: + *\li 'zmgr' to be a valid zone manager. + */ + unsigned int dns_zonemgr_getserialqueryrate(dns_zonemgr_t *zmgr); /*%< diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in index c92f8a7bc7..361cd310a3 100644 --- a/lib/dns/win32/libdns.def.in +++ b/lib/dns/win32/libdns.def.in @@ -1064,15 +1064,19 @@ dns_zonemgr_detach dns_zonemgr_forcemaint dns_zonemgr_getcount dns_zonemgr_getiolimit +dns_zonemgr_getnotifyrate dns_zonemgr_getserialqueryrate +dns_zonemgr_getstartupnotifyrate dns_zonemgr_getttransfersin dns_zonemgr_getttransfersperns dns_zonemgr_managezone dns_zonemgr_releasezone dns_zonemgr_resumexfrs dns_zonemgr_setiolimit +dns_zonemgr_setnotifyrate dns_zonemgr_setserialqueryrate dns_zonemgr_setsize +dns_zonemgr_setstartupnotifyrate dns_zonemgr_settransfersin dns_zonemgr_settransfersperns dns_zonemgr_shutdown diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 224c31a944..225735c83e 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -476,6 +476,10 @@ typedef struct { #define DNS_ZONEFLG_LOADPENDING 0x10000000U /*%< Loading scheduled */ #define DNS_ZONEFLG_NODELAY 0x20000000U #define DNS_ZONEFLG_SENDSECURE 0x40000000U +#define DNS_ZONEFLG_NEEDSTARTUPNOTIFY 0x80000000U /*%< need to send out notify + * due to the zone just + * being loaded for the + * first time. */ #define DNS_ZONE_OPTION(z,o) (((z)->options & (o)) != 0) #define DNS_ZONE_OPTION2(z,o) (((z)->options2 & (o)) != 0) @@ -515,6 +519,8 @@ struct dns_zonemgr { isc_pool_t * mctxpool; isc_ratelimiter_t * notifyrl; isc_ratelimiter_t * refreshrl; + isc_ratelimiter_t * startupnotifyrl; + isc_ratelimiter_t * startuprefreshrl; isc_rwlock_t rwlock; isc_mutex_t iolock; isc_rwlock_t urlock; @@ -527,7 +533,10 @@ struct dns_zonemgr { /* Configuration data. */ isc_uint32_t transfersin; isc_uint32_t transfersperns; + unsigned int notifyrate; + unsigned int startupnotifyrate; unsigned int serialqueryrate; + unsigned int startupserialqueryrate; /* Locked by iolock */ isc_uint32_t iolimit; @@ -555,9 +564,11 @@ struct dns_notify { dns_tsigkey_t *key; isc_dscp_t dscp; ISC_LINK(dns_notify_t) link; + isc_event_t *event; }; #define DNS_NOTIFY_NOSOA 0x0001U +#define DNS_NOTIFY_STARTUP 0x0002U /*% * dns_stub holds state while performing a 'stub' transfer. @@ -785,6 +796,8 @@ static isc_result_t delete_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff); static void zone_rekey(dns_zone_t *zone); static isc_result_t zone_send_securedb(dns_zone_t *zone, dns_db_t *db); +static void setrl(isc_ratelimiter_t *rl, unsigned int *rate, + unsigned int value); #define ENTER zone_debuglog(zone, me, 1, "enter") @@ -4480,7 +4493,8 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, zone_attachdb(zone, db); ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); DNS_ZONE_SETFLAG(zone, - DNS_ZONEFLG_LOADED|DNS_ZONEFLG_NEEDNOTIFY); + DNS_ZONEFLG_LOADED| + DNS_ZONEFLG_NEEDSTARTUPNOTIFY); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SENDSECURE) && inline_raw(zone)) { @@ -9330,7 +9344,8 @@ zone_maintenance(dns_zone_t *zone) { * Slaves send notifies before backing up to disk, masters after. */ if (zone->type == dns_zone_slave && - DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) && + (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) || + DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY)) && isc_time_compare(&now, &zone->notifytime) >= 0) zone_notify(zone, &now); @@ -9370,7 +9385,8 @@ zone_maintenance(dns_zone_t *zone) { switch (zone->type) { case dns_zone_master: case dns_zone_redirect: - if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) && + if ((DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) || + DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY))&& isc_time_compare(&now, &zone->notifytime) >= 0) zone_notify(zone, &now); default: @@ -9999,21 +10015,50 @@ dns_zone_setmaxretrytime(dns_zone_t *zone, isc_uint32_t val) { } static isc_boolean_t -notify_isqueued(dns_zone_t *zone, dns_name_t *name, isc_sockaddr_t *addr) { +notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name, + isc_sockaddr_t *addr) +{ dns_notify_t *notify; + dns_zonemgr_t *zmgr; + isc_result_t result; for (notify = ISC_LIST_HEAD(zone->notifies); notify != NULL; notify = ISC_LIST_NEXT(notify, link)) { if (notify->request != NULL) continue; + if ((flags & DNS_NOTIFY_STARTUP) == 0) + notify->flags &= ~DNS_NOTIFY_STARTUP; if (name != NULL && dns_name_dynamic(¬ify->ns) && dns_name_equal(name, ¬ify->ns)) - return (ISC_TRUE); + goto requeue; if (addr != NULL && isc_sockaddr_equal(addr, ¬ify->dst)) - return (ISC_TRUE); + goto requeue; } return (ISC_FALSE); + +requeue: + /* + * If we are enqueued on the startup ratelimiter and this is + * not a startup notify, re-enqueue on the normal notify + * ratelimiter. + */ + if (notify->event != NULL && (flags & DNS_NOTIFY_STARTUP) == 0) { + zmgr = notify->zone->zmgr; + result = isc_ratelimiter_dequeue(zmgr->startupnotifyrl, + notify->event); + if (result != ISC_R_SUCCESS) + return (ISC_TRUE); + result = isc_ratelimiter_enqueue(notify->zone->zmgr->notifyrl, + notify->zone->task, + ¬ify->event); + if (result != ISC_R_SUCCESS) { + isc_event_free(¬ify->event); + return (ISC_FALSE); + } + } + + return (ISC_TRUE); } static isc_boolean_t @@ -10111,6 +10156,7 @@ notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) { notify->find = NULL; notify->request = NULL; notify->key = NULL; + notify->event = NULL; isc_sockaddr_any(¬ify->dst); dns_name_init(¬ify->ns, NULL); ISC_LINK_INIT(notify, link); @@ -10186,22 +10232,27 @@ notify_find_address(dns_notify_t *notify) { static isc_result_t -notify_send_queue(dns_notify_t *notify) { +notify_send_queue(dns_notify_t *notify, isc_boolean_t startup) { isc_event_t *e; isc_result_t result; - e = isc_event_allocate(notify->mctx, NULL, - DNS_EVENT_NOTIFYSENDTOADDR, - notify_send_toaddr, - notify, sizeof(isc_event_t)); + INSIST(notify->event == NULL); + e = isc_event_allocate(notify->mctx, NULL, DNS_EVENT_NOTIFYSENDTOADDR, + notify_send_toaddr, notify, sizeof(isc_event_t)); if (e == NULL) return (ISC_R_NOMEMORY); + if (startup) + notify->event = e; e->ev_arg = notify; e->ev_sender = NULL; - result = isc_ratelimiter_enqueue(notify->zone->zmgr->notifyrl, + result = isc_ratelimiter_enqueue(startup + ? notify->zone->zmgr->startupnotifyrl + : notify->zone->zmgr->notifyrl, notify->zone->task, &e); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { isc_event_free(&e); + notify->event = NULL; + } return (result); } @@ -10226,6 +10277,8 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) { LOCK_ZONE(notify->zone); + notify->event = NULL; + if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_LOADED) == 0) { result = ISC_R_CANCELED; goto cleanup; @@ -10342,6 +10395,8 @@ notify_send(dns_notify_t *notify) { isc_sockaddr_t dst; isc_result_t result; dns_notify_t *new = NULL; + unsigned int flags; + isc_boolean_t startup; /* * Zone lock held by caller. @@ -10353,20 +10408,20 @@ notify_send(dns_notify_t *notify) { ai != NULL; ai = ISC_LIST_NEXT(ai, publink)) { dst = ai->sockaddr; - if (notify_isqueued(notify->zone, NULL, &dst)) + if (notify_isqueued(notify->zone, notify->flags, NULL, &dst)) continue; if (notify_isself(notify->zone, &dst)) continue; new = NULL; - result = notify_create(notify->mctx, - (notify->flags & DNS_NOTIFY_NOSOA), - &new); + flags = notify->flags & DNS_NOTIFY_NOSOA; + result = notify_create(notify->mctx, flags, &new); if (result != ISC_R_SUCCESS) goto cleanup; zone_iattach(notify->zone, &new->zone); ISC_LIST_APPEND(new->zone->notifies, new, link); new->dst = dst; - result = notify_send_queue(new); + startup = ISC_TF((notify->flags & DNS_NOTIFY_STARTUP) != 0); + result = notify_send_queue(new, startup); if (result != ISC_R_SUCCESS) goto cleanup; new = NULL; @@ -10412,11 +10467,14 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { dns_notifytype_t notifytype; unsigned int flags = 0; isc_boolean_t loggednotify = ISC_FALSE; + isc_boolean_t startup; REQUIRE(DNS_ZONE_VALID(zone)); 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); notifytype = zone->notifytype; DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime); UNLOCK_ZONE(zone); @@ -10440,6 +10498,12 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY)) flags |= DNS_NOTIFY_NOSOA; + /* + * Record that this was a notify due to starting up. + */ + if (startup) + flags |= DNS_NOTIFY_STARTUP; + /* * Get SOA RRset. */ @@ -10485,7 +10549,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { dns_tsigkey_t *key = NULL; dst = zone->notify[i]; - if (notify_isqueued(zone, NULL, &dst)) + if (notify_isqueued(zone, flags, NULL, &dst)) continue; result = notify_create(zone->mctx, flags, ¬ify); @@ -10507,7 +10571,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { } ISC_LIST_APPEND(zone->notifies, notify, link); - result = notify_send_queue(notify); + result = notify_send_queue(notify, startup); if (result != ISC_R_SUCCESS) notify_destroy(notify, ISC_TRUE); if (!loggednotify) { @@ -10557,7 +10621,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { } LOCK_ZONE(zone); - isqueued = notify_isqueued(zone, &ns.name, NULL); + isqueued = notify_isqueued(zone, flags, &ns.name, NULL); UNLOCK_ZONE(zone); if (isqueued) { result = dns_rdataset_next(&nsrdset); @@ -12127,7 +12191,8 @@ zone_settimer(dns_zone_t *zone, isc_time_t *now) { /* FALLTHROUGH */ case dns_zone_master: - if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY)) + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDNOTIFY) || + DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDSTARTUPNOTIFY)) next = zone->notifytime; if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDDUMP) && !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DUMPING)) { @@ -13202,9 +13267,12 @@ notify_done(isc_task_t *task, isc_event_t *event) { isc_event_free(&event); if (message != NULL && message->rcode == dns_rcode_formerr && (notify->flags & DNS_NOTIFY_NOSOA) == 0) { + isc_boolean_t startup; + notify->flags |= DNS_NOTIFY_NOSOA; dns_request_destroy(¬ify->request); - result = notify_send_queue(notify); + startup = ISC_TF((notify->flags & DNS_NOTIFY_STARTUP) != 0); + result = notify_send_queue(notify, startup); if (result != ISC_R_SUCCESS) notify_destroy(notify, ISC_FALSE); } else { @@ -15136,7 +15204,6 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, { dns_zonemgr_t *zmgr; isc_result_t result; - isc_interval_t interval; zmgr = isc_mem_get(mctx, sizeof(*zmgr)); if (zmgr == NULL) @@ -15153,6 +15220,8 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, zmgr->task = NULL; zmgr->notifyrl = NULL; zmgr->refreshrl = NULL; + zmgr->startupnotifyrl = NULL; + zmgr->startuprefreshrl = NULL; ISC_LIST_INIT(zmgr->zones); ISC_LIST_INIT(zmgr->waiting_for_xfrin); ISC_LIST_INIT(zmgr->xfrin_in_progress); @@ -15185,15 +15254,21 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, if (result != ISC_R_SUCCESS) goto free_notifyrl; - /* default to 20 refresh queries / notifies per second. */ - isc_interval_set(&interval, 0, 1000000000/2); - result = isc_ratelimiter_setinterval(zmgr->notifyrl, &interval); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - isc_ratelimiter_setpertic(zmgr->notifyrl, 10); + result = isc_ratelimiter_create(mctx, timermgr, zmgr->task, + &zmgr->startupnotifyrl); + if (result != ISC_R_SUCCESS) + goto free_refreshrl; - result = isc_ratelimiter_setinterval(zmgr->refreshrl, &interval); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - isc_ratelimiter_setpertic(zmgr->refreshrl, 10); + result = isc_ratelimiter_create(mctx, timermgr, zmgr->task, + &zmgr->startuprefreshrl); + if (result != ISC_R_SUCCESS) + goto free_startupnotifyrl; + + /* default to 20 refresh queries / notifies per second. */ + setrl(zmgr->notifyrl, &zmgr->notifyrate, 20); + setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, 20); + setrl(zmgr->refreshrl, &zmgr->serialqueryrate, 20); + setrl(zmgr->startuprefreshrl, &zmgr->startupserialqueryrate, 20); zmgr->iolimit = 1; zmgr->ioactive = 0; @@ -15202,7 +15277,7 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, result = isc_mutex_init(&zmgr->iolock); if (result != ISC_R_SUCCESS) - goto free_refreshrl; + goto free_startuprefreshrl; zmgr->magic = ZONEMGR_MAGIC; @@ -15213,6 +15288,10 @@ dns_zonemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr, free_iolock: DESTROYLOCK(&zmgr->iolock); #endif + free_startuprefreshrl: + isc_ratelimiter_detach(&zmgr->startuprefreshrl); + free_startupnotifyrl: + isc_ratelimiter_detach(&zmgr->startupnotifyrl); free_refreshrl: isc_ratelimiter_detach(&zmgr->refreshrl); free_notifyrl: @@ -15416,6 +15495,8 @@ dns_zonemgr_shutdown(dns_zonemgr_t *zmgr) { isc_ratelimiter_shutdown(zmgr->notifyrl); isc_ratelimiter_shutdown(zmgr->refreshrl); + isc_ratelimiter_shutdown(zmgr->startupnotifyrl); + isc_ratelimiter_shutdown(zmgr->startuprefreshrl); if (zmgr->task != NULL) isc_task_destroy(&zmgr->task); @@ -15547,6 +15628,8 @@ zonemgr_free(dns_zonemgr_t *zmgr) { DESTROYLOCK(&zmgr->iolock); isc_ratelimiter_detach(&zmgr->notifyrl); isc_ratelimiter_detach(&zmgr->refreshrl); + isc_ratelimiter_detach(&zmgr->startupnotifyrl); + isc_ratelimiter_detach(&zmgr->startuprefreshrl); isc_rwlock_destroy(&zmgr->urlock); isc_rwlock_destroy(&zmgr->rwlock); @@ -15909,15 +15992,13 @@ dns_zonemgr_dbdestroyed(isc_task_t *task, isc_event_t *event) { } #endif -void -dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) { +static void +setrl(isc_ratelimiter_t *rl, unsigned int *rate, unsigned int value) { isc_interval_t interval; isc_uint32_t s, ns; isc_uint32_t pertic; isc_result_t result; - REQUIRE(DNS_ZONEMGR_VALID(zmgr)); - if (value == 0) value = 1; @@ -15937,15 +16018,51 @@ dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) { isc_interval_set(&interval, s, ns); - result = isc_ratelimiter_setinterval(zmgr->notifyrl, &interval); + result = isc_ratelimiter_setinterval(rl, &interval); RUNTIME_CHECK(result == ISC_R_SUCCESS); - isc_ratelimiter_setpertic(zmgr->notifyrl, pertic); + isc_ratelimiter_setpertic(rl, pertic); - result = isc_ratelimiter_setinterval(zmgr->refreshrl, &interval); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - isc_ratelimiter_setpertic(zmgr->refreshrl, pertic); + *rate = value; +} - zmgr->serialqueryrate = value; +void +dns_zonemgr_setnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) { + + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + setrl(zmgr->notifyrl, &zmgr->notifyrate, value); +} + +void +dns_zonemgr_setstartupnotifyrate(dns_zonemgr_t *zmgr, unsigned int value) { + + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + setrl(zmgr->startupnotifyrl, &zmgr->startupnotifyrate, value); +} + +void +dns_zonemgr_setserialqueryrate(dns_zonemgr_t *zmgr, unsigned int value) { + + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + setrl(zmgr->refreshrl, &zmgr->serialqueryrate, value); + /* XXXMPA seperate out once we have the code to support this. */ + setrl(zmgr->startuprefreshrl, &zmgr->startupserialqueryrate, value); +} + +unsigned int +dns_zonemgr_getnotifyrate(dns_zonemgr_t *zmgr) { + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + return (zmgr->notifyrate); +} + +unsigned int +dns_zonemgr_getstartupnotifyrate(dns_zonemgr_t *zmgr) { + REQUIRE(DNS_ZONEMGR_VALID(zmgr)); + + return (zmgr->startupnotifyrate); } unsigned int diff --git a/lib/isc/include/isc/ratelimiter.h b/lib/isc/include/isc/ratelimiter.h index 00a7209758..670a1274b8 100644 --- a/lib/isc/include/isc/ratelimiter.h +++ b/lib/isc/include/isc/ratelimiter.h @@ -89,6 +89,16 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, *\li '(*eventp)->ev_sender' to be NULL. */ +isc_result_t +isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event); +/* + * Dequeue a event off the ratelimiter queue. + * + * Returns: + * \li ISC_R_NOTFOUND if the event is no longer linked to the rate limiter. + * \li ISC_R_SUCCESS + */ + void isc_ratelimiter_shutdown(isc_ratelimiter_t *ratelimiter); /*%< diff --git a/lib/isc/ratelimiter.c b/lib/isc/ratelimiter.c index fc66e9f61e..0273b81370 100644 --- a/lib/isc/ratelimiter.c +++ b/lib/isc/ratelimiter.c @@ -79,6 +79,7 @@ isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, result = isc_mutex_init(&rl->lock); if (result != ISC_R_SUCCESS) goto free_mem; + result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL, rl->task, ratelimiter_tick, rl, &rl->timer); @@ -109,6 +110,10 @@ free_mem: isc_result_t isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) { isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(rl != NULL); + REQUIRE(interval != NULL); + LOCK(&rl->lock); rl->interval = *interval; /* @@ -124,6 +129,9 @@ isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) { void isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, isc_uint32_t pertic) { + + REQUIRE(rl != NULL); + if (pertic == 0) pertic = 1; rl->pertic = pertic; @@ -136,8 +144,9 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, isc_result_t result = ISC_R_SUCCESS; isc_event_t *ev; - REQUIRE(eventp != NULL && *eventp != NULL); + REQUIRE(rl != NULL); REQUIRE(task != NULL); + REQUIRE(eventp != NULL && *eventp != NULL); ev = *eventp; REQUIRE(ev->ev_sender == NULL); @@ -165,6 +174,22 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, return (result); } +isc_result_t +isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(rl != NULL); + REQUIRE(event != NULL); + + LOCK(&rl->lock); + if (ISC_LINK_LINKED(event, ev_link)) + ISC_LIST_UNLINK(rl->pending, event, ev_link); + else + result = ISC_R_NOTFOUND; + UNLOCK(&rl->lock); + return (result); +} + static void ratelimiter_tick(isc_task_t *task, isc_event_t *event) { isc_result_t result = ISC_R_SUCCESS; @@ -211,6 +236,9 @@ void isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { isc_event_t *ev; isc_task_t *task; + + REQUIRE(rl != NULL); + LOCK(&rl->lock); rl->state = isc_ratelimiter_shuttingdown; (void)isc_timer_reset(rl->timer, isc_timertype_inactive, @@ -222,6 +250,7 @@ isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { isc_task_send(task, &ev); } isc_timer_detach(&rl->timer); + /* * Send an event to our task. The delivery of this event * indicates that no more timer events will be delivered. @@ -249,6 +278,7 @@ ratelimiter_free(isc_ratelimiter_t *rl) { void isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) { + REQUIRE(source != NULL); REQUIRE(target != NULL && *target == NULL); @@ -262,9 +292,13 @@ isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) { void isc_ratelimiter_detach(isc_ratelimiter_t **rlp) { - isc_ratelimiter_t *rl = *rlp; + isc_ratelimiter_t *rl; isc_boolean_t free_now = ISC_FALSE; + REQUIRE(rlp != NULL && *rlp != NULL); + + rl = *rlp; + LOCK(&rl->lock); REQUIRE(rl->refs > 0); rl->refs--; @@ -282,6 +316,8 @@ isc_result_t isc_ratelimiter_stall(isc_ratelimiter_t *rl) { isc_result_t result = ISC_R_SUCCESS; + REQUIRE(rl != NULL); + LOCK(&rl->lock); switch (rl->state) { case isc_ratelimiter_shuttingdown: @@ -305,6 +341,8 @@ isc_result_t isc_ratelimiter_release(isc_ratelimiter_t *rl) { isc_result_t result = ISC_R_SUCCESS; + REQUIRE(rl != NULL); + LOCK(&rl->lock); switch (rl->state) { case isc_ratelimiter_shuttingdown: diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index e623b8b683..6549df9080 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -476,6 +476,7 @@ isc_random_jitter isc_random_seed isc_ratelimiter_attach isc_ratelimiter_create +isc_ratelimiter_dequeue isc_ratelimiter_detach isc_ratelimiter_enqueue isc_ratelimiter_setinterval diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 09711e2b7f..bb3f34c10c 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -977,6 +977,7 @@ options_clauses[] = { { "memstatistics", &cfg_type_boolean, 0 }, { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE }, { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE }, + { "notify-rate", &cfg_type_uint32, 0 }, { "pid-file", &cfg_type_qstringornone, 0 }, { "port", &cfg_type_uint32, 0 }, { "querylog", &cfg_type_boolean, 0 }, @@ -989,6 +990,7 @@ options_clauses[] = { { "serial-query-rate", &cfg_type_uint32, 0 }, { "server-id", &cfg_type_serverid, 0 }, { "stacksize", &cfg_type_size, 0 }, + { "startup-notify-rate", &cfg_type_uint32, 0 }, { "statistics-file", &cfg_type_qstring, 0 }, { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI }, { "tcp-clients", &cfg_type_uint32, 0 },