]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
convert ns_client and related objects to use netmgr
authorEvan Hunt <each@isc.org>
Tue, 5 Nov 2019 23:34:35 +0000 (15:34 -0800)
committerEvan Hunt <each@isc.org>
Thu, 7 Nov 2019 19:55:37 +0000 (11:55 -0800)
- ns__client_request() is now called by netmgr with an isc_nmhandle_t
  parameter. The handle can then be permanently associated with an
  ns_client object.
- The task manager is paused so that isc_task events that may be
  triggred during client processing will not fire until after the netmgr is
  finished with it. Before any asynchronous event, the client MUST
  call isc_nmhandle_ref(client->handle), to prevent the client from
  being reset and reused while waiting for an event to process. When
  the asynchronous event is complete, isc_nmhandle_unref(client->handle)
  must be called to ensure the handle can be reused later.
- reference counting of client objects is now handled in the nmhandle
  object.  when the handle references drop to zero, the client's "reset"
  callback is used to free temporary resources and reiniialize it,
  whereupon the handle (and associated client) is placed in the
  "inactive handles" queue.  when the sysstem is shutdown and the
  handles are cleaned up, the client's "put" callback is called to free
  all remaining resources.
- because client allocation is no longer handled in the same way,
  the '-T clienttest' option has now been removed and is no longer
  used by any system tests.
- the unit tests require wrapping the isc_nmhandle_unref() function;
  when LD_WRAP is supported, that is used. otherwise we link a
  libwrap.so interposer library and use that.

53 files changed:
bin/named/include/named/globals.h
bin/named/main.c
bin/named/server.c
bin/tests/system/README
bin/tests/system/additional/ns1/named.args
bin/tests/system/allow-query/ns3/named.args
bin/tests/system/delzone/ns2/named.args
bin/tests/system/dnssec/ns6/named.args
bin/tests/system/dscp/ns1/named.args
bin/tests/system/dscp/ns2/named.args
bin/tests/system/dscp/ns3/named.args
bin/tests/system/dscp/ns4/named.args
bin/tests/system/dscp/ns5/named.args
bin/tests/system/dscp/ns6/named.args
bin/tests/system/dscp/ns7/named.args
bin/tests/system/dupsigs/ns1/named.args
bin/tests/system/fetchlimit/ns3/named.args [deleted file]
bin/tests/system/legacy/ns4/named.args
bin/tests/system/legacy/ns5/named.args
bin/tests/system/legacy/ns6/named.args
bin/tests/system/legacy/ns7/named.args
bin/tests/system/logfileconfig/tests.sh
bin/tests/system/mirror/ns3/named.args
bin/tests/system/mkeys/ns2/named.args
bin/tests/system/mkeys/ns3/named.args
bin/tests/system/mkeys/ns5/named1.args
bin/tests/system/mkeys/ns5/named2.args
bin/tests/system/mkeys/ns6/named.args
bin/tests/system/nsupdate/ns5/named.args
bin/tests/system/nsupdate/ns6/named.args
bin/tests/system/resolver/ns7/named.args [deleted file]
bin/tests/system/rndc/ns6/named.args
bin/tests/system/start.pl
lib/dns/dispatch.c
lib/dns/message.c
lib/ns/client.c
lib/ns/include/ns/client.h
lib/ns/include/ns/interfacemgr.h
lib/ns/include/ns/server.h
lib/ns/interfacemgr.c
lib/ns/notify.c
lib/ns/query.c
lib/ns/tests/Makefile.in
lib/ns/tests/listenlist_test.c
lib/ns/tests/notify_test.c
lib/ns/tests/nstest.c
lib/ns/tests/query_test.c
lib/ns/tests/wrap.c [new file with mode: 0644]
lib/ns/update.c
lib/ns/win32/libns.def
lib/ns/win32/libns.vcxproj.filters
lib/ns/xfrout.c
util/copyrights

index eac0fe18be39e03a28e307046cb091f3f5f0b852..7d5877c8ac4c8bdc29edae5769767e17bf8baa2f 100644 (file)
@@ -19,6 +19,7 @@
 #include <isc/rwlock.h>
 #include <isc/log.h>
 #include <isc/net.h>
+#include <isc/netmgr.h>
 
 #include <isccfg/aclconf.h>
 #include <isccfg/cfg.h>
@@ -62,6 +63,7 @@ EXTERN bool           named_g_run_done        INIT(false);
  */
 EXTERN isc_timermgr_t *                named_g_timermgr        INIT(NULL);
 EXTERN isc_socketmgr_t *       named_g_socketmgr       INIT(NULL);
+EXTERN isc_nm_t *              named_g_nm              INIT(NULL);
 EXTERN cfg_parser_t *          named_g_parser          INIT(NULL);
 EXTERN cfg_parser_t *          named_g_addparser       INIT(NULL);
 EXTERN const char *            named_g_version         INIT(VERSION);
index 21959ee756ec76a35a6ed19a32b4b93feb314618..d707916a7c0b30ff96102361097cc35e8391abd9 100644 (file)
@@ -24,6 +24,7 @@
 #include <isc/file.h>
 #include <isc/hash.h>
 #include <isc/httpd.h>
+#include <isc/netmgr.h>
 #include <isc/os.h>
 #include <isc/platform.h>
 #include <isc/print.h>
@@ -124,7 +125,6 @@ static int          maxudp = 0;
 /*
  * -T options:
  */
-static bool clienttest = false;
 static bool dropedns = false;
 static bool ednsformerr = false;
 static bool ednsnotimp = false;
