BIND 9.18.11

-----BEGIN PGP SIGNATURE-----
 
 iQJDBAABCgAtFiEENKwGS3ftSQfs1TU17QVz/8hFYQUFAmPAh6gPHG1pY2hhbEBp
 c2Mub3JnAAoJEO0Fc//IRWEFyGsQAJuggfdFRAFzH6QTlE+jYPGGPGGAEp9+lFuP
 ufCdlek5FKN/V/NFpuMfNeyQ3NhK93ofMzaluAg47vM5Cj2/lPxUUFea7w20tHVm
 Nqsxk4Lc+RsnngVNUwWtA6CkwAGHDQA0Rwa3OEjqPkm33KLwCeC3w3ufv6KPlT7m
 MubNOd7BogMBxDg63TnOlSBjcKFi/TzGCNmOVj1cyOj9QP52XeIe6iKol4g47mWG
 erQ8ZKV/vWoIRCwLdPheRgCgO/2KyHLGbtI+uJ53OExiYnrKL18wGnt1Foo8Er9V
 hOkBykzgtWTtgrl8Ljd1lbR6FjZvLgcWWIZ6oM2RXjD25942lNgyWYubQHsRHchi
 /vnFD3qg5SBBbCHuzIzy9QCk2YYwJiDpI8t2RngzhJOexHGcCLYyM99yriqNYnFw
 DHoFkcUbJiHGhtEzzGuhz7LrSySclvqQRYbWLh7qcuUIKGdbPiWB8BmZtAkaFyaN
 fOJYwk8pSlpgvFqaAOicG3hAWTUxcJ5U/wWdBFk7Xg3wZ/K2XLuA88QgxePh2S2L
 kYBwwD81amWMEZct1hq9PW42vFFiWjJtZnTceZjCVARQamJ/+QgjUapMfbnYb1jN
 ry4XQoFz3FhfT4Ow2cKfRUzrh8lrNUJNqMoNiXDnj4jjH1YwIN6NqIYqrXJUGeCU
 yaaBGMu/
 =XdZL
 -----END PGP SIGNATURE-----

Merge tag 'v9_18_11' into v9_18

BIND 9.18.11
This commit is contained in:
Michał Kępień 2023-01-25 21:26:22 +01:00
commit 8b4dcc27ef
26 changed files with 636 additions and 257 deletions

22
CHANGES
View file

