From 7cac4ca03ce24c2feded11f34bb5f53e1b6e2550 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 25 May 2022 12:02:18 -0700 Subject: [PATCH 1/4] clean up unused API the dns_adb_dumpfind() function was only used inside adb.c and can be static. dns_view_dumpdbtostream() was not used anywhere. --- lib/dns/adb.c | 6 +++--- lib/dns/include/dns/adb.h | 18 ++---------------- lib/dns/include/dns/view.h | 22 ---------------------- lib/dns/view.c | 19 ------------------- 4 files changed, 5 insertions(+), 60 deletions(-) diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 92fd72418f..7d549aa5ee 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -2880,8 +2880,8 @@ dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry, bool debug, } } -void -dns_adb_dumpfind(dns_adbfind_t *find, FILE *f) { +static void +dumpfind(dns_adbfind_t *find, FILE *f) { char tmp[512]; const char *tmpp = NULL; dns_adbaddrinfo_t *ai = NULL; @@ -2971,7 +2971,7 @@ print_find_list(FILE *f, dns_adbname_t *name) { find = ISC_LIST_HEAD(name->finds); while (find != NULL) { - dns_adb_dumpfind(find, f); + dumpfind(find, f); find = ISC_LIST_NEXT(find, plink); } } diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 05b9c91b29..4bfe6d0a42 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -454,29 +454,15 @@ dns_adb_destroyfind(dns_adbfind_t **find); void dns_adb_dump(dns_adb_t *adb, FILE *f); /*%< - * This function is only used for debugging. It will dump as much of the - * state of the running system as possible. + * Used by "rndc dumpdb": Dump the state of the running ADB. * * Requires: * - *\li adb be valid. + *\li adb is valid. * *\li f != NULL, and is a file open for writing. */ -void -dns_adb_dumpfind(dns_adbfind_t *find, FILE *f); -/*%< - * This function is only used for debugging. Dump the data associated - * with a find. - * - * Requires: - * - *\li find is valid. - * - * \li f != NULL, and is a file open for writing. - */ - isc_result_t dns_adb_marklame(dns_adb_t *adb, dns_adbaddrinfo_t *addr, const dns_name_t *qname, dns_rdatatype_t type, diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 5d78999941..7a9a48c269 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -868,28 +868,6 @@ dns_view_dialup(dns_view_t *view); * Perform dialup-time maintenance on the zones of 'view'. */ -isc_result_t -dns_view_dumpdbtostream(dns_view_t *view, FILE *fp); -/*%< - * Dump the current state of the view 'view' to the stream 'fp' - * for purposes of analysis or debugging. - * - * Currently the dumped state includes the view's cache; in the future - * it may also include other state such as the address database. - * It will not not include authoritative data since it is voluminous and - * easily obtainable by other means. - * - * Requires: - * - *\li 'view' is valid. - * - *\li 'fp' refers to a file open for writing. - * - * Returns: - * \li ISC_R_SUCCESS The cache was successfully dumped. - * \li others An error occurred (see dns_master_dump) - */ - isc_result_t dns_view_flushcache(dns_view_t *view, bool fixuponly); /*%< diff --git a/lib/dns/view.c b/lib/dns/view.c index 4200eeb8c5..3a00a0a46c 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -1472,25 +1472,6 @@ dns_view_checksig(dns_view_t *view, isc_buffer_t *source, dns_message_t *msg) { view->dynamickeys)); } -isc_result_t -dns_view_dumpdbtostream(dns_view_t *view, FILE *fp) { - isc_result_t result; - - REQUIRE(DNS_VIEW_VALID(view)); - - (void)fprintf(fp, ";\n; Cache dump of view '%s'\n;\n", view->name); - result = dns_master_dumptostream(view->mctx, view->cachedb, NULL, - &dns_master_style_cache, - dns_masterformat_text, NULL, fp); - if (result != ISC_R_SUCCESS) { - return (result); - } - dns_adb_dump(view->adb, fp); - dns_resolver_printbadcache(view->resolver, fp); - dns_badcache_print(view->failcache, "SERVFAIL cache", fp); - return (ISC_R_SUCCESS); -} - isc_result_t dns_view_flushcache(dns_view_t *view, bool fixuponly) { isc_result_t result; From 617589747854f07a570ff459deeae2e082f36efb Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 26 May 2022 02:33:52 -0700 Subject: [PATCH 2/4] add "rndc fetchlimit" to show fetchlimited servers this command runs dns_adb_dumpquota() to display all servers in the ADB that are being actively fetchlimited by the fetches-per-server controls (i.e, servers with a nonzero average timeout ratio or with the quota having been reduced from the default value). the "fetchlimit" system test has been updated to use the new command to check quota values instead of "rndc dumpdb". --- bin/named/control.c | 2 + bin/named/include/named/control.h | 1 + bin/named/include/named/server.h | 7 +++ bin/named/server.c | 67 +++++++++++++++++++++ bin/rndc/rndc.rst | 5 ++ bin/tests/system/fetchlimit/tests.sh | 21 +++---- doc/man/rndc.8in | 6 ++ lib/dns/adb.c | 87 ++++++++++++++++++++++++++++ lib/dns/include/dns/adb.h | 26 +++++++++ 9 files changed, 210 insertions(+), 12 deletions(-) diff --git a/bin/named/control.c b/bin/named/control.c index 29c848b070..0b742407fe 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -218,6 +218,8 @@ named_control_docommand(isccc_sexpr_t *message, bool readonly, result = ISC_R_SUCCESS; } else if (command_compare(command, NAMED_COMMAND_DUMPSTATS)) { result = named_server_dumpstats(named_g_server); + } else if (command_compare(command, NAMED_COMMAND_FETCHLIMIT)) { + result = named_server_fetchlimit(named_g_server, lex, text); } else if (command_compare(command, NAMED_COMMAND_FLUSH)) { result = named_server_flushcache(named_g_server, lex); } else if (command_compare(command, NAMED_COMMAND_FLUSHNAME)) { diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 1ea4c490fb..425b50696f 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -70,6 +70,7 @@ #define NAMED_COMMAND_DNSTAP "dnstap" #define NAMED_COMMAND_TCPTIMEOUTS "tcp-timeouts" #define NAMED_COMMAND_SERVESTALE "serve-stale" +#define NAMED_COMMAND_FETCHLIMIT "fetchlimit" isc_result_t named_controls_create(named_server_t *server, named_controls_t **ctrlsp); diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index d1c97cd853..ce45b440c1 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -401,3 +401,10 @@ named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text); isc_result_t named_server_servestale(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Report fetch-limited ADB server addresses. + */ +isc_result_t +named_server_fetchlimit(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); diff --git a/bin/named/server.c b/bin/named/server.c index dcb7a708f4..b109683711 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -16600,3 +16600,70 @@ cleanup: return (result); } + +isc_result_t +named_server_fetchlimit(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result = ISC_R_SUCCESS; + dns_view_t *view = NULL; + char *ptr = NULL, *viewname = NULL; + bool first = true; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Look for the view name. */ + viewname = next_token(lex, text); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + char tbuf[BUFSIZ]; + unsigned int used; + uint32_t val; + int s; + + if (view->rdclass != dns_rdataclass_in) { + continue; + } + + if (viewname != NULL && strcasecmp(view->name, viewname) != 0) { + continue; + } + + if (view->adb == NULL) { + continue; + } + + if (!first) { + putstr(text, "\n"); + } + putstr(text, "Rate limited servers, view "); + putstr(text, view->name); + + dns_adb_getquota(view->adb, &val, NULL, NULL, NULL, NULL); + s = snprintf(tbuf, sizeof(tbuf), + " (fetches-per-server %u):", val); + if (s < 0 || (unsigned)s > sizeof(tbuf)) { + return (ISC_R_NOSPACE); + } + first = false; + putstr(text, tbuf); + used = isc_buffer_usedlength(*text); + CHECK(dns_adb_dumpquota(view->adb, text)); + if (used == isc_buffer_usedlength(*text)) { + putstr(text, "\n None."); + } + } + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return (result); +} diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst index 6717e242d1..2236d1d911 100644 --- a/bin/rndc/rndc.rst +++ b/bin/rndc/rndc.rst @@ -204,6 +204,11 @@ Currently supported commands are: (See the ``dump-file`` option in the BIND 9 Administrator Reference Manual.) +.. option:: fetchlimit [view] + + This command dumps a list of servers which are currently being + rate-limited as a result of ``fetches-per-server`` settings. + .. option:: flush This command flushes the server's cache. diff --git a/bin/tests/system/fetchlimit/tests.sh b/bin/tests/system/fetchlimit/tests.sh index c10bee6677..a96caafa9d 100644 --- a/bin/tests/system/fetchlimit/tests.sh +++ b/bin/tests/system/fetchlimit/tests.sh @@ -58,12 +58,11 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) echo_i "dumping ADB data" -$RNDCCMD dumpdb -adb -info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/\1 \2/'` +info=$($RNDCCMD fetchlimit | grep 10.53.0.4 | sed 's/.*quota .*(\([0-9]*\).*atr \([.0-9]*\).*/\2 \1/') echo_i $info set -- $info -quota=$5 -[ ${5:-200} -lt 200 ] || ret=1 +quota=$2 +[ ${quota:-200} -lt 200 ] || ret=1 echo_i "checking servfail statistics" ret=0 @@ -92,12 +91,11 @@ for try in 1 2 3 4 5; do done echo_i "dumping ADB data" -$RNDCCMD dumpdb -adb -info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/\1 \2/'` +info=$($RNDCCMD fetchlimit | grep 10.53.0.4 | sed 's/.*quota .*(\([0-9]*\).*atr \([.0-9]*\).*/\2 \1/') echo_i $info set -- $info -[ ${5:-${quota}} -lt $quota ] || ret=1 -quota=$5 +[ ${2:-${quota}} -lt $quota ] || ret=1 +quota=$2 for try in 1 2 3 4 5 6 7 8 9 10; do burst c $try @@ -107,12 +105,11 @@ for try in 1 2 3 4 5 6 7 8 9 10; do done echo_i "dumping ADB data" -$RNDCCMD dumpdb -adb -info=`grep '10.53.0.4' ns3/named_dump.db | sed 's/.*\(atr [.0-9]*\).*\(quota [0-9]*\).*/\1 \2/'` +info=$($RNDCCMD fetchlimit | grep 10.53.0.4 | sed 's/.*quota .*(\([0-9]*\).*atr \([.0-9]*\).*/\2 \1/') echo_i $info set -- $info -[ ${5:-${quota}} -gt $quota ] || ret=1 -quota=$5 +[ ${2:-${quota}} -gt $quota ] || ret=1 +quota=$2 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) diff --git a/doc/man/rndc.8in b/doc/man/rndc.8in index 4023b82ee2..c21fab7a61 100644 --- a/doc/man/rndc.8in +++ b/doc/man/rndc.8in @@ -226,6 +226,12 @@ Manual.) .UNINDENT .INDENT 0.0 .TP +.B fetchlimit [view] +This command dumps a list of servers which are currently being +rate\-limited as a result of \fBfetches\-per\-server\fP settings. +.UNINDENT +.INDENT 0.0 +.TP .B flush This command flushes the server\(aqs cache. .UNINDENT diff --git a/lib/dns/adb.c b/lib/dns/adb.c index 7d549aa5ee..b97f3e6390 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -2976,6 +2976,67 @@ print_find_list(FILE *f, dns_adbname_t *name) { } } +static isc_result_t +putstr(isc_buffer_t **b, const char *str) { + isc_result_t result; + + result = isc_buffer_reserve(b, strlen(str)); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isc_buffer_putstr(*b, str); + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_adb_dumpquota(dns_adb_t *adb, isc_buffer_t **buf) { + isc_result_t result; + isc_ht_iter_t *it = NULL; + + REQUIRE(DNS_ADB_VALID(adb)); + + RWLOCK(&adb->entries_lock, isc_rwlocktype_read); + isc_ht_iter_create(adb->entrybuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_adbentrybucket_t *ebucket = NULL; + dns_adbentry_t *entry = NULL; + + isc_ht_iter_current(it, (void **)&ebucket); + LOCK(&ebucket->lock); + for (entry = ISC_LIST_HEAD(ebucket->entries); entry != NULL; + entry = ISC_LIST_NEXT(entry, plink)) + { + char addrbuf[ISC_NETADDR_FORMATSIZE]; + char text[BUFSIZ]; + isc_netaddr_t netaddr; + + if (entry->atr == 0.0 && entry->quota == adb->quota) { + continue; + } + + isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr); + isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf)); + + snprintf(text, sizeof(text), + "\n- quota %s (%" PRIuFAST32 "/%d) atr %0.2f", + addrbuf, atomic_load_relaxed(&entry->quota), + adb->quota, entry->atr); + putstr(buf, text); + } + UNLOCK(&ebucket->lock); + } + RWUNLOCK(&adb->entries_lock, isc_rwlocktype_read); + isc_ht_iter_destroy(&it); + + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + return (result); +} + static isc_result_t dbfind_name(dns_adbname_t *adbname, isc_stdtime_t now, dns_rdatatype_t rdtype) { isc_result_t result; @@ -3965,6 +4026,32 @@ dns_adb_setquota(dns_adb_t *adb, uint32_t quota, uint32_t freq, double low, adb->atr_discount = discount; } +void +dns_adb_getquota(dns_adb_t *adb, uint32_t *quotap, uint32_t *freqp, + double *lowp, double *highp, double *discountp) { + REQUIRE(DNS_ADB_VALID(adb)); + + if (quotap != NULL) { + *quotap = adb->quota; + } + + if (freqp != NULL) { + *freqp = adb->atr_freq; + } + + if (lowp != NULL) { + *lowp = adb->atr_low; + } + + if (highp != NULL) { + *highp = adb->atr_high; + } + + if (discountp != NULL) { + *discountp = adb->atr_discount; + } +} + bool dns_adbentry_overquota(dns_adbentry_t *entry) { uint_fast32_t quota, active; diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 4bfe6d0a42..1daa6b8e08 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -748,6 +748,20 @@ dns_adb_setquota(dns_adb_t *adb, uint32_t quota, uint32_t freq, double low, *\li 'adb' is valid. */ +void +dns_adb_getquota(dns_adb_t *adb, uint32_t *quotap, uint32_t *freqp, + double *lowp, double *highp, double *discountp); +/*%< + * Get the quota values set by dns_adb_setquota(). + * If any of the 'quotap', 'freqp', 'lowp', 'highp', and + * 'discountp' parameters are non-NULL, then the memory they + * point to will be updated to hold the corresponding quota + * or parameter value. + * + * Requires: + *\li 'adb' is valid. + */ + bool dns_adbentry_overquota(dns_adbentry_t *entry); /*%< @@ -782,4 +796,16 @@ dns_adb_getstats(dns_adb_t *adb); * Requires: * \li 'adb' is valid. */ + +isc_result_t +dns_adb_dumpquota(dns_adb_t *adb, isc_buffer_t **buf); +/*% + * Dump the addresses, current quota values, and current ATR values + * for all servers that are currently being fetchlimited. Servers + * for which the quota is still equal to the default and the ATR + * is zero are not printed. + * + * Requires: + * \li 'adb' is valid. + */ ISC_LANG_ENDDECLS From 549cf0f3e65e31e8a5d99ff819f38b8a80ea93be Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 26 May 2022 14:43:23 -0700 Subject: [PATCH 3/4] "rndc fetchlimit" now also lists rate-limited domains "rndc fetchlimit" now also prints a list of domain names that are currently rate-limited by "fetches-per-zone". The "fetchlimit" system test has been updated to use this feature to check that domain limits are applied correctly. --- bin/named/server.c | 17 ++++++- bin/rndc/rndc.rst | 6 ++- bin/tests/system/fetchlimit/tests.sh | 5 ++- doc/man/rndc.8in | 6 ++- lib/dns/include/dns/resolver.h | 5 +++ lib/dns/resolver.c | 66 ++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 7 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index b109683711..fadab6ece2 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -16622,7 +16622,7 @@ named_server_fetchlimit(named_server_t *server, isc_lex_t *lex, for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link)) { - char tbuf[BUFSIZ]; + char tbuf[100]; unsigned int used; uint32_t val; int s; @@ -16658,6 +16658,21 @@ named_server_fetchlimit(named_server_t *server, isc_lex_t *lex, if (used == isc_buffer_usedlength(*text)) { putstr(text, "\n None."); } + + putstr(text, "\nRate limited servers, view "); + putstr(text, view->name); + val = dns_resolver_getfetchesperzone(view->resolver); + s = snprintf(tbuf, sizeof(tbuf), + " (fetches-per-zone %u):", val); + if (s < 0 || (unsigned)s > sizeof(tbuf)) { + return (ISC_R_NOSPACE); + } + putstr(text, tbuf); + used = isc_buffer_usedlength(*text); + CHECK(dns_resolver_dumpquota(view->resolver, text)); + if (used == isc_buffer_usedlength(*text)) { + putstr(text, "\n None."); + } } cleanup: diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst index 2236d1d911..c075bf163a 100644 --- a/bin/rndc/rndc.rst +++ b/bin/rndc/rndc.rst @@ -206,8 +206,10 @@ Currently supported commands are: .. option:: fetchlimit [view] - This command dumps a list of servers which are currently being - rate-limited as a result of ``fetches-per-server`` settings. + This command dumps a list of servers that are currently being + rate-limited as a result of ``fetches-per-server`` settings, and + a list of domain names that are currently being rate-limited as + a result of ``fetches-per-zone`` settings. .. option:: flush diff --git a/bin/tests/system/fetchlimit/tests.sh b/bin/tests/system/fetchlimit/tests.sh index a96caafa9d..c0da8d2d7e 100644 --- a/bin/tests/system/fetchlimit/tests.sh +++ b/bin/tests/system/fetchlimit/tests.sh @@ -128,9 +128,10 @@ for try in 1 2 3 4 5; do success=$((success+1)) grep "status: SERVFAIL" dig.out.ns3.$try > /dev/null 2>&1 && \ fail=$(($fail+1)) - stat 30 50 || ret=1 + stat 40 40 || ret=1 + allowed=$($RNDCCMD fetchlimit | awk '/lamesub/ { print $6 }') + [ "${allowed:-0}" -eq 40 ] || ret=1 [ $ret -eq 1 ] && break - $RNDCCMD recursing 2>&1 | sed 's/^/ns3 /' | cat_i sleep 1 done echo_i "$success successful valid queries, $fail SERVFAIL" diff --git a/doc/man/rndc.8in b/doc/man/rndc.8in index c21fab7a61..a8814bfd71 100644 --- a/doc/man/rndc.8in +++ b/doc/man/rndc.8in @@ -227,8 +227,10 @@ Manual.) .INDENT 0.0 .TP .B fetchlimit [view] -This command dumps a list of servers which are currently being -rate\-limited as a result of \fBfetches\-per\-server\fP settings. +This command dumps a list of servers that are currently being +rate\-limited as a result of \fBfetches\-per\-server\fP settings, and +a list of domain names that are currently being rate\-limited as +a result of \fBfetches\-per\-zone\fP settings. .UNINDENT .INDENT 0.0 .TP diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index d1edc8e446..a9c2ce953f 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -534,6 +534,9 @@ dns_resolver_setclientsperquery(dns_resolver_t *resolver, uint32_t min, void dns_resolver_setfetchesperzone(dns_resolver_t *resolver, uint32_t clients); +uint32_t +dns_resolver_getfetchesperzone(dns_resolver_t *resolver); + void dns_resolver_getclientsperquery(dns_resolver_t *resolver, uint32_t *cur, uint32_t *min, uint32_t *max); @@ -703,6 +706,8 @@ dns_resolver_getquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which); void dns_resolver_dumpfetches(dns_resolver_t *resolver, isc_statsformat_t format, FILE *fp); +isc_result_t +dns_resolver_dumpquota(dns_resolver_t *res, isc_buffer_t **buf); #ifdef ENABLE_AFL /*% diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index ba258a82fe..984765e19f 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -10075,6 +10075,7 @@ destroy(dns_resolver_t *res) { } isc_mem_put(res->mctx, res->tasks, res->ntasks * sizeof(res->tasks[0])); + RWLOCK(&res->hash_lock, isc_rwlocktype_write); isc_ht_iter_create(res->buckets, &it); for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; result = isc_ht_iter_delcurrent_next(it)) @@ -10088,8 +10089,10 @@ destroy(dns_resolver_t *res) { } isc_ht_iter_destroy(&it); isc_ht_destroy(&res->buckets); + RWUNLOCK(&res->hash_lock, isc_rwlocktype_write); isc_rwlock_destroy(&res->hash_lock); + RWLOCK(&res->zonehash_lock, isc_rwlocktype_write); isc_ht_iter_create(res->zonebuckets, &it); for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; result = isc_ht_iter_delcurrent_next(it)) @@ -10109,6 +10112,7 @@ destroy(dns_resolver_t *res) { } isc_ht_iter_destroy(&it); isc_ht_destroy(&res->zonebuckets); + RWUNLOCK(&res->zonehash_lock, isc_rwlocktype_write); isc_rwlock_destroy(&res->zonehash_lock); if (res->dispatches4 != NULL) { @@ -11352,6 +11356,13 @@ dns_resolver_setfetchesperzone(dns_resolver_t *resolver, uint32_t clients) { atomic_store_release(&resolver->zspill, clients); } +uint32_t +dns_resolver_getfetchesperzone(dns_resolver_t *resolver) { + REQUIRE(VALID_RESOLVER(resolver)); + + return (atomic_load_relaxed(&resolver->zspill)); +} + bool dns_resolver_getzeronosoattl(dns_resolver_t *resolver) { REQUIRE(VALID_RESOLVER(resolver)); @@ -11486,6 +11497,61 @@ dns_resolver_dumpfetches(dns_resolver_t *res, isc_statsformat_t format, isc_ht_iter_destroy(&it); } +isc_result_t +dns_resolver_dumpquota(dns_resolver_t *res, isc_buffer_t **buf) { + isc_result_t result; + isc_ht_iter_t *it = NULL; + uint_fast32_t spill; + + REQUIRE(VALID_RESOLVER(res)); + + spill = atomic_load_acquire(&res->zspill); + if (spill == 0) { + return (ISC_R_SUCCESS); + } + + RWLOCK(&res->zonehash_lock, isc_rwlocktype_read); + isc_ht_iter_create(res->zonebuckets, &it); + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + zonebucket_t *bucket = NULL; + + isc_ht_iter_current(it, (void **)&bucket); + LOCK(&bucket->lock); + for (fctxcount_t *fc = ISC_LIST_HEAD(bucket->list); fc != NULL; + fc = ISC_LIST_NEXT(fc, link)) + { + char nb[DNS_NAME_FORMATSIZE], text[BUFSIZ]; + + if (fc->count < spill) { + continue; + } + + dns_name_format(fc->domain, nb, sizeof(nb)); + snprintf(text, sizeof(text), + "\n- %s: %u active (allowed %u spilled %u)", + nb, fc->count, fc->allowed, fc->dropped); + + result = isc_buffer_reserve(buf, strlen(text)); + if (result != ISC_R_SUCCESS) { + UNLOCK(&bucket->lock); + goto cleanup; + } + isc_buffer_putstr(*buf, text); + } + UNLOCK(&bucket->lock); + } + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + +cleanup: + RWUNLOCK(&res->zonehash_lock, isc_rwlocktype_read); + isc_ht_iter_destroy(&it); + return (result); +} + void dns_resolver_setquotaresponse(dns_resolver_t *resolver, dns_quotatype_t which, isc_result_t resp) { From 52bab72e843fea5a93138cea633710baefa6bd9e Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Thu, 26 May 2022 14:49:03 -0700 Subject: [PATCH 4/4] CHANGES and release note for [GL #665] --- CHANGES | 5 +++++ doc/notes/notes-current.rst | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index f2789a4e73..ba3506a109 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +5919. [func] The "rndc fetchlimit" command lists name servers + and domain names that are being rate-limited by + "fetches-per-server" or "fetches-per-zone" limits. + [GL #665] + 5918. [test] Convert system tests to use a default HMAC algorithm where the test is not HMAC specific. [GL #3433] diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 1a48dae0a5..29682896e0 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -25,7 +25,10 @@ Known Issues New Features ~~~~~~~~~~~~ -- None. +- The new ``rndc fetchlimit`` command prints a list of name server + addresses that are currently rate-limited due to ``fetches-per-server`` + and domain names that are rate limited due to ``fetches-per-zone``. + :gl:`#665` Removed Features ~~~~~~~~~~~~~~~~