]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add dispatch_getcp and dispatch_newtcp tests
authorOndřej Surý <ondrej@isc.org>
Mon, 23 Oct 2023 10:26:50 +0000 (12:26 +0200)
committerOndřej Surý <ondrej@isc.org>
Tue, 24 Oct 2023 11:07:03 +0000 (13:07 +0200)
Refactor the dispatch unit test to use more local variables (previously
dispatchmgr, dispatch and dispentry were all global), and add two new
tests:

* dispatch_getcp - test whether the TCP connection will get reused
* dispatch_newtcp - test that the TCP connection will not get reused
                    when DNS_DISPATCHOPT_UNSHARED is in effect

tests/dns/dispatch_test.c

index c8950069cd0d5c325c24819b611c8492165cd768..cb1f023d35d06bb5df31acb666d87f2d6a19849e 100644 (file)
@@ -65,8 +65,6 @@ static const char *tls_name_str = "ephemeral";
 static dns_transport_t *tls_transport = NULL;
 static dns_transport_list_t *transport_list = NULL;
 
-static dns_dispatchmgr_t *dispatchmgr = NULL;
-static dns_dispatch_t *dispatch = NULL;
 static isc_nmsocket_t *sock = NULL;
 
 static isc_nm_t *connect_nm = NULL;
@@ -80,6 +78,27 @@ struct {
        uint8_t message[12];
 } testdata;
 
+typedef struct {
+       dns_dispatchmgr_t *dispatchmgr;
+       dns_dispatch_t *dispatch;
+       dns_dispentry_t *dispentry;
+       uint16_t id;
+} test_dispatch_t;
+
+static void
+test_dispatch_done(test_dispatch_t *test) {
+       dns_dispatch_done(&test->dispentry);
+       dns_dispatch_detach(&test->dispatch);
+       dns_dispatchmgr_detach(&test->dispatchmgr);
+       isc_mem_put(mctx, test, sizeof(*test));
+}
+
+static void
+test_dispatch_shutdown(test_dispatch_t *test) {
+       test_dispatch_done(test);
+       isc_loopmgr_shutdown(loopmgr);
+}
+
 static int
 setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
        socklen_t addrlen = sizeof(*addr);
