]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Implement seamless TCP connection reuse in dns_dispatch
authorOndřej Surý <ondrej@isc.org>
Tue, 17 Feb 2026 10:05:33 +0000 (11:05 +0100)
committerOndřej Surý <ondrej@isc.org>
Tue, 14 Apr 2026 15:48:13 +0000 (17:48 +0200)
Previously, the user of dns_dispatch API had to first call
dns_dispatch_gettcp() and if that failed create a new TCP dispatch with
dns_dispatch_createtcp().  This has been changed and the TCP connection
reuse happens transparently inside dns_dispatch_createtcp().  There are
separate buckets for dns_resolver, dns_request and dns_xfrin units, so
these don't get mixed together.

lib/dns/dispatch.c
lib/dns/include/dns/dispatch.h
lib/dns/request.c
lib/dns/resolver.c
lib/dns/xfrin.c
tests/dns/dispatch_test.c

index 771c29b8887540896c59f827a0bb96995cef0d76..a6f7cc4e992448a4f8a49341666da253bc43bde1 100644 (file)
@@ -125,6 +125,7 @@ struct dns_dispatch {
 
        dns_dispatchopt_t options;
        dns_dispatchstate_t state;
+       dns_dispatchtype_t disptype;
 
        bool reading;
 
@@ -1131,6 +1132,7 @@ struct dispatch_key {
        const isc_sockaddr_t *local;
        const isc_sockaddr_t *peer;
        const dns_transport_t *transport;
+       const dns_dispatchtype_t disptype;
 };
 
 static uint32_t
@@ -1154,70 +1156,10 @@ dispatch_match(struct cds_lfht_node *node, const void *key0) {
                isc_sockaddr_equal(&disp->local, key->local));
 }
 
-isc_result_t
-dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
-                      const isc_sockaddr_t *destaddr,
-                      dns_transport_t *transport, dns_dispatchopt_t options,
-                      dns_dispatch_t **dispp) {
-       dns_dispatch_t *disp = NULL;
-       isc_tid_t tid = isc_tid();
-
-       REQUIRE(VALID_DISPATCHMGR(mgr));
-       REQUIRE(destaddr != NULL);
-
-       dispatch_allocate(mgr, isc_socktype_tcp, tid, &disp);
-
-       disp->options = options;
-       disp->peer = *destaddr;
-       if (transport != NULL) {
-               dns_transport_attach(transport, &disp->transport);
-       }
-
-       if (localaddr != NULL) {
-               disp->local = *localaddr;
-       } else {
-               int pf;
-               pf = isc_sockaddr_pf(destaddr);
-               isc_sockaddr_anyofpf(&disp->local, pf);
-               isc_sockaddr_setport(&disp->local, 0);
-       }
-
-       /*
-        * Append it to the dispatcher list.
-        */
-       struct dispatch_key key = {
-               .local = &disp->local,
-               .peer = &disp->peer,
-               .transport = transport,
-       };
-
-       if ((disp->options & DNS_DISPATCHOPT_UNSHARED) == 0) {
-               rcu_read_lock();
-               cds_lfht_add(mgr->tcps[tid], dispatch_hash(&key),
-                            &disp->ht_node);
-               rcu_read_unlock();
-       }
-
-       if (isc_log_wouldlog(90)) {
-               char addrbuf[ISC_SOCKADDR_FORMATSIZE];
-
-               isc_sockaddr_format(&disp->local, addrbuf,
-                                   ISC_SOCKADDR_FORMATSIZE);
-
-               mgr_log(mgr, ISC_LOG_DEBUG(90),
-                       "dns_dispatch_createtcp: created TCP dispatch %p for "
-                       "%s",
-                       disp, addrbuf);
-       }
-       *dispp = disp;
-
-       return ISC_R_SUCCESS;
-}
-
-isc_result_t
-dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr,
-                   const isc_sockaddr_t *localaddr, dns_transport_t *transport,
-                   dns_dispatch_t **dispp) {
+static isc_result_t
+dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
+               const isc_sockaddr_t *destaddr, dns_transport_t *transport,
+               dns_dispatchtype_t disptype, dns_dispatch_t **dispp) {
        dns_dispatch_t *disp_connected = NULL;
        dns_dispatch_t *disp_fallback = NULL;
        isc_result_t result = ISC_R_NOTFOUND;
@@ -1231,6 +1173,7 @@ dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr,
                .local = localaddr,
                .peer = destaddr,
                .transport = transport,
+               .disptype = disptype,
        };
 
        rcu_read_lock();