@ -20,8 +20,22 @@
not negotiate "dot" ALPN token could crash BIND
on shutdown. That has been fixed. [GL #3767]
6063. [bug] Revert a change that limited to honour single
read for TLSDNS as it broke XoT. [GL #3772]
--- 9.18.11 released ---
6067. [security] Fix serve-stale crash when recursive clients soft quota
is reached. (CVE-2022-3924) [GL #3619]
6066. [security] Handle RRSIG lookups when serve-stale is active.
(CVE-2022-3736) [GL #3622]
6064. [security] An UPDATE message flood could cause named to exhaust all
available memory. This flaw was addressed by adding a
new "update-quota" statement that controls the number of
simultaneous UPDATE messages that can be processed or
forwarded. The default is 100. A stats counter has been
added to record events when the update quota is
exceeded, and the XML and JSON statistics version
numbers have been updated. (CVE-2022-3094) [GL #3523]
6062. [func] The DSCP implementation, which has been
nonfunctional for some time, is now marked as
@ -88,7 +102,9 @@
[GL #3252]
5830. [func] Implement incremental resizing of isc_ht hash tables to
perform the rehashing gradually. [GL #3212]
perform the rehashing gradually. The catalog zone
implementation has been optimized to work with hundreds
of thousands of member zones. [GL #3212] [GL #3744]
--- 9.18.10 released ---

View file

@ -15,7 +15,7 @@
<xsl:output method="html" indent="yes" version="4.0"/>
<!-- the version number **below** must match version in bin/named/statschannel.c -->
<!-- don't forget to update "/xml/v<STATS_XML_VERSION_MAJOR>" in the HTTP endpoints listed below -->
<xsl:template match="statistics[@version=&quot;3.12&quot;]">
<xsl:template match="statistics[@version=&quot;3.13&quot;]">
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

View file

@ -137,6 +137,7 @@ options {\n\
trust-anchor-telemetry yes;\n\
udp-receive-buffer 0;\n\
udp-send-buffer 0;\n\
update-quota 100;\n\
\n\
/* view */\n\
allow-new-zones no;\n\

View file

@ -8686,6 +8686,7 @@ load_configuration(const char *filename, named_server_t *server,
configure_server_quota(maps, "tcp-clients", &server->sctx->tcpquota);
configure_server_quota(maps, "recursive-clients",
&server->sctx->recursionquota);
configure_server_quota(maps, "update-quota", &server->sctx->updquota);
max = isc_quota_getmax(&server->sctx->recursionquota);
if (max > 1000) {

View file

@ -55,11 +55,11 @@
#include "xsl_p.h"
#define STATS_XML_VERSION_MAJOR "3"
#define STATS_XML_VERSION_MINOR "12"
#define STATS_XML_VERSION_MINOR "13"
#define STATS_XML_VERSION STATS_XML_VERSION_MAJOR "." STATS_XML_VERSION_MINOR
#define STATS_JSON_VERSION_MAJOR "1"
#define STATS_JSON_VERSION_MINOR "6"
#define STATS_JSON_VERSION_MINOR "7"
#define STATS_JSON_VERSION STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR
#define CHECK(m) \
@ -351,6 +351,7 @@ init_desc(void) {
SET_NSSTATDESC(reclimitdropped,
"queries dropped due to recursive client limit",
"RecLimitDropped");
SET_NSSTATDESC(updatequota, "Update quota exceeded", "UpdateQuota");
INSIST(i == ns_statscounter_max);

View file

@ -72,6 +72,7 @@ options {
recursive-clients 3000;
serial-query-rate 100;
server-id none;
update-quota 200;
check-names primary warn;
check-names secondary ignore;
max-cache-size 20000000000000;

View file

@ -23,6 +23,7 @@ options {
recursion no;
notify yes;
minimal-responses no;
update-quota 1;
};
acl named-acl {
@ -83,6 +84,7 @@ zone "other.nil" {
check-integrity no;
check-mx warn;
update-policy local;
allow-query { !10.53.0.2; any; };
allow-query-on { 10.53.0.1; 127.0.0.1; };
allow-transfer { any; };
};

View file

@ -1353,6 +1353,34 @@ grep '10.53.0.1.*REFUSED' nsupdate.out-$n > /dev/null || ret=1
grep 'Reply from SOA query' nsupdate.out-$n > /dev/null || ret=1
[ $ret = 0 ] || { echo_i "failed"; status=1; }
n=$((n + 1))
ret=0
echo_i "check that update is rejected if query is not allowed ($n)"
{
$NSUPDATE -d <<END
local 10.53.0.2
server 10.53.0.1 ${PORT}
update add reject.other.nil 3600 IN TXT Whatever
send
END
} > nsupdate.out.test$n 2>&1
grep 'failed: REFUSED' nsupdate.out.test$n > /dev/null || ret=1
[ $ret = 0 ] || { echo_i "failed"; status=1; }
n=$((n + 1))
ret=0
echo_i "check that update is rejected if quota is exceeded ($n)"
for loop in 1 2 3 4 5 6 7 8 9 10; do
{
$NSUPDATE -4 -l -p ${PORT} -k ns1/session.key > /dev/null 2>&1 <<END
update add txt-$loop.other.nil 3600 IN TXT Whatever
send
END
} &
done
wait_for_log 10 "too many DNS UPDATEs queued" ns1/named.run || ret=1
[ $ret = 0 ] || { echo_i "failed"; status=1; }
if ! $FEATURETEST --gssapi ; then
echo_i "SKIPPED: GSSAPI tests"
else

View file

@ -31,3 +31,5 @@ rm -f keyname keyname.err
rm -f ns*/named.lock
rm -f ns1/example2.db
rm -f ns*/managed-keys.bind*
rm -f nsupdate.out.*
rm -f ns*/named.run.prev

View file

@ -25,25 +25,32 @@ options {
};
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "example" {
type secondary;
file "example.bk";
allow-update-forwarding { any; };
allow-update-forwarding { 10.53.0.1; };
primaries { 10.53.0.1; };
};
zone "example2" {
type secondary;
file "example2.bk";
allow-update-forwarding { any; };
allow-update-forwarding { 10.53.0.1; };
primaries { 10.53.0.1; };
};
zone "example3" {
type secondary;
file "example3.bk";
allow-update-forwarding { 10.53.0.1; };
primaries { 10.53.0.1; };
};

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port @PORT@;
tls-port @TLSPORT@;
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on tls ephemeral { 10.53.0.3; };
listen-on-v6 { none; };
recursion no;
notify yes;
update-quota 1;
};
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "example" {
type secondary;
file "example.bk";
allow-update-forwarding { any; };
primaries { 10.53.0.1; };
};

View file

@ -18,7 +18,7 @@ cp -f ns3/noprimary.db ns3/noprimary1.db
copy_setports ns1/named.conf.in ns1/named.conf
copy_setports ns2/named.conf.in ns2/named.conf
copy_setports ns3/named.conf.in ns3/named.conf
copy_setports ns3/named1.conf.in ns3/named.conf
if $FEATURETEST --enable-dnstap
then

View file

@ -81,6 +81,7 @@ if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
echo_i "updating zone (signed) ($n)"
ret=0
$NSUPDATE -y update.example:c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K -- - <<EOF || ret=1
local 10.53.0.1
server 10.53.0.3 ${PORT}
update add updated.example. 600 A 10.10.10.1
update add updated.example. 600 TXT Foo
@ -139,6 +140,7 @@ fi
echo_i "updating zone (unsigned) ($n)"
ret=0
$NSUPDATE -- - <<EOF || ret=1
local 10.53.0.1
server 10.53.0.3 ${PORT}
update add unsigned.example. 600 A 10.10.10.1
update add unsigned.example. 600 TXT Foo
@ -195,6 +197,7 @@ while [ $count -lt 5 -a $ret -eq 0 ]
do
(
$NSUPDATE -- - <<EOF
local 10.53.0.1
server 10.53.0.3 ${PORT}
zone noprimary
update add unsigned.noprimary. 600 A 10.10.10.1
@ -226,6 +229,7 @@ then
ret=0
keyname=`cat keyname`
$NSUPDATE -k $keyname.private -- - <<EOF
local 10.53.0.1
server 10.53.0.3 ${PORT}
zone example2
update add unsigned.example2. 600 A 10.10.10.1
@ -250,5 +254,40 @@ EOF
fi
fi
echo_i "attempting an update that should be rejected by ACL ($n)"
ret=0
{
$NSUPDATE -- - << EOF
local 10.53.0.2
server 10.53.0.3 ${PORT}
update add another.unsigned.example. 600 A 10.10.10.2
update add another.unsigned.example. 600 TXT Bar
send
EOF
} > nsupdate.out.$n 2>&1
grep REFUSED nsupdate.out.$n > /dev/null || ret=1
if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
n=`expr $n + 1`
n=$((n + 1))
ret=0
echo_i "attempting updates that should exceed quota ($n)"
# lower the update quota to 1.
copy_setports ns3/named2.conf.in ns3/named.conf
rndc_reconfig ns3 10.53.0.3
nextpart ns3/named.run > /dev/null
for loop in 1 2 3 4 5 6 7 8 9 10; do
{
$NSUPDATE -- - > /dev/null 2>&1 <<END
local 10.53.0.1
server 10.53.0.3 ${PORT}
update add txt-$loop.unsigned.example 300 IN TXT Whatever
send
END
} &
done
wait_for_log 10 "too many DNS UPDATEs queued" ns3/named.run || ret=1
[ $ret = 0 ] || { echo_i "failed"; status=1; }
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1

View file

@ -36,6 +36,7 @@ information about each release, and source code.
.. include:: ../notes/notes-known-issues.rst
.. include:: ../notes/notes-current.rst
.. include:: ../notes/notes-9.18.11.rst
.. include:: ../notes/notes-9.18.10.rst
.. include:: ../notes/notes-9.18.9.rst
.. include:: ../notes/notes-9.18.8.rst

View file

@ -3979,6 +3979,14 @@ system.
value as :any:`tcp-keepalive-timeout`. This value can be updated at
runtime by using :option:`rndc tcp-timeouts`.
.. namedconf:statement:: update-quota
:tags: server
:short: Specifies the maximum number of concurrent DNS UPDATE messages that can be processed by the server.
This is the maximum number of simultaneous DNS UPDATE messages that
the server will accept for updating local authoritiative zones or
forwarding to a primary server. The default is ``100``.
.. _intervals:
Periodic Task Intervals
@ -7902,6 +7910,11 @@ Name Server Statistics Counters
``UpdateBadPrereq``
This indicates the number of dynamic updates rejected due to a prerequisite failure.
``UpdateQuota``
This indicates the number of times a dynamic update or update
forwarding request was rejected because the number of pending
requests exceeded :any:`update-quota`.
``RateDropped``
This indicates the number of responses dropped due to rate limits.

View file

@ -250,7 +250,7 @@ at a very high level, looking up the name ``www.isc.org`` :
Let's take a quick break here and look at what we've got so far...
how can our server trust this answer? If a clever attacker had taken over
the ``isc.org`` name server(s), or course she would send matching
the ``isc.org`` name server(s), of course she would send matching
keys and signatures. We need to ask someone else to have confidence
that we are really talking to the real ``isc.org`` name server. This
is a critical part of DNSSEC: at some point, the DNS administrators

View file

@ -367,6 +367,7 @@ options {
udp\-receive\-buffer <integer>;
udp\-send\-buffer <integer>;
update\-check\-ksk <boolean>;
update\-quota <integer>;
use\-alt\-transfer\-source <boolean>; // deprecated
use\-v4\-udp\-ports { <portrange>; ... };
use\-v6\-udp\-ports { <portrange>; ... };

View file

@ -310,6 +310,7 @@ options {
udp-receive-buffer <integer>;
udp-send-buffer <integer>;
update-check-ksk <boolean>;
update-quota <integer>;
use-alt-transfer-source <boolean>; // deprecated
use-v4-udp-ports { <portrange>; ... };
use-v6-udp-ports { <portrange>; ... };

112
doc/notes/notes-9.18.11.rst Normal file
View file

@ -0,0 +1,112 @@
.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
..
.. SPDX-License-Identifier: MPL-2.0
..
.. This Source Code Form is subject to the terms of the Mozilla Public
.. License, v. 2.0. If a copy of the MPL was not distributed with this
.. file, you can obtain one at https://mozilla.org/MPL/2.0/.
..
.. See the COPYRIGHT file distributed with this work for additional
.. information regarding copyright ownership.
Notes for BIND 9.18.11
----------------------
Security Fixes
~~~~~~~~~~~~~~
- An UPDATE message flood could cause :iscman:`named` to exhaust all
available memory. This flaw was addressed by adding a new
:any:`update-quota` option that controls the maximum number of
outstanding DNS UPDATE messages that :iscman:`named` can hold in a
queue at any given time (default: 100). (CVE-2022-3094)
ISC would like to thank Rob Schulhof from Infoblox for bringing this
vulnerability to our attention. :gl:`#3523`
- :iscman:`named` could crash with an assertion failure when an RRSIG
query was received and :any:`stale-answer-client-timeout` was set to a
non-zero value. This has been fixed. (CVE-2022-3736)
ISC would like to thank Borja Marcos from Sarenet (with assistance by
Iratxe Niño from Fundación Sarenet) for bringing this vulnerability to
our attention. :gl:`#3622`
- :iscman:`named` running as a resolver with the
:any:`stale-answer-client-timeout` option set to any value greater
than ``0`` could crash with an assertion failure, when the
:any:`recursive-clients` soft quota was reached. This has been fixed.
(CVE-2022-3924)
ISC would like to thank Maksym Odinintsev from AWS for bringing this
vulnerability to our attention. :gl:`#3619`
New Features
~~~~~~~~~~~~
- The new :any:`update-quota` option can be used to control the number
of simultaneous DNS UPDATE messages that can be processed to update an
authoritative zone on a primary server, or forwarded to the primary
server by a secondary server. The default is 100. A new statistics
counter has also been added to record events when this quota is
exceeded, and the version numbers for the XML and JSON statistics
schemas have been updated. :gl:`#3523`
Removed Features
~~~~~~~~~~~~~~~~
- The Differentiated Services Code Point (DSCP) feature in BIND has been
non-operational since the new Network Manager was introduced in BIND
9.16. It is now marked as obsolete, and vestigial code implementing it
has been removed. Configuring DSCP values in ``named.conf`` now causes
a warning to be logged. :gl:`#3773`
Feature Changes
~~~~~~~~~~~~~~~
- The catalog zone implementation has been optimized to work with
hundreds of thousands of member zones. :gl:`#3212` :gl:`#3744`
Bug Fixes
~~~~~~~~~
- A rare assertion failure was fixed in outgoing TCP DNS connection
handling. :gl:`#3178` :gl:`#3636`
- Large zone transfers over TLS (XoT) could fail. This has been fixed.
:gl:`#3772`
- In addition to a previously fixed bug, another similar issue was
discovered where quotas could be erroneously reached for servers,
including any configured forwarders, resulting in SERVFAIL answers
being sent to clients. This has been fixed. :gl:`#3752`
- In certain query resolution scenarios (e.g. when following CNAME
records), :iscman:`named` configured to answer from stale cache could
return a SERVFAIL response despite a usable, non-stale answer being
present in the cache. This has been fixed. :gl:`#3678`
- When an outgoing request timed out, :iscman:`named` would retry up to
three times with the same server instead of trying the next available
name server. This has been fixed. :gl:`#3637`
- Recently used ADB names and ADB entries (IP addresses) could get
cleaned when ADB was under memory pressure. To mitigate this, only
actual ADB names and ADB entries are now counted (excluding internal
memory structures used for "housekeeping") and recently used (<= 10
seconds) ADB names and entries are excluded from the overmem memory
cleaner. :gl:`#3739`
- The "Prohibited" Extended DNS Error was inadvertently set in some
NOERROR responses. This has been fixed. :gl:`#3743`
- Previously, TLS session resumption could have led to handshake
failures when client certificates were used for authentication (Mutual
TLS). This has been fixed. :gl:`#3725`
Known Issues
~~~~~~~~~~~~
- There are no new known issues with this release. See :ref:`above
<relnotes_known_issues>` for a list of all known issues affecting this
BIND 9 branch.

View file

@ -10976,6 +10976,8 @@ dns_resolver_cancelfetch(dns_fetch_t *fetch) {
fetchctx_t *fctx = NULL;
dns_resolver_t *res = NULL;
dns_fetchevent_t *event = NULL;
dns_fetchevent_t *event_trystale = NULL;
dns_fetchevent_t *event_fetchdone = NULL;
REQUIRE(DNS_FETCH_VALID(fetch));
fctx = fetch->private;
@ -10987,9 +10989,9 @@ dns_resolver_cancelfetch(dns_fetch_t *fetch) {
LOCK(&res->buckets[fctx->bucketnum].lock);
/*
* Find the completion event for this fetch (as opposed
* Find the events for this fetch (as opposed
* to those for other fetches that have joined the same
* fctx) and send it with result = ISC_R_CANCELED.
* fctx) and send them with result = ISC_R_CANCELED.
*/
if (fctx->state != fetchstate_done) {
dns_fetchevent_t *next_event = NULL;
@ -10999,15 +11001,42 @@ dns_resolver_cancelfetch(dns_fetch_t *fetch) {
next_event = ISC_LIST_NEXT(event, ev_link);
if (event->fetch == fetch) {
ISC_LIST_UNLINK(fctx->events, event, ev_link);
break;
switch (event->ev_type) {
case DNS_EVENT_TRYSTALE:
INSIST(event_trystale == NULL);
event_trystale = event;
break;
case DNS_EVENT_FETCHDONE:
INSIST(event_fetchdone == NULL);
event_fetchdone = event;
break;
default:
UNREACHABLE();
}
if (event_trystale != NULL &&
event_fetchdone != NULL)
{
break;
}
}
}
}
if (event != NULL) {
isc_task_t *etask = event->ev_sender;
event->ev_sender = fctx;
event->result = ISC_R_CANCELED;
isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event));
/*
* The "trystale" event must be sent before the "fetchdone" event,
* because the latter clears the "recursing" query attribute, which is
* required by both events (handled by the same callback function).
*/
if (event_trystale != NULL) {
isc_task_t *etask = event_trystale->ev_sender;
event_trystale->ev_sender = fctx;
event_trystale->result = ISC_R_CANCELED;
isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event_trystale));
}
if (event_fetchdone != NULL) {
isc_task_t *etask = event_fetchdone->ev_sender;
event_fetchdone->ev_sender = fctx;
event_fetchdone->result = ISC_R_CANCELED;
isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event_fetchdone));
}
/*

View file

@ -1342,6 +1342,7 @@ static cfg_clausedef_t options_clauses[] = {
{ "treat-cr-as-space", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "udp-receive-buffer", &cfg_type_uint32, 0 },
{ "udp-send-buffer", &cfg_type_uint32, 0 },
{ "update-quota", &cfg_type_uint32, 0 },
{ "use-id-pool", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "use-ixfr", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },

View file

@ -84,6 +84,7 @@ struct ns_server {
isc_quota_t recursionquota;
isc_quota_t tcpquota;
isc_quota_t xfroutquota;
isc_quota_t updquota;
ISC_LIST(isc_quota_t) http_quotas;
isc_mutex_t http_quotas_lock;

View file

@ -107,7 +107,9 @@ enum {
ns_statscounter_reclimitdropped = 66,
ns_statscounter_max = 67,
ns_statscounter_updatequota = 67,
ns_statscounter_max = 68,
};
void

View file

@ -5237,6 +5237,15 @@ qctx_init(ns_client_t *client, dns_fetchevent_t **eventp, dns_rdatatype_t qtype,
qctx->result = ISC_R_SUCCESS;
qctx->findcoveringnsec = qctx->view->synthfromdnssec;
/*
* If it's an RRSIG or SIG query, we'll iterate the node.
*/
if (qctx->qtype == dns_rdatatype_rrsig ||
qctx->qtype == dns_rdatatype_sig)
{
qctx->type = dns_rdatatype_any;
}
CALL_HOOK_NORETURN(NS_QUERY_QCTX_INITIALIZED, qctx);
}
@ -5424,15 +5433,6 @@ query_setup(ns_client_t *client, dns_rdatatype_t qtype) {
CALL_HOOK(NS_QUERY_SETUP, &qctx);
/*
* If it's a SIG query, we'll iterate the node.
*/
if (qctx.qtype == dns_rdatatype_rrsig ||
qctx.qtype == dns_rdatatype_sig)
{
qctx.type = dns_rdatatype_any;
}
/*
* Check SERVFAIL cache
*/
@ -6236,7 +6236,9 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
if (event->ev_type == DNS_EVENT_TRYSTALE) {
query_lookup_stale(client);
if (devent->result != ISC_R_CANCELED) {
query_lookup_stale(client);
}
isc_event_free(ISC_EVENT_PTR(&event));
return;
}

View file

@ -54,6 +54,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
isc_quota_init(&sctx->xfroutquota, 10);
isc_quota_init(&sctx->tcpquota, 10);
isc_quota_init(&sctx->recursionquota, 100);
isc_quota_init(&sctx->updquota, 100);
ISC_LIST_INIT(sctx->http_quotas);
isc_mutex_init(&sctx->http_quotas_lock);
@ -136,6 +137,7 @@ ns_server_detach(ns_server_t **sctxp) {
isc_mem_put(sctx->mctx, altsecret, sizeof(*altsecret));
}
isc_quota_destroy(&sctx->updquota);
isc_quota_destroy(&sctx->recursionquota);
isc_quota_destroy(&sctx->tcpquota);
isc_quota_destroy(&sctx->xfroutquota);

View file

@ -231,6 +231,8 @@ struct update_event {
dns_zone_t *zone;
isc_result_t result;
dns_message_t *answer;
const dns_ssurule_t **rules;
size_t ruleslen;
};
/*%
@ -270,6 +272,9 @@ static void
forward_done(isc_task_t *task, isc_event_t *event);
static isc_result_t
add_rr_prepare_action(void *data, rr_t *rr);
static isc_result_t
rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
const dns_rdata_t *rdata, bool *flag);
/**************************************************************************/
@ -342,25 +347,26 @@ inc_stats(ns_client_t *client, dns_zone_t *zone, isc_statscounter_t counter) {
static isc_result_t
checkqueryacl(ns_client_t *client, dns_acl_t *queryacl, dns_name_t *zonename,
dns_acl_t *updateacl, dns_ssutable_t *ssutable) {
isc_result_t result;
char namebuf[DNS_NAME_FORMATSIZE];
char classbuf[DNS_RDATACLASS_FORMATSIZE];
int level;
isc_result_t result;
bool update_possible =
((updateacl != NULL && !dns_acl_isnone(updateacl)) ||
ssutable != NULL);
result = ns_client_checkaclsilent(client, NULL, queryacl, true);
if (result != ISC_R_SUCCESS) {
int level = update_possible ? ISC_LOG_ERROR : ISC_LOG_INFO;
dns_name_format(zonename, namebuf, sizeof(namebuf));
dns_rdataclass_format(client->view->rdclass, classbuf,
sizeof(classbuf));
level = (updateacl == NULL && ssutable == NULL) ? ISC_LOG_INFO
: ISC_LOG_ERROR;
ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
NS_LOGMODULE_UPDATE, level,
"update '%s/%s' denied due to allow-query",
namebuf, classbuf);
} else if (updateacl == NULL && ssutable == NULL) {
} else if (!update_possible) {
dns_name_format(zonename, namebuf, sizeof(namebuf));
dns_rdataclass_format(client->view->rdclass, classbuf,
sizeof(classbuf));
@ -1644,12 +1650,247 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
isc_result_t result = ISC_R_SUCCESS;
update_event_t *event = NULL;
isc_task_t *zonetask = NULL;
dns_ssutable_t *ssutable = NULL;
dns_message_t *request = client->message;
isc_mem_t *mctx = client->manager->mctx;
dns_aclenv_t *env = client->manager->aclenv;
dns_rdataclass_t zoneclass;
dns_rdatatype_t covers;
dns_name_t *zonename = NULL;
const dns_ssurule_t **rules = NULL;
size_t rule = 0, ruleslen = 0;
dns_db_t *db = NULL;
dns_dbversion_t *ver = NULL;
CHECK(dns_zone_getdb(zone, &db));
zonename = dns_db_origin(db);
zoneclass = dns_db_class(db);
dns_zone_getssutable(zone, &ssutable);
dns_db_currentversion(db, &ver);
/*
* Update message processing can leak record existence information
* so check that we are allowed to query this zone. Additionally,
* if we would refuse all updates for this zone, we bail out here.
*/
CHECK(checkqueryacl(client, dns_zone_getqueryacl(zone),
dns_zone_getorigin(zone),
dns_zone_getupdateacl(zone), ssutable));
/*
* Check requestor's permissions.
*/
if (ssutable == NULL) {
CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
"update", dns_zone_getorigin(zone), false,
false));
} else if (client->signer == NULL && !TCPCLIENT(client)) {
CHECK(checkupdateacl(client, NULL, "update",
dns_zone_getorigin(zone), false, true));
}
if (dns_zone_getupdatedisabled(zone)) {
FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled "
"because the zone is frozen. Use "
"'rndc thaw' to re-enable updates.");
}
/*
* Prescan the update section, checking for updates that
* are illegal or violate policy.
*/
if (ssutable != NULL) {
ruleslen = request->counts[DNS_SECTION_UPDATE];
rules = isc_mem_get(mctx, sizeof(*rules) * ruleslen);
memset(rules, 0, sizeof(*rules) * ruleslen);
}
for (rule = 0,
result = dns_message_firstname(request, DNS_SECTION_UPDATE);
result == ISC_R_SUCCESS;
rule++, result = dns_message_nextname(request, DNS_SECTION_UPDATE))
{
dns_name_t *name = NULL;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_ttl_t ttl;
dns_rdataclass_t update_class;
INSIST(ssutable == NULL || rule < ruleslen);
get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, &name,
&rdata, &covers, &ttl, &update_class);
if (!dns_name_issubdomain(name, zonename)) {
FAILC(DNS_R_NOTZONE, "update RR is outside zone");
}
if (update_class == zoneclass) {
/*
* Check for meta-RRs. The RFC2136 pseudocode says
* check for ANY|AXFR|MAILA|MAILB, but the text adds
* "or any other QUERY metatype"
*/
if (dns_rdatatype_ismeta(rdata.type)) {
FAILC(DNS_R_FORMERR, "meta-RR in update");
}
result = dns_zone_checknames(zone, name, &rdata);
if (result != ISC_R_SUCCESS) {
FAIL(DNS_R_REFUSED);
}
} else if (update_class == dns_rdataclass_any) {
if (ttl != 0 || rdata.length != 0 ||
(dns_rdatatype_ismeta(rdata.type) &&
rdata.type != dns_rdatatype_any))
{
FAILC(DNS_R_FORMERR, "meta-RR in update");
}
} else if (update_class == dns_rdataclass_none) {
if (ttl != 0 || dns_rdatatype_ismeta(rdata.type)) {
FAILC(DNS_R_FORMERR, "meta-RR in update");
}
} else {
update_log(client, zone, ISC_LOG_WARNING,
"update RR has incorrect class %d",
update_class);
FAIL(DNS_R_FORMERR);
}
/*
* draft-ietf-dnsind-simple-secure-update-01 says
* "Unlike traditional dynamic update, the client
* is forbidden from updating NSEC records."
*/
if (rdata.type == dns_rdatatype_nsec3) {
FAILC(DNS_R_REFUSED, "explicit NSEC3 updates are not "
"allowed "
"in secure zones");
} else if (rdata.type == dns_rdatatype_nsec) {
FAILC(DNS_R_REFUSED, "explicit NSEC updates are not "
"allowed "
"in secure zones");
} else if (rdata.type == dns_rdatatype_rrsig &&
!dns_name_equal(name, zonename))
{
FAILC(DNS_R_REFUSED, "explicit RRSIG updates are "
"currently "
"not supported in secure zones "
"except "
"at the apex");
}
if (ssutable != NULL) {
isc_netaddr_t netaddr;
dns_name_t *target = NULL;
dst_key_t *tsigkey = NULL;
dns_rdata_ptr_t ptr;
dns_rdata_in_srv_t srv;
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
if (client->message->tsigkey != NULL) {
tsigkey = client->message->tsigkey->key;
}
if ((update_class == dns_rdataclass_in ||
update_class == dns_rdataclass_none) &&
rdata.type == dns_rdatatype_ptr)
{
result = dns_rdata_tostruct(&rdata, &ptr, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
target = &ptr.ptr;
}
if ((update_class == dns_rdataclass_in ||
update_class == dns_rdataclass_none) &&
rdata.type == dns_rdatatype_srv)
{
result = dns_rdata_tostruct(&rdata, &srv, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
target = &srv.target;
}
if (update_class == dns_rdataclass_any &&
zoneclass == dns_rdataclass_in &&
(rdata.type == dns_rdatatype_ptr ||
rdata.type == dns_rdatatype_srv))
{
ssu_check_t ssuinfo;
ssuinfo.name = name;
ssuinfo.table = ssutable;
ssuinfo.signer = client->signer;
ssuinfo.addr = &netaddr;
ssuinfo.aclenv = env;
ssuinfo.tcp = TCPCLIENT(client);
ssuinfo.key = tsigkey;
result = foreach_rr(db, ver, name, rdata.type,
dns_rdatatype_none,
ssu_checkrr, &ssuinfo);
if (result != ISC_R_SUCCESS) {
FAILC(DNS_R_REFUSED,
"rejected by secure update");
}
} else if (target != NULL &&
update_class == dns_rdataclass_none)
{
bool flag;
CHECK(rr_exists(db, ver, name, &rdata, &flag));
if (flag &&
!dns_ssutable_checkrules(
ssutable, client->signer, name,
&netaddr, TCPCLIENT(client), env,
rdata.type, target, tsigkey,
&rules[rule]))
{
FAILC(DNS_R_REFUSED,
"rejected by secure update");
}
} else if (rdata.type != dns_rdatatype_any) {
if (!dns_ssutable_checkrules(
ssutable, client->signer, name,
&netaddr, TCPCLIENT(client), env,
rdata.type, target, tsigkey,
&rules[rule]))
{
FAILC(DNS_R_REFUSED, "rejected by "
"secure update");
}
} else {
if (!ssu_checkall(db, ver, name, ssutable,
client->signer, &netaddr, env,
TCPCLIENT(client), tsigkey))
{
FAILC(DNS_R_REFUSED, "rejected by "
"secure update");
}
}
}
}
if (result != ISC_R_NOMORE) {
FAIL(result);
}
update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK");
result = isc_quota_attach(&client->manager->sctx->updquota,
&(isc_quota_t *){ NULL });
if (result != ISC_R_SUCCESS) {
update_log(client, zone, LOGLEVEL_PROTOCOL,
"update failed: too many DNS UPDATEs queued (%s)",
isc_result_totext(result));
ns_stats_increment(client->manager->sctx->nsstats,
ns_statscounter_updatequota);
CHECK(DNS_R_DROP);
}
event = (update_event_t *)isc_event_allocate(
client->mctx, client, DNS_EVENT_UPDATE, update_action, NULL,
sizeof(*event));
event->zone = zone;
event->result = ISC_R_SUCCESS;
event->rules = rules;
event->ruleslen = ruleslen;
rules = NULL;
INSIST(client->nupdates == 0);
client->nupdates++;
@ -1659,6 +1900,20 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
dns_zone_gettask(zone, &zonetask);
isc_task_send(zonetask, ISC_EVENT_PTR(&event));
failure:
if (db != NULL) {
dns_db_closeversion(db, &ver, false);
dns_db_detach(&db);
}
if (rules != NULL) {
isc_mem_put(mctx, rules, sizeof(*rules) * ruleslen);
}
if (ssutable != NULL) {
dns_ssutable_detach(&ssutable);
}
return (result);
}
@ -1766,9 +2021,6 @@ ns_update_start(ns_client_t *client, isc_nmhandle_t *handle,
break;
case dns_zone_secondary:
case dns_zone_mirror:
CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone),
"update forwarding", zonename, true,
false));
dns_message_clonebuffer(client->message);
CHECK(send_forward_event(client, zone));
break;
@ -1779,16 +2031,21 @@ ns_update_start(ns_client_t *client, isc_nmhandle_t *handle,
failure:
if (result == DNS_R_REFUSED) {
INSIST(dns_zone_gettype(zone) == dns_zone_secondary ||
dns_zone_gettype(zone) == dns_zone_mirror);
inc_stats(client, zone, ns_statscounter_updaterej);
}
/*
* We failed without having sent an update event to the zone.
* We are still in the client task context, so we can
* simply give an error response without switching tasks.
*/
respond(client, result);
if (result == DNS_R_DROP) {
ns_client_drop(client, result);
isc_nmhandle_detach(&client->reqhandle);
} else {
respond(client, result);
}
if (zone != NULL) {
dns_zone_detach(&zone);
}
@ -2622,6 +2879,8 @@ update_action(isc_task_t *task, isc_event_t *event) {
update_event_t *uev = (update_event_t *)event;
dns_zone_t *zone = uev->zone;
ns_client_t *client = (ns_client_t *)event->ev_arg;
const dns_ssurule_t **rules = uev->rules;
size_t rule = 0, ruleslen = uev->ruleslen;
isc_result_t result;
dns_db_t *db = NULL;
dns_dbversion_t *oldver = NULL;
@ -2633,7 +2892,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
dns_rdatatype_t covers;
dns_message_t *request = client->message;
dns_rdataclass_t zoneclass;
dns_name_t *zonename;
dns_name_t *zonename = NULL;
dns_ssutable_t *ssutable = NULL;
dns_fixedname_t tmpnamefixed;
dns_name_t *tmpname = NULL;
@ -2645,10 +2904,6 @@ update_action(isc_task_t *task, isc_event_t *event) {
dns_ttl_t maxttl = 0;
uint32_t maxrecords;
uint64_t records;
dns_aclenv_t *env = client->manager->aclenv;
size_t ruleslen = 0;
size_t rule;
const dns_ssurule_t **rules = NULL;
INSIST(event->ev_type == DNS_EVENT_UPDATE);
@ -2659,14 +2914,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
zonename = dns_db_origin(db);
zoneclass = dns_db_class(db);
dns_zone_getssutable(zone, &ssutable);
/*
* Update message processing can leak record existence information
* so check that we are allowed to query this zone. Additionally
* if we would refuse all updates for this zone we bail out here.
*/
CHECK(checkqueryacl(client, dns_zone_getqueryacl(zone), zonename,
dns_zone_getupdateacl(zone), ssutable));
options = dns_zone_getoptions(zone);
/*
* Get old and new versions now that queryacl has been checked.
@ -2801,205 +3049,10 @@ update_action(isc_task_t *task, isc_event_t *event) {
update_log(client, zone, LOGLEVEL_DEBUG, "prerequisites are OK");
/*
* Check Requestor's Permissions. It seems a bit silly to do this
* only after prerequisite testing, but that is what RFC2136 says.
*/
if (ssutable == NULL) {
CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
"update", zonename, false, false));
} else if (client->signer == NULL && !TCPCLIENT(client)) {
CHECK(checkupdateacl(client, NULL, "update", zonename, false,
true));
}
if (dns_zone_getupdatedisabled(zone)) {
FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled "
"because the zone is frozen. Use "
"'rndc thaw' to re-enable updates.");
}
/*
* Perform the Update Section Prescan.
*/
if (ssutable != NULL) {
ruleslen = request->counts[DNS_SECTION_UPDATE];
rules = isc_mem_get(mctx, sizeof(*rules) * ruleslen);
memset(rules, 0, sizeof(*rules) * ruleslen);
}
for (rule = 0,
result = dns_message_firstname(request, DNS_SECTION_UPDATE);
result == ISC_R_SUCCESS;
rule++, result = dns_message_nextname(request, DNS_SECTION_UPDATE))
{
dns_name_t *name = NULL;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_ttl_t ttl;
dns_rdataclass_t update_class;
INSIST(ssutable == NULL || rule < ruleslen);
get_current_rr(request, DNS_SECTION_UPDATE, zoneclass, &name,
&rdata, &covers, &ttl, &update_class);
if (!dns_name_issubdomain(name, zonename)) {
FAILC(DNS_R_NOTZONE, "update RR is outside zone");
}
if (update_class == zoneclass) {
/*
* Check for meta-RRs. The RFC2136 pseudocode says
* check for ANY|AXFR|MAILA|MAILB, but the text adds
* "or any other QUERY metatype"
*/
if (dns_rdatatype_ismeta(rdata.type)) {
FAILC(DNS_R_FORMERR, "meta-RR in update");
}
result = dns_zone_checknames(zone, name, &rdata);
if (result != ISC_R_SUCCESS) {
FAIL(DNS_R_REFUSED);
}
} else if (update_class == dns_rdataclass_any) {
if (ttl != 0 || rdata.length != 0 ||
(dns_rdatatype_ismeta(rdata.type) &&
rdata.type != dns_rdatatype_any))
{
FAILC(DNS_R_FORMERR, "meta-RR in update");
}
} else if (update_class == dns_rdataclass_none) {
if (ttl != 0 || dns_rdatatype_ismeta(rdata.type)) {
FAILC(DNS_R_FORMERR, "meta-RR in update");
}
} else {
update_log(client, zone, ISC_LOG_WARNING,
"update RR has incorrect class %d",
update_class);
FAIL(DNS_R_FORMERR);
}
/*
* draft-ietf-dnsind-simple-secure-update-01 says
* "Unlike traditional dynamic update, the client
* is forbidden from updating NSEC records."
*/
if (rdata.type == dns_rdatatype_nsec3) {
FAILC(DNS_R_REFUSED, "explicit NSEC3 updates are not "
"allowed "
"in secure zones");
} else if (rdata.type == dns_rdatatype_nsec) {
FAILC(DNS_R_REFUSED, "explicit NSEC updates are not "
"allowed "
"in secure zones");
} else if (rdata.type == dns_rdatatype_rrsig &&
!dns_name_equal(name, zonename))
{
FAILC(DNS_R_REFUSED, "explicit RRSIG updates are "
"currently "
"not supported in secure zones "
"except "
"at the apex");
}
if (ssutable != NULL) {
isc_netaddr_t netaddr;
dns_name_t *target = NULL;
dst_key_t *tsigkey = NULL;
dns_rdata_ptr_t ptr;
dns_rdata_in_srv_t srv;
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
if (client->message->tsigkey != NULL) {
tsigkey = client->message->tsigkey->key;
}
if ((update_class == dns_rdataclass_in ||
update_class == dns_rdataclass_none) &&
rdata.type == dns_rdatatype_ptr)
{
result = dns_rdata_tostruct(&rdata, &ptr, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
target = &ptr.ptr;
}
if ((update_class == dns_rdataclass_in ||
update_class == dns_rdataclass_none) &&
rdata.type == dns_rdatatype_srv)
{
result = dns_rdata_tostruct(&rdata, &srv, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
target = &srv.target;
}
if (update_class == dns_rdataclass_any &&
zoneclass == dns_rdataclass_in &&
(rdata.type == dns_rdatatype_ptr ||
rdata.type == dns_rdatatype_srv))
{
ssu_check_t ssuinfo;
ssuinfo.name = name;
ssuinfo.table = ssutable;
ssuinfo.signer = client->signer;
ssuinfo.addr = &netaddr;
ssuinfo.aclenv = env;
ssuinfo.tcp = TCPCLIENT(client);
ssuinfo.key = tsigkey;
result = foreach_rr(db, ver, name, rdata.type,
dns_rdatatype_none,
ssu_checkrr, &ssuinfo);
if (result != ISC_R_SUCCESS) {
FAILC(DNS_R_REFUSED,
"rejected by secure update");
}
} else if (target != NULL &&
update_class == dns_rdataclass_none)
{
bool flag;
CHECK(rr_exists(db, ver, name, &rdata, &flag));
if (flag &&
!dns_ssutable_checkrules(
ssutable, client->signer, name,
&netaddr, TCPCLIENT(client), env,
rdata.type, target, tsigkey,
&rules[rule]))
{
FAILC(DNS_R_REFUSED,
"rejected by secure update");
}
} else if (rdata.type != dns_rdatatype_any) {
if (!dns_ssutable_checkrules(
ssutable, client->signer, name,
&netaddr, TCPCLIENT(client), env,
rdata.type, target, tsigkey,
&rules[rule]))
{
FAILC(DNS_R_REFUSED, "rejected by "
"secure update");
}
} else {
if (!ssu_checkall(db, ver, name, ssutable,
client->signer, &netaddr, env,
TCPCLIENT(client), tsigkey))
{
FAILC(DNS_R_REFUSED, "rejected by "
"secure update");
}
}
}
}
if (result != ISC_R_NOMORE) {
FAIL(result);
}
update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK");
/*
* Process the Update Section.
*/
options = dns_zone_getoptions(zone);
INSIST(ssutable == NULL || rules != NULL);
for (rule = 0,
result = dns_message_firstname(request, DNS_SECTION_UPDATE);
result == ISC_R_SUCCESS;
@ -3461,10 +3514,7 @@ update_action(isc_task_t *task, isc_event_t *event) {
if (result == ISC_R_SUCCESS && records > maxrecords) {
update_log(client, zone, ISC_LOG_ERROR,
"records in zone (%" PRIu64 ") "
"exceeds"
" max-"
"records"
" (%u)",
"exceeds max-records (%u)",
records, maxrecords);
result = DNS_R_TOOMANYRECORDS;
goto failure;
@ -3669,6 +3719,7 @@ updatedone_action(isc_task_t *task, isc_event_t *event) {
respond(client, uev->result);
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
isc_event_free(&event);
isc_nmhandle_detach(&client->updatehandle);
}
@ -3685,6 +3736,8 @@ forward_fail(isc_task_t *task, isc_event_t *event) {
INSIST(client->nupdates > 0);
client->nupdates--;
respond(client, DNS_R_SERVFAIL);
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
isc_event_free(&event);
isc_nmhandle_detach(&client->updatehandle);
}
@ -3722,6 +3775,8 @@ forward_done(isc_task_t *task, isc_event_t *event) {
client->nupdates--;
ns_client_sendraw(client, uev->answer);
dns_message_detach(&uev->answer);
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
isc_event_free(&event);
isc_nmhandle_detach(&client->reqhandle);
isc_nmhandle_detach(&client->updatehandle);
@ -3757,6 +3812,24 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
update_event_t *event = NULL;
isc_task_t *zonetask = NULL;
result = checkupdateacl(client, dns_zone_getforwardacl(zone),
"update forwarding", dns_zone_getorigin(zone),
true, false);
if (result != ISC_R_SUCCESS) {
return (result);
}
result = isc_quota_attach(&client->manager->sctx->updquota,
&(isc_quota_t *){ NULL });
if (result != ISC_R_SUCCESS) {
update_log(client, zone, LOGLEVEL_PROTOCOL,
"update failed: too many DNS UPDATEs queued (%s)",
isc_result_totext(result));
ns_stats_increment(client->manager->sctx->nsstats,
ns_statscounter_updatequota);
return (DNS_R_DROP);
}
event = (update_event_t *)isc_event_allocate(
client->mctx, client, DNS_EVENT_UPDATE, forward_action, NULL,
sizeof(*event));