Projects
Mega:24.09
bind
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 3
View file
_service:tar_scm:bind.spec
Changed
@@ -29,7 +29,7 @@ Name: bind License: MPLv2.0 Version: 9.18.21 -Release: 2 +Release: 3 Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -64,6 +64,11 @@ Patch6001:backport-CVE-2023-5517.patch Patch6002:backport-CVE-2023-5679.patch Patch6003:backport-CVE-2023-50387-CVE-2023-50868.patch +Patch6004:backport-CVE-2024-0760.patch +Patch6005:backport-optimize-the-slabheader-placement-for-certain-RRtypes.patch +Patch6006:backport-CVE-2024-1737.patch +Patch6007:backport-CVE-2024-1975.patch +Patch6008:backport-CVE-2024-4076.patch # Common patches %{?systemd_ordering} @@ -903,6 +908,12 @@ %endif %changelog +* Fri Aug 02 2024 chengyechun<chengyechun1@huawei.com> - 32:9.18.21-3 +- Type:CVE +- CVE:CVE-2024-0760,CVE-2024-1737,CVE-2024-1975,CVE-2024-4076 +- SUG:NA +- DESC:fix CVE-2024-0760,CVE-2024-1737,CVE-2024-1975,CVE-2024-4076 + * Tue Mar 19 2024 chengyechun<chengyechun1@huawei.com> - 32:9.18.21-2 - Type:CVE - CVE:CVE-2023-4408 CVE-2023-5517 CVE-2023-5679 CVE-2023-50387 CVE-2023-50868
View file
_service:tar_scm:backport-CVE-2024-0760.patch
Added
@@ -0,0 +1,981 @@ +From c33b3d26f695d342af3fa81ab404a366bb8ce873 Mon Sep 17 00:00:00 2001 +From: Artem Boldariev <artem@boldariev.com> +Date: Wed, 3 Jul 2024 13:58:32 +0300 +Subject: PATCH TCP/TLS DNS: unthrottle only when all input data processing + +This commit ensures that we restart reading only when all DNS data in +the input buffer is processed so the we will not get into the +situation when the buffer is overrun. + +Conflict:NA +Reference:https://downloads.isc.org/isc/bind9/9.18.28/patches/0001-CVE-2024-0760.patch + +--- + lib/isc/netmgr/netmgr-int.h | 27 +++++-- + lib/isc/netmgr/netmgr.c | 79 ++++++++++++++---- + lib/isc/netmgr/tcp.c | 71 +++++++++++++++- + lib/isc/netmgr/tcpdns.c | 59 +++++++++++++- + lib/isc/netmgr/tlsdns.c | 120 ++++++++++++++++++++------- + lib/ns/client.c | 156 +++++++++++++++++------------------- + lib/ns/include/ns/client.h | 6 +- + 7 files changed, 379 insertions(+), 139 deletions(-) + +diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h +index 6aca9ab..bc1ba73 100644 +--- a/lib/isc/netmgr/netmgr-int.h ++++ b/lib/isc/netmgr/netmgr-int.h +@@ -62,9 +62,10 @@ + #endif + + /* +- * The TCP receive buffer can fit one maximum sized DNS message plus its size, +- * the receive buffer here affects TCP, DoT and DoH. ++ * The TCP send and receive buffers can fit one maximum sized DNS message plus ++ * its size, the receive buffer here affects TCP, DoT and DoH. + */ ++#define ISC_NETMGR_TCP_SENDBUF_SIZE (sizeof(uint16_t) + UINT16_MAX) + #define ISC_NETMGR_TCP_RECVBUF_SIZE (sizeof(uint16_t) + UINT16_MAX) + + /* Pick the larger buffer */ +@@ -377,9 +378,10 @@ struct isc__nm_uvreq { + int magic; + isc_nmsocket_t *sock; + isc_nmhandle_t *handle; +- char tcplen2; /* The TCP DNS message length */ +- uv_buf_t uvbuf; /* translated isc_region_t, to be +- * sent or received */ ++ char tcplen2; /* The TCP DNS message length */ ++ uv_buf_t uvbuf; /* translated isc_region_t, to be ++ * sent or received */ ++ isc_region_t userbuf; + isc_sockaddr_t local; /* local address */ + isc_sockaddr_t peer; /* peer address */ + isc__nm_cb_t cb; /* callback */ +@@ -998,7 +1000,6 @@ struct isc_nmsocket { + TLS_STATE_ERROR, + TLS_STATE_CLOSING + } state; +- isc_region_t senddata; + ISC_LIST(isc__nm_uvreq_t) sendreqs; + bool cycle; + isc_result_t pending_error; +@@ -1063,6 +1064,12 @@ struct isc_nmsocket { + */ + uint64_t write_timeout; + ++ /* ++ * Reading was throttled over TCP as the peer does not read the ++ * data we are sending back. ++ */ ++ bool reading_throttled; ++ + /*% outer socket is for 'wrapped' sockets - e.g. tcpdns in tcp */ + isc_nmsocket_t *outer; + +@@ -2265,6 +2272,14 @@ isc__nmsocket_readtimeout_cb(uv_timer_t *timer); + void + isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult); + ++/*%< ++ * ++ * Maximum number of simultaneous handles in flight supported for a single ++ * connected TCPDNS socket. This value was chosen arbitrarily, and may be ++ * changed in the future. ++ */ ++#define STREAM_CLIENTS_PER_CONN 23 ++ + #define UV_RUNTIME_CHECK(func, ret) \ + if (ret != 0) { \ + FATAL_ERROR("%s failed: %s\n", #func, uv_strerror(ret)); \ +diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c +index 2310b4b..f9e3b70 100644 +--- a/lib/isc/netmgr/netmgr.c ++++ b/lib/isc/netmgr/netmgr.c +@@ -49,8 +49,15 @@ + * How many isc_nmhandles and isc_nm_uvreqs will we be + * caching for reuse in a socket. + */ +-#define ISC_NM_HANDLES_STACK_SIZE 600 +-#define ISC_NM_REQS_STACK_SIZE 600 ++#define ISC_NM_HANDLES_STACK_SIZE 16 ++#define ISC_NM_REQS_STACK_SIZE 16 ++ ++/*% ++ * Same, but for UDP sockets which tend to need larger values as they ++ * process many requests per socket. ++ */ ++#define ISC_NM_HANDLES_STACK_SIZE_UDP 64 ++#define ISC_NM_REQS_STACK_SIZE_UDP 64 + + /*% + * Shortcut index arrays to get access to statistics counters. +@@ -1506,16 +1513,25 @@ void + isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type, + isc_sockaddr_t *iface FLARG) { + uint16_t family; ++ size_t inactive_handles_stack_size = ISC_NM_HANDLES_STACK_SIZE; ++ size_t inactive_reqs_stack_size = ISC_NM_REQS_STACK_SIZE; + + REQUIRE(sock != NULL); + REQUIRE(mgr != NULL); + +- *sock = (isc_nmsocket_t){ .type = type, +- .fd = -1, +- .inactivehandles = isc_astack_new( +- mgr->mctx, ISC_NM_HANDLES_STACK_SIZE), +- .inactivereqs = isc_astack_new( +- mgr->mctx, ISC_NM_REQS_STACK_SIZE) }; ++ if (type == isc_nm_udpsocket) { ++ inactive_handles_stack_size = ISC_NM_HANDLES_STACK_SIZE_UDP; ++ inactive_reqs_stack_size = ISC_NM_REQS_STACK_SIZE_UDP; ++ } ++ ++ *sock = (isc_nmsocket_t){ ++ .type = type, ++ .fd = -1, ++ .inactivehandles = isc_astack_new(mgr->mctx, ++ inactive_handles_stack_size), ++ .inactivereqs = isc_astack_new(mgr->mctx, ++ inactive_reqs_stack_size) ++ }; + + ISC_LIST_INIT(sock->tls.sendreqs); + +@@ -2084,6 +2100,7 @@ isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult) { + + sock = req->sock; + ++ isc__nm_start_reading(sock); + isc__nmsocket_reset(sock); + } + +@@ -2093,7 +2110,6 @@ isc__nmsocket_readtimeout_cb(uv_timer_t *timer) { + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); +- REQUIRE(atomic_load(&sock->reading)); + + if (atomic_load(&sock->client)) { + uv_timer_stop(timer); +@@ -2340,8 +2356,10 @@ processbuffer(isc_nmsocket_t *sock) { + * timers. If we do have a full message, reset the timer. + * + * Stop reading if this is a client socket, or if the server socket +- * has been set to sequential mode. In this case we'll be called again +- * later by isc__nm_resume_processing(). ++ * has been set to sequential mode, or the number of queries we are ++ * processing simultaneously has reached the clients-per-connection ++ * limit. In this case we'll be called again later by ++ * isc__nm_resume_processing(). + */ + isc_result_t + isc__nm_process_sock_buffer(isc_nmsocket_t *sock) { +@@ -2349,14 +2367,41 @@ isc__nm_process_sock_buffer(isc_nmsocket_t *sock) { + int_fast32_t ah = atomic_load(&sock->ah); + isc_result_t result = processbuffer(sock); + switch (result) { +- case ISC_R_NOMORE: ++ case ISC_R_NOMORE: { + /* + * Don't reset the timer until we have a + * full DNS message. + */ +- result = isc__nm_start_reading(sock); +- if (result != ISC_R_SUCCESS) { +- return (result); ++ ++ /* ++ * Restart reading if we have less data in the send ++ * queue than the send buffer size, this means that the ++ * TCP client has started reading some data again. ++ * Starting reading when we go under the limit instead ++ * of waiting for all data has been flushed allows ++ * faster recovery (in case there was a congestion and ++ * now there isn't). ++ */ ++ size_t write_queue_size = ++ uv_stream_get_write_queue_size( ++ &sock->uv_handle.stream); ++ if (write_queue_size < ISC_NETMGR_TCP_SENDBUF_SIZE) { ++ if (sock->reading_throttled) { ++ isc_log_write(isc_lctx, ++ ISC_LOGCATEGORY_GENERAL, ++ ISC_LOGMODULE_NETMGR, ++ ISC_LOG_DEBUG(3), ++ "resuming TCP " ++ "connection, the other " ++ "side is reading the " ++ "data again (%zu)", ++ write_queue_size); ++ sock->reading_throttled = false; ++ } ++ result = isc__nm_start_reading(sock); ++ if (result != ISC_R_SUCCESS) { ++ return (result); ++ } + } + /* + * Start the timer only if there are no externally used +@@ -2368,6 +2413,7 @@ isc__nm_process_sock_buffer(isc_nmsocket_t *sock) { + isc__nmsocket_timer_start(sock); + } + goto done; ++ } + case ISC_R_CANCELED: + isc__nmsocket_timer_stop(sock); + isc__nm_stop_reading(sock); +@@ -2381,7 +2427,8 @@ isc__nm_process_sock_buffer(isc_nmsocket_t *sock) { + isc__nmsocket_timer_stop(sock); + + if (atomic_load(&sock->client) || +- atomic_load(&sock->sequential)) ++ atomic_load(&sock->sequential) || ++ atomic_load(&sock->ah) >= STREAM_CLIENTS_PER_CONN) + { + isc__nm_stop_reading(sock); + goto done; +diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c +index 16b53cc..37d44bd 100644 +--- a/lib/isc/netmgr/tcp.c ++++ b/lib/isc/netmgr/tcp.c +@@ -766,7 +766,7 @@ isc__nm_async_tcpstartread(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_tcpstartread_t *ievent = + (isc__netievent_tcpstartread_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; +- isc_result_t result; ++ isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); +@@ -774,7 +774,7 @@ isc__nm_async_tcpstartread(isc__networker_t *worker, isc__netievent_t *ev0) { + + if (isc__nmsocket_closing(sock)) { + result = ISC_R_CANCELED; +- } else { ++ } else if (!sock->reading_throttled) { + result = isc__nm_start_reading(sock); + } + +@@ -905,6 +905,32 @@ isc__nm_tcp_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { + + /* The readcb could have paused the reading */ + if (atomic_load(&sock->reading)) { ++ if (!sock->client) { ++ /* ++ * Stop reading if we have accumulated enough bytes in ++ * the send queue; this means that the TCP client is not ++ * reading back the data we sending to it, and there's ++ * no reason to continue processing more incoming DNS ++ * messages, if the client is not reading back the ++ * responses. ++ */ ++ size_t write_queue_size = ++ uv_stream_get_write_queue_size( ++ &sock->uv_handle.stream); ++ ++ if (write_queue_size >= ISC_NETMGR_TCP_SENDBUF_SIZE) { ++ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ++ ISC_LOGMODULE_NETMGR, ++ ISC_LOG_DEBUG(3), ++ "throttling TCP connection, " ++ "the other side is " ++ "not reading the data (%zu)", ++ write_queue_size); ++ sock->reading_throttled = true; ++ isc__nm_stop_reading(sock); ++ } ++ } ++ + /* The timer will be updated */ + isc__nmsocket_timer_restart(sock); + } +@@ -1095,6 +1121,34 @@ isc__nm_tcp_send(isc_nmhandle_t *handle, const isc_region_t *region, + return; + } + ++static void ++tcp_maybe_restart_reading(isc_nmsocket_t *sock) { ++ if (!sock->client && sock->reading_throttled && ++ !uv_is_active(&sock->uv_handle.handle)) ++ { ++ /* ++ * Restart reading if we have less data in the send queue than ++ * the send buffer size, this means that the TCP client has ++ * started reading some data again. Starting reading when we go ++ * under the limit instead of waiting for all data has been ++ * flushed allows faster recovery (in case there was a ++ * congestion and now there isn't). ++ */ ++ size_t write_queue_size = ++ uv_stream_get_write_queue_size(&sock->uv_handle.stream); ++ if (write_queue_size < ISC_NETMGR_TCP_SENDBUF_SIZE) { ++ isc_log_write( ++ isc_lctx, ISC_LOGCATEGORY_GENERAL, ++ ISC_LOGMODULE_NETMGR, ISC_LOG_DEBUG(3), ++ "resuming TCP connection, the other side " ++ "is reading the data again (%zu)", ++ write_queue_size); ++ sock->reading_throttled = false; ++ isc__nm_start_reading(sock); ++ } ++ } ++} ++ + static void + tcp_send_cb(uv_write_t *req, int status) { + isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; +@@ -1112,10 +1166,23 @@ tcp_send_cb(uv_write_t *req, int status) { + isc__nm_incstats(sock, STATID_SENDFAIL); + isc__nm_failed_send_cb(sock, uvreq, + isc__nm_uverr2result(status)); ++ ++ if (!sock->client && ++ (atomic_load(&sock->reading) || sock->reading_throttled)) ++ { ++ /* ++ * As we are resuming reading, it is not throttled ++ * anymore (technically). ++ */ ++ sock->reading_throttled = false; ++ isc__nm_start_reading(sock); ++ isc__nmsocket_reset(sock); ++ } + return; + } + + isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false); ++ tcp_maybe_restart_reading(sock); + } + + /* +diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c +index 46958d0..6d417f7 100644 +--- a/lib/isc/netmgr/tcpdns.c ++++ b/lib/isc/netmgr/tcpdns.c +@@ -733,7 +733,7 @@ isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0) { + isc__netievent_tcpdnsread_t *ievent = + (isc__netievent_tcpdnsread_t *)ev0; + isc_nmsocket_t *sock = ievent->sock; +- isc_result_t result; ++ isc_result_t result = ISC_R_SUCCESS; + + UNUSED(worker); + +@@ -742,7 +742,7 @@ isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0) { + + if (isc__nmsocket_closing(sock)) { + result = ISC_R_CANCELED; +- } else { ++ } else if (!sock->reading_throttled) { + result = isc__nm_process_sock_buffer(sock); + } + +@@ -905,6 +905,28 @@ isc__nm_tcpdns_read_cb(uv_stream_t *stream, ssize_t nread, + result = isc__nm_process_sock_buffer(sock); + if (result != ISC_R_SUCCESS) { + isc__nm_failed_read_cb(sock, result, true); ++ } else if (!sock->client) { ++ /* ++ * Stop reading if we have accumulated enough bytes in ++ * the send queue; this means that the TCP client is not ++ * reading back the data we sending to it, and there's ++ * no reason to continue processing more incoming DNS ++ * messages, if the client is not reading back the ++ * responses. ++ */ ++ size_t write_queue_size = ++ uv_stream_get_write_queue_size(&sock->uv_handle.stream); ++ ++ if (write_queue_size >= ISC_NETMGR_TCP_SENDBUF_SIZE) { ++ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ++ ISC_LOGMODULE_NETMGR, ISC_LOG_DEBUG(3), ++ "throttling TCP connection, " ++ "the other side is " ++ "not reading the data (%zu)", ++ write_queue_size); ++ sock->reading_throttled = true; ++ isc__nm_stop_reading(sock); ++ } + } + free: + if (nread < 0) { +@@ -1125,6 +1147,19 @@ isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region, + return; + } + ++static void ++tcpdns_maybe_restart_reading(isc_nmsocket_t *sock) { ++ if (!sock->client && sock->reading_throttled && ++ !uv_is_active(&sock->uv_handle.handle)) ++ { ++ isc_result_t result = isc__nm_process_sock_buffer(sock); ++ if (result != ISC_R_SUCCESS) { ++ atomic_store(&sock->reading, true); ++ isc__nm_failed_read_cb(sock, result, false); ++ } ++ } ++} ++ + static void + tcpdns_send_cb(uv_write_t *req, int status) { + isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data; +@@ -1142,10 +1177,23 @@ tcpdns_send_cb(uv_write_t *req, int status) { + isc__nm_incstats(sock, STATID_SENDFAIL); + isc__nm_failed_send_cb(sock, uvreq, + isc__nm_uverr2result(status)); ++ ++ if (!sock->client && ++ (atomic_load(&sock->reading) || sock->reading_throttled)) ++ { ++ /* ++ * As we are resuming reading, it is not throttled ++ * anymore (technically). ++ */ ++ sock->reading_throttled = false; ++ isc__nm_start_reading(sock); ++ isc__nmsocket_reset(sock); ++ } + return; + } + + isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false); ++ tcpdns_maybe_restart_reading(sock); + } + + /* +@@ -1211,6 +1259,13 @@ isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { + goto fail; + } + ++ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR, ++ ISC_LOG_DEBUG(3), ++ "throttling TCP connection, the other side is not " ++ "reading the data, switching to uv_write()"); ++ sock->reading_throttled = true; ++ isc__nm_stop_reading(sock); ++ + r = uv_write(&uvreq->uv_req.write, &sock->uv_handle.stream, bufs, nbufs, + tcpdns_send_cb); + if (r < 0) { +diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c +index 40e6fc8..f62dfd4 100644 +--- a/lib/isc/netmgr/tlsdns.c ++++ b/lib/isc/netmgr/tlsdns.c +@@ -88,6 +88,9 @@ tlsdns_set_tls_shutdown(isc_tls_t *tls) { + (void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN); + } + ++static void ++tlsdns_maybe_restart_reading(isc_nmsocket_t *sock); ++ + static bool + peer_verification_has_failed(isc_nmsocket_t *sock) { + if (sock->tls.tls != NULL && sock->tls.state == TLS_STATE_HANDSHAKE && +@@ -1076,6 +1079,19 @@ tls_cycle_input(isc_nmsocket_t *sock) { + size_t len; + + for (;;) { ++ /* ++ * There is a similar branch in ++ * isc__nm_process_sock_buffer() which is sufficient to ++ * stop excessive processing in TCP. However, as we wrap ++ * this call in a loop, we need to have it here in order ++ * to limit the number of loop iterations (and, ++ * consequently, the number of messages processed). ++ */ ++ if (atomic_load(&sock->ah) >= STREAM_CLIENTS_PER_CONN) { ++ isc__nm_stop_reading(sock); ++ break; ++ } ++ + (void)SSL_peek(sock->tls.tls, &(char){ '\0' }, 0); + + int pending = SSL_pending(sock->tls.tls); +@@ -1253,17 +1269,17 @@ call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result) { + } + + static void +-free_senddata(isc_nmsocket_t *sock, const isc_result_t result) { ++free_senddata(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, ++ const isc_result_t result) { + REQUIRE(VALID_NMSOCK(sock)); +- REQUIRE(sock->tls.senddata.base != NULL); +- REQUIRE(sock->tls.senddata.length > 0); ++ REQUIRE(req != NULL && req->userbuf.base != NULL && ++ req->userbuf.length > 0); + +- isc_mem_put(sock->mgr->mctx, sock->tls.senddata.base, +- sock->tls.senddata.length); +- sock->tls.senddata.base = NULL; +- sock->tls.senddata.length = 0; ++ isc_mem_put(sock->mgr->mctx, req->userbuf.base, req->userbuf.length); + + call_pending_send_callbacks(sock, result); ++ ++ isc__nm_uvreq_put(&req, sock); + } + + static void +@@ -1276,11 +1292,19 @@ tls_write_cb(uv_write_t *req, int status) { + isc_nm_timer_stop(uvreq->timer); + isc_nm_timer_detach(&uvreq->timer); + +- free_senddata(sock, result); +- +- isc__nm_uvreq_put(&uvreq, sock); ++ free_senddata(sock, uvreq, result); + + if (status != 0) { ++ if (!sock->client && ++ (atomic_load(&sock->reading) || sock->reading_throttled)) ++ { ++ /* ++ * As we are resuming reading, it is not throttled ++ * anymore (technically). ++ */ ++ sock->reading_throttled = false; ++ isc__nm_start_reading(sock); ++ } + tls_error(sock, result); + return; + } +@@ -1290,6 +1314,8 @@ tls_write_cb(uv_write_t *req, int status) { + tls_error(sock, result); + return; + } ++ ++ tlsdns_maybe_restart_reading(sock); + } + + static isc_result_t +@@ -1303,23 +1329,18 @@ tls_cycle_output(isc_nmsocket_t *sock) { + int rv; + int r; + +- if (sock->tls.senddata.base != NULL || +- sock->tls.senddata.length > 0) +- { +- break; +- } +- + if (pending > (int)ISC_NETMGR_TCP_RECVBUF_SIZE) { + pending = (int)ISC_NETMGR_TCP_RECVBUF_SIZE; + } + +- sock->tls.senddata.base = isc_mem_get(sock->mgr->mctx, pending); +- sock->tls.senddata.length = pending; +- + /* It's a bit misnomer here, but it does the right thing */ + req = isc__nm_get_read_req(sock, NULL); +- req->uvbuf.base = (char *)sock->tls.senddata.base; +- req->uvbuf.len = sock->tls.senddata.length; ++ ++ req->userbuf.base = isc_mem_get(sock->mgr->mctx, pending); ++ req->userbuf.length = (size_t)pending; ++ ++ req->uvbuf.base = (char *)req->userbuf.base; ++ req->uvbuf.len = (size_t)req->userbuf.length; + + rv = BIO_read_ex(sock->tls.app_rbio, req->uvbuf.base, + req->uvbuf.len, &bytes); +@@ -1331,32 +1352,36 @@ tls_cycle_output(isc_nmsocket_t *sock) { + + if (r == pending) { + /* Wrote everything, restart */ +- isc__nm_uvreq_put(&req, sock); +- free_senddata(sock, ISC_R_SUCCESS); ++ free_senddata(sock, req, ISC_R_SUCCESS); + continue; + } + + if (r > 0) { + /* Partial write, send rest asynchronously */ +- memmove(req->uvbuf.base, req->uvbuf.base + r, +- req->uvbuf.len - r); +- req->uvbuf.len = req->uvbuf.len - r; ++ req->uvbuf.base += r; ++ req->uvbuf.len -= r; + } else if (r == UV_ENOSYS || r == UV_EAGAIN) { + /* uv_try_write is not supported, send + * asynchronously */ + } else { + result = isc__nm_uverr2result(r); +- isc__nm_uvreq_put(&req, sock); +- free_senddata(sock, result); ++ free_senddata(sock, req, result); + break; + } + ++ isc_log_write( ++ isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR, ++ ISC_LOG_DEBUG(3), ++ "throttling TCP connection, the other side is not " ++ "reading the data, switching to uv_write()"); ++ sock->reading_throttled = true; ++ isc__nm_stop_reading(sock); ++ + r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, + &req->uvbuf, 1, tls_write_cb); + if (r < 0) { + result = isc__nm_uverr2result(r); +- isc__nm_uvreq_put(&req, sock); +- free_senddata(sock, result); ++ free_senddata(sock, req, result); + break; + } + +@@ -1525,6 +1550,28 @@ isc__nm_tlsdns_read_cb(uv_stream_t *stream, ssize_t nread, + result = tls_cycle(sock); + if (result != ISC_R_SUCCESS) { + isc__nm_failed_read_cb(sock, result, true); ++ } else if (!sock->client) { ++ /* ++ * Stop reading if we have accumulated enough bytes in ++ * the send queue; this means that the TCP client is not ++ * reading back the data we sending to it, and there's ++ * no reason to continue processing more incoming DNS ++ * messages, if the client is not reading back the ++ * responses. ++ */ ++ size_t write_queue_size = ++ uv_stream_get_write_queue_size(&sock->uv_handle.stream); ++ ++ if (write_queue_size >= ISC_NETMGR_TCP_SENDBUF_SIZE) { ++ isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ++ ISC_LOGMODULE_NETMGR, ISC_LOG_DEBUG(3), ++ "throttling TCP connection, " ++ "the other side is " ++ "not reading the data (%zu)", ++ write_queue_size); ++ sock->reading_throttled = true; ++ isc__nm_stop_reading(sock); ++ } + } + free: + async_tlsdns_cycle(sock); +@@ -1766,6 +1813,19 @@ isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region, + return; + } + ++static void ++tlsdns_maybe_restart_reading(isc_nmsocket_t *sock) { ++ if (!sock->client && sock->reading_throttled && ++ !uv_is_active(&sock->uv_handle.handle)) ++ { ++ isc_result_t result = isc__nm_process_sock_buffer(sock); ++ if (result != ISC_R_SUCCESS) { ++ atomic_store(&sock->reading, true); ++ isc__nm_failed_read_cb(sock, result, false); ++ } ++ } ++} ++ + /* + * Handle 'tcpsend' async event - send a packet on the socket + */ +diff --git a/lib/ns/client.c b/lib/ns/client.c +index a62343b..8981222 100644 +--- a/lib/ns/client.c ++++ b/lib/ns/client.c +@@ -101,6 +101,9 @@ + #define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */ + #define ECS_SIZE 20U /* 2 + 1 + 1 + 0..16 */ + ++#define TCPBUFFERS_FILLCOUNT 1U ++#define TCPBUFFERS_FREEMAX 8U ++ + #define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0) + #define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0) + #define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0) +@@ -330,12 +333,36 @@ client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), + "send failed: %s", + isc_result_totext(result)); ++ isc_nm_bad_request(handle); + } + } + + isc_nmhandle_detach(&handle); + } + ++static void ++client_setup_tcp_buffer(ns_client_t *client) { ++ REQUIRE(client->tcpbuf == NULL); ++ ++ client->tcpbuf = client->manager->tcp_buffer; ++ client->tcpbuf_size = NS_CLIENT_TCP_BUFFER_SIZE; ++} ++ ++static void ++client_put_tcp_buffer(ns_client_t *client) { ++ if (client->tcpbuf == NULL) { ++ return; ++ } ++ ++ if (client->tcpbuf != client->manager->tcp_buffer) { ++ isc_mem_put(client->manager->mctx, client->tcpbuf, ++ client->tcpbuf_size); ++ } ++ ++ client->tcpbuf = NULL; ++ client->tcpbuf_size = 0; ++} ++ + static void + client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer, + unsigned char **datap) { +@@ -345,12 +372,9 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer, + REQUIRE(datap != NULL); + + if (TCP_CLIENT(client)) { +- INSIST(client->tcpbuf == NULL); +- client->tcpbuf = isc_mem_get(client->manager->send_mctx, +- NS_CLIENT_TCP_BUFFER_SIZE); +- client->tcpbuf_size = NS_CLIENT_TCP_BUFFER_SIZE; ++ client_setup_tcp_buffer(client); + data = client->tcpbuf; +- isc_buffer_init(buffer, data, NS_CLIENT_TCP_BUFFER_SIZE); ++ isc_buffer_init(buffer, data, client->tcpbuf_size); + } else { + data = client->sendbuf; + if ((client->attributes & NS_CLIENTATTR_HAVECOOKIE) == 0) { +@@ -383,11 +407,49 @@ client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) { + + if (isc_buffer_base(buffer) == client->tcpbuf) { + size_t used = isc_buffer_usedlength(buffer); +- client->tcpbuf = isc_mem_reget(client->manager->send_mctx, +- client->tcpbuf, +- client->tcpbuf_size, used); +- client->tcpbuf_size = used; +- r.base = client->tcpbuf; ++ INSIST(client->tcpbuf_size == NS_CLIENT_TCP_BUFFER_SIZE); ++ ++ /* ++ * Copy the data into a smaller buffer before sending, ++ * and keep the original big TCP send buffer for reuse ++ * by other clients. ++ */ ++ if (used > NS_CLIENT_SEND_BUFFER_SIZE) { ++ /* ++ * We can save space by allocating a new buffer with a ++ * correct size and freeing the big buffer. ++ */ ++ unsigned char *new_tcpbuf = ++ isc_mem_get(client->manager->mctx, used); ++ memmove(new_tcpbuf, buffer->base, used); ++ ++ /* ++ * Put the big buffer so we can replace the pointer ++ * and the size with the new ones. ++ */ ++ client_put_tcp_buffer(client); ++ ++ /* ++ * Keep the new buffer's information so it can be freed. ++ */ ++ client->tcpbuf = new_tcpbuf; ++ client->tcpbuf_size = used; ++ ++ r.base = new_tcpbuf; ++ } else { ++ /* ++ * The data fits in the available space in ++ * 'sendbuf', there is no need for a new buffer. ++ */ ++ memmove(client->sendbuf, buffer->base, used); ++ ++ /* ++ * Put the big buffer, we don't need a dynamic buffer. ++ */ ++ client_put_tcp_buffer(client); ++ ++ r.base = client->sendbuf; ++ } + r.length = used; + } else { + isc_buffer_usedregion(buffer, &r); +@@ -461,8 +523,7 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) { + return; + done: + if (client->tcpbuf != NULL) { +- isc_mem_put(client->manager->send_mctx, client->tcpbuf, +- client->tcpbuf_size); ++ client_put_tcp_buffer(client); + } + + ns_client_drop(client, result); +@@ -746,8 +807,7 @@ renderend: + + cleanup: + if (client->tcpbuf != NULL) { +- isc_mem_put(client->manager->send_mctx, client->tcpbuf, +- client->tcpbuf_size); ++ client_put_tcp_buffer(client); + } + + if (cleanup_cctx) { +@@ -1629,8 +1689,7 @@ ns__client_reset_cb(void *client0) { + + ns_client_endrequest(client); + if (client->tcpbuf != NULL) { +- isc_mem_put(client->manager->send_mctx, client->tcpbuf, +- client->tcpbuf_size); ++ client_put_tcp_buffer(client); + } + + if (client->keytag != NULL) { +@@ -1661,8 +1720,6 @@ ns__client_put_cb(void *client0) { + client->magic = 0; + client->shuttingdown = true; + +- isc_mem_put(client->manager->send_mctx, client->sendbuf, +- NS_CLIENT_SEND_BUFFER_SIZE); + if (client->opt != NULL) { + INSIST(dns_rdataset_isassociated(client->opt)); + dns_rdataset_disassociate(client->opt); +@@ -2339,8 +2396,6 @@ ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) { + dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE, + &client->message); + +- client->sendbuf = isc_mem_get(client->manager->send_mctx, +- NS_CLIENT_SEND_BUFFER_SIZE); + /* + * Set magic earlier than usual because ns_query_init() + * and the functions it calls will require it. +@@ -2357,7 +2412,6 @@ ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) { + ns_clientmgr_t *oldmgr = client->manager; + ns_server_t *sctx = client->sctx; + isc_task_t *task = client->task; +- unsigned char *sendbuf = client->sendbuf; + dns_message_t *message = client->message; + isc_mem_t *oldmctx = client->mctx; + ns_query_t query = client->query; +@@ -2372,7 +2426,6 @@ ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) { + .manager = oldmgr, + .sctx = sctx, + .task = task, +- .sendbuf = sendbuf, + .message = message, + .query = query, + .tid = tid }; +@@ -2397,8 +2450,6 @@ ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) { + return (ISC_R_SUCCESS); + + cleanup: +- isc_mem_put(client->manager->send_mctx, client->sendbuf, +- NS_CLIENT_SEND_BUFFER_SIZE); + dns_message_detach(&client->message); + isc_task_detach(&client->task); + ns_clientmgr_detach(&client->manager); +@@ -2461,8 +2512,6 @@ clientmgr_destroy(ns_clientmgr_t *manager) { + isc_task_detach(&manager->task); + ns_server_detach(&manager->sctx); + +- isc_mem_detach(&manager->send_mctx); +- + isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager)); + } + +@@ -2499,61 +2548,6 @@ ns_clientmgr_create(ns_server_t *sctx, isc_taskmgr_t *taskmgr, + + ISC_LIST_INIT(manager->recursing); + +- /* +- * We create specialised per-worker memory context specifically +- * dedicated and tuned for allocating send buffers as it is a very +- * common operation. Not doing so may result in excessive memory +- * use in certain workloads. +- * +- * Please see this thread for more details: +- * +- * https://github.com/jemalloc/jemalloc/issues/2483 +- * +- * In particular, this information from the jemalloc developers is +- * of the most interest: +- * +- * https://github.com/jemalloc/jemalloc/issues/2483#issuecomment-1639019699 +- * https://github.com/jemalloc/jemalloc/issues/2483#issuecomment-1698173849 +- * +- * In essence, we use the following memory management strategy: +- * +- * 1. We use a per-worker memory arena for send buffers memory +- * allocation to reduce lock contention (In reality, we create a +- * per-client manager arena, but we have one client manager per +- * worker). +- * +- * 2. The automatically created arenas settings remain unchanged +- * and may be controlled by users (e.g. by setting the +- * "MALLOC_CONF" variable). +- * +- * 3. We attune the arenas to not use dirty pages cache as the +- * cache would have a poor reuse rate, and that is known to +- * significantly contribute to excessive memory use. +- * +- * 4. There is no strict need for the dirty cache, as there is a +- * per arena bin for each allocation size, so because we initially +- * allocate strictly 64K per send buffer (enough for a DNS +- * message), allocations would get directed to one bin (an "object +- * pool" or a "slab") maintained within an arena. That is, there +- * is an object pool already, specifically to optimise for the +- * case of frequent allocations of objects of the given size. The +- * object pool should suffice our needs, as we will end up +- * recycling the objects from there without the need to back it by +- * an additional layer of dirty pages cache. The dirty pages cache +- * would have worked better in the case when there are more +- * allocation bins involved due to a higher reuse rate (the case +- * of a more "generic" memory management). +- */ +- isc_mem_create_arena(&manager->send_mctx); +- isc_mem_setname(manager->send_mctx, "sendbufs"); +- (void)isc_mem_arena_set_dirty_decay_ms(manager->send_mctx, 0); +- /* +- * Disable muzzy pages cache too, as versions < 5.2.0 have it +- * enabled by default. The muzzy pages cache goes right below the +- * dirty pages cache and backs it. +- */ +- (void)isc_mem_arena_set_muzzy_decay_ms(manager->send_mctx, 0); +- + manager->magic = MANAGER_MAGIC; + + MTRACE("create"); +diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h +index 7a7196f..ea2d83e 100644 +--- a/lib/ns/include/ns/client.h ++++ b/lib/ns/include/ns/client.h +@@ -144,7 +144,6 @@ struct ns_clientmgr { + unsigned int magic; + + isc_mem_t *mctx; +- isc_mem_t *send_mctx; + ns_server_t *sctx; + isc_taskmgr_t *taskmgr; + isc_timermgr_t *timermgr; +@@ -159,6 +158,8 @@ struct ns_clientmgr { + /* Lock covers the recursing list */ + isc_mutex_t reclock; + client_list_t recursing; /*%< Recursing clients */ ++ ++ uint8_t tcp_bufferNS_CLIENT_TCP_BUFFER_SIZE; + }; + + /*% nameserver client structure */ +@@ -187,7 +188,6 @@ struct ns_client { + unsigned char *tcpbuf; + size_t tcpbuf_size; + dns_message_t *message; +- unsigned char *sendbuf; + dns_rdataset_t *opt; + dns_ednsopt_t *ede; + uint16_t udpsize; +@@ -240,6 +240,8 @@ struct ns_client { + * bits will be used as the rcode in the response message. + */ + int32_t rcode_override; ++ ++ uint8_t sendbufNS_CLIENT_SEND_BUFFER_SIZE; + }; + + #define NS_CLIENT_MAGIC ISC_MAGIC('N', 'S', 'C', 'c') +-- +2.33.0 +
View file
_service:tar_scm:backport-CVE-2024-1737.patch
Added
@@ -0,0 +1,1500 @@ +From 39d3e2a8ecc1cb4dccefa3ddea477a2887989485 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org> +Date: Sat, 25 May 2024 11:46:56 +0200 +Subject: PATCH Add a limit to the number of RR types for single name + +Previously, the number of RR types for a single owner name was limited +only by the maximum number of the types (64k). As the data structure +that holds the RR types for the database node is just a linked list, and +there are places where we just walk through the whole list (again and +again), adding a large number of RR types for a single owner named with +would slow down processing of such name (database node). + +Add a configurable limit to cap the number of the RR types for a single +owner. This is enforced at the database (rbtdb, qpzone, qpcache) level +and configured with new max-types-per-name configuration option that +can be configured globally, per-view and per-zone. + +(cherry picked from commit 00d16211d6368b99f070c1182d8c76b3798ca1db) + +Conflict:Adaptation of the dns_db_settask Function Context +Reference:https://downloads.isc.org/isc/bind9/9.18.28/patches/0002-CVE-2024-1737.patch + + +--- + bin/named/config.c | 2 + + bin/named/server.c | 18 ++++ + bin/named/zoneconf.c | 16 +++ + bin/tests/system/doth/ns2/named.conf.in | 1 + + bin/tests/system/doth/ns3/named.conf.in | 1 + + bin/tests/system/doth/ns4/named.conf.in | 1 + + bin/tests/system/doth/ns5/named.conf.in | 1 + + bin/tests/system/dyndb/driver/db.c | 69 ++++++++---- + doc/arm/reference.rst | 30 ++++++ + doc/misc/mirror.zoneopt | 2 + + doc/misc/options | 4 + + doc/misc/primary.zoneopt | 2 + + doc/misc/redirect.zoneopt | 2 + + doc/misc/secondary.zoneopt | 2 + + doc/misc/static-stub.zoneopt | 2 + + doc/misc/stub.zoneopt | 2 + + lib/dns/cache.c | 24 +++++ + lib/dns/db.c | 18 ++++ + lib/dns/dnsrps.c | 2 + + lib/dns/include/dns/cache.h | 12 +++ + lib/dns/include/dns/db.h | 19 ++++ + lib/dns/include/dns/rdataslab.h | 6 +- + lib/dns/include/dns/view.h | 14 +++ + lib/dns/include/dns/zone.h | 39 +++++++ + lib/dns/rbtdb.c | 138 +++++++++++++++++++++--- + lib/dns/rdataslab.c | 14 ++- + lib/dns/sdb.c | 46 +++++--- + lib/dns/sdlz.c | 79 +++++++++----- + lib/dns/view.c | 21 ++++ + lib/dns/xfrin.c | 24 +---- + lib/dns/zone.c | 96 +++++++++++++---- + lib/isccfg/namedconf.c | 6 ++ + lib/ns/update.c | 15 ++- + 33 files changed, 602 insertions(+), 126 deletions(-) + +diff --git a/bin/named/config.c b/bin/named/config.c +index f95e433..af8637e 100644 +--- a/bin/named/config.c ++++ b/bin/named/config.c +@@ -233,8 +233,10 @@ options {\n\ + ixfr-from-differences false;\n\ + max-journal-size default;\n\ + max-records 0;\n\ ++ max-records-per-type 100;\n\ + max-refresh-time 2419200; /* 4 weeks */\n\ + max-retry-time 1209600; /* 2 weeks */\n\ ++ max-types-per-name 100;\n\ + max-transfer-idle-in 60;\n\ + max-transfer-idle-out 60;\n\ + max-transfer-time-in 120;\n\ +diff --git a/bin/named/server.c b/bin/named/server.c +index bfe6df3..8d7f56e 100644 +--- a/bin/named/server.c ++++ b/bin/named/server.c +@@ -5563,6 +5563,24 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, + dns_resolver_setclientsperquery(view->resolver, cfg_obj_asuint32(obj), + max_clients_per_query); + ++ /* ++ * This is used for the cache and also as a default value ++ * for zone databases. ++ */ ++ obj = NULL; ++ result = named_config_get(maps, "max-records-per-type", &obj); ++ INSIST(result == ISC_R_SUCCESS); ++ dns_view_setmaxrrperset(view, cfg_obj_asuint32(obj)); ++ ++ /* ++ * This is used for the cache and also as a default value ++ * for zone databases. ++ */ ++ obj = NULL; ++ result = named_config_get(maps, "max-types-per-name", &obj); ++ INSIST(result == ISC_R_SUCCESS); ++ dns_view_setmaxtypepername(view, cfg_obj_asuint32(obj)); ++ + obj = NULL; + result = named_config_get(maps, "max-recursion-depth", &obj); + INSIST(result == ISC_R_SUCCESS); +diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c +index 44c2242..384a81e 100644 +--- a/bin/named/zoneconf.c ++++ b/bin/named/zoneconf.c +@@ -1083,6 +1083,22 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, + dns_zone_setmaxrecords(zone, 0); + } + ++ obj = NULL; ++ result = named_config_get(maps, "max-records-per-type", &obj); ++ INSIST(result == ISC_R_SUCCESS && obj != NULL); ++ dns_zone_setmaxrrperset(mayberaw, cfg_obj_asuint32(obj)); ++ if (zone != mayberaw) { ++ dns_zone_setmaxrrperset(zone, 0); ++ } ++ ++ obj = NULL; ++ result = named_config_get(maps, "max-types-per-name", &obj); ++ INSIST(result == ISC_R_SUCCESS && obj != NULL); ++ dns_zone_setmaxtypepername(mayberaw, cfg_obj_asuint32(obj)); ++ if (zone != mayberaw) { ++ dns_zone_setmaxtypepername(zone, 0); ++ } ++ + if (raw != NULL && filename != NULL) { + #define SIGNED ".signed" + size_t signedlen = strlen(filename) + sizeof(SIGNED); +diff --git a/bin/tests/system/doth/ns2/named.conf.in b/bin/tests/system/doth/ns2/named.conf.in +index e533f47..f10dac5 100644 +--- a/bin/tests/system/doth/ns2/named.conf.in ++++ b/bin/tests/system/doth/ns2/named.conf.in +@@ -49,6 +49,7 @@ options { + ixfr-from-differences yes; + check-integrity no; + dnssec-validation yes; ++ max-records-per-type 0; + transfers-in 100; + transfers-out 100; + }; +diff --git a/bin/tests/system/doth/ns3/named.conf.in b/bin/tests/system/doth/ns3/named.conf.in +index cd1ab9c..cd9fc63 100644 +--- a/bin/tests/system/doth/ns3/named.conf.in ++++ b/bin/tests/system/doth/ns3/named.conf.in +@@ -44,6 +44,7 @@ options { + ixfr-from-differences yes; + check-integrity no; + dnssec-validation yes; ++ max-records-per-type 0; + }; + + zone "." { +diff --git a/bin/tests/system/doth/ns4/named.conf.in b/bin/tests/system/doth/ns4/named.conf.in +index c7c6c91..43b7c78 100644 +--- a/bin/tests/system/doth/ns4/named.conf.in ++++ b/bin/tests/system/doth/ns4/named.conf.in +@@ -52,6 +52,7 @@ options { + ixfr-from-differences yes; + check-integrity no; + dnssec-validation yes; ++ max-records-per-type 0; + }; + + zone "." { +diff --git a/bin/tests/system/doth/ns5/named.conf.in b/bin/tests/system/doth/ns5/named.conf.in +index 6808618..9323637 100644 +--- a/bin/tests/system/doth/ns5/named.conf.in ++++ b/bin/tests/system/doth/ns5/named.conf.in +@@ -40,6 +40,7 @@ options { + ixfr-from-differences yes; + check-integrity no; + dnssec-validation yes; ++ max-records-per-type 0; + }; + + zone "." { +diff --git a/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c +index 334fd54..d34d1e0 100644 +--- a/bin/tests/system/dyndb/driver/db.c ++++ b/bin/tests/system/dyndb/driver/db.c +@@ -563,28 +563,57 @@ hashsize(dns_db_t *db) { + * determine which implementation of dns_db_*() function to call. + */ + static dns_dbmethods_t sampledb_methods = { +- attach, detach, beginload, +- endload, dump, currentversion, +- newversion, attachversion, closeversion, +- findnode, find, findzonecut, +- attachnode, detachnode, expirenode, +- printnode, createiterator, findrdataset, +- allrdatasets, addrdataset, subtractrdataset, +- deleterdataset, issecure, nodecount, +- ispersistent, overmem, settask, +- getoriginnode, transfernode, getnsec3parameters, +- findnsec3node, setsigningtime, getsigningtime, +- resigned, isdnssec, getrrsetstats, ++ attach, ++ detach, ++ beginload, ++ endload, ++ dump, ++ currentversion, ++ newversion, ++ attachversion, ++ closeversion, ++ findnode, ++ find, ++ findzonecut, ++ attachnode, ++ detachnode, ++ expirenode, ++ printnode, ++ createiterator, ++ findrdataset, ++ allrdatasets, ++ addrdataset, ++ subtractrdataset, ++ deleterdataset, ++ issecure, ++ nodecount, ++ ispersistent, ++ overmem, ++ settask, ++ getoriginnode, ++ transfernode, ++ getnsec3parameters, ++ findnsec3node, ++ setsigningtime, ++ getsigningtime, ++ resigned, ++ isdnssec, ++ getrrsetstats, + NULL, /* rpz_attach */ + NULL, /* rpz_ready */ +- findnodeext, findext, setcachestats, +- hashsize, NULL, /* nodefullname */ +- NULL, /* getsize */ +- NULL, /* setservestalettl */ +- NULL, /* getservestalettl */ +- NULL, /* setservestalerefresh */ +- NULL, /* getservestalerefresh */ +- NULL, /* setgluecachestats */ ++ findnodeext, ++ findext, ++ setcachestats, ++ hashsize, ++ NULL, /* nodefullname */ ++ NULL, /* getsize */ ++ NULL, /* setservestalettl */ ++ NULL, /* getservestalettl */ ++ NULL, /* setservestalerefresh */ ++ NULL, /* getservestalerefresh */ ++ NULL, /* setgluecachestats */ ++ NULL, /* setmaxrrperset */ ++ NULL /* setmaxtypepername */ + }; + + /* Auxiliary driver functions. */ +diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst +index e1b8228..29e246b 100644 +--- a/doc/arm/reference.rst ++++ b/doc/arm/reference.rst +@@ -3766,6 +3766,36 @@ system. + This sets the maximum number of records permitted in a zone. The default is + zero, which means the maximum is unlimited. + ++.. namedconf:statement:: max-records-per-type ++ :tags: server ++ :short: Sets the maximum number of records that can be stored in an RRset ++ ++ This sets the maximum number of resource records that can be stored ++ in an RRset in a database. When configured in :namedconf:ref:`options` ++ or :namedconf:ref:`view`, it controls the cache database; it also sets ++ the default value for zone databases, which can be overridden by setting ++ it at the :namedconf:ref:`zone` level. ++ ++ If set to a positive value, any attempt to cache or to add to a zone ++ an RRset with more than the specified number of records will result in ++ a failure. If set to 0, there is no cap on RRset size. The default is ++ 100. ++ ++.. namedconf:statement:: max-types-per-name ++ :tags: server ++ :short: Sets the maximum number of RR types that can be stored for an owner name ++ ++ This sets the maximum number of resource record types that can be stored ++ for a single owner name in a database. When configured in :namedconf:ref:`options` ++ or :namedconf:ref:`view`, it controls the cache database, and also sets ++ the default value for zone databases, which can be overridden by setting ++ it at the :namedconf:ref:`zone` level ++ ++ If set to a positive value, any attempt to cache or to add to a zone an owner ++ name with more than the specified number of resource record types will result ++ in a failure. If set to 0, there is no cap on RR types number. The default is ++ 100. ++ + .. namedconf:statement:: recursive-clients + :tags: query + :short: Specifies the maximum number of concurrent recursive queries the server can perform. +diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt +index ac371cd..5f688ca 100644 +--- a/doc/misc/mirror.zoneopt ++++ b/doc/misc/mirror.zoneopt +@@ -18,12 +18,14 @@ zone <string> <class> { + max-ixfr-ratio ( unlimited | <percentage> ); + max-journal-size ( default | unlimited | <sizeval> ); + max-records <integer>; ++ max-records-per-type <integer>; + max-refresh-time <integer>; + max-retry-time <integer>; + max-transfer-idle-in <integer>; + max-transfer-idle-out <integer>; + max-transfer-time-in <integer>; + max-transfer-time-out <integer>; ++ max-types-per-name <integer>; + min-refresh-time <integer>; + min-retry-time <integer>; + multi-master <boolean>; +diff --git a/doc/misc/options b/doc/misc/options +index a916701..5fe415d 100644 +--- a/doc/misc/options ++++ b/doc/misc/options +@@ -180,6 +180,7 @@ options { + max-journal-size ( default | unlimited | <sizeval> ); + max-ncache-ttl <duration>; + max-records <integer>; ++ max-records-per-type <integer>; + max-recursion-depth <integer>; + max-recursion-queries <integer>; + max-refresh-time <integer>; +@@ -190,6 +191,7 @@ options { + max-transfer-idle-out <integer>; + max-transfer-time-in <integer>; + max-transfer-time-out <integer>; ++ max-types-per-name <integer>; + max-udp-size <integer>; + max-zone-ttl ( unlimited | <duration> ); + memstatistics <boolean>; +@@ -470,6 +472,7 @@ view <string> <class> { + max-journal-size ( default | unlimited | <sizeval> ); + max-ncache-ttl <duration>; + max-records <integer>; ++ max-records-per-type <integer>; + max-recursion-depth <integer>; + max-recursion-queries <integer>; + max-refresh-time <integer>; +@@ -479,6 +482,7 @@ view <string> <class> { + max-transfer-idle-out <integer>; + max-transfer-time-in <integer>; + max-transfer-time-out <integer>; ++ max-types-per-name <integer>; + max-udp-size <integer>; + max-zone-ttl ( unlimited | <duration> ); + message-compression <boolean>; +diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt +index 8f646e3..1de2f21 100644 +--- a/doc/misc/primary.zoneopt ++++ b/doc/misc/primary.zoneopt +@@ -38,8 +38,10 @@ zone <string> <class> { + max-ixfr-ratio ( unlimited | <percentage> ); + max-journal-size ( default | unlimited | <sizeval> ); + max-records <integer>; ++ max-records-per-type <integer>; + max-transfer-idle-out <integer>; + max-transfer-time-out <integer>; ++ max-types-per-name <integer>; + max-zone-ttl ( unlimited | <duration> ); + notify ( explicit | master-only | primary-only | <boolean> ); + notify-delay <integer>; +diff --git a/doc/misc/redirect.zoneopt b/doc/misc/redirect.zoneopt +index bcd9a57..9d238c1 100644 +--- a/doc/misc/redirect.zoneopt ++++ b/doc/misc/redirect.zoneopt +@@ -7,6 +7,8 @@ zone <string> <class> { + masterfile-format ( raw | text ); + masterfile-style ( full | relative ); + max-records <integer>; ++ max-records-per-type <integer>; ++ max-types-per-name <integer>; + max-zone-ttl ( unlimited | <duration> ); + primaries port <integer> { ( <remote-servers> | <ipv4_address> port <integer> | <ipv6_address> port <integer> ) key <string> tls <string> ; ... }; + zone-statistics ( full | terse | none | <boolean> ); +diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt +index 3237aab..169fa9b 100644 +--- a/doc/misc/secondary.zoneopt ++++ b/doc/misc/secondary.zoneopt +@@ -30,12 +30,14 @@ zone <string> <class> { + max-ixfr-ratio ( unlimited | <percentage> ); + max-journal-size ( default | unlimited | <sizeval> ); + max-records <integer>; ++ max-records-per-type <integer>; + max-refresh-time <integer>; + max-retry-time <integer>; + max-transfer-idle-in <integer>; + max-transfer-idle-out <integer>; + max-transfer-time-in <integer>; + max-transfer-time-out <integer>; ++ max-types-per-name <integer>; + min-refresh-time <integer>; + min-retry-time <integer>; + multi-master <boolean>; +diff --git a/doc/misc/static-stub.zoneopt b/doc/misc/static-stub.zoneopt +index 5357528..93a3220 100644 +--- a/doc/misc/static-stub.zoneopt ++++ b/doc/misc/static-stub.zoneopt +@@ -5,6 +5,8 @@ zone <string> <class> { + forward ( first | only ); + forwarders port <integer> { ( <ipv4_address> | <ipv6_address> ) port <integer> ; ... }; + max-records <integer>; ++ max-records-per-type <integer>; ++ max-types-per-name <integer>; + server-addresses { ( <ipv4_address> | <ipv6_address> ); ... }; + server-names { <string>; ... }; + zone-statistics ( full | terse | none | <boolean> ); +diff --git a/doc/misc/stub.zoneopt b/doc/misc/stub.zoneopt +index 29c1d56..2834682 100644 +--- a/doc/misc/stub.zoneopt ++++ b/doc/misc/stub.zoneopt +@@ -12,10 +12,12 @@ zone <string> <class> { + masterfile-format ( raw | text ); + masterfile-style ( full | relative ); + max-records <integer>; ++ max-records-per-type <integer>; + max-refresh-time <integer>; + max-retry-time <integer>; + max-transfer-idle-in <integer>; + max-transfer-time-in <integer>; ++ max-types-per-name <integer>; + min-refresh-time <integer>; + min-retry-time <integer>; + multi-master <boolean>; +diff --git a/lib/dns/cache.c b/lib/dns/cache.c +index 7ffb6f8..782cf2b 100644 +--- a/lib/dns/cache.c ++++ b/lib/dns/cache.c +@@ -144,6 +144,8 @@ struct dns_cache { + dns_ttl_t serve_stale_ttl; + dns_ttl_t serve_stale_refresh; + isc_stats_t *stats; ++ uint32_t maxrrperset; ++ uint32_t maxtypepername; + }; + + /*** +@@ -175,6 +177,8 @@ cache_create_db(dns_cache_t *cache, dns_db_t **db) { + if (result == ISC_R_SUCCESS) { + dns_db_setservestalettl(*db, cache->serve_stale_ttl); + dns_db_setservestalerefresh(*db, cache->serve_stale_refresh); ++ dns_db_setmaxrrperset(*db, cache->maxrrperset); ++ dns_db_setmaxtypepername(*db, cache->maxtypepername); + } + return (result); + } +@@ -1194,6 +1198,26 @@ dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) { + } + } + ++void ++dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) { ++ REQUIRE(VALID_CACHE(cache)); ++ ++ cache->maxrrperset = value; ++ if (cache->db != NULL) { ++ dns_db_setmaxrrperset(cache->db, value); ++ } ++} ++ ++void ++dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value) { ++ REQUIRE(VALID_CACHE(cache)); ++ ++ cache->maxtypepername = value; ++ if (cache->db != NULL) { ++ dns_db_setmaxtypepername(cache->db, value); ++ } ++} ++ + /* + * XXX: Much of the following code has been copied in from statschannel.c. + * We should refactor this into a generic function in stats.c that can be +diff --git a/lib/dns/db.c b/lib/dns/db.c +index c95d19a..85f6daa 100644 +--- a/lib/dns/db.c ++++ b/lib/dns/db.c +@@ -1119,3 +1119,21 @@ dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats) { + + return (ISC_R_NOTIMPLEMENTED); + } ++ ++void ++dns_db_setmaxrrperset(dns_db_t *db, uint32_t value) { ++ REQUIRE(DNS_DB_VALID(db)); ++ ++ if (db->methods->setmaxrrperset != NULL) { ++ (db->methods->setmaxrrperset)(db, value); ++ } ++} ++ ++void ++dns_db_setmaxtypepername(dns_db_t *db, uint32_t value) { ++ REQUIRE(DNS_DB_VALID(db)); ++ ++ if (db->methods->setmaxtypepername != NULL) { ++ (db->methods->setmaxtypepername)(db, value); ++ } ++} +diff --git a/lib/dns/dnsrps.c b/lib/dns/dnsrps.c +index d4a1c65..73f11da 100644 +--- a/lib/dns/dnsrps.c ++++ b/lib/dns/dnsrps.c +@@ -975,6 +975,8 @@ static dns_dbmethods_t rpsdb_db_methods = { + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ + NULL, /* setgluecachestats */ ++ NULL, /* setmaxrrperset */ ++ NULL /* setmaxtypepername */ + }; + + static dns_rdatasetmethods_t rpsdb_rdataset_methods = { +diff --git a/lib/dns/include/dns/cache.h b/lib/dns/include/dns/cache.h +index 8fc9657..91e94c0 100644 +--- a/lib/dns/include/dns/cache.h ++++ b/lib/dns/include/dns/cache.h +@@ -283,6 +283,18 @@ dns_cache_updatestats(dns_cache_t *cache, isc_result_t result); + * Update cache statistics based on result code in 'result' + */ + ++void ++dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value); ++/*%< ++ * Set the maximum resource records per RRSet that can be cached. ++ */ ++ ++void ++dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value); ++/*%< ++ * Set the maximum resource record types per owner name that can be cached. ++ */ ++ + #ifdef HAVE_LIBXML2 + int + dns_cache_renderxml(dns_cache_t *cache, void *writer0); +diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h +index 9b53f04..b6e826b 100644 +--- a/lib/dns/include/dns/db.h ++++ b/lib/dns/include/dns/db.h +@@ -185,6 +185,8 @@ typedef struct dns_dbmethods { + isc_result_t (*setservestalerefresh)(dns_db_t *db, uint32_t interval); + isc_result_t (*getservestalerefresh)(dns_db_t *db, uint32_t *interval); + isc_result_t (*setgluecachestats)(dns_db_t *db, isc_stats_t *stats); ++ void (*setmaxrrperset)(dns_db_t *db, uint32_t value); ++ void (*setmaxtypepername)(dns_db_t *db, uint32_t value); + } dns_dbmethods_t; + + typedef isc_result_t (*dns_dbcreatefunc_t)(isc_mem_t *mctx, +@@ -1756,4 +1758,21 @@ dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats); + * dns_rdatasetstats_create(); otherwise NULL. + */ + ++void ++dns_db_setmaxrrperset(dns_db_t *db, uint32_t value); ++/*%< ++ * Set the maximum permissible number of RRs per RRset. If 'value' ++ * is nonzero, then any subsequent attempt to add an rdataset with ++ * more than 'value' RRs will return ISC_R_NOSPACE. ++ */ ++ ++void ++dns_db_setmaxtypepername(dns_db_t *db, uint32_t value); ++/*%< ++ * Set the maximum permissible number of RR types per owner name. ++ * ++ * If 'value' is nonzero, then any subsequent attempt to add an rdataset with a ++ * RR type that would exceed the number of already stored RR types will return ++ * ISC_R_NOSPACE. ++ */ + ISC_LANG_ENDDECLS +diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h +index 7364b8d..5729c00 100644 +--- a/lib/dns/include/dns/rdataslab.h ++++ b/lib/dns/include/dns/rdataslab.h +@@ -66,7 +66,8 @@ ISC_LANG_BEGINDECLS + + isc_result_t + dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, +- isc_region_t *region, unsigned int reservelen); ++ isc_region_t *region, unsigned int reservelen, ++ uint32_t limit); + /*%< + * Slabify a rdataset. The slab area will be allocated and returned + * in 'region'. +@@ -122,7 +123,8 @@ isc_result_t + dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, + unsigned int reservelen, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_rdatatype_t type, +- unsigned int flags, unsigned char **tslabp); ++ unsigned int flags, uint32_t maxrrperset, ++ unsigned char **tslabp); + /*%< + * Merge 'oslab' and 'nslab'. + */ +diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h +index 18b0b33..516c209 100644 +--- a/lib/dns/include/dns/view.h ++++ b/lib/dns/include/dns/view.h +@@ -191,6 +191,8 @@ struct dns_view { + dns_dlzdblist_t dlz_unsearched; + uint32_t fail_ttl; + dns_badcache_t *failcache; ++ uint32_t maxrrperset; ++ uint32_t maxtypepername; + + /* + * Configurable data for server use only, +@@ -1413,4 +1415,16 @@ dns_view_sfd_find(dns_view_t *view, const dns_name_t *name, + *\li 'foundname' to be valid with a buffer sufficient to hold the name. + */ + ++void ++dns_view_setmaxrrperset(dns_view_t *view, uint32_t value); ++/*%< ++ * Set the maximum resource records per RRSet that can be cached. ++ */ ++ ++void ++dns_view_setmaxtypepername(dns_view_t *view, uint32_t value); ++/*%< ++ * Set the maximum resource record types per owner name that can be cached. ++ */ ++ + ISC_LANG_ENDDECLS +diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h +index 10ed86c..3449065 100644 +--- a/lib/dns/include/dns/zone.h ++++ b/lib/dns/include/dns/zone.h +@@ -165,6 +165,19 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx); + *\li #ISC_R_UNEXPECTED + */ + ++isc_result_t ++dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp); ++/*%< ++ * Creates a new empty database for the 'zone'. ++ * ++ * Requires: ++ *\li 'zone' to be a valid zone. ++ *\li 'dbp' to point to NULL pointer. ++ * ++ * Returns: ++ *\li dns_db_create() error codes. ++ */ ++ + void + dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass); + /*%< +@@ -350,6 +363,32 @@ dns_zone_getmaxrecords(dns_zone_t *zone); + *\li uint32_t maxrecords. + */ + ++void ++dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t maxrrperset); ++/*%< ++ * Sets the maximum number of records per rrset permitted in a zone. ++ * 0 implies unlimited. ++ * ++ * Requires: ++ *\li 'zone' to be valid initialised zone. ++ * ++ * Returns: ++ *\li void ++ */ ++ ++void ++dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t maxtypepername); ++/*%< ++ * Sets the maximum number of resource record types per owner name ++ * permitted in a zone. 0 implies unlimited. ++ * ++ * Requires: ++ *\li 'zone' to be valid initialised zone. ++ * ++ * Returns: ++ *\li void ++ */ ++ + void + dns_zone_setmaxttl(dns_zone_t *zone, uint32_t maxttl); + /*%< +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index bc0f8d8..c22e021 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -450,6 +450,8 @@ struct dns_rbtdb { + rbtdb_serial_t current_serial; + rbtdb_serial_t least_serial; + rbtdb_serial_t next_serial; ++ uint32_t maxrrperset; ++ uint32_t maxtypepername; + rbtdb_version_t *current_version; + rbtdb_version_t *future_version; + rbtdb_versionlist_t open_versions; +@@ -913,6 +915,8 @@ prio_type(rbtdb_rdatatype_t type) { + case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_soa): + case dns_rdatatype_a: + case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_a): ++ case dns_rdatatype_mx: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_mx): + case dns_rdatatype_aaaa: + case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_aaaa): + case dns_rdatatype_nsec: +@@ -925,6 +929,22 @@ prio_type(rbtdb_rdatatype_t type) { + case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ds): + case dns_rdatatype_cname: + case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname): ++ case dns_rdatatype_dname: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname): ++ case dns_rdatatype_svcb: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_svcb): ++ case dns_rdatatype_https: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_https): ++ case dns_rdatatype_dnskey: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dnskey): ++ case dns_rdatatype_srv: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_srv): ++ case dns_rdatatype_txt: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_txt): ++ case dns_rdatatype_ptr: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ptr): ++ case dns_rdatatype_naptr: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_naptr): + return (true); + } + return (false); +@@ -6180,6 +6200,24 @@ update_recordsandxfrsize(bool add, rbtdb_version_t *rbtversion, + RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write); + } + ++static bool ++overmaxtype(dns_rbtdb_t *rbtdb, uint32_t ntypes) { ++ if (rbtdb->maxtypepername == 0) { ++ return (false); ++ } ++ ++ return (ntypes >= rbtdb->maxtypepername); ++} ++ ++static bool ++prio_header(rdatasetheader_t *header) { ++ if (NEGATIVE(header) && prio_type(RBTDB_RDATATYPE_EXT(header->type))) { ++ return (true); ++ } ++ ++ return (prio_type(header->type)); ++} ++ + /* + * write lock on rbtnode must be held. + */ +@@ -6191,7 +6229,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + rbtdb_changed_t *changed = NULL; + rdatasetheader_t *topheader = NULL, *topheader_prev = NULL; + rdatasetheader_t *header = NULL, *sigheader = NULL; +- rdatasetheader_t *prioheader = NULL; ++ rdatasetheader_t *prioheader = NULL, *expireheader = NULL; + unsigned char *merged = NULL; + isc_result_t result; + bool header_nx; +@@ -6201,6 +6239,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + rbtdb_rdatatype_t negtype, sigtype; + dns_trust_t trust; + int idx; ++ uint32_t ntypes = 0; + + /* + * Add an rdatasetheader_t to a node. +@@ -6276,6 +6315,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + { + if (topheader->type == sigtype) { + sigheader = topheader; ++ break; + } + } + negtype = RBTDB_RDATATYPE_VALUE(covers, 0); +@@ -6338,7 +6378,13 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + for (topheader = rbtnode->data; topheader != NULL; + topheader = topheader->next) + { +- if (prio_type(topheader->type)) { ++ if (IS_CACHE(rbtdb) && ACTIVE(topheader, now)) { ++ ++ntypes; ++ expireheader = topheader; ++ } else if (!IS_CACHE(rbtdb)) { ++ ++ntypes; ++ } ++ if (prio_header(topheader)) { + prioheader = topheader; + } + if (topheader->type == newheader->type || +@@ -6428,7 +6474,7 @@ find_header: + rbtdb->common.mctx, + rbtdb->common.rdclass, + (dns_rdatatype_t)header->type, flags, +- &merged); ++ rbtdb->maxrrperset, &merged); + } + if (result == ISC_R_SUCCESS) { + /* +@@ -6707,9 +6753,15 @@ find_header: + /* + * No rdatasets of the given type exist at the node. + */ ++ if (!IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) { ++ free_rdataset(rbtdb, rbtdb->common.mctx, ++ newheader); ++ return (DNS_R_TOOMANYRECORDS); ++ } ++ + newheader->down = NULL; + +- if (prio_type(newheader->type)) { ++ if (prio_header(newheader)) { + /* This is a priority type, prepend it */ + newheader->next = rbtnode->data; + rbtnode->data = newheader; +@@ -6722,6 +6774,31 @@ find_header: + newheader->next = rbtnode->data; + rbtnode->data = newheader; + } ++ ++ if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) { ++ if (expireheader == NULL) { ++ expireheader = newheader; ++ } ++ if (NEGATIVE(newheader) && ++ !prio_header(newheader)) ++ { ++ /* ++ * Add the new non-priority negative ++ * header to the database only ++ * temporarily. ++ */ ++ expireheader = newheader; ++ } ++ ++ set_ttl(rbtdb, expireheader, 0); ++ mark_header_ancient(rbtdb, expireheader); ++ /* ++ * FIXME: In theory, we should mark the RRSIG ++ * and the header at the same time, but there is ++ * no direct link between those two header, so ++ * we would have to check the whole list again. ++ */ ++ } + } + } + +@@ -6767,7 +6844,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, + + static isc_result_t + addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, +- dns_rdataset_t *rdataset) { ++ uint32_t maxrrperset, dns_rdataset_t *rdataset) { + struct noqname *noqname; + isc_mem_t *mctx = rbtdb->common.mctx; + dns_name_t name; +@@ -6788,12 +6865,12 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, + noqname->negsig = NULL; + noqname->type = neg.type; + dns_name_dup(&name, mctx, &noqname->name); +- result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0); ++ result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0, maxrrperset); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + noqname->neg = r.base; +- result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0); ++ result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0, maxrrperset); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } +@@ -6812,7 +6889,7 @@ cleanup: + + static isc_result_t + addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, +- dns_rdataset_t *rdataset) { ++ uint32_t maxrrperset, dns_rdataset_t *rdataset) { + struct noqname *closest; + isc_mem_t *mctx = rbtdb->common.mctx; + dns_name_t name; +@@ -6833,12 +6910,12 @@ addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, + closest->negsig = NULL; + closest->type = neg.type; + dns_name_dup(&name, mctx, &closest->name); +- result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0); ++ result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0, maxrrperset); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + closest->neg = r.base; +- result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0); ++ result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0, maxrrperset); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } +@@ -6916,7 +6993,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + } + + result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, +- ®ion, sizeof(rdatasetheader_t)); ++ ®ion, sizeof(rdatasetheader_t), ++ rbtdb->maxrrperset); + if (result != ISC_R_SUCCESS) { + return (result); + } +@@ -6974,7 +7052,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + RDATASET_ATTR_SET(newheader, RDATASET_ATTR_OPTOUT); + } + if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) { +- result = addnoqname(rbtdb, newheader, rdataset); ++ result = addnoqname(rbtdb, newheader, ++ rbtdb->maxrrperset, rdataset); + if (result != ISC_R_SUCCESS) { + free_rdataset(rbtdb, rbtdb->common.mctx, + newheader); +@@ -6982,7 +7061,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + } + } + if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0) { +- result = addclosest(rbtdb, newheader, rdataset); ++ result = addclosest(rbtdb, newheader, ++ rbtdb->maxrrperset, rdataset); + if (result != ISC_R_SUCCESS) { + free_rdataset(rbtdb, rbtdb->common.mctx, + newheader); +@@ -7148,7 +7228,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + nodefullname(db, node, nodename); + + result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, +- ®ion, sizeof(rdatasetheader_t)); ++ ®ion, sizeof(rdatasetheader_t), ++ 0); + if (result != ISC_R_SUCCESS) { + return (result); + } +@@ -7552,7 +7633,8 @@ loading_addrdataset(void *arg, const dns_name_t *name, + } + + result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, +- ®ion, sizeof(rdatasetheader_t)); ++ ®ion, sizeof(rdatasetheader_t), ++ rbtdb->maxrrperset); + if (result != ISC_R_SUCCESS) { + return (result); + } +@@ -8088,6 +8170,24 @@ setgluecachestats(dns_db_t *db, isc_stats_t *stats) { + return (ISC_R_SUCCESS); + } + ++static void ++setmaxrrperset(dns_db_t *db, uint32_t maxrrperset) { ++ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; ++ ++ REQUIRE(VALID_RBTDB(rbtdb)); ++ ++ rbtdb->maxrrperset = maxrrperset; ++} ++ ++static void ++setmaxtypepername(dns_db_t *db, uint32_t maxtypepername) { ++ dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; ++ ++ REQUIRE(VALID_RBTDB(rbtdb)); ++ ++ rbtdb->maxtypepername = maxtypepername; ++} ++ + static dns_stats_t * + getrrsetstats(dns_db_t *db) { + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; +@@ -8209,7 +8309,9 @@ static dns_dbmethods_t zone_methods = { attach, + NULL, /* getservestalettl */ + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ +- setgluecachestats }; ++ setgluecachestats, ++ setmaxrrperset, ++ setmaxtypepername }; + + static dns_dbmethods_t cache_methods = { attach, + detach, +@@ -8259,7 +8361,9 @@ static dns_dbmethods_t cache_methods = { attach, + getservestalettl, + setservestalerefresh, + getservestalerefresh, +- NULL }; ++ NULL, ++ setmaxrrperset, ++ setmaxtypepername }; + + isc_result_t + dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type, +diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c +index 24fdaa8..5c30d44 100644 +--- a/lib/dns/rdataslab.c ++++ b/lib/dns/rdataslab.c +@@ -114,7 +114,8 @@ fillin_offsets(unsigned char *offsetbase, unsigned int *offsettable, + + isc_result_t + dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, +- isc_region_t *region, unsigned int reservelen) { ++ isc_region_t *region, unsigned int reservelen, ++ uint32_t maxrrperset) { + /* + * Use &removed as a sentinel pointer for duplicate + * rdata as rdata.data == NULL is valid. +@@ -156,6 +157,10 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, + return (ISC_R_SUCCESS); + } + ++ if (maxrrperset > 0 && nitems > maxrrperset) { ++ return (DNS_R_TOOMANYRECORDS); ++ } ++ + if (nitems > 0xffff) { + return (ISC_R_NOSPACE); + } +@@ -482,7 +487,8 @@ isc_result_t + dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, + unsigned int reservelen, isc_mem_t *mctx, + dns_rdataclass_t rdclass, dns_rdatatype_t type, +- unsigned int flags, unsigned char **tslabp) { ++ unsigned int flags, uint32_t maxrrperset, ++ unsigned char **tslabp) { + unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent, *data; + unsigned int ocount, ncount, count, olength, tlength, tcount, length; + dns_rdata_t ordata = DNS_RDATA_INIT; +@@ -522,6 +528,10 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, + #endif /* if DNS_RDATASET_FIXED */ + INSIST(ocount > 0 && ncount > 0); + ++ if (maxrrperset > 0 && ocount + ncount > maxrrperset) { ++ return (DNS_R_TOOMANYRECORDS); ++ } ++ + #if DNS_RDATASET_FIXED + oncount = ncount; + #endif /* if DNS_RDATASET_FIXED */ +diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c +index 317eeb0..07d720e 100644 +--- a/lib/dns/sdb.c ++++ b/lib/dns/sdb.c +@@ -1269,20 +1269,33 @@ settask(dns_db_t *db, isc_task_t *task) { + } + + static dns_dbmethods_t sdb_methods = { +- attach, detach, +- beginload, endload, +- dump, currentversion, +- newversion, attachversion, +- closeversion, NULL, /* findnode */ +- NULL, /* find */ +- findzonecut, attachnode, +- detachnode, expirenode, +- printnode, createiterator, +- findrdataset, allrdatasets, +- addrdataset, subtractrdataset, +- deleterdataset, issecure, +- nodecount, ispersistent, +- overmem, settask, ++ attach, ++ detach, ++ beginload, ++ endload, ++ dump, ++ currentversion, ++ newversion, ++ attachversion, ++ closeversion, ++ NULL, /* findnode */ ++ NULL, /* find */ ++ findzonecut, ++ attachnode, ++ detachnode, ++ expirenode, ++ printnode, ++ createiterator, ++ findrdataset, ++ allrdatasets, ++ addrdataset, ++ subtractrdataset, ++ deleterdataset, ++ issecure, ++ nodecount, ++ ispersistent, ++ overmem, ++ settask, + getoriginnode, /* getoriginnode */ + NULL, /* transfernode */ + NULL, /* getnsec3parameters */ +@@ -1294,7 +1307,8 @@ static dns_dbmethods_t sdb_methods = { + NULL, /* getrrsetstats */ + NULL, /* rpz_attach */ + NULL, /* rpz_ready */ +- findnodeext, findext, ++ findnodeext, ++ findext, + NULL, /* setcachestats */ + NULL, /* hashsize */ + NULL, /* nodefullname */ +@@ -1304,6 +1318,8 @@ static dns_dbmethods_t sdb_methods = { + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ + NULL, /* setgluecachestats */ ++ NULL, /* setmaxrrperset */ ++ NULL /* setmaxtypepername */ + }; + + static isc_result_t +diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c +index 7ab08f6..f9d123d 100644 +--- a/lib/dns/sdlz.c ++++ b/lib/dns/sdlz.c +@@ -1242,34 +1242,57 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { + } + + static dns_dbmethods_t sdlzdb_methods = { +- attach, detach, beginload, +- endload, dump, currentversion, +- newversion, attachversion, closeversion, +- findnode, find, findzonecut, +- attachnode, detachnode, expirenode, +- printnode, createiterator, findrdataset, +- allrdatasets, addrdataset, subtractrdataset, +- deleterdataset, issecure, nodecount, +- ispersistent, overmem, settask, +- getoriginnode, NULL, /* transfernode */ +- NULL, /* getnsec3parameters */ +- NULL, /* findnsec3node */ +- NULL, /* setsigningtime */ +- NULL, /* getsigningtime */ +- NULL, /* resigned */ +- NULL, /* isdnssec */ +- NULL, /* getrrsetstats */ +- NULL, /* rpz_attach */ +- NULL, /* rpz_ready */ +- findnodeext, findext, NULL, /* setcachestats */ +- NULL, /* hashsize */ +- NULL, /* nodefullname */ +- NULL, /* getsize */ +- NULL, /* setservestalettl */ +- NULL, /* getservestalettl */ +- NULL, /* setservestalerefresh */ +- NULL, /* getservestalerefresh */ +- NULL, /* setgluecachestats */ ++ attach, ++ detach, ++ beginload, ++ endload, ++ dump, ++ currentversion, ++ newversion, ++ attachversion, ++ closeversion, ++ findnode, ++ find, ++ findzonecut, ++ attachnode, ++ detachnode, ++ expirenode, ++ printnode, ++ createiterator, ++ findrdataset, ++ allrdatasets, ++ addrdataset, ++ subtractrdataset, ++ deleterdataset, ++ issecure, ++ nodecount, ++ ispersistent, ++ overmem, ++ settask, ++ getoriginnode, ++ NULL, /* transfernode */ ++ NULL, /* getnsec3parameters */ ++ NULL, /* findnsec3node */ ++ NULL, /* setsigningtime */ ++ NULL, /* getsigningtime */ ++ NULL, /* resigned */ ++ NULL, /* isdnssec */ ++ NULL, /* getrrsetstats */ ++ NULL, /* rpz_attach */ ++ NULL, /* rpz_ready */ ++ findnodeext, ++ findext, ++ NULL, /* setcachestats */ ++ NULL, /* hashsize */ ++ NULL, /* nodefullname */ ++ NULL, /* getsize */ ++ NULL, /* setservestalettl */ ++ NULL, /* getservestalettl */ ++ NULL, /* setservestalerefresh */ ++ NULL, /* getservestalerefresh */ ++ NULL, /* setgluecachestats */ ++ NULL, /* setmaxrrperset */ ++ NULL /* setmaxtypepername */ + }; + + /* +diff --git a/lib/dns/view.c b/lib/dns/view.c +index 49c9aee..231041e 100644 +--- a/lib/dns/view.c ++++ b/lib/dns/view.c +@@ -892,6 +892,9 @@ dns_view_setcache(dns_view_t *view, dns_cache_t *cache, bool shared) { + dns_cache_attach(cache, &view->cache); + dns_cache_attachdb(cache, &view->cachedb); + INSIST(DNS_DB_VALID(view->cachedb)); ++ ++ dns_cache_setmaxrrperset(view->cache, view->maxrrperset); ++ dns_cache_setmaxtypepername(view->cache, view->maxtypepername); + } + + bool +@@ -2759,3 +2762,21 @@ dns_view_sfd_find(dns_view_t *view, const dns_name_t *name, + dns_name_copy(dns_rootname, foundname); + } + } ++ ++void ++dns_view_setmaxrrperset(dns_view_t *view, uint32_t value) { ++ REQUIRE(DNS_VIEW_VALID(view)); ++ view->maxrrperset = value; ++ if (view->cache != NULL) { ++ dns_cache_setmaxrrperset(view->cache, value); ++ } ++} ++ ++void ++dns_view_setmaxtypepername(dns_view_t *view, uint32_t value) { ++ REQUIRE(DNS_VIEW_VALID(view)); ++ view->maxtypepername = value; ++ if (view->cache != NULL) { ++ dns_cache_setmaxtypepername(view->cache, value); ++ } ++} +diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c +index 1aa982a..e5f1e0b 100644 +--- a/lib/dns/xfrin.c ++++ b/lib/dns/xfrin.c +@@ -211,8 +211,6 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_nm_t *netmgr, + static isc_result_t + axfr_init(dns_xfrin_ctx_t *xfr); + static isc_result_t +-axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp); +-static isc_result_t + axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, + dns_ttl_t ttl, dns_rdata_t *rdata); + static isc_result_t +@@ -288,7 +286,11 @@ axfr_init(dns_xfrin_ctx_t *xfr) { + dns_db_detach(&xfr->db); + } + +- CHECK(axfr_makedb(xfr, &xfr->db)); ++ CHECK(dns_zone_makedb(xfr->zone, &xfr->db)); ++ ++ dns_zone_rpz_enable_db(xfr->zone, xfr->db); ++ dns_zone_catz_enable_db(xfr->zone, xfr->db); ++ + dns_rdatacallbacks_init(&xfr->axfr); + CHECK(dns_db_beginload(xfr->db, &xfr->axfr)); + result = ISC_R_SUCCESS; +@@ -296,22 +298,6 @@ failure: + return (result); + } + +-static isc_result_t +-axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) { +- isc_result_t result; +- +- result = dns_db_create(xfr->mctx, /* XXX */ +- "rbt", /* XXX guess */ +- &xfr->name, dns_dbtype_zone, xfr->rdclass, 0, +- NULL, /* XXX guess */ +- dbp); +- if (result == ISC_R_SUCCESS) { +- dns_zone_rpz_enable_db(xfr->zone, *dbp); +- dns_zone_catz_enable_db(xfr->zone, *dbp); +- } +- return (result); +-} +- + static isc_result_t + axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, + dns_ttl_t ttl, dns_rdata_t *rdata) { +diff --git a/lib/dns/zone.c b/lib/dns/zone.c +index 3b95136..f14a166 100644 +--- a/lib/dns/zone.c ++++ b/lib/dns/zone.c +@@ -309,6 +309,8 @@ struct dns_zone { + uint32_t minretry; + + uint32_t maxrecords; ++ uint32_t maxrrperset; ++ uint32_t maxtypepername; + + isc_sockaddr_t *primaries; + dns_name_t **primarykeynames; +@@ -2327,31 +2329,13 @@ zone_load(dns_zone_t *zone, unsigned int flags, bool locked) { + dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_DEBUG(1), + "starting load"); + +- result = dns_db_create(zone->mctx, zone->db_argv0, &zone->origin, +- (zone->type == dns_zone_stub) ? dns_dbtype_stub +- : dns_dbtype_zone, +- zone->rdclass, zone->db_argc - 1, +- zone->db_argv + 1, &db); +- ++ result = dns_zone_makedb(zone, &db); + if (result != ISC_R_SUCCESS) { + dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_ERROR, + "loading zone: creating database: %s", + isc_result_totext(result)); + goto cleanup; + } +- dns_db_settask(db, zone->task); +- +- if (zone->type == dns_zone_primary || +- zone->type == dns_zone_secondary || zone->type == dns_zone_mirror) +- { +- result = dns_db_setgluecachestats(db, zone->gluecachestats); +- if (result == ISC_R_NOTIMPLEMENTED) { +- result = ISC_R_SUCCESS; +- } +- if (result != ISC_R_SUCCESS) { +- goto cleanup; +- } +- } + + if (!dns_db_ispersistent(db)) { + if (zone->masterfile != NULL || zone->stream != NULL) { +@@ -10063,6 +10047,7 @@ cleanup: + } + + dns_diff_clear(&_sig_diff); ++ dns_diff_clear(&post_diff); + + for (i = 0; i < nkeys; i++) { + dst_key_free(&zone_keysi); +@@ -12332,6 +12317,26 @@ dns_zone_setmaxrecords(dns_zone_t *zone, uint32_t val) { + zone->maxrecords = val; + } + ++void ++dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t val) { ++ REQUIRE(DNS_ZONE_VALID(zone)); ++ ++ zone->maxrrperset = val; ++ if (zone->db != NULL) { ++ dns_db_setmaxrrperset(zone->db, val); ++ } ++} ++ ++void ++dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t val) { ++ REQUIRE(DNS_ZONE_VALID(zone)); ++ ++ zone->maxtypepername = val; ++ if (zone->db != NULL) { ++ dns_db_setmaxtypepername(zone->db, val); ++ } ++} ++ + static bool + notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name, + isc_sockaddr_t *addr, dns_tsigkey_t *key, +@@ -14799,6 +14804,9 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { + goto cleanup; + } + dns_db_settask(stub->db, zone->task); ++ dns_db_setmaxrrperset(stub->db, zone->maxrrperset); ++ dns_db_setmaxtypepername(stub->db, ++ zone->maxtypepername); + } + + result = dns_db_newversion(stub->db, &stub->version); +@@ -17516,6 +17524,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) { + } + zone_attachdb(zone, db); + dns_db_settask(zone->db, zone->task); ++ dns_db_setmaxrrperset(zone->db, zone->maxrrperset); ++ dns_db_setmaxtypepername(zone->db, zone->maxtypepername); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY); + return (ISC_R_SUCCESS); + +@@ -22045,7 +22055,11 @@ failure: + * Something went wrong; try again in ten minutes or + * after a key refresh interval, whichever is shorter. + */ +- dnssec_log(zone, ISC_LOG_DEBUG(3), ++ int loglevel = ISC_LOG_DEBUG(3); ++ if (result != DNS_R_NOTLOADED) { ++ loglevel = ISC_LOG_ERROR; ++ } ++ dnssec_log(zone, loglevel, + "zone_rekey failure: %s (retry in %u seconds)", + isc_result_totext(result), + ISC_MIN(zone->refreshkeyinterval, 600)); +@@ -23706,3 +23720,45 @@ zmgr_tlsctx_attach(dns_zonemgr_t *zmgr, isc_tlsctx_cache_t **ptlsctx_cache) { + + RWUNLOCK(&zmgr->tlsctx_cache_rwlock, isc_rwlocktype_read); + } ++ ++isc_result_t ++dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp) { ++ REQUIRE(DNS_ZONE_VALID(zone)); ++ REQUIRE(dbp != NULL && *dbp == NULL); ++ ++ dns_db_t *db = NULL; ++ ++ isc_result_t result = dns_db_create( ++ zone->mctx, zone->db_argv0, &zone->origin, ++ (zone->type == dns_zone_stub) ? dns_dbtype_stub ++ : dns_dbtype_zone, ++ zone->rdclass, zone->db_argc - 1, zone->db_argv + 1, &db); ++ if (result != ISC_R_SUCCESS) { ++ return (result); ++ } ++ ++ switch (zone->type) { ++ case dns_zone_primary: ++ case dns_zone_secondary: ++ case dns_zone_mirror: ++ result = dns_db_setgluecachestats(db, zone->gluecachestats); ++ if (result == ISC_R_NOTIMPLEMENTED) { ++ result = ISC_R_SUCCESS; ++ } ++ if (result != ISC_R_SUCCESS) { ++ dns_db_detach(&db); ++ return (result); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ dns_db_settask(db, zone->task); ++ dns_db_setmaxrrperset(db, zone->maxrrperset); ++ dns_db_setmaxtypepername(db, zone->maxtypepername); ++ ++ *dbp = db; ++ ++ return (ISC_R_SUCCESS); ++} +diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c +index 5a8ccb2..7938bcb 100644 +--- a/lib/isccfg/namedconf.c ++++ b/lib/isccfg/namedconf.c +@@ -2300,6 +2300,12 @@ static cfg_clausedef_t zone_clauses = { + { "max-records", &cfg_type_uint32, + CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | + CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT }, ++ { "max-records-per-type", &cfg_type_uint32, ++ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | ++ CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT }, ++ { "max-types-per-name", &cfg_type_uint32, ++ CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | ++ CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT }, + { "max-refresh-time", &cfg_type_uint32, + CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB }, + { "max-retry-time", &cfg_type_uint32, +diff --git a/lib/ns/update.c b/lib/ns/update.c +index 983ca84..5d72686 100644 +--- a/lib/ns/update.c ++++ b/lib/ns/update.c +@@ -3302,9 +3302,18 @@ update_action(isc_task_t *task, isc_event_t *event) { + dns_diff_clear(&ctx.add_diff); + goto failure; + } +- CHECK(update_one_rr(db, ver, &diff, +- DNS_DIFFOP_ADD, +- name, ttl, &rdata)); ++ result = update_one_rr( ++ db, ver, &diff, DNS_DIFFOP_ADD, ++ name, ttl, &rdata); ++ if (result != ISC_R_SUCCESS) { ++ update_log(client, zone, ++ LOGLEVEL_PROTOCOL, ++ "adding an RR " ++ "failed: %s", ++ isc_result_totext( ++ result)); ++ goto failure; ++ } + } + } + } else if (update_class == dns_rdataclass_any) { +-- +2.33.0 +
View file
_service:tar_scm:backport-CVE-2024-1975.patch
Added
@@ -0,0 +1,352 @@ +From bef3d2cca3552100bbe44790c8c1a4f5bef06798 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20=C5=A0pa=C4=8Dek?= <pspacek@isc.org> +Date: Thu, 16 May 2024 12:10:41 +0200 +Subject: PATCH Remove support for SIG(0) message verification + +Conflict:Case adaptation +Reference:https://downloads.isc.org/isc/bind9/9.18.28/patches/0003-CVE-2024-1975.patch + +--- + bin/tests/system/tsiggss/authsock.pl | 5 ++ + bin/tests/system/tsiggss/tests.sh | 12 ++-- + bin/tests/system/upforwd/tests.sh | 9 ++- + doc/arm/general.rst | 6 +- + doc/arm/intro-security.inc.rst | 2 +- + doc/arm/reference.rst | 4 +- + doc/arm/security.inc.rst | 4 +- + doc/arm/sig0.inc.rst | 16 +---- + lib/dns/message.c | 99 ++-------------------------- + lib/ns/client.c | 7 ++ + 10 files changed, 40 insertions(+), 124 deletions(-) + +diff --git a/bin/tests/system/tsiggss/authsock.pl b/bin/tests/system/tsiggss/authsock.pl +index 4c76bf8..972252a 100644 +--- a/bin/tests/system/tsiggss/authsock.pl ++++ b/bin/tests/system/tsiggss/authsock.pl +@@ -33,6 +33,10 @@ if (!defined($path)) { + exit(1); + } + ++# Enable output autoflush so that it's not lost when the parent sends TERM. ++select STDOUT; ++$| = 1; ++ + unlink($path); + my $server = IO::Socket::UNIX->new(Local => $path, Type => SOCK_STREAM, Listen => 8) or + die "unable to create socket $path"; +@@ -50,6 +54,7 @@ if ($timeout != 0) { + } + + while (my $client = $server->accept()) { ++ printf("accept()\n"); + $client->recv(my $buf, 8, 0); + my ($version, $req_len) = unpack('N N', $buf); + +diff --git a/bin/tests/system/tsiggss/tests.sh b/bin/tests/system/tsiggss/tests.sh +index c37f32e..004ad83 100644 +--- a/bin/tests/system/tsiggss/tests.sh ++++ b/bin/tests/system/tsiggss/tests.sh +@@ -117,7 +117,7 @@ status=$((status + ret)) + + echo_i "testing external update policy (CNAME) with auth sock ($n)" + ret=0 +-$PERL ./authsock.pl --type=CNAME --path=ns1/auth.sock --pidfile=authsock.pid --timeout=120 >/dev/null 2>&1 & ++$PERL ./authsock.pl --type=CNAME --path=ns1/auth.sock --pidfile=authsock.pid --timeout=120 >authsock.log 2>&1 & + sleep 1 + test_update $n testcname.example.nil. CNAME "86400 CNAME testdenied.example.nil" "testdenied" || ret=1 + n=$((n + 1)) +@@ -131,17 +131,19 @@ n=$((n + 1)) + if "$ret" -ne 0 ; then echo_i "failed"; fi + status=$((status + ret)) + +-echo_i "testing external policy with SIG(0) key ($n)" ++echo_i "testing external policy with unsupported SIG(0) key ($n)" + ret=0 +-$NSUPDATE -k ns1/Kkey.example.nil.*.private <<END >/dev/null 2>&1 || ret=1 ++$NSUPDATE -d -k ns1/Kkey.example.nil.*.private <<END >nsupdate.out${n} 2>&1 || true ++debug + server 10.53.0.1 ${PORT} + zone example.nil + update add fred.example.nil 120 cname foo.bar. + send + END + output=$($DIG $DIGOPTS +short cname fred.example.nil.) +- -n "$output" || ret=1 +- $ret -eq 0 || echo_i "failed" ++# update must have failed - SIG(0) signer is not supported ++ -n "$output" && ret=1 ++grep -F "signer=key.example.nil" authsock.log >/dev/null && ret=1 + n=$((n + 1)) + if "$ret" -ne 0 ; then echo_i "failed"; fi + status=$((status + ret)) +diff --git a/bin/tests/system/upforwd/tests.sh b/bin/tests/system/upforwd/tests.sh +index 518eac6..d231d0f 100644 +--- a/bin/tests/system/upforwd/tests.sh ++++ b/bin/tests/system/upforwd/tests.sh +@@ -229,10 +229,12 @@ fi + n=$((n + 1)) + + if test -f keyname; then +- echo_i "checking update forwarding to with sig0 ($n)" ++ echo_i "checking update forwarding to with sig0 (expected to fail) ($n)" + ret=0 + keyname=$(cat keyname) +- $NSUPDATE -k $keyname.private -- - <<EOF ++ # SIG(0) is removed, update is expected to fail. ++ { ++ $NSUPDATE -k $keyname.private -- - <<EOF + local 10.53.0.1 + server 10.53.0.3 ${PORT} + zone example2 +@@ -240,8 +242,9 @@ if test -f keyname; then + update add unsigned.example2. 600 TXT Foo + send + EOF ++ } >nsupdate.out.$n 2>&1 && ret=1 + $DIG -p ${PORT} unsigned.example2 A @10.53.0.1 >dig.out.ns1.test$n +- grep "status: NOERROR" dig.out.ns1.test$n >/dev/null || ret=1 ++ grep "status: NOERROR" dig.out.ns1.test$n >/dev/null && ret=1 + if $ret != 0 ; then echo_i "failed"; fi + status=$((status + ret)) + n=$((n + 1)) +diff --git a/doc/arm/general.rst b/doc/arm/general.rst +index 5b65f6a..35f74b3 100644 +--- a/doc/arm/general.rst ++++ b/doc/arm/general.rst +@@ -379,10 +379,8 @@ Notes + .. #rfc1035_2 CLASS ANY queries are not supported. This is considered a + feature. + +-.. #rfc2931 When receiving a query signed with a SIG(0), the server is +- only able to verify the signature if it has the key in its local +- authoritative data; it cannot do recursion or validation to +- retrieve unknown keys. ++.. #rfc2931 Support for SIG(0) message verification was removed ++ as part of the mitigation of CVE-2024-1975. + + .. #rfc2874 Compliance is with loading and serving of A6 records only. + A6 records were moved to the experimental category by :rfc:`3363`. +diff --git a/doc/arm/intro-security.inc.rst b/doc/arm/intro-security.inc.rst +index 87db970..996e910 100644 +--- a/doc/arm/intro-security.inc.rst ++++ b/doc/arm/intro-security.inc.rst +@@ -47,7 +47,7 @@ or ports come preconfigured with local (loopback address) security preconfigured + If ``rndc`` is being invoked from a remote host, further configuration is required. + The ``nsupdate`` tool uses **Dynamic DNS (DDNS)** features and allows users to dynamically + change the contents of the zone file(s). ``nsupdate`` access and security may be controlled +-using ``named.conf`` :ref:`statements or using TSIG or SIG(0) cryptographic methods <dynamic_update_security>`. ++using ``named.conf`` :ref:`statements or via the TSIG cryptographic method <dynamic_update_security>`. + Clearly, if the remote hosts used for either ``rndc`` or DDNS lie within a network entirely + under the user's control, the security threat may be regarded as non-existent. Any implementation requirements, + therefore, depend on the site's security policy. +diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst +index 29e246b..157ab30 100644 +--- a/doc/arm/reference.rst ++++ b/doc/arm/reference.rst +@@ -7417,7 +7417,7 @@ the zone's filename, unless :any:`inline-signing` is enabled. + updates are allowed. It specifies a set of rules, in which each rule + either grants or denies permission for one or more names in the zone to + be updated by one or more identities. Identity is determined by the key +- that signed the update request, using either TSIG or SIG(0). In most ++ that signed the update request, using TSIG. In most + cases, :any:`update-policy` rules only apply to key-based identities. There + is no way to specify update permissions based on the client source address. + +@@ -7474,7 +7474,7 @@ the zone's filename, unless :any:`inline-signing` is enabled. + field. Details for each rule type are described below. + + The ``identity`` field must be set to a fully qualified domain name. In +- most cases, this represents the name of the TSIG or SIG(0) key that ++ most cases, this represents the name of the TSIG key that + must be used to sign the update request. If the specified name is a + wildcard, it is subject to DNS wildcard expansion, and the rule may + apply to multiple identities. When a TKEY exchange has been used to +diff --git a/doc/arm/security.inc.rst b/doc/arm/security.inc.rst +index 878fa37..8fc65d3 100644 +--- a/doc/arm/security.inc.rst ++++ b/doc/arm/security.inc.rst +@@ -85,7 +85,7 @@ Limiting access to the server by outside parties can help prevent + spoofing and denial of service (DoS) attacks against the server. + + ACLs match clients on the basis of up to three characteristics: 1) The +-client's IP address; 2) the TSIG or SIG(0) key that was used to sign the ++client's IP address; 2) the TSIG key that was used to sign the + request, if any; and 3) an address prefix encoded in an EDNS + Client-Subnet option, if any. + +@@ -126,7 +126,7 @@ and no queries at all from the networks specified in ``bogusnets``. + + In addition to network addresses and prefixes, which are matched against + the source address of the DNS request, ACLs may include ``key`` +-elements, which specify the name of a TSIG or SIG(0) key. ++elements, which specify the name of a TSIG key. + + When BIND 9 is built with GeoIP support, ACLs can also be used for + geographic access restrictions. This is done by specifying an ACL +diff --git a/doc/arm/sig0.inc.rst b/doc/arm/sig0.inc.rst +index 048dbea..6e6fc32 100644 +--- a/doc/arm/sig0.inc.rst ++++ b/doc/arm/sig0.inc.rst +@@ -12,17 +12,5 @@ + SIG(0) + ------ + +-BIND partially supports DNSSEC SIG(0) transaction signatures as +-specified in :rfc:`2535` and :rfc:`2931`. SIG(0) uses public/private keys to +-authenticate messages. Access control is performed in the same manner as with +-TSIG keys; privileges can be granted or denied in ACL directives based +-on the key name. +- +-When a SIG(0) signed message is received, it is only verified if +-the key is known and trusted by the server. The server does not attempt +-to recursively fetch or validate the key. +- +-SIG(0) signing of multiple-message TCP streams is not supported. +- +-The only tool shipped with BIND 9 that generates SIG(0) signed messages +-is :iscman:`nsupdate`. ++Support for DNSSEC SIG(0) transaction signatures has been removed. ++This is a countermeasure for CVE-2024-1975. +diff --git a/lib/dns/message.c b/lib/dns/message.c +index 8654e92..a379125 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -3288,111 +3288,24 @@ dns_message_dumpsig(dns_message_t *msg, char *txt1) { + + isc_result_t + dns_message_checksig(dns_message_t *msg, dns_view_t *view) { +- isc_buffer_t b, msgb; ++ isc_buffer_t msgb; + + REQUIRE(DNS_MESSAGE_VALID(msg)); + +- if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL) { ++ if (msg->tsigkey == NULL && msg->tsig == NULL) { + return (ISC_R_SUCCESS); + } + + INSIST(msg->saved.base != NULL); + isc_buffer_init(&msgb, msg->saved.base, msg->saved.length); + isc_buffer_add(&msgb, msg->saved.length); +- if (msg->tsigkey != NULL || msg->tsig != NULL) { + #ifdef SKAN_MSG_DEBUG +- dns_message_dumpsig(msg, "dns_message_checksig#1"); ++ dns_message_dumpsig(msg, "dns_message_checksig#1"); + #endif /* ifdef SKAN_MSG_DEBUG */ +- if (view != NULL) { +- return (dns_view_checksig(view, &msgb, msg)); +- } else { +- return (dns_tsig_verify(&msgb, msg, NULL, NULL)); +- } ++ if (view != NULL) { ++ return (dns_view_checksig(view, &msgb, msg)); + } else { +- dns_rdata_t rdata = DNS_RDATA_INIT; +- dns_rdata_sig_t sig; +- dns_rdataset_t keyset; +- isc_result_t result; +- +- result = dns_rdataset_first(msg->sig0); +- INSIST(result == ISC_R_SUCCESS); +- dns_rdataset_current(msg->sig0, &rdata); +- +- /* +- * This can occur when the message is a dynamic update, since +- * the rdata length checking is relaxed. This should not +- * happen in a well-formed message, since the SIG(0) is only +- * looked for in the additional section, and the dynamic update +- * meta-records are in the prerequisite and update sections. +- */ +- if (rdata.length == 0) { +- return (ISC_R_UNEXPECTEDEND); +- } +- +- result = dns_rdata_tostruct(&rdata, &sig, NULL); +- if (result != ISC_R_SUCCESS) { +- return (result); +- } +- +- dns_rdataset_init(&keyset); +- if (view == NULL) { +- result = DNS_R_KEYUNAUTHORIZED; +- goto freesig; +- } +- result = dns_view_simplefind(view, &sig.signer, +- dns_rdatatype_key /* SIG(0) */, 0, +- 0, false, &keyset, NULL); +- +- if (result != ISC_R_SUCCESS) { +- /* XXXBEW Should possibly create a fetch here */ +- result = DNS_R_KEYUNAUTHORIZED; +- goto freesig; +- } else if (keyset.trust < dns_trust_secure) { +- /* XXXBEW Should call a validator here */ +- result = DNS_R_KEYUNAUTHORIZED; +- goto freesig; +- } +- result = dns_rdataset_first(&keyset); +- INSIST(result == ISC_R_SUCCESS); +- for (; result == ISC_R_SUCCESS; +- result = dns_rdataset_next(&keyset)) +- { +- dst_key_t *key = NULL; +- +- dns_rdata_reset(&rdata); +- dns_rdataset_current(&keyset, &rdata); +- isc_buffer_init(&b, rdata.data, rdata.length); +- isc_buffer_add(&b, rdata.length); +- +- result = dst_key_fromdns(&sig.signer, rdata.rdclass, &b, +- view->mctx, &key); +- if (result != ISC_R_SUCCESS) { +- continue; +- } +- if (dst_key_alg(key) != sig.algorithm || +- dst_key_id(key) != sig.keyid || +- !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC || +- dst_key_proto(key) == DNS_KEYPROTO_ANY)) +- { +- dst_key_free(&key); +- continue; +- } +- result = dns_dnssec_verifymessage(&msgb, msg, key); +- dst_key_free(&key); +- if (result == ISC_R_SUCCESS) { +- break; +- } +- } +- if (result == ISC_R_NOMORE) { +- result = DNS_R_KEYUNAUTHORIZED; +- } +- +- freesig: +- if (dns_rdataset_isassociated(&keyset)) { +- dns_rdataset_disassociate(&keyset); +- } +- dns_rdata_freestruct(&sig); +- return (result); ++ return (dns_tsig_verify(&msgb, msg, NULL, NULL)); + } + } + +diff --git a/lib/ns/client.c b/lib/ns/client.c +index 8981222..5d2ad0b 100644 +--- a/lib/ns/client.c ++++ b/lib/ns/client.c +@@ -2168,6 +2168,13 @@ ns__client_request(isc_nmhandle_t *handle, isc_result_t eresult, + ns_client_log(client, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), + "request is signed by a nonauthoritative key"); ++ } else if (result == DNS_R_NOTVERIFIEDYET && ++ client->message->sig0 != NULL) ++ { ++ ns_client_log(client, DNS_LOGCATEGORY_SECURITY, ++ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3), ++ "request has a SIG(0) signature but its support " ++ "was removed (CVE-2024-1975)"); + } else { + char tsigrcode64; + isc_buffer_t b; +-- +2.33.0 +
View file
_service:tar_scm:backport-CVE-2024-4076.patch
Added
@@ -0,0 +1,34 @@ +From 9cfd20cd90fab4c97fe91f68555b7a2e05b808e8 Mon Sep 17 00:00:00 2001 +From: Mark Andrews <marka@isc.org> +Date: Tue, 16 Jan 2024 14:25:27 +1100 +Subject: PATCH Clear qctx->zversion + +Clear qctx->zversion when clearing qctx->zrdataset et al in +lib/ns/query.c:qctx_freedata. The uncleared pointer could lead to +an assertion failure if zone data needed to be re-saved which could +happen with stale data support enabled. + +(cherry picked from commit 179fb3532ab8d4898ab070b2db54c0ce872ef709) + +Conflict:NA +Reference:https://downloads.isc.org/isc/bind9/9.18.28/patches/0004-CVE-2024-4076.patch + +--- + lib/ns/query.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/ns/query.c b/lib/ns/query.c +index 40e1232..7884514 100644 +--- a/lib/ns/query.c ++++ b/lib/ns/query.c +@@ -5323,6 +5323,7 @@ qctx_freedata(query_ctx_t *qctx) { + ns_client_releasename(qctx->client, &qctx->zfname); + dns_db_detachnode(qctx->zdb, &qctx->znode); + dns_db_detach(&qctx->zdb); ++ qctx->zversion = NULL; + } + + if (qctx->event != NULL && !qctx->client->nodetach) { +-- +2.33.0 +
View file
_service:tar_scm:backport-optimize-the-slabheader-placement-for-certain-RRtypes.patch
Added
@@ -0,0 +1,98 @@ +From 8ef414a7f38a04cfc11df44adaedaf3126fa3878 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org> +Date: Mon, 29 Jan 2024 16:36:30 +0100 +Subject: PATCH Optimize the slabheader placement for certain RRTypes + +Mark the infrastructure RRTypes as "priority" types and place them at +the beginning of the rdataslab header data graph. The non-priority +types either go right after the priority types (if any). + +(cherry picked from commit 3ac482be7fd058d284e89873021339579fad0615) + +Conflict:NA +Reference:https://gitlab.isc.org/isc-projects/bind9/-/commit/8ef414a7f38a04cfc11df44adaedaf3126fa3878 + +--- + lib/dns/rbtdb.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 42 insertions(+), 2 deletions(-) + +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index 7793be8..bc0f8d8 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -906,6 +906,30 @@ set_ttl(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, dns_ttl_t newttl) { + } + } + ++static bool ++prio_type(rbtdb_rdatatype_t type) { ++ switch (type) { ++ case dns_rdatatype_soa: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_soa): ++ case dns_rdatatype_a: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_a): ++ case dns_rdatatype_aaaa: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_aaaa): ++ case dns_rdatatype_nsec: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec): ++ case dns_rdatatype_nsec3: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec3): ++ case dns_rdatatype_ns: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns): ++ case dns_rdatatype_ds: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ds): ++ case dns_rdatatype_cname: ++ case RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname): ++ return (true); ++ } ++ return (false); ++} ++ + /*% + * These functions allow the heap code to rank the priority of each + * element. It returns true if v1 happens "sooner" than v2. +@@ -6167,6 +6191,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + rbtdb_changed_t *changed = NULL; + rdatasetheader_t *topheader = NULL, *topheader_prev = NULL; + rdatasetheader_t *header = NULL, *sigheader = NULL; ++ rdatasetheader_t *prioheader = NULL; + unsigned char *merged = NULL; + isc_result_t result; + bool header_nx; +@@ -6313,6 +6338,9 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, + for (topheader = rbtnode->data; topheader != NULL; + topheader = topheader->next) + { ++ if (prio_type(topheader->type)) { ++ prioheader = topheader; ++ } + if (topheader->type == newheader->type || + topheader->type == negtype) + { +@@ -6679,9 +6707,21 @@ find_header: + /* + * No rdatasets of the given type exist at the node. + */ +- newheader->next = rbtnode->data; + newheader->down = NULL; +- rbtnode->data = newheader; ++ ++ if (prio_type(newheader->type)) { ++ /* This is a priority type, prepend it */ ++ newheader->next = rbtnode->data; ++ rbtnode->data = newheader; ++ } else if (prioheader != NULL) { ++ /* Append after the priority headers */ ++ newheader->next = prioheader->next; ++ prioheader->next = newheader; ++ } else { ++ /* There were no priority headers */ ++ newheader->next = rbtnode->data; ++ rbtnode->data = newheader; ++ } + } + } + +-- +2.33.0 +
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2