@@ -1242,23 +1185,19 @@ dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr,
                INSIST(disp->tid == isc_tid());
                INSIST(disp->socktype == isc_socktype_tcp);
 
+               if (disp->disptype != disptype) {
+                       continue;
+               }
+
                switch (disp->state) {
                case DNS_DISPATCHSTATE_NONE:
                        /* A dispatch in indeterminate state, skip it */
                        break;
                case DNS_DISPATCHSTATE_CONNECTED:
-                       if (ISC_LIST_EMPTY(disp->active)) {
-                               /* Ignore dispatch with no responses */
-                               break;
-                       }
                        /* We found a connected dispatch */
                        dns_dispatch_attach(disp, &disp_connected);
                        break;
                case DNS_DISPATCHSTATE_CONNECTING:
-                       if (ISC_LIST_EMPTY(disp->pending)) {
-                               /* Ignore dispatch with no responses */
-                               break;
-                       }
                        /* We found "a" dispatch, store it for later */
                        if (disp_fallback == NULL) {
                                dns_dispatch_attach(disp, &disp_fallback);
@@ -1298,6 +1237,103 @@ dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr,
        return result;
 }
 
+static void
+dispatch_createtcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
+                  const isc_sockaddr_t *destaddr, dns_transport_t *transport,
+                  dns_dispatchtype_t disptype, dns_dispatchopt_t options,
+                  dns_dispatch_t **dispp) {
+       dns_dispatch_t *disp = NULL;
+       isc_tid_t tid = isc_tid();
+
+       dispatch_allocate(mgr, isc_socktype_tcp, tid, &disp);
+
+       disp->disptype = disptype;
+       disp->options = options;
+       disp->peer = *destaddr;
+       if (transport != NULL) {
+               dns_transport_attach(transport, &disp->transport);
+       }
+
+       if (localaddr != NULL) {
+               disp->local = *localaddr;
+       } else {
+               int pf;
+               pf = isc_sockaddr_pf(destaddr);
+               isc_sockaddr_anyofpf(&disp->local, pf);
+               isc_sockaddr_setport(&disp->local, 0);
+       }
+
+       /*
+        * Append it to the dispatcher list.
+        */
+       if ((options & DNS_DISPATCHOPT_FIXEDID) == 0) {
+               struct dispatch_key key = {
+                       .local = &disp->local,
+                       .peer = &disp->peer,
+                       .transport = transport,
+                       .disptype = disptype,
+               };
+               rcu_read_lock();
+               cds_lfht_add(mgr->tcps[tid], dispatch_hash(&key),
+                            &disp->ht_node);
+               rcu_read_unlock();
+       }
+
+       *dispp = disp;
+}
+
+isc_result_t
+dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
+                      const isc_sockaddr_t *destaddr,
+                      dns_transport_t *transport, dns_dispatchtype_t disptype,
+                      dns_dispatchopt_t options, dns_dispatch_t **dispp) {
+       REQUIRE(VALID_DISPATCHMGR(mgr));
+       REQUIRE(destaddr != NULL);
+
+       isc_result_t result;
+
+       if ((options & DNS_DISPATCHOPT_FIXEDID) == 0) {
+               result = dispatch_gettcp(mgr, localaddr, destaddr, transport,
+                                        disptype, dispp);
+               if (result == ISC_R_SUCCESS) {
+                       if (isc_log_wouldlog(90)) {
+                               char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+
+                               isc_sockaddr_format(&(*dispp)->local, addrbuf,
+                                                   ISC_SOCKADDR_FORMATSIZE);
+
+                               mgr_log(mgr, ISC_LOG_DEBUG(90),
+                                       "dns_dispatch_createtcp: reused TCP "
+                                       "dispatch %p for "
+                                       "%s",
+                                       *dispp, addrbuf);
+                       }
+                       return result;
+               }
+       }
+
+       /*
+        * Otherwise allocate new TCP dispatch.
+        */
+
+       dispatch_createtcp(mgr, localaddr, destaddr, transport, disptype,
+                          options, dispp);
+
+       if (isc_log_wouldlog(90)) {
+               char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+
+               isc_sockaddr_format(&(*dispp)->local, addrbuf,
+                                   ISC_SOCKADDR_FORMATSIZE);
+
+               mgr_log(mgr, ISC_LOG_DEBUG(90),
+                       "dns_dispatch_createtcp: created TCP dispatch %p for "
+                       "%s",
+                       *dispp, addrbuf);
+       }
+
+       return ISC_R_SUCCESS;
+}
+
 isc_result_t
 dns_dispatch_createudp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
                       dns_dispatch_t **dispp) {
@@ -1372,8 +1408,8 @@ dispatch_destroy(dns_dispatch_t *disp) {
 
        disp->magic = 0;
 
-       if (disp->socktype == isc_socktype_tcp &&
-           (disp->options & DNS_DISPATCHOPT_UNSHARED) == 0)
+       if ((disp->options & DNS_DISPATCHOPT_FIXEDID) == 0 &&
+           disp->socktype == isc_socktype_tcp)
        {
                (void)cds_lfht_del(mgr->tcps[tid], &disp->ht_node);
        }
index 3e54f00662338719231776b1d5efca0d97040e59..f7cc6eaf273e0203cdef2bfd43d462c3f187fd51 100644 (file)
@@ -71,9 +71,14 @@ struct dns_dispatchset {
 
 typedef enum dns_dispatchopt {
        DNS_DISPATCHOPT_FIXEDID = 1 << 0,
-       DNS_DISPATCHOPT_UNSHARED = 1 << 1, /* Don't share this connection */
 } dns_dispatchopt_t;
 
+typedef enum dns_dispatchtype {
+       DNS_DISPATCHTYPE_RESOLVER,
+       DNS_DISPATCHTYPE_REQUEST,
+       DNS_DISPATCHTYPE_XFRIN,
+} dns_dispatchtype_t;
+
 isc_result_t
 dns_dispatchmgr_create(isc_mem_t *mctx, dns_dispatchmgr_t **mgrp);
 /*%<
@@ -181,8 +186,8 @@ dns_dispatch_createudp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
 isc_result_t
 dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr,
                       const isc_sockaddr_t *destaddr,
-                      dns_transport_t *transport, dns_dispatchopt_t options,
-                      dns_dispatch_t **dispp);
+                      dns_transport_t *transport, dns_dispatchtype_t disptype,
+                      dns_dispatchopt_t options, dns_dispatch_t **dispp);
 /*%<
  * Create a new TCP dns_dispatch.
  *
@@ -261,35 +266,6 @@ dns_dispatch_resume(dns_dispentry_t *resp, unsigned int timeout);
  *\li  'resp' is valid.
  */
 
-isc_result_t
-dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr,
-                   const isc_sockaddr_t *localaddr, dns_transport_t *transport,
-                   dns_dispatch_t **dispp);
-/*
- * Attempt to connect to a existing TCP connection that was created with
- * parameters that match destaddr, localaddr and transport.
- *
- * If localaddr is NULL, we ignore the dispatch's localaddr when looking
- * for a match.  However, if transport is NULL, then the matching dispatch
- * must also have been created with a NULL transport.
- *
- * Requires:
- *\li  mgr to be valid dispatch manager.
- *
- *\li  dstaddr to be a valid sockaddr.
- *
- *\li  localaddr to be NULL or a valid sockaddr.
- *
- *\li  transport is NULL or a valid transport.
- *
- *\li  dispp to be non NULL and *dispp to be NULL
- *
- * Returns:
- *\li  ISC_R_SUCCESS   -- success.
- *
- *\li  Anything else   -- failure.
- */
-
 typedef void (*dispatch_cb_t)(isc_result_t eresult, isc_region_t *region,
                              void *cbarg);
 
index 64a4602e624b121b3511a5c99c2bfa4c226124b3..4634333aa6841a251efc3de7412b021dae4b850d 100644 (file)
@@ -334,27 +334,12 @@ isblackholed(dns_dispatchmgr_t *dispatchmgr, const isc_sockaddr_t *destaddr) {
 }
 
 static isc_result_t
-tcp_dispatch(bool newtcp, dns_requestmgr_t *requestmgr,
-            const isc_sockaddr_t *srcaddr, const isc_sockaddr_t *destaddr,
-            dns_transport_t *transport, dns_dispatch_t **dispatchp) {
-       isc_result_t result;
-
-       if (!newtcp) {
-               result = dns_dispatch_gettcp(requestmgr->dispatchmgr, destaddr,
-                                            srcaddr, transport, dispatchp);
-               if (result == ISC_R_SUCCESS) {
-                       char peer[ISC_SOCKADDR_FORMATSIZE];
-
-                       isc_sockaddr_format(destaddr, peer, sizeof(peer));
-                       req_log(ISC_LOG_DEBUG(1),
-                               "attached to TCP connection to %s", peer);
-                       return result;
-               }
-       }
-
-       result = dns_dispatch_createtcp(requestmgr->dispatchmgr, srcaddr,
-                                       destaddr, transport, 0, dispatchp);
-       return result;
+tcp_dispatch(dns_requestmgr_t *requestmgr, const isc_sockaddr_t *srcaddr,
+            const isc_sockaddr_t *destaddr, dns_transport_t *transport,
+            dns_dispatch_t **dispatchp) {
+       return dns_dispatch_createtcp(requestmgr->dispatchmgr, srcaddr,
+                                     destaddr, transport,
+                                     DNS_DISPATCHTYPE_REQUEST, 0, dispatchp);
 }
 
 static isc_result_t
@@ -387,14 +372,14 @@ udp_dispatch(dns_requestmgr_t *requestmgr, const isc_sockaddr_t *srcaddr,
 }
 
 static isc_result_t
-get_dispatch(bool tcp, bool newtcp, dns_requestmgr_t *requestmgr,
+get_dispatch(bool tcp, dns_requestmgr_t *requestmgr,
             const isc_sockaddr_t *srcaddr, const isc_sockaddr_t *destaddr,
             dns_transport_t *transport, dns_dispatch_t **dispatchp) {
        isc_result_t result;
 
        if (tcp) {
-               result = tcp_dispatch(newtcp, requestmgr, srcaddr, destaddr,
-                                     transport, dispatchp);
+               result = tcp_dispatch(requestmgr, srcaddr, destaddr, transport,
+                                     dispatchp);
        } else {
                result = udp_dispatch(requestmgr, srcaddr, destaddr, dispatchp);
        }
@@ -416,7 +401,6 @@ dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
        isc_mem_t *mctx = NULL;
        dns_messageid_t id;
        bool tcp = false;
-       bool newtcp = false;
        isc_region_t r;
        unsigned int dispopt = 0;
 
@@ -465,29 +449,19 @@ dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
        isc_buffer_allocate(mctx, &request->query, r.length + (tcp ? 2 : 0));
        CHECK(isc_buffer_copyregion(request->query, &r));
 
-again:
-       CHECK(get_dispatch(tcp, newtcp, requestmgr, srcaddr, destaddr,
-                          transport, &request->dispatch));
+       CHECK(get_dispatch(tcp, requestmgr, srcaddr, destaddr, transport,
+                          &request->dispatch));
 
        if ((options & DNS_REQUESTOPT_FIXEDID) != 0) {
                id = (r.base[0] << 8) | r.base[1];
                dispopt |= DNS_DISPATCHOPT_FIXEDID;
        }
 
-       result = dns_dispatch_add(request->dispatch, loop, dispopt,
-                                 request->connect_timeout, request->timeout,
-                                 destaddr, transport, tlsctx_cache,
-                                 req_connected, req_senddone, req_response,
-                                 request, &id, &request->dispentry);
-       if (result != ISC_R_SUCCESS) {
-               if ((options & DNS_REQUESTOPT_FIXEDID) != 0 && !newtcp) {
-                       dns_dispatch_detach(&request->dispatch);
-                       newtcp = true;
-                       goto again;
-               }
-
-               goto cleanup;
-       }
+       CHECK(dns_dispatch_add(request->dispatch, loop, dispopt,
+                              request->connect_timeout, request->timeout,
+                              destaddr, transport, tlsctx_cache, req_connected,
+                              req_senddone, req_response, request, &id,
+                              &request->dispentry));
 
        /* Add message ID. */
        isc_buffer_usedregion(request->query, &r);
@@ -586,7 +560,7 @@ dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
        CHECK(dns_message_settsigkey(message, request->tsigkey));
 
 again:
-       CHECK(get_dispatch(tcp, false, requestmgr, srcaddr, destaddr, transport,
+       CHECK(get_dispatch(tcp, requestmgr, srcaddr, destaddr, transport,
                           &request->dispatch));
 
        CHECK(dns_dispatch_add(request->dispatch, loop, 0,
index 64135c2bf017a7e3ffec636fe82c53967ad621dc..b2e925f55b03c13459615bcfac0355fc02c9b96b 100644 (file)
@@ -2195,7 +2195,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 
                result = dns_dispatch_createtcp(fctx->dispatchmgr, &addr,
                                                &sockaddr, addrinfo->transport,
-                                               DNS_DISPATCHOPT_UNSHARED,
+                                               DNS_DISPATCHTYPE_RESOLVER, 0,
                                                &query->dispatch);
                if (result != ISC_R_SUCCESS) {
                        goto cleanup_query;
index eca48700a92e8a36f6a2baf24b02112a0c4a1235..8f974c87465e2b80b45ae286db82e9202e61d9d5 100644 (file)
@@ -1362,7 +1362,7 @@ xfrin_start(dns_xfrin_t *xfr) {
        primaries_timeout = isc_nm_getprimariestimeout();
        result = dns_dispatch_createtcp(dispmgr, &xfr->sourceaddr,
                                        &xfr->primaryaddr, xfr->transport,
-                                       DNS_DISPATCHOPT_UNSHARED, &xfr->disp);
+                                       DNS_DISPATCHTYPE_XFRIN, 0, &xfr->disp);
        dns_dispatchmgr_detach(&dispmgr);
        CHECK(result);
 
index 0165df27427c709e7a089f077f883d42363f7035..79190e36f54d7619f7a38e4df61cd6d5b7f027d5 100644 (file)
@@ -496,56 +496,23 @@ connected_shutdown(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
 }
 
 static void
-connected_gettcp(isc_result_t eresult ISC_ATTR_UNUSED,
-                isc_region_t *region ISC_ATTR_UNUSED, void *arg) {
-       test_dispatch_t *test1 = arg;
-
-       /* Client 2 */
-       isc_result_t result;
-       test_dispatch_t *test2 = isc_mem_get(isc_g_mctx, sizeof(*test2));
-       *test2 = (test_dispatch_t){
-               .dispatchmgr = dns_dispatchmgr_ref(test1->dispatchmgr),
-       };
-
-       result = dns_dispatch_gettcp(test2->dispatchmgr, &tcp_server_addr,
-                                    &tcp_connect_addr, NULL, &test2->dispatch);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
-       assert_ptr_equal(test1->dispatch, test2->dispatch);
-
-       result = dns_dispatch_add(
-               test2->dispatch, isc_loop_main(), 0, T_CLIENT_CONNECT,
-               T_CLIENT_INIT, &tcp_server_addr, NULL, NULL, connected_shutdown,
-               client_senddone, response_noop, test2, &test2->id,
-               &test2->dispentry);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
-       dns_dispatch_connect(test2->dispentry);
-
-       test_dispatch_done(test1);
-}
-
-static void
-connected_newtcp(isc_result_t eresult ISC_ATTR_UNUSED,
-                isc_region_t *region ISC_ATTR_UNUSED, void *arg) {
+connected_sharedtcp(isc_result_t eresult ISC_ATTR_UNUSED,
+                   isc_region_t *region ISC_ATTR_UNUSED, void *arg) {
        test_dispatch_t *test3 = arg;
 
-       /* Client - unshared */
+       /* Second client — should reuse the first client's TCP dispatch. */
        isc_result_t result;
        test_dispatch_t *test4 = isc_mem_get(isc_g_mctx, sizeof(*test4));
        *test4 = (test_dispatch_t){
                .dispatchmgr = dns_dispatchmgr_ref(test3->dispatchmgr),
        };
-       result = dns_dispatch_gettcp(test4->dispatchmgr, &tcp_server_addr,
-                                    &tcp_connect_addr, NULL, &test4->dispatch);
-       assert_int_equal(result, ISC_R_NOTFOUND);
 
        result = dns_dispatch_createtcp(
                test4->dispatchmgr, &tcp_connect_addr, &tcp_server_addr, NULL,
-               DNS_DISPATCHOPT_UNSHARED, &test4->dispatch);
+               DNS_DISPATCHTYPE_RESOLVER, 0, &test4->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       assert_ptr_not_equal(test3->dispatch, test4->dispatch);
+       assert_ptr_equal(test3->dispatch, test4->dispatch);
 
        result = dns_dispatch_add(
                test4->dispatch, isc_loop_main(), 0, T_CLIENT_CONNECT,
@@ -591,9 +558,9 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_connect) {
        result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, NULL, 0,
-                                       &test->dispatch);
+       result = dns_dispatch_createtcp(
+               test->dispatchmgr, &tcp_connect_addr, &tcp_server_addr, NULL,
+               DNS_DISPATCHTYPE_RESOLVER, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_dispatch_add(
@@ -636,9 +603,9 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_response) {
        result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, NULL, 0,
-                                       &test->dispatch);
+       result = dns_dispatch_createtcp(
+               test->dispatchmgr, &tcp_connect_addr, &tcp_server_addr, NULL,
+               DNS_DISPATCHTYPE_RESOLVER, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_dispatch_add(test->dispatch, isc_loop_main(), 0,
@@ -671,9 +638,9 @@ ISC_LOOP_TEST_IMPL(dispatch_tcp_response) {
        result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, NULL, 0,
-                                       &test->dispatch);
+       result = dns_dispatch_createtcp(
+               test->dispatchmgr, &tcp_connect_addr, &tcp_server_addr, NULL,
+               DNS_DISPATCHTYPE_RESOLVER, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_dispatch_add(test->dispatch, isc_loop_main(), 0,
@@ -710,9 +677,9 @@ ISC_LOOP_TEST_IMPL(dispatch_tls_response) {
        result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(test->dispatchmgr, &tls_connect_addr,
-                                       &tls_server_addr, tls_transport, 0,
-                                       &test->dispatch);
+       result = dns_dispatch_createtcp(
+               test->dispatchmgr, &tls_connect_addr, &tls_server_addr,
+               tls_transport, DNS_DISPATCHTYPE_RESOLVER, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_dispatch_add(
@@ -796,7 +763,7 @@ ISC_LOOP_TEST_IMPL(dispatch_getnext) {
        dns_dispatch_connect(test->dispentry);
 }
 
-ISC_LOOP_TEST_IMPL(dispatch_gettcp) {
+ISC_LOOP_TEST_IMPL(dispatch_sharedtcp) {
        isc_result_t result;
        test_dispatch_t *test = isc_mem_get(isc_g_mctx, sizeof(*test));
        *test = (test_dispatch_t){ 0 };
@@ -810,61 +777,27 @@ ISC_LOOP_TEST_IMPL(dispatch_gettcp) {
        /* ensure we stop listening after the test is done */
        isc_loop_teardown(isc_loop_main(), stop_listening, sock);
 
-       result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
-       /* Client */
-       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, NULL, 0,
-                                       &test->dispatch);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
-       result = dns_dispatch_add(
-               test->dispatch, isc_loop_main(), 0, T_CLIENT_CONNECT,
-               T_CLIENT_INIT, &tcp_server_addr, NULL, NULL, connected_gettcp,
-               client_senddone, response_noop, test, &test->id,
-               &test->dispentry);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
-       dns_dispatch_connect(test->dispentry);
-}
-
-ISC_LOOP_TEST_IMPL(dispatch_newtcp) {
-       isc_result_t result;
-       test_dispatch_t *test = isc_mem_get(isc_g_mctx, sizeof(*test));
-       *test = (test_dispatch_t){ 0 };
-
-       /* Server */
-       result = isc_nm_listenstreamdns(ISC_NM_LISTEN_ONE, &tcp_server_addr,
-                                       nameserver, NULL, accept_cb, NULL, 0,
-                                       NULL, NULL, ISC_NM_PROXY_NONE, &sock);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
-       /* ensure we stop listening after the test is done */
-       isc_loop_teardown(isc_loop_main(), stop_listening, sock);
-
-       /* Client - unshared */
+       /* First client — creates the shared TCP dispatch. */
        result = dns_dispatchmgr_create(isc_g_mctx, &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_dispatch_createtcp(
                test->dispatchmgr, &tcp_connect_addr, &tcp_server_addr, NULL,
-               DNS_DISPATCHOPT_UNSHARED, &test->dispatch);
+               DNS_DISPATCHTYPE_RESOLVER, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        result = dns_dispatch_add(
                test->dispatch, isc_loop_main(), 0, T_CLIENT_CONNECT,
-               T_CLIENT_INIT, &tcp_server_addr, NULL, NULL, connected_newtcp,
-               client_senddone, response_noop, test, &test->id,
-               &test->dispentry);
+               T_CLIENT_INIT, &tcp_server_addr, NULL, NULL,
+               connected_sharedtcp, client_senddone, response_noop, test,
+               &test->id, &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        dns_dispatch_connect(test->dispentry);
 }
 
 ISC_TEST_LIST_START
-ISC_TEST_ENTRY_CUSTOM(dispatch_gettcp, setup_test, teardown_test)
-ISC_TEST_ENTRY_CUSTOM(dispatch_newtcp, setup_test, teardown_test)
+ISC_TEST_ENTRY_CUSTOM(dispatch_sharedtcp, setup_test, teardown_test)
 ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_udp_response, setup_test, teardown_test)
 ISC_TEST_ENTRY_CUSTOM(dispatchset_create, setup_test, teardown_test)
 ISC_TEST_ENTRY_CUSTOM(dispatchset_get, setup_test, teardown_test)