@@ -131,7 +150,10 @@ setup_test(void **state) {
 
        uv_os_sock_t socket = -1;
 
-       setup_loopmgr(state);
+       /* Create just 1 loop for this test */
+       isc_loopmgr_create(mctx, 1, &loopmgr);
+       mainloop = isc_loop_main(loopmgr);
+
        setup_netmgr(state);
 
        isc_netmgr_create(mctx, loopmgr, &connect_nm);
@@ -221,7 +243,8 @@ teardown_test(void **state) {
 }
 
 static isc_result_t
-make_dispatchset(unsigned int ndisps, dns_dispatchset_t **dsetp) {
+make_dispatchset(dns_dispatchmgr_t *dispatchmgr, unsigned int ndisps,
+                dns_dispatchset_t **dsetp) {
        isc_result_t result;
        isc_sockaddr_t any;
        dns_dispatch_t *disp = NULL;
@@ -242,6 +265,7 @@ make_dispatchset(unsigned int ndisps, dns_dispatchset_t **dsetp) {
 ISC_LOOP_TEST_IMPL(dispatchset_create) {
        dns_dispatchset_t *dset = NULL;
        isc_result_t result;
+       dns_dispatchmgr_t *dispatchmgr = NULL;
 
        UNUSED(arg);
 
@@ -249,11 +273,11 @@ ISC_LOOP_TEST_IMPL(dispatchset_create) {
                                        &dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = make_dispatchset(1, &dset);
+       result = make_dispatchset(dispatchmgr, 1, &dset);
        assert_int_equal(result, ISC_R_SUCCESS);
        dns_dispatchset_destroy(&dset);
 
-       result = make_dispatchset(10, &dset);
+       result = make_dispatchset(dispatchmgr, 10, &dset);
        assert_int_equal(result, ISC_R_SUCCESS);
        dns_dispatchset_destroy(&dset);
 
@@ -265,6 +289,7 @@ ISC_LOOP_TEST_IMPL(dispatchset_create) {
 /* test dispatch set per-loop dispatch */
 ISC_LOOP_TEST_IMPL(dispatchset_get) {
        isc_result_t result;
+       dns_dispatchmgr_t *dispatchmgr = NULL;
        dns_dispatchset_t *dset = NULL;
        dns_dispatch_t *d1, *d2, *d3, *d4, *d5;
        uint32_t tid_saved;
@@ -275,7 +300,7 @@ ISC_LOOP_TEST_IMPL(dispatchset_get) {
                                        &dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = make_dispatchset(1, &dset);
+       result = make_dispatchset(dispatchmgr, 1, &dset);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        d1 = dns_dispatchset_get(dset);
@@ -291,7 +316,7 @@ ISC_LOOP_TEST_IMPL(dispatchset_get) {
 
        dns_dispatchset_destroy(&dset);
 
-       result = make_dispatchset(4, &dset);
+       result = make_dispatchset(dispatchmgr, 4, &dset);
        assert_int_equal(result, ISC_R_SUCCESS);
 
        /*
@@ -322,21 +347,20 @@ ISC_LOOP_TEST_IMPL(dispatchset_get) {
        isc_loopmgr_shutdown(loopmgr);
 }
 
-static dns_dispentry_t *dispentry = NULL;
 static atomic_bool first = true;
 
 static void
-server_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+server_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
        UNUSED(handle);
        UNUSED(eresult);
-       UNUSED(cbarg);
+       UNUSED(arg);
 
        return;
 }
 
 static void
 nameserver(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
-          void *cbarg ISC_ATTR_UNUSED) {
+          void *arg ISC_ATTR_UNUSED) {
        isc_region_t response1, response2;
        static unsigned char buf1[16];
        static unsigned char buf2[16];
@@ -369,79 +393,160 @@ nameserver(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
 }
 
 static isc_result_t
-accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
        UNUSED(handle);
-       UNUSED(cbarg);
+       UNUSED(arg);
 
        return (eresult);
 }
 
 static void
 noop_nameserver(isc_nmhandle_t *handle, isc_result_t eresult,
-               isc_region_t *region, void *cbarg) {
+               isc_region_t *region, void *arg) {
        UNUSED(handle);
        UNUSED(eresult);
        UNUSED(region);
-       UNUSED(cbarg);
+       UNUSED(arg);
 }
 
 static void
-response_getnext(isc_result_t result, isc_region_t *region, void *arg) {
-       UNUSED(region);
-       UNUSED(arg);
+response_getnext(isc_result_t result, isc_region_t *region ISC_ATTR_UNUSED,
+                void *arg) {
+       test_dispatch_t *test = arg;
 
        if (atomic_compare_exchange_strong(&first, &(bool){ true }, false)) {
-               result = dns_dispatch_getnext(dispentry);
+               result = dns_dispatch_getnext(test->dispentry);
                assert_int_equal(result, ISC_R_SUCCESS);
        } else {
-               dns_dispatch_done(&dispentry);
-               isc_loopmgr_shutdown(loopmgr);
+               test_dispatch_shutdown(test);
        }
 }
 
 static void
-response(isc_result_t eresult, isc_region_t *region, void *arg) {
-       UNUSED(region);
-       UNUSED(arg);
+response_noop(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
+             void *arg) {
+       test_dispatch_t *test = arg;
+
+       if (eresult == ISC_R_SUCCESS) {
+               test_dispatch_done(test);
+       }
+}
+
+static void
+response_shutdown(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
+                 void *arg) {
+       test_dispatch_t *test = arg;
 
        assert_int_equal(eresult, ISC_R_SUCCESS);
 
-       dns_dispatch_done(&dispentry);
-       isc_loopmgr_shutdown(loopmgr);
+       test_dispatch_shutdown(test);
 }
 
 static void
-response_timeout(isc_result_t eresult, isc_region_t *region, void *arg) {
-       UNUSED(region);
-       UNUSED(arg);
+response_timeout(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
+                void *arg) {
+       test_dispatch_t *test = arg;
 
        assert_int_equal(eresult, ISC_R_TIMEDOUT);
 
-       dns_dispatch_done(&dispentry);
-       isc_loopmgr_shutdown(loopmgr);
+       test_dispatch_shutdown(test);
 }
 
 static void
-connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
-       isc_region_t *r = (isc_region_t *)cbarg;
-
+client_senddone(isc_result_t eresult, isc_region_t *region, void *arg) {
        UNUSED(eresult);
        UNUSED(region);
+       UNUSED(arg);
+}
+
+static void
+connected(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
+         void *arg) {
+       test_dispatch_t *test = arg;
+
+       REQUIRE(eresult == ISC_R_SUCCESS);
 
-       dns_dispatch_send(dispentry, r);
+       dns_dispatch_send(test->dispentry, &testdata.region);
 }
 
 static void
-client_senddone(isc_result_t eresult, isc_region_t *region, void *cbarg) {
-       UNUSED(eresult);
-       UNUSED(region);
-       UNUSED(cbarg);
+connected_shutdown(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
+                  void *arg) {
+       test_dispatch_t *test = arg;
+
+       REQUIRE(eresult == ISC_R_SUCCESS);
+
+       test_dispatch_shutdown(test);
 }
 
 static void
-timeout_connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
-       UNUSED(region);
-       UNUSED(cbarg);
+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(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, &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(loopmgr), 0,
+                                 T_CLIENT_CONNECT, &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) {
+       test_dispatch_t *test3 = arg;
+
+       /* Client - unshared */
+       isc_result_t result;
+       test_dispatch_t *test4 = isc_mem_get(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, &test4->dispatch);
+       assert_int_equal(result, ISC_R_NOTFOUND);
+
+       result = dns_dispatch_createtcp(
+               test4->dispatchmgr, &tcp_connect_addr, &tcp_server_addr,
+               DNS_DISPATCHOPT_UNSHARED, &test4->dispatch);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       assert_ptr_not_equal(test3->dispatch, test4->dispatch);
+
+       result = dns_dispatch_add(test4->dispatch, isc_loop_main(loopmgr), 0,
+                                 T_CLIENT_CONNECT, &tcp_server_addr, NULL,
+                                 NULL, connected_shutdown, client_senddone,
+                                 response_noop, test4, &test4->id,
+                                 &test4->dispentry);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       dns_dispatch_connect(test4->dispentry);
+
+       test_dispatch_done(test3);
+}
+
+static void
+timeout_connected(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
+                 void *arg) {
+       test_dispatch_t *test = arg;
 
        if (eresult == ISC_R_ADDRNOTAVAIL || eresult == ISC_R_CONNREFUSED) {
                /* FIXME: Skip */
@@ -449,14 +554,13 @@ timeout_connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
                assert_int_equal(eresult, ISC_R_TIMEDOUT);
        }
 
-       dns_dispatch_done(&dispentry);
-
-       isc_loopmgr_shutdown(loopmgr);
+       test_dispatch_shutdown(test);
 }
 
 ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_connect) {
        isc_result_t result;
-       uint16_t id;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
 
        /* Client */
        tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
@@ -466,26 +570,24 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_connect) {
        testdata.region.length = sizeof(testdata.message);
 
        result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
-                                       &dispatchmgr);
+                                       &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, 0, &dispatch);
+       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
+                                       &tcp_server_addr, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatchmgr_detach(&dispatchmgr);
 
-       result = dns_dispatch_add(dispatch, isc_loop_main(loopmgr), 0,
+       result = dns_dispatch_add(test->dispatch, isc_loop_main(loopmgr), 0,
                                  T_CLIENT_CONNECT, &tcp_server_addr, NULL,
                                  NULL, timeout_connected, client_senddone,
-                                 response_timeout, &testdata.region, &id,
-                                 &dispentry);
+                                 response_timeout, test, &test->id,
+                                 &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatch_detach(&dispatch);
 
-       testdata.message[0] = (id >> 8) & 0xff;
-       testdata.message[1] = id & 0xff;
+       testdata.message[0] = (test->id >> 8) & 0xff;
+       testdata.message[1] = test->id & 0xff;
 
-       dns_dispatch_connect(dispentry);
+       dns_dispatch_connect(test->dispentry);
 }
 
 static void
@@ -499,7 +601,8 @@ stop_listening(void *arg) {
 
 ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_response) {
        isc_result_t result;
-       uint16_t id;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
 
        /* Server */
        result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE,
@@ -512,27 +615,26 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_tcp_response) {
 
        /* Client */
        result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
-                                       &dispatchmgr);
+                                       &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, 0, &dispatch);
+       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
+                                       &tcp_server_addr, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatchmgr_detach(&dispatchmgr);
 
        result = dns_dispatch_add(
-               dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
+               test->dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
                &tcp_server_addr, NULL, NULL, connected, client_senddone,
-               response_timeout, &testdata.region, &id, &dispentry);
+               response_timeout, test, &test->id, &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatch_detach(&dispatch);
 
-       dns_dispatch_connect(dispentry);
+       dns_dispatch_connect(test->dispentry);
 }
 
 ISC_LOOP_TEST_IMPL(dispatch_tcp_response) {
        isc_result_t result;
-       uint16_t id;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
 
        /* Server */
        result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE,
@@ -547,30 +649,29 @@ ISC_LOOP_TEST_IMPL(dispatch_tcp_response) {
        testdata.region.length = sizeof(testdata.message);
 
        result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
-                                       &dispatchmgr);
+                                       &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
-                                       &tcp_server_addr, 0, &dispatch);
+       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
+                                       &tcp_server_addr, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatchmgr_detach(&dispatchmgr);
 
-       result = dns_dispatch_add(dispatch, isc_loop_main(loopmgr), 0,
-                                 T_CLIENT_CONNECT, &tcp_server_addr, NULL,
-                                 NULL, connected, client_senddone, response,
-                                 &testdata.region, &id, &dispentry);
+       result = dns_dispatch_add(
+               test->dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
+               &tcp_server_addr, NULL, NULL, connected, client_senddone,
+               response_shutdown, test, &test->id, &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatch_detach(&dispatch);
 
-       testdata.message[0] = (id >> 8) & 0xff;
-       testdata.message[1] = id & 0xff;
+       testdata.message[0] = (test->id >> 8) & 0xff;
+       testdata.message[1] = test->id & 0xff;
 
-       dns_dispatch_connect(dispentry);
+       dns_dispatch_connect(test->dispentry);
 }
 
 ISC_LOOP_TEST_IMPL(dispatch_tls_response) {
        isc_result_t result;
-       uint16_t id;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
 
        /* Server */
        result = isc_nm_listenstreamdns(
@@ -585,37 +686,32 @@ ISC_LOOP_TEST_IMPL(dispatch_tls_response) {
        testdata.region.length = sizeof(testdata.message);
 
        result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
-                                       &dispatchmgr);
+                                       &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createtcp(dispatchmgr, &tls_connect_addr,
-                                       &tls_server_addr, 0, &dispatch);
+       result = dns_dispatch_createtcp(test->dispatchmgr, &tls_connect_addr,
+                                       &tls_server_addr, 0, &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatchmgr_detach(&dispatchmgr);
 
-       result = dns_dispatch_add(dispatch, isc_loop_main(loopmgr), 0,
+       result = dns_dispatch_add(test->dispatch, isc_loop_main(loopmgr), 0,
                                  T_CLIENT_CONNECT, &tls_server_addr,
                                  tls_transport, tls_tlsctx_client_cache,
-                                 connected, client_senddone, response,
-                                 &testdata.region, &id, &dispentry);
+                                 connected, client_senddone, response_shutdown,
+                                 test, &test->id, &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatch_detach(&dispatch);
 
-       testdata.message[0] = (id >> 8) & 0xff;
-       testdata.message[1] = id & 0xff;
+       testdata.message[0] = (test->id >> 8) & 0xff;
+       testdata.message[1] = test->id & 0xff;
 
-       dns_dispatch_connect(dispentry);
+       dns_dispatch_connect(test->dispentry);
 }
 
 ISC_LOOP_TEST_IMPL(dispatch_timeout_udp_response) {
        isc_result_t result;
-       uint16_t id;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
 
        /* Server */
-       result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
-                                       &dispatchmgr);
-       assert_int_equal(result, ISC_R_SUCCESS);
-
        result = isc_nm_listenudp(netmgr, ISC_NM_LISTEN_ONE, &udp_server_addr,
                                  noop_nameserver, NULL, &sock);
        assert_int_equal(result, ISC_R_SUCCESS);
@@ -624,25 +720,28 @@ ISC_LOOP_TEST_IMPL(dispatch_timeout_udp_response) {
        isc_loop_teardown(isc_loop_main(loopmgr), stop_listening, sock);
 
        /* Client */
-       result = dns_dispatch_createudp(dispatchmgr, &udp_connect_addr,
-                                       &dispatch);
+       result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+                                       &test->dispatchmgr);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       result = dns_dispatch_createudp(test->dispatchmgr, &udp_connect_addr,
+                                       &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatchmgr_detach(&dispatchmgr);
 
        result = dns_dispatch_add(
-               dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
+               test->dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
                &udp_server_addr, NULL, NULL, connected, client_senddone,
-               response_timeout, &testdata.region, &id, &dispentry);
+               response_timeout, test, &test->id, &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatch_detach(&dispatch);
 
-       dns_dispatch_connect(dispentry);
+       dns_dispatch_connect(test->dispentry);
 }
 
 /* test dispatch getnext */
 ISC_LOOP_TEST_IMPL(dispatch_getnext) {
        isc_result_t result;
-       uint16_t id;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
 
        /* Server */
        result = isc_nm_listenudp(netmgr, ISC_NM_LISTEN_ONE, &udp_server_addr,
@@ -656,28 +755,93 @@ ISC_LOOP_TEST_IMPL(dispatch_getnext) {
        testdata.region.length = sizeof(testdata.message);
 
        result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
-                                       &dispatchmgr);
+                                       &test->dispatchmgr);
        assert_int_equal(result, ISC_R_SUCCESS);
 
-       result = dns_dispatch_createudp(dispatchmgr, &udp_connect_addr,
-                                       &dispatch);
+       result = dns_dispatch_createudp(test->dispatchmgr, &udp_connect_addr,
+                                       &test->dispatch);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatchmgr_detach(&dispatchmgr);
 
        result = dns_dispatch_add(
-               dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
+               test->dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
                &udp_server_addr, NULL, NULL, connected, client_senddone,
-               response_getnext, &testdata.region, &id, &dispentry);
+               response_getnext, test, &test->id, &test->dispentry);
        assert_int_equal(result, ISC_R_SUCCESS);
-       dns_dispatch_detach(&dispatch);
 
-       testdata.message[0] = (id >> 8) & 0xff;
-       testdata.message[1] = id & 0xff;
+       testdata.message[0] = (test->id >> 8) & 0xff;
+       testdata.message[1] = test->id & 0xff;
+
+       dns_dispatch_connect(test->dispentry);
+}
+
+ISC_LOOP_TEST_IMPL(dispatch_gettcp) {
+       isc_result_t result;
+       test_dispatch_t *test = isc_mem_get(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
+
+       /* Server */
+       result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE,
+                                       &tcp_server_addr, nameserver, NULL,
+                                       accept_cb, NULL, 0, NULL, NULL, &sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       /* ensure we stop listening after the test is done */
+       isc_loop_teardown(isc_loop_main(loopmgr), stop_listening, sock);
+
+       result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+                                       &test->dispatchmgr);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       /* Client */
+       result = dns_dispatch_createtcp(test->dispatchmgr, &tcp_connect_addr,
+                                       &tcp_server_addr, 0, &test->dispatch);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       result = dns_dispatch_add(
+               test->dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
+               &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(mctx, sizeof(*test));
+       *test = (test_dispatch_t){ 0 };
+
+       /* Server */
+       result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ONE,
+                                       &tcp_server_addr, nameserver, NULL,
+                                       accept_cb, NULL, 0, NULL, NULL, &sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       /* ensure we stop listening after the test is done */
+       isc_loop_teardown(isc_loop_main(loopmgr), stop_listening, sock);
+
+       /* Client - unshared */
+       result = dns_dispatchmgr_create(mctx, loopmgr, connect_nm,
+                                       &test->dispatchmgr);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       result = dns_dispatch_createtcp(
+               test->dispatchmgr, &tcp_connect_addr, &tcp_server_addr,
+               DNS_DISPATCHOPT_UNSHARED, &test->dispatch);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       result = dns_dispatch_add(
+               test->dispatch, isc_loop_main(loopmgr), 0, T_CLIENT_CONNECT,
+               &tcp_server_addr, NULL, NULL, connected_newtcp, client_senddone,
+               response_noop, test, &test->id, &test->dispentry);
+       assert_int_equal(result, ISC_R_SUCCESS);
 
-       dns_dispatch_connect(dispentry);
+       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_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)