@@ -622,17 +622,12 @@ parse_T_opt(char *option) {
        /*
         * force the server to behave (or misbehave) in
         * specified ways for testing purposes.
-        *
-        * clienttest: make clients single shot with their
-        *             own memory context.
         * delay=xxxx: delay client responses by xxxx ms to
         *             simulate remote servers.
         * dscp=x:     check that dscp values are as
         *             expected and assert otherwise.
         */
-       if (!strcmp(option, "clienttest")) {
-               clienttest = true;
-       } else if (!strncmp(option, "delay=", 6)) {
+       if (!strncmp(option, "delay=", 6)) {
                delay = atoi(option + 6);
        } else if (!strcmp(option, "dropedns")) {
                dropedns = true;
@@ -897,8 +892,15 @@ create_managers(void) {
                      "using %u UDP listener%s per interface",
                      named_g_udpdisp, named_g_udpdisp == 1 ? "" : "s");
 
-       result = isc_taskmgr_create(named_g_mctx, named_g_cpus, 0, NULL,
-                                   &named_g_taskmgr);
+       named_g_nm = isc_nm_start(named_g_mctx, named_g_cpus);
+       if (named_g_nm == NULL) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                               "isc_nm_start() failed");
+               return (ISC_R_UNEXPECTED);
+       }
+
+       result = isc_taskmgr_create(named_g_mctx, named_g_cpus, 0,
+                                   named_g_nm, &named_g_taskmgr);
        if (result != ISC_R_SUCCESS) {
                UNEXPECTED_ERROR(__FILE__, __LINE__,
                                 "isc_taskmgr_create() failed: %s",
@@ -923,6 +925,7 @@ create_managers(void) {
                return (ISC_R_UNEXPECTED);
        }
        isc_socketmgr_maxudp(named_g_socketmgr, maxudp);
+       isc_nm_maxudp(named_g_nm, maxudp);
        result = isc_socketmgr_getmaxsockets(named_g_socketmgr, &socks);
        if (result == ISC_R_SUCCESS) {
                isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
@@ -941,6 +944,7 @@ destroy_managers(void) {
        isc_taskmgr_destroy(&named_g_taskmgr);
        isc_timermgr_destroy(&named_g_timermgr);
        isc_socketmgr_destroy(&named_g_socketmgr);
+       isc_nm_destroy(&named_g_nm);
 }
 
 static void
@@ -1254,8 +1258,6 @@ setup(void) {
        /*
         * Modify server context according to command line options
         */
-       if (clienttest)
-               ns_server_setoption(sctx, NS_SERVER_CLIENTTEST, true);
        if (disable4)
                ns_server_setoption(sctx, NS_SERVER_DISABLE4, true);
        if (disable6)
index ca216599fff6e1d19204c41830f4547d58bfbe22..bf4f4a0ebc0552c9a17ba761980ba8308d255bed 100644 (file)
@@ -9462,6 +9462,7 @@ run_server(isc_task_t *task, isc_event_t *event) {
        CHECKFATAL(ns_interfacemgr_create(named_g_mctx, server->sctx,
                                          named_g_taskmgr, named_g_timermgr,
                                          named_g_socketmgr,
+                                         named_g_nm,
                                          named_g_dispatchmgr,
                                          server->task, named_g_udpdisp, geoip,
                                          &server->interfacemgr),
@@ -9525,6 +9526,12 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
        UNUSED(task);
        INSIST(task == server->task);
 
+       /*
+        * We need to shutdown the interface before going
+        * exclusive (which would pause the netmgr).
+        */
+       ns_interfacemgr_shutdown(server->interfacemgr);
+
        result = isc_task_beginexclusive(server->task);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
@@ -9582,7 +9589,6 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
        isc_timer_detach(&server->pps_timer);
        isc_timer_detach(&server->tat_timer);
 
-       ns_interfacemgr_shutdown(server->interfacemgr);
        ns_interfacemgr_detach(&server->interfacemgr);
 
        dns_dispatchmgr_destroy(&named_g_dispatchmgr);
index f527e790d411def54206afd504e81e84f092b92d..13286c054e6815b4c24157897c79eba21be50a6b 100644 (file)
@@ -584,10 +584,6 @@ By default, start.pl starts a "named" server with the following options:
                     preventing multiple instances of this named running in this
                     directory (which could possibly interfere with the test).
 
-In addition, start.pl also sets the following undocumented flag:
-
-    -T clienttest   Makes clients single-shot with their own memory context.
-
 All output is sent to a file called "named.run" in the nameserver directory.
 
 The options used to start named can be altered.  There are three ways of doing
@@ -608,9 +604,9 @@ the named command-line arguments.  The rest of the file is ignored.
 
 3. Tweaking the default command line arguments with "-T" options.  This flag is
 used to alter the behavior of BIND for testing and is not documented in the
-ARM.  The "clienttest" option has already been mentioned, but the presence of
-certain files in the "nsN" directory adds flags to the default command line
-(the content of the files is irrelevant - it is only the presence that counts):
+ARM.  The presence of certain files in the "nsN" directory adds flags to
+the default command line (the content of the files is irrelevant - it
+is only the presence that counts):
 
     named.noaa       Appends "-T noaa" to the command line, which causes
                      "named" to never set the AA bit in an answer.
@@ -635,7 +631,6 @@ certain files in the "nsN" directory adds flags to the default command line
                      the additional section if the response is triggered by RPZ
                      rewriting).
 
-
 Starting Other Nameservers
 ---
 In contrast to "named", nameservers written in Perl or Python (whose script
index 39b8c1ae55bbcf9f84c39c902c8a9768bca67897..0db1ead8340947fd76a4600399d68caa55c4fee8 100644 (file)
@@ -1,2 +1,2 @@
 # this server runs named with only one worker thread
--m record,size,mctx -c named.conf -d 99 -D additional-ns1 -X named.lock -g -T clienttest -n 1
+-m record,size,mctx -c named.conf -d 99 -D additional-ns1 -X named.lock -g -n 1
index 0f50735bfd9430f56fbe508da88211b544928052..5dd675d0fd241577ec156dfc1680f743b38e9f16 100644 (file)
@@ -1,2 +1,2 @@
 # this server only has 127.0.0.1 in its localhost/localnets ACLs
--m record,size,mctx -c named.conf -d 99 -D allow-query-ns3 -X named.lock -g -T clienttest -T fixedlocal
+-m record,size,mctx -c named.conf -d 99 -D allow-query-ns3 -X named.lock -g -T fixedlocal
index 12588aa1966b709026f126c8b54f995cdc0b050d..b3028b1d29fbddaf77eda467e0fb8944d3adac59 100644 (file)
@@ -1 +1 @@
--D delzone-ns2 -X named.lock -m record,size,mctx -T clienttest -c named.conf -g -U 4
+-D delzone-ns2 -X named.lock -m record,size,mctx -c named.conf -g -U 4
index f500166416ea73525abe25b1f2305c7f03189a9f..21242f17ff663dcfb1985f6a3699812818800564 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -c named.conf -d 99 -D dnssec-ns6 -X named.lock -g -T nonearest -T clienttest -T tat=1
+-m record,size,mctx -c named.conf -d 99 -D dnssec-ns6 -X named.lock -g -T nonearest -T tat=1
index 248cee7d5575c64a5b83ebead354b0159d0adc9a..4986abce05974d8cfc648bc5e3cceb461a6b3758 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns1 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns1 -X named.lock -g -U 4 -T dscp=46
index 4a205add68a97182ef1462bc8ae88947cf8e7d2a..91635e8c04bc978745425171320b6107644c6da1 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns2 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns2 -X named.lock -g -U 4 -T dscp=46
index cf4a1821d659ba743225c3e6a7195acd3f75f067..ec9b5934dad0c392e30edf5cd67a9409a177f988 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns3 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns3 -X named.lock -g -U 4 -T dscp=46
index 57678fe2fbbaf04adf6ada3ee8696d9d653b6695..6da9eff607509450a9767eb16ecb5b4c6e755aea 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns4 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns4 -X named.lock -g -U 4 -T dscp=46
index 83824885881aac11fff68ead470a5dd7e557f94f..dc556e7e86bfb97dd45844d9da7fd5a18b1b504d 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns5 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns5 -X named.lock -g -U 4 -T dscp=46
index 482dd408708ab1b603a29676a8b1295f4a4bfc8b..c7389076ba458afe44c78feb8dba8a2d4449d4d1 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns6 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns6 -X named.lock -g -U 4 -T dscp=46
index 0528448c11ccccf2ae525f5410df0c2690354fe1..39ccaa4bbf8009dd101532ca9a271833ccdee3a3 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D dscp-ns7 -X named.lock -g -U 4 -T dscp=46
+-m record,size,mctx -c named.conf -d 99 -D dscp-ns7 -X named.lock -g -U 4 -T dscp=46
index c7cab8aa5b60f51c5bf5569a40118dd894c23914..2eed0529a73f813bc8f72cb99aa9cb6956ec89c6 100644 (file)
@@ -1 +1 @@
--D dupsigs-ns1 -X named.lock -m record,size,mctx -T clienttest -c named.conf -d 99 -g -U 4 -T sigvalinsecs
+-D dupsigs-ns1 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4 -T sigvalinsecs
diff --git a/bin/tests/system/fetchlimit/ns3/named.args b/bin/tests/system/fetchlimit/ns3/named.args
deleted file mode 100644 (file)
index 1d7ee74..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# Don't specify '-T clienttest' as it consumes lots of memory with this test
--D fetchlimit-ns3 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4
index 1f11800927055fc262ecf7be4fa916dd850caaf4..24af8c11275be06f0ce5fc4336518d9e92a52f92 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns4 -X named.lock -g -U 4 -T noedns
+-m record,size,mctx -c named.conf -d 99 -D legacy-ns4 -X named.lock -g -U 4 -T noedns
index 54aa083f26189e9ac8ae8deecb82ea216adf7cfb..515e77d0d9c192f0b244f485b9d8963f6af3c33e 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns5 -X named.lock -g -U 4 -T noedns
+-m record,size,mctx -c named.conf -d 99 -D legacy-ns5 -X named.lock -g -U 4 -T noedns
index b9a278ec5715962d6d6d805b68676977300b511a..cdc570f7c8dce2af5a30117e9bd4692596ecaecd 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns6 -X named.lock -g -U 4 -T maxudp512
+-m record,size,mctx -c named.conf -d 99 -D legacy-ns6 -X named.lock -g -U 4 -T maxudp512
index fbe0ebfa1d3e65750cfa22d83756e33a8a1dbba0..2a1a61217bb3f6ab03660f4a7386f268f84d4d00 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D legacy-ns7 -X named.lock -g -U 4 -T maxudp512
+-m record,size,mctx -c named.conf -d 99 -D legacy-ns7 -X named.lock -g -U 4 -T maxudp512
index 1a43b27a3cbcdbe25ca7c6632e8463a6eb1d0b83..67d06d8ad045c1fdc187e769754c19df1152d580 100644 (file)
@@ -36,7 +36,7 @@ DLFILE="named_deflog"
 
 PIDFILE="${THISDIR}/${CONFDIR}/named.pid"
 myRNDC="$RNDC -c ${THISDIR}/${CONFDIR}/rndc.conf"
-myNAMED="$NAMED -c ${THISDIR}/${CONFDIR}/named.conf -m record,size,mctx -T clienttest -T nosyslog -d 99 -D logfileconfig-ns1 -X named.lock -U 4"
+myNAMED="$NAMED -c ${THISDIR}/${CONFDIR}/named.conf -m record,size,mctx -T nosyslog -d 99 -D logfileconfig-ns1 -X named.lock -U 4"
 
 # Test given condition.  If true, test again after a second.  Used for testing
 # filesystem-dependent conditions in order to prevent false negatives caused by
index 5330759bd42b1711a3fc2121fee5ca406a740470..be1cb49ce3dcbcbc493c5c0aa258ce276fe2f42b 100644 (file)
@@ -1 +1 @@
--D mirror-ns3 -X named.lock -m record,size,mctx -T clienttest -c named.conf -d 99 -g -U 4 -T tat=3
+-D mirror-ns3 -X named.lock -m record,size,mctx -c named.conf -d 99 -g -U 4 -T tat=3
index 614243233d4c0fc8e72ea7946fa4edbb3889a9d3..9f4ad4e46b59c015e3a46093017538acc5702431 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D mkeys-ns2 -X named.lock -g -T mkeytimers=5/10/20 -T tat=1
+-m record,size,mctx -c named.conf -d 99 -D mkeys-ns2 -X named.lock -g -T mkeytimers=5/10/20 -T tat=1
index b8fb00856239d6f90c65978b70bc31a849efa3b2..376aa253cf0c73e3e134b78f10eee65277f27be0 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -D mkeys-ns3 -X named.lock -g -T mkeytimers=5/10/20
+-m record,size,mctx -c named.conf -d 99 -D mkeys-ns3 -X named.lock -g -T mkeytimers=5/10/20
index efb102a4bae6d0989399b7a024ee7306d3e8476f..2e6aadc2c1bf15dfd388bdccae4833ae25afd94c 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g
+-m record,size,mctx -c named.conf -d 99 -X named.lock -g
index d222b7faea7e49228501e7bf6206f4e0a1efb175..3eaf260cff9f516bbd2e71e1e5875116053c0a0e 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40
+-m record,size,mctx -c named.conf -d 99 -X named.lock -g -T mkeytimers=2/20/40
index 02f8f670f69e0e167604cf3d65277b421a02f3bd..74ea7e0a813d27d72cbabe2e1a0ce0be9e867eb0 100644 (file)
@@ -1 +1 @@
--m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -T mkeytimers=5/10/20
+-m record,size,mctx -c named.conf -d 99 -X named.lock -g -T mkeytimers=5/10/20
index 49cb45ebe6b48dfa8136c3a6807cfc0e1553efa2..6555b33a530c9cf2423bc5f4b66aaff652b2daaf 100644 (file)
@@ -1 +1 @@
--D nsupdate-ns5 -m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
+-D nsupdate-ns5 -m record,size,mctx -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
index 75ca52915e09e15eea9dc682c1299d18b6f1caf6..827afb99487be0e28b8eb462a072fb70bd5c0301 100644 (file)
@@ -1 +1 @@
--D nsupdate-ns6 -m record,size,mctx -T clienttest -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
+-D nsupdate-ns6 -m record,size,mctx -c named.conf -d 99 -X named.lock -g -U 4 -T fixedlocal
diff --git a/bin/tests/system/resolver/ns7/named.args b/bin/tests/system/resolver/ns7/named.args
deleted file mode 100644 (file)
index 1e4a9bf..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# this server runs named with the "-T clienttest" option omitted
--m record,size,mctx -c named.conf -d 99 -D resolver-ns7 -X named.lock -g
index 479c740f814b0da80fff13927d3aa69de57bfc53..e876eb89305246423ee7bb8b816f5c87fc58b4f3 100644 (file)
@@ -1,3 +1,3 @@
 # teardown of a huge zone with tracing enabled takes way too long 
 # -m none is set so that stop.pl does not timeout
--D rndc-ns6 -X named.lock -m none -T clienttest -c named.conf -d 99 -g -U 4
+-D rndc-ns6 -X named.lock -m none -c named.conf -d 99 -g -U 4
index 869c1f0eb29d9266ccf0c9754b022d03d47ed3ca..1ecbd70f22694d8acde339e310ed41b9019306c8 100755 (executable)
@@ -257,7 +257,6 @@ sub construct_ns_command {
                $command .= "-D $test-$server ";
                $command .= "-X named.lock ";
                $command .= "-m record,size,mctx ";
-               $command .= "-T clienttest ";
 
                foreach my $t_option(
                        "dropedns", "ednsformerr", "ednsnotimp", "ednsrefused",
index c955d216dc780f2b2237d44574e92b5d3f78c81f..a3e28f3e12d38e366da11ef4c06014db9fab62ed 100644 (file)
@@ -598,7 +598,8 @@ deref_portentry(dns_dispatch_t *disp, dispportentry_t **portentryp) {
        dns_qid_t *qid;
 
        REQUIRE(disp->port_table != NULL);
-       REQUIRE(portentry != NULL && isc_refcount_current(&portentry->refs) > 0);
+       REQUIRE(portentry != NULL &&
+               isc_refcount_current(&portentry->refs) > 0);
 
        if (isc_refcount_decrement(&portentry->refs) == 1) {
                qid = DNS_QID(disp);
index 9c6292a9a38f0b940075da75366d7e702f84f770..31321b86444ff9506b7321b8c883821942055ae9 100644 (file)
@@ -1103,7 +1103,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
                        result = ISC_R_NOMEMORY;
                        goto cleanup;
                }
-               rdataset =  isc_mempool_get(msg->rdspool);
+               rdataset = isc_mempool_get(msg->rdspool);
                if (rdataset == NULL) {
                        result = ISC_R_NOMEMORY;
                        goto cleanup;
index ceccec6e0e7a76470e44bc664c25dc49d7f8b4e8..9104c3809ea58af6c390f8288f9a892d21e6f548 100644 (file)
@@ -54,6 +54,7 @@
 #include <dns/view.h>
 #include <dns/zone.h>
 
+#include <ns/client.h>
 #include <ns/interfacemgr.h>
 #include <ns/log.h>
 #include <ns/notify.h>
 
 #define TCP_CLIENT(c)  (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
 
-#define TCP_BUFFER_SIZE                        (65535 + 2)
-#define SEND_BUFFER_SIZE               4096
-#define RECV_BUFFER_SIZE               4096
-
-#define NMCTXS                         100
-/*%<
- * Number of 'mctx pools' for clients. (Should this be configurable?)
- * When enabling threads, we use a pool of memory contexts shared by
- * client objects, since concurrent access to a shared context would cause
- * heavy contentions.  The above constant is expected to be enough for
- * completely avoiding contentions among threads for an authoritative-only
- * server.
- */
-
 #define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */
 #define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
 
 #define WANTPAD(x) (((x)->attributes & NS_CLIENTATTR_WANTPAD) != 0)
 #define USEKEEPALIVE(x) (((x)->attributes & NS_CLIENTATTR_USEKEEPALIVE) != 0)
 
-/*% nameserver client manager structure */
-struct ns_clientmgr {
-       /* Unlocked. */
-       unsigned int                    magic;
-
-       /* The queue object has its own locks */
-       client_queue_t                  inactive;     /*%< To be recycled */
-
-       isc_mem_t *                     mctx;
-       ns_server_t *                   sctx;
-       isc_taskmgr_t *                 taskmgr;
-       isc_timermgr_t *                timermgr;
-       isc_task_t *                    excl;
-
-       /* Lock covers manager state. */
-       isc_mutex_t                     lock;
-       bool                    exiting;
-
-       /* Lock covers the clients list */
-       isc_mutex_t                     listlock;
-       client_list_t                   clients;      /*%< All active clients */
-
-       /* Lock covers the recursing list */
-       isc_mutex_t                     reclock;
-       client_list_t                   recursing;    /*%< Recursing clients */
-
-#if NMCTXS > 0
-       /*%< mctx pool for clients. */
-       unsigned int                    nextmctx;
-       isc_mem_t *                     mctxpool[NMCTXS];
-#endif
-};
-
 #define MANAGER_MAGIC                  ISC_MAGIC('N', 'S', 'C', 'm')
 #define VALID_MANAGER(m)               ISC_MAGIC_VALID(m, MANAGER_MAGIC)
 
-/*!
- * Client object states.  Ordering is significant: higher-numbered
- * states are generally "more active", meaning that the client can
- * have more dynamically allocated data, outstanding events, etc.
- * In the list below, any such properties listed for state N
- * also apply to any state > N.
- *
- * To force the client into a less active state, set client->newstate
- * to that state and call exit_check().  This will cause any
- * activities defined for higher-numbered states to be aborted.
- */
-
-#define NS_CLIENTSTATE_FREED    0
-/*%<
- * The client object no longer exists.
- */
-
-#define NS_CLIENTSTATE_INACTIVE 1
-/*%<
- * The client object exists and has a task and timer.
- * Its "query" struct and sendbuf are initialized.
- * It is on the client manager's list of inactive clients.
- * It has a message and OPT, both in the reset state.
- */
-
-#define NS_CLIENTSTATE_READY    2
-/*%<
- * The client object is either a TCP or a UDP one, and
- * it is associated with a network interface.  It is on the
- * client manager's list of active clients.
- *
- * If it is a TCP client object, it has a TCP listener socket
- * and an outstanding TCP listen request.
- *
- * If it is a UDP client object, it has a UDP listener socket
- * and an outstanding UDP receive request.
- */
-
-#define NS_CLIENTSTATE_READING  3
-/*%<
- * The client object is a TCP client object that has received
- * a connection.  It has a tcpsocket, tcpmsg, TCP quota, and an
- * outstanding TCP read request.  This state is not used for
- * UDP client objects.
- */
-
-#define NS_CLIENTSTATE_WORKING  4
-/*%<
- * The client object has received a request and is working
- * on it.  It has a view, and it may have any of a non-reset OPT,
- * recursion quota, and an outstanding write request.
- */
-
-#define NS_CLIENTSTATE_RECURSING  5
-/*%<
- * The client object is recursing.  It will be on the 'recursing'
- * list.
- */
-
-#define NS_CLIENTSTATE_MAX      9
-/*%<
- * Sentinel value used to indicate "no state".  When client->newstate
- * has this value, we are not attempting to exit the current state.
- * Must be greater than any valid state.
- */
-
 /*
  * Enable ns_client_dropport() by default.
  */
@@ -230,22 +118,16 @@ struct ns_clientmgr {
 
 LIBNS_EXTERNAL_DATA unsigned int ns_client_requests;
 
-static void read_settimeout(ns_client_t *client, bool newconn);
-static void client_read(ns_client_t *client, bool newconn);
-static void client_accept(ns_client_t *client);
-static void client_udprecv(ns_client_t *client);
+static void clientmgr_attach(ns_clientmgr_t *source, ns_clientmgr_t **targetp);
+static void clientmgr_detach(ns_clientmgr_t **mp);
 static void clientmgr_destroy(ns_clientmgr_t *manager);
-static bool exit_check(ns_client_t *client);
 static void ns_client_endrequest(ns_client_t *client);
-static void client_start(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, 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 void compute_cookie(ns_client_t *client, uint32_t when,
                           uint32_t nonce, const unsigned char *secret,
                           isc_buffer_t *buf);
+static void
+get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp);
 
 void
 ns_client_recursing(ns_client_t *client) {
@@ -253,7 +135,7 @@ ns_client_recursing(ns_client_t *client) {
        REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
 
        LOCK(&client->manager->reclock);
-       client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
+       client->state = NS_CLIENTSTATE_RECURSING;
        ISC_LIST_APPEND(client->manager->recursing, client, rlink);
        UNLOCK(&client->manager->reclock);
 }
@@ -275,596 +157,9 @@ ns_client_killoldestquery(ns_client_t *client) {
 
 void
 ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
-       isc_result_t result;
-       isc_interval_t interval;
-
-       isc_interval_set(&interval, seconds, 0);
-       result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
-                                &interval, false);
-       client->timerset = true;
-       if (result != ISC_R_SUCCESS) {
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
-                             "setting timeout: %s",
-                             isc_result_totext(result));
-               /* Continue anyway. */
-       }
-}
-
-static void
-read_settimeout(ns_client_t *client, bool newconn) {
-       isc_result_t result;
-       isc_interval_t interval;
-       unsigned int ds;
-
-       if (newconn)
-               ds = client->sctx->initialtimo;
-       else if (USEKEEPALIVE(client))
-               ds = client->sctx->keepalivetimo;
-       else
-               ds = client->sctx->idletimo;
-
-       isc_interval_set(&interval, ds / 10, 100000000 * (ds % 10));
-       result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
-                                &interval, false);
-       client->timerset = true;
-       if (result != ISC_R_SUCCESS) {
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
-                             "setting timeout: %s",
-                             isc_result_totext(result));
-               /* Continue anyway. */
-       }
-}
-
-/*%
- * 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 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->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(&client->sctx->tcpquota, &quota);
-       } else {
-               result = isc_quota_attach(&client->sctx->tcpquota, &quota);
-       }
-       if (result != ISC_R_SUCCESS) {
-               return (result);
-       }
-
-       /*
-        * A global memory context is used for the allocation as different
-        * client structures may have different memory contexts assigned and a
-        * reference counter allocated here might need to be freed by a
-        * different client.  The performance impact caused by memory context
-        * contention here is expected to be negligible, given that this code
-        * is only executed for TCP connections.
-        */
-       tconn = isc_mem_allocate(client->sctx->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 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
-tcpconn_attach(ns_client_t *source, ns_client_t *target) {
-       int old_refs;
-
-       REQUIRE(source->tcpconn != NULL);
-       REQUIRE(target->tcpconn == NULL);
-       REQUIRE(source->tcpconn->pipelined);
-
-       old_refs = isc_refcount_increment(&source->tcpconn->refs);
-       INSIST(old_refs > 0);
-       target->tcpconn = source->tcpconn;
-}
-
-/*%
- * 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, we detach from the TCP quota and free the tcpconn
- * object. Either way, client->tcpconn is set to NULL.
- */
-static void
-tcpconn_detach(ns_client_t *client) {
-       ns_tcpconn_t *tconn = NULL;
-       int old_refs;
-
-       REQUIRE(client->tcpconn != NULL);
-
-       tconn = client->tcpconn;
-       client->tcpconn = NULL;
-
-       old_refs = isc_refcount_decrement(&tconn->refs);
-       INSIST(old_refs > 0);
-
-       if (old_refs == 1) {
-               isc_quota_detach(&tconn->tcpquota);
-               isc_mem_free(client->sctx->mctx, tconn);
-       }
-}
-
-/*%
- * 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.
- *
- * 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
-mark_tcp_active(ns_client_t *client, bool active) {
-       if (active && !client->tcpactive) {
-               isc_refcount_increment0(&client->interface->ntcpactive);
-               client->tcpactive = active;
-       } else if (!active && client->tcpactive) {
-               uint32_t old =
-                       isc_refcount_decrement(&client->interface->ntcpactive);
-               INSIST(old > 0);
-               client->tcpactive = active;
-       }
-}
-
-/*%
- * Check for a deactivation or shutdown request and take appropriate
- * action.  Returns true if either is in progress; in this case
- * the caller must no longer use the client object as it may have been
- * freed.
- */
-static bool
-exit_check(ns_client_t *client) {
-       bool destroy_manager = false;
-       ns_clientmgr_t *manager = NULL;
-
-       REQUIRE(NS_CLIENT_VALID(client));
-       manager = client->manager;
-
-       if (client->state <= client->newstate)
-               return (false); /* Business as usual. */
-
-       INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
-
-       /*
-        * We need to detach from the view early when shutting down
-        * the server to break the following vicious circle:
-        *
-        *  - The resolver will not shut down until the view refcount is zero
-        *  - The view refcount does not go to zero until all clients detach
-        *  - The client does not detach from the view until references is zero
-        *  - references does not go to zero until the resolver has shut down
-        *
-        * Keep the view attached until any outstanding updates complete.
-        */
-       if (client->nupdates == 0 &&
-           client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
-               dns_view_detach(&client->view);
-
-       if (client->state == NS_CLIENTSTATE_WORKING ||
-           client->state == NS_CLIENTSTATE_RECURSING)
-       {
-               INSIST(client->newstate <= NS_CLIENTSTATE_READING);
-               /*
-                * Let the update processing complete.
-                */
-               if (client->nupdates > 0)
-                       return (true);
-
-               /*
-                * We are trying to abort request processing.
-                */
-               if (client->nsends > 0) {
-                       isc_socket_t *sock;
-                       if (TCP_CLIENT(client))
-                               sock = client->tcpsocket;
-                       else
-                               sock = client->udpsocket;
-                       isc_socket_cancel(sock, client->task,
-                                         ISC_SOCKCANCEL_SEND);
-               }
-
-               if (! (client->nsends == 0 && client->nrecvs == 0 &&
-                      isc_refcount_current(&client->references) == 0))
-               {
-                       /*
-                        * Still waiting for I/O cancel completion.
-                        * or lingering references.
-                        */
-                       return (true);
-               }
-
-               /*
-                * I/O cancel is complete.  Burn down all state
-                * related to the current request.  Ensure that
-                * the client is no longer on the recursing list.
-                *
-                * We need to check whether the client is still linked,
-                * because it may already have been removed from the
-                * recursing list by ns_client_killoldestquery()
-                */
-               if (client->state == NS_CLIENTSTATE_RECURSING) {
-                       LOCK(&manager->reclock);
-                       if (ISC_LINK_LINKED(client, rlink))
-                               ISC_LIST_UNLINK(manager->recursing,
-                                               client, rlink);
-                       UNLOCK(&manager->reclock);
-               }
-               ns_client_endrequest(client);
-
-               client->state = NS_CLIENTSTATE_READING;
-               INSIST(client->recursionquota == NULL);
-
-               if (NS_CLIENTSTATE_READING == client->newstate) {
-                       INSIST(client->tcpconn != NULL);
-                       if (!client->tcpconn->pipelined) {
-                               client_read(client, false);
-                               client->newstate = NS_CLIENTSTATE_MAX;
-                               return (true); /* We're done. */
-                       } else if (client->mortal) {
-                               client->newstate = NS_CLIENTSTATE_INACTIVE;
-                       } else
-                               return (false);
-               }
-       }
-
-       if (client->state == NS_CLIENTSTATE_READING) {
-               /*
-                * We are trying to abort the current TCP connection,
-                * if any.
-                */
-               INSIST(client->recursionquota == NULL);
-               INSIST(client->newstate <= NS_CLIENTSTATE_READY);
-
-               if (client->nreads > 0) {
-                       dns_tcpmsg_cancelread(&client->tcpmsg);
-                       /* Still waiting for read cancel completion? */
-                       if (client->nreads > 0) {
-                               return (true);
-                       }
-               }
-
-               if (client->tcpmsg_valid) {
-                       dns_tcpmsg_invalidate(&client->tcpmsg);
-                       client->tcpmsg_valid = false;
-               }
-
-               /*
-                * 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.
-                *
-                * 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.
-                *
-                * 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->mortal && TCP_CLIENT(client) &&
-                   client->newstate != NS_CLIENTSTATE_FREED &&
-                   (client->sctx->options & NS_SERVER_CLIENTTEST) == 0 &&
-                   isc_refcount_current(&client->interface->ntcpaccepting) == 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);
-                       mark_tcp_active(client, false);
-               }
-
-               if (client->timerset) {
-                       (void)isc_timer_reset(client->timer,
-                                             isc_timertype_inactive,
-                                             NULL, NULL, true);
-                       client->timerset = false;
-               }
-
-               client->peeraddr_valid = false;
-
-               client->state = NS_CLIENTSTATE_READY;
-
-               /*
-                * We don't need the client; send it to the inactive
-                * queue for recycling.
-                */
-               if (client->mortal) {
-                       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 {
-                               client_udprecv(client);
-                       }
-                       client->newstate = NS_CLIENTSTATE_MAX;
-                       return (true);
-               }
-       }
-
-       if (client->state == NS_CLIENTSTATE_READY) {
-               INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
-
-               /*
-                * We are trying to enter the inactive state.
-                */
-               if (client->naccepts > 0) {
-                       isc_socket_cancel(client->tcplistener, client->task,
-                                         ISC_SOCKCANCEL_ACCEPT);
-                       /* Still waiting for accept cancel completion? */
-                       if (client->naccepts > 0) {
-                               return (true);
-                       }
-               }
-
-               /* Accept cancel is complete. */
-               if (client->nrecvs > 0) {
-                       isc_socket_cancel(client->udpsocket, client->task,
-                                         ISC_SOCKCANCEL_RECV);
-                       /* Still waiting for recv cancel completion? */
-                       if (client->nrecvs > 0) {
-                               return (true);
-                       }
-               }
-
-               /* Still waiting for control event to be delivered */
-               if (client->nctls > 0) {
-                       return (true);
-               }
-
-               INSIST(client->naccepts == 0);
-               INSIST(client->recursionquota == NULL);
-               if (client->tcplistener != NULL) {
-                       isc_socket_detach(&client->tcplistener);
-                       mark_tcp_active(client, false);
-               }
-               if (client->udpsocket != NULL) {
-                       isc_socket_detach(&client->udpsocket);
-               }
-
-               /* 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;
-               client->sendcb = NULL;
-
-               if (client->keytag != NULL) {
-                       isc_mem_put(client->mctx, client->keytag,
-                                   client->keytag_len);
-                       client->keytag_len = 0;
-               }
-
-               /*
-                * Put the client on the inactive list.  If we are aiming for
-                * the "freed" state, it will be removed from the inactive
-                * list shortly, and we need to keep the manager locked until
-                * that has been done, lest the manager decide to reactivate
-                * the dying client inbetween.
-                */
-               client->state = NS_CLIENTSTATE_INACTIVE;
-               INSIST(client->recursionquota == NULL);
-
-               if (client->state == client->newstate) {
-                       client->newstate = NS_CLIENTSTATE_MAX;
-                       if ((client->sctx->options &
-                            NS_SERVER_CLIENTTEST) == 0 &&
-                           manager != NULL && !manager->exiting)
-                       {
-                               ISC_QUEUE_PUSH(manager->inactive, client,
-                                              ilink);
-                       }
-                       if (client->needshutdown) {
-                               isc_task_shutdown(client->task);
-                       }
-                       return (true);
-               }
-       }
-
-       if (client->state == NS_CLIENTSTATE_INACTIVE) {
-               INSIST(client->newstate == NS_CLIENTSTATE_FREED);
-               /*
-                * We are trying to free the client.
-                *
-                * When "shuttingdown" is true, either the task has received
-                * its shutdown event or no shutdown event has ever been
-                * set up.  Thus, we have no outstanding shutdown
-                * event at this point.
-                */
-               REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
-
-               INSIST(client->recursionquota == NULL);
-               INSIST(!ISC_QLINK_LINKED(client, ilink));
-
-               if (manager != NULL) {
-                       LOCK(&manager->listlock);
-                       ISC_LIST_UNLINK(manager->clients, client, link);
-                       LOCK(&manager->lock);
-                       if (manager->exiting &&
-                           ISC_LIST_EMPTY(manager->clients))
-                               destroy_manager = true;
-                       UNLOCK(&manager->lock);
-                       UNLOCK(&manager->listlock);
-               }
-
-               ns_query_free(client);
-               isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
-               isc_event_free((isc_event_t **)&client->sendevent);
-               isc_event_free((isc_event_t **)&client->recvevent);
-               isc_timer_detach(&client->timer);
-               if (client->delaytimer != NULL)
-                       isc_timer_detach(&client->delaytimer);
-
-               if (client->tcpbuf != NULL)
-                       isc_mem_put(client->mctx, client->tcpbuf,
-                                   TCP_BUFFER_SIZE);
-               if (client->opt != NULL) {
-                       INSIST(dns_rdataset_isassociated(client->opt));
-                       dns_rdataset_disassociate(client->opt);
-                       dns_message_puttemprdataset(client->message,
-                                                   &client->opt);
-               }
-               if (client->keytag != NULL) {
-                       isc_mem_put(client->mctx, client->keytag,
-                                   client->keytag_len);
-                       client->keytag_len = 0;
-               }
-
-               dns_message_destroy(&client->message);
-
-               /*
-                * Detaching the task must be done after unlinking from
-                * the manager's lists because the manager accesses
-                * client->task.
-                */
-               if (client->task != NULL)
-                       isc_task_detach(&client->task);
-
-               CTRACE("free");
-               client->magic = 0;
-
-               /*
-                * Check that there are no other external references to
-                * the memory context.
-                */
-               if ((client->sctx->options & NS_SERVER_CLIENTTEST) != 0 &&
-                   isc_mem_references(client->mctx) != 1)
-               {
-                       isc_mem_stats(client->mctx, stderr);
-                       INSIST(0);
-                       ISC_UNREACHABLE();
-               }
-
-               /*
-                * Destroy the fetchlock mutex that was created in
-                * ns_query_init().
-                */
-               isc_mutex_destroy(&client->query.fetchlock);
-
-               if (client->sctx != NULL)
-                       ns_server_detach(&client->sctx);
-
-               isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
-       }
-
-       if (destroy_manager && manager != NULL)
-               clientmgr_destroy(manager);
-
-       return (true);
-}
-
-/*%
- * The client's task has received the client's control event
- * as part of the startup process.
- */
-static void
-client_start(isc_task_t *task, isc_event_t *event) {
-       ns_client_t *client = (ns_client_t *) event->ev_arg;
-
-       INSIST(task == client->task);
-
-       UNUSED(task);
-
-       INSIST(client->nctls == 1);
-       client->nctls--;
-
-       if (exit_check(client))
-               return;
-
-       if (TCP_CLIENT(client)) {
-               if (client->tcpconn != NULL) {
-                       client_read(client, false);
-               } else {
-                       client_accept(client);
-               }
-       } else {
-               client_udprecv(client);
-       }
-}
-
-/*%
- * The client's task has received a shutdown event.
- */
-static void
-client_shutdown(isc_task_t *task, isc_event_t *event) {
-       ns_client_t *client;
-
-       REQUIRE(event != NULL);
-       REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
-       client = event->ev_arg;
-       REQUIRE(NS_CLIENT_VALID(client));
-       REQUIRE(task == client->task);
-
-       UNUSED(task);
-
-       CTRACE("shutdown");
-
-       isc_event_free(&event);
-
-       if (client->shutdown != NULL) {
-               (client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
-               client->shutdown = NULL;
-               client->shutdown_arg = NULL;
-       }
-
-       if (ISC_QLINK_LINKED(client, ilink))
-               ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);
-
-       client->newstate = NS_CLIENTSTATE_FREED;
-       client->needshutdown = false;
-       (void)exit_check(client);
+       UNUSED(client);
+       UNUSED(seconds);
+       /* XXXWPK TODO use netmgr to set timeout */
 }
 
 static void
@@ -879,9 +174,15 @@ ns_client_endrequest(ns_client_t *client) {
 
        CTRACE("endrequest");
 
-       if (client->next != NULL) {
-               (client->next)(client);
-               client->next = NULL;
+       LOCK(&client->manager->reclock);
+       if (ISC_LINK_LINKED(client, rlink)) {
+               ISC_LIST_UNLINK(client->manager->recursing, client, rlink);
+       }
+       UNLOCK(&client->manager->reclock);
+
+       if (client->cleanup != NULL) {
+               (client->cleanup)(client);
+               client->cleanup = NULL;
        }
 
        if (client->view != NULL) {
@@ -906,17 +207,22 @@ ns_client_endrequest(ns_client_t *client) {
        dns_ecs_init(&client->ecs);
        dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
 
+       /*
+        * Clean up from recursion - normally this would be done in
+        * fetch_callback(), but if we're shutting down and canceling then
+        * it might not have happened.
+        */
        if (client->recursionquota != NULL) {
                isc_quota_detach(&client->recursionquota);
                ns_stats_decrement(client->sctx->nsstats,
                                   ns_statscounter_recursclients);
        }
 
+
        /*
-        * Clear all client attributes that are specific to
-        * the request; that's all except the TCP flag.
+        * Clear all client attributes that are specific to the request
         */
-       client->attributes &= NS_CLIENTATTR_TCP;
+       client->attributes = 0;
 #ifdef ENABLE_AFL
        if (client->sctx->fuzznotify != NULL &&
            (client->sctx->fuzztype == isc_fuzz_client ||
@@ -926,73 +232,36 @@ ns_client_endrequest(ns_client_t *client) {
                client->sctx->fuzznotify();
        }
 #endif /* ENABLE_AFL */
-
 }
 
 void
-ns_client_next(ns_client_t *client, isc_result_t result) {
-       int newstate;
-
+ns_client_drop(ns_client_t *client, isc_result_t result) {
        REQUIRE(NS_CLIENT_VALID(client));
        REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
-               client->state == NS_CLIENTSTATE_RECURSING ||
-               client->state == NS_CLIENTSTATE_READING);
-
-       CTRACE("next");
+               client->state == NS_CLIENTSTATE_RECURSING);
 
-       if (result != ISC_R_SUCCESS)
+       CTRACE("drop");
+       if (result != ISC_R_SUCCESS) {
                ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
                              NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
                              "request failed: %s", isc_result_totext(result));
-
-       /*
-        * An error processing a TCP request may have left
-        * the connection out of sync.  To be safe, we always
-        * sever the connection when result != ISC_R_SUCCESS.
-        */
-       if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
-               newstate = NS_CLIENTSTATE_READING;
-       else
-               newstate = NS_CLIENTSTATE_READY;
-
-       if (client->newstate > newstate)
-               client->newstate = newstate;
-       (void)exit_check(client);
+       }
 }
 
-
 static void
-client_senddone(isc_task_t *task, isc_event_t *event) {
-       ns_client_t *client;
-       isc_socketevent_t *sevent = (isc_socketevent_t *) event;
+client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+       ns_client_t *client = cbarg;
 
-       REQUIRE(sevent != NULL);
-       REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
-       client = sevent->ev_arg;
-       REQUIRE(NS_CLIENT_VALID(client));
-       REQUIRE(task == client->task);
-       REQUIRE(sevent == client->sendevent);
-
-       UNUSED(task);
+       REQUIRE(client->handle == handle);
 
        CTRACE("senddone");
-
-       if (sevent->result != ISC_R_SUCCESS)
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
-                             "error sending response: %s",
-                             isc_result_totext(sevent->result));
-
-       INSIST(client->nsends > 0);
-       client->nsends--;
-
-       if (client->tcpbuf != NULL) {
-               INSIST(TCP_CLIENT(client));
-               isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
-               client->tcpbuf = NULL;
+       if (result != ISC_R_SUCCESS) {
+               ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                             "send failed: %s", isc_result_totext(result));
        }
 
-       ns_client_next(client, ISC_R_SUCCESS);
+       isc_nmhandle_unref(handle);
 }
 
 /*%
@@ -1006,34 +275,37 @@ client_senddone(isc_task_t *task, isc_event_t *event) {
 static isc_result_t
 client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
                    isc_buffer_t *tcpbuffer, uint32_t length,
-                   unsigned char *sendbuf, unsigned char **datap)
+                   unsigned char **datap)
 {
        unsigned char *data;
        uint32_t bufsize;
        isc_result_t result;
 
-       INSIST(datap != NULL);
-       INSIST((tcpbuffer == NULL && length != 0) ||
-              (tcpbuffer != NULL && length == 0));
+       REQUIRE(datap != NULL);
+       REQUIRE((tcpbuffer == NULL && length != 0) ||
+               (tcpbuffer != NULL && length == 0));
 
        if (TCP_CLIENT(client)) {
                INSIST(client->tcpbuf == NULL);
-               if (length + 2 > TCP_BUFFER_SIZE) {
+               if (length + 2 > NS_CLIENT_TCP_BUFFER_SIZE) {
                        result = ISC_R_NOSPACE;
                        goto done;
                }
-               client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
+               client->tcpbuf = isc_mem_get(client->mctx,
+                                            NS_CLIENT_TCP_BUFFER_SIZE);
                data = client->tcpbuf;
                if (tcpbuffer != NULL) {
-                       isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
-                       isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
+                       isc_buffer_init(tcpbuffer, data,
+                                       NS_CLIENT_TCP_BUFFER_SIZE);
+                       isc_buffer_init(buffer, data,
+                                       NS_CLIENT_TCP_BUFFER_SIZE);
                } else {
-                       isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
+                       isc_buffer_init(buffer, data,
+                                       NS_CLIENT_TCP_BUFFER_SIZE);
                        INSIST(length <= 0xffff);
-                       isc_buffer_putuint16(buffer, (uint16_t)length);
                }
        } else {
-               data = sendbuf;
+               data = client->sendbuf;
                if ((client->attributes & NS_CLIENTATTR_HAVECOOKIE) == 0) {
                        if (client->view != NULL)
                                bufsize = client->view->nocookieudp;
@@ -1043,8 +315,8 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
                        bufsize = client->udpsize;
                if (bufsize > client->udpsize)
                        bufsize = client->udpsize;
-               if (bufsize > SEND_BUFFER_SIZE)
-                       bufsize = SEND_BUFFER_SIZE;
+               if (bufsize > NS_CLIENT_SEND_BUFFER_SIZE)
+                       bufsize = NS_CLIENT_SEND_BUFFER_SIZE;
                if (length > bufsize) {
                        result = ISC_R_NOSPACE;
                        goto done;
@@ -1060,82 +332,13 @@ client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
 
 static isc_result_t
 client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
-       struct in6_pktinfo *pktinfo;
-       isc_result_t result;
        isc_region_t r;
-       isc_sockaddr_t *address;
-       isc_socket_t *sock;
-       isc_netaddr_t netaddr;
-       int match;
-       unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
-
-       if (TCP_CLIENT(client)) {
-               sock = client->tcpsocket;
-               address = NULL;
-       } else {
-               dns_aclenv_t *env =
-                       ns_interfacemgr_getaclenv(client->interface->mgr);
-
-               sock = client->udpsocket;
-               address = &client->peeraddr;
-
-               isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
-               if (client->sctx->blackholeacl != NULL &&
-                   (dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
-                                  env, &match, NULL) == ISC_R_SUCCESS) &&
-                   match > 0)
-               {
-                       return (DNS_R_BLACKHOLED);
-               }
-               sockflags |= ISC_SOCKFLAG_NORETRY;
-       }
-
-       if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
-           (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
-               pktinfo = &client->pktinfo;
-       else
-               pktinfo = NULL;
-
-       if (client->dispatch != NULL) {
-               isc_dscp_t dscp = dns_dispatch_getdscp(client->dispatch);
-               if (dscp != -1) {
-                       client->dscp = dscp;
-               }
-       }
-
-       if (client->dscp == -1) {
-               client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
-               client->sendevent->dscp = 0;
-       } else {
-               client->sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
-               client->sendevent->dscp = client->dscp;
-       }
 
        isc_buffer_usedregion(buffer, &r);
 
-       /*
-        * If this is a UDP client and the IPv6 packet can't be
-        * encapsulated without generating a PTB on a 1500 octet
-        * MTU link force fragmentation at 1280 if it is a IPv6
-        * response.
-        */
-       client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_USEMINMTU;
-       if (!TCP_CLIENT(client) && r.length > 1432)
-               client->sendevent->attributes |= ISC_SOCKEVENTATTR_USEMINMTU;
-
-       CTRACE("sendto");
+       INSIST(client->handle != NULL);
 
-       result = isc_socket_sendto2(sock, &r, client->task,
-                                   address, pktinfo,
-                                   client->sendevent, sockflags);
-       if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
-               client->nsends++;
-               if (result == ISC_R_SUCCESS)
-                       client_senddone(client->task,
-                                       (isc_event_t *)client->sendevent);
-               result = ISC_R_SUCCESS;
-       }
-       return (result);
+       return (isc_nm_send(client->handle, &r, client_senddone, client));
 }
 
 void
@@ -1145,7 +348,6 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
        isc_buffer_t buffer;
        isc_region_t r;
        isc_region_t *mr;
-       unsigned char sendbuf[SEND_BUFFER_SIZE];
 
        REQUIRE(NS_CLIENT_VALID(client));
 
@@ -1157,8 +359,7 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
                goto done;
        }
 
-       result = client_allocsendbuf(client, &buffer, NULL, mr->length,
-                                    sendbuf, &data);
+       result = client_allocsendbuf(client, &buffer, NULL, mr->length, &data);
        if (result != ISC_R_SUCCESS)
                goto done;
 
@@ -1178,22 +379,23 @@ ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
 
  done:
        if (client->tcpbuf != NULL) {
-               isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+               isc_mem_put(client->mctx, client->tcpbuf,
+                           NS_CLIENT_TCP_BUFFER_SIZE);
                client->tcpbuf = NULL;
        }
-       ns_client_next(client, result);
+
+       ns_client_drop(client, result);
 }
 
-static void
-client_send(ns_client_t *client) {
+void
+ns_client_send(ns_client_t *client) {
        isc_result_t result;
        unsigned char *data;
-       isc_buffer_t buffer;
-       isc_buffer_t tcpbuffer;
+       isc_buffer_t buffer = { .magic = 0 };
+       isc_buffer_t tcpbuffer = { .magic = 0 };
        isc_region_t r;
        dns_compress_t cctx;
        bool cleanup_cctx = false;
-       unsigned char sendbuf[SEND_BUFFER_SIZE];
        unsigned int render_opts;
        unsigned int preferred_glue;
        bool opt_included = false;
@@ -1205,9 +407,18 @@ client_send(ns_client_t *client) {
        isc_region_t zr;
 #endif /* HAVE_DNSTAP */
 
+       /*
+        * XXXWPK TODO
+        * Delay the response according to the -T delay option
+        */
+
        REQUIRE(NS_CLIENT_VALID(client));
+       /*
+        * We need to do it to make sure the client and handle
+        * won't disappear from under us with client_senddone.
+        */
 
-       env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
 
        CTRACE("send");
 
@@ -1247,8 +458,7 @@ client_send(ns_client_t *client) {
        /*
         * XXXRTH  The following doesn't deal with TCP buffer resizing.
         */
-       result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
-                                    sendbuf, &data);
+       result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0, &data);
        if (result != ISC_R_SUCCESS)
                goto done;
 
@@ -1364,7 +574,6 @@ client_send(ns_client_t *client) {
                client->sendcb(&buffer);
        } else if (TCP_CLIENT(client)) {
                isc_buffer_usedregion(&buffer, &r);
-               isc_buffer_putuint16(&tcpbuffer, (uint16_t) r.length);
                isc_buffer_add(&tcpbuffer, r.length);
 #ifdef HAVE_DNSTAP
                if (client->view != NULL) {
@@ -1377,6 +586,8 @@ client_send(ns_client_t *client) {
 
                /* don't count the 2-octet length header */
                respsize = isc_buffer_usedlength(&tcpbuffer) - 2;
+
+               isc_nmhandle_ref(client->handle);
                result = client_sendpkg(client, &tcpbuffer);
 
                switch (isc_sockaddr_pf(&client->peeraddr)) {
@@ -1408,6 +619,8 @@ client_send(ns_client_t *client) {
 #endif /* HAVE_DNSTAP */
 
                respsize = isc_buffer_usedlength(&buffer);
+
+               isc_nmhandle_ref(client->handle);
                result = client_sendpkg(client, &buffer);
 
                switch (isc_sockaddr_pf(&client->peeraddr)) {
@@ -1446,85 +659,19 @@ client_send(ns_client_t *client) {
                ns_stats_increment(client->sctx->nsstats,
                                   ns_statscounter_truncatedresp);
 
-       if (result == ISC_R_SUCCESS)
+       if (result == ISC_R_SUCCESS) {
                return;
+       }
 
  done:
        if (client->tcpbuf != NULL) {
-               isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+               isc_mem_put(client->mctx, client->tcpbuf,
+                           NS_CLIENT_TCP_BUFFER_SIZE);
                client->tcpbuf = NULL;
        }
 
        if (cleanup_cctx)
                dns_compress_invalidate(&cctx);
-
-       ns_client_next(client, result);
-}
-
-/*
- * Completes the sending of a delayed client response.
- */
-static void
-client_delay(isc_task_t *task, isc_event_t *event) {
-       ns_client_t *client;
-
-       REQUIRE(event != NULL);
-       REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
-               event->ev_type == ISC_TIMEREVENT_IDLE);
-       client = event->ev_arg;
-       REQUIRE(NS_CLIENT_VALID(client));
-       REQUIRE(task == client->task);
-       REQUIRE(client->delaytimer != NULL);
-
-       UNUSED(task);
-
-       CTRACE("client_delay");
-
-       isc_event_free(&event);
-       isc_timer_detach(&client->delaytimer);
-
-       client_send(client);
-       ns_client_detach(&client);
-}
-
-void
-ns_client_send(ns_client_t *client) {
-       /*
-        * Delay the response according to the -T delay option
-        */
-       if (client->sctx->delay != 0) {
-               ns_client_t *dummy = NULL;
-               isc_result_t result;
-               isc_interval_t interval;
-
-               /*
-                * Replace ourselves if we have not already been replaced.
-                */
-               if (!client->mortal) {
-                       result = ns_client_replace(client);
-                       if (result != ISC_R_SUCCESS)
-                               goto nodelay;
-               }
-
-               ns_client_attach(client, &dummy);
-               if (client->sctx->delay >= 1000)
-                       isc_interval_set(&interval, client->sctx->delay / 1000,
-                                (client->sctx->delay % 1000) * 1000000);
-               else
-                       isc_interval_set(&interval, 0,
-                                        client->sctx->delay * 1000000);
-               result = isc_timer_create(client->manager->timermgr,
-                                         isc_timertype_once, NULL, &interval,
-                                         client->task, client_delay,
-                                         client, &client->delaytimer);
-               if (result == ISC_R_SUCCESS)
-                       return;
-
-               ns_client_detach(&dummy);
-       }
-
- nodelay:
-       client_send(client);
 }
 
 #if NS_CLIENT_DROPPORT
@@ -1591,7 +738,7 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                              NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
                              "dropped error (%.*s) response: suspicious port",
                              (int)isc_buffer_usedlength(&b), buf);
-               ns_client_next(client, ISC_R_SUCCESS);
+               ns_client_drop(client, ISC_R_SUCCESS);
                return;
        }
 #endif
@@ -1641,7 +788,7 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                                                   ns_statscounter_ratedropped);
                                ns_stats_increment(client->sctx->nsstats,
                                                   ns_statscounter_dropped);
-                               ns_client_next(client, DNS_R_DROP);
+                               ns_client_drop(client, DNS_R_DROP);
                                return;
                        }
                }
@@ -1666,7 +813,7 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                 */
                result = dns_message_reply(message, false);
                if (result != ISC_R_SUCCESS) {
-                       ns_client_next(client, result);
+                       ns_client_drop(client, result);
                        return;
                }
        }
@@ -1693,7 +840,7 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
                                      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
                                      "possible error packet loop, "
                                      "FORMERR dropped");
-                       ns_client_next(client, result);
+                       ns_client_drop(client, result);
                        return;
                }
                client->formerrcache.addr = client->peeraddr;
@@ -1749,7 +896,7 @@ ns_client_addopt(ns_client_t *client, dns_message_t *message,
        REQUIRE(opt != NULL && *opt == NULL);
        REQUIRE(message != NULL);
 
-       env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
        view = client->view;
        resolver = (view != NULL) ? view->resolver : NULL;
        if (resolver != NULL)
@@ -2324,8 +1471,8 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) {
                        case DNS_OPT_TCP_KEEPALIVE:
                                if (!USEKEEPALIVE(client))
                                        ns_stats_increment(
-                                                  client->sctx->nsstats,
-                                                  ns_statscounter_keepaliveopt);
+                                                client->sctx->nsstats,
+                                                ns_statscounter_keepaliveopt);
                                client->attributes |=
                                        NS_CLIENTATTR_USEKEEPALIVE;
                                isc_buffer_forward(&optbuf, optlen);
@@ -2355,10 +1502,88 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) {
                }
        }
 
-       ns_stats_increment(client->sctx->nsstats, ns_statscounter_edns0in);
-       client->attributes |= NS_CLIENTATTR_WANTOPT;
+       ns_stats_increment(client->sctx->nsstats, ns_statscounter_edns0in);
+       client->attributes |= NS_CLIENTATTR_WANTOPT;
+
+       return (result);
+}
+
+void
+ns__client_reset_cb(void *client0) {
+       ns_client_t *client = client0;
+
+       ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                     "reset client");
+
+       ns_client_endrequest(client);
+       if (client->tcpbuf != NULL) {
+               isc_mem_put(client->mctx, client->tcpbuf,
+                           NS_CLIENT_TCP_BUFFER_SIZE);
+       }
+
+       if (client->keytag != NULL) {
+               isc_mem_put(client->mctx, client->keytag,
+                           client->keytag_len);
+               client->keytag_len = 0;
+       }
+
+       client->state = NS_CLIENTSTATE_READY;
+       INSIST(client->recursionquota == NULL);
+}
+
+void
+ns__client_put_cb(void *client0) {
+       ns_client_t *client = client0;
+
+       ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                     "freeing client");
+
+       /*
+        * Call this first because it requires a valid client.
+        */
+       ns_query_free(client);
+
+       client->magic = 0;
+       client->shuttingdown = true;
+
+       if (client->manager != NULL) {
+               clientmgr_detach(&client->manager);
+       }
+
+       isc_mem_put(client->mctx, client->recvbuf, NS_CLIENT_RECV_BUFFER_SIZE);
+       if (client->opt != NULL) {
+               INSIST(dns_rdataset_isassociated(client->opt));
+               dns_rdataset_disassociate(client->opt);
+               dns_message_puttemprdataset(client->message,
+                                           &client->opt);
+       }
+
+       dns_message_destroy(&client->message);
+
+       /*
+        * Detaching the task must be done after unlinking from
+        * the manager's lists because the manager accesses
+        * client->task.
+        */
+       if (client->task != NULL) {
+               isc_task_detach(&client->task);
+       }
+
+       /*
+        * Destroy the fetchlock mutex that was created in
+        * ns_query_init().
+        */
+       isc_mutex_destroy(&client->query.fetchlock);
 
-       return (result);
+       if (client->sctx != NULL) {
+               ns_server_detach(&client->sctx);
+       }
+
+       if (client->mctx != NULL) {
+               isc_mem_detach(&client->mctx);
+       }
 }
 
 /*
@@ -2366,9 +1591,11 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) {
  * or tcpmsg (TCP case).
  */
 void
-ns__client_request(isc_task_t *task, isc_event_t *event) {
+ns__client_request(isc_nmhandle_t *handle, isc_region_t *region, void *arg) {
        ns_client_t *client;
-       isc_socketevent_t *sevent;
+       bool newclient = false;
+       ns_clientmgr_t *mgr;
+       ns_interface_t *ifp;
        isc_result_t result;
        isc_result_t sigresult = ISC_R_SUCCESS;
        isc_buffer_t *buffer;
@@ -2386,91 +1613,70 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
 #ifdef HAVE_DNSTAP
        dns_dtmsgtype_t dtmsgtype;
 #endif
+       ifp = (ns_interface_t *) arg;
 
-       REQUIRE(event != NULL);
-       client = event->ev_arg;
-       REQUIRE(NS_CLIENT_VALID(client));
-       REQUIRE(task == client->task);
+       mgr = ifp->clientmgr;
+       REQUIRE(VALID_MANAGER(mgr));
+
+       client = isc_nmhandle_getdata(handle);
+       if (client == NULL) {
+               client = isc_nmhandle_getextra(handle);
+
+               result = ns__client_setup(client, mgr, true);
+               if (result != ISC_R_SUCCESS) {
+                       return;
+               }
+
+               ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                             "allocate new client");
+       } else {
+               result = ns__client_setup(client, NULL, false);
+               if (result != ISC_R_SUCCESS) {
+                       return;
+               }
+       }
+
+       client->state = NS_CLIENTSTATE_READY;
+       client->dscp = ifp->dscp;
+
+       isc_task_pause(client->task);
+       if (client->handle == NULL) {
+               isc_nmhandle_setdata(handle, client,
+                                    ns__client_reset_cb, ns__client_put_cb);
+               client->handle = handle;
+       }
+       if (isc_nmhandle_is_stream(handle)) {
+               client->attributes |= NS_CLIENTATTR_TCP;
+               unsigned int curr_tcpquota =
+                       isc_quota_getused(&client->sctx->tcpquota);
+               ns_stats_update_if_greater(client->sctx->nsstats,
+                                          ns_statscounter_tcphighwater,
+                                          curr_tcpquota);
+       }
 
        INSIST(client->recursionquota == NULL);
 
-       INSIST(client->state == (TCP_CLIENT(client) ?
-                                      NS_CLIENTSTATE_READING :
-                                      NS_CLIENTSTATE_READY));
+       INSIST(client->state == NS_CLIENTSTATE_READY);
 
        ns_client_requests++;
 
-       if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
-               INSIST(!TCP_CLIENT(client));
-               sevent = (isc_socketevent_t *)event;
-               REQUIRE(sevent == client->recvevent);
-               isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
-               isc_buffer_add(&tbuffer, sevent->n);
-               buffer = &tbuffer;
-               result = sevent->result;
-               if (result == ISC_R_SUCCESS) {
-                       client->peeraddr = sevent->address;
-                       client->peeraddr_valid = true;
-               }
-               if ((sevent->attributes & ISC_SOCKEVENTATTR_DSCP) != 0) {
-                       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(90),
-                             "received DSCP %d", sevent->dscp);
-                       if (client->dscp == -1)
-                               client->dscp = sevent->dscp;
-               }
-               if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
-                       client->attributes |= NS_CLIENTATTR_PKTINFO;
-                       client->pktinfo = sevent->pktinfo;
-               }
-               if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
-                       client->attributes |= NS_CLIENTATTR_MULTICAST;
-               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;
-               result = client->tcpmsg.result;
-               INSIST(client->nreads == 1);
-               /*
-                * client->peeraddr was set when the connection was accepted.
-                */
-               client->nreads--;
-       }
+       isc_buffer_init(&tbuffer, region->base, region->length);
+       isc_buffer_add(&tbuffer, region->length);
+       buffer = &tbuffer;
+
+       client->peeraddr = isc_nmhandle_peeraddr(client->handle);
+
+       client->peeraddr_valid = true;
 
        reqsize = isc_buffer_usedlength(buffer);
-       /* don't count the length header */
-       if (TCP_CLIENT(client))
-               reqsize -= 2;
 
-       if (exit_check(client)) {
-               return;
-       }
-       client->state = client->newstate = NS_CLIENTSTATE_WORKING;
+       client->state = NS_CLIENTSTATE_WORKING;
 
-       isc_task_getcurrenttimex(task, &client->requesttime);
+       TIME_NOW(&client->requesttime);
        client->tnow = client->requesttime;
        client->now = isc_time_seconds(&client->tnow);
 
-       if (result != ISC_R_SUCCESS) {
-               if (TCP_CLIENT(client)) {
-                       ns_client_next(client, result);
-               } else {
-                       if  (result != ISC_R_CANCELED)
-                               isc_log_write(ns_lctx, NS_LOGCATEGORY_CLIENT,
-                                             NS_LOGMODULE_CLIENT,
-                                             ISC_LOG_ERROR,
-                                             "UDP client handler shutting "
-                                             "down due to fatal receive "
-                                             "error: %s",
-                                             isc_result_totext(result));
-                       isc_task_shutdown(client->task);
-               }
-               return;
-       }
-
        isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
 
 #if NS_CLIENT_DROPPORT
@@ -2479,7 +1685,7 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
                              NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
                              "dropped request: suspicious port");
