summaryrefslogtreecommitdiffstats
path: root/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch')
-rw-r--r--meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch911
1 files changed, 0 insertions, 911 deletions
diff --git a/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch b/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch
deleted file mode 100644
index 987e75bc0e..0000000000
--- a/meta/recipes-connectivity/bind/bind/0005-refactor-tcpquota-and-pipeline-refs-allow-special-ca.patch
+++ /dev/null
@@ -1,911 +0,0 @@
-Backport patch to fix CVE-2018-5743.
-
-Ref:
-https://security-tracker.debian.org/tracker/CVE-2018-5743
-
-CVE: CVE-2018-5743
-Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/commit/c47ccf6]
-
-Signed-off-by: Kai Kang <kai.kang@windriver.com>
-
-From c47ccf630f147378568b33e8fdb7b754f228c346 Mon Sep 17 00:00:00 2001
-From: Evan Hunt <each@isc.org>
-Date: Fri, 5 Apr 2019 16:26:05 -0700
-Subject: [PATCH 5/6] refactor tcpquota and pipeline refs; allow special-case
- overrun in isc_quota
-
-- if the TCP quota has been exceeded but there are no clients listening
- for new connections on the interface, we can now force attachment to the
- quota using isc_quota_force(), instead of carrying on with the quota not
- attached.
-- the TCP client quota is now referenced via a reference-counted
- 'ns_tcpconn' object, one of which is created whenever a client begins
- listening for new connections, and attached to by members of that
- client's pipeline group. when the last reference to the tcpconn
- object is detached, it is freed and the TCP quota slot is released.
-- reduce code duplication by adding mark_tcp_active() function.
-- convert counters to atomic.
-
-(cherry picked from commit 7e8222378ca24f1302a0c1c638565050ab04681b)
-(cherry picked from commit 4939451275722bfda490ea86ca13e84f6bc71e46)
-(cherry picked from commit 13f7c918b8720d890408f678bd73c20e634539d9)
----
- bin/named/client.c | 444 +++++++++++--------------
- bin/named/include/named/client.h | 12 +-
- bin/named/include/named/interfacemgr.h | 6 +-
- bin/named/interfacemgr.c | 1 +
- lib/isc/include/isc/quota.h | 7 +
- lib/isc/quota.c | 33 +-
- lib/isc/win32/libisc.def.in | 1 +
- 7 files changed, 236 insertions(+), 268 deletions(-)
-
-diff --git a/bin/named/client.c b/bin/named/client.c
-index 61e96dd28c..d826ab32bf 100644
---- a/bin/named/client.c
-+++ b/bin/named/client.c
-@@ -244,8 +244,7 @@ static void client_start(isc_task_t *task, isc_event_t *event);
- static void client_request(isc_task_t *task, isc_event_t *event);
- static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
- static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
-- dns_dispatch_t *disp, ns_client_t *oldclient,
-- bool tcp);
-+ dns_dispatch_t *disp, bool tcp);
- static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp,
- isc_socket_t *sock, ns_client_t *oldclient);
- static inline bool
-@@ -301,16 +300,32 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
- }
-
- /*%
-- * Allocate a reference counter that will track the number of client structures
-- * using the TCP connection that 'client' called accept() for. This counter
-- * will be shared between all client structures associated with this TCP
-- * connection.
-+ * Allocate a reference-counted object that will maintain a single pointer to
-+ * the (also reference-counted) TCP client quota, shared between all the
-+ * clients processing queries on a single TCP connection, so that all
-+ * clients sharing the one socket will together consume only one slot in
-+ * the 'tcp-clients' quota.
- */
--static void
--pipeline_init(ns_client_t *client) {
-- isc_refcount_t *refs;
-+static isc_result_t
-+tcpconn_init(ns_client_t *client, bool force) {
-+ isc_result_t result;
-+ isc_quota_t *quota = NULL;
-+ ns_tcpconn_t *tconn = NULL;
-
-- REQUIRE(client->pipeline_refs == NULL);
-+ REQUIRE(client->tcpconn == NULL);
-+
-+ /*
-+ * Try to attach to the quota first, so we won't pointlessly
-+ * allocate memory for a tcpconn object if we can't get one.
-+ */
-+ if (force) {
-+ result = isc_quota_force(&ns_g_server->tcpquota, &quota);
-+ } else {
-+ result = isc_quota_attach(&ns_g_server->tcpquota, &quota);
-+ }
-+ if (result != ISC_R_SUCCESS) {
-+ return (result);
-+ }
-
- /*
- * A global memory context is used for the allocation as different
-@@ -320,78 +335,80 @@ pipeline_init(ns_client_t *client) {
- * contention here is expected to be negligible, given that this code
- * is only executed for TCP connections.
- */
-- refs = isc_mem_allocate(ns_g_mctx, sizeof(*refs));
-- isc_refcount_init(refs, 1);
-- client->pipeline_refs = refs;
-+ tconn = isc_mem_allocate(ns_g_mctx, sizeof(*tconn));
-+
-+ isc_refcount_init(&tconn->refs, 1);
-+ tconn->tcpquota = quota;
-+ quota = NULL;
-+ tconn->pipelined = false;
-+
-+ client->tcpconn = tconn;
-+
-+ return (ISC_R_SUCCESS);
- }
-
- /*%
-- * Increase the count of client structures using the TCP connection that
-- * 'source' is associated with and put a pointer to that count in 'target',
-- * thus associating it with the same TCP connection.
-+ * Increase the count of client structures sharing the TCP connection
-+ * that 'source' is associated with; add a pointer to the same tcpconn
-+ * to 'target', thus associating it with the same TCP connection.
- */
- static void
--pipeline_attach(ns_client_t *source, ns_client_t *target) {
-+tcpconn_attach(ns_client_t *source, ns_client_t *target) {
- int refs;
-
-- REQUIRE(source->pipeline_refs != NULL);
-- REQUIRE(target->pipeline_refs == NULL);
-+ REQUIRE(source->tcpconn != NULL);
-+ REQUIRE(target->tcpconn == NULL);
-+ REQUIRE(source->tcpconn->pipelined);
-
-- isc_refcount_increment(source->pipeline_refs, &refs);
-+ isc_refcount_increment(&source->tcpconn->refs, &refs);
- INSIST(refs > 1);
-- target->pipeline_refs = source->pipeline_refs;
-+ target->tcpconn = source->tcpconn;
- }
-
- /*%
-- * Decrease the count of client structures using the TCP connection that
-+ * Decrease the count of client structures sharing the TCP connection that
- * 'client' is associated with. If this is the last client using this TCP
-- * connection, free the reference counter and return true; otherwise, return
-- * false.
-+ * connection, we detach from the TCP quota and free the tcpconn
-+ * object. Either way, client->tcpconn is set to NULL.
- */
--static bool
--pipeline_detach(ns_client_t *client) {
-- isc_refcount_t *refcount;
-+static void
-+tcpconn_detach(ns_client_t *client) {
-+ ns_tcpconn_t *tconn = NULL;
- int refs;
-
-- REQUIRE(client->pipeline_refs != NULL);
--
-- refcount = client->pipeline_refs;
-- client->pipeline_refs = NULL;
-+ REQUIRE(client->tcpconn != NULL);
-
-- isc_refcount_decrement(refcount, refs);
-+ tconn = client->tcpconn;
-+ client->tcpconn = NULL;
-
-+ isc_refcount_decrement(&tconn->refs, &refs);
- if (refs == 0) {
-- isc_mem_free(ns_g_mctx, refs);
-- return (true);
-+ isc_quota_detach(&tconn->tcpquota);
-+ isc_mem_free(ns_g_mctx, tconn);
- }
--
-- return (false);
- }
-
--/*
-- * Detach a client from the TCP client quota if appropriate, and set
-- * the quota pointer to NULL.
-- *
-- * Sometimes when the TCP client quota is exhausted but there are no other
-- * clients servicing the interface, a client will be allowed to continue
-- * running despite not having been attached to the quota. In this event,
-- * the TCP quota was never attached to the client, so when the client (or
-- * associated pipeline group) shuts down, the quota must NOT be detached.
-+/*%
-+ * Mark a client as active and increment the interface's 'ntcpactive'
-+ * counter, as a signal that there is at least one client servicing
-+ * TCP queries for the interface. If we reach the TCP client quota at
-+ * some point, this will be used to determine whether a quota overrun
-+ * should be permitted.
- *
-- * Otherwise, if the quota pointer is set, it should be detached. If not
-- * set at all, we just return without doing anything.
-+ * Marking the client active with the 'tcpactive' flag ensures proper
-+ * accounting, by preventing us from incrementing or decrementing
-+ * 'ntcpactive' more than once per client.
- */
- static void
--tcpquota_disconnect(ns_client_t *client) {
-- if (client->tcpquota == NULL) {
-- return;
-- }
--
-- if (client->tcpattached) {
-- isc_quota_detach(&client->tcpquota);
-- client->tcpattached = false;
-- } else {
-- client->tcpquota = NULL;
-+mark_tcp_active(ns_client_t *client, bool active) {
-+ if (active && !client->tcpactive) {
-+ isc_atomic_xadd(&client->interface->ntcpactive, 1);
-+ client->tcpactive = active;
-+ } else if (!active && client->tcpactive) {
-+ uint32_t old =
-+ isc_atomic_xadd(&client->interface->ntcpactive, -1);
-+ INSIST(old > 0);
-+ client->tcpactive = active;
- }
- }
-
-@@ -484,7 +501,8 @@ exit_check(ns_client_t *client) {
- INSIST(client->recursionquota == NULL);
-
- if (NS_CLIENTSTATE_READING == client->newstate) {
-- if (!client->pipelined) {
-+ INSIST(client->tcpconn != NULL);
-+ if (!client->tcpconn->pipelined) {
- client_read(client);
- client->newstate = NS_CLIENTSTATE_MAX;
- return (true); /* We're done. */
-@@ -507,8 +525,8 @@ exit_check(ns_client_t *client) {
- dns_tcpmsg_cancelread(&client->tcpmsg);
- }
-
-- if (client->nreads != 0) {
-- /* Still waiting for read cancel completion. */
-+ /* Still waiting for read cancel completion. */
-+ if (client->nreads > 0) {
- return (true);
- }
-
-@@ -518,43 +536,45 @@ exit_check(ns_client_t *client) {
- }
-
- /*
-- * Detach from pipeline group and from TCP client quota,
-- * if appropriate.
-+ * Soon the client will be ready to accept a new TCP
-+ * connection or UDP request, but we may have enough
-+ * clients doing that already. Check whether this client
-+ * needs to remain active and allow it go inactive if
-+ * not.
- *
-- * - If no pipeline group is active, attempt to
-- * detach from the TCP client quota.
-+ * UDP clients always go inactive at this point, but a TCP
-+ * client may need to stay active and return to READY
-+ * state if no other clients are available to listen
-+ * for TCP requests on this interface.
- *
-- * - If a pipeline group is active, detach from it;
-- * if the return code indicates that there no more
-- * clients left if this pipeline group, we also detach
-- * from the TCP client quota.
-- *
-- * - Otherwise we don't try to detach, we just set the
-- * TCP quota pointer to NULL if it wasn't NULL already.
-- *
-- * tcpquota_disconnect() will set tcpquota to NULL, either
-- * by detaching it or by assignment, depending on the
-- * needs of the client. See the comments on that function
-- * for further information.
-+ * Regardless, if we're going to FREED state, that means
-+ * the system is shutting down and we don't need to
-+ * retain clients.
- */
-- if (client->pipeline_refs == NULL || pipeline_detach(client)) {
-- tcpquota_disconnect(client);
-- } else {
-- client->tcpquota = NULL;
-- client->tcpattached = false;
-+ if (client->mortal && TCP_CLIENT(client) &&
-+ client->newstate != NS_CLIENTSTATE_FREED &&
-+ !ns_g_clienttest &&
-+ isc_atomic_xadd(&client->interface->ntcpaccepting, 0) == 0)
-+ {
-+ /* Nobody else is accepting */
-+ client->mortal = false;
-+ client->newstate = NS_CLIENTSTATE_READY;
-+ }
-+
-+ /*
-+ * Detach from TCP connection and TCP client quota,
-+ * if appropriate. If this is the last reference to
-+ * the TCP connection in our pipeline group, the
-+ * TCP quota slot will be released.
-+ */
-+ if (client->tcpconn) {
-+ tcpconn_detach(client);
- }
-
- if (client->tcpsocket != NULL) {
- CTRACE("closetcp");
- isc_socket_detach(&client->tcpsocket);
--
-- if (client->tcpactive) {
-- LOCK(&client->interface->lock);
-- INSIST(client->interface->ntcpactive > 0);
-- client->interface->ntcpactive--;
-- UNLOCK(&client->interface->lock);
-- client->tcpactive = false;
-- }
-+ mark_tcp_active(client, false);
- }
-
- if (client->timerset) {
-@@ -567,35 +587,6 @@ exit_check(ns_client_t *client) {
- client->peeraddr_valid = false;
-
- client->state = NS_CLIENTSTATE_READY;
-- INSIST(client->recursionquota == NULL);
--
-- /*
-- * Now the client is ready to accept a new TCP connection
-- * or UDP request, but we may have enough clients doing
-- * that already. Check whether this client needs to remain
-- * active and force it to go inactive if not.
-- *
-- * UDP clients go inactive at this point, but a TCP client
-- * may need to remain active and go into ready state if
-- * no other clients are available to listen for TCP
-- * requests on this interface or (in the case of pipelined
-- * clients) to read for additional messages on the current
-- * connection.
-- */
-- if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
-- LOCK(&client->interface->lock);
-- if ((client->interface->ntcpaccepting == 0 ||
-- (client->pipelined &&
-- client->interface->ntcpactive < 2)) &&
-- client->newstate != NS_CLIENTSTATE_FREED)
-- {
-- client->mortal = false;
-- client->newstate = NS_CLIENTSTATE_READY;
-- }
-- UNLOCK(&client->interface->lock);
-- }
--
-- client->pipelined = false;
-
- /*
- * We don't need the client; send it to the inactive
-@@ -630,7 +621,7 @@ exit_check(ns_client_t *client) {
- }
-
- /* Still waiting for accept cancel completion. */
-- if (! (client->naccepts == 0)) {
-+ if (client->naccepts > 0) {
- return (true);
- }
-
-@@ -641,7 +632,7 @@ exit_check(ns_client_t *client) {
- }
-
- /* Still waiting for recv cancel completion. */
-- if (! (client->nrecvs == 0)) {
-+ if (client->nrecvs > 0) {
- return (true);
- }
-
-@@ -654,14 +645,7 @@ exit_check(ns_client_t *client) {
- INSIST(client->recursionquota == NULL);
- if (client->tcplistener != NULL) {
- isc_socket_detach(&client->tcplistener);
--
-- if (client->tcpactive) {
-- LOCK(&client->interface->lock);
-- INSIST(client->interface->ntcpactive > 0);
-- client->interface->ntcpactive--;
-- UNLOCK(&client->interface->lock);
-- client->tcpactive = false;
-- }
-+ mark_tcp_active(client, false);
- }
- if (client->udpsocket != NULL) {
- isc_socket_detach(&client->udpsocket);
-@@ -816,7 +800,7 @@ client_start(isc_task_t *task, isc_event_t *event) {
- return;
-
- if (TCP_CLIENT(client)) {
-- if (client->pipelined) {
-+ if (client->tcpconn != NULL) {
- client_read(client);
- } else {
- client_accept(client);
-@@ -2470,6 +2454,7 @@ client_request(isc_task_t *task, isc_event_t *event) {
- client->nrecvs--;
- } else {
- INSIST(TCP_CLIENT(client));
-+ INSIST(client->tcpconn != NULL);
- REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
- REQUIRE(event->ev_sender == &client->tcpmsg);
- buffer = &client->tcpmsg.buffer;
-@@ -2657,17 +2642,19 @@ client_request(isc_task_t *task, isc_event_t *event) {
- /*
- * Pipeline TCP query processing.
- */
-- if (client->message->opcode != dns_opcode_query) {
-- client->pipelined = false;
-+ if (TCP_CLIENT(client) &&
-+ client->message->opcode != dns_opcode_query)
-+ {
-+ client->tcpconn->pipelined = false;
- }
-- if (TCP_CLIENT(client) && client->pipelined) {
-+ if (TCP_CLIENT(client) && client->tcpconn->pipelined) {
- /*
- * We're pipelining. Replace the client; the
-- * the replacement can read the TCP socket looking
-- * for new messages and this client can process the
-+ * replacement can read the TCP socket looking
-+ * for new messages and this one can process the
- * current message asynchronously.
- *
-- * There are now at least three clients using this
-+ * There will now be at least three clients using this
- * TCP socket - one accepting new connections,
- * one reading an existing connection to get new
- * messages, and one answering the message already
-@@ -2675,7 +2662,7 @@ client_request(isc_task_t *task, isc_event_t *event) {
- */
- result = ns_client_replace(client);
- if (result != ISC_R_SUCCESS) {
-- client->pipelined = false;
-+ client->tcpconn->pipelined = false;
- }
- }
-
-@@ -3233,10 +3220,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
- client->signer = NULL;
- dns_name_init(&client->signername, NULL);
- client->mortal = false;
-- client->pipelined = false;
-- client->pipeline_refs = NULL;
-- client->tcpquota = NULL;
-- client->tcpattached = false;
-+ client->tcpconn = NULL;
- client->recursionquota = NULL;
- client->interface = NULL;
- client->peeraddr_valid = false;
-@@ -3341,9 +3325,10 @@ client_read(ns_client_t *client) {
-
- static void
- client_newconn(isc_task_t *task, isc_event_t *event) {
-+ isc_result_t result;
- ns_client_t *client = event->ev_arg;
- isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
-- isc_result_t result;
-+ uint32_t old;
-
- REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
- REQUIRE(NS_CLIENT_VALID(client));
-@@ -3363,10 +3348,8 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
- INSIST(client->naccepts == 1);
- client->naccepts--;
-
-- LOCK(&client->interface->lock);
-- INSIST(client->interface->ntcpaccepting > 0);
-- client->interface->ntcpaccepting--;
-- UNLOCK(&client->interface->lock);
-+ old = isc_atomic_xadd(&client->interface->ntcpaccepting, -1);
-+ INSIST(old > 0);
-
- /*
- * We must take ownership of the new socket before the exit
-@@ -3399,7 +3382,7 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
- "accept failed: %s",
- isc_result_totext(nevent->result));
-- tcpquota_disconnect(client);
-+ tcpconn_detach(client);
- }
-
- if (exit_check(client))
-@@ -3437,15 +3420,13 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
- * telnetting to port 53 (once per CPU) will
- * deny service to legitimate TCP clients.
- */
-- client->pipelined = false;
- result = ns_client_replace(client);
- if (result == ISC_R_SUCCESS &&
- (ns_g_server->keepresporder == NULL ||
- !allowed(&netaddr, NULL, NULL, 0, NULL,
- ns_g_server->keepresporder)))
- {
-- pipeline_init(client);
-- client->pipelined = true;
-+ client->tcpconn->pipelined = true;
- }
-
- client_read(client);
-@@ -3462,78 +3443,59 @@ client_accept(ns_client_t *client) {
- CTRACE("accept");
-
- /*
-- * The tcpquota object can only be simultaneously referenced a
-- * pre-defined number of times; this is configured by 'tcp-clients'
-- * in named.conf. If we can't attach to it here, that means the TCP
-- * client quota has been exceeded.
-+ * Set up a new TCP connection. This means try to attach to the
-+ * TCP client quota (tcp-clients), but fail if we're over quota.
- */
-- result = isc_quota_attach(&ns_g_server->tcpquota,
-- &client->tcpquota);
-+ result = tcpconn_init(client, false);
- if (result != ISC_R_SUCCESS) {
-- bool exit;
-+ bool exit;
-
-- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-- NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
-- "no more TCP clients: %s",
-- isc_result_totext(result));
--
-- /*
-- * We have exceeded the system-wide TCP client
-- * quota. But, we can't just block this accept
-- * in all cases, because if we did, a heavy TCP
-- * load on other interfaces might cause this
-- * interface to be starved, with no clients able
-- * to accept new connections.
-- *
-- * So, we check here to see if any other clients
-- * are already servicing TCP queries on this
-- * interface (whether accepting, reading, or
-- * processing). If there are at least two
-- * (one reading and one processing a request)
-- * then it's okay *not* to call accept - we
-- * can let this client go inactive and another
-- * one will resume accepting when it's done.
-- *
-- * If there aren't enough active clients on the
-- * interface, then we can be a little bit
-- * flexible about the quota. We'll allow *one*
-- * extra client through to ensure we're listening
-- * on every interface.
-- *
-- * (Note: In practice this means that the real
-- * TCP client quota is tcp-clients plus the
-- * number of listening interfaces plus 2.)
-- */
-- LOCK(&client->interface->lock);
-- exit = (client->interface->ntcpactive > 1);
-- UNLOCK(&client->interface->lock);
-+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-+ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
-+ "TCP client quota reached: %s",
-+ isc_result_totext(result));
-
-- if (exit) {
-- client->newstate = NS_CLIENTSTATE_INACTIVE;
-- (void)exit_check(client);
-- return;
-- }
-+ /*
-+ * We have exceeded the system-wide TCP client quota. But,
-+ * we can't just block this accept in all cases, because if
-+ * we did, a heavy TCP load on other interfaces might cause
-+ * this interface to be starved, with no clients able to
-+ * accept new connections.
-+ *
-+ * So, we check here to see if any other clients are
-+ * already servicing TCP queries on this interface (whether
-+ * accepting, reading, or processing). If we find at least
-+ * one, then it's okay *not* to call accept - we can let this
-+ * client go inactive and another will take over when it's
-+ * done.
-+ *
-+ * If there aren't enough active clients on the interface,
-+ * then we can be a little bit flexible about the quota.
-+ * We'll allow *one* extra client through to ensure we're
-+ * listening on every interface; we do this by setting the
-+ * 'force' option to tcpconn_init().
-+ *
-+ * (Note: In practice this means that the real TCP client
-+ * quota is tcp-clients plus the number of listening
-+ * interfaces plus 1.)
-+ */
-+ exit = (isc_atomic_xadd(&client->interface->ntcpactive, 0) > 0);
-+ if (exit) {
-+ client->newstate = NS_CLIENTSTATE_INACTIVE;
-+ (void)exit_check(client);
-+ return;
-+ }
-
-- } else {
-- client->tcpattached = true;
-+ result = tcpconn_init(client, true);
-+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
- }
-
- /*
-- * By incrementing the interface's ntcpactive counter we signal
-- * that there is at least one client servicing TCP queries for the
-- * interface.
-- *
-- * We also make note of the fact in the client itself with the
-- * tcpactive flag. This ensures proper accounting by preventing
-- * us from accidentally incrementing or decrementing ntcpactive
-- * more than once per client object.
-+ * If this client was set up using get_client() or get_worker(),
-+ * then TCP is already marked active. However, if it was restarted
-+ * from exit_check(), it might not be, so we take care of it now.
- */
-- if (!client->tcpactive) {
-- LOCK(&client->interface->lock);
-- client->interface->ntcpactive++;
-- UNLOCK(&client->interface->lock);
-- client->tcpactive = true;
-- }
-+ mark_tcp_active(client, true);
-
- result = isc_socket_accept(client->tcplistener, client->task,
- client_newconn, client);
-@@ -3549,15 +3511,8 @@ client_accept(ns_client_t *client) {
- "isc_socket_accept() failed: %s",
- isc_result_totext(result));
-
-- tcpquota_disconnect(client);
--
-- if (client->tcpactive) {
-- LOCK(&client->interface->lock);
-- client->interface->ntcpactive--;
-- UNLOCK(&client->interface->lock);
-- client->tcpactive = false;
-- }
--
-+ tcpconn_detach(client);
-+ mark_tcp_active(client, false);
- return;
- }
-
-@@ -3582,9 +3537,7 @@ client_accept(ns_client_t *client) {
- * listening for connections itself to prevent the interface
- * going dead.
- */
-- LOCK(&client->interface->lock);
-- client->interface->ntcpaccepting++;
-- UNLOCK(&client->interface->lock);
-+ isc_atomic_xadd(&client->interface->ntcpaccepting, 1);
- }
-
- static void
-@@ -3655,24 +3608,25 @@ ns_client_replace(ns_client_t *client) {
- REQUIRE(client->manager != NULL);
-
- tcp = TCP_CLIENT(client);
-- if (tcp && client->pipelined) {
-+ if (tcp && client->tcpconn != NULL && client->tcpconn->pipelined) {
- result = get_worker(client->manager, client->interface,
- client->tcpsocket, client);
- } else {
- result = get_client(client->manager, client->interface,
-- client->dispatch, client, tcp);
-+ client->dispatch, tcp);
-
-- /*
-- * The responsibility for listening for new requests is hereby
-- * transferred to the new client. Therefore, the old client
-- * should refrain from listening for any more requests.
-- */
-- client->mortal = true;
- }
- if (result != ISC_R_SUCCESS) {
- return (result);
- }
-
-+ /*
-+ * The responsibility for listening for new requests is hereby
-+ * transferred to the new client. Therefore, the old client
-+ * should refrain from listening for any more requests.
-+ */
-+ client->mortal = true;
-+
- return (ISC_R_SUCCESS);
- }
-
-@@ -3806,7 +3760,7 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
-
- static isc_result_t
- get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
-- dns_dispatch_t *disp, ns_client_t *oldclient, bool tcp)
-+ dns_dispatch_t *disp, bool tcp)
- {
- isc_result_t result = ISC_R_SUCCESS;
- isc_event_t *ev;
-@@ -3850,15 +3804,7 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
- client->dscp = ifp->dscp;
-
- if (tcp) {
-- client->tcpattached = false;
-- if (oldclient != NULL) {
-- client->tcpattached = oldclient->tcpattached;
-- }
--
-- LOCK(&client->interface->lock);
-- client->interface->ntcpactive++;
-- UNLOCK(&client->interface->lock);
-- client->tcpactive = true;
-+ mark_tcp_active(client, true);
-
- client->attributes |= NS_CLIENTATTR_TCP;
- isc_socket_attach(ifp->tcpsocket,
-@@ -3923,16 +3869,14 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock,
- ns_interface_attach(ifp, &client->interface);
- client->newstate = client->state = NS_CLIENTSTATE_WORKING;
- INSIST(client->recursionquota == NULL);
-- client->tcpquota = &ns_g_server->tcpquota;
-- client->tcpattached = oldclient->tcpattached;
-
- client->dscp = ifp->dscp;
-
- client->attributes |= NS_CLIENTATTR_TCP;
- client->mortal = true;
-
-- pipeline_attach(oldclient, client);
-- client->pipelined = true;
-+ tcpconn_attach(oldclient, client);
-+ mark_tcp_active(client, true);
-
- isc_socket_attach(ifp->tcpsocket, &client->tcplistener);
- isc_socket_attach(sock, &client->tcpsocket);
-@@ -3940,11 +3884,6 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock,
- (void)isc_socket_getpeername(client->tcpsocket, &client->peeraddr);
- client->peeraddr_valid = true;
-
-- LOCK(&client->interface->lock);
-- client->interface->ntcpactive++;
-- UNLOCK(&client->interface->lock);
-- client->tcpactive = true;
--
- INSIST(client->tcpmsg_valid == false);
- dns_tcpmsg_init(client->mctx, client->tcpsocket, &client->tcpmsg);
- client->tcpmsg_valid = true;
-@@ -3970,8 +3909,7 @@ ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
- MTRACE("createclients");
-
- for (disp = 0; disp < n; disp++) {
-- result = get_client(manager, ifp, ifp->udpdispatch[disp],
-- NULL, tcp);
-+ result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
- if (result != ISC_R_SUCCESS)
- break;
- }
-diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
-index e2c40acd28..969ee4c08f 100644
---- a/bin/named/include/named/client.h
-+++ b/bin/named/include/named/client.h
-@@ -78,6 +78,13 @@
- *** Types
- ***/
-
-+/*% reference-counted TCP connection object */
-+typedef struct ns_tcpconn {
-+ isc_refcount_t refs;
-+ isc_quota_t *tcpquota;
-+ bool pipelined;
-+} ns_tcpconn_t;
-+
- /*% nameserver client structure */
- struct ns_client {
- unsigned int magic;
-@@ -131,10 +138,7 @@ struct ns_client {
- dns_name_t signername; /*%< [T]SIG key name */
- dns_name_t *signer; /*%< NULL if not valid sig */
- bool mortal; /*%< Die after handling request */
-- bool pipelined; /*%< TCP queries not in sequence */
-- isc_refcount_t *pipeline_refs;
-- isc_quota_t *tcpquota;
-- bool tcpattached;
-+ ns_tcpconn_t *tcpconn;
- isc_quota_t *recursionquota;
- ns_interface_t *interface;
-
-diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h
-index 61b08826a6..3535ef22a8 100644
---- a/bin/named/include/named/interfacemgr.h
-+++ b/bin/named/include/named/interfacemgr.h
-@@ -9,8 +9,6 @@
- * information regarding copyright ownership.
- */
-
--/* $Id: interfacemgr.h,v 1.35 2011/07/28 23:47:58 tbox Exp $ */
--
- #ifndef NAMED_INTERFACEMGR_H
- #define NAMED_INTERFACEMGR_H 1
-
-@@ -77,11 +75,11 @@ struct ns_interface {
- /*%< UDP dispatchers. */
- isc_socket_t * tcpsocket; /*%< TCP socket. */
- isc_dscp_t dscp; /*%< "listen-on" DSCP value */
-- int ntcpaccepting; /*%< Number of clients
-+ int32_t ntcpaccepting; /*%< Number of clients
- ready to accept new
- TCP connections on this
- interface */
-- int ntcpactive; /*%< Number of clients
-+ int32_t ntcpactive; /*%< Number of clients
- servicing TCP queries
- (whether accepting or
- connected) */
-diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c
-index 955096ef47..d9f6df5802 100644
---- a/bin/named/interfacemgr.c
-+++ b/bin/named/interfacemgr.c
-@@ -388,6 +388,7 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
- */
- ifp->ntcpaccepting = 0;
- ifp->ntcpactive = 0;
-+
- ifp->nudpdispatch = 0;
-
- ifp->dscp = -1;
-diff --git a/lib/isc/include/isc/quota.h b/lib/isc/include/isc/quota.h
-index b9bf59877a..36c5830242 100644
---- a/lib/isc/include/isc/quota.h
-+++ b/lib/isc/include/isc/quota.h
-@@ -100,6 +100,13 @@ isc_quota_attach(isc_quota_t *quota, isc_quota_t **p);
- * quota if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA).
- */
-
-+isc_result_t
-+isc_quota_force(isc_quota_t *quota, isc_quota_t **p);
-+/*%<
-+ * Like isc_quota_attach, but will attach '*p' to the quota
-+ * even if the hard quota has been exceeded.
-+ */
-+
- void
- isc_quota_detach(isc_quota_t **p);
- /*%<
-diff --git a/lib/isc/quota.c b/lib/isc/quota.c
-index 3ddff0d875..556a61f21d 100644
---- a/lib/isc/quota.c
-+++ b/lib/isc/quota.c
-@@ -74,20 +74,39 @@ isc_quota_release(isc_quota_t *quota) {
- UNLOCK(&quota->lock);
- }
-
--isc_result_t
--isc_quota_attach(isc_quota_t *quota, isc_quota_t **p)
--{
-+static isc_result_t
-+doattach(isc_quota_t *quota, isc_quota_t **p, bool force) {
- isc_result_t result;
-- INSIST(p != NULL && *p == NULL);
-+ REQUIRE(p != NULL && *p == NULL);
-+
- result = isc_quota_reserve(quota);
-- if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA)
-+ if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
-+ *p = quota;
-+ } else if (result == ISC_R_QUOTA && force) {
-+ /* attach anyway */
-+ LOCK(&quota->lock);
-+ quota->used++;
-+ UNLOCK(&quota->lock);
-+
- *p = quota;
-+ result = ISC_R_SUCCESS;
-+ }
-+
- return (result);
- }
-
-+isc_result_t
-+isc_quota_attach(isc_quota_t *quota, isc_quota_t **p) {
-+ return (doattach(quota, p, false));
-+}
-+
-+isc_result_t
-+isc_quota_force(isc_quota_t *quota, isc_quota_t **p) {
-+ return (doattach(quota, p, true));
-+}
-+
- void
--isc_quota_detach(isc_quota_t **p)
--{
-+isc_quota_detach(isc_quota_t **p) {
- INSIST(p != NULL && *p != NULL);
- isc_quota_release(*p);
- *p = NULL;
-diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in
-index a82facec0f..7b9f23d776 100644
---- a/lib/isc/win32/libisc.def.in
-+++ b/lib/isc/win32/libisc.def.in
-@@ -519,6 +519,7 @@ isc_portset_removerange
- isc_quota_attach
- isc_quota_destroy
- isc_quota_detach
-+isc_quota_force
- isc_quota_init
- isc_quota_max
- isc_quota_release
---
-2.20.1
-