diff --git a/bin/tests/system/resolver/ns4/named.noaa b/bin/tests/system/resolver/ns4/named.noaa deleted file mode 100644 index be78cc2c94..0000000000 --- a/bin/tests/system/resolver/ns4/named.noaa +++ /dev/null @@ -1,12 +0,0 @@ -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. - -Add -T noaa. diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index 066f42460f..b35bea001c 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -234,6 +234,10 @@ done if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) +stop_server ns4 +touch ns4/named.noaa +start_server --noclean --restart --port ${PORT} ns4 || ret=1 + n=$((n + 1)) echo_i "RT21594 regression test check setup ($n)" ret=0 @@ -270,6 +274,10 @@ grep "status: NXDOMAIN" dig.ns5.out.${n} >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) +stop_server ns4 +rm ns4/named.noaa +start_server --noclean --restart --port ${PORT} ns4 || ret=1 + n=$((n + 1)) echo_i "check that replacement of additional data by a negative cache no data entry clears the additional RRSIGs ($n)" ret=0 diff --git a/doc/arm/advanced.inc.rst b/doc/arm/advanced.inc.rst index 73202eb171..5ebb6a3dc9 100644 --- a/doc/arm/advanced.inc.rst +++ b/doc/arm/advanced.inc.rst @@ -99,7 +99,7 @@ from a primary server, the secondary checks to see that its version of the zone is the current version and, if not, initiates a zone transfer. For more information about DNS NOTIFY, see the description of the -:namedconf:ref:`notify` and :namedconf:ref`also-notify` statements. +:namedconf:ref:`notify` and :namedconf:ref:`also-notify` statements. The NOTIFY protocol is specified in :rfc:`1996`. .. note:: diff --git a/doc/arm/changelog.rst b/doc/arm/changelog.rst index ec3a049e62..eeb76f8592 100644 --- a/doc/arm/changelog.rst +++ b/doc/arm/changelog.rst @@ -18,6 +18,7 @@ Changelog development. Regular users should refer to :ref:`Release Notes ` for changes relevant to them. +.. include:: ../changelog/changelog-9.21.4.rst .. include:: ../changelog/changelog-9.21.3.rst .. include:: ../changelog/changelog-9.21.2.rst .. include:: ../changelog/changelog-9.21.1.rst diff --git a/doc/arm/notes.rst b/doc/arm/notes.rst index ac2806e5ec..6446243051 100644 --- a/doc/arm/notes.rst +++ b/doc/arm/notes.rst @@ -47,6 +47,7 @@ The list of known issues affecting the latest version in the 9.21 branch can be found at https://gitlab.isc.org/isc-projects/bind9/-/wikis/Known-Issues-in-BIND-9.21 +.. include:: ../notes/notes-9.21.4.rst .. include:: ../notes/notes-9.21.3.rst .. include:: ../notes/notes-9.21.2.rst .. include:: ../notes/notes-9.21.1.rst diff --git a/doc/changelog/changelog-9.21.4.rst b/doc/changelog/changelog-9.21.4.rst new file mode 100644 index 0000000000..d303591912 --- /dev/null +++ b/doc/changelog/changelog-9.21.4.rst @@ -0,0 +1,304 @@ +.. 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. + +BIND 9.21.4 +----------- + +Security Fixes +~~~~~~~~~~~~~~ + +- [CVE-2024-12705] DNS-over-HTTP(s) flooding fixes. ``bddaff32104`` + + Fix DNS-over-HTTP(S) implementation issues that arise under heavy + query load. Optimize resource usage for :iscman:`named` instances that + accept queries over DNS-over-HTTP(S). + + Previously, :iscman:`named` would process all incoming HTTP/2 data at + once, which could overwhelm the server, especially when dealing with + clients that send requests but don't wait for responses. That has been + fixed. Now, :iscman:`named` handles HTTP/2 data in smaller chunks and + throttles reading until the remote side reads the response data. It + also throttles clients that send too many requests at once. + + Additionally, :iscman:`named` now carefully processes data sent by + some clients, which can be considered "flooding." It logs these + clients and drops connections from them. :gl:`#4795` + + In some cases, :iscman:`named` could leave DNS-over-HTTP(S) + connections in the `CLOSE_WAIT` state indefinitely. That also has been + fixed. ISC would like to thank JF Billaud for thoroughly investigating + the issue and verifying the fix. :gl:`#5083` :gl:`#4795` :gl:`#5083` + +- [CVE-2024-11187] Limit the additional processing for large RDATA sets. + ``4d054cca7a0`` + + When answering queries, don't add data to the additional section if + the answer has more than 13 names in the RDATA. This limits the number + of lookups into the database(s) during a single client query, reducing + query processing load. :gl:`#5034` + +New Features +~~~~~~~~~~~~ + +- Add Extended DNS Error Code 22 - No Reachable Authority. + ``3972eacdad2`` + + When the resolver is trying to query an authority server and + eventually timed out, a SERVFAIL answer is given to the client. Add + the Extended DNS Error Code 22 - No Reachable Authority to the + response. :gl:`#2268` :gl:`!9743` + +- Enable extraction of exact local socket addresses. ``44d5dbeab63`` + + Enable extracting the exact address/port that a local wildcard/TCP + socket is bound to, improving the accuracy of dnstap logging and + providing more information in debug logs produced by system tests. + Since this requires issuing an extra system call on some hot paths, + this new feature is only enabled when the ``ISC_SOCKET_DETAILS`` + preprocessor macro is set at compile time. :gl:`#4344` :gl:`!8348` + +- Log both "from" and "to" socket in debug messages. ``6230bc883a5`` + + Debug messages logging network traffic now include information about + both sides of each communication channel rather than just one of them. + :gl:`#4345` :gl:`!8349` + +- Add "Zone has [AAAA/A] records but is not served by IPv[6/4]" + warnings. ``ef6dc36e530`` + + Check that zones with AAAA records are served by IPv6 servers and that + zones with A records are served by IPv4 servers. Sometimes, IPv6 + services are accidentally misconfigured and zones with IPv6 (AAAA) + address records are not served by DNS servers with IPv6 addresses, + which means they need to use translation devices to look up those IPv6 + addresses. The reverse is also sometimes true: zones with A records + are not resolvable over IPv4 when they should be. To prevent this, + BIND now looks for these misconfigured zones and issues a warning if + they are found. :gl:`#4370` :gl:`!8393` + +- Add a new option to configure the maximum number of outgoing queries + per client request. ``80a5745a1f8`` + + The configuration option 'max-query-count' sets how many outgoing + queries per client request is allowed. The existing + 'max-recursion-queries' is the number of permissible queries for a + single name and is reset on every CNAME redirection. This new option + is a global limit on the client request. The default is 200. + + This allows us to send a bit more queries while looking up a single + name. The default for 'max-recursion-queries' is changed from 32 to + 50. :gl:`#4980` :gl:`#4921` :gl:`!9737` + +Removed Features +~~~~~~~~~~~~~~~~ + +- Remove dnssec-must-be-secure feature. ``f5f792f1ed2`` + + :gl:`#4482` :gl:`!9851` + +- Remove 'sortlist' option. ``2bce06e170a`` + + The `sortlist` option, which was deprecated in BIND 9.20, has now been + removed. :gl:`#4665` :gl:`!9839` + +- Remove fixed value for the rrset-order option. ``5bee088dd1f`` + + Remove the "fixed" value from the "rrset-order" option and from the + autoconf script. :gl:`#4666` :gl:`!9852` + +- Remove the log message about incomplete IPv6 API. ``3779a81d501`` + + The log message would not be ever reached, because the IPv6 API is + always considered to be complete. Just remove the dead code. + :gl:`#5068` :gl:`!9798` + +- Remove trusted-keys and managed-keys options. ``9de6b228d41`` + + These options have been deprecated in 9.19 in favor of the + 'trust-anchors' option and are now being removed. :gl:`#5080` + :gl:`!9855` + +- Drop single-use RETERR macro. ``f6ff4fff85e`` + + If the RETERR define is only used once in a file, just drop the macro. + :gl:`!9871` + +- Remove C++ support from the public header. ``8d9bc93e81e`` + + Since BIND 9 headers are not longer public, there's no reason to keep + the ISC_LANG_BEGINDECL and ISC_LANG_ENDDECL macros to support + including them from C++ projects. :gl:`!9925` + +- Remove DLV remnants. ``f4377a3cd69`` + + DLV is long gone, so we can remove design documentation around DLV, + related command line options (that were already a hard failure), and + some DLV related test remnants. :gl:`!9888` + +Feature Changes +~~~~~~~~~~~~~~~ + +- Update picohttpparser.{c,h} with upstream repository. ``9428077f481`` + + :gl:`#4485` :gl:`!9857` + +- The configuration clauses parental-agents and primaries are renamed to + remote-servers. ``858ba71eafc`` + + The top blocks 'primaries' and 'parental-agents' are no longer + preferred and should be renamed to 'remote-servers'. The zone + statements 'parental-agents' and 'primaries' are still used, and may + refer to any 'remote-servers' top block. :gl:`#4544` :gl:`!9822` + +- Add TLS SNI extension to all outgoing TLS connections. ``6eb77ed2b07`` + + This change ensures that SNI extension is used in outgoing connections + over TLS (e.g. for DoT and DoH) when applicable. :gl:`#5099` + :gl:`!9923` + +- Detect and possibly define constexpr using Autoconf. ``1fea227ab8b`` + + Previously, we had an ISC_CONSTEXPR macro that was expanded to either + `constexpr` or `static const`, depending on compiler support. To make + the code cleaner, move `constexpr` support detection to Autoconf; if + `constexpr` support is missing from the compiler, define `constexpr` + as `static const` in config.h. :gl:`!9924` + +- Remove unused maxquerycount. ``43622594f48`` + + Related to #4980 :gl:`!9850` + +- Use query counters in validator code. ``63060314098`` + + Commit af7db8951364a89c468eda1535efb3f53adc2c1f as part of #4141 was + supposed to apply the 'max-recursion-queries' quota to validator + queries, but the counter was never actually passed on to + 'dns_resolver_createfetch()'. This has been fixed, and the global + query counter ('max-query-count', per client request) is now also + added. + + Related to #4980 :gl:`!9856` + +Bug Fixes +~~~~~~~~~ + +- Accept resolv.conf with more than 8 search domains. ``eda02dc3424`` + + :gl:`#1259` :gl:`!2446` + +- Fix nsupdate hang when processing a large update. ``fa56e0d8b10`` + + To mitigate DNS flood attacks over a single TCP connection, we + throttle the connection when the other side does not read the data. + Throttling should only occur on server-side sockets, but erroneously + also happened for nsupdate, which acts as a client. When nsupdate + started throttling the connection, it never attempts to read again. + This has been fixed. :gl:`#4910` :gl:`!9709` + +- Lock and attach when returning zone stats. ``3c720c64250`` + + When returning zone statistics counters, the statistics sets are now + attached while the zone is locked. This addresses Coverity warnings + CID 468720, 468728 and 468729. :gl:`#4934` :gl:`!9488` + +- Fix possible assertion failure when reloading server while processing + updates. ``be5266a7c61`` + + :gl:`#5006` :gl:`!9745` + +- Preserve cache across reconfig when using attach-cache. + ``0b287f3aaf9`` + + When the `attach-cache` option is used in the `options` block with an + arbitrary name, it causes all views to use the same cache. Previously, + this configuration caused the cache to be deleted and a new cache + created every time the server was reconfigured. This has been fixed. + :gl:`#5061` :gl:`!9787` + +- Resolve the spurious drops in performance due GLUE cache. + ``e2c1941efd2`` + + For performance reasons, the returned GLUE records are cached on the + first use. The current implementation could randomly cause a + performance drop and increased memory use. This has been fixed. + :gl:`#5064` :gl:`!9831` + +- Fix dnssec-signzone signing non-DNSKEY RRsets with revoked keys. + ``1435770b1a7`` + + `dnssec-signzone` was using revoked keys for signing RRsets other than + DNSKEY. This has been corrected. :gl:`#5070` :gl:`!9800` + +- Disable deterministic ecdsa for fips builds. ``707dded9798`` + + FIPS 186-5 [1] allows the usage deterministic ECDSA (Section 6.3) + which is compabile with RFC 6979 [2] but OpenSSL seems to follow FIPS + 186-4 (Section 6.3) [3] which only allows for random k values, failing + k value generation for OpenSSL >=3.2. [4] + + Fix signing by not using deterministic ECDSA when FIPS mode is active. + + [1]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf [2]: + https://datatracker.ietf.org/doc/html/rfc6979 [3]: + https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf [4]: https: + //github.com/openssl/openssl/blob/85f17585b0d8b55b335f561e2862db14a20b + 1e64/crypto/ec/ecdsa_ossl.c#L201-L207 :gl:`#5072` :gl:`!9808` + +- Revert "Lock and attach when returning zone stats" ``de6f199f4d2`` + + :gl:`#5082` :gl:`!9859` + +- Unknown directive in resolv.conf not handled properly. ``48901ef57e7`` + + The line after an unknown directive in resolv.conf could accidentally + be skipped, potentially affecting dig, host, nslookup, nsupdate, or + delv. This has been fixed. :gl:`#5084` :gl:`!9865` + +- Querying an NSEC3-signed zone for an empty record could trigger an + assertion. ``3a94afa03a1`` + + A bug in the qpzone database could trigger a crash when querying for a + deleted name, or a newly-added empty non-terminal name, in an + NSEC3-signed zone. This has been fixed. :gl:`#5108` :gl:`!9928` + +- Fix response policy zones and catalog zones with an $INCLUDE statement + defined. ``19a2aab136a`` + + Response policy zones (RPZ) and catalog zones were not working + correctly if they had an $INCLUDE statement defined. This has been + fixed. :gl:`#5111` :gl:`!9930` + +- Clean up incorrect logging module names. ``3db39ec7ad5`` + + Some files used logmodule names that had been copied in from + elsewhere; these have now been given module names of their own. Also, + the RBT and RBTDB logmodules have been removed, since they are now + unused. :gl:`!9895` + +- Finalize removal of memory debug flags size and mctx. ``667383587b2`` + + Commit 4b3d0c66009d30f5c0bc12ee128fc59f1d853f44 has removed them, but + did not remove few traces in documentation and help. Remove them from + remaining places. :gl:`!9606` + +- Mark loop as shuttingdown earlier in shutdown_cb. ``d71869d6a78`` + + :gl:`!9827` + +- Use CMM_{STORE,LOAD}_SHARED to store/load glue in gluelist. + ``6ce55429f14`` + + ThreadSanitizer has trouble understanding that gluelist->glue is + constant after it is assigned to the slabheader with cmpxchg. Help + ThreadSanitizer to understand the code by using CMM_STORE_SHARED and + CMM_LOAD_SHARED on gluelist->glue. :gl:`!9929` + + diff --git a/doc/notes/notes-9.21.4.rst b/doc/notes/notes-9.21.4.rst new file mode 100644 index 0000000000..cc62e17575 --- /dev/null +++ b/doc/notes/notes-9.21.4.rst @@ -0,0 +1,195 @@ +.. 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.21.4 +--------------------- + +Security Fixes +~~~~~~~~~~~~~~ + +- DNS-over-HTTPS flooding fixes. :cve:`2024-12705` + + Fix DNS-over-HTTPS implementation issues that arise under heavy + query load. Optimize resource usage for :iscman:`named` instances that + accept queries over DNS-over-HTTPS. + + Previously, :iscman:`named` processed all incoming HTTP/2 data at + once, which could overwhelm the server, especially when dealing with + clients that sent requests but did not wait for responses. That has been + fixed. Now, :iscman:`named` handles HTTP/2 data in smaller chunks and + throttles reading until the remote side reads the response data. It + also throttles clients that send too many requests at once. + + In addition, :iscman:`named` now evaluates excessive streams opened by + clients that include no DNS data, which is considered "flooding." It + logs these clients and drops connections from them. :gl:`#4795` + + In some cases, :iscman:`named` could leave DNS-over-HTTPS + connections in the `CLOSE_WAIT` state indefinitely. That has also been + fixed. :gl:`#5083` + + ISC would like to thank Jean-François Billaud for his assistance with + investigating this issue. + +- Limit additional section processing for large RDATA sets. + :cve:`2024-11187` + + When answering queries, don't add data to the additional section if + the answer has more than 13 names in the RDATA. This limits the number + of lookups into the database(s) during a single client query, reducing + the query-processing load. :gl:`#5034` + + ISC would like to thank Toshifumi Sakaguchi for bringing this + vulnerability to our attention. + +New Features +~~~~~~~~~~~~ + +- Add Extended DNS Error Code 22 - No Reachable Authority. + + When the resolver is trying to query an authoritative server and + eventually times out, a SERVFAIL answer is given to the client. Add + the Extended DNS Error Code 22 - No Reachable Authority to the + response. :gl:`#2268` + +- Add "Zone has [AAAA/A] records but is not served by IPv[6/4]" + warnings. + + Check that zones with AAAA records are served by IPv6 servers and that + zones with A records are served by IPv4 servers. Sometimes, IPv6 + services are accidentally misconfigured and zones with IPv6 (AAAA) + address records are not served by DNS servers with IPv6 addresses, + which means they need to use translation devices to look up those IPv6 + addresses. The reverse is also sometimes true: zones with A records + are not resolvable over IPv4 when they should be. To prevent this, + BIND now looks for these misconfigured zones and issues a warning if + they are found. :gl:`#4370` + +- Add a new option to configure the maximum number of outgoing queries + per client request. + + The configuration option :any:`max-query-count` sets how many outgoing + queries per client request are allowed. The existing + :any:`max-recursion-queries` value is the number of permissible queries for a + single name and is reset on every CNAME redirection. This new option + is a global limit on the client request. The default is 200. + + The default for :any:`max-recursion-queries` is changed from 32 to + 50. This allows :any:`named` to send a few more queries + while looking up a single name. :gl:`#4980` :gl:`#4921` + +- Use the Server Name Indication (SNI) extension for all outgoing TLS + connections. + + This improves compatibility with other DNS server software. + :gl:`#5099` + +Removed Features +~~~~~~~~~~~~~~~~ + +- Remove the ``dnssec-must-be-secure`` feature. :gl:`#4482` + +- Remove ``sortlist`` option. + + The ``sortlist`` option, which was deprecated in BIND 9.20, has now been + removed. :gl:`#4665` + +- Remove support for fixed RRset ordering. + + Remove the ``fixed`` value from the :any:`rrset-order` option and the + ``--enable-fixed-rrset`` option from the ``./configure`` script. + :gl:`#4666` + +- Remove ``trusted-keys`` and ``managed-keys`` options. + + These options have been deprecated in 9.19 in favor of the + :any:`trust-anchors` option and are now being removed. :gl:`#5080` + +Feature Changes +~~~~~~~~~~~~~~~ + +- The configuration clauses ``parental-agents`` and ``primaries`` are renamed to + :any:`remote-servers`. + + The top blocks ``primaries`` and ``parental-agents`` are no longer + preferred and should be renamed to :any:`remote-servers`. The zone + statements :any:`parental-agents` and :any:`primaries` are still used, and may + refer to any :any:`remote-servers` top block. :gl:`#4544` + +Bug Fixes +~~~~~~~~~ + +- Querying an NSEC3-signed zone for an empty record could trigger an + assertion. + + A bug in the qpzone database could trigger a crash when querying for a + deleted name, or a newly added empty non-terminal name, in an + NSEC3-signed zone. This has been fixed. :gl:`#5108` + +- Fix :iscman:`nsupdate` hang when processing a large update. + + To mitigate DNS flood attacks over a single TCP connection, throttle + the connection when the other side does not read the data. Throttling + should only occur on server-side sockets, but erroneously also + happened for :iscman:`nsupdate`, which acts as a client. When + :iscman:`nsupdate` started throttling the connection, it never + attempted to read again. This has been fixed. :gl:`#4910` + +- Fix possible assertion failure when reloading server while processing + update policy rules. :gl:`#5006` + +- Preserve cache across reconfig when using :any:`attach-cache`. + + When the :any:`attach-cache` option is used in the ``options`` block with an + arbitrary name, it causes all views to use the same cache. Previously, + this configuration caused the cache to be deleted and a new cache + to be created every time the server was reconfigured. This has been fixed. + :gl:`#5061` + +- Resolve the spurious drops in performance due to glue cache. + + For performance reasons, the returned glue records are cached on the + first use. The current implementation could randomly cause a + performance drop and increased memory use. This has been fixed. + :gl:`#5064` + +- Fix :iscman:`dnssec-signzone` signing non-DNSKEY RRsets with revoked keys. + + :any:`dnssec-signzone` was using revoked keys for signing RRsets other than + DNSKEY. This has been corrected. :gl:`#5070` + +- Disable deterministic ECDSA for FIPS builds. + + `FIPS 186-5 `_ allows use + of deterministic ECDSA (Section 6.3), which is compatible with + :rfc:`6979`, but OpenSSL seems to follow `FIPS 186-4 + `_ + (Section 6.3), which only allows random ``k`` values. This causes ``k`` + value generation to fail for OpenSSL >= 3.2, making BIND unable to + generate ECDSA signatures when in FIPS mode. + + This signing is now fixed by not using deterministic ECDSA when FIPS mode is active. :gl:`#5072` + +- Fix improper handling of unknown directives in ``resolv.conf``. + + The line after an unknown directive in ``resolv.conf`` could accidentally be + skipped, potentially affecting :iscman:`dig`, :iscman:`host`, + :iscman:`nslookup`, :iscman:`nsupdate`, or :iscman:`delv`. This has been + fixed. :gl:`#5084` + +- Fix response policy zones and catalog zones with an ``$INCLUDE`` statement + defined. + + Response policy zones (RPZ) and catalog zones were not working + correctly if they had an ``$INCLUDE`` statement defined. This has been + fixed. :gl:`#5111` + + diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index ae835b961d..89518b0ebf 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -53,6 +53,8 @@ #include #include +#define DNS_RDATASET_MAXADDITIONAL 13 + /* Fixed RRSet helper macros */ #define DNS_RDATASET_LENGTH 2; @@ -503,7 +505,8 @@ dns_rdataset_towirepartial(dns_rdataset_t *rdataset, isc_result_t dns_rdataset_additionaldata(dns_rdataset_t *rdataset, const dns_name_t *owner_name, - dns_additionaldatafunc_t add, void *arg); + dns_additionaldatafunc_t add, void *arg, + size_t limit); /*%< * For each rdata in rdataset, call 'add' for each name and type in the * rdata which is subject to additional section processing. @@ -522,10 +525,15 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset, *\li If a call to dns_rdata_additionaldata() is not successful, the * result returned will be the result of dns_rdataset_additionaldata(). * + *\li If the 'limit' is non-zero and the number of the rdatasets is larger + * than the 'limit', no additional data will be generated. + * * Returns: * *\li #ISC_R_SUCCESS * + *\li #DNS_R_TOOMANYRECORDS in case rdataset count is larger than 'limit' + * *\li Any error that dns_rdata_additionaldata() can return. */ diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index ae903f24e0..fd674facf6 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -1532,10 +1532,11 @@ find_coveringnsec(qpc_search_t *search, const dns_name_t *name, } static isc_result_t -find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, - dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { +qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, + dns_dbnode_t **nodep, dns_name_t *foundname, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset DNS__DB_FLARG) { qpcnode_t *node = NULL; isc_result_t result; qpc_search_t search; @@ -1977,10 +1978,11 @@ tree_exit: } static isc_result_t -findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, - isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname, - dns_name_t *dcname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { +qpcache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options, + isc_stdtime_t now, dns_dbnode_t **nodep, + dns_name_t *foundname, dns_name_t *dcname, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset DNS__DB_FLARG) { qpcnode_t *node = NULL; isc_rwlock_t *lock = NULL; isc_result_t result; @@ -2152,10 +2154,10 @@ tree_exit: } static isc_result_t -findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now, - dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { +qpcache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatatype_t type, dns_rdatatype_t covers, + isc_stdtime_t now, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset DNS__DB_FLARG) { qpcache_t *qpdb = (qpcache_t *)db; qpcnode_t *qpnode = (qpcnode_t *)node; dns_slabheader_t *header = NULL, *header_next = NULL; @@ -2554,7 +2556,7 @@ free_qpdb(qpcache_t *qpdb, bool log) { } static void -qpdb_destroy(dns_db_t *arg) { +qpcache_destroy(dns_db_t *arg) { qpcache_t *qpdb = (qpcache_t *)arg; bool want_free = false; unsigned int i; @@ -2682,8 +2684,8 @@ new_qpcnode(qpcache_t *qpdb, const dns_name_t *name) { } static isc_result_t -findnode(dns_db_t *db, const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG) { +qpcache_findnode(dns_db_t *db, const dns_name_t *name, bool create, + dns_dbnode_t **nodep DNS__DB_FLARG) { qpcache_t *qpdb = (qpcache_t *)db; qpcnode_t *node = NULL; isc_result_t result; @@ -2718,8 +2720,8 @@ unlock: } static void -attachnode(dns_db_t *db, dns_dbnode_t *source, - dns_dbnode_t **targetp DNS__DB_FLARG) { +qpcache_attachnode(dns_db_t *db, dns_dbnode_t *source, + dns_dbnode_t **targetp DNS__DB_FLARG) { REQUIRE(VALID_QPDB((qpcache_t *)db)); REQUIRE(targetp != NULL && *targetp == NULL); @@ -2733,7 +2735,7 @@ attachnode(dns_db_t *db, dns_dbnode_t *source, } static void -detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { +qpcache_detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { qpcache_t *qpdb = (qpcache_t *)db; qpcnode_t *node = NULL; bool want_free = false; @@ -2788,8 +2790,8 @@ detachnode(dns_db_t *db, dns_dbnode_t **targetp DNS__DB_FLARG) { } static isc_result_t -createiterator(dns_db_t *db, unsigned int options ISC_ATTR_UNUSED, - dns_dbiterator_t **iteratorp) { +qpcache_createiterator(dns_db_t *db, unsigned int options ISC_ATTR_UNUSED, + dns_dbiterator_t **iteratorp) { qpcache_t *qpdb = (qpcache_t *)db; qpc_dbit_t *qpdbiter = NULL; @@ -2811,9 +2813,9 @@ createiterator(dns_db_t *db, unsigned int options ISC_ATTR_UNUSED, } static isc_result_t -allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - unsigned int options, isc_stdtime_t now, - dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { +qpcache_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + unsigned int options, isc_stdtime_t now, + dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { qpcache_t *qpdb = (qpcache_t *)db; qpcnode_t *qpnode = (qpcnode_t *)node; qpc_rditer_t *iterator = NULL; @@ -3388,9 +3390,10 @@ expire_ttl_headers(qpcache_t *qpdb, unsigned int locknum, isc_stdtime_t now, bool cache_is_overmem DNS__DB_FLARG); static isc_result_t -addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options, - dns_rdataset_t *addedrdataset DNS__DB_FLARG) { +qpcache_addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + isc_stdtime_t now, dns_rdataset_t *rdataset, + unsigned int options, + dns_rdataset_t *addedrdataset DNS__DB_FLARG) { qpcache_t *qpdb = (qpcache_t *)db; qpcnode_t *qpnode = (qpcnode_t *)node; isc_region_t region; @@ -3571,8 +3574,9 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } static isc_result_t -deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, - dns_rdatatype_t type, dns_rdatatype_t covers DNS__DB_FLARG) { +qpcache_deleterdataset(dns_db_t *db, dns_dbnode_t *node, + dns_dbversion_t *version, dns_rdatatype_t type, + dns_rdatatype_t covers DNS__DB_FLARG) { qpcache_t *qpdb = (qpcache_t *)db; qpcnode_t *qpnode = (qpcnode_t *)node; isc_result_t result; @@ -4380,17 +4384,17 @@ setmaxtypepername(dns_db_t *db, uint32_t value) { } static dns_dbmethods_t qpdb_cachemethods = { - .destroy = qpdb_destroy, - .findnode = findnode, - .find = find, - .findzonecut = findzonecut, - .attachnode = attachnode, - .detachnode = detachnode, - .createiterator = createiterator, - .findrdataset = findrdataset, - .allrdatasets = allrdatasets, - .addrdataset = addrdataset, - .deleterdataset = deleterdataset, + .destroy = qpcache_destroy, + .findnode = qpcache_findnode, + .find = qpcache_find, + .findzonecut = qpcache_findzonecut, + .attachnode = qpcache_attachnode, + .detachnode = qpcache_detachnode, + .createiterator = qpcache_createiterator, + .findrdataset = qpcache_findrdataset, + .allrdatasets = qpcache_allrdatasets, + .addrdataset = qpcache_addrdataset, + .deleterdataset = qpcache_deleterdataset, .nodecount = nodecount, .getoriginnode = getoriginnode, .getrrsetstats = getrrsetstats, diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c index cf8f55d765..2b75a24a30 100644 --- a/lib/dns/qpzone.c +++ b/lib/dns/qpzone.c @@ -1555,10 +1555,11 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, } static isc_result_t -findrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion, - dns_rdatatype_t type, dns_rdatatype_t covers, - isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { +qpzone_findrdataset(dns_db_t *db, dns_dbnode_t *dbnode, + dns_dbversion_t *dbversion, dns_rdatatype_t type, + dns_rdatatype_t covers, isc_stdtime_t now ISC_ATTR_UNUSED, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset DNS__DB_FLARG) { qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = (qpznode_t *)dbnode; dns_slabheader_t *header = NULL, *header_next = NULL; @@ -2556,8 +2557,8 @@ findnodeintree(qpzonedb_t *qpdb, const dns_name_t *name, bool create, } static isc_result_t -findnode(dns_db_t *db, const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG) { +qpzone_findnode(dns_db_t *db, const dns_name_t *name, bool create, + dns_dbnode_t **nodep DNS__DB_FLARG) { qpzonedb_t *qpdb = (qpzonedb_t *)db; REQUIRE(VALID_QPZONE(qpdb)); @@ -2567,8 +2568,8 @@ findnode(dns_db_t *db, const dns_name_t *name, bool create, } static isc_result_t -findnsec3node(dns_db_t *db, const dns_name_t *name, bool create, - dns_dbnode_t **nodep DNS__DB_FLARG) { +qpzone_findnsec3node(dns_db_t *db, const dns_name_t *name, bool create, + dns_dbnode_t **nodep DNS__DB_FLARG) { qpzonedb_t *qpdb = (qpzonedb_t *)db; REQUIRE(VALID_QPZONE(qpdb)); @@ -2616,9 +2617,9 @@ matchparams(dns_slabheader_t *header, qpz_search_t *search) { } static isc_result_t -setup_delegation(qpz_search_t *search, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { +qpzone_setup_delegation(qpz_search_t *search, dns_dbnode_t **nodep, + dns_name_t *foundname, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset DNS__DB_FLARG) { dns_name_t *zcname = NULL; dns_typepair_t type; qpznode_t *node = NULL; @@ -3212,7 +3213,7 @@ again: } static isc_result_t -check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) { +qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) { qpz_search_t *search = arg; dns_slabheader_t *header = NULL, *header_next = NULL; dns_slabheader_t *dname_header = NULL, *sigdname_header = NULL; @@ -3339,11 +3340,11 @@ check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) { } static isc_result_t -find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, - dns_rdatatype_t type, unsigned int options, - isc_stdtime_t now ISC_ATTR_UNUSED, dns_dbnode_t **nodep, - dns_name_t *foundname, dns_rdataset_t *rdataset, - dns_rdataset_t *sigrdataset DNS__DB_FLARG) { +qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, + dns_rdatatype_t type, unsigned int options, + isc_stdtime_t now ISC_ATTR_UNUSED, dns_dbnode_t **nodep, + dns_name_t *foundname, dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset DNS__DB_FLARG) { isc_result_t result; qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = NULL; @@ -3413,7 +3414,7 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, isc_result_t tresult; dns_qpchain_node(&search.chain, i, NULL, (void **)&n, NULL); - tresult = check_zonecut(n, &search DNS__DB_FLARG_PASS); + tresult = qpzone_check_zonecut(n, &search DNS__DB_FLARG_PASS); if (tresult != DNS_R_CONTINUE) { result = tresult; search.chain.len = i - 1; @@ -3425,7 +3426,7 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, if (result == DNS_R_PARTIALMATCH) { partial_match: if (search.zonecut != NULL) { - result = setup_delegation( + result = qpzone_setup_delegation( &search, nodep, foundname, rdataset, sigrdataset DNS__DB_FLARG_PASS); goto tree_exit; @@ -3715,7 +3716,7 @@ found: * Return the delegation. */ NODE_UNLOCK(lock, &nlocktype); - result = setup_delegation( + result = qpzone_setup_delegation( &search, nodep, foundname, rdataset, sigrdataset DNS__DB_FLARG_PASS); goto tree_exit; @@ -3863,9 +3864,10 @@ tree_exit: } static isc_result_t -allrdatasets(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion, - unsigned int options, isc_stdtime_t now ISC_ATTR_UNUSED, - dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { +qpzone_allrdatasets(dns_db_t *db, dns_dbnode_t *dbnode, + dns_dbversion_t *dbversion, unsigned int options, + isc_stdtime_t now ISC_ATTR_UNUSED, + dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) { qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = (qpznode_t *)dbnode; qpz_version_t *version = (qpz_version_t *)dbversion; @@ -4599,8 +4601,8 @@ dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) { } static isc_result_t -createiterator(dns_db_t *db, unsigned int options, - dns_dbiterator_t **iteratorp) { +qpzone_createiterator(dns_db_t *db, unsigned int options, + dns_dbiterator_t **iteratorp) { qpzonedb_t *qpdb = (qpzonedb_t *)db; qpdb_dbiterator_t *iter = NULL; @@ -4638,9 +4640,11 @@ createiterator(dns_db_t *db, unsigned int options, } static isc_result_t -addrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion, - isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset, - unsigned int options, dns_rdataset_t *addedrdataset DNS__DB_FLARG) { +qpzone_addrdataset(dns_db_t *db, dns_dbnode_t *dbnode, + dns_dbversion_t *dbversion, + isc_stdtime_t now ISC_ATTR_UNUSED, dns_rdataset_t *rdataset, + unsigned int options, + dns_rdataset_t *addedrdataset DNS__DB_FLARG) { isc_result_t result; qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = (qpznode_t *)dbnode; @@ -4767,9 +4771,10 @@ addrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion, } static isc_result_t -subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion, - dns_rdataset_t *rdataset, unsigned int options, - dns_rdataset_t *newrdataset DNS__DB_FLARG) { +qpzone_subtractrdataset(dns_db_t *db, dns_dbnode_t *dbnode, + dns_dbversion_t *dbversion, dns_rdataset_t *rdataset, + unsigned int options, + dns_rdataset_t *newrdataset DNS__DB_FLARG) { qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = (qpznode_t *)dbnode; qpz_version_t *version = (qpz_version_t *)dbversion; @@ -4958,8 +4963,9 @@ unlock: } static isc_result_t -deleterdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion, - dns_rdatatype_t type, dns_rdatatype_t covers DNS__DB_FLARG) { +qpzone_deleterdataset(dns_db_t *db, dns_dbnode_t *dbnode, + dns_dbversion_t *dbversion, dns_rdatatype_t type, + dns_rdatatype_t covers DNS__DB_FLARG) { qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = (qpznode_t *)dbnode; qpz_version_t *version = (qpz_version_t *)dbversion; @@ -5071,9 +5077,10 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, dns_rdataset_init(&rdataset_aaaa); dns_rdataset_init(&sigrdataset_aaaa); - result = find(ctx->db, name, ctx->version, dns_rdatatype_a, - DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_a, name_a, - &rdataset_a, &sigrdataset_a DNS__DB_FLARG_PASS); + result = qpzone_find(ctx->db, name, ctx->version, dns_rdatatype_a, + DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_a, + name_a, &rdataset_a, + &sigrdataset_a DNS__DB_FLARG_PASS); if (result == DNS_R_GLUE) { glue = new_glue(ctx->db->mctx, name_a); @@ -5089,10 +5096,10 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, } } - result = find(ctx->db, name, ctx->version, dns_rdatatype_aaaa, - DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_aaaa, - name_aaaa, &rdataset_aaaa, - &sigrdataset_aaaa DNS__DB_FLARG_PASS); + result = qpzone_find(ctx->db, name, ctx->version, dns_rdatatype_aaaa, + DNS_DBFIND_GLUEOK, 0, (dns_dbnode_t **)&node_aaaa, + name_aaaa, &rdataset_aaaa, + &sigrdataset_aaaa DNS__DB_FLARG_PASS); if (result == DNS_R_GLUE) { if (glue == NULL) { glue = new_glue(ctx->db->mctx, name_aaaa); @@ -5264,7 +5271,7 @@ create_gluelist(qpzonedb_t *qpdb, qpz_version_t *version, qpznode_t *node, */ (void)dns_rdataset_additionaldata(rdataset, dns_rootname, - glue_nsdname_cb, &ctx); + glue_nsdname_cb, &ctx, 0); CMM_STORE_SHARED(gluelist->glue, ctx.glue); @@ -5358,22 +5365,22 @@ static dns_dbmethods_t qpdb_zonemethods = { .newversion = newversion, .attachversion = attachversion, .closeversion = closeversion, - .findnode = findnode, - .find = find, + .findnode = qpzone_findnode, + .find = qpzone_find, .attachnode = attachnode, .detachnode = detachnode, - .createiterator = createiterator, - .findrdataset = findrdataset, - .allrdatasets = allrdatasets, - .addrdataset = addrdataset, - .subtractrdataset = subtractrdataset, - .deleterdataset = deleterdataset, + .createiterator = qpzone_createiterator, + .findrdataset = qpzone_findrdataset, + .allrdatasets = qpzone_allrdatasets, + .addrdataset = qpzone_addrdataset, + .subtractrdataset = qpzone_subtractrdataset, + .deleterdataset = qpzone_deleterdataset, .issecure = issecure, .nodecount = nodecount, .setloop = setloop, .getoriginnode = getoriginnode, .getnsec3parameters = getnsec3parameters, - .findnsec3node = findnsec3node, + .findnsec3node = qpzone_findnsec3node, .setsigningtime = setsigningtime, .getsigningtime = getsigningtime, .getsize = getsize, diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index b09ff3cf29..dc6fc4d867 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -479,7 +479,8 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name, isc_result_t dns_rdataset_additionaldata(dns_rdataset_t *rdataset, const dns_name_t *owner_name, - dns_additionaldatafunc_t add, void *arg) { + dns_additionaldatafunc_t add, void *arg, + size_t limit) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_result_t result; @@ -491,6 +492,10 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset, REQUIRE(DNS_RDATASET_VALID(rdataset)); REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0); + if (limit != 0 && dns_rdataset_count(rdataset) > limit) { + return DNS_R_TOOMANYRECORDS; + } + result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS) { return result; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index d17bacd8d7..61d0da0ae6 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -8578,9 +8578,6 @@ rctx_answer_any(respctx_t *rctx) { rdataset->attributes |= DNS_RDATASETATTR_ANSWER; rdataset->attributes |= DNS_RDATASETATTR_CACHE; rdataset->trust = rctx->trust; - - (void)dns_rdataset_additionaldata(rdataset, rctx->aname, - check_related, rctx); } return ISC_R_SUCCESS; @@ -8628,7 +8625,8 @@ rctx_answer_match(respctx_t *rctx) { rctx->ardataset->attributes |= DNS_RDATASETATTR_CACHE; rctx->ardataset->trust = rctx->trust; (void)dns_rdataset_additionaldata(rctx->ardataset, rctx->aname, - check_related, rctx); + check_related, rctx, + DNS_RDATASET_MAXADDITIONAL); for (sigrdataset = ISC_LIST_HEAD(rctx->aname->list); sigrdataset != NULL; @@ -8835,7 +8833,8 @@ rctx_authority_positive(respctx_t *rctx) { */ (void)dns_rdataset_additionaldata( rdataset, name, check_related, - rctx); + rctx, + DNS_RDATASET_MAXADDITIONAL); done = true; } } @@ -9342,8 +9341,11 @@ rctx_referral(respctx_t *rctx) { */ INSIST(rctx->ns_rdataset != NULL); FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING); + /* + * We want to append **all** the GLUE records here. + */ (void)dns_rdataset_additionaldata(rctx->ns_rdataset, rctx->ns_name, - check_related, rctx); + check_related, rctx, 0); #if CHECK_FOR_GLUE_IN_ANSWER /* * Look in the answer section for "glue" that is incorrectly @@ -9458,7 +9460,8 @@ again: if (CHASE(rdataset)) { rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; (void)dns_rdataset_additionaldata( - rdataset, name, check_related, rctx); + rdataset, name, check_related, rctx, + DNS_RDATASET_MAXADDITIONAL); rescan = true; } } diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 9fd1097f1f..2287035921 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -85,6 +85,37 @@ #define INITIAL_DNS_MESSAGE_BUFFER_SIZE (512) +/* + * The value should be small enough to not allow a server to open too + * many streams at once. It should not be too small either because + * the incoming data will be split into too many chunks with each of + * them processed asynchronously. + */ +#define INCOMING_DATA_CHUNK_SIZE (256) + +/* + * Often processing a chunk does not change the number of streams. In + * that case we can process more than once, but we still should have a + * hard limit on that. + */ +#define INCOMING_DATA_MAX_CHUNKS_AT_ONCE (4) + +/* + * These constants define the grace period to help detect flooding clients. + * + * The first one defines how much data can be processed before opening + * a first stream and received at least some useful (=DNS) data. + * + * The second one defines how much data from a client we read before + * trying to drop a clients who sends not enough useful data. + * + * The third constant defines how many streams we agree to process + * before checking if there was at least one DNS request received. + */ +#define INCOMING_DATA_INITIAL_STREAM_SIZE (1536) +#define INCOMING_DATA_GRACE_SIZE (MAX_ALLOWED_DATA_IN_HEADERS) +#define MAX_STREAMS_BEFORE_FIRST_REQUEST (50) + typedef struct isc_nm_http_response_status { size_t code; size_t content_length; @@ -143,6 +174,7 @@ struct isc_nm_http_session { ISC_LIST(http_cstream_t) cstreams; ISC_LIST(isc_nmsocket_h2_t) sstreams; size_t nsstreams; + uint64_t total_opened_sstreams; isc_nmhandle_t *handle; isc_nmhandle_t *client_httphandle; @@ -155,6 +187,18 @@ struct isc_nm_http_session { isc__nm_http_pending_callbacks_t pending_write_callbacks; isc_buffer_t *pending_write_data; + + /* + * The statistical values below are for usage on server-side + * only. They are meant to detect clients that are taking too many + * resources from the server. + */ + uint64_t received; /* How many requests have been received. */ + uint64_t submitted; /* How many responses were submitted to send */ + uint64_t processed; /* How many responses were processed. */ + + uint64_t processed_incoming_data; + uint64_t processed_useful_data; /* DNS data */ }; typedef enum isc_http_error_responses { @@ -177,6 +221,7 @@ typedef struct isc_http_send_req { void *cbarg; isc_buffer_t *pending_write_data; isc__nm_http_pending_callbacks_t pending_write_callbacks; + uint64_t submitted; } isc_http_send_req_t; #define HTTP_ENDPOINTS_MAGIC ISC_MAGIC('H', 'T', 'E', 'P') @@ -189,10 +234,26 @@ static bool http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle, isc_nm_cb_t cb, void *cbarg); +static void +http_log_flooding_peer(isc_nm_http_session_t *session); + +static bool +http_is_flooding_peer(isc_nm_http_session_t *session); + +static ssize_t +http_process_input_data(isc_nm_http_session_t *session, + isc_buffer_t *input_data); + +static inline bool +http_too_many_active_streams(isc_nm_http_session_t *session); + static void http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle, isc_nm_cb_t send_cb, void *send_cbarg); +static void +http_do_bio_async(isc_nm_http_session_t *session); + static void failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result, isc_nm_http_session_t *session); @@ -499,9 +560,20 @@ finish_http_session(isc_nm_http_session_t *session) { if (!session->closed) { session->closed = true; session->reading = false; + isc_nm_read_stop(session->handle); + isc__nmsocket_timer_stop(session->handle->sock); isc_nmhandle_close(session->handle); } + /* + * Free any unprocessed incoming data in order to not process + * it during indirect calls to http_do_bio() that might happen + * when calling the failed callbacks. + */ + if (session->buf != NULL) { + isc_buffer_free(&session->buf); + } + if (session->client) { client_call_failed_read_cb(ISC_R_UNEXPECTED, session); } else { @@ -575,6 +647,7 @@ on_server_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data, if (new_bufsize <= MAX_DNS_MESSAGE_SIZE && new_bufsize <= h2->content_length) { + session->processed_useful_data += len; isc_buffer_putmem(&h2->rbuf, data, len); break; } @@ -623,6 +696,9 @@ call_unlink_cstream_readcb(http_cstream_t *cstream, isc_buffer_usedregion(cstream->rbuf, &read_data); cstream->read_cb(session->client_httphandle, result, &read_data, cstream->read_cbarg); + if (result == ISC_R_SUCCESS) { + isc__nmsocket_timer_restart(session->handle->sock); + } put_http_cstream(session->mctx, cstream); } @@ -664,6 +740,9 @@ on_server_stream_close_callback(int32_t stream_id, ISC_LIST_UNLINK(session->sstreams, sock->h2, link); session->nsstreams--; + if (sock->h2->request_received) { + session->submitted++; + } /* * By making a call to isc__nmsocket_prep_destroy(), we ensure that @@ -975,6 +1054,181 @@ client_submit_request(isc_nm_http_session_t *session, http_cstream_t *stream) { return ISC_R_SUCCESS; } +static ssize_t +http_process_input_data(isc_nm_http_session_t *session, + isc_buffer_t *input_data) { + ssize_t readlen = 0; + ssize_t processed = 0; + isc_region_t chunk = { 0 }; + size_t before, after; + size_t i; + + REQUIRE(VALID_HTTP2_SESSION(session)); + REQUIRE(input_data != NULL); + + if (!http_session_active(session)) { + return 0; + } + + /* + * For clients that initiate request themselves just process + * everything. + */ + if (session->client) { + isc_buffer_remainingregion(input_data, &chunk); + if (chunk.length == 0) { + return 0; + } + + readlen = nghttp2_session_mem_recv(session->ngsession, + chunk.base, chunk.length); + + if (readlen >= 0) { + isc_buffer_forward(input_data, readlen); + session->processed_incoming_data += readlen; + } + + return readlen; + } + + /* + * If no streams are created during processing, we might process + * more than one chunk at a time. Still we should not overdo that + * to avoid processing too much data at once as such behaviour is + * known for trashing the memory allocator at times. + */ + for (before = after = session->nsstreams, i = 0; + after <= before && i < INCOMING_DATA_MAX_CHUNKS_AT_ONCE; + after = session->nsstreams, i++) + { + const uint64_t active_streams = + (session->received - session->processed); + + /* + * If there are non completed send requests in flight -let's + * not process any incoming data, as it could lead to piling + * up too much send data in send buffers. With many clients + * connected it can lead to excessive memory consumption on + * the server instance. + */ + if (session->sending > 0) { + break; + } + + /* + * If we have reached the maximum number of streams used, we + * might stop processing for now, as nghttp2 will happily + * consume as much data as possible. + */ + if (session->nsstreams >= session->max_concurrent_streams && + active_streams > 0) + { + break; + } + + if (http_too_many_active_streams(session)) { + break; + } + + isc_buffer_remainingregion(input_data, &chunk); + if (chunk.length == 0) { + break; + } + + chunk.length = ISC_MIN(chunk.length, INCOMING_DATA_CHUNK_SIZE); + + readlen = nghttp2_session_mem_recv(session->ngsession, + chunk.base, chunk.length); + + if (readlen >= 0) { + isc_buffer_forward(input_data, readlen); + session->processed_incoming_data += readlen; + processed += readlen; + } else { + isc_buffer_clear(input_data); + return readlen; + } + } + + return processed; +} + +static void +http_log_flooding_peer(isc_nm_http_session_t *session) { + const int log_level = ISC_LOG_DEBUG(1); + if (session->handle != NULL && isc_log_wouldlog(log_level)) { + char client_sabuf[ISC_SOCKADDR_FORMATSIZE]; + char local_sabuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(&session->handle->sock->peer, client_sabuf, + sizeof(client_sabuf)); + isc_sockaddr_format(&session->handle->sock->iface, local_sabuf, + sizeof(local_sabuf)); + isc__nmsocket_log(session->handle->sock, log_level, + "Dropping a flooding HTTP/2 peer " + "%s (on %s) - processed: %" PRIu64 + " bytes, of them useful: %" PRIu64 "", + client_sabuf, local_sabuf, + session->processed_incoming_data, + session->processed_useful_data); + } +} + +static bool +http_is_flooding_peer(isc_nm_http_session_t *session) { + if (session->client) { + return false; + } + + /* + * A flooding client can try to open a lot of streams before + * submitting a request. Let's drop such clients. + */ + if (session->received == 0 && + session->total_opened_sstreams > MAX_STREAMS_BEFORE_FIRST_REQUEST) + { + return true; + } + + /* + * We have processed enough data to open at least one stream and + * get some useful data. + */ + if (session->processed_incoming_data > + INCOMING_DATA_INITIAL_STREAM_SIZE && + (session->total_opened_sstreams == 0 || + session->processed_useful_data == 0)) + { + return true; + } + + if (session->processed_incoming_data < INCOMING_DATA_GRACE_SIZE) { + return false; + } + + /* + * The overhead of DoH per DNS message can be minimum 160-180 + * bytes. We should allow more for extra information that can be + * included in headers, so let's use 256 bytes. Minimum DNS + * message size is 12 bytes. So, (256+12)/12=22. Even that can be + * too restricting for some edge cases, but should be good enough + * for any practical purposes. Not to mention that HTTP/2 may + * include legitimate data that is completely useless for DNS + * purposes... + * + * Anyway, at that point we should have processed enough requests + * for such clients (if any). + */ + if (session->processed_useful_data == 0 || + (session->processed_incoming_data / + session->processed_useful_data) > 22) + { + return true; + } + + return false; +} + /* * Read callback from TLS socket. */ @@ -984,6 +1238,7 @@ http_readcb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, isc_nm_http_session_t *session = (isc_nm_http_session_t *)data; isc_nm_http_session_t *tmpsess = NULL; ssize_t readlen; + isc_buffer_t input; REQUIRE(VALID_HTTP2_SESSION(session)); @@ -1001,11 +1256,17 @@ http_readcb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, goto done; } - readlen = nghttp2_session_mem_recv(session->ngsession, region->base, - region->length); + isc_buffer_init(&input, region->base, region->length); + isc_buffer_add(&input, region->length); + + readlen = http_process_input_data(session, &input); if (readlen < 0) { failed_read_cb(ISC_R_UNEXPECTED, session); goto done; + } else if (http_is_flooding_peer(session)) { + http_log_flooding_peer(session); + failed_read_cb(ISC_R_RANGE, session); + goto done; } if ((size_t)readlen < region->length) { @@ -1017,11 +1278,12 @@ http_readcb(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, isc_buffer_putmem(session->buf, region->base + readlen, unread_size); isc_nm_read_stop(session->handle); + http_do_bio_async(session); + } else { + /* We might have something to receive or send, do IO */ + http_do_bio(session, NULL, NULL, NULL); } - /* We might have something to receive or send, do IO */ - http_do_bio(session, NULL, NULL, NULL); - done: isc__nm_httpsession_detach(&tmpsess); } @@ -1059,14 +1321,18 @@ http_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) { } isc_buffer_free(&req->pending_write_data); + session->processed += req->submitted; isc_mem_put(session->mctx, req, sizeof(*req)); session->sending--; - http_do_bio(session, NULL, NULL, NULL); - isc_nmhandle_detach(&transphandle); - if (result != ISC_R_SUCCESS && session->sending == 0) { + + if (result == ISC_R_SUCCESS) { + http_do_bio(session, NULL, NULL, NULL); + } else { finish_http_session(session); } + isc_nmhandle_detach(&transphandle); + isc__nm_httpsession_detach(&session); } @@ -1227,7 +1493,9 @@ http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle, *send = (isc_http_send_req_t){ .pending_write_data = session->pending_write_data, .cb = cb, - .cbarg = cbarg }; + .cbarg = cbarg, + .submitted = session->submitted }; + session->submitted = 0; session->pending_write_data = NULL; move_pending_send_callbacks(session, send); @@ -1249,6 +1517,28 @@ nothing_to_send: return false; } +static inline bool +http_too_many_active_streams(isc_nm_http_session_t *session) { + const uint64_t active_streams = session->received - session->processed; + const uint64_t max_active_streams = + ISC_MIN(ISC_NETMGR_MAX_STREAM_CLIENTS_PER_CONN, + session->max_concurrent_streams); + + if (session->client) { + return false; + } + + /* + * Do not process incoming data if there are too many active DNS + * clients (streams) per connection. + */ + if (active_streams >= max_active_streams) { + return true; + } + + return false; +} + static void http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle, isc_nm_cb_t send_cb, void *send_cbarg) { @@ -1264,42 +1554,86 @@ http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle, finish_http_session(session); } return; - } else if (nghttp2_session_want_read(session->ngsession) == 0 && - nghttp2_session_want_write(session->ngsession) == 0 && - session->pending_write_data == NULL) - { - session->closing = true; + } + + if (send_cb != NULL) { + INSIST(VALID_NMHANDLE(send_httphandle)); + (void)http_send_outgoing(session, send_httphandle, send_cb, + send_cbarg); + return; + } + + INSIST(send_httphandle == NULL); + INSIST(send_cb == NULL); + INSIST(send_cbarg == NULL); + + if (session->pending_write_data != NULL && session->sending == 0) { + (void)http_send_outgoing(session, NULL, NULL, NULL); return; } if (nghttp2_session_want_read(session->ngsession) != 0) { if (!session->reading) { - /* We have not yet started - * reading from this handle */ + /* We have not yet started reading from this handle */ + isc__nmsocket_timer_start(session->handle->sock); isc_nm_read(session->handle, http_readcb, session); session->reading = true; } else if (session->buf != NULL) { size_t remaining = isc_buffer_remaininglength(session->buf); - /* Leftover data in the - * buffer, use it */ - size_t readlen = nghttp2_session_mem_recv( - session->ngsession, - isc_buffer_current(session->buf), remaining); + /* Leftover data in the buffer, use it */ + size_t remaining_after = 0; + ssize_t readlen = 0; + isc_nm_http_session_t *tmpsess = NULL; - if (readlen == remaining) { + /* + * Let's ensure that HTTP/2 session and its associated + * data will not go "out of scope" too early. + */ + isc__nm_httpsession_attach(session, &tmpsess); + + readlen = http_process_input_data(session, + session->buf); + + remaining_after = + isc_buffer_remaininglength(session->buf); + + if (readlen < 0) { + failed_read_cb(ISC_R_UNEXPECTED, session); + } else if (http_is_flooding_peer(session)) { + http_log_flooding_peer(session); + failed_read_cb(ISC_R_RANGE, session); + } else if ((size_t)readlen == remaining) { isc_buffer_free(&session->buf); + http_do_bio(session, NULL, NULL, NULL); + } else if (remaining_after > 0 && + remaining_after < remaining) + { + /* + * We have processed a part of the data, now + * let's delay processing of whatever is left + * here. We want it to be an async operation so + * that we will: + * + * a) let other things run; + * b) have finer grained control over how much + * data is processed at once, because nghttp2 + * would happily consume as much data we pass to + * it and that could overwhelm the server. + */ + http_do_bio_async(session); } else { - isc_buffer_forward(session->buf, readlen); + (void)http_send_outgoing(session, NULL, NULL, + NULL); } - http_do_bio(session, send_httphandle, send_cb, - send_cbarg); + isc__nm_httpsession_detach(&tmpsess); return; } else { - /* Resume reading, it's - * idempotent, wait for more + /* + * Resume reading, it's idempotent, wait for more */ + isc__nmsocket_timer_start(session->handle->sock); isc_nm_read(session->handle, http_readcb, session); } } else { @@ -1307,20 +1641,54 @@ http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle, isc_nm_read_stop(session->handle); } - if (send_cb != NULL) { - INSIST(VALID_NMHANDLE(send_httphandle)); - (void)http_send_outgoing(session, send_httphandle, send_cb, - send_cbarg); - } else { - INSIST(send_httphandle == NULL); - INSIST(send_cb == NULL); - INSIST(send_cbarg == NULL); - (void)http_send_outgoing(session, NULL, NULL, NULL); + /* we might have some data to send after processing */ + (void)http_send_outgoing(session, NULL, NULL, NULL); + + if (nghttp2_session_want_read(session->ngsession) == 0 && + nghttp2_session_want_write(session->ngsession) == 0 && + session->pending_write_data == NULL) + { + session->closing = true; + isc_nm_read_stop(session->handle); + if (session->sending == 0) { + finish_http_session(session); + } } return; } +static void +http_do_bio_async_cb(void *arg) { + isc_nm_http_session_t *session = arg; + + REQUIRE(VALID_HTTP2_SESSION(session)); + + if (session->handle != NULL && + !isc__nmsocket_closing(session->handle->sock)) + { + http_do_bio(session, NULL, NULL, NULL); + } + + isc__nm_httpsession_detach(&session); +} + +static void +http_do_bio_async(isc_nm_http_session_t *session) { + isc_nm_http_session_t *tmpsess = NULL; + + REQUIRE(VALID_HTTP2_SESSION(session)); + + if (session->handle == NULL || + isc__nmsocket_closing(session->handle->sock)) + { + return; + } + isc__nm_httpsession_attach(session, &tmpsess); + isc_async_run(session->handle->sock->worker->loop, http_do_bio_async_cb, + tmpsess); +} + static isc_result_t get_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) { http_cstream_t *cstream = sock->h2->connect.cstream; @@ -1442,6 +1810,7 @@ transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { } http_transpost_tcp_nodelay(handle); + isc__nmhandle_set_manual_timer(session->handle, true); http_call_connect_cb(http_sock, session, result); @@ -1723,6 +2092,7 @@ server_on_begin_headers_callback(nghttp2_session *ngsession, session->nsstreams++; isc__nm_httpsession_attach(session, &socket->h2->session); ISC_LIST_APPEND(session->sstreams, socket->h2, link); + session->total_opened_sstreams++; nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id, socket); @@ -1782,6 +2152,8 @@ server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value, socket->worker->mctx, dns_value, dns_value_len, &socket->h2->query_data_len); + socket->h2->session->processed_useful_data += + dns_value_len; } else { socket->h2->query_too_large = true; return ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE; @@ -2090,6 +2462,12 @@ server_call_cb(isc_nmsocket_t *socket, const isc_result_t result, handle = isc__nmhandle_get(socket, NULL, NULL); if (result != ISC_R_SUCCESS) { data = NULL; + } else if (socket->h2->session->handle != NULL) { + isc__nmsocket_timer_restart(socket->h2->session->handle->sock); + } + if (result == ISC_R_SUCCESS) { + socket->h2->request_received = true; + socket->h2->session->received++; } socket->h2->cb(handle, result, data, socket->h2->cbarg); isc_nmhandle_detach(&handle); @@ -2106,6 +2484,12 @@ isc__nm_http_bad_request(isc_nmhandle_t *handle) { REQUIRE(!sock->client); REQUIRE(VALID_HTTP2_SESSION(sock->h2->session)); + if (sock->h2->response_submitted || + !http_session_active(sock->h2->session)) + { + return; + } + (void)server_send_error_response(ISC_HTTP_ERROR_BAD_REQUEST, sock->h2->session->ngsession, sock); } @@ -2492,6 +2876,8 @@ httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { isc__nmsocket_attach(httpserver, &session->serversocket); server_send_connection_header(session); + isc__nmhandle_set_manual_timer(session->handle, true); + /* TODO H2 */ http_do_bio(session, NULL, NULL, NULL); return ISC_R_SUCCESS; diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index d790590ec4..e6c6e82830 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -474,6 +474,7 @@ typedef struct isc_nmsocket_h2 { isc_nm_http_endpoints_t *peer_endpoints; + bool request_received; bool response_submitted; struct { char *uri; diff --git a/lib/isc/netmgr/proxystream.c b/lib/isc/netmgr/proxystream.c index 883e8cf942..8d65ea5444 100644 --- a/lib/isc/netmgr/proxystream.c +++ b/lib/isc/netmgr/proxystream.c @@ -121,6 +121,7 @@ proxystream_on_header_data_cb(const isc_result_t result, * the case of TCP it is disabled by default */ proxystream_read_stop(sock); + isc__nmsocket_timer_stop(sock); isc__nmhandle_set_manual_timer(sock->outerhandle, false); sock->proxy.header_processed = true; @@ -775,6 +776,7 @@ isc__nm_proxystream_close(isc_nmsocket_t *sock) { * external references, we can close everything. */ proxystream_read_stop(sock); + isc__nmsocket_timer_stop(sock); if (sock->outerhandle != NULL) { sock->reading = false; isc_nm_read_stop(sock->outerhandle); diff --git a/lib/isc/netmgr/streamdns.c b/lib/isc/netmgr/streamdns.c index 30b3903986..c80b2285c9 100644 --- a/lib/isc/netmgr/streamdns.c +++ b/lib/isc/netmgr/streamdns.c @@ -1009,6 +1009,7 @@ streamdns_close_direct(isc_nmsocket_t *sock) { if (sock->outerhandle != NULL) { sock->streamdns.reading = false; + isc__nmsocket_timer_stop(sock); isc_nm_read_stop(sock->outerhandle); isc_nmhandle_close(sock->outerhandle); isc_nmhandle_detach(&sock->outerhandle); diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index ca3ed8b7f4..4f98b50862 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -759,7 +759,9 @@ isc__nm_tcp_read_stop(isc_nmhandle_t *handle) { isc_nmsocket_t *sock = handle->sock; - isc__nmsocket_timer_stop(sock); + if (!sock->manual_read_timer) { + isc__nmsocket_timer_stop(sock); + } isc__nm_stop_reading(sock); sock->reading = false; diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index c599600f10..8d5fe1fd37 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -465,6 +465,7 @@ tls_try_handshake(isc_nmsocket_t *sock, isc_result_t *presult) { isc__nmsocket_log_tls_session_reuse(sock, sock->tlsstream.tls); tlshandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); + isc__nmsocket_timer_stop(sock); tls_read_stop(sock); if (isc__nm_closing(sock->worker)) { @@ -1154,6 +1155,10 @@ isc__nm_tls_read_stop(isc_nmhandle_t *handle) { handle->sock->reading = false; + if (!handle->sock->manual_read_timer) { + isc__nmsocket_timer_stop(handle->sock); + } + tls_read_stop(handle->sock); } @@ -1174,6 +1179,7 @@ isc__nm_tls_close(isc_nmsocket_t *sock) { */ tls_read_stop(sock); if (sock->outerhandle != NULL) { + isc__nmsocket_timer_stop(sock); isc_nm_read_stop(sock->outerhandle); isc_nmhandle_close(sock->outerhandle); isc_nmhandle_detach(&sock->outerhandle); diff --git a/lib/ns/query.c b/lib/ns/query.c index 486e667e64..72b9a21ebd 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -2141,7 +2141,8 @@ addname: if (trdataset != NULL && dns_rdatatype_followadditional(type)) { if (client->additionaldepth++ < client->view->max_restarts) { eresult = dns_rdataset_additionaldata( - trdataset, fname, query_additional_cb, qctx); + trdataset, fname, query_additional_cb, qctx, + DNS_RDATASET_MAXADDITIONAL); } client->additionaldepth--; } @@ -2238,7 +2239,7 @@ regular: * We don't care if dns_rdataset_additionaldata() fails. */ (void)dns_rdataset_additionaldata(rdataset, name, query_additional_cb, - qctx); + qctx, DNS_RDATASET_MAXADDITIONAL); CTRACE(ISC_LOG_DEBUG(3), "query_additional: done"); }