-               ns_client_next(client, ISC_R_SUCCESS);
+               isc_task_unpause(client->task);
                return;
        }
 #endif
@@ -2493,8 +1699,8 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
         * Check the blackhole ACL for UDP only, since TCP is done in
         * client_newconn.
         */
-       env = ns_interfacemgr_getaclenv(client->interface->mgr);
-       if (!TCP_CLIENT(client)) {
+       env = ns_interfacemgr_getaclenv(client->manager->interface->mgr);
+       if (newclient) {
                if (client->sctx->blackholeacl != NULL &&
                    (dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
                                   env, &match, NULL) == ISC_R_SUCCESS) &&
@@ -2503,30 +1709,18 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                        ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
                                      NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
                                      "blackholed UDP datagram");
-                       ns_client_next(client, ISC_R_SUCCESS);
+                       isc_task_unpause(client->task);
                        return;
                }
        }
 
-       /*
-        * Silently drop multicast requests for the present.
-        * XXXMPA revisit this as mDNS spec was published.
-        */
-       if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
-                             "dropping multicast request");
-               ns_client_next(client, DNS_R_REFUSED);
-               return;
-       }
-
        result = dns_message_peekheader(buffer, &id, &flags);
        if (result != ISC_R_SUCCESS) {
                /*
                 * There isn't enough header to determine whether
                 * this was a request or a response.  Drop it.
                 */
-               ns_client_next(client, result);
+               isc_task_unpause(client->task);
                return;
        }
 
@@ -2536,15 +1730,9 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
         * If it's a TCP response, discard it here.
         */
        if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
-               if (TCP_CLIENT(client)) {
-                       CTRACE("unexpected response");
-                       ns_client_next(client, DNS_R_FORMERR);
-                       return;
-               } else {
-                       dns_dispatch_importrecv(client->dispatch, event);
-                       ns_client_next(client, ISC_R_SUCCESS);
-                       return;
-               }
+               CTRACE("unexpected response");
+               isc_task_unpause(client->task);
+               return;
        }
 
        /*
@@ -2611,34 +1799,20 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                        result = DNS_R_FORMERR;
                }
                ns_client_error(client, result);
+               isc_task_unpause(client->task);
                return;
        }
 
        /*
-        * Pipeline TCP query processing.
+        * Disable pipelined TCP query processing if necessary.
         */
        if (TCP_CLIENT(client) &&
-           client->message->opcode != dns_opcode_query)
+           (client->message->opcode != dns_opcode_query ||
+            (client->sctx->keepresporder != NULL &&
+             dns_acl_allowed(&netaddr, NULL,
+                             client->sctx->keepresporder, env))))
        {
-               client->tcpconn->pipelined = false;
-       }
-       if (TCP_CLIENT(client) && client->tcpconn->pipelined) {
-               /*
-                * We're pipelining. Replace the client; the
-                * replacement can read the TCP socket looking
-                * for new messages and this one can process the
-                * current message asynchronously.
-                *
-                * 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
-                * received.
-                */
-               result = ns_client_replace(client);
-               if (result != ISC_R_SUCCESS) {
-                       client->tcpconn->pipelined = false;
-               }
+               isc_nm_tcpdns_sequential(handle);
        }
 
        dns_opcodestats_increment(client->sctx->opcodestats,
@@ -2657,17 +1831,14 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
 
        client->message->rcode = dns_rcode_noerror;
 
-       /* RFC1123 section 6.1.3.2 */
-       if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
-               client->message->flags &= ~DNS_MESSAGEFLAG_RD;
-
        /*
         * Deal with EDNS.
         */
-       if ((client->sctx->options & NS_SERVER_NOEDNS) != 0)
+       if ((client->sctx->options & NS_SERVER_NOEDNS) != 0) {
                opt = NULL;
-       else
+       } else {
                opt = dns_message_getopt(client->message);
+       }
 
        client->ecs.source = 0;
        client->ecs.scope = 0;
@@ -2679,6 +1850,7 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                 */
                if ((client->sctx->options & NS_SERVER_EDNSFORMERR) != 0) {
                        ns_client_error(client, DNS_R_FORMERR);
+                       isc_task_unpause(client->task);
                        return;
                }
 
@@ -2687,6 +1859,7 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                 */
                if ((client->sctx->options & NS_SERVER_EDNSNOTIMP) != 0) {
                        ns_client_error(client, DNS_R_NOTIMP);
+                       isc_task_unpause(client->task);
                        return;
                }
 
@@ -2695,6 +1868,7 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                 */
                if ((client->sctx->options & NS_SERVER_EDNSREFUSED) != 0) {
                        ns_client_error(client, DNS_R_REFUSED);
+                       isc_task_unpause(client->task);
                        return;
                }
 
@@ -2702,13 +1876,16 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                 * Are we dropping all EDNS queries?
                 */
                if ((client->sctx->options & NS_SERVER_DROPEDNS) != 0) {
-                       ns_client_next(client, ISC_R_SUCCESS);
+                       ns_client_drop(client, ISC_R_SUCCESS);
+                       isc_task_unpause(client->task);
                        return;
                }
 
                result = process_opt(client, opt);
-               if (result != ISC_R_SUCCESS)
+               if (result != ISC_R_SUCCESS) {
+                       isc_task_unpause(client->task);
                        return;
+               }
        }
 
        if (client->message->rdclass == 0) {
@@ -2719,19 +1896,26 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                        result = dns_message_reply(client->message, true);
                        if (result != ISC_R_SUCCESS) {
                                ns_client_error(client, result);
+                               isc_task_unpause(client->task);
                                return;
                        }
-                       if (notimp)
+
+                       if (notimp) {
                                client->message->rcode = dns_rcode_notimp;
+                       }
+
                        ns_client_send(client);
+                       isc_task_unpause(client->task);
                        return;
                }
