summaryrefslogtreecommitdiffstats
path: root/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch')
-rw-r--r--meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch670
1 files changed, 670 insertions, 0 deletions
diff --git a/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch b/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch
new file mode 100644
index 0000000000..ca4e8b1a66
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/0002-tcp-clients-could-still-be-exceeded-v2.patch
@@ -0,0 +1,670 @@
+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/719f604]
+
+Signed-off-by: Kai Kang <kai.kang@windriver.com>
+
+From 719f604e3fad5b7479bd14e2fa0ef4413f0a8fdc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Witold=20Kr=C4=99cicki?= <wpk@isc.org>
+Date: Fri, 4 Jan 2019 12:50:51 +0100
+Subject: [PATCH 2/6] tcp-clients could still be exceeded (v2)
+
+the TCP client quota could still be ineffective under some
+circumstances. this change:
+
+- improves quota accounting to ensure that TCP clients are
+ properly limited, while still guaranteeing that at least one client
+ is always available to serve TCP connections on each interface.
+- uses more descriptive names and removes one (ntcptarget) that
+ was no longer needed
+- adds comments
+
+(cherry picked from commit 924651f1d5e605cd186d03f4f7340bcc54d77cc2)
+(cherry picked from commit 55a7a458e30e47874d34bdf1079eb863a0512396)
+---
+ bin/named/client.c | 311 ++++++++++++++++++++-----
+ bin/named/include/named/client.h | 14 +-
+ bin/named/include/named/interfacemgr.h | 11 +-
+ bin/named/interfacemgr.c | 8 +-
+ 4 files changed, 267 insertions(+), 77 deletions(-)
+
+diff --git a/bin/named/client.c b/bin/named/client.c
+index 0739dd48af..a7b49a0f71 100644
+--- a/bin/named/client.c
++++ b/bin/named/client.c
+@@ -246,10 +246,11 @@ 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, bool tcp);
+ static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp,
+- isc_socket_t *sock);
++ isc_socket_t *sock, ns_client_t *oldclient);
+ static inline bool
+-allowed(isc_netaddr_t *addr, dns_name_t *signer, isc_netaddr_t *ecs_addr,
+- uint8_t ecs_addrlen, uint8_t *ecs_scope, dns_acl_t *acl);
++allowed(isc_netaddr_t *addr, dns_name_t *signer,
++ isc_netaddr_t *ecs_addr, uint8_t ecs_addrlen,
++ uint8_t *ecs_scope, dns_acl_t *acl)
+ static void compute_cookie(ns_client_t *client, uint32_t when,
+ uint32_t nonce, const unsigned char *secret,
+ isc_buffer_t *buf);
+@@ -405,8 +406,11 @@ exit_check(ns_client_t *client) {
+ */
+ INSIST(client->recursionquota == NULL);
+ INSIST(client->newstate <= NS_CLIENTSTATE_READY);
+- if (client->nreads > 0)
++
++ if (client->nreads > 0) {
+ dns_tcpmsg_cancelread(&client->tcpmsg);
++ }
++
+ if (client->nreads != 0) {
+ /* Still waiting for read cancel completion. */
+ return (true);
+@@ -416,25 +420,58 @@ exit_check(ns_client_t *client) {
+ dns_tcpmsg_invalidate(&client->tcpmsg);
+ client->tcpmsg_valid = false;
+ }
++
+ 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;
++ }
+ }
+
+ if (client->tcpquota != NULL) {
+- isc_quota_detach(&client->tcpquota);
+- } else {
+ /*
+- * We went over quota with this client, we don't
+- * want to restart listening unless this is the
+- * last client on this interface, which is
+- * checked later.
++ * If we are not in a pipeline group, or
++ * we are the last client in the group, detach from
++ * tcpquota; otherwise, transfer the quota to
++ * another client in the same group.
+ */
+- if (TCP_CLIENT(client)) {
+- client->mortal = true;
++ if (!ISC_LINK_LINKED(client, glink) ||
++ (client->glink.next == NULL &&
++ client->glink.prev == NULL))
++ {
++ isc_quota_detach(&client->tcpquota);
++ } else if (client->glink.next != NULL) {
++ INSIST(client->glink.next->tcpquota == NULL);
++ client->glink.next->tcpquota = client->tcpquota;
++ client->tcpquota = NULL;
++ } else {
++ INSIST(client->glink.prev->tcpquota == NULL);
++ client->glink.prev->tcpquota = client->tcpquota;
++ client->tcpquota = NULL;
+ }
+ }
+
++ /*
++ * Unlink from pipeline group.
++ */
++ if (ISC_LINK_LINKED(client, glink)) {
++ if (client->glink.next != NULL) {
++ client->glink.next->glink.prev =
++ client->glink.prev;
++ }
++ if (client->glink.prev != NULL) {
++ client->glink.prev->glink.next =
++ client->glink.next;
++ }
++ ISC_LINK_INIT(client, glink);
++ }
++
+ if (client->timerset) {
+ (void)isc_timer_reset(client->timer,
+ isc_timertype_inactive,
+@@ -455,15 +492,16 @@ exit_check(ns_client_t *client) {
+ * 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 TCP clients
+- * may remain active if we have fewer active TCP client
+- * objects than desired due to an earlier quota exhaustion.
++ * UDP clients go inactive at this point, but a TCP client
++ * will needs to remain active if no other clients are
++ * listening for TCP requests on this interface, to
++ * prevent this interface from going nonresponsive.
+ */
+ if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
+ LOCK(&client->interface->lock);
+- if (client->interface->ntcpcurrent <
+- client->interface->ntcptarget)
++ if (client->interface->ntcpaccepting == 0) {
+ client->mortal = false;
++ }
+ UNLOCK(&client->interface->lock);
+ }
+
+@@ -472,15 +510,17 @@ exit_check(ns_client_t *client) {
+ * queue for recycling.
+ */
+ if (client->mortal) {
+- if (client->newstate > NS_CLIENTSTATE_INACTIVE)
++ if (client->newstate > NS_CLIENTSTATE_INACTIVE) {
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
++ }
+ }
+
+ if (NS_CLIENTSTATE_READY == client->newstate) {
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+- } else
++ } else {
+ client_udprecv(client);
++ }
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (true);
+ }
+@@ -492,41 +532,57 @@ exit_check(ns_client_t *client) {
+ /*
+ * We are trying to enter the inactive state.
+ */
+- if (client->naccepts > 0)
++ if (client->naccepts > 0) {
+ isc_socket_cancel(client->tcplistener, client->task,
+ ISC_SOCKCANCEL_ACCEPT);
++ }
+
+ /* Still waiting for accept cancel completion. */
+- if (! (client->naccepts == 0))
++ if (! (client->naccepts == 0)) {
+ return (true);
++ }
+
+ /* Accept cancel is complete. */
+- if (client->nrecvs > 0)
++ if (client->nrecvs > 0) {
+ isc_socket_cancel(client->udpsocket, client->task,
+ ISC_SOCKCANCEL_RECV);
++ }
+
+ /* Still waiting for recv cancel completion. */
+- if (! (client->nrecvs == 0))
++ if (! (client->nrecvs == 0)) {
+ return (true);
++ }
+
+ /* Still waiting for control event to be delivered */
+- if (client->nctls > 0)
++ if (client->nctls > 0) {
+ return (true);
+-
+- /* Deactivate the client. */
+- if (client->interface)
+- ns_interface_detach(&client->interface);
++ }
+
+ INSIST(client->naccepts == 0);
+ INSIST(client->recursionquota == NULL);
+- if (client->tcplistener != NULL)
++ if (client->tcplistener != NULL) {
+ isc_socket_detach(&client->tcplistener);
+
+- if (client->udpsocket != NULL)
++ if (client->tcpactive) {
++ LOCK(&client->interface->lock);
++ INSIST(client->interface->ntcpactive > 0);
++ client->interface->ntcpactive--;
++ UNLOCK(&client->interface->lock);
++ client->tcpactive = false;
++ }
++ }
++ if (client->udpsocket != NULL) {
+ isc_socket_detach(&client->udpsocket);
++ }
+
+- if (client->dispatch != NULL)
++ /* Deactivate the client. */
++ if (client->interface != NULL) {
++ ns_interface_detach(&client->interface);
++ }
++
++ if (client->dispatch != NULL) {
+ dns_dispatch_detach(&client->dispatch);
++ }
+
+ client->attributes = 0;
+ client->mortal = false;
+@@ -551,10 +607,13 @@ exit_check(ns_client_t *client) {
+ client->newstate = NS_CLIENTSTATE_MAX;
+ if (!ns_g_clienttest && manager != NULL &&
+ !manager->exiting)
++ {
+ ISC_QUEUE_PUSH(manager->inactive, client,
+ ilink);
+- if (client->needshutdown)
++ }
++ if (client->needshutdown) {
+ isc_task_shutdown(client->task);
++ }
+ return (true);
+ }
+ }
+@@ -675,7 +734,6 @@ client_start(isc_task_t *task, isc_event_t *event) {
+ }
+ }
+
+-
+ /*%
+ * The client's task has received a shutdown event.
+ */
+@@ -2507,17 +2565,12 @@ client_request(isc_task_t *task, isc_event_t *event) {
+ /*
+ * Pipeline TCP query processing.
+ */
+- if (client->message->opcode != dns_opcode_query)
++ if (client->message->opcode != dns_opcode_query) {
+ client->pipelined = false;
++ }
+ if (TCP_CLIENT(client) && client->pipelined) {
+- result = isc_quota_reserve(&ns_g_server->tcpquota);
+- if (result == ISC_R_SUCCESS)
+- result = ns_client_replace(client);
++ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS) {
+- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+- "no more TCP clients(read): %s",
+- isc_result_totext(result));
+ client->pipelined = false;
+ }
+ }
+@@ -3087,6 +3140,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
+ client->filter_aaaa = dns_aaaa_ok;
+ #endif
+ client->needshutdown = ns_g_clienttest;
++ client->tcpactive = false;
+
+ ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
+ NS_EVENT_CLIENTCONTROL, client_start, client, client,
+@@ -3100,6 +3154,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
+ client->formerrcache.id = 0;
+ ISC_LINK_INIT(client, link);
+ ISC_LINK_INIT(client, rlink);
++ ISC_LINK_INIT(client, glink);
+ ISC_QLINK_INIT(client, ilink);
+ client->keytag = NULL;
+ client->keytag_len = 0;
+@@ -3193,12 +3248,19 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+
+ INSIST(client->state == NS_CLIENTSTATE_READY);
+
++ /*
++ * The accept() was successful and we're now establishing a new
++ * connection. We need to make note of it in the client and
++ * interface objects so client objects can do the right thing
++ * when going inactive in exit_check() (see comments in
++ * client_accept() for details).
++ */
+ INSIST(client->naccepts == 1);
+ client->naccepts--;
+
+ LOCK(&client->interface->lock);
+- INSIST(client->interface->ntcpcurrent > 0);
+- client->interface->ntcpcurrent--;
++ INSIST(client->interface->ntcpaccepting > 0);
++ client->interface->ntcpaccepting--;
+ UNLOCK(&client->interface->lock);
+
+ /*
+@@ -3232,6 +3294,9 @@ 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));
++ if (client->tcpquota != NULL) {
++ isc_quota_detach(&client->tcpquota);
++ }
+ }
+
+ if (exit_check(client))
+@@ -3270,18 +3335,12 @@ client_newconn(isc_task_t *task, isc_event_t *event) {
+ * deny service to legitimate TCP clients.
+ */
+ client->pipelined = false;
+- result = isc_quota_attach(&ns_g_server->tcpquota,
+- &client->tcpquota);
+- if (result == ISC_R_SUCCESS)
+- result = ns_client_replace(client);
+- if (result != ISC_R_SUCCESS) {
+- ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+- NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+- "no more TCP clients(accept): %s",
+- isc_result_totext(result));
+- } else if (ns_g_server->keepresporder == NULL ||
+- !allowed(&netaddr, NULL, NULL, 0, NULL,
+- ns_g_server->keepresporder)) {
++ result = ns_client_replace(client);
++ if (result == ISC_R_SUCCESS &&
++ (client->sctx->keepresporder == NULL ||
++ !allowed(&netaddr, NULL, NULL, 0, NULL,
++ ns_g_server->keepresporder)))
++ {
+ client->pipelined = true;
+ }
+
+@@ -3298,12 +3357,80 @@ 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.
++ */
++ result = isc_quota_attach(&client->sctx->tcpquota,
++ &client->tcpquota);
++ if (result != ISC_R_SUCCESS) {
++ 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 client
++ * is already servicing TCP queries on this
++ * interface (whether accepting, reading, or
++ * processing).
++ *
++ * If so, then it's okay *not* to call
++ * accept - we can let this client to go inactive
++ * and the other one handle the next connection
++ * when it's ready.
++ *
++ * But if not, then we need to be a little bit
++ * flexible about the quota. We allow *one* extra
++ * TCP 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 interfaces.)
++ */
++ LOCK(&client->interface->lock);
++ exit = (client->interface->ntcpactive > 0);
++ UNLOCK(&client->interface->lock);
++
++ if (exit) {
++ client->newstate = NS_CLIENTSTATE_INACTIVE;
++ (void)exit_check(client);
++ return;
++ }
++ }
++
++ /*
++ * 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 (!client->tcpactive) {
++ LOCK(&client->interface->lock);
++ client->interface->ntcpactive++;
++ UNLOCK(&client->interface->lock);
++ client->tcpactive = true;
++ }
++
+ result = isc_socket_accept(client->tcplistener, client->task,
+ client_newconn, client);
+ if (result != ISC_R_SUCCESS) {
+- UNEXPECTED_ERROR(__FILE__, __LINE__,
+- "isc_socket_accept() failed: %s",
+- isc_result_totext(result));
+ /*
+ * XXXRTH What should we do? We're trying to accept but
+ * it didn't work. If we just give up, then TCP
+@@ -3311,12 +3438,39 @@ client_accept(ns_client_t *client) {
+ *
+ * For now, we just go idle.
+ */
++ UNEXPECTED_ERROR(__FILE__, __LINE__,
++ "isc_socket_accept() failed: %s",
++ isc_result_totext(result));
++ if (client->tcpquota != NULL) {
++ isc_quota_detach(&client->tcpquota);
++ }
+ return;
+ }
++
++ /*
++ * The client's 'naccepts' counter indicates that this client has
++ * called accept() and is waiting for a new connection. It should
++ * never exceed 1.
++ */
+ INSIST(client->naccepts == 0);
+ client->naccepts++;
++
++ /*
++ * The interface's 'ntcpaccepting' counter is incremented when
++ * any client calls accept(), and decremented in client_newconn()
++ * once the connection is established.
++ *
++ * When the client object is shutting down after handling a TCP
++ * request (see exit_check()), it looks to see whether this value is
++ * non-zero. If so, that means another client has already called
++ * accept() and is waiting to establish the next connection, which
++ * means the first client is free to go inactive. Otherwise,
++ * the first client must come back and call accept() again; this
++ * guarantees there will always be at least one client listening
++ * for new TCP connections on each interface.
++ */
+ LOCK(&client->interface->lock);
+- client->interface->ntcpcurrent++;
++ client->interface->ntcpaccepting++;
+ UNLOCK(&client->interface->lock);
+ }
+
+@@ -3390,13 +3544,14 @@ ns_client_replace(ns_client_t *client) {
+ tcp = TCP_CLIENT(client);
+ if (tcp && client->pipelined) {
+ result = get_worker(client->manager, client->interface,
+- client->tcpsocket);
++ client->tcpsocket, client);
+ } else {
+ result = get_client(client->manager, client->interface,
+ client->dispatch, tcp);
+ }
+- if (result != ISC_R_SUCCESS)
++ if (result != ISC_R_SUCCESS) {
+ return (result);
++ }
+
+ /*
+ * The responsibility for listening for new requests is hereby
+@@ -3585,6 +3740,7 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ client->attributes |= NS_CLIENTATTR_TCP;
+ isc_socket_attach(ifp->tcpsocket,
+ &client->tcplistener);
++
+ } else {
+ isc_socket_t *sock;
+
+@@ -3602,7 +3758,8 @@ get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ }
+
+ static isc_result_t
+-get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock)
++get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock,
++ ns_client_t *oldclient)
+ {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *ev;
+@@ -3610,6 +3767,7 @@ get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock)
+ MTRACE("get worker");
+
+ REQUIRE(manager != NULL);
++ REQUIRE(oldclient != NULL);
+
+ if (manager->exiting)
+ return (ISC_R_SHUTTINGDOWN);
+@@ -3642,7 +3800,28 @@ 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;
++
++ /*
++ * Transfer TCP quota to the new client.
++ */
++ INSIST(client->tcpquota == NULL);
++ INSIST(oldclient->tcpquota != NULL);
++ client->tcpquota = oldclient->tcpquota;
++ oldclient->tcpquota = NULL;
++
++ /*
++ * Link to a pipeline group, creating it if needed.
++ */
++ if (!ISC_LINK_LINKED(oldclient, glink)) {
++ oldclient->glink.next = NULL;
++ oldclient->glink.prev = NULL;
++ }
++ client->glink.next = oldclient->glink.next;
++ client->glink.prev = oldclient;
++ if (oldclient->glink.next != NULL) {
++ oldclient->glink.next->glink.prev = client;
++ }
++ oldclient->glink.next = client;
+
+ client->dscp = ifp->dscp;
+
+@@ -3656,6 +3835,12 @@ 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;
+diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
+index b23a7b191d..1f7973f9c5 100644
+--- a/bin/named/include/named/client.h
++++ b/bin/named/include/named/client.h
+@@ -94,7 +94,8 @@ struct ns_client {
+ int nupdates;
+ int nctls;
+ int references;
+- bool needshutdown; /*
++ bool tcpactive;
++ bool needshutdown; /*
+ * Used by clienttest to get
+ * the client to go from
+ * inactive to free state
+@@ -130,9 +131,9 @@ struct ns_client {
+ isc_stdtime_t now;
+ isc_time_t tnow;
+ 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 */
++ dns_name_t *signer; /*%< NULL if not valid sig */
++ bool mortal; /*%< Die after handling request */
++ bool pipelined; /*%< TCP queries not in sequence */
+ isc_quota_t *tcpquota;
+ isc_quota_t *recursionquota;
+ ns_interface_t *interface;
+@@ -143,8 +144,8 @@ struct ns_client {
+ isc_sockaddr_t destsockaddr;
+
+ isc_netaddr_t ecs_addr; /*%< EDNS client subnet */
+- uint8_t ecs_addrlen;
+- uint8_t ecs_scope;
++ uint8_t ecs_addrlen;
++ uint8_t ecs_scope;
+
+ struct in6_pktinfo pktinfo;
+ isc_dscp_t dscp;
+@@ -166,6 +167,7 @@ struct ns_client {
+
+ ISC_LINK(ns_client_t) link;
+ ISC_LINK(ns_client_t) rlink;
++ ISC_LINK(ns_client_t) glink;
+ ISC_QLINK(ns_client_t) ilink;
+ unsigned char cookie[8];
+ uint32_t expire;
+diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h
+index 7d1883e1e8..61b08826a6 100644
+--- a/bin/named/include/named/interfacemgr.h
++++ b/bin/named/include/named/interfacemgr.h
+@@ -77,9 +77,14 @@ struct ns_interface {
+ /*%< UDP dispatchers. */
+ isc_socket_t * tcpsocket; /*%< TCP socket. */
+ isc_dscp_t dscp; /*%< "listen-on" DSCP value */
+- int ntcptarget; /*%< Desired number of concurrent
+- TCP accepts */
+- int ntcpcurrent; /*%< Current ditto, locked */
++ int ntcpaccepting; /*%< Number of clients
++ ready to accept new
++ TCP connections on this
++ interface */
++ int ntcpactive; /*%< Number of clients
++ servicing TCP queries
++ (whether accepting or
++ connected) */
+ int nudpdispatch; /*%< Number of UDP dispatches */
+ ns_clientmgr_t * clientmgr; /*%< Client manager. */
+ ISC_LINK(ns_interface_t) link;
+diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c
+index 419927bf54..955096ef47 100644
+--- a/bin/named/interfacemgr.c
++++ b/bin/named/interfacemgr.c
+@@ -386,8 +386,8 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ * connections will be handled in parallel even though there is
+ * only one client initially.
+ */
+- ifp->ntcptarget = 1;
+- ifp->ntcpcurrent = 0;
++ ifp->ntcpaccepting = 0;
++ ifp->ntcpactive = 0;
+ ifp->nudpdispatch = 0;
+
+ ifp->dscp = -1;
+@@ -522,9 +522,7 @@ ns_interface_accepttcp(ns_interface_t *ifp) {
+ */
+ (void)isc_socket_filter(ifp->tcpsocket, "dataready");
+
+- result = ns_clientmgr_createclients(ifp->clientmgr,
+- ifp->ntcptarget, ifp,
+- true);
++ result = ns_clientmgr_createclients(ifp->clientmgr, 1, ifp, true);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "TCP ns_clientmgr_createclients(): %s",
+--
+2.20.1
+