+
                ns_client_log(client, NS_LOGCATEGORY_CLIENT,
                              NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
                              "message class could not be determined");
                ns_client_dumpmessage(client,
                                      "message class could not be determined");
                ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
+               isc_task_unpause(client->task);
                return;
        }
 
@@ -2743,47 +1927,17 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
         * receiving socket (this needs a system call and can be heavy).
         * For IPv6 UDP queries, we get this from the pktinfo structure (if
         * supported).
+        *
         * If all the attempts fail (this can happen due to memory shortage,
         * etc), we regard this as an error for safety.
         */
-       if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
+       if ((client->manager->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
                isc_netaddr_fromsockaddr(&client->destaddr,
-                                        &client->interface->addr);
+                                        &client->manager->interface->addr);
        else {
-               isc_sockaddr_t sockaddr;
-               result = ISC_R_FAILURE;
-
-               if (TCP_CLIENT(client))
-                       result = isc_socket_getsockname(client->tcpsocket,
-                                                       &sockaddr);
-               if (result == ISC_R_SUCCESS)
-                       isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
-               if (result != ISC_R_SUCCESS &&
-                   client->interface->addr.type.sa.sa_family == AF_INET6 &&
-                   (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
-                       /*
-                        * XXXJT technically, we should convert the receiving
-                        * interface ID to a proper scope zone ID.  However,
-                        * due to the fact there is no standard API for this,
-                        * we only handle link-local addresses and use the
-                        * interface index as link ID.  Despite the assumption,
-                        * it should cover most typical cases.
-                        */
-                       isc_netaddr_fromin6(&client->destaddr,
-                                           &client->pktinfo.ipi6_addr);
-                       if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
-                               isc_netaddr_setzone(&client->destaddr,
-                                               client->pktinfo.ipi6_ifindex);
-                       result = ISC_R_SUCCESS;
-               }
-               if (result != ISC_R_SUCCESS) {
-                       UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                        "failed to get request's "
-                                        "destination: %s",
-                                        isc_result_totext(result));
-                       ns_client_next(client, ISC_R_SUCCESS);
-                       return;
-               }
+               isc_sockaddr_t sockaddr =
+                       isc_nmhandle_localaddr(client->handle);
+               isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
        }
 
        isc_sockaddr_fromnetaddr(&client->destsockaddr, &client->destaddr, 0);
@@ -2816,6 +1970,7 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                              "no matching view in class '%s'", classname);
                ns_client_dumpmessage(client, "no matching view in class");
                ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
+               isc_task_unpause(client->task);
                return;
        }
 
@@ -2908,14 +2063,17 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                                      "request has invalid signature: %s (%s)",
                                      isc_result_totext(result), tsigrcode);
                }
+
                /*
                 * Accept update messages signed by unknown keys so that
                 * update forwarding works transparently through slaves
                 * that don't have all the same keys as the master.
                 */
                if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
-                     client->message->opcode == dns_opcode_update)) {
+                     client->message->opcode == dns_opcode_update))
+               {
                        ns_client_error(client, sigresult);
+                       isc_task_unpause(client->task);
                        return;
                }
        }
@@ -2942,7 +2100,9 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
            ns_client_checkaclsilent(client, &client->destaddr,
                                     client->view->cacheonacl,
                                     true) == ISC_R_SUCCESS)
+       {
                ra = true;
+       }
 
        if (ra == true) {
                client->attributes |= NS_CLIENTATTR_RA;
@@ -2984,6 +2144,7 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                            &client->requesttime, NULL, buffer);
 #endif /* HAVE_DNSTAP */
 
+               isc_nmhandle_ref(client->handle);
                ns_query_start(client);
                break;
        case dns_opcode_update:
@@ -2994,11 +2155,13 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                            &client->requesttime, NULL, buffer);
 #endif /* HAVE_DNSTAP */
                ns_client_settimeout(client, 60);
+               isc_nmhandle_ref(client->handle);
                ns_update_start(client, sigresult);
                break;
        case dns_opcode_notify:
                CTRACE("notify");
                ns_client_settimeout(client, 60);
+               isc_nmhandle_ref(client->handle);
                ns_notify_start(client);
                break;
        case dns_opcode_iquery:
@@ -3006,648 +2169,245 @@ ns__client_request(isc_task_t *task, isc_event_t *event) {
                ns_client_error(client, DNS_R_NOTIMP);
                break;
        default:
-               CTRACE("unknown opcode");
-               ns_client_error(client, DNS_R_NOTIMP);
-       }
-}
-
-static void
-client_timeout(isc_task_t *task, isc_event_t *event) {
-       ns_client_t *client;
-
-       REQUIRE(event != NULL);
-       REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
-               event->ev_type == ISC_TIMEREVENT_IDLE);
-       client = event->ev_arg;
-       REQUIRE(NS_CLIENT_VALID(client));
-       REQUIRE(task == client->task);
-       REQUIRE(client->timer != NULL);
-
-       UNUSED(task);
-
-       CTRACE("timeout");
-
-       isc_event_free(&event);
-
-       if (client->shutdown != NULL) {
-               (client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
-               client->shutdown = NULL;
-               client->shutdown_arg = NULL;
-       }
-
-       if (client->newstate > NS_CLIENTSTATE_READY)
-               client->newstate = NS_CLIENTSTATE_READY;
-       (void)exit_check(client);
-}
-
-static isc_result_t
-get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
-       isc_mem_t *clientmctx;
-#if NMCTXS > 0
-       unsigned int nextmctx;
-#endif
-
-       MTRACE("clientmctx");
-
-       /*
-        * Caller must be holding the manager lock.
-        */
-       if ((manager->sctx->options & NS_SERVER_CLIENTTEST) != 0) {
-               isc_mem_create(mctxp);
-               isc_mem_setname(*mctxp, "client", NULL);
-               return (ISC_R_SUCCESS);
-       }
-#if NMCTXS > 0
-       nextmctx = manager->nextmctx++;
-       if (manager->nextmctx == NMCTXS)
-               manager->nextmctx = 0;
-
-       INSIST(nextmctx < NMCTXS);
-
-       clientmctx = manager->mctxpool[nextmctx];
-       if (clientmctx == NULL) {
-               isc_mem_create(&clientmctx);
-               isc_mem_setname(clientmctx, "client", NULL);
-
-               manager->mctxpool[nextmctx] = clientmctx;
-       }
-#else
-       clientmctx = manager->mctx;
-#endif
-
-       isc_mem_attach(clientmctx, mctxp);
-
-       return (ISC_R_SUCCESS);
-}
-
-static isc_result_t
-client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
-       ns_client_t *client;
-       isc_result_t result;
-       isc_mem_t *mctx = NULL;
-
-       /*
-        * Caller must be holding the manager lock.
-        *
-        * Note: creating a client does not add the client to the
-        * manager's client list or set the client's manager pointer.
-        * The caller is responsible for that.
-        */
-
-       REQUIRE(clientp != NULL && *clientp == NULL);
-
-       result = get_clientmctx(manager, &mctx);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-
-       client = isc_mem_get(mctx, sizeof(*client));
-       client->mctx = mctx;
-
-       client->sctx = NULL;
-       ns_server_attach(manager->sctx, &client->sctx);
-
-       client->task = NULL;
-       result = isc_task_create(manager->taskmgr, 0, &client->task);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_client;
-       isc_task_setname(client->task, "client", client);
-
-       client->timer = NULL;
-       result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
-                                 NULL, NULL, client->task, client_timeout,
-                                 client, &client->timer);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_task;
-       client->timerset = false;
-
-       client->delaytimer = NULL;
-
-       client->message = NULL;
-       result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
-                                   &client->message);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_timer;
-
-       /* XXXRTH  Hardwired constants */
-
-       client->sendevent = isc_socket_socketevent(client->mctx, client,
-                                                  ISC_SOCKEVENT_SENDDONE,
-                                                  client_senddone, client);
-       if (client->sendevent == NULL) {
-               result = ISC_R_NOMEMORY;
-               goto cleanup_message;
-       }
-
-       client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
-
-       client->recvevent = isc_socket_socketevent(client->mctx, client,
-                                                  ISC_SOCKEVENT_RECVDONE,
-                                                  ns__client_request, client);
-       if (client->recvevent == NULL) {
-               result = ISC_R_NOMEMORY;
-               goto cleanup_recvbuf;
-       }
-
-       client->magic = NS_CLIENT_MAGIC;
-       client->manager = NULL;
-       client->state = NS_CLIENTSTATE_INACTIVE;
-       client->newstate = NS_CLIENTSTATE_MAX;
-       client->naccepts = 0;
-       client->nreads = 0;
-       client->nsends = 0;
-       client->nrecvs = 0;
-       client->nupdates = 0;
-       client->nctls = 0;
-       isc_refcount_init(&client->references, 0);
-       client->attributes = 0;
-       client->view = NULL;
-       client->dispatch = NULL;
-       client->udpsocket = NULL;
-       client->tcplistener = NULL;
-       client->tcpsocket = NULL;
-       client->tcpmsg_valid = false;
-       client->tcpbuf = NULL;
-       client->opt = NULL;
-       client->udpsize = 512;
-       client->dscp = -1;
-       client->extflags = 0;
-       client->ednsversion = -1;
-       client->next = NULL;
-       client->shutdown = NULL;
-       client->shutdown_arg = NULL;
-       client->signer = NULL;
-       dns_name_init(&client->signername, NULL);
-       client->mortal = false;
-       client->sendcb = NULL;
-       client->tcpconn = NULL;
-       client->recursionquota = NULL;
-       client->interface = NULL;
-       client->peeraddr_valid = false;
-       dns_ecs_init(&client->ecs);
-       client->needshutdown = ((client->sctx->options &
-                                NS_SERVER_CLIENTTEST) != 0);
-       client->tcpactive = false;
-
-       ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
-                      NS_EVENT_CLIENTCONTROL, client_start, client, client,
-                      NULL, NULL);
-       /*
-        * Initialize FORMERR cache to sentinel value that will not match
-        * any actual FORMERR response.
-        */
-       isc_sockaddr_any(&client->formerrcache.addr);
-       client->formerrcache.time = 0;
-       client->formerrcache.id = 0;
-       ISC_LINK_INIT(client, link);
-       ISC_LINK_INIT(client, rlink);
-       ISC_QLINK_INIT(client, ilink);
-       client->keytag = NULL;
-       client->keytag_len = 0;
-       client->rcode_override = -1;    /* not set */
-
-       /*
-        * We call the init routines for the various kinds of client here,
-        * after we have created an otherwise valid client, because some
-        * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
-        */
-       result = ns_query_init(client);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_recvevent;
-
-       result = isc_task_onshutdown(client->task, client_shutdown, client);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup_query;
-
-       CTRACE("create");
-
-       *clientp = client;
-
-       return (ISC_R_SUCCESS);
-
- cleanup_query:
-       ns_query_free(client);
-
- cleanup_recvevent:
-       isc_event_free((isc_event_t **)&client->recvevent);
-
- cleanup_recvbuf:
-       isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
-
-       isc_event_free((isc_event_t **)&client->sendevent);
-
-       client->magic = 0;
-
- cleanup_message:
-       dns_message_destroy(&client->message);
-
- cleanup_timer:
-       isc_timer_detach(&client->timer);
-
- cleanup_task:
-       isc_task_detach(&client->task);
-
- cleanup_client:
-       if (client->sctx != NULL)
-               ns_server_detach(&client->sctx);
-       isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
-
-       return (result);
-}
-
-static void
-client_read(ns_client_t *client, bool newconn) {
-       isc_result_t result;
-
-       CTRACE("read");
-
-       result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
-                                       ns__client_request, client);
-       if (result != ISC_R_SUCCESS)
-               goto fail;
-
-       /*
-        * Set a timeout to limit the amount of time we will wait
-        * for a request on this TCP connection.
-        */
-       read_settimeout(client, newconn);
-
-       client->state = client->newstate = NS_CLIENTSTATE_READING;
-       INSIST(client->nreads == 0);
-       INSIST(client->recursionquota == NULL);
-       client->nreads++;
-
-       return;
- fail:
-       ns_client_next(client, result);
-}
-
-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;
-       dns_aclenv_t *env;
-       uint32_t old;
-
-       REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
-       REQUIRE(NS_CLIENT_VALID(client));
-       REQUIRE(client->task == task);
-
-       env = ns_interfacemgr_getaclenv(client->interface->mgr);
-
-       UNUSED(task);
-
-       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--;
-
-       old = isc_refcount_decrement(&client->interface->ntcpaccepting);
-       INSIST(old > 0);
-
-       /*
-        * We must take ownership of the new socket before the exit
-        * check to make sure it gets destroyed if we decide to exit.
-        */
-       if (nevent->result == ISC_R_SUCCESS) {
-               client->tcpsocket = nevent->newsocket;
-               isc_socket_setname(client->tcpsocket, "client-tcp", NULL);
-               client->state = NS_CLIENTSTATE_READING;
-               INSIST(client->recursionquota == NULL);
-
-               (void)isc_socket_getpeername(client->tcpsocket,
-                                            &client->peeraddr);
-               client->peeraddr_valid = true;
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                          NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
-                          "new TCP connection");
-       } else {
-               /*
-                * XXXRTH  What should we do?  We're trying to accept but
-                *         it didn't work.  If we just give up, then TCP
-                *         service may eventually stop.
-                *
-                *         For now, we just go idle.
-                *
-                *         Going idle is probably the right thing if the
-                *         I/O was canceled.
-                */
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
-                             "accept failed: %s",
-                             isc_result_totext(nevent->result));
-               tcpconn_detach(client);
+               CTRACE("unknown opcode");
+               ns_client_error(client, DNS_R_NOTIMP);
        }
 
-       if (exit_check(client))
-               goto freeevent;
-
-       if (nevent->result == ISC_R_SUCCESS) {
-               int match;
-               isc_netaddr_t netaddr;
+       isc_task_unpause(client->task);
+}
 
-               isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+static void
+get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
+       isc_mem_t *clientmctx;
+#if CLIENT_NMCTXS > 0
+       unsigned int nextmctx;
+#endif
 
-               if (client->sctx->blackholeacl != NULL &&
-                   (dns_acl_match(&netaddr, NULL, client->sctx->blackholeacl,
-                                  env, &match, NULL) == ISC_R_SUCCESS) &&
-                   match > 0)
-               {
-                       ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
-                                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
-                                     "blackholed connection attempt");
-                       client->newstate = NS_CLIENTSTATE_READY;
-                       (void)exit_check(client);
-                       goto freeevent;
-               }
+       MTRACE("clientmctx");
 
-               INSIST(client->tcpmsg_valid == false);
-               dns_tcpmsg_init(client->mctx, client->tcpsocket,
-                               &client->tcpmsg);
-               client->tcpmsg_valid = true;
+#if CLIENT_NMCTXS > 0
+       LOCK(&manager->lock);
+       if (isc_nm_tid()>=0) {
+               nextmctx = isc_nm_tid();
+       } else {
+               nextmctx = manager->nextmctx++;
+               if (manager->nextmctx == CLIENT_NMCTXS)
+                       manager->nextmctx = 0;
 
-               /*
-                * Let a new client take our place immediately, before
-                * we wait for a request packet.  If we don't,
-                * telnetting to port 53 (once per CPU) will
-                * deny service to legitimate TCP clients.
-                */
-               result = ns_client_replace(client);
-               if (result == ISC_R_SUCCESS &&
-                   (client->sctx->keepresporder == NULL ||
-                    !dns_acl_allowed(&netaddr, NULL,
-                                    client->sctx->keepresporder, env)))
-               {
-                       client->tcpconn->pipelined = true;
-               }
+               INSIST(nextmctx < CLIENT_NMCTXS);
+       }
 
-               client_read(client, true);
+       clientmctx = manager->mctxpool[nextmctx];
+       if (clientmctx == NULL) {
+               isc_mem_create(&clientmctx);
+               isc_mem_setname(clientmctx, "client", NULL);
+               manager->mctxpool[nextmctx] = clientmctx;
        }
+       UNLOCK(&manager->lock);
+#else
+       clientmctx = manager->mctx;
+#endif
 
- freeevent:
-       isc_event_free(&event);
+       isc_mem_attach(clientmctx, mctxp);
 }
 
-static void
-client_accept(ns_client_t *client) {
+isc_result_t
+ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) {
        isc_result_t result;
 
-       CTRACE("accept");
        /*
-        * 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.
+        * Caller must be holding the manager lock.
+        *
+        * Note: creating a client does not add the client to the
+        * manager's client list or set the client's manager pointer.
+        * The caller is responsible for that.
         */
-       result = tcpconn_init(client, false);
-       if (result != ISC_R_SUCCESS) {
-               bool exit;
 
-               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                             NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
-                             "TCP client quota reached: %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 we find that at
-                * least one client other than this one is active, 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_refcount_current(&client->interface->ntcpactive) >
-                       (client->tcpactive ? 1U : 0U));
-               if (exit) {
-                       client->newstate = NS_CLIENTSTATE_INACTIVE;
-                       (void)exit_check(client);
-                       return;
-               }
+       REQUIRE(NS_CLIENT_VALID(client) || (new && client != NULL));
+       REQUIRE(VALID_MANAGER(mgr) || !new);
 
-               result = tcpconn_init(client, true);
-               RUNTIME_CHECK(result == ISC_R_SUCCESS);
-       }
+       if (new) {
+               *client = (ns_client_t) {
+                       .magic = 0
+               };
 
-       /* TCP high-water stats update. */
-       unsigned int curr_tcpquota = isc_quota_getused(&client->sctx->tcpquota);
-       ns_stats_update_if_greater(client->sctx->nsstats,
-                                  ns_statscounter_tcphighwater,
-                                  curr_tcpquota);
+               get_clientmctx(mgr, &client->mctx);
+               clientmgr_attach(mgr, &client->manager);
+               ns_server_attach(mgr->sctx, &client->sctx);
+               result = isc_task_create(mgr->taskmgr, 20,  &client->task);
+               if (result != ISC_R_SUCCESS) {
+                       goto cleanup;
+               }
+               result = dns_message_create(client->mctx,
+                                           DNS_MESSAGE_INTENTPARSE,
+                                           &client->message);
+               if (result != ISC_R_SUCCESS) {
+                       goto cleanup;
+               }
 
-       /*
-        * 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.
-        */
-       mark_tcp_active(client, true);
 
-       result = isc_socket_accept(client->tcplistener, client->task,
-                                  client_newconn, client);
-       if (result != ISC_R_SUCCESS) {
+               client->recvbuf = isc_mem_get(client->mctx,
+                                             NS_CLIENT_RECV_BUFFER_SIZE);
                /*
-                * XXXRTH  What should we do?  We're trying to accept but
-                *         it didn't work.  If we just give up, then TCP
-                *         service may eventually stop.
-                *
-                *         For now, we just go idle.
+                * Set magic earlier than usual because ns_query_init()
+                * and the functions it calls will require it.
                 */
-               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                "isc_socket_accept() failed: %s",
-                                isc_result_totext(result));
-
-               tcpconn_detach(client);
-               mark_tcp_active(client, false);
-               return;
+               client->magic = NS_CLIENT_MAGIC;
+               result = ns_query_init(client);
+               if (result != ISC_R_SUCCESS) {
+                       goto cleanup;
+               }
+       } else {
+               ns_clientmgr_t *oldmgr = client->manager;
+               ns_server_t *sctx = client->sctx;
+               isc_task_t *task = client->task;
+               unsigned char *recvbuf = client->recvbuf;
+               dns_message_t *message = client->message;
+               isc_mem_t *oldmctx = client->mctx;
+               ns_query_t query = client->query;
+
+               *client = (ns_client_t) {
+                       .magic = 0,
+                       .mctx = oldmctx,
+                       .manager = oldmgr,
+                       .sctx = sctx,
+                       .task = task,
+                       .recvbuf = recvbuf,
+                       .message = message,
+                       .query = query
+               };
        }
 
-       /*
-        * 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++;
+       client->state = NS_CLIENTSTATE_INACTIVE;
+       client->udpsize = 512;
+       client->dscp = -1;
+       client->ednsversion = -1;
+       dns_name_init(&client->signername, NULL);
+       dns_ecs_init(&client->ecs);
+       isc_sockaddr_any(&client->formerrcache.addr);
+       client->formerrcache.time = 0;
+       client->formerrcache.id = 0;
+       ISC_LINK_INIT(client, rlink);
+       client->rcode_override = -1;    /* not set */
 
-       /*
-        * 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()), if this value is at least one, that
-        * means another client has called accept() and is waiting to
-        * establish the next connection. That means the client may be
-        * be free to become inactive; otherwise it may need to start
-        * listening for connections itself to prevent the interface
-        * going dead.
-        */
-       isc_refcount_increment0(&client->interface->ntcpaccepting);
-}
+       client->magic = NS_CLIENT_MAGIC;
 
-static void
-client_udprecv(ns_client_t *client) {
-       isc_result_t result;
-       isc_region_t r;
+       CTRACE("client_setup");
 
-       CTRACE("udprecv");
+       return (ISC_R_SUCCESS);
 
-       r.base = client->recvbuf;
-       r.length = RECV_BUFFER_SIZE;
-       result = isc_socket_recv2(client->udpsocket, &r, 1,
-                                 client->task, client->recvevent, 0);
-       if (result != ISC_R_SUCCESS) {
-               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                "isc_socket_recv2() failed: %s",
-                                isc_result_totext(result));
-               /*
-                * This cannot happen in the current implementation, since
-                * isc_socket_recv2() cannot fail if flags == 0.
-                *
-                * If this does fail, we just go idle.
-                */
-               return;
+ cleanup:
+       if (client->recvbuf != NULL) {
+               isc_mem_put(client->mctx, client->recvbuf,
+                           NS_CLIENT_RECV_BUFFER_SIZE);
        }
-       INSIST(client->nrecvs == 0);
-       client->nrecvs++;
-}
-
-void
-ns_client_attach(ns_client_t *source, ns_client_t **targetp) {
-       uint32_t oldrefs;
-       REQUIRE(NS_CLIENT_VALID(source));
-       REQUIRE(targetp != NULL && *targetp == NULL);
 
-       oldrefs = isc_refcount_increment(&source->references);
-       ns_client_log(source, NS_LOGCATEGORY_CLIENT,
-                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
-                     "ns_client_attach: ref = %d", oldrefs+1);
-       *targetp = source;
-}
+       if (client->message != NULL) {
+               dns_message_destroy(&client->message);
+       }
 
-void
-ns_client_detach(ns_client_t **clientp) {
-       int32_t oldrefs;
-       ns_client_t *client = *clientp;
-       oldrefs = isc_refcount_decrement(&client->references);
-       INSIST(oldrefs > 0);
+       if (client->task != NULL) {
+               isc_task_detach(&client->task);
+       }
 
-       *clientp = NULL;
-       ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
-                     "ns_client_detach: ref = %d", oldrefs-1);
-       (void)exit_check(client);
+       return (result);
 }
 
 bool
 ns_client_shuttingdown(ns_client_t *client) {
-       return (client->newstate == NS_CLIENTSTATE_FREED);
+       return (client->shuttingdown);
 }
 
-isc_result_t
-ns_client_replace(ns_client_t *client) {
-       isc_result_t result;
-       bool tcp;
+/***
+ *** Client Manager
+ ***/
 
-       CTRACE("replace");
+static void
+clientmgr_attach(ns_clientmgr_t *source, ns_clientmgr_t **targetp) {
+       int32_t oldrefs;
 
-       REQUIRE(client != NULL);
-       REQUIRE(client->manager != NULL);
+       REQUIRE(VALID_MANAGER(source));
+       REQUIRE(targetp != NULL && *targetp == NULL);
 
-       tcp = TCP_CLIENT(client);
-       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, tcp);
+       oldrefs = isc_refcount_increment(&source->references);
+       isc_log_write(ns_lctx, NS_LOGCATEGORY_CLIENT,
+                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                     "clientmgr @%p attach: %d", source, oldrefs + 1);
 
-       }
-       if (result != ISC_R_SUCCESS) {
-               return (result);
-       }
+       *targetp = source;
+}
 
-       /*
-        * 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;
+static void
+clientmgr_detach(ns_clientmgr_t **mp) {
+       ns_clientmgr_t *mgr = *mp;
+       int32_t oldrefs;
+       oldrefs = isc_refcount_decrement(&mgr->references);
+       INSIST(oldrefs > 0);
 
-       return (ISC_R_SUCCESS);
-}
+       isc_log_write(ns_lctx, NS_LOGCATEGORY_CLIENT,
+                     NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+                     "clientmgr @%p detach: %d", mgr, oldrefs - 1);
+       if (oldrefs == 1) {
+               clientmgr_destroy(mgr);
+       }
 
-/***
- *** Client Manager
- ***/
+       *mp = NULL;
+}
 
 static void
 clientmgr_destroy(ns_clientmgr_t *manager) {
-#if NMCTXS > 0
+#if CLIENT_NMCTXS > 0
        int i;
 #endif
 
-       REQUIRE(ISC_LIST_EMPTY(manager->clients));
-
        MTRACE("clientmgr_destroy");
 
-#if NMCTXS > 0
-       for (i = 0; i < NMCTXS; i++) {
+       manager->magic = 0;
+
+#if CLIENT_NMCTXS > 0
+       for (i = 0; i < CLIENT_NMCTXS; i++) {
                if (manager->mctxpool[i] != NULL)
                        isc_mem_detach(&manager->mctxpool[i]);
        }
 #endif
 
-       ISC_QUEUE_DESTROY(manager->inactive);
+       if (manager->interface != NULL) {
+               ns_interface_detach(&manager->interface);
+       }
 
        isc_mutex_destroy(&manager->lock);
-       isc_mutex_destroy(&manager->listlock);
        isc_mutex_destroy(&manager->reclock);
 
        if (manager->excl != NULL)
                isc_task_detach(&manager->excl);
 
+       for (i = 0; i < CLIENT_NTASKS; i++) {
+               if (manager->taskpool[i] != NULL) {
+                       isc_task_detach(&manager->taskpool[i]);
+               }
+       }
+       isc_mem_put(manager->mctx, manager->taskpool,
+                   CLIENT_NTASKS * sizeof(isc_task_t *));
        ns_server_detach(&manager->sctx);
 
-       manager->magic = 0;
        isc_mem_put(manager->mctx, manager, sizeof(*manager));
 }
 
 isc_result_t
 ns_clientmgr_create(isc_mem_t *mctx, ns_server_t *sctx, isc_taskmgr_t *taskmgr,
-                   isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
+                   isc_timermgr_t *timermgr, ns_interface_t *interface,
+                   ns_clientmgr_t **managerp)
 {
        ns_clientmgr_t *manager;
        isc_result_t result;
-#if NMCTXS > 0
+#if CLIENT_NMCTXS > 0
        int i;
 #endif
 
        manager = isc_mem_get(mctx, sizeof(*manager));
+       *manager = (ns_clientmgr_t) { .magic = 0 };
 
        isc_mutex_init(&manager->lock);
-       isc_mutex_init(&manager->listlock);
        isc_mutex_init(&manager->reclock);
 
        manager->excl = NULL;
@@ -3659,17 +2419,24 @@ ns_clientmgr_create(isc_mem_t *mctx, ns_server_t *sctx, isc_taskmgr_t *taskmgr,
        manager->mctx = mctx;
        manager->taskmgr = taskmgr;
        manager->timermgr = timermgr;
-       manager->exiting = false;
 
+       ns_interface_attach(interface, &manager->interface);
+
+       manager->exiting = false;
+       manager->taskpool =
+               isc_mem_get(mctx, CLIENT_NTASKS*sizeof(isc_task_t *));
+       for (i = 0; i < CLIENT_NTASKS; i++) {
+               manager->taskpool[i] = NULL;
+               isc_task_create(manager->taskmgr, 20, &manager->taskpool[i]);
+       }
+       isc_refcount_init(&manager->references, 1);
        manager->sctx = NULL;
        ns_server_attach(sctx, &manager->sctx);
 
-       ISC_LIST_INIT(manager->clients);
        ISC_LIST_INIT(manager->recursing);
-       ISC_QUEUE_INIT(manager->inactive, ilink);
-#if NMCTXS > 0
+#if CLIENT_NMCTXS > 0
        manager->nextmctx = 0;
-       for (i = 0; i < NMCTXS; i++)
+       for (i = 0; i < CLIENT_NMCTXS; i++)
                manager->mctxpool[i] = NULL; /* will be created on-demand */
 #endif
        manager->magic = MANAGER_MAGIC;
@@ -3682,7 +2449,6 @@ ns_clientmgr_create(isc_mem_t *mctx, ns_server_t *sctx, isc_taskmgr_t *taskmgr,
 
  cleanup_reclock:
        isc_mutex_destroy(&manager->reclock);
-       isc_mutex_destroy(&manager->listlock);
        isc_mutex_destroy(&manager->lock);
 
        isc_mem_put(manager->mctx, manager, sizeof(*manager));
@@ -3694,8 +2460,8 @@ void
 ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
        isc_result_t result;
        ns_clientmgr_t *manager;
-       ns_client_t *client;
-       bool need_destroy = false, unlock = false;
+       bool unlock = false;
+       int32_t oldrefs;
 
        REQUIRE(managerp != NULL);
        manager = *managerp;
@@ -3703,246 +2469,29 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
 
        MTRACE("destroy");
 
+       /* XXXWPK TODO we need to pause netmgr here */
        /*
         * Check for success because we may already be task-exclusive
         * at this point.  Only if we succeed at obtaining an exclusive
         * lock now will we need to relinquish it later.
         */
        result = isc_task_beginexclusive(manager->excl);
-       if (result == ISC_R_SUCCESS)
+       if (result == ISC_R_SUCCESS) {
                unlock = true;
+       }
 
        manager->exiting = true;
 
-       for (client = ISC_LIST_HEAD(manager->clients);
-            client != NULL;
-            client = ISC_LIST_NEXT(client, link))
-               isc_task_shutdown(client->task);
-
-       if (ISC_LIST_EMPTY(manager->clients))
-               need_destroy = true;
-
-       if (unlock)
+       if (unlock) {
                isc_task_endexclusive(manager->excl);
-
-       if (need_destroy)
-               clientmgr_destroy(manager);
-
-       *managerp = NULL;
-}
-
-static isc_result_t
-get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
-          dns_dispatch_t *disp, bool tcp)
-{
-       isc_result_t result = ISC_R_SUCCESS;
-       isc_event_t *ev;
-       ns_client_t *client;
-       MTRACE("get client");
-
-       REQUIRE(manager != NULL);
-
-       if (manager->exiting)
-               return (ISC_R_SHUTTINGDOWN);
-
-       /*
-        * Allocate a client.  First try to get a recycled one;
-        * if that fails, make a new one.
-        */
-       client = NULL;
-       if ((manager->sctx->options & NS_SERVER_CLIENTTEST) == 0) {
-               ISC_QUEUE_POP(manager->inactive, ilink, client);
-       }
-
-       if (client != NULL) {
-               MTRACE("recycle");
-       } else {
-               MTRACE("create new");
-
-               LOCK(&manager->lock);
-               result = client_create(manager, &client);
-               UNLOCK(&manager->lock);
-               if (result != ISC_R_SUCCESS)
-                       return (result);
-
-               LOCK(&manager->listlock);
-               ISC_LIST_APPEND(manager->clients, client, link);
-               UNLOCK(&manager->listlock);
-       }
-
-       client->manager = manager;
-       ns_interface_attach(ifp, &client->interface);
-       client->state = NS_CLIENTSTATE_READY;
-       client->sctx = manager->sctx;
-       INSIST(client->recursionquota == NULL);
-
-       client->dscp = ifp->dscp;
-       client->rcode_override = -1;    /* not set */
-
-       if (tcp) {
-               mark_tcp_active(client, true);
-
-               client->attributes |= NS_CLIENTATTR_TCP;
-               isc_socket_attach(ifp->tcpsocket,
-                                 &client->tcplistener);
-
-       } else {
-               isc_socket_t *sock;
-
-               dns_dispatch_attach(disp, &client->dispatch);
-               sock = dns_dispatch_getsocket(client->dispatch);
-               isc_socket_attach(sock, &client->udpsocket);
-       }
-
-       INSIST(client->nctls == 0);
-       client->nctls++;
-       ev = &client->ctlevent;
-       isc_task_send(client->task, &ev);
-
-       return (result);
-}
-
-static isc_result_t
-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;
-       ns_client_t *client;
-       MTRACE("get worker");
-
-       REQUIRE(manager != NULL);
-       REQUIRE(oldclient != NULL);
-
-       if (manager->exiting)
-               return (ISC_R_SHUTTINGDOWN);
-
-       /*
-        * Allocate a client.  First try to get a recycled one;
-        * if that fails, make a new one.
-        */
-       client = NULL;
-       if ((manager->sctx->options & NS_SERVER_CLIENTTEST) == 0)
-               ISC_QUEUE_POP(manager->inactive, ilink, client);
-
-       if (client != NULL)
-               MTRACE("recycle");
-       else {
-               MTRACE("create new");
-
-               LOCK(&manager->lock);
-               result = client_create(manager, &client);
-               UNLOCK(&manager->lock);
-               if (result != ISC_R_SUCCESS)
-                       return (result);
-
-               LOCK(&manager->listlock);
-               ISC_LIST_APPEND(manager->clients, client, link);
-               UNLOCK(&manager->listlock);
-       }
-
-       client->manager = manager;
-       ns_interface_attach(ifp, &client->interface);
-       client->newstate = client->state = NS_CLIENTSTATE_WORKING;
-       INSIST(client->recursionquota == NULL);
-       client->sctx = manager->sctx;
-
-       client->dscp = ifp->dscp;
-
-       client->attributes |= NS_CLIENTATTR_TCP;
-       client->mortal = true;
-       client->sendcb = NULL;
-       client->rcode_override = -1;    /* not set */
-
-       tcpconn_attach(oldclient, client);
-       mark_tcp_active(client, true);
-
-       isc_socket_attach(ifp->tcpsocket, &client->tcplistener);
-       isc_socket_attach(sock, &client->tcpsocket);
-       isc_socket_setname(client->tcpsocket, "worker-tcp", NULL);
-       (void)isc_socket_getpeername(client->tcpsocket, &client->peeraddr);
-       client->peeraddr_valid = true;
-
-       INSIST(client->tcpmsg_valid == false);
-       dns_tcpmsg_init(client->mctx, client->tcpsocket, &client->tcpmsg);
-       client->tcpmsg_valid = true;
-
-       INSIST(client->nctls == 0);
-       client->nctls++;
-       ev = &client->ctlevent;
-       isc_task_send(client->task, &ev);
-
-       return (result);
-}
-
-isc_result_t
-ns__clientmgr_getclient(ns_clientmgr_t *manager, ns_interface_t *ifp,
-                       bool tcp, ns_client_t **clientp)
-{
-       isc_result_t result = ISC_R_SUCCESS;
-       ns_client_t *client;
-       MTRACE("getclient");
-
-       REQUIRE(VALID_MANAGER(manager));
-       REQUIRE(clientp != NULL && *clientp == NULL);
-
-       if (manager->exiting)
-               return (ISC_R_SHUTTINGDOWN);
-
-       client = NULL;
-       ISC_QUEUE_POP(manager->inactive, ilink, client);
-       if (client != NULL)
-               MTRACE("getclient (recycle)");
-       else {
-               MTRACE("getclient (create)");
-
-               LOCK(&manager->lock);
-               result = client_create(manager, &client);
-               UNLOCK(&manager->lock);
-               if (result != ISC_R_SUCCESS)
-                       return (result);
-
-               LOCK(&manager->listlock);
-               ISC_LIST_APPEND(manager->clients, client, link);
-               UNLOCK(&manager->listlock);
-       }
-
-       client->manager = manager;
-       ns_interface_attach(ifp, &client->interface);
-       client->state = NS_CLIENTSTATE_READY;
-       INSIST(client->recursionquota == NULL);
-
-       client->dscp = ifp->dscp;
-       isc_refcount_increment(&client->references);
-
-       if (tcp) {
-               client->attributes |= NS_CLIENTATTR_TCP;
        }
 
-       *clientp = client;
-
-       return (result);
-}
-
-isc_result_t
-ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
-                          ns_interface_t *ifp, bool tcp)
-{
-       isc_result_t result = ISC_R_SUCCESS;
-       unsigned int disp;
-
-       REQUIRE(VALID_MANAGER(manager));
-       REQUIRE(n > 0);
-
-       MTRACE("createclients");
-
-       for (disp = 0; disp < n; disp++) {
-               result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
-               if (result != ISC_R_SUCCESS)
-                       break;
+       oldrefs = isc_refcount_decrement(&manager->references);
+       if (oldrefs == 1) {
+               clientmgr_destroy(manager);
        }
 
-       return (result);
+       *managerp = NULL;
 }
 
 isc_sockaddr_t *
@@ -3960,7 +2509,8 @@ ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
                         dns_acl_t *acl, bool default_allow)
 {
        isc_result_t result;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_aclenv_t *env =
+               ns_interfacemgr_getaclenv(client->manager->interface->mgr);
        isc_netaddr_t tmpnetaddr;
        int match;
 
index c75eafb6559e2c2750188df40f82b2e531919642..0ad452ad8a1775af668b5745716495e79553f67c 100644 (file)
  * notified of this by calling one of the following functions
  * exactly once in the context of its task:
  * \code
- *   ns_client_send()  (sending a non-error response)
+ *   ns_client_send()   (sending a non-error response)
  *   ns_client_sendraw() (sending a raw response)
- *   ns_client_error() (sending an error response)
- *   ns_client_next()  (sending no response)
+ *   ns_client_error()  (sending an error response)
+ *   ns_client_drop() (sending no response, logging the reason)
  *\endcode
  * This will release any resources used by the request and
  * and allow the ns_client_t to listen for the next request.
@@ -60,6 +60,7 @@
 #include <isc/buffer.h>
 #include <isc/list.h>
 #include <isc/magic.h>
+#include <isc/netmgr.h>
 #include <isc/stdtime.h>
 #include <isc/quota.h>
 #include <isc/platform.h>
  *** Types
  ***/
 
-/*% reference-counted TCP connection object */
-typedef struct ns_tcpconn {
-       isc_refcount_t          refs;
-       isc_quota_t             *tcpquota;
-       bool                    pipelined;
-} ns_tcpconn_t;
+#define NS_CLIENT_TCP_BUFFER_SIZE                      (65535 + 2)
+#define NS_CLIENT_SEND_BUFFER_SIZE             4096
+#define NS_CLIENT_RECV_BUFFER_SIZE             4096
+
+#define CLIENT_NMCTXS                          100
+/*%<
+ * Number of 'mctx pools' for clients. (Should this be configurable?)
+ * When enabling threads, we use a pool of memory contexts shared by
+ * client objects, since concurrent access to a shared context would cause
+ * heavy contentions.  The above constant is expected to be enough for
+ * completely avoiding contentions among threads for an authoritative-only
+ * server.
+ */
+
+#define CLIENT_NTASKS                          100
+/*%<
+ * Number of tasks to be used by clients - those are used only when recursing
+ */
+
+/*!
+ * Client object states.  Ordering is significant: higher-numbered
+ * states are generally "more active", meaning that the client can
+ * have more dynamically allocated data, outstanding events, etc.
+ * In the list below, any such properties listed for state N
+ * also apply to any state > N.
+ */
+
+typedef enum {
+       NS_CLIENTSTATE_FREED = 0,
+       /*%<
+        * The client object no longer exists.
+        */
+
+       NS_CLIENTSTATE_INACTIVE = 1,
+       /*%<
+        * The client object exists and has a task and timer.
+        * Its "query" struct and sendbuf are initialized.
+        * It has a message and OPT, both in the reset state.
+        */
+
+       NS_CLIENTSTATE_READY = 2,
+       /*%<
+        * The client object is either a TCP or a UDP one, and
+        * it is associated with a network interface.  It is on the
+        * client manager's list of active clients.
+        *
+        * If it is a TCP client object, it has a TCP listener socket
+        * and an outstanding TCP listen request.
+        *
+        * If it is a UDP client object, it has a UDP listener socket
+        * and an outstanding UDP receive request.
+        */
+
+       NS_CLIENTSTATE_WORKING = 3,
+       /*%<
+        * The client object has received a request and is working
+        * on it.  It has a view, and it may have any of a non-reset OPT,
+        * recursion quota, and an outstanding write request.
+        */
+
+       NS_CLIENTSTATE_RECURSING = 4,
+       /*%<
+        * The client object is recursing.  It will be on the
+        * 'recursing' list.
+        */
+
+       NS_CLIENTSTATE_MAX = 5
+       /*%<
+        * Sentinel value used to indicate "no state".
+        */
+} ns_clientstate_t;
+
+typedef ISC_LIST(ns_client_t) client_list_t;
+
+/*% nameserver client manager structure */
+struct ns_clientmgr {
+       /* Unlocked. */
+       unsigned int            magic;
+
+       isc_mem_t *             mctx;
+       ns_server_t *           sctx;
+       isc_taskmgr_t *         taskmgr;
+       isc_timermgr_t *        timermgr;
+       isc_task_t *            excl;
+       isc_refcount_t          references;
+
+       /* Attached by clients, needed for e.g. recursion */
+       isc_task_t **           taskpool;
+
+       ns_interface_t          *interface;
+
+       /* Lock covers manager state. */
+       isc_mutex_t             lock;
+       bool                    exiting;
+
+       /* Lock covers the recursing list */
+       isc_mutex_t             reclock;
+       client_list_t           recursing;    /*%< Recursing clients */
+
+#if CLIENT_NMCTXS > 0
+       /*%< mctx pool for clients. */
+       unsigned int            nextmctx;
+       isc_mem_t *             mctxpool[CLIENT_NMCTXS];
+#endif
+};
 
 /*% nameserver client structure */
 struct ns_client {
        unsigned int            magic;
        isc_mem_t               *mctx;
+       bool                    allocated;      /* Do we need to free it? */
        ns_server_t             *sctx;
        ns_clientmgr_t          *manager;
-       int                     state;
-       int                     newstate;
+       ns_clientstate_t        state;
        int                     naccepts;
        int                     nreads;
        int                     nsends;
        int                     nrecvs;
        int                     nupdates;
        int                     nctls;
-       isc_refcount_t          references;
-       bool                    tcpactive;
-       bool                    needshutdown;   /*
-                                                * Used by clienttest to get
-                                                * the client to go from
-                                                * inactive to free state
-                                                * by shutting down the
-                                                * client's task.
-                                                */
+       bool                    shuttingdown;
        unsigned int            attributes;
        isc_task_t              *task;
        dns_view_t              *view;
        dns_dispatch_t          *dispatch;
-       isc_socket_t            *udpsocket;
-       isc_socket_t            *tcplistener;
-       isc_socket_t            *tcpsocket;
+       isc_nmhandle_t          *handle;
        unsigned char           *tcpbuf;
-       dns_tcpmsg_t            tcpmsg;
-       bool                    tcpmsg_valid;
-       isc_timer_t             *timer;
-       isc_timer_t             *delaytimer;
-       bool                    timerset;
        dns_message_t           *message;
-       isc_socketevent_t       *sendevent;
-       isc_socketevent_t       *recvevent;
        unsigned char           *recvbuf;
+       unsigned char           sendbuf[NS_CLIENT_SEND_BUFFER_SIZE];
        dns_rdataset_t          *opt;
        uint16_t                udpsize;
        uint16_t                extflags;
        int16_t                 ednsversion;    /* -1 noedns */
-       void                    (*next)(ns_client_t *);
+       void                    (*cleanup)(ns_client_t *);
        void                    (*shutdown)(void *arg, isc_result_t result);
        void                    *shutdown_arg;
        ns_query_t              query;
@@ -141,9 +225,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 */
-       ns_tcpconn_t            *tcpconn;
        isc_quota_t             *recursionquota;
-       ns_interface_t          *interface;
 
        isc_sockaddr_t          peeraddr;
        bool                    peeraddr_valid;
@@ -154,7 +236,6 @@ struct ns_client {
 
        struct in6_pktinfo      pktinfo;
        isc_dscp_t              dscp;
-       isc_event_t             ctlevent;
        /*%
         * Information about recent FORMERR response(s), for
         * FORMERR loop avoidance.  This is separate for each
@@ -170,9 +251,7 @@ struct ns_client {
        /*% Callback function to send a response when unit testing */
        void                    (*sendcb)(isc_buffer_t *buf);
 
-       ISC_LINK(ns_client_t)   link;
        ISC_LINK(ns_client_t)   rlink;
-       ISC_QLINK(ns_client_t)  ilink;
        unsigned char           cookie[8];
        uint32_t                expire;
        unsigned char           *keytag;
@@ -187,9 +266,6 @@ struct ns_client {
        int32_t                 rcode_override;
 };
 
-typedef ISC_QUEUE(ns_client_t) client_queue_t;
-typedef ISC_LIST(ns_client_t) client_list_t;
-
 #define NS_CLIENT_MAGIC                        ISC_MAGIC('N','S','C','c')
 #define NS_CLIENT_VALID(c)             ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC)
 
@@ -256,10 +332,10 @@ ns_client_error(ns_client_t *client, isc_result_t result);
  */
 
 void
-ns_client_next(ns_client_t *client, isc_result_t result);
+ns_client_drop(ns_client_t *client, isc_result_t result);
 /*%<
- * Finish processing the current client request,
- * return no response to the client.
+ * Log the reason the current client request has failed; no response
+ * will be sent.
  */
 
 bool
@@ -268,18 +344,6 @@ ns_client_shuttingdown(ns_client_t *client);
  * Return true iff the client is currently shutting down.
  */
 
-void
-ns_client_attach(ns_client_t *source, ns_client_t **target);
-/*%<
- * Attach '*targetp' to 'source'.
- */
-
-void
-ns_client_detach(ns_client_t **clientp);
-/*%<
- * Detach '*clientp' from its client.
- */
-
 isc_result_t
 ns_client_replace(ns_client_t *client);
 /*%<
@@ -296,7 +360,8 @@ ns_client_settimeout(ns_client_t *client, unsigned int seconds);
 
 isc_result_t
 ns_clientmgr_create(isc_mem_t *mctx, ns_server_t *sctx, isc_taskmgr_t *taskmgr,
-                   isc_timermgr_t *timermgr, ns_clientmgr_t **managerp);
+                   isc_timermgr_t *timermgr, ns_interface_t *ifp,
+                   ns_clientmgr_t **managerp);
 /*%<
  * Create a client manager.
  */
@@ -308,15 +373,6 @@ ns_clientmgr_destroy(ns_clientmgr_t **managerp);
  * managed by it.
  */
 
-isc_result_t
-ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
-                          ns_interface_t *ifp, bool tcp);
-/*%<
- * Create up to 'n' clients listening on interface 'ifp'.
- * If 'tcp' is true, the clients will listen for TCP connections,
- * otherwise for UDP requests.
- */
-
 isc_sockaddr_t *
 ns_client_getsockaddr(ns_client_t *client);
 /*%<
@@ -427,16 +483,14 @@ isc_result_t
 ns_client_addopt(ns_client_t *client, dns_message_t *message,
                 dns_rdataset_t **opt);
 
-isc_result_t
-ns__clientmgr_getclient(ns_clientmgr_t *manager, ns_interface_t *ifp,
-                       bool tcp, ns_client_t **clientp);
 /*
  * Get a client object from the inactive queue, or create one, as needed.
  * (Not intended for use outside this module and associated tests.)
  */
 
 void
-ns__client_request(isc_task_t *task, isc_event_t *event);
+ns__client_request(isc_nmhandle_t *handle, isc_region_t *region, void *arg);
+
 /*
  * Handle client requests.
  * (Not intended for use outside this module and associated tests.)
@@ -508,4 +562,24 @@ ns_client_findversion(ns_client_t *client, dns_db_t *db);
  * allocated by ns_client_newdbversion().
  */
 
+isc_result_t
+ns__client_setup(ns_client_t *client, ns_clientmgr_t *manager, bool new);
+/*%<
+ * Perform initial setup of an allocated client.
+ */
+
+void
+ns__client_reset_cb(void *client0);
+/*%<
+ * Reset the client object so that it can be reused.
+ */
+
+void
+ns__client_put_cb(void *client0);
+/*%<
+ * Free all resources allocated to this client object, so that
+ * it can be freed.
+ */
+
+
 #endif /* NS_CLIENT_H */
index 82d76f9e41d4939c90394073772fb0d86488311c..0212460cc4d3cc351de4b499288199614adba834 100644 (file)
@@ -44,6 +44,7 @@
 
 #include <isc/magic.h>
 #include <isc/mem.h>
+#include <isc/netmgr.h>
 #include <isc/socket.h>
 #include <isc/refcount.h>
 
 /*% The nameserver interface structure */
 struct ns_interface {
        unsigned int            magic;          /*%< Magic number. */
-       ns_interfacemgr_t *     mgr;            /*%< Interface manager. */
+       ns_interfacemgr_t       *mgr;           /*%< Interface manager. */
        isc_mutex_t             lock;
        isc_refcount_t          references;
        unsigned int            generation;     /*%< Generation number. */
        isc_sockaddr_t          addr;           /*%< Address and port. */
-       unsigned int            flags;          /*%< Interface characteristics */
+       unsigned int            flags;          /*%< Interface flags */
        char                    name[32];       /*%< Null terminated. */
-       dns_dispatch_t *        udpdispatch[MAX_UDP_DISPATCH];
+       dns_dispatch_t          *udpdispatch[MAX_UDP_DISPATCH];
                                                /*%< UDP dispatchers. */
-       isc_socket_t *          tcpsocket;      /*%< TCP socket. */
+       isc_socket_t            *tcpsocket;     /*%< TCP socket. */
+       isc_nmsocket_t          *udplistensocket;
+       isc_nmsocket_t          *tcplistensocket;
        isc_dscp_t              dscp;           /*%< "listen-on" DSCP value */
        isc_refcount_t          ntcpaccepting;  /*%< Number of clients
                                                     ready to accept new
@@ -86,7 +89,7 @@ struct ns_interface {
                                                     (whether accepting or
                                                     connected) */
        int                     nudpdispatch;   /*%< Number of UDP dispatches */
-       ns_clientmgr_t *        clientmgr;      /*%< Client manager. */
+       ns_clientmgr_t          *clientmgr;     /*%< Client manager. */
        ISC_LINK(ns_interface_t) link;
 };
 
@@ -95,15 +98,11 @@ struct ns_interface {
  ***/
 
 isc_result_t
-ns_interfacemgr_create(isc_mem_t *mctx,
-                      ns_server_t *sctx,
-                      isc_taskmgr_t *taskmgr,
-                      isc_timermgr_t *timermgr,
-                      isc_socketmgr_t *socketmgr,
-                      dns_dispatchmgr_t *dispatchmgr,
-                      isc_task_t *task,
-                      unsigned int udpdisp,
-                      dns_geoip_databases_t *geoip,
+ns_interfacemgr_create(isc_mem_t *mctx, ns_server_t *sctx,
+                      isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
+                      isc_socketmgr_t *socketmgr, isc_nm_t *nm,
+                      dns_dispatchmgr_t *dispatchmgr, isc_task_t *task,
+                      unsigned int udpdisp, dns_geoip_databases_t *geoip,
                       ns_interfacemgr_t **mgrp);
 /*%<
  * Create a new interface manager.
index c4a0f71ecdade5e97e9099e3c9465aa569676645..fa27bdd446149ac09021ab56a3d726cf2f78b9eb 100644 (file)
@@ -36,7 +36,6 @@
 #define NS_SERVER_NOAA         0x00000002U     /*%< -T noaa */
 #define NS_SERVER_NOSOA                0x00000004U     /*%< -T nosoa */
 #define NS_SERVER_NONEAREST    0x00000008U     /*%< -T nonearest */
-#define NS_SERVER_CLIENTTEST   0x00000010U     /*%< -T clienttest */
 #define NS_SERVER_NOEDNS       0x00000020U     /*%< -T noedns */
 #define NS_SERVER_DROPEDNS     0x00000040U     /*%< -T dropedns */
 #define NS_SERVER_NOTCP                0x00000080U     /*%< -T notcp */
index a473d50eea9f56a4d197e51776b3dd4743ff507e..0d932857bb644ef60b1ff8348a1e0da61b01d30a 100644 (file)
@@ -14,6 +14,7 @@
 #include <stdbool.h>
 
 #include <isc/interfaceiter.h>
+#include <isc/netmgr.h>
 #include <isc/os.h>
 #include <isc/random.h>
 #include <isc/string.h>
@@ -72,6 +73,7 @@ struct ns_interfacemgr {
        isc_task_t *            excl;           /*%< Exclusive task. */
        isc_timermgr_t *        timermgr;       /*%< Timer manager. */
        isc_socketmgr_t *       socketmgr;      /*%< Socket manager. */
+       isc_nm_t *              nm;             /*%< Net manager. */
        dns_dispatchmgr_t *     dispatchmgr;
        unsigned int            generation;     /*%< Current generation no. */
        ns_listenlist_t *       listenon4;
@@ -172,6 +174,7 @@ ns_interfacemgr_create(isc_mem_t *mctx,
                       isc_taskmgr_t *taskmgr,
                       isc_timermgr_t *timermgr,
                       isc_socketmgr_t *socketmgr,
+                      isc_nm_t *nm,
                       dns_dispatchmgr_t *dispatchmgr,
                       isc_task_t *task,
                       unsigned int udpdisp,
@@ -208,6 +211,7 @@ ns_interfacemgr_create(isc_mem_t *mctx,
        mgr->taskmgr = taskmgr;
        mgr->timermgr = timermgr;
        mgr->socketmgr = socketmgr;
+       mgr->nm = nm;
        mgr->dispatchmgr = dispatchmgr;
        mgr->generation = 1;
        mgr->listenon4 = NULL;
@@ -249,8 +253,9 @@ ns_interfacemgr_create(isc_mem_t *mctx,
        }
 
        mgr->task = NULL;
-       if (mgr->route != NULL)
+       if (mgr->route != NULL) {
                isc_task_attach(task, &mgr->task);
+       }
        isc_refcount_init(&mgr->references, (mgr->route != NULL) ? 2 : 1);
 #else
        isc_refcount_init(&mgr->references, 1);
@@ -379,30 +384,19 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
        REQUIRE(NS_INTERFACEMGR_VALID(mgr));
 
        ifp = isc_mem_get(mgr->mctx, sizeof(*ifp));
+       *ifp = (ns_interface_t){
+               .generation = mgr->generation,
+               .addr = *addr,
+               .dscp = -1
+       };
 
-       ifp->mgr = NULL;
-       ifp->generation = mgr->generation;
-       ifp->addr = *addr;
-       ifp->flags = 0;
        strlcpy(ifp->name, name, sizeof(ifp->name));
-       ifp->clientmgr = NULL;
 
        isc_mutex_init(&ifp->lock);
 
-       result = ns_clientmgr_create(mgr->mctx, mgr->sctx,
-                                    mgr->taskmgr, mgr->timermgr,
-                                    &ifp->clientmgr);
-       if (result != ISC_R_SUCCESS) {
-               isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
-                             "ns_clientmgr_create() failed: %s",
-                             isc_result_totext(result));
-               goto clientmgr_create_failure;
-       }
-
-       for (disp = 0; disp < MAX_UDP_DISPATCH; disp++)
+       for (disp = 0; disp < MAX_UDP_DISPATCH; disp++) {
                ifp->udpdispatch[disp] = NULL;
-
-       ifp->tcpsocket = NULL;
+       }
 
        /*
         * Create a single TCP client object.  It will replace itself
@@ -413,10 +407,6 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
        isc_refcount_init(&ifp->ntcpaccepting, 0);
        isc_refcount_init(&ifp->ntcpactive, 0);
 
-       ifp->nudpdispatch = 0;
-
-       ifp->dscp = -1;
-
        ISC_LINK_INIT(ifp, link);
 
        ns_interfacemgr_attach(mgr, &ifp->mgr);
@@ -424,11 +414,22 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
 
        isc_refcount_init(&ifp->references, 1);
        ifp->magic = IFACE_MAGIC;
+
+       result = ns_clientmgr_create(mgr->mctx, mgr->sctx,
+                                    mgr->taskmgr, mgr->timermgr, ifp,
+                                    &ifp->clientmgr);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+                             "ns_clientmgr_create() failed: %s",
+                             isc_result_totext(result));
+               goto failure;
+       }
+
        *ifpret = ifp;
 
        return (ISC_R_SUCCESS);
 
clientmgr_create_failure:
+ failure:
        isc_mutex_destroy(&ifp->lock);
 
        ifp->magic = 0;
@@ -440,127 +441,43 @@ ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
 static isc_result_t
 ns_interface_listenudp(ns_interface_t *ifp) {
        isc_result_t result;
-       unsigned int attrs;
-       unsigned int attrmask;
-       int disp, i;
-
-       attrs = 0;
-       attrs |= DNS_DISPATCHATTR_UDP;
-       if (isc_sockaddr_pf(&ifp->addr) == AF_INET)
-               attrs |= DNS_DISPATCHATTR_IPV4;
-       else
-               attrs |= DNS_DISPATCHATTR_IPV6;
-       attrs |= DNS_DISPATCHATTR_NOLISTEN;
-       attrs |= DNS_DISPATCHATTR_CANREUSE;
-       attrmask = 0;
-       attrmask |= DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
-       attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
-
-       ifp->nudpdispatch = ISC_MIN(ifp->mgr->udpdisp, MAX_UDP_DISPATCH);
-       for (disp = 0; disp < ifp->nudpdispatch; disp++) {
-               result = dns_dispatch_getudp_dup(ifp->mgr->dispatchmgr,
-                                                ifp->mgr->socketmgr,
-                                                ifp->mgr->taskmgr, &ifp->addr,
-                                                4096, UDPBUFFERS,
-                                                32768, 8219, 8237,
-                                                attrs, attrmask,
-                                                &ifp->udpdispatch[disp],
-                                                disp == 0
-                                                   ? NULL
-                                                   : ifp->udpdispatch[0]);
-               if (result != ISC_R_SUCCESS) {
-                       isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
-                                     "could not listen on UDP socket: %s",
-                                     isc_result_totext(result));
-                       goto udp_dispatch_failure;
-               }
-
-       }
-
-       result = ns_clientmgr_createclients(ifp->clientmgr, ifp->nudpdispatch,
-                                           ifp, false);
-       if (result != ISC_R_SUCCESS) {
-               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                "UDP ns_clientmgr_createclients(): %s",
-                                isc_result_totext(result));
-               goto addtodispatch_failure;
-       }
-
-       return (ISC_R_SUCCESS);
-
- addtodispatch_failure:
-       for (i = disp - 1; i >= 0; i--) {
-               dns_dispatch_changeattributes(ifp->udpdispatch[i], 0,
-                                             DNS_DISPATCHATTR_NOLISTEN);
-               dns_dispatch_detach(&(ifp->udpdispatch[i]));
-       }
-       ifp->nudpdispatch = 0;
 
- udp_dispatch_failure:
+       /* Reserve space for an ns_client_t with the netmgr handle */
+       result = isc_nm_listenudp(ifp->mgr->nm,
+                                 (isc_nmiface_t *) &ifp->addr,
+                                 ns__client_request, ifp,
+                                 sizeof(ns_client_t),
+                                 &ifp->udplistensocket);
        return (result);
 }
 
 static isc_result_t
-ns_interface_accepttcp(ns_interface_t *ifp) {
+ns_interface_listentcp(ns_interface_t *ifp) {
        isc_result_t result;
 
-       /*
-        * Open a TCP socket.
-        */
-       result = isc_socket_create(ifp->mgr->socketmgr,
-                                  isc_sockaddr_pf(&ifp->addr),
-                                  isc_sockettype_tcp,
-                                  &ifp->tcpsocket);
+       /* Reserve space for an ns_client_t with the netmgr handle */
+       result = isc_nm_listentcpdns(ifp->mgr->nm,
+                                    (isc_nmiface_t *) &ifp->addr,
+                                    ns__client_request, ifp,
+                                    sizeof(ns_client_t),
+                                    &ifp->mgr->sctx->tcpquota,
+                                    &ifp->tcplistensocket);
        if (result != ISC_R_SUCCESS) {
                isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
                                 "creating TCP socket: %s",
                                 isc_result_totext(result));
-               goto tcp_socket_failure;
        }
-       isc_socket_setname(ifp->tcpsocket, "dispatcher", NULL);
+
+#if 0
 #ifndef ISC_ALLOW_MAPPED
        isc_socket_ipv6only(ifp->tcpsocket, true);
 #endif
-       result = isc_socket_bind(ifp->tcpsocket, &ifp->addr,
-                                ISC_SOCKET_REUSEADDRESS);
-       if (result != ISC_R_SUCCESS) {
-               isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
-                                "binding TCP socket: %s",
-                                isc_result_totext(result));
-               goto tcp_bind_failure;
-       }
 
        if (ifp->dscp != -1)
                isc_socket_dscp(ifp->tcpsocket, ifp->dscp);
 
-       result = isc_socket_listen(ifp->tcpsocket, ifp->mgr->backlog);
-       if (result != ISC_R_SUCCESS) {
-               isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
-                                "listening on TCP socket: %s",
-                                isc_result_totext(result));
-               goto tcp_listen_failure;
-       }
-
-       /*
-        * If/when there a multiple filters listen to the
-        * result.
-        */
        (void)isc_socket_filter(ifp->tcpsocket, "dataready");
-
-       result = ns_clientmgr_createclients(ifp->clientmgr, 1, ifp, true);
-       if (result != ISC_R_SUCCESS) {
-               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                "TCP ns_clientmgr_createclients(): %s",
-                                isc_result_totext(result));
-               goto accepttcp_failure;
-       }
-       return (ISC_R_SUCCESS);
-
- accepttcp_failure:
- tcp_listen_failure:
- tcp_bind_failure:
-       isc_socket_detach(&ifp->tcpsocket);
- tcp_socket_failure:
+#endif
        return (result);
 }
 
@@ -591,7 +508,7 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
        if (((mgr->sctx->options & NS_SERVER_NOTCP) == 0) &&
            accept_tcp == true)
        {
-               result = ns_interface_accepttcp(ifp);
+               result = ns_interface_listentcp(ifp);
                if (result != ISC_R_SUCCESS) {
                        if ((result == ISC_R_ADDRINUSE) &&
                            (addr_in_use != NULL))
@@ -617,8 +534,17 @@ ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
 
 void
 ns_interface_shutdown(ns_interface_t *ifp) {
-       if (ifp->clientmgr != NULL)
+       if (ifp->udplistensocket != NULL) {
+               isc_nm_udp_stoplistening(ifp->udplistensocket);
+               isc_nmsocket_detach(&ifp->udplistensocket);
+       }
+       if (ifp->tcplistensocket != NULL) {
+               isc_nm_tcpdns_stoplistening(ifp->tcplistensocket);
+               isc_nmsocket_detach(&ifp->tcplistensocket);
+       }
+       if (ifp->clientmgr != NULL) {
                ns_clientmgr_destroy(&ifp->clientmgr);
+       }
 }
 
 static void
index 714819d69be47b99514ae7363396caaa61d290f2..be4ef5dca79725734692d1541916daccd78f157f 100644 (file)
@@ -53,7 +53,8 @@ respond(ns_client_t *client, isc_result_t result) {
        if (msg_result != ISC_R_SUCCESS)
                msg_result = dns_message_reply(message, false);
        if (msg_result != ISC_R_SUCCESS) {
-               ns_client_next(client, msg_result);
+               ns_client_drop(client, msg_result);
+               isc_nmhandle_unref(client->handle);
                return;
        }
        message->rcode = rcode;
@@ -61,7 +62,9 @@ respond(ns_client_t *client, isc_result_t result) {
                message->flags |= DNS_MESSAGEFLAG_AA;
        else
                message->flags &= ~DNS_MESSAGEFLAG_AA;
+
        ns_client_send(client);
+       isc_nmhandle_unref(client->handle);
 }
 
 void
index 4e43b1d92c9c0a2722f2cb146bc0abb16c84383f..7adf04d96f4d25dad44b48fe3ba7ef94d9e83a18 100644 (file)
@@ -560,6 +560,7 @@ query_send(ns_client_t *client) {
 
        inc_stats(client, counter);
        ns_client_send(client);
+       isc_nmhandle_unref(client->handle);
 }
 
 static void
@@ -585,17 +586,20 @@ query_error(ns_client_t *client, isc_result_t result, int line) {
        log_queryerror(client, result, line, loglevel);
 
        ns_client_error(client, result);
+       isc_nmhandle_unref(client->handle);
 }
 
 static void
 query_next(ns_client_t *client, isc_result_t result) {
-       if (result == DNS_R_DUPLICATE)
+       if (result == DNS_R_DUPLICATE) {
                inc_stats(client, ns_statscounter_duplicate);
-       else if (result == DNS_R_DROP)
+       } else if (result == DNS_R_DROP) {
                inc_stats(client, ns_statscounter_dropped);
-       else
+       } else {
                inc_stats(client, ns_statscounter_failure);
-       ns_client_next(client, result);
+       }
+       ns_client_drop(client, result);
+       isc_nmhandle_unref(client->handle);
 }
 
 static inline void
@@ -655,7 +659,8 @@ query_reset(ns_client_t *client, bool everything) {
         */
        for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
             dbversion != NULL;
-            dbversion = dbversion_next) {
+            dbversion = dbversion_next)
+       {
                dbversion_next = ISC_LIST_NEXT(dbversion, link);
                dns_db_closeversion(dbversion->db, &dbversion->version,
                                    false);
@@ -742,7 +747,7 @@ query_reset(ns_client_t *client, bool everything) {
 }
 
 static void
-query_next_callback(ns_client_t *client) {
+query_cleanup(ns_client_t *client) {
        query_reset(client, false);
 }
 
@@ -2423,6 +2428,8 @@ free_devent(ns_client_t *client, isc_event_t **eventp,
 
        REQUIRE((void*)(*eventp) == (void *)(*deventp));
 
+       CTRACE(ISC_LOG_DEBUG(3), "free_devent");
+
        if (devent->fetch != NULL) {
                dns_resolver_destroyfetch(&devent->fetch);
        }
@@ -2438,12 +2445,14 @@ free_devent(ns_client_t *client, isc_event_t **eventp,
        if (devent->sigrdataset != NULL) {
                ns_client_putrdataset(client, &devent->sigrdataset);
        }
+
        /*
         * If the two pointers are the same then leave the setting of
         * (*deventp) to NULL to isc_event_free.
         */
-       if ((void *)eventp != (void *)deventp)
+       if ((void *)eventp != (void *)deventp) {
                (*deventp) = NULL;
+       }
        isc_event_free(eventp);
 }
 
@@ -2459,14 +2468,17 @@ prefetch_done(isc_task_t *task, isc_event_t *event) {
        REQUIRE(NS_CLIENT_VALID(client));
        REQUIRE(task == client->task);
 
+       CTRACE(ISC_LOG_DEBUG(3), "prefetch_done");
+
        LOCK(&client->query.fetchlock);
        if (client->query.prefetch != NULL) {
                INSIST(devent->fetch == client->query.prefetch);
                client->query.prefetch = NULL;
        }
        UNLOCK(&client->query.fetchlock);
+
        free_devent(client, &event, &devent);
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 }
 
 static void
@@ -2476,40 +2488,38 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
        isc_result_t result;
        isc_sockaddr_t *peeraddr;
        dns_rdataset_t *tmprdataset;
-       ns_client_t *dummy = NULL;
        unsigned int options;
 
+       CTRACE(ISC_LOG_DEBUG(3), "query_prefetch");
+
        if (client->query.prefetch != NULL ||
            client->view->prefetch_trigger == 0U ||
            rdataset->ttl > client->view->prefetch_trigger ||
            (rdataset->attributes & DNS_RDATASETATTR_PREFETCH) == 0)
+       {
                return;
+       }
 
        if (client->recursionquota == NULL) {
                result = isc_quota_attach(&client->sctx->recursionquota,
                                          &client->recursionquota);
-               if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
-                       ns_stats_increment(client->sctx->nsstats,
-                                          ns_statscounter_recursclients);
-               }
-               if (result == ISC_R_SUCCESS && !client->mortal &&
-                   !TCP(client))
-               {
-                       result = ns_client_replace(client);
-               }
                if (result != ISC_R_SUCCESS) {
                        return;
                }
        }
 
        tmprdataset = ns_client_newrdataset(client);
-       if (tmprdataset == NULL)
+       if (tmprdataset == NULL) {
                return;
-       if (!TCP(client))
+       }
+
+       if (!TCP(client)) {
                peeraddr = &client->peeraddr;
-       else
+       } else {
                peeraddr = NULL;
-       ns_client_attach(client, &dummy);
+       }
+
+       isc_nmhandle_ref(client->handle);
        options = client->query.fetchoptions | DNS_FETCHOPT_PREFETCH;
        result = dns_resolver_createfetch(client->view->resolver,
                                          qname, rdataset->type, NULL, NULL,
@@ -2520,8 +2530,9 @@ query_prefetch(ns_client_t *client, dns_name_t *qname,
                                          &client->query.prefetch);
        if (result != ISC_R_SUCCESS) {
                ns_client_putrdataset(client, &tmprdataset);
-               ns_client_detach(&dummy);
+               isc_nmhandle_unref(client->handle);
        }
+
        dns_rdataset_clearprefetch(rdataset);
        ns_stats_increment(client->sctx->nsstats,
                           ns_statscounter_prefetch);
@@ -2692,38 +2703,34 @@ query_rpzfetch(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t type) {
        isc_result_t result;
        isc_sockaddr_t *peeraddr;
        dns_rdataset_t *tmprdataset;
-       ns_client_t *dummy = NULL;
        unsigned int options;
 
+       CTRACE(ISC_LOG_DEBUG(3), "query_rpzfetch");
+
        if (client->query.prefetch != NULL)
                return;
 
        if (client->recursionquota == NULL) {
                result = isc_quota_attach(&client->sctx->recursionquota,
                                          &client->recursionquota);
-               if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) {
-                       ns_stats_increment(client->sctx->nsstats,
-                                          ns_statscounter_recursclients);
-               }
-               if (result == ISC_R_SUCCESS && !client->mortal &&
-                   !TCP(client))
-               {
-                       result = ns_client_replace(client);
-               }
                if (result != ISC_R_SUCCESS) {
                        return;
                }
        }
 
        tmprdataset = ns_client_newrdataset(client);
-       if (tmprdataset == NULL)
+       if (tmprdataset == NULL) {
                return;
-       if (!TCP(client))
+       }
+
+       if (!TCP(client)) {
                peeraddr = &client->peeraddr;
-       else
+       } else {
                peeraddr = NULL;
-       ns_client_attach(client, &dummy);
+       }
+
        options = client->query.fetchoptions;
+       isc_nmhandle_ref(client->handle);
        result = dns_resolver_createfetch(client->view->resolver, qname, type,
                                          NULL, NULL, NULL, peeraddr,
                                          client->message->id, options, 0,
@@ -2732,7 +2739,7 @@ query_rpzfetch(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t type) {
                                          &client->query.prefetch);
        if (result != ISC_R_SUCCESS) {
                ns_client_putrdataset(client, &tmprdataset);
-               ns_client_detach(&dummy);
+               isc_nmhandle_unref(client->handle);
        }
 }
 
@@ -4644,7 +4651,8 @@ dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
             dns_rdataset_t *sigrdataset)
 {
        isc_netaddr_t netaddr;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_aclenv_t *env =
+               ns_interfacemgr_getaclenv(client->manager->interface->mgr);
        dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64);
        unsigned int flags = 0;
        unsigned int i, count;
@@ -5001,6 +5009,7 @@ qctx_init(ns_client_t *client, dns_fetchevent_t *event,
 
        /* Set this first so CCTRACE will work */
        qctx->client = client;
+
        dns_view_attach(client->view, &qctx->view);
 
        CCTRACE(ISC_LOG_DEBUG(3), "qctx_init");
@@ -5078,9 +5087,6 @@ qctx_destroy(query_ctx_t *qctx) {
        CALL_HOOK_NORETURN(NS_QUERY_QCTX_DESTROYED, qctx);
 
        dns_view_detach(&qctx->view);
-       if (qctx->detach_client) {
-               ns_client_detach(&qctx->client);
-       }
 }
 
 /*%
@@ -5583,6 +5589,8 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
        REQUIRE(task == client->task);
        REQUIRE(RECURSING(client));
 
+       CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
+
        LOCK(&client->query.fetchlock);
        if (client->query.fetch != NULL) {
                /*
@@ -5605,9 +5613,28 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
        UNLOCK(&client->query.fetchlock);
        INSIST(client->query.fetch == NULL);
 
-       client->query.attributes &= ~NS_QUERYATTR_RECURSING;
        SAVE(fetch, devent->fetch);
 
+       /*
+        * We're done recursing, detach from quota and unlink from
+        * the manager's recursing-clients list.
+        */
+
+       if (client->recursionquota != NULL) {
+               isc_quota_detach(&client->recursionquota);
+               ns_stats_decrement(client->sctx->nsstats,
+                                  ns_statscounter_recursclients);
+       }
+
+       LOCK(&client->manager->reclock);
+       if (ISC_LINK_LINKED(client, rlink)) {
+               ISC_LIST_UNLINK(client->manager->recursing, client, rlink);
+       }
+       UNLOCK(&client->manager->reclock);
+
+       client->query.attributes &= ~NS_QUERYATTR_RECURSING;
+       client->state = NS_CLIENTSTATE_WORKING;
+
        /*
         * If this client is shutting down, or this transaction
         * has timed out, do not resume the find.
@@ -5621,10 +5648,6 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
                } else {
                        query_next(client, ISC_R_CANCELED);
                }
-               /*
-                * This may destroy the client.
-                */
-               ns_client_detach(&client);
        } else {
                query_ctx_t qctx;
 
@@ -5654,6 +5677,7 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
        }
 
        dns_resolver_destroyfetch(&fetch);
+       isc_nmhandle_unref(client->handle);
 }
 
 /*%
@@ -5733,8 +5757,9 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
 
        recparam_update(&client->query.recparam, qtype, qname, qdomain);
 
-       if (!resuming)
+       if (!resuming) {
                inc_stats(client, ns_statscounter_recursion);
+       }
 
        /*
         * We are about to recurse, which means that this client will
@@ -5793,22 +5818,10 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
                        }
                        ns_client_killoldestquery(client);
                }
-               if (result == ISC_R_SUCCESS && !client->mortal &&
-                   !TCP(client)) {
-                       result = ns_client_replace(client);
-                       if (result != ISC_R_SUCCESS) {
-                               ns_client_log(client, NS_LOGCATEGORY_CLIENT,
-                                             NS_LOGMODULE_QUERY,
-                                             ISC_LOG_WARNING,
-                                             "ns_client_replace() failed: %s",
-                                             isc_result_totext(result));
-                               isc_quota_detach(&client->recursionquota);
-                               ns_stats_decrement(client->sctx->nsstats,
-                                          ns_statscounter_recursclients);
-                       }
-               }
-               if (result != ISC_R_SUCCESS)
+               if (result != ISC_R_SUCCESS) {
                        return (result);
+               }
+
                ns_client_recursing(client);
        }
 
@@ -5841,6 +5854,7 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
                peeraddr = &client->peeraddr;
        }
 
+       isc_nmhandle_ref(client->handle);
        result = dns_resolver_createfetch(client->view->resolver,
                                          qname, qtype, qdomain, nameservers,
                                          NULL, peeraddr, client->message->id,
@@ -5849,6 +5863,7 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
                                          client, rdataset, sigrdataset,
                                          &client->query.fetch);
        if (result != ISC_R_SUCCESS) {
+               isc_nmhandle_unref(client->handle);
                ns_client_putrdataset(client, &rdataset);
                if (sigrdataset != NULL) {
                        ns_client_putrdataset(client, &sigrdataset);
@@ -5879,6 +5894,8 @@ query_resume(query_ctx_t *qctx) {
        char tbuf[DNS_RDATATYPE_FORMATSIZE];
 #endif
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_resume");
+
        CALL_HOOK(NS_QUERY_RESUME_BEGIN, qctx);
 
        qctx->want_restart = false;
@@ -6320,6 +6337,8 @@ static isc_result_t
 query_checkrpz(query_ctx_t *qctx, isc_result_t result) {
        isc_result_t rresult;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_checkrpz");
+
        rresult = rpz_rewrite(qctx->client, qctx->qtype,
                              result, qctx->resuming,
                              qctx->rdataset, qctx->sigrdataset);
@@ -6902,6 +6921,8 @@ query_respond_any(query_ctx_t *qctx) {
        dns_rdatatype_t onetype = 0;    /* type to use for minimal-any */
        isc_buffer_t b;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_respond_any");
+
        CALL_HOOK(NS_QUERY_RESPOND_ANY_BEGIN, qctx);
 
        result = dns_db_allrdatasets(qctx->db, qctx->node,
@@ -7123,6 +7144,8 @@ static void
 query_getexpire(query_ctx_t *qctx) {
        dns_zone_t *raw = NULL, *mayberaw;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_getexpire");
+
        if (qctx->zone == NULL || !qctx->is_zone ||
            qctx->qtype != dns_rdatatype_soa ||
            qctx->client->query.restarts != 0 ||
@@ -7177,6 +7200,8 @@ query_addanswer(query_ctx_t *qctx) {
        dns_rdataset_t **sigrdatasetp = NULL;
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_addanswer");
+
        CALL_HOOK(NS_QUERY_ADDANSWER_BEGIN, qctx);
 
        if (qctx->dns64) {
@@ -7237,6 +7262,8 @@ static isc_result_t
 query_respond(query_ctx_t *qctx) {
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_respond");
+
        /*
         * Check to see if the AAAA RRset has non-excluded addresses
         * in it.  If not look for a A RRset.
@@ -7332,7 +7359,8 @@ query_respond(query_ctx_t *qctx) {
 static isc_result_t
 query_dns64(query_ctx_t *qctx) {
        ns_client_t *client = qctx->client;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_aclenv_t *env =
+               ns_interfacemgr_getaclenv(client->manager->interface->mgr);
        dns_name_t *name, *mname;
        dns_rdata_t *dns64_rdata;
        dns_rdata_t rdata = DNS_RDATA_INIT;
@@ -7677,6 +7705,8 @@ static isc_result_t
 query_notfound(query_ctx_t *qctx) {
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_notfound");
+
        CALL_HOOK(NS_QUERY_NOTFOUND_BEGIN, qctx);
 
        INSIST(!qctx->is_zone);
@@ -7915,6 +7945,8 @@ static isc_result_t
 query_delegation(query_ctx_t *qctx) {
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_delegation");
+
        CALL_HOOK(NS_QUERY_DELEGATION_BEGIN, qctx);
 
        qctx->authoritative = false;
@@ -7989,6 +8021,8 @@ query_delegation_recurse(query_ctx_t *qctx) {
        isc_result_t result;
        dns_name_t *qname = qctx->client->query.qname;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_delegation_recurse");
+
        if (!RECURSIONOK(qctx->client)) {
                return (ISC_R_COMPLETE);
        }
@@ -8186,6 +8220,8 @@ static isc_result_t
 query_nodata(query_ctx_t *qctx, isc_result_t res) {
        isc_result_t result = res;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_nodata");
+
        CALL_HOOK(NS_QUERY_NODATA_BEGIN, qctx);
 
 #ifdef dns64_bis_return_excluded_addresses
@@ -8308,6 +8344,9 @@ query_nodata(query_ctx_t *qctx, isc_result_t res) {
 isc_result_t
 query_sign_nodata(query_ctx_t *qctx) {
        isc_result_t result;
+
+       CCTRACE(ISC_LOG_DEBUG(3), "query_sign_nodata");
+
        /*
         * Look for a NSEC3 record if we don't have a NSEC record.
         */
@@ -8504,6 +8543,8 @@ query_nxdomain(query_ctx_t *qctx, bool empty_wild) {
        uint32_t ttl;
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_nxdomain");
+
        CALL_HOOK(NS_QUERY_NXDOMAIN_BEGIN, qctx);
 
        INSIST(qctx->is_zone || REDIRECT(qctx->client));
@@ -8595,6 +8636,8 @@ static isc_result_t
 query_redirect(query_ctx_t *qctx)  {
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_redirect");
+
        result = redirect(qctx->client, qctx->fname, qctx->rdataset,
                          &qctx->node, &qctx->db, &qctx->version,
                          qctx->type);
@@ -8790,6 +8833,8 @@ query_synthwildcard(query_ctx_t *qctx, dns_rdataset_t *rdataset,
        dns_rdataset_t *cloneset = NULL, *clonesigset = NULL;
        dns_rdataset_t **sigrdatasetp;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_synthwildcard");
+
        /*
         * We want the answer to be first, so save the
         * NOQNAME proof's name now or else discard it.
@@ -8943,6 +8988,8 @@ query_synthnxdomain(query_ctx_t *qctx,
        isc_result_t result;
        dns_rdataset_t *cloneset = NULL, *clonesigset = NULL;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_synthnxdomain");
+
        /*
         * Detemine the correct TTL to use for the SOA and RRSIG
         */
@@ -9109,6 +9156,8 @@ query_coveringnsec(query_ctx_t *qctx) {
        isc_result_t result = ISC_R_SUCCESS;
        unsigned int dboptions = qctx->client->query.dboptions;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_coveringnsec");
+
        dns_rdataset_init(&rdataset);
        dns_rdataset_init(&sigrdataset);
 
@@ -9367,6 +9416,8 @@ query_ncache(query_ctx_t *qctx, isc_result_t result) {
               result == DNS_R_NCACHENXRRSET ||
               result == DNS_R_NXDOMAIN);
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_ncache");
+
        CALL_HOOK(NS_QUERY_NCACHE_BEGIN, qctx);
 
        qctx->authoritative = false;
@@ -9402,6 +9453,8 @@ static isc_result_t
 query_zerottl_refetch(query_ctx_t *qctx) {
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_zerottl_refetch");
+
        if (qctx->is_zone || qctx->resuming || STALE(qctx->rdataset) ||
            qctx->rdataset->ttl != 0 || !RECURSIONOK(qctx->client))
        {
@@ -9417,8 +9470,7 @@ query_zerottl_refetch(query_ctx_t *qctx) {
                                  NULL, NULL, qctx->resuming);
        if (result == ISC_R_SUCCESS) {
                CALL_HOOK(NS_QUERY_ZEROTTL_RECURSE, qctx);
-               qctx->client->query.attributes |=
-                       NS_QUERYATTR_RECURSING;
+               qctx->client->query.attributes |= NS_QUERYATTR_RECURSING;
 
                if (qctx->dns64) {
                        qctx->client->query.attributes |=
@@ -9450,6 +9502,8 @@ query_cname(query_ctx_t *qctx) {
        dns_rdata_t rdata = DNS_RDATA_INIT;
        dns_rdata_cname_t cname;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_cname");
+
        CALL_HOOK(NS_QUERY_CNAME_BEGIN, qctx);
 
        result = query_zerottl_refetch(qctx);
@@ -9559,6 +9613,8 @@ query_dname(query_ctx_t *qctx) {
        isc_result_t result;
        unsigned int nlabels;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_dname");
+
        CALL_HOOK(NS_QUERY_DNAME_BEGIN, qctx);
 
        /*
@@ -9774,6 +9830,8 @@ static isc_result_t
 query_prepresponse(query_ctx_t *qctx) {
        isc_result_t result;
 
+       CCTRACE(ISC_LOG_DEBUG(3), "query_prepresponse");
+
        CALL_HOOK(NS_QUERY_PREP_RESPONSE_BEGIN, qctx);
 
        if (WANTDNSSEC(qctx->client) &&
@@ -10599,7 +10657,8 @@ static void
 query_setup_sortlist(query_ctx_t *qctx) {
        isc_netaddr_t netaddr;
        ns_client_t *client = qctx->client;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_aclenv_t *env =
+               ns_interfacemgr_getaclenv(client->manager->interface->mgr);
        const void *order_arg = NULL;
 
        isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
@@ -10932,7 +10991,6 @@ ns_query_start(ns_client_t *client) {
        isc_result_t result;
        dns_message_t *message;
        dns_rdataset_t *rdataset;
-       ns_client_t *qclient;
        dns_rdatatype_t qtype;
        unsigned int saved_extflags;
        unsigned int saved_flags;
@@ -10945,26 +11003,10 @@ ns_query_start(ns_client_t *client) {
 
        CTRACE(ISC_LOG_DEBUG(3), "ns_query_start");
 
-       /*
-        * Test only.
-        */
-       if (((client->sctx->options & NS_SERVER_CLIENTTEST) != 0) &&
-           !TCP(client))
-       {
-               result = ns_client_replace(client);
-               if (result == ISC_R_SHUTTINGDOWN) {
-                       ns_client_next(client, result);
-                       return;
-               } else if (result != ISC_R_SUCCESS) {
-                       query_error(client, result, __LINE__);
-                       return;
-               }
-       }
-
        /*
         * Ensure that appropriate cleanups occur.
         */
-       client->next = query_next_callback;
+       client->cleanup = query_cleanup;
 
        if ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
                client->query.attributes |= NS_QUERYATTR_WANTRECURSION;
@@ -11035,8 +11077,9 @@ ns_query_start(ns_client_t *client) {
                         * section.
                         */
                        query_error(client, DNS_R_FORMERR, __LINE__);
-               } else
+               } else {
                        query_error(client, result, __LINE__);
+               }
                return;
        }
 
@@ -11069,10 +11112,11 @@ ns_query_start(ns_client_t *client) {
                        result = dns_tkey_processquery(client->message,
                                                    client->sctx->tkeyctx,
                                                    client->view->dynamickeys);
-                       if (result == ISC_R_SUCCESS)
+                       if (result == ISC_R_SUCCESS) {
                                query_send(client);
-                       else
+                       } else {
                                query_error(client, result, __LINE__);
+                       }
                        return;
                default: /* TSIG, etc. */
                        query_error(client, DNS_R_FORMERR, __LINE__);
@@ -11118,8 +11162,9 @@ ns_query_start(ns_client_t *client) {
        {
                client->query.dboptions |= DNS_DBFIND_PENDINGOK;
                client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
-       } else if (!client->view->enablevalidation)
+       } else if (!client->view->enablevalidation) {
                client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
+       }
 
        if (client->view->qminimization) {
                client->query.fetchoptions |= DNS_FETCHOPT_QMINIMIZE |
@@ -11172,7 +11217,5 @@ ns_query_start(ns_client_t *client) {
        if (WANTDNSSEC(client) || WANTAD(client))
                message->flags |= DNS_MESSAGEFLAG_AD;
 
-       qclient = NULL;
-       ns_client_attach(client, &qclient);
-       (void)query_setup(qclient, qtype);
+       (void)query_setup(client, qtype);
 }
index 8b952a6375ca4a94b2fb3550c7c8a42b41c2f48d..0ba61773ffeb8d76498de02a62d6e87202351f3b 100644 (file)
@@ -15,6 +15,8 @@ VERSION=@BIND9_VERSION@
 
 @BIND9_MAKE_INCLUDES@
 
+WRAP_OPTIONS = -Wl,--wrap=isc_nmhandle_unref
+
 CINCLUDES =    -I. -Iinclude ${NS_INCLUDES} ${DNS_INCLUDES} ${ISC_INCLUDES} \
                ${OPENSSL_CFLAGS} \
                @CMOCKA_CFLAGS@
@@ -29,6 +31,12 @@ NSDEPLIBS =  ../libns.@A@
 
 LIBS =         @LIBS@ @CMOCKA_LIBS@
 
+SO_CFLAGS =    @CFLAGS@ @SO_CFLAGS@
+SO_LDFLAGS =   @LDFLAGS@ @SO_LDFLAGS@
+
+SO_OBJS =      wrap.@O@
+SO_SRCS =      wrap.c
+SO_TARGETS =   libwrap.la
 
 OBJS =         nstest.@O@
 SRCS =         nstest.c \
@@ -41,29 +49,39 @@ SUBDIRS =
 TARGETS =      listenlist_test@EXEEXT@ \
                notify_test@EXEEXT@ \
                plugin_test@EXEEXT@ \
-               query_test@EXEEXT@
+               query_test@EXEEXT@ \
+               @SO_TARGETS@
+
+LD_WRAP_TESTS=@LD_WRAP_TESTS@
 
 @BIND9_MAKE_RULES@
 
-listenlist_test@EXEEXT@: listenlist_test.@O@ nstest.@O@ ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+libwrap.la: wrap.@O@
+       ${LIBTOOL_MODE_LINK} @SO_LD@ ${SO_LDFLAGS} -Wl,-z,interpose -o $@ wrap.@O@ ${LIBS}
+
+listenlist_test@EXEEXT@: listenlist_test.@O@ nstest.@O@ libwrap.la ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+       if test "${LD_WRAP_TESTS}" = true -a -z "${LIBTOOL}"; then WRAP="${WRAP_OPTIONS}"; fi; \
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \
-               ${LDFLAGS} -o $@ listenlist_test.@O@ nstest.@O@ \
-               ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
+               ${LDFLAGS} $${WRAP} -Wl,-rpath=${top_builddir}/lib/ns/tests -o $@ listenlist_test.@O@ nstest.@O@ \
+               libwrap.la ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
 
-notify_test@EXEEXT@: notify_test.@O@ nstest.@O@ ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+notify_test@EXEEXT@: notify_test.@O@ nstest.@O@ libwrap.la ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+       if test "${LD_WRAP_TESTS}" = true -a -z "${LIBTOOL}"; then WRAP="${WRAP_OPTIONS}"; fi; \
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \
-               ${LDFLAGS} -o $@ notify_test.@O@ nstest.@O@ \
-               ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
+               ${LDFLAGS} $${WRAP} -Wl,-rpath=${top_builddir}/lib/ns/tests -o $@ notify_test.@O@ nstest.@O@ libwrap.la \
+               libwrap.la ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
 
-plugin_test@EXEEXT@: plugin_test.@O@ nstest.@O@ ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+plugin_test@EXEEXT@: plugin_test.@O@ nstest.@O@ libwrap.la ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+       if test "${LD_WRAP_TESTS}" = true -a -z "${LIBTOOL}"; then WRAP="${WRAP_OPTIONS}"; fi; \
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \
-               ${LDFLAGS} -o $@ plugin_test.@O@ nstest.@O@ \
-               ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
+               ${LDFLAGS} $${WRAP} -Wl,-rpath=${top_builddir}/lib/ns/tests -o $@ plugin_test.@O@ nstest.@O@ \
+               libwrap.la ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
 
-query_test@EXEEXT@: query_test.@O@ nstest.@O@ ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+query_test@EXEEXT@: query_test.@O@ nstest.@O@ libwrap.la ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
+       if test "${LD_WRAP_TESTS}" = true -a -z "${LIBTOOL}"; then WRAP="${WRAP_OPTIONS}"; fi; \
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \
-               ${LDFLAGS} -o $@ query_test.@O@ nstest.@O@ \
-               ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
+               ${LDFLAGS} $${WRAP} -Wl,-rpath=${top_builddir}/lib/ns/tests -o $@ query_test.@O@ nstest.@O@ \
+               libwrap.la ${NSLIBS} ${DNSLIBS} ${ISCLIBS} ${LIBS}
 
 unit::
        sh ${top_builddir}/unit/unittest.sh
index 32466d1e0c2acd3d3d85e279d210c3260ffce857..ca8130b181d133dd1272bf95acb3e9c37363a46d 100644 (file)
@@ -119,6 +119,15 @@ main(void) {
                                                _setup, _teardown),
        };
 
+       /*
+        * We disable this test when the address sanitizer is in
+        * the use, as libuv will trigger errors.
+        */
+       if (getenv("ASAN_OPTIONS") != NULL) {
+               printf("1..0 # Skip ASAN is in use\n");
+               return (0);
+       }
+
        return (cmocka_run_group_tests(tests, NULL, NULL));
 }
 #else /* HAVE_CMOCKA */
@@ -127,7 +136,7 @@ main(void) {
 
 int
 main(void) {
-       printf("1..0 # Skipped: cmocka not available\n");
+       printf("1..0 # Skip cmocka not available\n");
        return (0);
 }
 
index 8cae165209314545568e43ad352627aba3cbea2b..7f3974dd67df68aa04f030c2062f4aa33bd32767 100644 (file)
@@ -136,8 +136,7 @@ notify_start(void **state) {
         * Clean up
         */
        ns_test_cleanup_zone();
-
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 }
 
 int
@@ -147,6 +146,15 @@ main(void) {
                                                _setup, _teardown),
        };
 
+       /*
+        * We disable this test when the address sanitizer is in
+        * the use, as libuv will trigger errors.
+        */
+       if (getenv("ASAN_OPTIONS") != NULL) {
+               printf("1..0 # Skip ASAN is in use\n");
+               return (0);
+       }
+
        return (cmocka_run_group_tests(tests, NULL, NULL));
 }
 #else /* HAVE_CMOCKA */
@@ -155,7 +163,7 @@ main(void) {
 
 int
 main(void) {
-       printf("1..0 # Skipped: cmocka not available\n");
+       printf("1..0 # Skip cmocka not available\n");
        return (0);
 }
 
index cf7f00256e231acdf73ac4743e17cb25ee9871ef..1d0354ff19cca1466190d340694a0b8008fef1b4 100644 (file)
@@ -22,6 +22,7 @@
 #include <isc/file.h>
 #include <isc/hash.h>
 #include <isc/mem.h>
+#include <isc/netmgr.h>
 #include <isc/os.h>
 #include <isc/print.h>
 #include <isc/random.h>
@@ -55,6 +56,7 @@ isc_taskmgr_t *taskmgr = NULL;
 isc_task_t *maintask = NULL;
 isc_timermgr_t *timermgr = NULL;
 isc_socketmgr_t *socketmgr = NULL;
+isc_nm_t *nm = NULL;
 dns_zonemgr_t *zonemgr = NULL;
 dns_dispatchmgr_t *dispatchmgr = NULL;
 ns_clientmgr_t *clientmgr = NULL;
@@ -70,6 +72,37 @@ static bool test_running = false;
 
 static dns_zone_t *served_zone = NULL;
 
+/*
+ * We don't want to use netmgr-based client accounting, we need to emulate it.
+ */
+atomic_uint_fast32_t client_refs[16];
+atomic_uintptr_t client_addrs[16];
+
+void
+__wrap_isc_nmhandle_unref(isc_nmhandle_t *handle);
+
+void
+__wrap_isc_nmhandle_unref(isc_nmhandle_t *handle) {
+       ns_client_t *client = (ns_client_t *)handle;
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               if (atomic_load(&client_addrs[i]) == (uintptr_t) client) {
+                       break;
+               }
+       }
+       REQUIRE(i < 16);
+
+       if (atomic_fetch_sub(&client_refs[i], 1) == 1) {
+               dns_view_detach(&client->view);
+               client->state = 4;
+               ns__client_reset_cb(client);
+               ns__client_put_cb(client);
+               isc_mem_put(mctx, client, sizeof(ns_client_t));
+       }
+       return;
+}
+
 /*
  * Logging categories: this needs to match the list in lib/ns/log.c.
  */
@@ -108,10 +141,6 @@ static void
 shutdown_managers(isc_task_t *task, isc_event_t *event) {
        UNUSED(task);
 
-       if (clientmgr != NULL) {
-               ns_clientmgr_destroy(&clientmgr);
-       }
-
        if (interfacemgr != NULL) {
                ns_interfacemgr_shutdown(interfacemgr);
                ns_interfacemgr_detach(&interfacemgr);
@@ -148,9 +177,22 @@ cleanup_managers(void) {
        if (sctx != NULL) {
                ns_server_detach(&sctx);
        }
+       if (interfacemgr != NULL) {
+               ns_interfacemgr_detach(&interfacemgr);
+       }
        if (socketmgr != NULL) {
                isc_socketmgr_destroy(&socketmgr);
        }
+       ns_test_nap(500000);
+       if (nm != NULL ){
+               /*
+                * Force something in the workqueue as a workaround
+                * for libuv bug - not sending uv_close callback.
+                */
+               isc_nm_pause(nm);
+               isc_nm_resume(nm);
+               isc_nm_detach(&nm);
+       }
        if (taskmgr != NULL) {
                isc_taskmgr_destroy(&taskmgr);
        }
@@ -186,17 +228,16 @@ create_managers(void) {
 
        CHECK(isc_socketmgr_create(mctx, &socketmgr));
 
+       nm = isc_nm_start(mctx, ncpus);
+
        CHECK(ns_server_create(mctx, matchview, &sctx));
 
        CHECK(dns_dispatchmgr_create(mctx, &dispatchmgr));
 
        CHECK(ns_interfacemgr_create(mctx, sctx, taskmgr, timermgr,
-                                    socketmgr, dispatchmgr, maintask,
+                                    socketmgr, nm, dispatchmgr, maintask,
                                     ncpus, NULL, &interfacemgr));
 
-       CHECK(ns_clientmgr_create(mctx, sctx, taskmgr, timermgr,
-                                 &clientmgr));
-
        CHECK(ns_listenlist_default(mctx, 5300, -1, true, &listenon));
        ns_interfacemgr_setlistenon4(interfacemgr, listenon);
        ns_listenlist_detach(&listenon);
@@ -212,6 +253,8 @@ create_managers(void) {
         * we'll just sleep for a bit and hope.
         */
        ns_test_nap(500000);
+       ns_interface_t *ifp = ns__interfacemgr_getif(interfacemgr);
+       clientmgr = ifp->clientmgr;
 
        run_managers = true;
 
@@ -510,16 +553,28 @@ ns_test_getclient(ns_interface_t *ifp0, bool tcp,
                  ns_client_t **clientp)
 {
        isc_result_t result;
-       ns_interface_t *ifp = ifp0;
+       ns_client_t *client = isc_mem_get(mctx, sizeof(ns_client_t));
+       int i;
 
-       if (ifp == NULL) {
-               ifp = ns__interfacemgr_getif(interfacemgr);
-       }
-       if (ifp == NULL) {
-               return (ISC_R_FAILURE);
+       UNUSED(ifp0);
+       UNUSED(tcp);
+
+       result = ns__client_setup(client, clientmgr, true);
+
+       for (i = 0; i < 16; i++) {
+               if (atomic_load(&client_addrs[i]) == (uintptr_t) NULL ||
+                   atomic_load(&client_addrs[i]) == (uintptr_t) client)
+               {
+                       break;
+               }
        }
+       REQUIRE(i < 16);
+
+       atomic_store(&client_refs[i], 2);
+       atomic_store(&client_addrs[i], (uintptr_t) client);
+       client->handle = (isc_nmhandle_t *) client; /* Hack */
+       *clientp = client;
 
-       result = ns__clientmgr_getclient(clientmgr, ifp, tcp, clientp);
        return (result);
 }
 
@@ -765,14 +820,14 @@ ns_test_qctx_create(const ns_test_qctx_create_params_t *params,
         * Reference count for "client" is now at 2, so decrement it in order
         * for it to drop to zero when "qctx" gets destroyed.
         */
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 
        return (ISC_R_SUCCESS);
 
 destroy_query:
        dns_message_destroy(&client->message);
 detach_client:
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 
        return (result);
 }
@@ -786,14 +841,15 @@ ns_test_qctx_destroy(query_ctx_t **qctxp) {
 
        qctx = *qctxp;
 
-       ns_client_detach(&qctx->client);
-
        if (qctx->zone != NULL) {
                dns_zone_detach(&qctx->zone);
        }
        if (qctx->db != NULL) {
                dns_db_detach(&qctx->db);
        }
+       if (qctx->client != NULL) {
+               isc_nmhandle_unref(qctx->client->handle);
+       }
 
        isc_mem_put(mctx, qctx, sizeof(*qctx));
        *qctxp = NULL;
index 415de2a88742a4fce88041d530129a362207a095..3540a3f91e63ac10736c93473f73da52b6883038 100644 (file)
@@ -598,6 +598,15 @@ main(void) {
                                                _setup, _teardown),
        };
 
+       /*
+        * We disable this test when the address sanitizer is in
+        * the use, as libuv will trigger errors.
+        */
+       if (getenv("ASAN_OPTIONS") != NULL) {
+               printf("1..0 # Skip ASAN is in use\n");
+               return (0);
+       }
+
        return (cmocka_run_group_tests(tests, NULL, NULL));
 }
 #else /* HAVE_CMOCKA */
@@ -606,7 +615,7 @@ main(void) {
 
 int
 main(void) {
-       printf("1..0 # Skipped: cmocka not available\n");
+       printf("1..0 # Skip cmocka not available\n");
        return (0);
 }
 
diff --git a/lib/ns/tests/wrap.c b/lib/ns/tests/wrap.c
new file mode 100644 (file)
index 0000000..41b8449
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/mem.h>
+#include <isc/netmgr.h>
+#include <isc/util.h>
+
+#include <dns/view.h>
+
+#include <ns/client.h>
+
+/*
+ * This overrides calls to isc_nmhandle_unref(), sending them to
+ * __wrap_isc_nmhandle_unref(), when libtool is in use and LD_WRAP
+ * can't be used.
+ */
+
+extern void
+__wrap_isc_nmhandle_unref(isc_nmhandle_t *handle);
+
+void
+isc_nmhandle_unref(isc_nmhandle_t *handle) {
+       __wrap_isc_nmhandle_unref(handle);
+}
index 3baf428bd883c27c3369efe0c39810d6d773c1c2..bdf55c10a9a38d0904a41049ff7ee9d0a5b97f62 100644 (file)
@@ -1529,7 +1529,6 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
        isc_result_t result = ISC_R_SUCCESS;
        update_event_t *event = NULL;
        isc_task_t *zonetask = NULL;
-       ns_client_t *evclient;
 
        event = (update_event_t *)
                isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
@@ -1537,12 +1536,11 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
        event->zone = zone;
        event->result = ISC_R_SUCCESS;
 
-       evclient = NULL;
-       ns_client_attach(client, &evclient);
        INSIST(client->nupdates == 0);
        client->nupdates++;
-       event->ev_arg = evclient;
+       event->ev_arg = client;
 
+       isc_nmhandle_ref(client->handle);
        dns_zone_gettask(zone, &zonetask);
        isc_task_send(zonetask, ISC_EVENT_PTR(&event));
 
@@ -1559,6 +1557,7 @@ respond(ns_client_t *client, isc_result_t result) {
        client->message->rcode = dns_result_torcode(result);
 
        ns_client_send(client);
+       isc_nmhandle_unref(client->handle);
        return;
 
  msg_failure:
@@ -1566,7 +1565,8 @@ respond(ns_client_t *client, isc_result_t result) {
                      ISC_LOG_ERROR,
                      "could not create update response message: %s",
                      isc_result_totext(msg_result));
-       ns_client_next(client, msg_result);
+       ns_client_drop(client, msg_result);
+       isc_nmhandle_unref(client->handle);
 }
 
 void
@@ -2518,7 +2518,8 @@ update_action(isc_task_t *task, isc_event_t *event) {
        dns_ttl_t maxttl = 0;
        uint32_t maxrecords;
        uint64_t records;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_aclenv_t *env =
+               ns_interfacemgr_getaclenv(client->manager->interface->mgr);
 
        INSIST(event->ev_type == DNS_EVENT_UPDATE);
 
@@ -3389,7 +3390,7 @@ updatedone_action(isc_task_t *task, isc_event_t *event) {
        client->nupdates--;
        respond(client, uev->result);
        isc_event_free(&event);
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 }
 
 /*%
@@ -3406,10 +3407,9 @@ forward_fail(isc_task_t *task, isc_event_t *event) {
        client->nupdates--;
        respond(client, DNS_R_SERVFAIL);
        isc_event_free(&event);
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 }
 
-
 static void
 forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
        update_event_t *uev = arg;
@@ -3443,7 +3443,7 @@ forward_done(isc_task_t *task, isc_event_t *event) {
        ns_client_sendraw(client, uev->answer);
        dns_message_destroy(&uev->answer);
        isc_event_free(&event);
-       ns_client_detach(&client);
+       isc_nmhandle_unref(client->handle);
 }
 
 static void
@@ -3461,8 +3461,10 @@ forward_action(isc_task_t *task, isc_event_t *event) {
                isc_task_send(client->task, &event);
                inc_stats(client, zone, ns_statscounter_updatefwdfail);
                dns_zone_detach(&zone);
-       } else
+       } else {
                inc_stats(client, zone, ns_statscounter_updatereqfwd);
+       }
+
        isc_task_detach(&task);
 }
 
@@ -3473,13 +3475,6 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
        isc_result_t result = ISC_R_SUCCESS;
        update_event_t *event = NULL;
        isc_task_t *zonetask = NULL;
-       ns_client_t *evclient;
-
-       /*
-        * This may take some time so replace this client.
-        */
-       if (!client->mortal && (client->attributes & NS_CLIENTATTR_TCP) == 0)
-               CHECK(ns_client_replace(client));
 
        event = (update_event_t *)
                isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
@@ -3487,11 +3482,9 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
        event->zone = zone;
        event->result = ISC_R_SUCCESS;
 
-       evclient = NULL;
-       ns_client_attach(client, &evclient);
        INSIST(client->nupdates == 0);
        client->nupdates++;
-       event->ev_arg = evclient;
+       event->ev_arg = client;
 
        dns_name_format(dns_zone_getorigin(zone), namebuf,
                        sizeof(namebuf));
@@ -3503,10 +3496,11 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
                      namebuf, classbuf);
 
        dns_zone_gettask(zone, &zonetask);
+       isc_nmhandle_ref(client->handle);
        isc_task_send(zonetask, ISC_EVENT_PTR(&event));
 
- failure:
-       if (event != NULL)
+       if (event != NULL) {
                isc_event_free(ISC_EVENT_PTR(&event));
+       }
        return (result);
 }
index d221b0d5b2e16763681ee572bb3b8bac664bd496..2105a3e49317873ce057cf6a47cbad6b69228701 100644 (file)
@@ -3,18 +3,19 @@ LIBRARY libns
 ; Exported Functions
 EXPORTS
 
+ns__client_put_cb
 ns__client_request
-ns__clientmgr_getclient
+ns__client_reset_cb
+ns__client_setup
 ns__interfacemgr_getif
 ns__interfacemgr_nextif
 ns__query_sfcache
 ns__query_start
 ns_client_aclmsg
 ns_client_addopt
-ns_client_attach
 ns_client_checkacl
 ns_client_checkaclsilent
-ns_client_detach
+ns_client_drop
 ns_client_dumprecursing
 ns_client_error
 ns_client_findversion
@@ -29,19 +30,16 @@ ns_client_newdbversion
 ns_client_newname
 ns_client_newnamebuf
 ns_client_newrdataset
-ns_client_next
 ns_client_putrdataset
 ns_client_qnamereplace
 ns_client_recursing
 ns_client_releasename
-ns_client_replace
 ns_client_send
 ns_client_sendraw
 ns_client_settimeout
 ns_client_shuttingdown
 ns_client_sourceip
 ns_clientmgr_create
-ns_clientmgr_createclients
 ns_clientmgr_destroy
 ns_hook_add
 ns_hooktable_create
index 499e7380f0445a96e35ce6378ab9fedb7b234228..2931412cbf76b21cd0f1ed016cda21396ea449e2 100644 (file)
     <None Include="libns.def" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="DLLMain.c">
-      <Filter>Library Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="version.c">
-      <Filter>Library Source Files</Filter>
-    </ClCompile>
     <ClCompile Include="..\client.c">
       <Filter>Source Files</Filter>
     </ClCompile>
     <ClCompile Include="..\xfrout.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="DLLMain.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="version.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\include\ns\client.h">
       <Filter>Header Files</Filter>
     </ClInclude>
   </ItemGroup>
-</Project>
+</Project>
\ No newline at end of file
index 7672f59d9a52b1fd362657e76d494fca05bf2564..9c393c63acd2792ecead7b17b95c8c1eb9c2e3ec 100644 (file)
@@ -669,6 +669,7 @@ typedef struct {
                                                   names and rdatas */
        isc_buffer_t            txlenbuf;       /* Transmit length buffer */
        isc_buffer_t            txbuf;          /* Transmit message buffer */
+       size_t                  cbytes;         /* Length of current message */
        void                    *txmem;
        unsigned int            txmemlen;
        dns_tsigkey_t           *tsigkey;       /* Key used to create TSIG */
@@ -682,24 +683,21 @@ typedef struct {
        struct xfr_stats        stats;          /*%< Transfer statistics */
 } xfrout_ctx_t;
 
-static isc_result_t
+static void
 xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
                  unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
                  dns_rdataclass_t qclass, dns_zone_t *zone,
                  dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
                  rrstream_t *stream, dns_tsigkey_t *tsigkey,
-                 isc_buffer_t *lasttsig,
-                 bool verified_tsig,
-                 unsigned int maxtime,
-                 unsigned int idletime,
-                 bool many_answers,
-                 xfrout_ctx_t **xfrp);
+                 isc_buffer_t *lasttsig, bool verified_tsig,
+                 unsigned int maxtime, unsigned int idletime,
+                 bool many_answers, xfrout_ctx_t **xfrp);
 
 static void
 sendstream(xfrout_ctx_t *xfr);
 
 static void
-xfrout_senddone(isc_task_t *task, isc_event_t *event);
+xfrout_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg);
 
 static void
 xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg);
@@ -1067,29 +1065,26 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
 
 
        if (is_dlz) {
-               CHECK(xfrout_ctx_create(mctx, client, request->id,
-                                       question_name, reqtype, question_class,
-                                       zone, db, ver, quota, stream,
-                                       dns_message_gettsigkey(request),
-                                       tsigbuf,
-                                       request->verified_sig,
-                                       3600,
-                                       3600,
-                                       (format == dns_many_answers) ?
-                                       true : false,
-                                       &xfr));
+               xfrout_ctx_create(mctx, client, request->id,
+                                 question_name, reqtype, question_class,
+                                 zone, db, ver, quota, stream,
+                                 dns_message_gettsigkey(request),
+                                 tsigbuf, request->verified_sig,
+                                 3600, 3600,
+                                 (format == dns_many_answers)
+                                  ? true : false,
+                                 &xfr);
        } else {
-               CHECK(xfrout_ctx_create(mctx, client, request->id,
-                                       question_name, reqtype, question_class,
-                                       zone, db, ver, quota, stream,
-                                       dns_message_gettsigkey(request),
-                                       tsigbuf,
-                                       request->verified_sig,
-                                       dns_zone_getmaxxfrout(zone),
-                                       dns_zone_getidleout(zone),
-                                       (format == dns_many_answers) ?
-                                       true : false,
-                                       &xfr));
+               xfrout_ctx_create(mctx, client, request->id,
+                                 question_name, reqtype, question_class,
+                                 zone, db, ver, quota, stream,
+                                 dns_message_gettsigkey(request),
+                                 tsigbuf, request->verified_sig,
+                                 dns_zone_getmaxxfrout(zone),
+                                 dns_zone_getidleout(zone),
+                                 (format == dns_many_answers)
+                                  ?  true : false,
+                                 &xfr);
        }
 
        xfr->mnemonic = mnemonic;
@@ -1189,10 +1184,11 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
                              NS_LOGMODULE_XFER_OUT,
                              ISC_LOG_DEBUG(3), "zone transfer setup failed");
                ns_client_error(client, result);
+               isc_nmhandle_unref(client->handle);
        }
 }
 
-static isc_result_t
+static void
 xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
                  dns_name_t *qname, dns_rdatatype_t qtype,
                  dns_rdataclass_t qclass, dns_zone_t *zone,
@@ -1203,16 +1199,18 @@ xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
                  bool many_answers, xfrout_ctx_t **xfrp)
 {
        xfrout_ctx_t *xfr;
-       isc_result_t result;
        unsigned int len;
        void *mem;
 
-       INSIST(xfrp != NULL && *xfrp == NULL);
+       REQUIRE(xfrp != NULL && *xfrp == NULL);
+
+       UNUSED(maxtime);
+       UNUSED(idletime);
+
        xfr = isc_mem_get(mctx, sizeof(*xfr));
        xfr->mctx = NULL;
        isc_mem_attach(mctx, &xfr->mctx);
-       xfr->client = NULL;
-       ns_client_attach(client, &xfr->client);
+       xfr->client = client;
        xfr->id = id;
        xfr->qname = qname;
        xfr->qtype = qtype;
@@ -1271,8 +1269,10 @@ xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
        xfr->txmem = mem;
        xfr->txmemlen = len;
 
+#if 0
        CHECK(dns_timer_setidle(xfr->client->timer,
                                maxtime, idletime, false));
+#endif
 
        /*
         * Register a shutdown callback with the client, so that we
@@ -1289,14 +1289,8 @@ xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
        xfr->stream = stream;
 
        *xfrp = xfr;
-       return (ISC_R_SUCCESS);
-
-failure:
-       xfrout_ctx_destroy(&xfr);
-       return (result);
 }
 
-
 /*
  * Arrange to send as much as we can of "stream" without blocking.
  *
@@ -1310,8 +1304,6 @@ sendstream(xfrout_ctx_t *xfr) {
        dns_message_t *tcpmsg = NULL;
        dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */
        isc_result_t result;
-       isc_region_t used;
-       isc_region_t region;
        dns_rdataset_t *qrdataset;
        dns_name_t *msgname = NULL;
        dns_rdata_t *msgrdata = NULL;
@@ -1320,7 +1312,6 @@ sendstream(xfrout_ctx_t *xfr) {
        dns_compress_t cctx;
        bool cleanup_cctx = false;
        bool is_tcp;
-
        int n_rrs;
 
        isc_buffer_clear(&xfr->buf);
@@ -1545,6 +1536,7 @@ sendstream(xfrout_ctx_t *xfr) {
        }
 
        if (is_tcp) {
+               isc_region_t used;
                CHECK(dns_compress_init(&cctx, -1, xfr->mctx));
                dns_compress_setsensitive(&cctx, true);
                cleanup_cctx = true;
@@ -1556,22 +1548,20 @@ sendstream(xfrout_ctx_t *xfr) {
                cleanup_cctx = false;
 
                isc_buffer_usedregion(&xfr->txbuf, &used);
-               isc_buffer_putuint16(&xfr->txlenbuf,
-                                    (uint16_t)used.length);
-               region.base = xfr->txlenbuf.base;
-               region.length = 2 + used.length;
+
                xfrout_log(xfr, ISC_LOG_DEBUG(8),
                           "sending TCP message of %d bytes",
                           used.length);
-               CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */
-                                     &region, xfr->client->task,
-                                     xfrout_senddone,
-                                     xfr));
+
+               CHECK(isc_nm_send(xfr->client->handle, &used,
+                                 xfrout_senddone, xfr));
                xfr->sends++;
+               xfr->cbytes = used.length;
        } else {
                xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response");
                ns_client_send(xfr->client);
                xfr->stream->methods->pause(xfr->stream);
+               isc_nmhandle_unref(xfr->client->handle);
                xfrout_ctx_destroy(&xfr);
                return;
        }
@@ -1615,7 +1605,6 @@ sendstream(xfrout_ctx_t *xfr) {
 static void
 xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
        xfrout_ctx_t *xfr = *xfrp;
-       ns_client_t *client = NULL;
 
        INSIST(xfr->sends == 0);
 
@@ -1639,28 +1628,18 @@ xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
        if (xfr->db != NULL)
                dns_db_detach(&xfr->db);
 
-       /*
-        * We want to detch the client after we have released the memory
-        * context as ns_client_detach checks the memory reference count.
-        */
-       ns_client_attach(xfr->client, &client);
-       ns_client_detach(&xfr->client);
        isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
-       ns_client_detach(&client);
 
        *xfrp = NULL;
 }
 
 static void
-xfrout_senddone(isc_task_t *task, isc_event_t *event) {
-       isc_socketevent_t *sev = (isc_socketevent_t *)event;
-       xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg;
-       isc_result_t evresult = sev->result;
+xfrout_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+       xfrout_ctx_t *xfr = (xfrout_ctx_t *)arg;
 
-       UNUSED(task);
+       REQUIRE((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0);
 
-       INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
-       INSIST((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0);
+       INSIST(handle == xfr->client->handle);
 
        xfr->sends--;
        INSIST(xfr->sends == 0);
@@ -1669,20 +1648,23 @@ xfrout_senddone(isc_task_t *task, isc_event_t *event) {
         * Update transfer statistics if sending succeeded, accounting for the
         * two-byte TCP length prefix included in the number of bytes sent.
         */
-       if (evresult == ISC_R_SUCCESS) {
+       if (result == ISC_R_SUCCESS) {
                xfr->stats.nmsg++;
-               xfr->stats.nbytes += sev->region.length - 2;
+               xfr->stats.nbytes += xfr->cbytes;
        }
 
-       isc_event_free(&event);
-
+#if 0
        (void)isc_timer_touch(xfr->client->timer);
+#endif
+
        if (xfr->shuttingdown == true) {
                xfrout_maybe_destroy(xfr);
-       } else if (evresult != ISC_R_SUCCESS) {
-               xfrout_fail(xfr, evresult, "send");
+       } else if (result != ISC_R_SUCCESS) {
+               xfrout_fail(xfr, result, "send");
        } else if (xfr->end_of_stream == false) {
                sendstream(xfr);
+               /* Return now so we don't unref the handle */
+               return;
        } else {
                /* End of zone transfer stream. */
                uint64_t msecs, persec;
@@ -1707,9 +1689,10 @@ xfrout_senddone(isc_task_t *task, isc_event_t *event) {
                           (unsigned int) (msecs % 1000),
                           (unsigned int) persec);
 
-               ns_client_next(xfr->client, ISC_R_SUCCESS);
                xfrout_ctx_destroy(&xfr);
        }
+
+       isc_nmhandle_unref(handle);
 }
 
 static void
@@ -1723,6 +1706,7 @@ xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) {
 static void
 xfrout_maybe_destroy(xfrout_ctx_t *xfr) {
        INSIST(xfr->shuttingdown == true);
+#if 0
        if (xfr->sends > 0) {
                /*
                 * If we are currently sending, cancel it and wait for
@@ -1731,9 +1715,13 @@ xfrout_maybe_destroy(xfrout_ctx_t *xfr) {
                isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task,
                                  ISC_SOCKCANCEL_SEND);
        } else {
-               ns_client_next(xfr->client, ISC_R_CANCELED);
+#endif
+               ns_client_drop(xfr->client, ISC_R_CANCELED);
+               isc_nmhandle_unref(xfr->client->handle);
                xfrout_ctx_destroy(&xfr);
+#if 0
        }
+#endif
 }
 
 static void
index 29b3c2efcd4aaebb057013071ade0fc0aec1abf2..66eb5bed7dc4ac0795f47444caf9d6d152da03f1 100644 (file)
 ./bin/tests/system/emptyzones/setup.sh         SH      2014,2016,2018,2019
 ./bin/tests/system/emptyzones/tests.sh         SH      2014,2015,2016,2018,2019
 ./bin/tests/system/feature-test.c              C       2016,2017,2018,2019
-./bin/tests/system/fetchlimit/ans4/ans.pl      PERL    2015,2016,2018,2019
+./bin/tests/system/fetchlimit/ans4/ans.pl      PERL    2019
 ./bin/tests/system/fetchlimit/clean.sh         SH      2015,2016,2018,2019
-./bin/tests/system/fetchlimit/ns3/named.args   X       2015,2018,2019
 ./bin/tests/system/fetchlimit/prereq.sh                SH      2018,2019
 ./bin/tests/system/fetchlimit/setup.sh         SH      2015,2016,2018,2019
 ./bin/tests/system/fetchlimit/tests.sh         SH      2015,2016,2018,2019
 ./bin/tests/system/redirect/ns5/sign.sh                SH      2019
 ./bin/tests/system/redirect/setup.sh           SH      2011,2012,2013,2014,2015,2016,2017,2018,2019
 ./bin/tests/system/redirect/tests.sh           SH      2011,2012,2013,2014,2015,2016,2018,2019
-./bin/tests/system/resolver/ans2/ans.pl                PERL    2000,2001,2004,2007,2009,2010,2012,2016,2018,2019
+./bin/tests/system/resolver/ans2/ans.pl                PERL    2019
 ./bin/tests/system/resolver/ans3/ans.pl                PERL    2000,2001,2004,2007,2009,2012,2016,2018,2019
 ./bin/tests/system/resolver/ans8/ans.pl                PERL    2017,2018,2019
 ./bin/tests/system/resolver/clean.sh           SH      2008,2009,2010,2011,2012,2013,2014,2015,2016,2018,2019
 ./bin/tests/system/resolver/ns4/named.noaa     TXT.BRIEF       2010,2016,2018,2019
 ./bin/tests/system/resolver/ns6/keygen.sh      SH      2010,2012,2014,2016,2017,2018,2019
-./bin/tests/system/resolver/ns7/named.args     X       2011,2012,2014,2018,2019
 ./bin/tests/system/resolver/prereq.sh          SH      2000,2001,2004,2007,2012,2014,2016,2018,2019
 ./bin/tests/system/resolver/setup.sh           SH      2010,2011,2012,2013,2014,2016,2017,2018,2019
 ./bin/tests/system/resolver/tests.sh           SH      2000,2001,2004,2007,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019
 ./lib/ns/tests/plugin_test.c                   C       2019
 ./lib/ns/tests/query_test.c                    C       2017,2018,2019
 ./lib/ns/tests/testdata/notify/notify1.msg     X       2017,2018,2019
+./lib/ns/tests/wrap.c                          C       2019
 ./lib/ns/update.c                              C       2017,2018,2019
 ./lib/ns/version.c                             C       2017,2018,2019
 ./lib/ns/win32/DLLMain.c                       C       2017,2018,2019