]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Refactor TLSDNS module to work with libuv/ssl directly
authorOndřej Surý <ondrej@sury.org>
Thu, 17 Dec 2020 10:40:29 +0000 (11:40 +0100)
committerOndřej Surý <ondrej@sury.org>
Mon, 25 Jan 2021 08:19:22 +0000 (09:19 +0100)
* Following the example set in 634bdfb16d8, the tlsdns netmgr
  module now uses libuv and SSL primitives directly, rather than
  opening a TLS socket which opens a TCP socket, as the previous
  model was difficult to debug.  Closes #2335.

* Remove the netmgr tls layer (we will have to re-add it for DoH)

* Add isc_tls API to wrap the OpenSSL SSL_CTX object into libisc
  library; move the OpenSSL initialization/deinitialization from dstapi
  needed for OpenSSL 1.0.x to the isc_tls_{initialize,destroy}()

* Add couple of new shims needed for OpenSSL 1.0.x

* When LibreSSL is used, require at least version 2.7.0 that
  has the best OpenSSL 1.1.x compatibility and auto init/deinit

* Enforce OpenSSL 1.1.x usage on Windows

* Added a TLSDNS unit test and implemented a simple TLSDNS echo
  server and client.

32 files changed:
bin/dig/dighost.c
bin/dig/dighost.h
bin/tests/.gitignore
bin/tests/Makefile.am
bin/tests/test_client.c [new file with mode: 0644]
bin/tests/test_server.c [new file with mode: 0644]
config.h.win32
configure.ac
lib/dns/openssl_link.c
lib/isc/Makefile.am
lib/isc/include/isc/netmgr.h
lib/isc/include/isc/tls.h [new file with mode: 0644]
lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/tcpdns.c
lib/isc/netmgr/tls.c [deleted file]
lib/isc/netmgr/tlsdns.c
lib/isc/openssl_shim.c
lib/isc/openssl_shim.h
lib/isc/tests/Makefile.am
lib/isc/tests/tlsdns_test.c [new file with mode: 0644]
lib/isc/tls.c [new file with mode: 0644]
lib/isc/win32/libisc.def.in
lib/isc/win32/libisc.vcxproj.filters.in
lib/isc/win32/libisc.vcxproj.in
lib/ns/include/ns/listenlist.h
lib/ns/interfacemgr.c
lib/ns/listenlist.c
lib/ns/tests/nstest.c
lib/ns/tests/nstest.h
util/copyrights
win32utils/Configure

index 4346ce5d75aecb23c38af99840e842734998db13..a8df930b54373470c4224c70e7f1d3d28110a88b 100644 (file)
@@ -2828,10 +2828,13 @@ start_tcp(dig_query_t *query) {
                REQUIRE(query != NULL);
 
                if (query->lookup->tls_mode) {
+                       result = isc_tlsctx_createclient(&query->tlsctx);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
                        result = isc_nm_tlsdnsconnect(
                                netmgr, (isc_nmiface_t *)&localaddr,
                                (isc_nmiface_t *)&query->sockaddr,
-                               tcp_connected, query, local_timeout, 0);
+                               tcp_connected, query, local_timeout, 0,
+                               query->tlsctx);
                        check_result(result, "isc_nm_tcpdnsconnect");
                } else {
                        result = isc_nm_tcpdnsconnect(
@@ -3240,6 +3243,10 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
        LOCK_LOOKUP;
        lookup_attach(query->lookup, &l);
 
+       if (query->tlsctx != NULL) {
+               isc_tlsctx_free(&query->tlsctx);
+       }
+
        if (eresult == ISC_R_CANCELED) {
                debug("in cancel handler");
                isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr));
@@ -3306,10 +3313,6 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
 
        launch_next_query(query);
        query_detach(&query);
-       if (l->tls_mode) {
-               /* FIXME: This is a accounting bug in TLSDNS */
-               isc_nmhandle_detach(&handle);
-       }
        lookup_detach(&l);
        UNLOCK_LOOKUP;
 }
index bae21a37cdd982e6270c426c15f5f0dc383d4cd0..b719b4856e5f7e1ebab3baaa01c74fa39543ab61 100644 (file)
@@ -24,6 +24,7 @@
 #include <isc/list.h>
 #include <isc/magic.h>
 #include <isc/mem.h>
+#include <isc/netmgr.h>
 #include <isc/print.h>
 #include <isc/refcount.h>
 #include <isc/sockaddr.h>
@@ -199,6 +200,7 @@ struct dig_query {
        uint64_t byte_count;
        isc_timer_t *timer;
        uint8_t tries;
+       isc_tlsctx_t *tlsctx;
 };
 
 struct dig_server {
index 0946f966d0ce6a7d2756773352930c5e7ae0970e..26185a78897523171bfa3ca0034129cd093b34d9 100644 (file)
@@ -8,3 +8,6 @@ dlopen
 keycreate
 keydelete
 gssapi_krb
+/wire_test
+/test_client
+/test_server
index 2947df96c9653003a24bbe6b40692d279711d398..5366cc44966c87e76edbd04e50ca798c8a8ed17a 100644 (file)
@@ -2,13 +2,33 @@ include $(top_srcdir)/Makefile.top
 
 SUBDIRS = system
 
-noinst_PROGRAMS = wire_test
+noinst_PROGRAMS =      \
+       test_client     \
+       test_server     \
+       wire_test
 
-AM_CPPFLAGS +=                 \
+test_client_CPPFLAGS =         \
+       $(AM_CPPFLAGS)          \
+       $(LIBISC_CFLAGS)
+
+test_client_LDADD =            \
+       $(LIBISC_LIBS)          \
+       $(LIBDNS_LIBS)
+
+test_server_CPPFLAGS =         \
+       $(AM_CPPFLAGS)          \
+       $(LIBISC_CFLAGS)
+
+test_server_LDADD =            \
+       $(LIBISC_LIBS)          \
+       $(LIBDNS_LIBS)
+
+wire_test_CPPFLAGS =           \
+       $(AM_CPPFLAGS)          \
        $(LIBISC_CFLAGS)        \
        $(LIBDNS_CFLAGS)
 
-LDADD =                                \
+wire_test_LDADD =              \
        $(LIBISC_LIBS)          \
        $(LIBDNS_LIBS)
 
diff --git a/bin/tests/test_client.c b/bin/tests/test_client.c
new file mode 100644 (file)
index 0000000..3075213
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/netmgr.h>
+#include <isc/os.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+typedef enum { UDP, TCP, DOT, DOH } protocol_t;
+
+static const char *protocols[] = { "udp", "tcp", "dot", "doh" };
+
+static isc_mem_t *mctx = NULL;
+static isc_nm_t *netmgr = NULL;
+
+static protocol_t protocol;
+static const char *address;
+static const char *port;
+static int family = AF_UNSPEC;
+static isc_sockaddr_t sockaddr_local;
+static isc_sockaddr_t sockaddr_remote;
+static int workers;
+static int timeout;
+static uint8_t messagebuf[2 * 65536];
+static isc_region_t message = { .length = 0, .base = messagebuf };
+static int out = -1;
+
+static isc_result_t
+parse_port(const char *input) {
+       char *endptr = NULL;
+       long val = strtol(input, &endptr, 10);
+
+       if ((*endptr != '\0') || (val <= 0) || (val >= 65536)) {
+               return (ISC_R_BADNUMBER);
+       }
+
+       port = input;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+parse_protocol(const char *input) {
+       for (size_t i = 0; i < ARRAY_SIZE(protocols); i++) {
+               if (!strcasecmp(input, protocols[i])) {
+                       protocol = i;
+                       return (ISC_R_SUCCESS);
+               }
+       }
+
+       return (ISC_R_BADNUMBER);
+}
+
+static isc_result_t
+parse_address(const char *input) {
+       struct in6_addr in6;
+       struct in_addr in;
+
+       if (inet_pton(AF_INET6, input, &in6) == 1) {
+               family = AF_INET6;
+               address = input;
+               return (ISC_R_SUCCESS);
+       }
+
+       if (inet_pton(AF_INET, input, &in) == 1) {
+               family = AF_INET;
+               address = input;
+               return (ISC_R_SUCCESS);
+       }
+
+       return (ISC_R_BADADDRESSFORM);
+}
+
+static int
+parse_workers(const char *input) {
+       char *endptr = NULL;
+       long val = strtol(input, &endptr, 10);
+
+       if ((*endptr != '\0') || (val <= 0) || (val >= 128)) {
+               return (ISC_R_BADNUMBER);
+       }
+
+       workers = val;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+parse_timeout(const char *input) {
+       char *endptr = NULL;
+       long val = strtol(input, &endptr, 10);
+
+       if ((*endptr != '\0') || (val <= 0) || (val >= 120)) {
+               return (ISC_R_BADNUMBER);
+       }
+
+       timeout = (in_port_t)val * 1000;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+parse_input(const char *input) {
+       int in = -1;
+
+       if (!strcmp(input, "-")) {
+               in = 0;
+       } else {
+               in = open(input, O_RDONLY);
+       }
+       RUNTIME_CHECK(in >= 0);
+
+       message.length = read(in, message.base, sizeof(messagebuf));
+
+       close(in);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+parse_output(const char *input) {
+       if (!strcmp(input, "-")) {
+               out = 1;
+       } else {
+               out = open(input, O_WRONLY | O_CREAT,
+                          S_IRUSR | S_IRGRP | S_IROTH);
+       }
+       RUNTIME_CHECK(out >= 0);
+
+       return (ISC_R_SUCCESS);
+}
+
+static void
+parse_options(int argc, char **argv) {
+       char buf[ISC_NETADDR_FORMATSIZE];
+
+       /* Set defaults */
+       RUNTIME_CHECK(parse_protocol("UDP") == ISC_R_SUCCESS);
+       RUNTIME_CHECK(parse_port("53000") == ISC_R_SUCCESS);
+       RUNTIME_CHECK(parse_address("::0") == ISC_R_SUCCESS);
+       workers = isc_os_ncpus();
+
+       while (true) {
+               int c;
+               int option_index = 0;
+               static struct option long_options[] = {
+                       { "port", required_argument, NULL, 'p' },
+                       { "address", required_argument, NULL, 'a' },
+                       { "protocol", required_argument, NULL, 'P' },
+                       { "workers", required_argument, NULL, 'w' },
+                       { "timeout", required_argument, NULL, 't' },
+                       { "input", required_argument, NULL, 'i' },
+                       { "output", required_argument, NULL, 'o' },
+                       { 0, 0, NULL, 0 }
+               };
+
+               c = getopt_long(argc, argv, "a:p:P:w:t:i:o:", long_options,
+                               &option_index);
+               if (c == -1) {
+                       break;
+               }
+
+               switch (c) {
+               case 'a':
+                       RUNTIME_CHECK(parse_address(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'p':
+                       RUNTIME_CHECK(parse_port(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'P':
+                       RUNTIME_CHECK(parse_protocol(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'w':
+                       RUNTIME_CHECK(parse_workers(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 't':
+                       RUNTIME_CHECK(parse_timeout(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'i':
+                       RUNTIME_CHECK(parse_input(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'o':
+                       RUNTIME_CHECK(parse_output(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               default:
+                       INSIST(0);
+               }
+       }
+
+       INSIST(optind < argc);
+
+       {
+               struct addrinfo hints = {
+                       .ai_family = family,
+                       .ai_socktype = (protocol == UDP) ? SOCK_DGRAM
+                                                        : SOCK_STREAM,
+               };
+               struct addrinfo *result = NULL;
+               int r = getaddrinfo(address, NULL, &hints, &result);
+               RUNTIME_CHECK(r == 0);
+
+               for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next)
+               {
+                       RUNTIME_CHECK(isc_sockaddr_fromsockaddr(&sockaddr_local,
+                                                               rp->ai_addr) ==
+                                     ISC_R_SUCCESS);
+               }
+       }
+
+       {
+               struct addrinfo hints = {
+                       .ai_family = family,
+                       .ai_socktype = (protocol == UDP) ? SOCK_DGRAM
+                                                        : SOCK_STREAM,
+               };
+               struct addrinfo *result = NULL;
+               int r = getaddrinfo(argv[optind], port, &hints, &result);
+               RUNTIME_CHECK(r == 0);
+
+               for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next)
+               {
+                       RUNTIME_CHECK(isc_sockaddr_fromsockaddr(
+                                             &sockaddr_remote, rp->ai_addr) ==
+                                     ISC_R_SUCCESS);
+               }
+       }
+
+       isc_sockaddr_format(&sockaddr_local, buf, sizeof(buf));
+
+       printf("Will connect from %s://%s", protocols[protocol], buf);
+
+       isc_sockaddr_format(&sockaddr_remote, buf, sizeof(buf));
+
+       printf("to %s, %d workers\n", buf, workers);
+}
+
+static void
+_signal(int sig, void (*handler)(int)) {
+       struct sigaction sa = { .sa_handler = handler };
+
+       RUNTIME_CHECK(sigfillset(&sa.sa_mask) == 0);
+       RUNTIME_CHECK(sigaction(sig, &sa, NULL) >= 0);
+}
+
+static void
+setup(void) {
+       sigset_t sset;
+
+       _signal(SIGPIPE, SIG_IGN);
+       _signal(SIGHUP, SIG_DFL);
+       _signal(SIGTERM, SIG_DFL);
+       _signal(SIGINT, SIG_DFL);
+
+       RUNTIME_CHECK(sigemptyset(&sset) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGHUP) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGINT) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGTERM) == 0);
+       RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
+
+       isc_mem_create(&mctx);
+
+       netmgr = isc_nm_start(mctx, workers);
+}
+
+static void
+teardown(void) {
+       if (out > 0) {
+               close(out);
+       }
+
+       isc_nm_destroy(&netmgr);
+       isc_mem_destroy(&mctx);
+}
+
+static void
+yield(void) {
+       sigset_t sset;
+       int sig;
+
+       RUNTIME_CHECK(sigemptyset(&sset) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGHUP) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGINT) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGTERM) == 0);
+       RUNTIME_CHECK(sigwait(&sset, &sig) == 0);
+
+       fprintf(stderr, "Shutting down...\n");
+}
+
+static void
+read_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
+       void *cbarg) {
+       isc_nmhandle_t *readhandle = cbarg;
+
+       REQUIRE(handle != NULL);
+       REQUIRE(eresult == ISC_R_SUCCESS || eresult == ISC_R_CANCELED ||
+               eresult == ISC_R_EOF);
+       REQUIRE(cbarg != NULL);
+
+       fprintf(stderr, "%s(..., %s, ...)\n", __func__,
+               isc_result_totext(eresult));
+
+       if (eresult == ISC_R_SUCCESS) {
+               printf("RECEIVED %u bytes\n", region->length);
+               if (out >= 0) {
+                       ssize_t len = write(out, region->base, region->length);
+                       close(out);
+                       REQUIRE((size_t)len == region->length);
+               }
+       }
+
+       isc_nmhandle_detach(&readhandle);
+       kill(getpid(), SIGTERM);
+}
+
+static void
+send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+       REQUIRE(handle != NULL);
+       REQUIRE(eresult == ISC_R_SUCCESS || eresult == ISC_R_CANCELED ||
+               eresult == ISC_R_EOF);
+       REQUIRE(cbarg == NULL);
+}
+
+static void
+connect_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+       isc_nmhandle_t *readhandle = NULL;
+
+       REQUIRE(handle != NULL);
+       REQUIRE(eresult == ISC_R_SUCCESS);
+       UNUSED(cbarg);
+
+       if (eresult != ISC_R_SUCCESS) {
+               kill(getpid(), SIGTERM);
+               return;
+       }
+
+       fprintf(stderr, "ECHO_CLIENT:%s\n", __func__);
+
+       isc_nmhandle_attach(handle, &readhandle);
+       isc_nm_read(handle, read_cb, readhandle);
+       isc_nm_send(handle, &message, send_cb, NULL);
+}
+
+static void
+run(void) {
+       isc_result_t result;
+
+       switch (protocol) {
+       case UDP:
+               result = isc_nm_udpconnect(netmgr,
+                                          (isc_nmiface_t *)&sockaddr_local,
+                                          (isc_nmiface_t *)&sockaddr_remote,
+                                          connect_cb, NULL, timeout, 0);
+               break;
+       case TCP:
+               result = isc_nm_tcpdnsconnect(netmgr,
+                                             (isc_nmiface_t *)&sockaddr_local,
+                                             (isc_nmiface_t *)&sockaddr_remote,
+                                             connect_cb, NULL, timeout, 0);
+               break;
+       case DOT: {
+               isc_tlsctx_t *tlsdns_ctx = NULL;
+               isc_tlsctx_createclient(&tlsdns_ctx);
+
+               result = isc_nm_tlsdnsconnect(
+                       netmgr, (isc_nmiface_t *)&sockaddr_local,
+                       (isc_nmiface_t *)&sockaddr_remote, connect_cb, NULL,
+                       timeout, 0, tlsdns_ctx);
+               break;
+       }
+       case DOH:
+               INSIST(0);
+               ISC_UNREACHABLE();
+               break;
+       default:
+               INSIST(0);
+               ISC_UNREACHABLE();
+       }
+       REQUIRE(result == ISC_R_SUCCESS);
+
+       yield();
+
+       isc_nm_closedown(netmgr);
+}
+
+int
+main(int argc, char **argv) {
+       parse_options(argc, argv);
+
+       setup();
+
+       run();
+
+       teardown();
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/bin/tests/test_server.c b/bin/tests/test_server.c
new file mode 100644 (file)
index 0000000..b0af7a2
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <getopt.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/netmgr.h>
+#include <isc/os.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+typedef enum { UDP, TCP, DOT, DOH } protocol_t;
+
+static const char *protocols[] = { "udp", "tcp", "dot", "doh" };
+
+static isc_mem_t *mctx = NULL;
+static isc_nm_t *netmgr = NULL;
+
+static protocol_t protocol;
+static in_port_t port;
+static isc_netaddr_t netaddr;
+static isc_sockaddr_t sockaddr __attribute__((unused));
+static int workers;
+
+static void
+read_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
+       void *cbarg);
+static void
+send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg);
+
+static isc_result_t
+parse_port(const char *input) {
+       char *endptr = NULL;
+       long val = strtol(input, &endptr, 10);
+
+       if ((*endptr != '\0') || (val <= 0) || (val >= 65536)) {
+               return (ISC_R_BADNUMBER);
+       }
+
+       port = (in_port_t)val;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+parse_protocol(const char *input) {
+       for (size_t i = 0; i < ARRAY_SIZE(protocols); i++) {
+               if (!strcasecmp(input, protocols[i])) {
+                       protocol = i;
+                       return (ISC_R_SUCCESS);
+               }
+       }
+
+       return (ISC_R_BADNUMBER);
+}
+
+static isc_result_t
+parse_address(const char *input) {
+       struct in6_addr in6;
+       struct in_addr in;
+
+       if (inet_pton(AF_INET6, input, &in6) == 1) {
+               isc_netaddr_fromin6(&netaddr, &in6);
+               return (ISC_R_SUCCESS);
+       }
+
+       if (inet_pton(AF_INET, input, &in) == 1) {
+               isc_netaddr_fromin(&netaddr, &in);
+               return (ISC_R_SUCCESS);
+       }
+
+       return (ISC_R_BADADDRESSFORM);
+}
+
+static int
+parse_workers(const char *input) {
+       char *endptr = NULL;
+       long val = strtol(input, &endptr, 10);
+
+       if ((*endptr != '\0') || (val <= 0) || (val >= 128)) {
+               return (ISC_R_BADNUMBER);
+       }
+
+       workers = val;
+
+       return (ISC_R_SUCCESS);
+}
+
+static void
+parse_options(int argc, char **argv) {
+       char buf[ISC_NETADDR_FORMATSIZE];
+
+       /* Set defaults */
+       RUNTIME_CHECK(parse_protocol("UDP") == ISC_R_SUCCESS);
+       RUNTIME_CHECK(parse_port("53000") == ISC_R_SUCCESS);
+       RUNTIME_CHECK(parse_address("::1") == ISC_R_SUCCESS);
+       workers = isc_os_ncpus();
+
+       while (true) {
+               int c;
+               int option_index = 0;
+               static struct option long_options[] = {
+                       { "port", required_argument, NULL, 'p' },
+                       { "address", required_argument, NULL, 'a' },
+                       { "protocol", required_argument, NULL, 'P' },
+                       { "workers", required_argument, NULL, 'w' },
+                       { 0, 0, NULL, 0 }
+               };
+
+               c = getopt_long(argc, argv, "a:p:P:w:", long_options,
+                               &option_index);
+               if (c == -1) {
+                       break;
+               }
+
+               switch (c) {
+               case 'a':
+                       RUNTIME_CHECK(parse_address(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'p':
+                       RUNTIME_CHECK(parse_port(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'P':
+                       RUNTIME_CHECK(parse_protocol(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               case 'w':
+                       RUNTIME_CHECK(parse_workers(optarg) == ISC_R_SUCCESS);
+                       break;
+
+               default:
+                       INSIST(0);
+               }
+       }
+
+       isc_sockaddr_fromnetaddr(&sockaddr, &netaddr, port);
+
+       isc_sockaddr_format(&sockaddr, buf, sizeof(buf));
+
+       printf("Will listen at %s://%s, %d workers\n", protocols[protocol], buf,
+              workers);
+}
+
+static void
+_signal(int sig, void (*handler)(int)) {
+       struct sigaction sa = { .sa_handler = handler };
+
+       RUNTIME_CHECK(sigfillset(&sa.sa_mask) == 0);
+       RUNTIME_CHECK(sigaction(sig, &sa, NULL) >= 0);
+}
+
+static void
+setup(void) {
+       sigset_t sset;
+
+       _signal(SIGPIPE, SIG_IGN);
+       _signal(SIGHUP, SIG_DFL);
+       _signal(SIGTERM, SIG_DFL);
+       _signal(SIGINT, SIG_DFL);
+
+       RUNTIME_CHECK(sigemptyset(&sset) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGHUP) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGINT) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGTERM) == 0);
+       RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
+
+       isc_mem_create(&mctx);
+
+       netmgr = isc_nm_start(mctx, workers);
+}
+
+static void
+teardown(void) {
+       isc_nm_destroy(&netmgr);
+       isc_mem_destroy(&mctx);
+}
+
+static void
+yield(void) {
+       sigset_t sset;
+       int sig;
+
+       RUNTIME_CHECK(sigemptyset(&sset) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGHUP) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGINT) == 0);
+       RUNTIME_CHECK(sigaddset(&sset, SIGTERM) == 0);
+       RUNTIME_CHECK(sigwait(&sset, &sig) == 0);
+
+       fprintf(stderr, "Shutting down...\n");
+}
+
+static void
+read_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
+       void *cbarg) {
+       isc_region_t *reply = NULL;
+
+       REQUIRE(handle != NULL);
+       REQUIRE(eresult == ISC_R_SUCCESS);
+       UNUSED(cbarg);
+
+       fprintf(stderr, "RECEIVED %u bytes\n", region->length);
+
+       if (region->length >= 12) {
+               /* long enough to be a DNS header, set QR bit */
+               ((uint8_t *)region->base)[2] ^= 0x80;
+       }
+
+       reply = isc_mem_get(mctx, sizeof(isc_region_t) + region->length);
+       reply->length = region->length;
+       reply->base = (uint8_t *)reply + sizeof(isc_region_t);
+       memmove(reply->base, region->base, region->length);
+
+       isc_nm_send(handle, reply, send_cb, reply);
+       return;
+}
+
+static void
+send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+       isc_region_t *reply = cbarg;
+
+       REQUIRE(handle != NULL);
+       REQUIRE(eresult == ISC_R_SUCCESS);
+
+       isc_mem_put(mctx, cbarg, sizeof(isc_region_t) + reply->length);
+}
+
+static isc_result_t
+accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+       REQUIRE(handle != NULL);
+       REQUIRE(eresult == ISC_R_SUCCESS);
+       UNUSED(cbarg);
+
+       return (ISC_R_SUCCESS);
+}
+
+static void
+run(void) {
+       isc_result_t result;
+       isc_nmsocket_t *sock = NULL;
+
+       switch (protocol) {
+       case UDP:
+               result = isc_nm_listenudp(netmgr, (isc_nmiface_t *)&sockaddr,
+                                         read_cb, NULL, 0, &sock);
+               break;
+       case TCP:
+               result = isc_nm_listentcpdns(netmgr, (isc_nmiface_t *)&sockaddr,
+                                            read_cb, NULL, accept_cb, NULL, 0,
+                                            0, NULL, &sock);
+               break;
+       case DOT: {
+               isc_tlsctx_t *tlsdns_ctx = NULL;
+               isc_tlsctx_createserver(NULL, NULL, &tlsdns_ctx);
+
+               result = isc_nm_listentlsdns(netmgr, (isc_nmiface_t *)&sockaddr,
+                                            read_cb, NULL, accept_cb, NULL, 0,
+                                            0, NULL, tlsdns_ctx, &sock);
+               break;
+       }
+       case DOH:
+               INSIST(0);
+               ISC_UNREACHABLE();
+               break;
+       default:
+               INSIST(0);
+               ISC_UNREACHABLE();
+       }
+       REQUIRE(result == ISC_R_SUCCESS);
+
+       yield();
+
+       isc_nm_stoplistening(sock);
+       isc_nmsocket_close(&sock);
+}
+
+int
+main(int argc, char **argv) {
+       parse_options(argc, argv);
+
+       setup();
+
+       run();
+
+       teardown();
+
+       exit(EXIT_SUCCESS);
+}
index 448411e8de9444b11e047db0fea250f7b375be1f..630385d49d965bbdef3048616649db7d6a7a142c 100644 (file)
@@ -277,19 +277,19 @@ typedef __int64 off_t;
 @DNS_RDATASET_FIXED@
 
 /* Define if OpenSSL includes Ed25519 support */
-@HAVE_OPENSSL_ED25519@
+#define HAVE_OPENSSL_ED25519 1
 
 /* Define if OpenSSL includes Ed448 support */
-@HAVE_OPENSSL_ED448@
+#define HAVE_OPENSSL_ED448 1
 
 /* Define if your OpenSSL version supports DH functions. */
-@HAVE_DH_GET0_KEY@
+#define HAVE_DH_GET0_KEY 1
 
 /* Define if your OpenSSL version supports ECDSA functions. */
-@HAVE_ECDSA_SIG_GET0@
+#define HAVE_ECDSA_SIG_GET0 1
 
 /* Define if your OpenSSL version supports RSA functions. */
-@HAVE_RSA_SET0_KEY@
+#define HAVE_RSA_SET0_KEY 1
 
 /* define if OpenSSL is used for Public-Key Cryptography */
 @USE_OPENSSL@
@@ -297,9 +297,6 @@ typedef __int64 off_t;
 /* Define if native PKCS#11 is used as cryptographic library provider */
 @USE_PKCS11@
 
-/* HMAC_*() return ints */
-@HMAC_RETURN_INT@
-
 /* Define to 1 if you have the `readline' function. */
 @HAVE_READLINE@
 
@@ -328,34 +325,64 @@ typedef __int64 off_t;
 @WANT_QUERYTRACE@
 
 /* Define to 1 if you have the `CRYPTO_zalloc' function. */
-@HAVE_CRYPTO_ZALLOC@
+#define HAVE_CRYPTO_ZALLOC 1
 
 /* Define to 1 if you have the `EVP_CIPHER_CTX_free' function. */
-@HAVE_EVP_CIPHER_CTX_FREE@
+#define HAVE_EVP_CIPHER_CTX_FREE 1
 
 /* Define to 1 if you have the `EVP_CIPHER_CTX_new' function. */
-@HAVE_EVP_CIPHER_CTX_NEW@
+#define HAVE_EVP_CIPHER_CTX_NEW 1
 
 /* Define to 1 if you have the `EVP_MD_CTX_free' function. */
-@HAVE_EVP_MD_CTX_FREE@
+#define HAVE_EVP_MD_CTX_FREE 1
 
 /* Define to 1 if you have the `EVP_MD_CTX_new' function. */
-@HAVE_EVP_MD_CTX_NEW@
+#define HAVE_EVP_MD_CTX_NEW 1
 
 /* Define to 1 if you have the `EVP_MD_CTX_reset' function. */
-@HAVE_EVP_MD_CTX_RESET@
+#define HAVE_EVP_MD_CTX_RESET 1
 
 /* Define to 1 if you have the `HMAC_CTX_free' function. */
-@HAVE_HMAC_CTX_FREE@
+#define HAVE_HMAC_CTX_FREE 1
 
 /* Define to 1 if you have the `HMAC_CTX_get_md' function. */
-@HAVE_HMAC_CTX_GET_MD@
+#define HAVE_HMAC_CTX_GET_MD 1
 
 /* Define to 1 if you have the `HMAC_CTX_new' function. */
-@HAVE_HMAC_CTX_NEW@
+#define HAVE_HMAC_CTX_NEW 1
 
 /* Define to 1 if you have the `HMAC_CTX_reset' function. */
-@HAVE_HMAC_CTX_RESET@
+#define HAVE_HMAC_CTX_RESET 1
+
+/* Define to 1 if you have the `SSL_read_ex' function. */
+#define HAVE_SSL_READ_EX 1
+
+/* Define to 1 if you have the `SSL_peek_ex' function. */
+#define HAVE_SSL_PEEK_EX 1
+
+/* Define to 1 if you have the `SSL_write_ex' function. */
+#define HAVE_SSL_WRITE_EX 1
+
+/* Define to 1 if you have the `BIO_read_ex' function. */
+#define HAVE_BIO_READ_EX 1
+
+/* Define to 1 if you have the `BIO_write_ex' function. */
+#define HAVE_BIO_WRITE_EX 1
+
+/* Define to 1 if you have the `OPENSSL_init_crypto' function. */
+#define HAVE_OPENSSL_INIT_CRYPTO 1
+
+/* Define to 1 if you have the `OPENSSL_init_ssl' function. */
+#define HAVE_OPENSSL_INIT_SSL 1
+
+/* Define to 1 if you have the `TLS_client_method' function. */
+#define HAVE_TLS_CLIENT_METHOD 1
+
+/* Define to 1 if you have the `TLS_server_method' function. */
+#define HAVE_TLS_SERVER_METHOD 1
+
+/* Define to 1 if you have the `SSL_CTX_up_ref' function. */
+#define SSL_CTX_UP_REF 1
 
 /* Define to 1 if you have the `uv_handle_get_data' function. */
 @HAVE_UV_HANDLE_GET_DATA@
index 154e023212aa13bce41f26adab51911fa886c17f..75fc3fbd501ae7c1c367cb165fa10c637b68a147 100644 (file)
@@ -623,11 +623,12 @@ AX_SAVE_FLAGS([openssl])
 CFLAGS="$CFLAGS $OPENSSL_CFLAGS"
 LIBS="$LIBS $OPENSSL_LIBS"
 
-AC_MSG_CHECKING([for OpenSSL >= 1.0.0 or LibreSSL])
+AC_MSG_CHECKING([for OpenSSL >= 1.0.0 or LibreSSL >= 2.7.0])
 AC_COMPILE_IFELSE(
     [AC_LANG_PROGRAM([[#include <openssl/opensslv.h>]],
-                    [[#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x1000000fL)
-                      #error OpenSSL >= 1.0.0 or LibreSSL required
+                    [[#if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x1000000fL)) || \\
+                          (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x02070000fL))
+                      #error OpenSSL >= 1.0.0 or LibreSSL >= 2.7.0 required
                       #endif
                      ]])],
     [AC_MSG_RESULT([yes])],
@@ -637,10 +638,15 @@ AC_COMPILE_IFELSE(
 # Check for functions added in OpenSSL or LibreSSL
 #
 
+AC_CHECK_FUNCS([OPENSSL_init_ssl OPENSSL_init_crypto])
 AC_CHECK_FUNCS([CRYPTO_zalloc])
 AC_CHECK_FUNCS([EVP_CIPHER_CTX_new EVP_CIPHER_CTX_free])
 AC_CHECK_FUNCS([EVP_MD_CTX_new EVP_MD_CTX_free EVP_MD_CTX_reset])
 AC_CHECK_FUNCS([HMAC_CTX_new HMAC_CTX_free HMAC_CTX_reset HMAC_CTX_get_md])
+AC_CHECK_FUNCS([SSL_read_ex SSL_peek_ex SSL_write_ex])
+AC_CHECK_FUNCS([BIO_read_ex BIO_write_ex])
+AC_CHECK_FUNCS([SSL_CTX_up_ref])
+AC_CHECK_FUNCS([SSL_CTX_set_min_proto_version])
 
 #
 # Check for algorithm support in OpenSSL
@@ -706,7 +712,7 @@ AC_CHECK_FUNCS([EVP_aes_128_ecb EVP_aes_192_ecb EVP_aes_256_ecb], [:],
 #
 AC_CHECK_FUNCS([DH_get0_key ECDSA_SIG_get0 RSA_set0_key])
 
-AC_CHECK_FUNCS([TLS_server_method])
+AC_CHECK_FUNCS([TLS_server_method TLS_client_method])
 
 #
 # Check whether FIPS mode is available and whether we should enable it
index 5ba79569e63ea707fabdec88d5ffadd51af43a6c..ff4760b917baa9224e784d21582796d17113642d 100644 (file)
@@ -29,6 +29,7 @@
 #include <isc/platform.h>
 #include <isc/string.h>
 #include <isc/thread.h>
+#include <isc/tls.h>
 #include <isc/util.h>
 
 #include <dns/log.h>
@@ -44,12 +45,6 @@ static isc_mem_t *dst__mctx = NULL;
 #include <openssl/engine.h>
 #endif /* if !defined(OPENSSL_NO_ENGINE) */
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-static isc_mutex_t *locks = NULL;
-static int nlocks;
-#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-       * defined(LIBRESSL_VERSION_NUMBER) */
-
 #if !defined(OPENSSL_NO_ENGINE)
 static ENGINE *e = NULL;
 #endif /* if !defined(OPENSSL_NO_ENGINE) */
@@ -71,34 +66,6 @@ enable_fips_mode(void) {
 #endif /* HAVE_FIPS_MODE */
 }
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-static void
-lock_callback(int mode, int type, const char *file, int line) {
-       UNUSED(file);
-       UNUSED(line);
-       if ((mode & CRYPTO_LOCK) != 0) {
-               LOCK(&locks[type]);
-       } else {
-               UNLOCK(&locks[type]);
-       }
-}
-#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-       * defined(LIBRESSL_VERSION_NUMBER) */
-
-#if defined(LIBRESSL_VERSION_NUMBER)
-static unsigned long
-id_callback(void) {
-       return ((unsigned long)isc_thread_self());
-}
-#endif /* if defined(LIBRESSL_VERSION_NUMBER) */
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static void
-_set_thread_id(CRYPTO_THREADID *id) {
-       CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
-}
-#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
-
 isc_result_t
 dst__openssl_init(isc_mem_t *mctx, const char *engine) {
        isc_result_t result;
@@ -112,19 +79,7 @@ dst__openssl_init(isc_mem_t *mctx, const char *engine) {
 
        enable_fips_mode();
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-       nlocks = CRYPTO_num_locks();
-       locks = isc_mem_allocate(dst__mctx, sizeof(isc_mutex_t) * nlocks);
-       isc_mutexblock_init(locks, nlocks);
-       CRYPTO_set_locking_callback(lock_callback);
-#if defined(LIBRESSL_VERSION_NUMBER)
-       CRYPTO_set_id_callback(id_callback);
-#elif OPENSSL_VERSION_NUMBER < 0x10100000L
-       CRYPTO_THREADID_set_callback(_set_thread_id);
-#endif /* if defined(LIBRESSL_VERSION_NUMBER) */
-       ERR_load_crypto_strings();
-#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-       * defined(LIBRESSL_VERSION_NUMBER) */
+       isc_tls_initialize();
 
 #if !defined(OPENSSL_NO_ENGINE)
 #if !defined(CONF_MFLAGS_DEFAULT_SECTION)
@@ -178,13 +133,6 @@ cleanup_rm:
        }
        e = NULL;
 #endif /* if !defined(OPENSSL_NO_ENGINE) */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-       CRYPTO_set_locking_callback(NULL);
-       isc_mutexblock_destroy(locks, nlocks);
-       isc_mem_free(dst__mctx, locks);
-       locks = NULL;
-#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
-       * defined(LIBRESSL_VERSION_NUMBER) */
        return (result);
 }
 
@@ -206,25 +154,13 @@ dst__openssl_destroy(void) {
 #endif /* if !defined(OPENSSL_NO_ENGINE) */
        CRYPTO_cleanup_all_ex_data();
        ERR_clear_error();
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-       ERR_remove_thread_state(NULL);
-#elif defined(LIBRESSL_VERSION_NUMBER)
-       ERR_remove_state(0);
-#endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L */
-       ERR_free_strings();
 
 #ifdef DNS_CRYPTO_LEAKS
        CRYPTO_mem_leaks_fp(stderr);
 #endif /* ifdef DNS_CRYPTO_LEAKS */
 
-       if (locks != NULL) {
-               CRYPTO_set_locking_callback(NULL);
-               isc_mutexblock_destroy(locks, nlocks);
-               isc_mem_free(dst__mctx, locks);
-               locks = NULL;
-       }
-#endif /* if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
-       * defined(LIBRESSL_VERSION_NUMBER) */
+#endif
+       isc_tls_destroy();
        isc_mem_detach(&dst__mctx);
 }
 
index 216189fcfd6e142944d526ad93bdb804369c336d..57f5b19f5a65ad2dd377d177f87b509cf4fea927 100644 (file)
@@ -87,6 +87,7 @@ libisc_la_HEADERS =                   \
        include/isc/task.h              \
        include/isc/taskpool.h          \
        include/isc/timer.h             \
+       include/isc/tls.h               \
        include/isc/tm.h                \
        include/isc/types.h             \
        include/isc/utf8.h              \
@@ -128,7 +129,6 @@ libisc_la_SOURCES =         \
        netmgr/tcp.c            \
        netmgr/tcpdns.c         \
        netmgr/tlsdns.c         \
-       netmgr/tls.c            \
        netmgr/udp.c            \
        netmgr/uv-compat.c      \
        netmgr/uv-compat.h      \
@@ -209,6 +209,7 @@ libisc_la_SOURCES =         \
        task.c                  \
        taskpool.c              \
        timer.c                 \
+       tls.c                   \
        tm.c                    \
        utf8.c                  \
        pthreads/condition.c    \
index 7bb21b48e44005b260faf45e15bf61f7e632b5cc..455101bafbef84ce8600476a832883284dad6593 100644 (file)
 #include <isc/mem.h>
 #include <isc/region.h>
 #include <isc/result.h>
+#include <isc/tls.h>
 #include <isc/types.h>
 
-typedef struct ssl_ctx_st isc_ssl_ctx_t;
-
 /*
  * Replacement for isc_sockettype_t provided by socket.h.
  */
@@ -365,11 +364,11 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface,
  */
 
 isc_result_t
-isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
-                   void *cbarg, isc_nm_accept_cb_t accept_cb,
-                   void *accept_cbarg, size_t extrahandlesize, int backlog,
-                   isc_quota_t *quota, isc_ssl_ctx_t *sslctx,
-                   isc_nmsocket_t **sockp);
+isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface,
+                   isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
+                   isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+                   size_t extrahandlesize, int backlog, isc_quota_t *quota,
+                   isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp);
 /*%<
  * Same as isc_nm_listentcpdns but for an SSL (DoT) socket.
  */
@@ -455,17 +454,6 @@ isc_nm_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
  * \li 'mgr' is a valid netmgr.
  */
 
-isc_result_t
-isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
-                isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
-                size_t extrahandlesize, int backlog, isc_quota_t *quota,
-                isc_ssl_ctx_t *sslctx, isc_nmsocket_t **sockp);
-
-isc_result_t
-isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
-                 isc_nm_cb_t cb, void *cbarg, isc_ssl_ctx_t *ctx,
-                 unsigned int timeout, size_t extrahandlesize);
-
 void
 isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp);
 /*%<
@@ -492,7 +480,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
 isc_result_t
 isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
                     isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
-                    size_t extrahandlesize);
+                    size_t extrahandlesize, isc_tlsctx_t *sslctx);
 /*%<
  * Establish a DNS client connection via a TCP or TLS connection, bound to
  * the address 'local' and connected to the address 'peer'.
@@ -506,7 +494,3 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
  * The connected socket can only be accessed via the handle passed to
  * 'cb'.
  */
-
-isc_result_t
-isc_nm_tls_create_server_ctx(const char *keyfile, const char *certfile,
-                            isc_ssl_ctx_t **ctxp);
diff --git a/lib/isc/include/isc/tls.h b/lib/isc/include/isc/tls.h
new file mode 100644 (file)
index 0000000..d031826
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/mem.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/tls.h>
+#include <isc/types.h>
+
+typedef struct ssl_ctx_st isc_tlsctx_t;
+
+void
+isc_tls_initialize(void);
+
+void
+isc_tls_destroy(void);
+
+void
+isc_tlsctx_free(isc_tlsctx_t **ctpx);
+/*%
+ * Free the TLS client/server context.
+ *
+ * Require:
+ *\li  'ctxp' != NULL and '*ctxp' != NULL.
+ */
+
+isc_result_t
+isc_tlsctx_createserver(const char *keyfile, const char *certfile,
+                       isc_tlsctx_t **ctxp);
+/*%
+ * Set up TLS server context.
+ *
+ * Require:
+ *\li  'ctxp' != NULL and '*ctxp' == NULL.
+ */
+
+isc_result_t
+isc_tlsctx_createclient(isc_tlsctx_t **ctxp);
+/*%
+ * Set up TLS client context.
+ *
+ * Require:
+ *\li  'ctxp' != NULL and '*ctxp' == NULL.
+ */
index 18fbe243c48a25d0359083f97f904da6e3fd7e7f..0d7bdae4225f057570407da60e94979bab2f958c 100644 (file)
 #define ISC_NETMGR_RECVBUF_SIZE (65536)
 #endif
 
+#define ISC_NETMGR_SENDBUF_SIZE (sizeof(uint16_t) + UINT16_MAX)
+
+/*%
+ * Regular TCP buffer size.
+ */
+#define NM_REG_BUF 4096
+
+/*%
+ * Larger buffer for when the regular one isn't enough; this will
+ * hold two full DNS packets with lengths.  netmgr receives 64k at
+ * most in TCPDNS or TLSDNS connections, so there's no risk of overrun
+ * when using a buffer this size.
+ */
+#define NM_BIG_BUF (65535 + 2) * 2
+
 #if defined(SO_REUSEPORT_LB) || (defined(SO_REUSEPORT) && defined(__linux__))
 #define HAVE_SO_REUSEPORT_LB 1
 #endif
@@ -162,6 +177,7 @@ typedef struct isc__networker {
        isc_refcount_t references;
        atomic_int_fast64_t pktcount;
        char *recvbuf;
+       char *sendbuf;
        bool recvbuf_inuse;
 } isc__networker_t;
 
@@ -236,12 +252,6 @@ typedef enum isc__netievent_type {
        netievent_tcpdnsclose,
        netievent_tcpdnsstop,
 
-       netievent_tlsclose,
-       netievent_tlssend,
-       netievent_tlsstartread,
-       netievent_tlsconnect,
-       netievent_tlsdobio,
-
        netievent_tlsdnsaccept,
        netievent_tlsdnsconnect,
        netievent_tlsdnssend,
@@ -249,6 +259,8 @@ typedef enum isc__netievent_type {
        netievent_tlsdnscancel,
        netievent_tlsdnsclose,
        netievent_tlsdnsstop,
+       netievent_tlsdnscycle,
+       netievent_tlsdnsshutdown,
 
        netievent_close,
        netievent_shutdown,
@@ -267,6 +279,7 @@ typedef enum isc__netievent_type {
        netievent_udplisten,
        netievent_tcplisten,
        netievent_tcpdnslisten,
+       netievent_tlsdnslisten,
        netievent_resume,
        netievent_detach,
 } isc__netievent_type;
@@ -629,8 +642,6 @@ typedef enum isc_nmsocket_type {
        isc_nm_tcplistener,
        isc_nm_tcpdnslistener,
        isc_nm_tcpdnssocket,
-       isc_nm_tlslistener,
-       isc_nm_tlssocket,
        isc_nm_tlsdnslistener,
        isc_nm_tlsdnssocket
 } isc_nmsocket_type;
@@ -674,22 +685,24 @@ struct isc_nmsocket {
 
        /*% TLS stuff */
        struct tls {
-               bool server;
-               BIO *app_bio;
                SSL *ssl;
                SSL_CTX *ctx;
-               BIO *ssl_bio;
+               BIO *app_rbio;
+               BIO *app_wbio;
+               BIO *ssl_rbio;
+               BIO *ssl_wbio;
                enum {
-                       TLS_INIT,
-                       TLS_HANDSHAKE,
-                       TLS_IO,
-                       TLS_ERROR,
-                       TLS_CLOSING
+                       TLS_STATE_NONE,
+                       TLS_STATE_HANDSHAKE,
+                       TLS_STATE_IO,
+                       TLS_STATE_ERROR,
+                       TLS_STATE_CLOSING
                } state;
-               isc_region_t senddata;
-               bool sending;
+               uv_buf_t senddata;
+               bool cycle;
+               isc_result_t pending_error;
                /* List of active send requests. */
-               ISC_LIST(isc__nm_uvreq_t) sends;
+               isc__nm_uvreq_t *pending_req;
        } tls;
 
        /*%
@@ -1027,13 +1040,6 @@ isc__nm_async_connectcb(isc__networker_t *worker, isc__netievent_t *ev0);
 
  */
 
-isc_result_t
-isc__nm_acceptcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
-                isc_result_t eresult);
-/*%<
- * Issue a synchronous accept callback on the socket.
- */
-
 void
 isc__nm_readcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq,
               isc_result_t eresult);
@@ -1214,25 +1220,6 @@ isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0);
  * stoplisten, send, read, pause, close).
  */
 
-void
-isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0);
-
-void
-isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0);
-
-void
-isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
-
-void
-isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0);
-
-void
-isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0);
-
-/*%<
- * Callback handlers for asynchronouse TLS events.
- */
-
 void
 isc__nm_async_tcpdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0);
 void
@@ -1293,6 +1280,14 @@ isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle);
  * Stop reading on a connected TCPDNS handle.
  */
 
+void
+isc__nm_async_tlsdnscycle(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0);
 void
 isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
                    isc_nm_cb_t cb, void *cbarg);
@@ -1322,6 +1317,10 @@ isc__nm_tlsdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
  * associated with 'handle', and the TCP socket it wraps around.
  */
 
+void
+isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0);
 void
 isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0);
 void
@@ -1330,6 +1329,8 @@ void
 isc__nm_async_tlsdnssend(isc__networker_t *worker, isc__netievent_t *ev0);
 void
 isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0);
+void
+isc__nm_async_tlsdnsshutdown(isc__networker_t *worker, isc__netievent_t *ev0);
 
 void
 isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0);
@@ -1343,35 +1344,6 @@ isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle);
  * Stop reading on a connected TLSDNS handle.
  */
 
-void
-isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
-                void *cbarg);
-
-void
-isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg);
-
-void
-isc__nm_tls_close(isc_nmsocket_t *sock);
-/*%<
- * Close a TLS socket.
- */
-
-void
-isc__nm_tls_pauseread(isc_nmhandle_t *handle);
-/*%<
- * Pause reading on this handle, while still remembering the callback.
- */
-
-void
-isc__nm_tls_resumeread(isc_nmhandle_t *handle);
-/*%<
- * Resume reading from the handle.
- *
- */
-
-void
-isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
-
 #define isc__nm_uverr2result(x) \
        isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__)
 isc_result_t
@@ -1463,12 +1435,6 @@ isc__nm_socket_connectiontimeout(uv_os_sock_t fd, int timeout_ms);
  * the minimum value must be at least 1000 (1 second).
  */
 
-void
-isc__nm_tls_initialize(void);
-/*%<
- * Initialize OpenSSL library, idempotent.
- */
-
 /*
  * typedef all the netievent types
  */
@@ -1478,11 +1444,6 @@ NETIEVENT_SOCKET_TYPE(tcpclose);
 NETIEVENT_SOCKET_TYPE(tcplisten);
 NETIEVENT_SOCKET_TYPE(tcppauseread);
 NETIEVENT_SOCKET_TYPE(tcpstop);
-NETIEVENT_SOCKET_TYPE(tlsclose);
-/* NETIEVENT_SOCKET_TYPE(tlsconnect); */ /* unique type, defined independently
-                                         */
-NETIEVENT_SOCKET_TYPE(tlsdobio);
-NETIEVENT_SOCKET_TYPE(tlsstartread);
 NETIEVENT_SOCKET_TYPE(udpclose);
 NETIEVENT_SOCKET_TYPE(udplisten);
 NETIEVENT_SOCKET_TYPE(udpread);
@@ -1501,13 +1462,17 @@ NETIEVENT_SOCKET_QUOTA_TYPE(tcpdnsaccept);
 NETIEVENT_SOCKET_TYPE(tlsdnsclose);
 NETIEVENT_SOCKET_TYPE(tlsdnsread);
 NETIEVENT_SOCKET_TYPE(tlsdnsstop);
+NETIEVENT_SOCKET_TYPE(tlsdnsshutdown);
+NETIEVENT_SOCKET_TYPE(tlsdnslisten);
+NETIEVENT_SOCKET_REQ_TYPE(tlsdnsconnect);
 NETIEVENT_SOCKET_REQ_TYPE(tlsdnssend);
 NETIEVENT_SOCKET_HANDLE_TYPE(tlsdnscancel);
+NETIEVENT_SOCKET_QUOTA_TYPE(tlsdnsaccept);
+NETIEVENT_SOCKET_TYPE(tlsdnscycle);
 
 NETIEVENT_SOCKET_REQ_TYPE(tcpconnect);
 NETIEVENT_SOCKET_REQ_TYPE(tcpsend);
 NETIEVENT_SOCKET_TYPE(tcpstartread);
-NETIEVENT_SOCKET_REQ_TYPE(tlssend);
 NETIEVENT_SOCKET_REQ_TYPE(udpconnect);
 
 NETIEVENT_SOCKET_REQ_RESULT_TYPE(connectcb);
@@ -1533,10 +1498,6 @@ NETIEVENT_SOCKET_DECL(tcplisten);
 NETIEVENT_SOCKET_DECL(tcppauseread);
 NETIEVENT_SOCKET_DECL(tcpstartread);
 NETIEVENT_SOCKET_DECL(tcpstop);
-NETIEVENT_SOCKET_DECL(tlsclose);
-NETIEVENT_SOCKET_DECL(tlsconnect);
-NETIEVENT_SOCKET_DECL(tlsdobio);
-NETIEVENT_SOCKET_DECL(tlsstartread);
 NETIEVENT_SOCKET_DECL(udpclose);
 NETIEVENT_SOCKET_DECL(udplisten);
 NETIEVENT_SOCKET_DECL(udpread);
@@ -1555,12 +1516,16 @@ NETIEVENT_SOCKET_QUOTA_DECL(tcpdnsaccept);
 NETIEVENT_SOCKET_DECL(tlsdnsclose);
 NETIEVENT_SOCKET_DECL(tlsdnsread);
 NETIEVENT_SOCKET_DECL(tlsdnsstop);
+NETIEVENT_SOCKET_DECL(tlsdnsshutdown);
+NETIEVENT_SOCKET_DECL(tlsdnslisten);
+NETIEVENT_SOCKET_REQ_DECL(tlsdnsconnect);
 NETIEVENT_SOCKET_REQ_DECL(tlsdnssend);
 NETIEVENT_SOCKET_HANDLE_DECL(tlsdnscancel);
+NETIEVENT_SOCKET_QUOTA_DECL(tlsdnsaccept);
+NETIEVENT_SOCKET_DECL(tlsdnscycle);
 
 NETIEVENT_SOCKET_REQ_DECL(tcpconnect);
 NETIEVENT_SOCKET_REQ_DECL(tcpsend);
-NETIEVENT_SOCKET_REQ_DECL(tlssend);
 NETIEVENT_SOCKET_REQ_DECL(udpconnect);
 
 NETIEVENT_SOCKET_REQ_RESULT_DECL(connectcb);
index f30669b5a45fec4bad27f64e20d8cd50566acc99..584142a52cf719685dc526ba20eabb380575f857 100644 (file)
@@ -17,6 +17,7 @@
 #include <isc/buffer.h>
 #include <isc/condition.h>
 #include <isc/errno.h>
+#include <isc/log.h>
 #include <isc/magic.h>
 #include <isc/mem.h>
 #include <isc/netmgr.h>
 #include <isc/stats.h>
 #include <isc/strerr.h>
 #include <isc/thread.h>
+#include <isc/tls.h>
 #include <isc/util.h>
 
 #include "netmgr-int.h"
+#include "openssl_shim.h"
 #include "uv-compat.h"
 
 #ifdef NETMGR_TRACE
@@ -213,7 +216,7 @@ isc_nm_start(isc_mem_t *mctx, uint32_t workers) {
        isc__nm_winsock_initialize();
 #endif /* WIN32 */
 
-       isc__nm_tls_initialize();
+       isc_tls_initialize();
 
        mgr = isc_mem_get(mctx, sizeof(*mgr));
        *mgr = (isc_nm_t){ .nworkers = workers };
@@ -276,6 +279,7 @@ isc_nm_start(isc_mem_t *mctx, uint32_t workers) {
                worker->ievents = isc_queue_new(mgr->mctx, 128);
                worker->ievents_prio = isc_queue_new(mgr->mctx, 128);
                worker->recvbuf = isc_mem_get(mctx, ISC_NETMGR_RECVBUF_SIZE);
+               worker->sendbuf = isc_mem_get(mctx, ISC_NETMGR_SENDBUF_SIZE);
 
                /*
                 * We need to do this here and not in nm_thread to avoid a
@@ -346,6 +350,8 @@ nm_destroy(isc_nm_t **mgr0) {
                isc_mutex_destroy(&worker->lock);
                isc_condition_destroy(&worker->cond);
 
+               isc_mem_put(mgr->mctx, worker->sendbuf,
+                           ISC_NETMGR_SENDBUF_SIZE);
                isc_mem_put(mgr->mctx, worker->recvbuf,
                            ISC_NETMGR_RECVBUF_SIZE);
                isc_thread_join(worker->thread, NULL);
@@ -368,6 +374,8 @@ nm_destroy(isc_nm_t **mgr0) {
                    mgr->nworkers * sizeof(isc__networker_t));
        isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
 
+       isc_tls_destroy();
+
 #ifdef WIN32
        isc__nm_winsock_destroy();
 #endif /* WIN32 */
@@ -708,17 +716,16 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) {
                NETIEVENT_CASE(tcpdnsread);
                NETIEVENT_CASE(tcpdnsstop);
 
-               NETIEVENT_CASE(tlsstartread);
-               NETIEVENT_CASE(tlssend);
-               NETIEVENT_CASE(tlsclose);
-               NETIEVENT_CASE(tlsconnect);
-               NETIEVENT_CASE(tlsdobio);
-
+               NETIEVENT_CASE(tlsdnscycle);
+               NETIEVENT_CASE(tlsdnsaccept);
+               NETIEVENT_CASE(tlsdnslisten);
+               NETIEVENT_CASE(tlsdnsconnect);
                NETIEVENT_CASE(tlsdnssend);
                NETIEVENT_CASE(tlsdnscancel);
                NETIEVENT_CASE(tlsdnsclose);
                NETIEVENT_CASE(tlsdnsread);
                NETIEVENT_CASE(tlsdnsstop);
+               NETIEVENT_CASE(tlsdnsshutdown);
 
                NETIEVENT_CASE(connectcb);
                NETIEVENT_CASE(readcb);
@@ -769,10 +776,6 @@ NETIEVENT_SOCKET_DEF(tcplisten);
 NETIEVENT_SOCKET_DEF(tcppauseread);
 NETIEVENT_SOCKET_DEF(tcpstartread);
 NETIEVENT_SOCKET_DEF(tcpstop);
-NETIEVENT_SOCKET_DEF(tlsclose);
-NETIEVENT_SOCKET_DEF(tlsconnect);
-NETIEVENT_SOCKET_DEF(tlsdobio);
-NETIEVENT_SOCKET_DEF(tlsstartread);
 NETIEVENT_SOCKET_DEF(udpclose);
 NETIEVENT_SOCKET_DEF(udplisten);
 NETIEVENT_SOCKET_DEF(udpread);
@@ -791,12 +794,16 @@ NETIEVENT_SOCKET_QUOTA_DEF(tcpdnsaccept);
 NETIEVENT_SOCKET_DEF(tlsdnsclose);
 NETIEVENT_SOCKET_DEF(tlsdnsread);
 NETIEVENT_SOCKET_DEF(tlsdnsstop);
+NETIEVENT_SOCKET_DEF(tlsdnslisten);
+NETIEVENT_SOCKET_REQ_DEF(tlsdnsconnect);
 NETIEVENT_SOCKET_REQ_DEF(tlsdnssend);
 NETIEVENT_SOCKET_HANDLE_DEF(tlsdnscancel);
+NETIEVENT_SOCKET_QUOTA_DEF(tlsdnsaccept);
+NETIEVENT_SOCKET_DEF(tlsdnscycle);
+NETIEVENT_SOCKET_DEF(tlsdnsshutdown);
 
 NETIEVENT_SOCKET_REQ_DEF(tcpconnect);
 NETIEVENT_SOCKET_REQ_DEF(tcpsend);
-NETIEVENT_SOCKET_REQ_DEF(tlssend);
 NETIEVENT_SOCKET_REQ_DEF(udpconnect);
 
 NETIEVENT_SOCKET_REQ_RESULT_DEF(connectcb);
@@ -1087,9 +1094,6 @@ isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
                case isc_nm_tcpdnssocket:
                        isc__nm_tcpdns_close(sock);
                        return;
-               case isc_nm_tlssocket:
-                       isc__nm_tls_close(sock);
-                       break;
                case isc_nm_tlsdnssocket:
                        isc__nm_tlsdns_close(sock);
                        return;
@@ -1349,7 +1353,7 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
 #endif
        UNLOCK(&sock->lock);
 
-       if (sock->type == isc_nm_tcpsocket || sock->type == isc_nm_tlssocket ||
+       if (sock->type == isc_nm_tcpsocket ||
            (sock->type == isc_nm_udpsocket && atomic_load(&sock->client)) ||
            (sock->type == isc_nm_tcpdnssocket && atomic_load(&sock->client)) ||
            (sock->type == isc_nm_tlsdnssocket && atomic_load(&sock->client)))
@@ -1386,7 +1390,6 @@ isc_nmhandle_is_stream(isc_nmhandle_t *handle) {
 
        return (handle->sock->type == isc_nm_tcpsocket ||
                handle->sock->type == isc_nm_tcpdnssocket ||
-               handle->sock->type == isc_nm_tlssocket ||
                handle->sock->type == isc_nm_tlsdnssocket);
 }
 
@@ -1664,9 +1667,6 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
        case isc_nm_tcpdnssocket:
                isc__nm_tcpdns_send(handle, region, cb, cbarg);
                break;
-       case isc_nm_tlssocket:
-               isc__nm_tls_send(handle, region, cb, cbarg);
-               break;
        case isc_nm_tlsdnssocket:
                isc__nm_tlsdns_send(handle, region, cb, cbarg);
                break;
@@ -1697,9 +1697,6 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
        case isc_nm_tcpdnssocket:
                isc__nm_tcpdns_read(handle, cb, cbarg);
                break;
-       case isc_nm_tlssocket:
-               isc__nm_tls_read(handle, cb, cbarg);
-               break;
        case isc_nm_tlsdnssocket:
                isc__nm_tlsdns_read(handle, cb, cbarg);
                break;
@@ -1742,9 +1739,6 @@ isc_nm_pauseread(isc_nmhandle_t *handle) {
        case isc_nm_tcpsocket:
                isc__nm_tcp_pauseread(handle);
                break;
-       case isc_nm_tlssocket:
-               isc__nm_tls_pauseread(handle);
-               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1761,9 +1755,6 @@ isc_nm_resumeread(isc_nmhandle_t *handle) {
        case isc_nm_tcpsocket:
                isc__nm_tcp_resumeread(handle);
                break;
-       case isc_nm_tlssocket:
-               isc__nm_tls_resumeread(handle);
-               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1784,9 +1775,6 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) {
        case isc_nm_tcplistener:
                isc__nm_tcp_stoplistening(sock);
                break;
-       case isc_nm_tlslistener:
-               isc__nm_tls_stoplistening(sock);
-               break;
        case isc_nm_tlsdnslistener:
                isc__nm_tlsdns_stoplistening(sock);
                break;
@@ -1975,7 +1963,7 @@ shutdown_walk_cb(uv_handle_t *handle, void *arg) {
                isc__nm_tcpdns_shutdown(sock);
                break;
        case isc_nm_tlsdnssocket:
-               /* dummy now */
+               isc__nm_tlsdns_shutdown(sock);
                break;
        case isc_nm_udplistener:
        case isc_nm_tcplistener:
@@ -2334,10 +2322,6 @@ nmsocket_type_totext(isc_nmsocket_type type) {
                return ("isc_nm_tcpdnslistener");
        case isc_nm_tcpdnssocket:
                return ("isc_nm_tcpdnssocket");
-       case isc_nm_tlssocket:
-               return ("isc_nm_tlssocket");
-       case isc_nm_tlslistener:
-               return ("isc_nm_tlslistener");
        case isc_nm_tlsdnslistener:
                return ("isc_nm_tlsdnslistener");
        case isc_nm_tlsdnssocket:
index 1372b63573a2b09b8280b386c30efca472c01789..cbc2cab0edb5a1f2003f035697098dcb87ecbb41 100644 (file)
@@ -113,16 +113,6 @@ stop_reading(isc_nmsocket_t *sock);
 static isc__nm_uvreq_t *
 get_read_req(isc_nmsocket_t *sock);
 
-/*
- * Regular TCP buffer, should suffice in most cases.
- */
-#define NM_REG_BUF 4096
-/*
- * Two full DNS packets with lengths.
- * netmgr receives 64k at most so there's no risk
- * of overrun.
- */
-#define NM_BIG_BUF (65535 + 2) * 2
 static inline void
 alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) {
        REQUIRE(len <= NM_BIG_BUF);
@@ -790,7 +780,7 @@ get_read_req(isc_nmsocket_t *sock) {
                req->handle = isc__nmhandle_get(sock, NULL, NULL);
        }
 
-       return req;
+       return (req);
 }
 
 static void
@@ -813,25 +803,26 @@ start_sock_timer(isc_nmsocket_t *sock) {
        if (sock->read_timeout > 0) {
                int r = uv_timer_start(&sock->timer, readtimeout_cb,
                                       sock->read_timeout, 0);
-               REQUIRE(r == 0);
+               RUNTIME_CHECK(r == 0);
        }
 }
 
 static void
 stop_sock_timer(isc_nmsocket_t *sock) {
        int r = uv_timer_stop(&sock->timer);
-       REQUIRE(r == 0);
+       RUNTIME_CHECK(r == 0);
 }
 
 static void
 start_reading(isc_nmsocket_t *sock) {
+       int r;
+
        if (sock->reading) {
                return;
        }
 
-       int r = uv_read_start(&sock->uv_handle.stream, tcpdns_alloc_cb,
-                             read_cb);
-       REQUIRE(r == 0);
+       r = uv_read_start(&sock->uv_handle.stream, tcpdns_alloc_cb, read_cb);
+       RUNTIME_CHECK(r == 0);
        sock->reading = true;
 
        start_sock_timer(sock);
@@ -839,12 +830,14 @@ start_reading(isc_nmsocket_t *sock) {
 
 static void
 stop_reading(isc_nmsocket_t *sock) {
+       int r;
+
        if (!sock->reading) {
                return;
        }
 
-       int r = uv_read_stop(&sock->uv_handle.stream);
-       REQUIRE(r == 0);
+       r = uv_read_stop(&sock->uv_handle.stream);
+       RUNTIME_CHECK(r == 0);
        sock->reading = false;
 
        stop_sock_timer(sock);
@@ -898,10 +891,11 @@ tcpdns_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
        isc_nmsocket_t *sock = uv_handle_get_data(handle);
        isc__networker_t *worker = NULL;
 
+       UNUSED(size);
+
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->type == isc_nm_tcpdnssocket);
        REQUIRE(isc__nm_in_netthread());
-       UNUSED(size);
 
        worker = &sock->mgr->workers[sock->tid];
        INSIST(!worker->recvbuf_inuse);
@@ -917,9 +911,10 @@ isc__nm_async_tcpdnsread(isc__networker_t *worker, isc__netievent_t *ev0) {
                (isc__netievent_tcpdnsread_t *)ev0;
        isc_nmsocket_t *sock = ievent->sock;
 
+       UNUSED(worker);
+
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
-       UNUSED(worker);
 
        if (inactive(sock)) {
                sock->reading = true;
@@ -1303,9 +1298,10 @@ isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
        isc_nmsocket_t *sock = ievent->sock;
        isc__nm_uvreq_t *uvreq = ievent->req;
 
+       UNUSED(worker);
+
        REQUIRE(sock->type == isc_nm_tcpdnssocket);
        REQUIRE(sock->tid == isc_nm_tid());
-       UNUSED(worker);
 
        result = tcpdns_send_direct(sock, uvreq);
        if (result != ISC_R_SUCCESS) {
@@ -1316,12 +1312,13 @@ isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
 
 static isc_result_t
 tcpdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+       int r;
+
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(VALID_UVREQ(req));
        REQUIRE(sock->tid == isc_nm_tid());
        REQUIRE(sock->type == isc_nm_tcpdnssocket);
 
-       int r;
        uv_buf_t bufs[2] = { { .base = req->tcplen, .len = 2 },
                             { .base = req->uvbuf.base,
                               .len = req->uvbuf.len } };
@@ -1342,12 +1339,13 @@ tcpdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
 static void
 tcpdns_stop_cb(uv_handle_t *handle) {
        isc_nmsocket_t *sock = uv_handle_get_data(handle);
-       uv_handle_set_data(handle, NULL);
 
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
        REQUIRE(atomic_load(&sock->closing));
 
+       uv_handle_set_data(handle, NULL);
+
        if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
                                            true)) {
                INSIST(0);
@@ -1364,6 +1362,7 @@ tcpdns_stop_cb(uv_handle_t *handle) {
 static void
 tcpdns_close_cb(uv_handle_t *handle) {
        isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
        uv_handle_set_data(handle, NULL);
 
        REQUIRE(VALID_NMSOCK(sock));
@@ -1498,11 +1497,11 @@ isc__nm_async_tcpdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
                (isc__netievent_tcpdnsclose_t *)ev0;
        isc_nmsocket_t *sock = ievent->sock;
 
+       UNUSED(worker);
+
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
 
-       UNUSED(worker);
-
        tcpdns_close_direct(sock);
 }
 
@@ -1560,19 +1559,22 @@ isc__nm_async_tcpdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
                (isc__netievent_tcpdnscancel_t *)ev0;
        isc_nmsocket_t *sock = ievent->sock;
 
+       UNUSED(worker);
+
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
-       UNUSED(worker);
 
        failed_read_cb(sock, ISC_R_EOF);
 }
 
 void
 isc__nm_tcpdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+       isc_nmsocket_t *sock = NULL;
+
        REQUIRE(VALID_NMHANDLE(handle));
        REQUIRE(VALID_NMSOCK(handle->sock));
 
-       isc_nmsocket_t *sock = handle->sock;
+       sock = handle->sock;
 
        sock->read_timeout = timeout;
        if (uv_is_active((uv_handle_t *)&sock->timer)) {
@@ -1582,11 +1584,13 @@ isc__nm_tcpdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
 
 void
 isc_nm_tcpdns_sequential(isc_nmhandle_t *handle) {
+       isc_nmsocket_t *sock = NULL;
+
        REQUIRE(VALID_NMHANDLE(handle));
        REQUIRE(VALID_NMSOCK(handle->sock));
        REQUIRE(handle->sock->type == isc_nm_tcpdnssocket);
 
-       isc_nmsocket_t *sock = handle->sock;
+       sock = handle->sock;
 
        /*
         * We don't want pipelining on this connection. That means
@@ -1603,11 +1607,13 @@ isc_nm_tcpdns_sequential(isc_nmhandle_t *handle) {
 
 void
 isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle, bool value) {
+       isc_nmsocket_t *sock = NULL;
+
        REQUIRE(VALID_NMHANDLE(handle));
        REQUIRE(VALID_NMSOCK(handle->sock));
        REQUIRE(handle->sock->type != isc_nm_tcpdnssocket);
 
-       isc_nmsocket_t *sock = handle->sock;
+       sock = handle->sock;
 
        atomic_store(&sock->keepalive, value);
 }
diff --git a/lib/isc/netmgr/tls.c b/lib/isc/netmgr/tls.c
deleted file mode 100644 (file)
index f685493..0000000
+++ /dev/null
@@ -1,907 +0,0 @@
-/*
- * 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 https://mozilla.org/MPL/2.0/.
- *
- * See the COPYRIGHT file distributed with this work for additional
- * information regarding copyright ownership.
- */
-
-#include <libgen.h>
-#include <unistd.h>
-#include <uv.h>
-
-#include <openssl/err.h>
-#include <openssl/ssl.h>
-
-#include <isc/atomic.h>
-#include <isc/buffer.h>
-#include <isc/condition.h>
-#include <isc/log.h>
-#include <isc/magic.h>
-#include <isc/mem.h>
-#include <isc/netmgr.h>
-#include <isc/once.h>
-#include <isc/quota.h>
-#include <isc/random.h>
-#include <isc/refcount.h>
-#include <isc/region.h>
-#include <isc/result.h>
-#include <isc/sockaddr.h>
-#include <isc/stdtime.h>
-#include <isc/thread.h>
-#include <isc/util.h>
-
-#include "netmgr-int.h"
-#include "uv-compat.h"
-
-#define TLS_BUF_SIZE 65536
-
-static isc_result_t
-tls_error_to_result(int tls_err) {
-       switch (tls_err) {
-       case SSL_ERROR_ZERO_RETURN:
-               return (ISC_R_EOF);
-       default:
-               return (ISC_R_UNEXPECTED);
-       }
-}
-
-static void
-tls_do_bio(isc_nmsocket_t *sock);
-
-static void
-tls_close_direct(isc_nmsocket_t *sock);
-
-static void
-async_tls_do_bio(isc_nmsocket_t *sock);
-
-/*
- * The socket is closing, outerhandle has been detached, listener is
- * inactive, or the netmgr is closing: any operation on it should abort
- * with ISC_R_CANCELED.
- */
-static bool
-inactive(isc_nmsocket_t *sock) {
-       return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
-               sock->outerhandle == NULL ||
-               (sock->listener != NULL &&
-                !isc__nmsocket_active(sock->listener)) ||
-               atomic_load(&sock->mgr->closing));
-}
-
-static void
-tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
-       isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
-
-       UNUSED(handle);
-       /*  XXXWPK TODO */
-       UNUSED(eresult);
-
-       isc_mem_put(sock->mgr->mctx, sock->tls.senddata.base,
-                   sock->tls.senddata.length);
-       sock->tls.senddata = (isc_region_t){ NULL, 0 };
-       sock->tls.sending = false;
-
-       async_tls_do_bio(sock);
-}
-
-static void
-async_tls_do_bio(isc_nmsocket_t *sock) {
-       isc__netievent_tlsdobio_t *ievent =
-               isc__nm_get_netievent_tlsdobio(sock->mgr, sock);
-       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                              (isc__netievent_t *)ievent);
-}
-
-static void
-tls_do_bio(isc_nmsocket_t *sock) {
-       isc_result_t result = ISC_R_SUCCESS;
-       int pending, tls_err = 0;
-       int rv;
-       isc__nm_uvreq_t *req;
-
-       REQUIRE(sock->tid == isc_nm_tid());
-       /* We will resume read if TLS layer wants us to */
-       isc_nm_pauseread(sock->outerhandle);
-
-       if (inactive(sock)) {
-               result = ISC_R_CANCELED;
-               goto error;
-       }
-
-       if (sock->tls.state == TLS_INIT) {
-               (void)SSL_do_handshake(sock->tls.ssl);
-               sock->tls.state = TLS_HANDSHAKE;
-       }
-
-       if (sock->tls.state == TLS_ERROR) {
-               result = ISC_R_FAILURE;
-               goto error;
-       }
-
-       /* Data from TLS to client */
-       char buf[1];
-       if (sock->tls.state == TLS_IO && sock->recv_cb != NULL &&
-           !atomic_load(&sock->readpaused))
-       {
-               (void)SSL_peek(sock->tls.ssl, buf, 1);
-               while ((pending = SSL_pending(sock->tls.ssl)) > 0) {
-                       if (pending > TLS_BUF_SIZE) {
-                               pending = TLS_BUF_SIZE;
-                       }
-                       isc_region_t region = {
-                               isc_mem_get(sock->mgr->mctx, pending), pending
-                       };
-                       isc_region_t dregion;
-                       memset(region.base, 0, region.length);
-                       rv = SSL_read(sock->tls.ssl, region.base,
-                                     region.length);
-                       /* Pending succeded, so should read */
-                       RUNTIME_CHECK(rv == pending);
-                       dregion = (isc_region_t){ region.base, rv };
-                       sock->recv_cb(sock->statichandle, ISC_R_SUCCESS,
-                                     &dregion, sock->recv_cbarg);
-                       isc_mem_put(sock->mgr->mctx, region.base,
-                                   region.length);
-               }
-       }
-
-       /* Peek to move the session forward */
-       (void)SSL_peek(sock->tls.ssl, buf, 1);
-
-       /* Data from TLS to network */
-       pending = BIO_pending(sock->tls.app_bio);
-       if (!sock->tls.sending && pending > 0) {
-               if (pending > TLS_BUF_SIZE) {
-                       pending = TLS_BUF_SIZE;
-               }
-               sock->tls.sending = true;
-               sock->tls.senddata.base = isc_mem_get(sock->mgr->mctx, pending);
-               sock->tls.senddata.length = pending;
-               rv = BIO_read(sock->tls.app_bio, sock->tls.senddata.base,
-                             pending);
-               /* There's something pending, read must succed */
-               RUNTIME_CHECK(rv == pending);
-               isc_nm_send(sock->outerhandle, &sock->tls.senddata,
-                           tls_senddone, sock);
-               /* We'll continue in tls_senddone */
-               return;
-       }
-
-       /* Get the potential error code */
-       rv = SSL_peek(sock->tls.ssl, buf, 1);
-
-       if (rv < 0) {
-               tls_err = SSL_get_error(sock->tls.ssl, rv);
-       }
-
-       /* Only after doing the IO we can check if SSL handshake is done */
-       if (sock->tls.state == TLS_HANDSHAKE &&
-           SSL_is_init_finished(sock->tls.ssl) == 1)
-       {
-               isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL);
-               if (sock->tls.server) {
-                       sock->listener->accept_cb(sock->statichandle,
-                                                 ISC_R_SUCCESS,
-                                                 sock->listener->accept_cbarg);
-               } else {
-                       sock->connect_cb(tlshandle, ISC_R_SUCCESS,
-                                        sock->connect_cbarg);
-               }
-               isc_nmhandle_detach(&tlshandle);
-               sock->tls.state = TLS_IO;
-               async_tls_do_bio(sock);
-               return;
-       }
-
-       switch (tls_err) {
-       case 0:
-               return;
-       case SSL_ERROR_WANT_WRITE:
-               if (!sock->tls.sending) {
-                       /*
-                        * Launch tls_do_bio asynchronously. If we're sending
-                        * already the send callback will call it.
-                        */
-                       async_tls_do_bio(sock);
-               } else {
-                       return;
-               }
-               break;
-       case SSL_ERROR_WANT_READ:
-               isc_nm_resumeread(sock->outerhandle);
-               break;
-       default:
-               result = tls_error_to_result(tls_err);
-               goto error;
-       }
-
-       while ((req = ISC_LIST_HEAD(sock->tls.sends)) != NULL) {
-               INSIST(VALID_UVREQ(req));
-               rv = SSL_write(sock->tls.ssl, req->uvbuf.base, req->uvbuf.len);
-               if (rv < 0) {
-                       if (!sock->tls.sending) {
-                               async_tls_do_bio(sock);
-                       }
-                       return;
-               }
-               if (rv != (int)req->uvbuf.len) {
-                       sock->tls.state = TLS_ERROR;
-                       async_tls_do_bio(sock);
-                       return;
-               }
-               ISC_LIST_UNLINK(sock->tls.sends, req, link);
-               req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
-               isc__nm_uvreq_put(&req, sock);
-       }
-
-       return;
-
-error:
-       isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
-                     ISC_LOG_ERROR, "SSL error in BIO: %d %s", tls_err,
-                     isc_result_totext(result));
-       if (ISC_LIST_HEAD(sock->tls.sends) != NULL) {
-               while ((req = ISC_LIST_HEAD(sock->tls.sends)) != NULL) {
-                       req->cb.send(sock->statichandle, result, req->cbarg);
-                       ISC_LIST_UNLINK(sock->tls.sends, req, link);
-                       isc__nm_uvreq_put(&req, sock);
-               }
-       } else if (sock->recv_cb != NULL) {
-               sock->recv_cb(sock->statichandle, result, NULL,
-                             sock->recv_cbarg);
-       } else {
-               tls_close_direct(sock);
-       }
-}
-
-static void
-tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
-          void *cbarg) {
-       isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
-       int rv;
-
-       REQUIRE(VALID_NMSOCK(tlssock));
-       REQUIRE(VALID_NMHANDLE(handle));
-       REQUIRE(tlssock->tid == isc_nm_tid());
-       if (result != ISC_R_SUCCESS) {
-               /* Connection closed */
-               /*
-                * TODO accept_cb should be called if we're not
-                * initialized yet!
-                */
-               if (tlssock->recv_cb != NULL) {
-                       tlssock->recv_cb(tlssock->statichandle, result, region,
-                                        tlssock->recv_cbarg);
-               }
-               isc__nm_tls_close(tlssock);
-               return;
-       }
-       rv = BIO_write(tlssock->tls.app_bio, region->base, region->length);
-
-       if (rv != (int)region->length) {
-               /* XXXWPK log it? */
-               tlssock->tls.state = TLS_ERROR;
-       }
-       tls_do_bio(tlssock);
-}
-
-static isc_result_t
-initialize_tls(isc_nmsocket_t *sock, bool server) {
-       REQUIRE(sock->tid == isc_nm_tid());
-
-       if (BIO_new_bio_pair(&(sock->tls.ssl_bio), TLS_BUF_SIZE,
-                            &(sock->tls.app_bio), TLS_BUF_SIZE) != 1)
-       {
-               SSL_free(sock->tls.ssl);
-               return (ISC_R_TLSERROR);
-       }
-
-       SSL_set_bio(sock->tls.ssl, sock->tls.ssl_bio, sock->tls.ssl_bio);
-       if (server) {
-               SSL_set_accept_state(sock->tls.ssl);
-       } else {
-               SSL_set_connect_state(sock->tls.ssl);
-       }
-       isc_nm_read(sock->outerhandle, tls_readcb, sock);
-       tls_do_bio(sock);
-       return (ISC_R_SUCCESS);
-}
-
-static isc_result_t
-tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
-       REQUIRE(VALID_NMSOCK(cbarg));
-       isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg;
-       isc_nmsocket_t *tlssock = NULL;
-       int r;
-
-       REQUIRE(tlslistensock->type == isc_nm_tlslistener);
-
-       /* If accept() was unsuccessful we can't do anything */
-       if (result != ISC_R_SUCCESS) {
-               return (result);
-       }
-
-       /*
-        * We need to create a 'wrapper' tlssocket for this connection.
-        */
-       tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock));
-       isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket,
-                          handle->sock->iface);
-
-       tlssock->extrahandlesize = tlslistensock->extrahandlesize;
-       isc__nmsocket_attach(tlslistensock, &tlssock->listener);
-       isc_nmhandle_attach(handle, &tlssock->outerhandle);
-       tlssock->peer = handle->sock->peer;
-       tlssock->read_timeout = atomic_load(&handle->sock->mgr->init);
-       tlssock->tid = isc_nm_tid();
-       tlssock->tls.server = true;
-       tlssock->tls.state = TLS_INIT;
-       tlssock->tls.ctx = tlslistensock->tls.ctx;
-       /* We need to initialize SSL now to reference SSL_CTX properly */
-       tlssock->tls.ssl = SSL_new(tlssock->tls.ctx);
-       ISC_LIST_INIT(tlssock->tls.sends);
-       if (tlssock->tls.ssl == NULL) {
-               atomic_store(&tlssock->closed, true);
-               isc__nmsocket_detach(&tlssock);
-               return (ISC_R_TLSERROR);
-       }
-
-       r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
-                         &tlssock->timer);
-       RUNTIME_CHECK(r == 0);
-
-       tlssock->timer.data = tlssock;
-       tlssock->timer_initialized = true;
-       tlssock->tls.ctx = tlslistensock->tls.ctx;
-
-       result = initialize_tls(tlssock, true);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
-       /* TODO: catch failure code, detach tlssock, and log the error */
-
-       return (result);
-}
-
-isc_result_t
-isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
-                isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
-                size_t extrahandlesize, int backlog, isc_quota_t *quota,
-                SSL_CTX *sslctx, isc_nmsocket_t **sockp) {
-       isc_result_t result;
-       isc_nmsocket_t *tlssock = isc_mem_get(mgr->mctx, sizeof(*tlssock));
-
-       isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface);
-       tlssock->accept_cb = accept_cb;
-       tlssock->accept_cbarg = accept_cbarg;
-       tlssock->extrahandlesize = extrahandlesize;
-       tlssock->tls.ctx = sslctx;
-       /* We need to initialize SSL now to reference SSL_CTX properly */
-       tlssock->tls.ssl = SSL_new(tlssock->tls.ctx);
-       if (tlssock->tls.ssl == NULL) {
-               atomic_store(&tlssock->closed, true);
-               isc__nmsocket_detach(&tlssock);
-               return (ISC_R_TLSERROR);
-       }
-
-       /*
-        * tlssock will be a TLS 'wrapper' around an unencrypted stream.
-        * We set tlssock->outer to a socket listening for a TCP connection.
-        */
-       result = isc_nm_listentcp(mgr, iface, tlslisten_acceptcb, tlssock,
-                                 extrahandlesize, backlog, quota,
-                                 &tlssock->outer);
-       if (result == ISC_R_SUCCESS) {
-               atomic_store(&tlssock->listening, true);
-               *sockp = tlssock;
-               return (ISC_R_SUCCESS);
-       } else {
-               atomic_store(&tlssock->closed, true);
-               isc__nmsocket_detach(&tlssock);
-               return (result);
-       }
-}
-
-void
-isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) {
-       int rv;
-       isc__netievent_tcpsend_t *ievent = (isc__netievent_tcpsend_t *)ev0;
-       isc_nmsocket_t *sock = ievent->sock;
-       isc__nm_uvreq_t *req = ievent->req;
-       ievent->req = NULL;
-       REQUIRE(VALID_UVREQ(req));
-       REQUIRE(sock->tid == isc_nm_tid());
-       UNUSED(worker);
-
-       if (inactive(sock)) {
-               req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg);
-               isc__nm_uvreq_put(&req, sock);
-               return;
-       }
-       if (!ISC_LIST_EMPTY(sock->tls.sends)) {
-               /* We're not the first */
-               ISC_LIST_APPEND(sock->tls.sends, req, link);
-               tls_do_bio(sock);
-               return;
-       }
-
-       rv = SSL_write(sock->tls.ssl, req->uvbuf.base, req->uvbuf.len);
-       if (rv < 0) {
-               /*
-                * We might need to read, we might need to write, or the
-                * TLS socket might be dead - in any case, we need to
-                * enqueue the uvreq and let the TLS BIO layer do the rest.
-                */
-               ISC_LIST_APPEND(sock->tls.sends, req, link);
-               tls_do_bio(sock);
-               return;
-       }
-       if (rv != (int)req->uvbuf.len) {
-               sock->tls.state = TLS_ERROR;
-               async_tls_do_bio(sock);
-               return;
-       }
-       req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
-       isc__nm_uvreq_put(&req, sock);
-       tls_do_bio(sock);
-       return;
-}
-
-void
-isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
-                void *cbarg) {
-       isc__netievent_tlssend_t *ievent = NULL;
-       isc__nm_uvreq_t *uvreq = NULL;
-       isc_nmsocket_t *sock = NULL;
-       REQUIRE(VALID_NMHANDLE(handle));
-       REQUIRE(VALID_NMSOCK(handle->sock));
-
-       sock = handle->sock;
-
-       REQUIRE(sock->type == isc_nm_tlssocket);
-
-       if (inactive(sock)) {
-               cb(handle, ISC_R_CANCELED, cbarg);
-               return;
-       }
-
-       uvreq = isc__nm_uvreq_get(sock->mgr, sock);
-       isc_nmhandle_attach(handle, &uvreq->handle);
-       uvreq->cb.send = cb;
-       uvreq->cbarg = cbarg;
-
-       uvreq->uvbuf.base = (char *)region->base;
-       uvreq->uvbuf.len = region->length;
-
-       /*
-        * We need to create an event and pass it using async channel
-        */
-       ievent = isc__nm_get_netievent_tlssend(sock->mgr, sock, uvreq);
-       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                              (isc__netievent_t *)ievent);
-}
-
-void
-isc__nm_async_tlsstartread(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc__netievent_tlsstartread_t *ievent =
-               (isc__netievent_tlsstartread_t *)ev0;
-       isc_nmsocket_t *sock = ievent->sock;
-
-       REQUIRE(sock->tid == isc_nm_tid());
-       UNUSED(worker);
-
-       tls_do_bio(sock);
-}
-
-void
-isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
-       REQUIRE(VALID_NMHANDLE(handle));
-       REQUIRE(VALID_NMSOCK(handle->sock));
-       REQUIRE(handle->sock->statichandle == handle);
-       REQUIRE(handle->sock->tid == isc_nm_tid());
-
-       isc__netievent_tlsstartread_t *ievent = NULL;
-       isc_nmsocket_t *sock = handle->sock;
-
-       if (inactive(sock)) {
-               cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
-               return;
-       }
-
-       sock->recv_cb = cb;
-       sock->recv_cbarg = cbarg;
-
-       ievent = isc__nm_get_netievent_tlsstartread(sock->mgr, sock);
-       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                              (isc__netievent_t *)ievent);
-}
-
-void
-isc__nm_tls_pauseread(isc_nmhandle_t *handle) {
-       REQUIRE(VALID_NMHANDLE(handle));
-       REQUIRE(VALID_NMSOCK(handle->sock));
-       isc_nmsocket_t *sock = handle->sock;
-
-       atomic_store(&sock->readpaused, true);
-}
-
-void
-isc__nm_tls_resumeread(isc_nmhandle_t *handle) {
-       REQUIRE(VALID_NMHANDLE(handle));
-       REQUIRE(VALID_NMSOCK(handle->sock));
-       isc_nmsocket_t *sock = handle->sock;
-
-       atomic_store(&sock->readpaused, false);
-       async_tls_do_bio(sock);
-}
-
-static void
-timer_close_cb(uv_handle_t *handle) {
-       isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
-       tls_close_direct(sock);
-}
-
-static void
-tls_close_direct(isc_nmsocket_t *sock) {
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->tid == isc_nm_tid());
-
-       if (sock->timer_running) {
-               uv_timer_stop(&sock->timer);
-               sock->timer_running = false;
-       }
-
-       /* We don't need atomics here, it's all in single network thread
-        */
-       if (sock->timer_initialized) {
-               /*
-                * We need to fire the timer callback to clean it up,
-                * it will then call us again (via detach) so that we
-                * can finally close the socket.
-                */
-               sock->timer_initialized = false;
-               uv_timer_stop(&sock->timer);
-               uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
-       } else {
-               /*
-                * At this point we're certain that there are no
-                * external references, we can close everything.
-                */
-               if (sock->outerhandle != NULL) {
-                       isc_nm_pauseread(sock->outerhandle);
-                       isc_nmhandle_detach(&sock->outerhandle);
-               }
-               if (sock->listener != NULL) {
-                       isc__nmsocket_detach(&sock->listener);
-               }
-               if (sock->tls.ssl != NULL) {
-                       SSL_free(sock->tls.ssl);
-                       sock->tls.ssl = NULL;
-                       /* These are destroyed when we free SSL* */
-                       sock->tls.ctx = NULL;
-                       sock->tls.ssl_bio = NULL;
-               }
-               if (sock->tls.app_bio != NULL) {
-                       BIO_free(sock->tls.app_bio);
-                       sock->tls.app_bio = NULL;
-               }
-               atomic_store(&sock->closed, true);
-               isc__nmsocket_detach(&sock);
-       }
-}
-
-void
-isc__nm_tls_close(isc_nmsocket_t *sock) {
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->type == isc_nm_tlssocket);
-
-       if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
-                                           true)) {
-               return;
-       }
-
-       if (sock->tid == isc_nm_tid()) {
-               tls_close_direct(sock);
-       } else {
-               isc__netievent_tlsclose_t *ievent =
-                       isc__nm_get_netievent_tlsclose(sock->mgr, sock);
-               isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                                      (isc__netievent_t *)ievent);
-       }
-}
-
-void
-isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc__netievent_tlsclose_t *ievent = (isc__netievent_tlsclose_t *)ev0;
-
-       REQUIRE(ievent->sock->tid == isc_nm_tid());
-       UNUSED(worker);
-
-       tls_close_direct(ievent->sock);
-}
-
-void
-isc__nm_tls_stoplistening(isc_nmsocket_t *sock) {
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->type == isc_nm_tlslistener);
-
-       atomic_store(&sock->listening, false);
-       atomic_store(&sock->closed, true);
-       sock->recv_cb = NULL;
-       sock->recv_cbarg = NULL;
-       if (sock->tls.ssl != NULL) {
-               SSL_free(sock->tls.ssl);
-               sock->tls.ssl = NULL;
-               sock->tls.ctx = NULL;
-       }
-
-       if (sock->outer != NULL) {
-               isc_nm_stoplistening(sock->outer);
-               isc__nmsocket_detach(&sock->outer);
-       }
-}
-
-isc_result_t
-isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
-                 isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx,
-                 unsigned int timeout, size_t extrahandlesize) {
-       isc_nmsocket_t *nsock = NULL;
-       isc__netievent_tlsconnect_t *ievent = NULL;
-       isc_result_t result = ISC_R_SUCCESS;
-
-       REQUIRE(VALID_NM(mgr));
-
-       nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
-       isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local);
-       nsock->extrahandlesize = extrahandlesize;
-       nsock->result = ISC_R_SUCCESS;
-       nsock->connect_cb = cb;
-       nsock->connect_cbarg = cbarg;
-       nsock->connect_timeout = timeout;
-       nsock->tls.ctx = ctx;
-       /* We need to initialize SSL now to reference SSL_CTX properly
-        */
-       nsock->tls.ssl = SSL_new(nsock->tls.ctx);
-       if (nsock->tls.ssl == NULL) {
-               atomic_store(&nsock->closed, true);
-               isc__nmsocket_detach(&nsock);
-               return (ISC_R_TLSERROR);
-       }
-
-       ievent = isc__nm_get_netievent_tlsconnect(mgr, nsock);
-       ievent->local = local->addr;
-       ievent->peer = peer->addr;
-       ievent->ctx = ctx;
-
-       if (isc__nm_in_netthread()) {
-               nsock->tid = isc_nm_tid();
-               isc__nm_async_tlsconnect(&mgr->workers[nsock->tid],
-                                        (isc__netievent_t *)ievent);
-               isc__nm_put_netievent_tlsconnect(mgr, ievent);
-       } else {
-               nsock->tid = isc_random_uniform(mgr->nworkers);
-               isc__nm_enqueue_ievent(&mgr->workers[nsock->tid],
-                                      (isc__netievent_t *)ievent);
-       }
-
-       return (result);
-}
-
-static void
-tls_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
-       isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
-
-       REQUIRE(VALID_NMSOCK(tlssock));
-
-       if (result != ISC_R_SUCCESS) {
-               tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
-               LOCK(&tlssock->parent->lock);
-               tlssock->parent->result = result;
-               UNLOCK(&tlssock->parent->lock);
-               tls_close_direct(tlssock);
-               return;
-       }
-
-       INSIST(VALID_NMHANDLE(handle));
-
-       tlssock->peer = isc_nmhandle_peeraddr(handle);
-       isc_nmhandle_attach(handle, &tlssock->outerhandle);
-       result = initialize_tls(tlssock, false);
-       if (result != ISC_R_SUCCESS) {
-               tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
-               LOCK(&tlssock->parent->lock);
-               tlssock->parent->result = result;
-               UNLOCK(&tlssock->parent->lock);
-               tls_close_direct(tlssock);
-               return;
-       }
-}
-void
-isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc__netievent_tlsconnect_t *ievent =
-               (isc__netievent_tlsconnect_t *)ev0;
-       isc_nmsocket_t *tlssock = ievent->sock;
-       isc_result_t result;
-       int r;
-
-       UNUSED(worker);
-
-       tlssock->tid = isc_nm_tid();
-       r = uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
-                         &tlssock->timer);
-       RUNTIME_CHECK(r == 0);
-
-       tlssock->timer.data = tlssock;
-       tlssock->timer_initialized = true;
-       tlssock->tls.state = TLS_INIT;
-
-       result = isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local,
-                                  (isc_nmiface_t *)&ievent->peer,
-                                  tls_connect_cb, tlssock,
-                                  tlssock->connect_timeout, 0);
-       if (result != ISC_R_SUCCESS) {
-               /* FIXME: We need to pass valid handle */
-               tlssock->connect_cb(NULL, result, tlssock->connect_cbarg);
-               LOCK(&tlssock->parent->lock);
-               tlssock->parent->result = result;
-               UNLOCK(&tlssock->parent->lock);
-               tls_close_direct(tlssock);
-               return;
-       }
-}
-
-void
-isc__nm_async_tlsdobio(isc__networker_t *worker, isc__netievent_t *ev0) {
-       UNUSED(worker);
-       isc__netievent_tlsdobio_t *ievent = (isc__netievent_tlsdobio_t *)ev0;
-       tls_do_bio(ievent->sock);
-}
-
-isc_result_t
-isc_nm_tls_create_server_ctx(const char *keyfile, const char *certfile,
-                            SSL_CTX **ctxp) {
-       INSIST(ctxp != NULL);
-       INSIST(*ctxp == NULL);
-       int rv;
-       unsigned long err;
-       bool ephemeral = (keyfile == NULL && certfile == NULL);
-       X509 *cert = NULL;
-       EVP_PKEY *pkey = NULL;
-       BIGNUM *bn = NULL;
-       RSA *rsa = NULL;
-
-       if (ephemeral) {
-               INSIST(keyfile == NULL);
-               INSIST(certfile == NULL);
-       } else {
-               INSIST(keyfile != NULL);
-               INSIST(certfile != NULL);
-       }
-
-#ifdef HAVE_TLS_SERVER_METHOD
-       const SSL_METHOD *method = TLS_server_method();
-#else
-       const SSL_METHOD *method = SSLv23_server_method();
-#endif
-
-       SSL_CTX *ctx = SSL_CTX_new(method);
-       RUNTIME_CHECK(ctx != NULL);
-       if (ephemeral) {
-               rsa = RSA_new();
-               if (rsa == NULL) {
-                       goto ssl_error;
-               }
-               bn = BN_new();
-               if (bn == NULL) {
-                       goto ssl_error;
-               }
-               BN_set_word(bn, RSA_F4);
-               rv = RSA_generate_key_ex(rsa, 4096, bn, NULL);
-               if (rv != 1) {
-                       goto ssl_error;
-               }
-               cert = X509_new();
-               if (cert == NULL) {
-                       goto ssl_error;
-               }
-               pkey = EVP_PKEY_new();
-               if (pkey == NULL) {
-                       goto ssl_error;
-               }
-
-               /*
-                * EVP_PKEY_assign_*() set the referenced key to key
-                * however these use the supplied key internally and so
-                * key will be freed when the parent pkey is freed.
-                */
-               EVP_PKEY_assign(pkey, EVP_PKEY_RSA, rsa);
-               rsa = NULL;
-               ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
-
-               X509_gmtime_adj(X509_get_notBefore(cert), 0);
-               /*
-                * We set the vailidy for 10 years.
-                */
-               X509_gmtime_adj(X509_get_notAfter(cert), 3650 * 24 * 3600);
-
-               X509_set_pubkey(cert, pkey);
-
-               X509_NAME *name = X509_get_subject_name(cert);
-
-               X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
-                                          (const unsigned char *)"AQ", -1, -1,
-                                          0);
-               X509_NAME_add_entry_by_txt(
-                       name, "O", MBSTRING_ASC,
-                       (const unsigned char *)"BIND9 ephemeral "
-                                              "certificate",
-                       -1, -1, 0);
-               X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
-                                          (const unsigned char *)"bind9.local",
-                                          -1, -1, 0);
-
-               X509_set_issuer_name(cert, name);
-               X509_sign(cert, pkey, EVP_sha256());
-               rv = SSL_CTX_use_certificate(ctx, cert);
-               if (rv != 1) {
-                       goto ssl_error;
-               }
-               rv = SSL_CTX_use_PrivateKey(ctx, pkey);
-               if (rv != 1) {
-                       goto ssl_error;
-               }
-
-               X509_free(cert);
-               EVP_PKEY_free(pkey);
-               BN_free(bn);
-       } else {
-               rv = SSL_CTX_use_certificate_file(ctx, certfile,
-                                                 SSL_FILETYPE_PEM);
-               if (rv != 1) {
-                       goto ssl_error;
-               }
-               rv = SSL_CTX_use_PrivateKey_file(ctx, keyfile,
-                                                SSL_FILETYPE_PEM);
-               if (rv != 1) {
-                       goto ssl_error;
-               }
-       }
-       *ctxp = ctx;
-       return (ISC_R_SUCCESS);
-
-ssl_error:
-       err = ERR_get_error();
-       char errbuf[256];
-       ERR_error_string_n(err, errbuf, 256);
-       isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
-                     ISC_LOG_ERROR, "Error initializing TLS context: %s",
-                     errbuf);
-       if (ctx != NULL) {
-               SSL_CTX_free(ctx);
-       }
-       if (cert != NULL) {
-               X509_free(cert);
-       }
-       if (pkey != NULL) {
-               EVP_PKEY_free(pkey);
-       }
-       if (bn != NULL) {
-               BN_free(bn);
-       }
-       if (rsa != NULL) {
-               RSA_free(rsa);
-       }
-
-       return (ISC_R_TLSERROR);
-}
-
-void
-isc__nm_tls_initialize() {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-       SSL_library_init();
-#else
-       OPENSSL_init_ssl(0, NULL);
-#endif
-}
index 7359e0f452e01d03423da8d551056e817bce755b..d518dc97eccebe1a444aea2aa5efde48fe7435e0 100644 (file)
@@ -9,26 +9,34 @@
  * information regarding copyright ownership.
  */
 
+#include <libgen.h>
 #include <unistd.h>
 #include <uv.h>
 
 #include <isc/atomic.h>
 #include <isc/buffer.h>
 #include <isc/condition.h>
+#include <isc/errno.h>
+#include <isc/log.h>
 #include <isc/magic.h>
 #include <isc/mem.h>
 #include <isc/netmgr.h>
+#include <isc/quota.h>
 #include <isc/random.h>
 #include <isc/refcount.h>
 #include <isc/region.h>
 #include <isc/result.h>
 #include <isc/sockaddr.h>
+#include <isc/stdtime.h>
 #include <isc/thread.h>
 #include <isc/util.h>
 
 #include "netmgr-int.h"
+#include "openssl_shim.h"
 #include "uv-compat.h"
 
+#define TLS_BUF_SIZE 65536
+
 #define TLSDNS_CLIENTS_PER_CONN 23
 /*%<
  *
  * changed in the future.
  */
 
+static atomic_uint_fast32_t last_tlsdnsquota_log = ATOMIC_VAR_INIT(0);
+
+static void
+tls_error(isc_nmsocket_t *sock, isc_result_t result);
+
 static void
-dnslisten_readcb(isc_nmhandle_t *handle, isc_result_t eresult,
-                isc_region_t *region, void *arg);
+tlsdns_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf);
 
 static void
 resume_processing(void *arg);
 
+static isc_result_t
+tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+
 static void
 tlsdns_close_direct(isc_nmsocket_t *sock);
 
-static inline size_t
-dnslen(unsigned char *base) {
-       return ((base[0] << 8) + (base[1]));
-}
+static isc_result_t
+tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req);
+static void
+tlsdns_connect_cb(uv_connect_t *uvreq, int status);
 
-/*
- * COMPAT CODE
- */
+static void
+tlsdns_connection_cb(uv_stream_t *server, int status);
+
+static void
+read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
+
+static void
+tlsdns_close_cb(uv_handle_t *uvhandle);
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota);
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0);
+
+static void
+failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult);
+
+static void
+failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+              isc_result_t eresult);
+
+static void
+stop_tlsdns_parent(isc_nmsocket_t *sock);
+static void
+stop_tlsdns_child(isc_nmsocket_t *sock);
+
+static void
+start_reading(isc_nmsocket_t *sock);
+
+static void
+stop_reading(isc_nmsocket_t *sock);
+
+static void
+start_sock_timer(isc_nmsocket_t *sock);
+
+static void
+process_sock_buffer(isc_nmsocket_t *sock);
+
+static isc__nm_uvreq_t *
+get_read_req(isc_nmsocket_t *sock);
+
+static void
+async_tlsdns_cycle(isc_nmsocket_t *sock) __attribute__((unused));
+
+static isc_result_t
+tls_cycle(isc_nmsocket_t *sock);
 
-static void *
-isc__nm_get_ievent(isc_nm_t *mgr, isc__netievent_type type) {
-       isc__netievent_storage_t *event = isc_mempool_get(mgr->evpool);
+static bool
+can_log_tlsdns_quota(void) {
+       isc_stdtime_t now, last;
+
+       isc_stdtime_get(&now);
+       last = atomic_exchange_relaxed(&last_tlsdnsquota_log, now);
+       if (now != last) {
+               return (true);
+       }
 
-       *event = (isc__netievent_storage_t){ .ni.type = type };
-       return (event);
+       return (false);
 }
 
-/*
- * Regular TCP buffer, should suffice in most cases.
- */
-#define NM_REG_BUF 4096
-/*
- * Two full DNS packets with lengths.
- * netmgr receives 64k at most so there's no risk
- * of overrun.
- */
-#define NM_BIG_BUF (65535 + 2) * 2
 static inline void
 alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) {
        REQUIRE(len <= NM_BIG_BUF);
@@ -91,609 +145,1871 @@ alloc_dnsbuf(isc_nmsocket_t *sock, size_t len) {
        }
 }
 
+static bool
+inactive(isc_nmsocket_t *sock) {
+       return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
+               atomic_load(&sock->mgr->closing) ||
+               (sock->server != NULL && !isc__nmsocket_active(sock->server)));
+}
+
 static void
-timer_close_cb(uv_handle_t *handle) {
-       isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
+failed_accept_cb(isc_nmsocket_t *sock, isc_result_t eresult) {
+       REQUIRE(sock->accepting);
+       REQUIRE(sock->server);
 
-       REQUIRE(VALID_NMSOCK(sock));
+       /*
+        * Detach the quota early to make room for other connections;
+        * otherwise it'd be detached later asynchronously, and clog
+        * the quota unnecessarily.
+        */
+       if (sock->quota != NULL) {
+               isc_quota_detach(&sock->quota);
+       }
 
-       atomic_store(&sock->closed, true);
-       tlsdns_close_direct(sock);
+       isc__nmsocket_detach(&sock->server);
+
+       sock->accepting = false;
+
+       switch (eresult) {
+       case ISC_R_NOTCONNECTED:
+               /* IGNORE: The client disconnected before we could accept */
+               break;
+       default:
+               isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+                             ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+                             "Accepting TCP connection failed: %s",
+                             isc_result_totext(eresult));
+       }
 }
 
 static void
-dnstcp_readtimeout(uv_timer_t *timer) {
-       isc_nmsocket_t *sock =
-               (isc_nmsocket_t *)uv_handle_get_data((uv_handle_t *)timer);
-
+failed_connect_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+                 isc_result_t eresult) {
        REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(VALID_UVREQ(req));
        REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(atomic_load(&sock->connecting));
+       REQUIRE(req->cb.connect != NULL);
 
-       /* Close the TCP connection; its closure should fire ours. */
-       if (sock->outerhandle != NULL) {
-               isc_nmhandle_detach(&sock->outerhandle);
-       }
+       atomic_store(&sock->connecting, false);
+
+       isc__nmsocket_clearcb(sock);
+
+       isc__nm_connectcb(sock, req, eresult);
+
+       isc__nmsocket_prep_destroy(sock);
 }
 
-/*
- * Accept callback for TCP-DNS connection.
- */
 static isc_result_t
-dnslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
-       isc_nmsocket_t *dnslistensock = (isc_nmsocket_t *)cbarg;
-       isc_nmsocket_t *dnssock = NULL;
-       isc_nmhandle_t *readhandle = NULL;
-       isc_nm_accept_cb_t accept_cb;
-       void *accept_cbarg;
+tlsdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+       isc__networker_t *worker = NULL;
+       isc_result_t result = ISC_R_DEFAULT;
+       int r;
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(VALID_UVREQ(req));
 
-       REQUIRE(VALID_NMSOCK(dnslistensock));
-       REQUIRE(dnslistensock->type == isc_nm_tlsdnslistener);
+       REQUIRE(isc__nm_in_netthread());
+       REQUIRE(sock->tid == isc_nm_tid());
 
-       if (result != ISC_R_SUCCESS) {
-               return (result);
-       }
+       worker = &sock->mgr->workers[sock->tid];
 
-       accept_cb = dnslistensock->accept_cb;
-       accept_cbarg = dnslistensock->accept_cbarg;
+       atomic_store(&sock->connecting, true);
 
-       if (accept_cb != NULL) {
-               result = accept_cb(handle, ISC_R_SUCCESS, accept_cbarg);
-               if (result != ISC_R_SUCCESS) {
-                       return (result);
+       r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+       RUNTIME_CHECK(r == 0);
+       uv_handle_set_data(&sock->uv_handle.handle, sock);
+
+       r = uv_timer_init(&worker->loop, &sock->timer);
+       RUNTIME_CHECK(r == 0);
+       uv_handle_set_data((uv_handle_t *)&sock->timer, sock);
+
+       r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+       if (r != 0) {
+               isc__nm_closesocket(sock->fd);
+               isc__nm_incstats(sock->mgr, sock->statsindex[STATID_OPENFAIL]);
+               goto done;
+       }
+       isc__nm_incstats(sock->mgr, sock->statsindex[STATID_OPEN]);
+
+       if (req->local.length != 0) {
+               r = uv_tcp_bind(&sock->uv_handle.tcp, &req->local.type.sa, 0);
+               /*
+                * In case of shared socket UV_EINVAL will be returned and needs
+                * to be ignored
+                */
+               if (r != 0 && r != UV_EINVAL) {
+                       isc__nm_incstats(sock->mgr,
+                                        sock->statsindex[STATID_BINDFAIL]);
+                       goto done;
                }
        }
 
-       /* We need to create a 'wrapper' dnssocket for this connection */
-       dnssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*dnssock));
-       isc__nmsocket_init(dnssock, handle->sock->mgr, isc_nm_tlsdnssocket,
-                          handle->sock->iface);
+       uv_handle_set_data(&req->uv_req.handle, req);
+       r = uv_tcp_connect(&req->uv_req.connect, &sock->uv_handle.tcp,
+                          &req->peer.type.sa, tlsdns_connect_cb);
+       if (r != 0) {
+               isc__nm_incstats(sock->mgr,
+                                sock->statsindex[STATID_CONNECTFAIL]);
+               goto done;
+       }
+       isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CONNECT]);
+
+       atomic_store(&sock->connected, true);
 
-       dnssock->extrahandlesize = dnslistensock->extrahandlesize;
-       isc__nmsocket_attach(dnslistensock, &dnssock->listener);
+done:
+       result = isc__nm_uverr2result(r);
 
-       isc__nmsocket_attach(dnssock, &dnssock->self);
+       LOCK(&sock->lock);
+       sock->result = result;
+       SIGNAL(&sock->cond);
+       if (!atomic_load(&sock->active)) {
+               WAIT(&sock->scond, &sock->lock);
+       }
+       INSIST(atomic_load(&sock->active));
+       UNLOCK(&sock->lock);
+
+       return (result);
+}
+
+void
+isc__nm_async_tlsdnsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnsconnect_t *ievent =
+               (isc__netievent_tlsdnsconnect_t *)ev0;
+       isc_nmsocket_t *sock = ievent->sock;
+       isc__nm_uvreq_t *req = ievent->req;
+       isc_result_t result = ISC_R_SUCCESS;
 
-       isc_nmhandle_attach(handle, &dnssock->outerhandle);
+       UNUSED(worker);
 
-       dnssock->peer = handle->sock->peer;
-       dnssock->read_timeout = atomic_load(&handle->sock->mgr->init);
-       dnssock->tid = isc_nm_tid();
-       dnssock->closehandle_cb = resume_processing;
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(sock->iface != NULL);
+       REQUIRE(sock->parent == NULL);
+       REQUIRE(sock->tid == isc_nm_tid());
 
-       uv_timer_init(&dnssock->mgr->workers[isc_nm_tid()].loop,
-                     &dnssock->timer);
-       dnssock->timer.data = dnssock;
-       dnssock->timer_initialized = true;
-       uv_timer_start(&dnssock->timer, dnstcp_readtimeout,
-                      dnssock->read_timeout, 0);
-       dnssock->timer_running = true;
+       result = tlsdns_connect_direct(sock, req);
+       if (result != ISC_R_SUCCESS) {
+               atomic_store(&sock->active, false);
+               isc__nm_tlsdns_close(sock);
+               isc__nm_uvreq_put(&req, sock);
+       }
 
        /*
-        * Add a reference to handle to keep it from being freed by
-        * the caller. It will be detached in dnslisted_readcb() when
-        * the connection is closed or there is no more data to be read.
+        * The sock is now attached to the handle.
         */
-       isc_nmhandle_attach(handle, &readhandle);
-       isc_nm_read(readhandle, dnslisten_readcb, dnssock);
-       isc__nmsocket_detach(&dnssock);
-
-       return (ISC_R_SUCCESS);
+       isc__nmsocket_detach(&sock);
 }
 
-/*
- * Process a single packet from the incoming buffer.
- *
- * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something
- * was processed; return ISC_R_NOMORE if there isn't a full message
- * to be processed.
- *
- * The caller will need to unreference the handle.
- */
-static isc_result_t
-processbuffer(isc_nmsocket_t *dnssock, isc_nmhandle_t **handlep) {
-       size_t len;
+static void
+tlsdns_connect_cb(uv_connect_t *uvreq, int status) {
+       isc_result_t result;
+       isc__nm_uvreq_t *req = NULL;
+       isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)uvreq->handle);
+       struct sockaddr_storage ss;
+       int r;
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(atomic_load(&sock->connecting));
+
+       req = uv_handle_get_data((uv_handle_t *)uvreq);
+
+       REQUIRE(VALID_UVREQ(req));
+       REQUIRE(VALID_NMHANDLE(req->handle));
+
+       /* Socket was closed midflight by isc__nm_tlsdns_shutdown() */
+       if (!isc__nmsocket_active(sock)) {
+               result = ISC_R_CANCELED;
+               goto error;
+       }
+
+       if (status != 0) {
+               result = isc__nm_uverr2result(status);
+               goto error;
+       }
+
+       isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CONNECT]);
+       r = uv_tcp_getpeername(&sock->uv_handle.tcp, (struct sockaddr *)&ss,
+                              &(int){ sizeof(ss) });
+       if (r != 0) {
+               result = isc__nm_uverr2result(r);
+               goto error;
+       }
 
-       REQUIRE(VALID_NMSOCK(dnssock));
-       REQUIRE(dnssock->tid == isc_nm_tid());
-       REQUIRE(handlep != NULL && *handlep == NULL);
+       sock->tls.state = TLS_STATE_NONE;
+       sock->tls.ssl = SSL_new(sock->tls.ctx);
+       RUNTIME_CHECK(sock->tls.ssl != NULL);
 
        /*
-        * If we don't even have the length yet, we can't do
-        * anything.
+        *
         */
-       if (dnssock->buf_len < 2) {
-               return (ISC_R_NOMORE);
-       }
+       r = BIO_new_bio_pair(&sock->tls.ssl_wbio, TLS_BUF_SIZE,
+                            &sock->tls.app_rbio, TLS_BUF_SIZE);
+       RUNTIME_CHECK(r == 1);
+
+       r = BIO_new_bio_pair(&sock->tls.ssl_rbio, TLS_BUF_SIZE,
+                            &sock->tls.app_wbio, TLS_BUF_SIZE);
+       RUNTIME_CHECK(r == 1);
 
+#if HAVE_SSL_SET0_RBIO && HAVE_SSL_SET0_WBIO
        /*
-        * Process the first packet from the buffer, leaving
-        * the rest (if any) for later.
+        * Note that if the rbio and wbio are the same then SSL_set0_rbio() and
+        * SSL_set0_wbio() each take ownership of one reference. Therefore it
+        * may be necessary to increment the number of references available
+        * using BIO_up_ref(3) before calling the set0 functions.
         */
-       len = dnslen(dnssock->buf);
-       if (len <= dnssock->buf_len - 2) {
-               isc_nmhandle_t *dnshandle = NULL;
-               isc_nmsocket_t *listener = NULL;
-               isc_nm_recv_cb_t cb = NULL;
-               void *cbarg = NULL;
-
-               if (atomic_load(&dnssock->client) &&
-                   dnssock->statichandle != NULL) {
-                       isc_nmhandle_attach(dnssock->statichandle, &dnshandle);
-               } else {
-                       dnshandle = isc__nmhandle_get(dnssock, NULL, NULL);
-               }
+       SSL_set0_rbio(sock->tls.ssl, sock->tls.ssl_rbio);
+       SSL_set0_wbio(sock->tls.ssl, sock->tls.ssl_wbio);
+#else
+       SSL_set_bio(sock->tls.ssl, sock->tls.ssl_rbio, sock->tls.ssl_wbio);
+#endif
 
-               listener = dnssock->listener;
-               if (listener != NULL) {
-                       cb = listener->recv_cb;
-                       cbarg = listener->recv_cbarg;
-               } else if (dnssock->recv_cb != NULL) {
-                       cb = dnssock->recv_cb;
-                       cbarg = dnssock->recv_cbarg;
-                       /*
-                        * We need to clear the read callback *before*
-                        * calling it, because it might make another
-                        * call to isc_nm_read() and set up a new callback.
-                        */
-                       isc__nmsocket_clearcb(dnssock);
-               }
+       SSL_set_connect_state(sock->tls.ssl);
 
-               if (cb != NULL) {
-                       cb(dnshandle, ISC_R_SUCCESS,
-                          &(isc_region_t){ .base = dnssock->buf + 2,
-                                           .length = len },
-                          cbarg);
-               }
+       result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
-               len += 2;
-               dnssock->buf_len -= len;
-               if (len > 0) {
-                       memmove(dnssock->buf, dnssock->buf + len,
-                               dnssock->buf_len);
-               }
+       sock->tls.pending_req = req;
 
-               *handlep = dnshandle;
-               return (ISC_R_SUCCESS);
+       start_reading(sock);
+       result = tls_cycle(sock);
+       if (result != ISC_R_SUCCESS) {
+               goto error;
        }
 
-       return (ISC_R_NOMORE);
+       return;
+
+error:
+       failed_connect_cb(sock, req, result);
 }
 
-/*
- * We've got a read on our underlying socket. Check whether
- * we have a complete DNS packet and, if so, call the callback.
- */
-static void
-dnslisten_readcb(isc_nmhandle_t *handle, isc_result_t eresult,
-                isc_region_t *region, void *arg) {
-       isc_nmsocket_t *dnssock = (isc_nmsocket_t *)arg;
-       unsigned char *base = NULL;
-       bool done = false;
-       size_t len;
+isc_result_t
+isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
+                    isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+                    size_t extrahandlesize, isc_tlsctx_t *sslctx) {
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *sock = NULL;
+       isc__netievent_tlsdnsconnect_t *ievent = NULL;
+       isc__nm_uvreq_t *req = NULL;
+       sa_family_t sa_family;
+       uv_os_sock_t fd;
 
-       REQUIRE(VALID_NMSOCK(dnssock));
-       REQUIRE(dnssock->tid == isc_nm_tid());
-       REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NM(mgr));
+       REQUIRE(local != NULL);
+       REQUIRE(peer != NULL);
 
-       if (!isc__nmsocket_active(dnssock) || atomic_load(&dnssock->closing) ||
-           dnssock->outerhandle == NULL ||
-           (dnssock->listener != NULL &&
-            !isc__nmsocket_active(dnssock->listener)) ||
-           atomic_load(&dnssock->mgr->closing))
-       {
-               if (eresult == ISC_R_SUCCESS) {
-                       eresult = ISC_R_CANCELED;
-               }
+       sa_family = peer->addr.type.sa.sa_family;
+
+       /*
+        * The socket() call can fail spuriously on FreeBSD 12, so we need to
+        * handle the failure early and gracefully.
+        */
+       result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &fd);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
        }
 
-       if (region == NULL || eresult != ISC_R_SUCCESS) {
-               isc_nm_recv_cb_t cb = dnssock->recv_cb;
-               void *cbarg = dnssock->recv_cbarg;
+       sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+       isc__nmsocket_init(sock, mgr, isc_nm_tlsdnssocket, local);
 
-               /* Connection closed */
-               dnssock->result = eresult;
-               isc__nmsocket_clearcb(dnssock);
-               if (atomic_load(&dnssock->client) && cb != NULL) {
-                       cb(dnssock->statichandle, eresult, NULL, cbarg);
-               }
+       sock->extrahandlesize = extrahandlesize;
+       sock->connect_timeout = timeout;
+       sock->result = ISC_R_DEFAULT;
+       sock->fd = fd;
+       sock->tls.ctx = sslctx;
 
-               if (dnssock->self != NULL) {
-                       isc__nmsocket_detach(&dnssock->self);
-               }
-               if (dnssock->outerhandle != NULL) {
-                       isc__nmsocket_clearcb(dnssock->outerhandle->sock);
-                       isc_nmhandle_detach(&dnssock->outerhandle);
-               }
-               if (dnssock->listener != NULL) {
-                       isc__nmsocket_detach(&dnssock->listener);
-               }
+       atomic_init(&sock->client, true);
 
-               /*
-                * Server connections will hold two handle references when
-                * shut down, but client (tlsdnsconnect) connections have
-                * only one.
-                */
-               if (!atomic_load(&dnssock->client)) {
-                       isc_nmhandle_detach(&handle);
-               }
-               return;
-       }
+       result = isc__nm_socket_connectiontimeout(fd, timeout);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+       req = isc__nm_uvreq_get(mgr, sock);
+       req->cb.connect = cb;
+       req->cbarg = cbarg;
+       req->peer = peer->addr;
+       req->local = local->addr;
+       req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface->addr);
 
-       base = region->base;
-       len = region->length;
+       ievent = isc__nm_get_netievent_tlsdnsconnect(mgr, sock, req);
 
-       if (dnssock->buf_len + len > dnssock->buf_size) {
-               alloc_dnsbuf(dnssock, dnssock->buf_len + len);
+       if (isc__nm_in_netthread()) {
+               atomic_store(&sock->active, true);
+               sock->tid = isc_nm_tid();
+               isc__nm_async_tlsdnsconnect(&mgr->workers[sock->tid],
+                                           (isc__netievent_t *)ievent);
+               isc__nm_put_netievent_tlsdnsconnect(mgr, ievent);
+       } else {
+               atomic_init(&sock->active, false);
+               sock->tid = isc_random_uniform(mgr->nworkers);
+               isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
+                                      (isc__netievent_t *)ievent);
        }
-       memmove(dnssock->buf + dnssock->buf_len, base, len);
-       dnssock->buf_len += len;
+       LOCK(&sock->lock);
+       result = sock->result;
+       while (result == ISC_R_DEFAULT) {
+               WAIT(&sock->cond, &sock->lock);
+               result = sock->result;
+       }
+       atomic_store(&sock->active, true);
+       BROADCAST(&sock->scond);
+       UNLOCK(&sock->lock);
+       INSIST(result != ISC_R_DEFAULT);
 
-       dnssock->read_timeout = (atomic_load(&dnssock->keepalive)
-                                        ? atomic_load(&dnssock->mgr->keepalive)
-                                        : atomic_load(&dnssock->mgr->idle));
+       return (result);
+}
 
-       do {
-               isc_result_t result;
-               isc_nmhandle_t *dnshandle = NULL;
+static uv_os_sock_t
+isc__nm_tlsdns_lb_socket(sa_family_t sa_family) {
+       isc_result_t result;
+       uv_os_sock_t sock;
 
-               result = processbuffer(dnssock, &dnshandle);
-               if (result != ISC_R_SUCCESS) {
-                       /*
-                        * There wasn't anything in the buffer to process.
-                        */
-                       return;
-               }
+       result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
-               /*
-                * We have a packet: stop timeout timers
-                */
-               if (dnssock->timer_initialized) {
-                       uv_timer_stop(&dnssock->timer);
-               }
+       (void)isc__nm_socket_incoming_cpu(sock);
 
-               if (atomic_load(&dnssock->sequential) ||
-                   dnssock->recv_cb == NULL) {
-                       /*
-                        * There are two reasons we might want to pause here:
-                        * - We're in sequential mode and we've received
-                        *   a whole packet, so we're done until it's been
-                        *   processed; or
-                        * - We no longer have a read callback.
-                        */
-                       isc_nm_pauseread(dnssock->outerhandle);
-                       done = true;
-               } else {
-                       /*
-                        * We're pipelining, so we now resume processing
-                        * packets until the clients-per-connection limit
-                        * is reached (as determined by the number of
-                        * active handles on the socket). When the limit
-                        * is reached, pause reading.
-                        */
-                       if (atomic_load(&dnssock->ah) >=
-                           TLSDNS_CLIENTS_PER_CONN) {
-                               isc_nm_pauseread(dnssock->outerhandle);
-                               done = true;
-                       }
-               }
+       /* FIXME: set mss */
+
+       result = isc__nm_socket_reuse(sock);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
 
-               isc_nmhandle_detach(&dnshandle);
-       } while (!done);
+#if HAVE_SO_REUSEPORT_LB
+       result = isc__nm_socket_reuse_lb(sock);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+#endif
+
+       return (sock);
 }
 
-/*
- * isc_nm_listentlsdns works exactly as listentlsdns but on an SSL socket.
- */
 isc_result_t
-isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
-                   void *cbarg, isc_nm_accept_cb_t accept_cb,
-                   void *accept_cbarg, size_t extrahandlesize, int backlog,
-                   isc_quota_t *quota, SSL_CTX *sslctx,
-                   isc_nmsocket_t **sockp) {
-       isc_nmsocket_t *dnslistensock = isc_mem_get(mgr->mctx,
-                                                   sizeof(*dnslistensock));
-       isc_result_t result;
+isc_nm_listentlsdns(isc_nm_t *mgr, isc_nmiface_t *iface,
+                   isc_nm_recv_cb_t recv_cb, void *recv_cbarg,
+                   isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+                   size_t extrahandlesize, int backlog, isc_quota_t *quota,
+                   isc_tlsctx_t *sslctx, isc_nmsocket_t **sockp) {
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *sock = NULL;
+       sa_family_t sa_family = iface->addr.type.sa.sa_family;
+       size_t children_size = 0;
+#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
+       uv_os_sock_t fd = -1;
+#endif
 
        REQUIRE(VALID_NM(mgr));
-       REQUIRE(sslctx != NULL);
-
-       isc__nmsocket_init(dnslistensock, mgr, isc_nm_tlsdnslistener, iface);
-       dnslistensock->recv_cb = cb;
-       dnslistensock->recv_cbarg = cbarg;
-       dnslistensock->accept_cb = accept_cb;
-       dnslistensock->accept_cbarg = accept_cbarg;
-       dnslistensock->extrahandlesize = extrahandlesize;
-
-       result = isc_nm_listentls(mgr, iface, dnslisten_acceptcb, dnslistensock,
-                                 extrahandlesize, backlog, quota, sslctx,
-                                 &dnslistensock->outer);
+
+       sock = isc_mem_get(mgr->mctx, sizeof(*sock));
+       isc__nmsocket_init(sock, mgr, isc_nm_tlsdnslistener, iface);
+
+       sock->rchildren = 0;
+#if defined(WIN32)
+       sock->nchildren = 1;
+#else
+       sock->nchildren = mgr->nworkers;
+#endif
+       children_size = sock->nchildren * sizeof(sock->children[0]);
+       sock->children = isc_mem_get(mgr->mctx, children_size);
+       memset(sock->children, 0, children_size);
+
+       sock->result = ISC_R_DEFAULT;
+       sock->tid = isc_random_uniform(sock->nchildren);
+       sock->fd = -1;
+       sock->tls.ctx = sslctx;
+
+#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
+       fd = isc__nm_tlsdns_lb_socket(sa_family);
+#endif
+
+       for (size_t i = 0; i < sock->nchildren; i++) {
+               isc__netievent_tlsdnslisten_t *ievent = NULL;
+               isc_nmsocket_t *csock = &sock->children[i];
+
+               isc__nmsocket_init(csock, mgr, isc_nm_tlsdnssocket, iface);
+               csock->parent = sock;
+               csock->accept_cb = accept_cb;
+               csock->accept_cbarg = accept_cbarg;
+               csock->recv_cb = recv_cb;
+               csock->recv_cbarg = recv_cbarg;
+               csock->extrahandlesize = extrahandlesize;
+               csock->backlog = backlog;
+               csock->tid = i;
+               csock->tls.ctx = sslctx;
+
+               /*
+                * We don't attach to quota, just assign - to avoid
+                * increasing quota unnecessarily.
+                */
+               csock->pquota = quota;
+               isc_quota_cb_init(&csock->quotacb, quota_accept_cb, csock);
+
+#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
+               csock->fd = isc__nm_tlsdns_lb_socket(sa_family);
+#else
+               csock->fd = dup(fd);
+#endif
+               REQUIRE(csock->fd >= 0);
+
+               ievent = isc__nm_get_netievent_tlsdnslisten(mgr, csock);
+               isc__nm_enqueue_ievent(&mgr->workers[i],
+                                      (isc__netievent_t *)ievent);
+       }
+
+#if !HAVE_SO_REUSEPORT_LB && !defined(WIN32)
+       isc__nm_closesocket(fd);
+#endif
+
+       LOCK(&sock->lock);
+       while (sock->rchildren != sock->nchildren) {
+               WAIT(&sock->cond, &sock->lock);
+       }
+       result = sock->result;
+       atomic_store(&sock->active, true);
+       BROADCAST(&sock->scond);
+       UNLOCK(&sock->lock);
+       INSIST(result != ISC_R_DEFAULT);
+
        if (result == ISC_R_SUCCESS) {
-               atomic_store(&dnslistensock->listening, true);
-               *sockp = dnslistensock;
-               return (ISC_R_SUCCESS);
+               REQUIRE(sock->rchildren == sock->nchildren);
+               *sockp = sock;
        } else {
-               atomic_store(&dnslistensock->closed, true);
-               isc__nmsocket_detach(&dnslistensock);
-               return (result);
+               atomic_store(&sock->active, false);
+               isc__nm_tlsdns_stoplistening(sock);
+               isc_nmsocket_close(&sock);
        }
+
+       return (result);
 }
 
 void
-isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc__netievent_tlsdnsstop_t *ievent =
-               (isc__netievent_tlsdnsstop_t *)ev0;
-       isc_nmsocket_t *sock = ievent->sock;
+isc__nm_async_tlsdnslisten(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnslisten_t *ievent =
+               (isc__netievent_tlsdnslisten_t *)ev0;
+       isc_nmiface_t *iface = NULL;
+       sa_family_t sa_family;
+       int r;
+       int flags = 0;
+       isc_nmsocket_t *sock = NULL;
+       isc_result_t result = ISC_R_DEFAULT;
 
-       UNUSED(worker);
+       REQUIRE(VALID_NMSOCK(ievent->sock));
+       REQUIRE(ievent->sock->tid == isc_nm_tid());
+       REQUIRE(VALID_NMSOCK(ievent->sock->parent));
 
-       REQUIRE(isc__nm_in_netthread());
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->type == isc_nm_tlsdnslistener);
+       sock = ievent->sock;
+       iface = sock->iface;
+       sa_family = iface->addr.type.sa.sa_family;
+
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(sock->iface != NULL);
+       REQUIRE(sock->parent != NULL);
        REQUIRE(sock->tid == isc_nm_tid());
 
-       atomic_store(&sock->listening, false);
-       atomic_store(&sock->closed, true);
+       /* TODO: set min mss */
 
-       isc__nmsocket_clearcb(sock);
+       r = uv_tcp_init(&worker->loop, &sock->uv_handle.tcp);
+       RUNTIME_CHECK(r == 0);
+       uv_handle_set_data(&sock->uv_handle.handle, sock);
+       /* This keeps the socket alive after everything else is gone */
+       isc__nmsocket_attach(sock, &(isc_nmsocket_t *){ NULL });
 
-       if (sock->outer != NULL) {
-               isc__nm_tls_stoplistening(sock->outer);
-               isc__nmsocket_detach(&sock->outer);
-       }
-}
+       r = uv_timer_init(&worker->loop, &sock->timer);
+       RUNTIME_CHECK(r == 0);
+       uv_handle_set_data((uv_handle_t *)&sock->timer, sock);
 
-void
-isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) {
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->type == isc_nm_tlsdnslistener);
+       LOCK(&sock->parent->lock);
 
-       isc__netievent_tlsdnsstop_t *ievent =
-               isc__nm_get_ievent(sock->mgr, netievent_tlsdnsstop);
-       isc__nmsocket_attach(sock, &ievent->sock);
-       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                              (isc__netievent_t *)ievent);
-}
+       r = uv_tcp_open(&sock->uv_handle.tcp, sock->fd);
+       if (r < 0) {
+               isc__nm_closesocket(sock->fd);
+               isc__nm_incstats(sock->mgr, sock->statsindex[STATID_OPENFAIL]);
+               goto done;
+       }
+       isc__nm_incstats(sock->mgr, sock->statsindex[STATID_OPEN]);
 
-void
-isc_nm_tlsdns_sequential(isc_nmhandle_t *handle) {
-       REQUIRE(VALID_NMHANDLE(handle));
+       if (sa_family == AF_INET6) {
+               flags = UV_TCP_IPV6ONLY;
+       }
 
-       if (handle->sock->type != isc_nm_tlsdnssocket ||
-           handle->sock->outerhandle == NULL)
-       {
-               return;
+#if HAVE_SO_REUSEPORT_LB || defined(WIN32)
+       r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+                               &sock->iface->addr.type.sa, flags);
+       if (r < 0) {
+               isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
+               goto done;
+       }
+#else
+       if (sock->parent->fd == -1) {
+               r = isc_uv_tcp_freebind(&sock->uv_handle.tcp,
+                                       &sock->iface->addr.type.sa, flags);
+               if (r < 0) {
+                       isc__nm_incstats(sock->mgr,
+                                        sock->statsindex[STATID_BINDFAIL]);
+                       goto done;
+               }
+               sock->parent->uv_handle.tcp.flags = sock->uv_handle.tcp.flags;
+               sock->parent->fd = sock->fd;
+       } else {
+               /* The socket is already bound, just copy the flags */
+               sock->uv_handle.tcp.flags = sock->parent->uv_handle.tcp.flags;
        }
+#endif
 
        /*
-        * We don't want pipelining on this connection. That means
-        * that we need to pause after reading each request, and
-        * resume only after the request has been processed. This
-        * is done in resume_processing(), which is the socket's
-        * closehandle_cb callback, called whenever a handle
-        * is released.
+        * The callback will run in the same thread uv_listen() was called
+        * from, so a race with tlsdns_connection_cb() isn't possible.
         */
-       isc_nm_pauseread(handle->sock->outerhandle);
-       atomic_store(&handle->sock->sequential, true);
-}
+       r = uv_listen((uv_stream_t *)&sock->uv_handle.tcp, sock->backlog,
+                     tlsdns_connection_cb);
+       if (r != 0) {
+               isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+                             ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+                             "uv_listen failed: %s",
+                             isc_result_totext(isc__nm_uverr2result(r)));
+               isc__nm_incstats(sock->mgr, sock->statsindex[STATID_BINDFAIL]);
+               goto done;
+       }
 
-void
-isc_nm_tlsdns_keepalive(isc_nmhandle_t *handle, bool value) {
-       REQUIRE(VALID_NMHANDLE(handle));
+       atomic_store(&sock->listening, true);
+
+done:
+       result = isc__nm_uverr2result(r);
+       if (result != ISC_R_SUCCESS) {
+               sock->pquota = NULL;
+       }
+
+       sock->parent->rchildren += 1;
+       if (sock->parent->result == ISC_R_DEFAULT) {
+               sock->parent->result = result;
+       }
+       SIGNAL(&sock->parent->cond);
+       if (!atomic_load(&sock->parent->active)) {
+               WAIT(&sock->parent->scond, &sock->parent->lock);
+       }
+       INSIST(atomic_load(&sock->parent->active));
+       UNLOCK(&sock->parent->lock);
+}
+
+static void
+tlsdns_connection_cb(uv_stream_t *server, int status) {
+       isc_nmsocket_t *ssock = uv_handle_get_data((uv_handle_t *)server);
+       isc_result_t result;
+       isc_quota_t *quota = NULL;
+
+       if (status != 0) {
+               result = isc__nm_uverr2result(status);
+               goto done;
+       }
+
+       REQUIRE(VALID_NMSOCK(ssock));
+       REQUIRE(ssock->tid == isc_nm_tid());
+
+       if (inactive(ssock)) {
+               result = ISC_R_CANCELED;
+               goto done;
+       }
+
+       if (ssock->pquota != NULL) {
+               result = isc_quota_attach_cb(ssock->pquota, &quota,
+                                            &ssock->quotacb);
+               if (result == ISC_R_QUOTA) {
+                       isc__nm_incstats(ssock->mgr,
+                                        ssock->statsindex[STATID_ACCEPTFAIL]);
+                       return;
+               }
+       }
+
+       result = accept_connection(ssock, quota);
+done:
+       if (result != ISC_R_SUCCESS && result != ISC_R_NOCONN) {
+               if ((result != ISC_R_QUOTA && result != ISC_R_SOFTQUOTA) ||
+                   can_log_tlsdns_quota())
+               {
+                       isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+                                     ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+                                     "TCP connection failed: %s",
+                                     isc_result_totext(result));
+               }
+       }
+}
+
+static void
+enqueue_stoplistening(isc_nmsocket_t *sock) {
+       isc__netievent_tlsdnsstop_t *ievent =
+               isc__nm_get_netievent_tlsdnsstop(sock->mgr, sock);
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_tlsdns_stoplistening(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->type == isc_nm_tlsdnslistener);
+
+       if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+                                           true)) {
+               INSIST(0);
+               ISC_UNREACHABLE();
+       }
+       enqueue_stoplistening(sock);
+}
+
+static void
+tls_shutdown(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
+
+       isc__netievent_tlsdnsshutdown_t *ievent =
+               isc__nm_get_netievent_tlsdnsshutdown(sock->mgr, sock);
+       isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                                    (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsdnsshutdown(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnsshutdown_t *ievent =
+               (isc__netievent_tlsdnsshutdown_t *)ev0;
+       isc_nmsocket_t *sock = ievent->sock;
+       int rv;
+       int err;
+       isc_result_t result;
+
+       UNUSED(worker);
+
+       REQUIRE(VALID_NMSOCK(ievent->sock));
+
+       if (sock->tls.state != TLS_STATE_IO) {
+               /* Nothing to do */
+               return;
+       }
+
+       rv = SSL_shutdown(sock->tls.ssl);
+
+       if (rv == 1) {
+               sock->tls.state = TLS_STATE_NONE;
+               /* FIXME: continue closing the socket */
+               return;
+       }
+
+       if (rv == 0) {
+               result = tls_cycle(sock);
+               if (result != ISC_R_SUCCESS) {
+                       tls_error(sock, result);
+                       return;
+               }
+
+               /* Reschedule closing the socket */
+               tls_shutdown(sock);
+               return;
+       }
+
+       err = SSL_get_error(sock->tls.ssl, rv);
+
+       switch (err) {
+       case SSL_ERROR_WANT_READ:
+       case SSL_ERROR_WANT_WRITE:
+       case SSL_ERROR_WANT_X509_LOOKUP:
+               result = tls_cycle(sock);
+               if (result != ISC_R_SUCCESS) {
+                       tls_error(sock, result);
+                       return;
+               }
+
+               /* Reschedule closing the socket */
+               tls_shutdown(sock);
+               return;
+       case 0:
+               INSIST(0);
+               ISC_UNREACHABLE();
+       case SSL_ERROR_ZERO_RETURN:
+               tls_error(sock, ISC_R_EOF);
+               break;
+       default:
+               tls_error(sock, ISC_R_TLSERROR);
+       }
+       return;
+}
+
+void
+isc__nm_async_tlsdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnsstop_t *ievent =
+               (isc__netievent_tlsdnsstop_t *)ev0;
+       isc_nmsocket_t *sock = ievent->sock;
+
+       UNUSED(worker);
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+
+       if (sock->parent != NULL) {
+               stop_tlsdns_child(sock);
+               return;
+       }
+
+       /*
+        * If network manager is interlocked, re-enqueue the event for later.
+        */
+       if (!isc__nm_acquire_interlocked(sock->mgr)) {
+               enqueue_stoplistening(sock);
+       } else {
+               stop_tlsdns_parent(sock);
+               isc__nm_drop_interlocked(sock->mgr);
+       }
+}
+
+static void
+failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(result != ISC_R_SUCCESS);
+
+       stop_reading(sock);
+
+       if (sock->tls.pending_req) {
+               isc__nm_uvreq_t *req = sock->tls.pending_req;
+               sock->tls.pending_req = NULL;
+               failed_connect_cb(sock, req, ISC_R_CANCELED);
+       }
+
+       if (!sock->recv_read) {
+               goto destroy;
+       }
+       sock->recv_read = false;
+
+       if (sock->recv_cb != NULL) {
+               isc__nm_uvreq_t *req = get_read_req(sock);
+               isc__nmsocket_clearcb(sock);
+               isc__nm_readcb(sock, req, result);
+       }
+
+destroy:
+       isc__nmsocket_prep_destroy(sock);
+
+       /* We need to detach from quota after the read callback function had a
+        * chance to be executed. */
+       if (sock->quota) {
+               isc_quota_detach(&sock->quota);
+       }
+}
+
+static void
+failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req,
+              isc_result_t eresult) {
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(VALID_UVREQ(req));
+
+       if (req->cb.send != NULL) {
+               isc__nm_sendcb(sock, req, eresult);
+       } else {
+               isc__nm_uvreq_put(&req, sock);
+       }
+}
+
+static isc__nm_uvreq_t *
+get_read_req(isc_nmsocket_t *sock) {
+       isc__nm_uvreq_t *req = NULL;
+
+       req = isc__nm_uvreq_get(sock->mgr, sock);
+       req->cb.recv = sock->recv_cb;
+       req->cbarg = sock->recv_cbarg;
+
+       if (atomic_load(&sock->client)) {
+               isc_nmhandle_attach(sock->statichandle, &req->handle);
+       } else {
+               req->handle = isc__nmhandle_get(sock, NULL, NULL);
+       }
+
+       return (req);
+}
+
+static void
+readtimeout_cb(uv_timer_t *timer) {
+       isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer);
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(sock->reading);
+
+       /*
+        * Timeout; stop reading and process whatever we have.
+        */
+
+       failed_read_cb(sock, ISC_R_TIMEDOUT);
+}
+
+static void
+start_sock_timer(isc_nmsocket_t *sock) {
+       if (sock->read_timeout > 0) {
+               int r = uv_timer_start(&sock->timer, readtimeout_cb,
+                                      sock->read_timeout, 0);
+               RUNTIME_CHECK(r == 0);
+       }
+}
+
+static void
+stop_sock_timer(isc_nmsocket_t *sock) {
+       int r = uv_timer_stop(&sock->timer);
+       RUNTIME_CHECK(r == 0);
+}
+
+static void
+start_reading(isc_nmsocket_t *sock) {
+       int r;
+
+       if (sock->reading) {
+               return;
+       }
+
+       r = uv_read_start(&sock->uv_handle.stream, tlsdns_alloc_cb, read_cb);
+       RUNTIME_CHECK(r == 0);
+       sock->reading = true;
+
+       start_sock_timer(sock);
+}
+
+static void
+stop_reading(isc_nmsocket_t *sock) {
+       int r;
+
+       if (!sock->reading) {
+               return;
+       }
+
+       r = uv_read_stop(&sock->uv_handle.stream);
+       RUNTIME_CHECK(r == 0);
+       sock->reading = false;
+
+       stop_sock_timer(sock);
+}
+
+void
+isc__nm_tlsdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+       REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NMSOCK(handle->sock));
+
+       isc_nmsocket_t *sock = handle->sock;
+       isc__netievent_tlsdnsread_t *ievent = NULL;
+
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(sock->statichandle == handle);
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(!sock->recv_read);
+
+       sock->recv_cb = cb;
+       sock->recv_cbarg = cbarg;
+       sock->recv_read = true;
+       if (sock->read_timeout == 0) {
+               sock->read_timeout =
+                       (atomic_load(&sock->keepalive)
+                                ? atomic_load(&sock->mgr->keepalive)
+                                : atomic_load(&sock->mgr->idle));
+       }
+
+       ievent = isc__nm_get_netievent_tlsdnsread(sock->mgr, sock);
+
+       /*
+        * This MUST be done asynchronously, no matter which thread we're
+        * in. The callback function for isc_nm_read() often calls
+        * isc_nm_read() again; if we tried to do that synchronously
+        * we'd clash in processbuffer() and grow the stack indefinitely.
+        */
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+
+       return;
+}
+
+/*%<
+ * Allocator for TCP read operations. Limited to size 2^16.
+ *
+ * Note this doesn't actually allocate anything, it just assigns the
+ * worker's receive buffer to a socket, and marks it as "in use".
+ */
+static void
+tlsdns_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
+       isc_nmsocket_t *sock = uv_handle_get_data(handle);
+       isc__networker_t *worker = NULL;
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(isc__nm_in_netthread());
+
+       /*
+        * We need to limit the individual chunks to be read, so the BIO_write()
+        * will always succeed and the consumed before the next readcb is
+        * called.
+        */
+       if (size >= TLS_BUF_SIZE) {
+               size = TLS_BUF_SIZE;
+       }
+
+       worker = &sock->mgr->workers[sock->tid];
+       INSIST(!worker->recvbuf_inuse);
+
+       buf->base = worker->recvbuf;
+       buf->len = size;
+       worker->recvbuf_inuse = true;
+}
+
+void
+isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnsread_t *ievent =
+               (isc__netievent_tlsdnsread_t *)ev0;
+       isc_nmsocket_t *sock = ievent->sock;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       UNUSED(worker);
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+
+       if (inactive(sock)) {
+               sock->reading = true;
+               failed_read_cb(sock, ISC_R_CANCELED);
+               return;
+       }
+
+       result = tls_cycle(sock);
+       if (result != ISC_R_SUCCESS) {
+               stop_reading(sock);
+               failed_read_cb(sock, result);
+       }
+}
+
+/*
+ * Process a single packet from the incoming buffer.
+ *
+ * Return ISC_R_SUCCESS and attach 'handlep' to a handle if something
+ * was processed; return ISC_R_NOMORE if there isn't a full message
+ * to be processed.
+ *
+ * The caller will need to unreference the handle.
+ */
+static isc_result_t
+processbuffer(isc_nmsocket_t *sock) {
+       size_t len;
+       isc__nm_uvreq_t *req = NULL;
+       isc_nmhandle_t *handle = NULL;
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+
+       if (inactive(sock)) {
+               return (ISC_R_CANCELED);
+       }
+
+       /*
+        * If we don't even have the length yet, we can't do
+        * anything.
+        */
+       if (sock->buf_len < 2) {
+               return (ISC_R_NOMORE);
+       }
+
+       /*
+        * Process the first packet from the buffer, leaving
+        * the rest (if any) for later.
+        */
+       len = ntohs(*(uint16_t *)sock->buf);
+       if (len > sock->buf_len - 2) {
+               return (ISC_R_NOMORE);
+       }
+
+       req = get_read_req(sock);
+       REQUIRE(VALID_UVREQ(req));
+
+       /*
+        * We need to launch the resume_processing after the buffer has
+        * been consumed, thus we need to delay the detaching the handle.
+        */
+       isc_nmhandle_attach(req->handle, &handle);
+
+       /*
+        * The callback will be called synchronously because the
+        * result is ISC_R_SUCCESS, so we don't need to have
+        * the buffer on the heap
+        */
+       req->uvbuf.base = (char *)sock->buf + 2;
+       req->uvbuf.len = len;
+
+       /*
+        * If isc__nm_tlsdns_read() was called, it will be satisfied by single
+        * DNS message in the next call.
+        */
+       sock->recv_read = false;
+
+       /*
+        * The assertion failure here means that there's a errnoneous extra
+        * nmhandle detach happening in the callback and resume_processing gets
+        * called while we are still processing the buffer.
+        */
+       REQUIRE(sock->processing == false);
+       sock->processing = true;
+       isc__nm_readcb(sock, req, ISC_R_SUCCESS);
+       sock->processing = false;
+
+       len += 2;
+       sock->buf_len -= len;
+       if (len > 0) {
+               memmove(sock->buf, sock->buf + len, sock->buf_len);
+       }
+
+       isc_nmhandle_detach(&handle);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+tls_cycle_input(isc_nmsocket_t *sock) {
+       isc_result_t result = ISC_R_SUCCESS;
+       int err = 0;
+       int rv = 1;
+
+       if (sock->tls.state == TLS_STATE_IO) {
+               size_t len;
+
+               for (;;) {
+                       (void)SSL_peek(sock->tls.ssl, &(char){ '\0' }, 0);
+
+                       int pending = SSL_pending(sock->tls.ssl);
+                       if (pending > TLS_BUF_SIZE) {
+                               pending = TLS_BUF_SIZE;
+                       }
+
+                       if ((sock->buf_len + pending) > sock->buf_size) {
+                               alloc_dnsbuf(sock, sock->buf_len + pending);
+                       }
+
+                       len = 0;
+                       rv = SSL_read_ex(sock->tls.ssl,
+                                        sock->buf + sock->buf_len,
+                                        sock->buf_size - sock->buf_len, &len);
+                       if (rv != 1) {
+                               /* Process what's in the buffer so far */
+                               process_sock_buffer(sock);
+
+                               /* FIXME: Should we call failed_read_cb()? */
+                               break;
+                       }
+
+                       REQUIRE((size_t)pending == len);
+
+                       sock->buf_len += len;
+
+                       process_sock_buffer(sock);
+               }
+       } else if (!SSL_is_init_finished(sock->tls.ssl)) {
+               if (SSL_is_server(sock->tls.ssl)) {
+                       rv = SSL_accept(sock->tls.ssl);
+               } else {
+                       rv = SSL_connect(sock->tls.ssl);
+               }
+
+       } else {
+               rv = 1;
+       }
+
+       if (rv <= 0) {
+               err = SSL_get_error(sock->tls.ssl, rv);
+       }
+
+       switch (err) {
+       case SSL_ERROR_WANT_READ:
+               if (sock->tls.state == TLS_STATE_NONE &&
+                   !SSL_is_init_finished(sock->tls.ssl)) {
+                       sock->tls.state = TLS_STATE_HANDSHAKE;
+                       start_reading(sock);
+               }
+               /* else continue reading */
+               break;
+       case SSL_ERROR_WANT_WRITE:
+               async_tlsdns_cycle(sock);
+               break;
+       case SSL_ERROR_WANT_X509_LOOKUP:
+               /* Continue reading/writing */
+               break;
+       case 0:
+               /* Everything is ok, continue */
+               break;
+       case SSL_ERROR_ZERO_RETURN:
+               return (ISC_R_EOF);
+       default:
+               return (ISC_R_TLSERROR);
+       }
+
+       /* Stop state after handshake */
+       if (sock->tls.state == TLS_STATE_HANDSHAKE &&
+           SSL_is_init_finished(sock->tls.ssl))
+       {
+               sock->tls.state = TLS_STATE_IO;
+
+               if (SSL_is_server(sock->tls.ssl)) {
+                       REQUIRE(sock->recv_handle != NULL);
+                       result = sock->accept_cb(sock->recv_handle,
+                                                ISC_R_SUCCESS,
+                                                sock->accept_cbarg);
+
+                       if (result != ISC_R_SUCCESS) {
+                               isc_nmhandle_detach(&sock->recv_handle);
+                               goto failure;
+                       }
+               } else {
+                       isc__nm_uvreq_t *req = sock->tls.pending_req;
+                       sock->tls.pending_req = NULL;
+
+                       atomic_store(&sock->connecting, false);
+
+                       isc__nm_connectcb(sock, req, ISC_R_SUCCESS);
+               }
+               async_tlsdns_cycle(sock);
+       }
+failure:
+       return (result);
+}
+
+static void
+tls_error(isc_nmsocket_t *sock, isc_result_t result) {
+       switch (sock->tls.state) {
+       case TLS_STATE_HANDSHAKE:
+               stop_reading(sock);
+               break;
+       case TLS_STATE_IO:
+               stop_reading(sock);
+               break;
+       case TLS_STATE_ERROR:
+               return;
+       default:
+               break;
+       }
+
+       sock->tls.state = TLS_STATE_ERROR;
+       sock->tls.pending_error = result;
+
+       /* tlsdns_close_direct(sock); */
+}
+
+static void
+free_senddata(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tls.senddata.base != NULL);
+       REQUIRE(sock->tls.senddata.len > 0);
+
+       isc_mem_put(sock->mgr->mctx, sock->tls.senddata.base,
+                   sock->tls.senddata.len);
+       sock->tls.senddata.base = NULL;
+       sock->tls.senddata.len = 0;
+}
+
+static void
+tls_write_cb(uv_write_t *req, int status) {
+       isc_result_t result;
+       isc__nm_uvreq_t *uvreq = (isc__nm_uvreq_t *)req->data;
+       isc_nmsocket_t *sock = uvreq->sock;
+
+       free_senddata(sock);
+
+       if (status != 0) {
+               tls_error(sock, isc__nm_uverr2result(status));
+               return;
+       }
+
+       isc__nm_uvreq_put(&uvreq, sock);
+
+       result = tls_cycle(sock);
+       if (result != ISC_R_SUCCESS) {
+               tls_error(sock, result);
+               return;
+       }
+}
+
+static isc_result_t
+tls_cycle_output(isc_nmsocket_t *sock) {
+       isc_result_t result = ISC_R_SUCCESS;
+       int pending;
+
+       while ((pending = BIO_pending(sock->tls.app_rbio)) > 0) {
+               isc__nm_uvreq_t *req = NULL;
+               size_t bytes;
+               int rv;
+               int err;
+
+               if (sock->tls.senddata.base != NULL ||
+                   sock->tls.senddata.len > 0) {
+                       break;
+               }
+
+               if (pending > TLS_BUF_SIZE) {
+                       pending = TLS_BUF_SIZE;
+               }
+
+               sock->tls.senddata.base = isc_mem_get(sock->mgr->mctx, pending);
+               sock->tls.senddata.len = pending;
+
+               rv = BIO_read_ex(sock->tls.app_rbio, sock->tls.senddata.base,
+                                pending, &bytes);
+
+               RUNTIME_CHECK(rv == 1);
+               INSIST((size_t)pending == bytes);
+
+               err = uv_try_write(&sock->uv_handle.stream, &sock->tls.senddata,
+                                  1);
+
+               if (err == pending) {
+                       /* Wrote everything, restart */
+                       free_senddata(sock);
+                       continue;
+               }
+
+               if (err > 0) {
+                       /* Partial write, send rest asynchronously */
+                       memmove(sock->tls.senddata.base,
+                               sock->tls.senddata.base + err, pending - err);
+                       sock->tls.senddata.len = pending - err;
+               } else if (err == UV_ENOSYS || err == UV_EAGAIN) {
+                       /* uv_try_write is not supported, send asynchronously */
+               } else {
+                       result = isc__nm_uverr2result(err);
+                       free_senddata(sock);
+                       break;
+               }
+
+               req = isc__nm_uvreq_get(sock->mgr, sock);
+               req->uvbuf.base = (char *)sock->tls.senddata.base;
+               req->uvbuf.len = sock->tls.senddata.len;
+
+               err = uv_write(&req->uv_req.write, &sock->uv_handle.stream,
+                              &sock->tls.senddata, 1, tls_write_cb);
+
+               INSIST(err == 0);
+
+               break;
+       }
+
+       return (result);
+}
+
+static isc_result_t
+tls_pop_error(isc_nmsocket_t *sock) {
+       isc_result_t result;
+
+       if (sock->tls.state != TLS_STATE_ERROR) {
+               return (ISC_R_SUCCESS);
+       }
+
+       if (sock->tls.pending_error == ISC_R_SUCCESS) {
+               return (ISC_R_TLSERROR);
+       }
+
+       result = sock->tls.pending_error;
+       sock->tls.pending_error = ISC_R_SUCCESS;
+
+       return (result);
+}
+
+static isc_result_t
+tls_cycle(isc_nmsocket_t *sock) {
+       isc_result_t result;
+
+       result = tls_pop_error(sock);
+       if (result != ISC_R_SUCCESS) {
+               goto done;
+       }
+
+       if (sock->tls.cycle) {
+               return (ISC_R_SUCCESS);
+       }
+
+       sock->tls.cycle = true;
+       result = tls_cycle_input(sock);
+       if (result != ISC_R_SUCCESS) {
+               goto done;
+       }
+
+       result = tls_cycle_output(sock);
+       if (result != ISC_R_SUCCESS) {
+               goto done;
+       }
+done:
+       sock->tls.cycle = false;
+
+       return (result);
+}
+
+static void
+async_tlsdns_cycle(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
+
+       /* Socket was closed midflight by isc__nm_tlsdns_shutdown() */
+       if (!isc__nmsocket_active(sock)) {
+               return;
+       }
+
+       isc__netievent_tlsdnscycle_t *ievent =
+               isc__nm_get_netievent_tlsdnscycle(sock->mgr, sock);
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tlsdnscycle(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnscycle_t *ievent =
+               (isc__netievent_tlsdnscycle_t *)ev0;
+       isc_result_t result;
+       isc_nmsocket_t *sock;
+
+       UNUSED(worker);
+
+       REQUIRE(VALID_NMSOCK(ievent->sock));
+       REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+       sock = ievent->sock;
+
+       result = tls_cycle(sock);
+       if (result != ISC_R_SUCCESS) {
+               tls_error(sock, result);
+       }
+}
+
+static void
+read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
+       isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)stream);
+       size_t len;
+       isc_result_t result;
+       int rv;
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(sock->reading);
+       REQUIRE(buf != NULL);
+
+       if (inactive(sock)) {
+               failed_read_cb(sock, ISC_R_CANCELED);
+               goto free;
+       }
+
+       if (nread < 0) {
+               if (nread != UV_EOF) {
+                       isc__nm_incstats(sock->mgr,
+                                        sock->statsindex[STATID_RECVFAIL]);
+               }
+
+               failed_read_cb(sock, isc__nm_uverr2result(nread));
+
+               goto free;
+       }
+
+       if (!atomic_load(&sock->client)) {
+               sock->read_timeout = atomic_load(&sock->mgr->idle);
+       }
+
+       /*
+        * The input has to be fed into BIO
+        */
+       rv = BIO_write_ex(sock->tls.app_wbio, buf->base, (size_t)nread, &len);
+
+       if (rv <= 0 || (size_t)nread != len) {
+               failed_read_cb(sock, ISC_R_TLSERROR);
+               goto free;
+       }
+
+       result = tls_cycle(sock);
+       if (result != ISC_R_SUCCESS) {
+               failed_read_cb(sock, result);
+       }
+free:
+       async_tlsdns_cycle(sock);
+       isc__nm_free_uvbuf(sock, buf);
+}
+
+static void
+quota_accept_cb(isc_quota_t *quota, void *sock0) {
+       isc_nmsocket_t *sock = (isc_nmsocket_t *)sock0;
+
+       REQUIRE(VALID_NMSOCK(sock));
+
+       /*
+        * Create a tlsdnsaccept event and pass it using the async channel.
+        */
+
+       isc__netievent_tlsdnsaccept_t *ievent =
+               isc__nm_get_netievent_tlsdnsaccept(sock->mgr, sock, quota);
+       isc__nm_maybe_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                                    (isc__netievent_t *)ievent);
+}
+
+/*
+ * This is called after we get a quota_accept_cb() callback.
+ */
+void
+isc__nm_async_tlsdnsaccept(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnsaccept_t *ievent =
+               (isc__netievent_tlsdnsaccept_t *)ev0;
+       isc_result_t result;
+
+       UNUSED(worker);
+
+       REQUIRE(VALID_NMSOCK(ievent->sock));
+       REQUIRE(ievent->sock->tid == isc_nm_tid());
+
+       result = accept_connection(ievent->sock, ievent->quota);
+       if (result != ISC_R_SUCCESS && result != ISC_R_NOCONN) {
+               if ((result != ISC_R_QUOTA && result != ISC_R_SOFTQUOTA) ||
+                   can_log_tlsdns_quota())
+               {
+                       isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
+                                     ISC_LOGMODULE_NETMGR, ISC_LOG_ERROR,
+                                     "TCP connection failed: %s",
+                                     isc_result_totext(result));
+               }
+       }
+}
+
+static isc_result_t
+accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) {
+       isc_nmsocket_t *csock = NULL;
+       isc__networker_t *worker = NULL;
+       int r;
+       isc_result_t result;
+       struct sockaddr_storage peer_ss;
+       struct sockaddr_storage local_ss;
+       isc_sockaddr_t local;
+       isc_nmhandle_t *handle = NULL;
+
+       REQUIRE(VALID_NMSOCK(ssock));
+       REQUIRE(ssock->tid == isc_nm_tid());
+
+       if (inactive(ssock)) {
+               if (quota != NULL) {
+                       isc_quota_detach(&quota);
+               }
+               return (ISC_R_CANCELED);
+       }
+
+       REQUIRE(ssock->accept_cb != NULL);
+
+       csock = isc_mem_get(ssock->mgr->mctx, sizeof(isc_nmsocket_t));
+       isc__nmsocket_init(csock, ssock->mgr, isc_nm_tlsdnssocket,
+                          ssock->iface);
+       csock->tid = ssock->tid;
+       csock->extrahandlesize = ssock->extrahandlesize;
+       isc__nmsocket_attach(ssock, &csock->server);
+       csock->accept_cb = ssock->accept_cb;
+       csock->accept_cbarg = ssock->accept_cbarg;
+       csock->recv_cb = ssock->recv_cb;
+       csock->recv_cbarg = ssock->recv_cbarg;
+       csock->quota = quota;
+       csock->accepting = true;
+
+       worker = &csock->mgr->workers[csock->tid];
+
+       r = uv_tcp_init(&worker->loop, &csock->uv_handle.tcp);
+       RUNTIME_CHECK(r == 0);
+       uv_handle_set_data(&csock->uv_handle.handle, csock);
+
+       r = uv_timer_init(&worker->loop, &csock->timer);
+       RUNTIME_CHECK(r == 0);
+       uv_handle_set_data((uv_handle_t *)&csock->timer, csock);
+
+       r = uv_accept(&ssock->uv_handle.stream, &csock->uv_handle.stream);
+       if (r != 0) {
+               result = isc__nm_uverr2result(r);
+               goto failure;
+       }
+
+       r = uv_tcp_getpeername(&csock->uv_handle.tcp,
+                              (struct sockaddr *)&peer_ss,
+                              &(int){ sizeof(peer_ss) });
+       if (r != 0) {
+               result = isc__nm_uverr2result(r);
+               goto failure;
+       }
+
+       result = isc_sockaddr_fromsockaddr(&csock->peer,
+                                          (struct sockaddr *)&peer_ss);
+       if (result != ISC_R_SUCCESS) {
+               goto failure;
+       }
+
+       r = uv_tcp_getsockname(&csock->uv_handle.tcp,
+                              (struct sockaddr *)&local_ss,
+                              &(int){ sizeof(local_ss) });
+       if (r != 0) {
+               result = isc__nm_uverr2result(r);
+               goto failure;
+       }
+
+       result = isc_sockaddr_fromsockaddr(&local,
+                                          (struct sockaddr *)&local_ss);
+       if (result != ISC_R_SUCCESS) {
+               goto failure;
+       }
+
+       /*
+        * The handle will be either detached on acceptcb failure or in the
+        * readcb.
+        */
+       handle = isc__nmhandle_get(csock, NULL, &local);
+
+       result = ssock->accept_cb(handle, ISC_R_SUCCESS, ssock->accept_cbarg);
+       if (result != ISC_R_SUCCESS) {
+               isc_nmhandle_detach(&handle);
+               goto failure;
+       }
+
+       csock->tls.state = TLS_STATE_NONE;
+
+       csock->tls.ssl = SSL_new(ssock->tls.ctx);
+       RUNTIME_CHECK(csock->tls.ssl != NULL);
+
+       r = BIO_new_bio_pair(&csock->tls.ssl_wbio, TLS_BUF_SIZE,
+                            &csock->tls.app_rbio, TLS_BUF_SIZE);
+       RUNTIME_CHECK(r == 1);
+
+       r = BIO_new_bio_pair(&csock->tls.ssl_rbio, TLS_BUF_SIZE,
+                            &csock->tls.app_wbio, TLS_BUF_SIZE);
+       RUNTIME_CHECK(r == 1);
+
+#if HAVE_SSL_SET0_RBIO && HAVE_SSL_SET0_WBIO
+       /*
+        * Note that if the rbio and wbio are the same then SSL_set0_rbio() and
+        * SSL_set0_wbio() each take ownership of one reference. Therefore it
+        * may be necessary to increment the number of references available
+        * using BIO_up_ref(3) before calling the set0 functions.
+        */
+       SSL_set0_rbio(csock->tls.ssl, csock->tls.ssl_rbio);
+       SSL_set0_wbio(csock->tls.ssl, csock->tls.ssl_wbio);
+#else
+       SSL_set_bio(csock->tls.ssl, csock->tls.ssl_rbio, csock->tls.ssl_wbio);
+#endif
+
+       SSL_set_accept_state(csock->tls.ssl);
+
+       /* FIXME: Set SSL_MODE_RELEASE_BUFFERS */
+
+       csock->accepting = false;
+
+       isc__nm_incstats(csock->mgr, csock->statsindex[STATID_ACCEPT]);
+
+       csock->read_timeout = atomic_load(&csock->mgr->init);
+
+       csock->closehandle_cb = resume_processing;
+
+       /*
+        * We need to keep the handle alive until we fail to read or connection
+        * is closed by the other side, it will be detached via
+        * prep_destroy()->tlsdns_close_direct().
+        */
+       isc_nmhandle_attach(handle, &csock->recv_handle);
+
+       /*
+        * The initial timer has been set, update the read timeout for the next
+        * reads.
+        */
+       csock->read_timeout = (atomic_load(&csock->keepalive)
+                                      ? atomic_load(&csock->mgr->keepalive)
+                                      : atomic_load(&csock->mgr->idle));
+
+       isc_nmhandle_detach(&handle);
+
+       start_reading(csock);
+
+       /*
+        * sock is now attached to the handle.
+        */
+       isc__nmsocket_detach(&csock);
+
+       return (ISC_R_SUCCESS);
+
+failure:
+       atomic_store(&csock->active, false);
+
+       failed_accept_cb(csock, result);
+
+       isc__nmsocket_prep_destroy(csock);
+
+       isc__nmsocket_detach(&csock);
+
+       return (result);
+}
+
+void
+isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
+                   isc_nm_cb_t cb, void *cbarg) {
+       REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NMSOCK(handle->sock));
+
+       isc_nmsocket_t *sock = handle->sock;
+       isc__netievent_tlsdnssend_t *ievent = NULL;
+       isc__nm_uvreq_t *uvreq = NULL;
+
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+       uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+       *(uint16_t *)uvreq->tcplen = htons(region->length);
+       uvreq->uvbuf.base = (char *)region->base;
+       uvreq->uvbuf.len = region->length;
+
+       isc_nmhandle_attach(handle, &uvreq->handle);
+
+       uvreq->cb.send = cb;
+       uvreq->cbarg = cbarg;
+
+       ievent = isc__nm_get_netievent_tlsdnssend(sock->mgr, sock, uvreq);
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+       return;
+}
+
+/*
+ * Handle 'tcpsend' async event - send a packet on the socket
+ */
+void
+isc__nm_async_tlsdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc_result_t result;
+       isc__netievent_tlsdnssend_t *ievent =
+               (isc__netievent_tlsdnssend_t *)ev0;
+       isc_nmsocket_t *sock = ievent->sock;
+       isc__nm_uvreq_t *uvreq = ievent->req;
+
+       UNUSED(worker);
+
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(sock->tid == isc_nm_tid());
+
+       result = tlsdns_send_direct(sock, uvreq);
+       if (result != ISC_R_SUCCESS) {
+               isc__nm_incstats(sock->mgr, sock->statsindex[STATID_SENDFAIL]);
+               failed_send_cb(sock, uvreq, result);
+       }
+}
+
+static void
+tlsdns_send_enqueue(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+       isc__netievent_tlsdnssend_t *ievent =
+               isc__nm_get_netievent_tlsdnssend(sock->mgr, sock, req);
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+}
+
+static isc_result_t
+tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) {
+       isc_result_t result;
+       int err = 0;
+       int rv;
+       size_t bytes = 0;
+       size_t sendlen;
+       isc__networker_t *worker = NULL;
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(VALID_UVREQ(req));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+
+       result = tls_pop_error(sock);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
+       }
+
+       if (inactive(sock)) {
+               return (ISC_R_CANCELED);
+       }
+
+       /* Writes won't succeed until handshake end */
+       if (!SSL_is_init_finished(sock->tls.ssl)) {
+               goto requeue;
+       }
+
+       /*
+        * There's no SSL_writev(), so we need to use a local buffer to assemble
+        * the whole message
+        */
+       worker = &sock->mgr->workers[sock->tid];
+       sendlen = req->uvbuf.len + sizeof(uint16_t);
+       memmove(worker->sendbuf, req->tcplen, sizeof(uint16_t));
+       memmove(worker->sendbuf + sizeof(uint16_t), req->uvbuf.base,
+               req->uvbuf.len);
+
+       rv = SSL_write_ex(sock->tls.ssl, worker->sendbuf, sendlen, &bytes);
+       if (rv > 0) {
+               /* SSL_write_ex() doesn't do partial writes */
+               INSIST(sendlen == bytes);
+
+               isc__nm_sendcb(sock, req, ISC_R_SUCCESS);
+               async_tlsdns_cycle(sock);
+               return (ISC_R_SUCCESS);
+       }
+
+       /* Nothing was written, maybe enqueue? */
+       err = SSL_get_error(sock->tls.ssl, rv);
+
+       switch (err) {
+       case SSL_ERROR_WANT_WRITE:
+       case SSL_ERROR_WANT_READ:
+               break;
+       case 0:
+               INSIST(0);
+               ISC_UNREACHABLE();
+       default:
+               return (ISC_R_TLSERROR);
+       }
+
+       result = tls_cycle(sock);
+
+requeue:
+
+       tlsdns_send_enqueue(sock, req);
+
+       return (result);
+}
+
+static void
+tlsdns_stop_cb(uv_handle_t *handle) {
+       isc_nmsocket_t *sock = uv_handle_get_data(handle);
+
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(atomic_load(&sock->closing));
 
-       if (handle->sock->type != isc_nm_tlsdnssocket ||
-           handle->sock->outerhandle == NULL)
-       {
-               return;
+       uv_handle_set_data(handle, NULL);
+
+       if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+                                           true)) {
+               INSIST(0);
+               ISC_UNREACHABLE();
        }
 
-       atomic_store(&handle->sock->keepalive, value);
-       atomic_store(&handle->sock->outerhandle->sock->keepalive, value);
+       isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CLOSE]);
+
+       atomic_store(&sock->listening, false);
+
+       BIO_free_all(sock->tls.app_rbio);
+       BIO_free_all(sock->tls.app_wbio);
+
+       sock->tls.ctx = NULL;
+
+       isc__nmsocket_detach(&sock);
 }
 
 static void
-resume_processing(void *arg) {
-       isc_nmsocket_t *sock = (isc_nmsocket_t *)arg;
-       isc_result_t result;
+tlsdns_close_cb(uv_handle_t *handle) {
+       isc_nmsocket_t *sock = uv_handle_get_data(handle);
+       uv_handle_set_data(handle, NULL);
 
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(atomic_load(&sock->closing));
 
-       if (sock->type != isc_nm_tlsdnssocket || sock->outerhandle == NULL) {
-               return;
+       if (!atomic_compare_exchange_strong(&sock->closed, &(bool){ false },
+                                           true)) {
+               INSIST(0);
+               ISC_UNREACHABLE();
        }
 
-       if (atomic_load(&sock->ah) == 0) {
-               /* Nothing is active; sockets can timeout now */
-               if (sock->timer_initialized) {
-                       uv_timer_start(&sock->timer, dnstcp_readtimeout,
-                                      sock->read_timeout, 0);
-                       sock->timer_running = true;
-               }
-       }
+       isc__nm_incstats(sock->mgr, sock->statsindex[STATID_CLOSE]);
 
-       /*
-        * For sequential sockets: Process what's in the buffer, or
-        * if there aren't any messages buffered, resume reading.
-        */
-       if (atomic_load(&sock->sequential)) {
-               isc_nmhandle_t *handle = NULL;
+       if (sock->server != NULL) {
+               isc__nmsocket_detach(&sock->server);
+       }
 
-               result = processbuffer(sock, &handle);
-               if (result == ISC_R_SUCCESS) {
-                       if (sock->timer_initialized) {
-                               uv_timer_stop(&sock->timer);
-                       }
-                       isc_nmhandle_detach(&handle);
-               } else if (sock->outerhandle != NULL) {
-                       isc_nm_resumeread(sock->outerhandle);
-               }
+       atomic_store(&sock->connected, false);
 
-               return;
+       if (sock->tls.ssl) {
+               SSL_free(sock->tls.ssl);
+               sock->tls.ssl = NULL;
        }
 
-       /*
-        * For pipelined sockets: If we're under the clients-per-connection
-        * limit, resume processing until we reach the limit again.
-        */
-       do {
-               isc_nmhandle_t *dnshandle = NULL;
-
-               result = processbuffer(sock, &dnshandle);
-               if (result != ISC_R_SUCCESS) {
-                       /*
-                        * Nothing in the buffer; resume reading.
-                        */
-                       if (sock->outerhandle != NULL) {
-                               isc_nm_resumeread(sock->outerhandle);
-                       }
+       BIO_free_all(sock->tls.app_rbio);
+       BIO_free_all(sock->tls.app_wbio);
 
-                       break;
-               }
+       sock->tls.ctx = NULL;
 
-               if (sock->timer_initialized) {
-                       uv_timer_stop(&sock->timer);
-               }
-               isc_nmhandle_detach(&dnshandle);
-       } while (atomic_load(&sock->ah) < TLSDNS_CLIENTS_PER_CONN);
+       isc__nmsocket_prep_destroy(sock);
 }
 
 static void
-tlsdnssend_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
-       isc__nm_uvreq_t *req = (isc__nm_uvreq_t *)cbarg;
-       REQUIRE(VALID_UVREQ(req));
-
-       UNUSED(handle);
+timer_close_cb(uv_handle_t *handle) {
+       isc_nmsocket_t *sock = uv_handle_get_data(handle);
 
-       req->cb.send(req->handle, result, req->cbarg);
-       isc_mem_put(req->sock->mgr->mctx, req->uvbuf.base, req->uvbuf.len);
-       isc__nm_uvreq_put(&req, req->handle->sock);
-       isc_nmhandle_detach(&handle);
-}
+       uv_handle_set_data(handle, NULL);
 
-/*
- * The socket is closing, outerhandle has been detached, listener is
- * inactive, or the netmgr is closing: any operation on it should abort
- * with ISC_R_CANCELED.
- */
-static bool
-inactive(isc_nmsocket_t *sock) {
-       return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
-               sock->outerhandle == NULL ||
-               (sock->listener != NULL &&
-                !isc__nmsocket_active(sock->listener)) ||
-               atomic_load(&sock->mgr->closing));
+       if (sock->parent) {
+               uv_close(&sock->uv_handle.handle, tlsdns_stop_cb);
+       } else {
+               uv_close(&sock->uv_handle.handle, tlsdns_close_cb);
+       }
 }
 
-void
-isc__nm_async_tlsdnssend(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc__netievent_tlsdnssend_t *ievent =
-               (isc__netievent_tlsdnssend_t *)ev0;
-       isc__nm_uvreq_t *req = ievent->req;
-       isc_nmsocket_t *sock = ievent->sock;
-       isc_nmhandle_t *sendhandle = NULL;
-       isc_region_t r;
-
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(VALID_UVREQ(req));
-       REQUIRE(worker->id == sock->tid);
-       REQUIRE(sock->tid == isc_nm_tid());
+static void
+stop_tlsdns_child(isc_nmsocket_t *sock) {
        REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(sock->tid == isc_nm_tid());
 
-       if (inactive(sock)) {
-               req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg);
-               isc_mem_put(sock->mgr->mctx, req->uvbuf.base, req->uvbuf.len);
-               isc__nm_uvreq_put(&req, req->handle->sock);
+       if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
+                                           true)) {
                return;
        }
 
-       r.base = (unsigned char *)req->uvbuf.base;
-       r.length = req->uvbuf.len;
-       isc_nmhandle_attach(sock->outerhandle, &sendhandle);
-       isc_nm_send(sendhandle, &r, tlsdnssend_cb, req);
-}
-
-/*
- * isc__nm_tcp_send sends buf to a peer on a socket.
- */
-void
-isc__nm_tlsdns_send(isc_nmhandle_t *handle, isc_region_t *region,
-                   isc_nm_cb_t cb, void *cbarg) {
-       isc__nm_uvreq_t *uvreq = NULL;
-
-       REQUIRE(VALID_NMHANDLE(handle));
+       tlsdns_close_direct(sock);
 
-       isc_nmsocket_t *sock = handle->sock;
+       LOCK(&sock->parent->lock);
+       sock->parent->rchildren -= 1;
+       UNLOCK(&sock->parent->lock);
+       BROADCAST(&sock->parent->cond);
+}
 
+static void
+stop_tlsdns_parent(isc_nmsocket_t *sock) {
        REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(sock->type == isc_nm_tlsdnslistener);
 
-       if (inactive(sock)) {
-               cb(handle, ISC_R_CANCELED, cbarg);
-               return;
-       }
+       for (size_t i = 0; i < sock->nchildren; i++) {
+               isc__netievent_tlsdnsstop_t *ievent = NULL;
+               isc_nmsocket_t *csock = &sock->children[i];
 
-       uvreq = isc__nm_uvreq_get(sock->mgr, sock);
-       isc_nmhandle_attach(handle, &uvreq->handle);
-       uvreq->cb.send = cb;
-       uvreq->cbarg = cbarg;
+               REQUIRE(VALID_NMSOCK(csock));
 
-       uvreq->uvbuf.base = isc_mem_get(sock->mgr->mctx, region->length + 2);
-       uvreq->uvbuf.len = region->length + 2;
-       *(uint16_t *)uvreq->uvbuf.base = htons(region->length);
-       memmove(uvreq->uvbuf.base + 2, region->base, region->length);
+               atomic_store(&csock->active, false);
 
-       isc__netievent_tlsdnssend_t *ievent = NULL;
+               if (csock->tid == isc_nm_tid()) {
+                       stop_tlsdns_child(csock);
+                       continue;
+               }
+
+               ievent = isc__nm_get_netievent_tlsdnsstop(sock->mgr, csock);
+               isc__nm_enqueue_ievent(&sock->mgr->workers[csock->tid],
+                                      (isc__netievent_t *)ievent);
+       }
 
-       ievent = isc__nm_get_ievent(sock->mgr, netievent_tlsdnssend);
-       ievent->req = uvreq;
-       isc__nmsocket_attach(sock, &ievent->sock);
+       LOCK(&sock->lock);
+       while (sock->rchildren > 0) {
+               WAIT(&sock->cond, &sock->lock);
+       }
+       atomic_store(&sock->closed, true);
+       UNLOCK(&sock->lock);
 
-       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                              (isc__netievent_t *)ievent);
+       isc__nmsocket_prep_destroy(sock);
 }
 
 static void
 tlsdns_close_direct(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(atomic_load(&sock->closing));
 
-       if (sock->timer_running) {
-               uv_timer_stop(&sock->timer);
-               sock->timer_running = false;
+       if (sock->quota != NULL) {
+               isc_quota_detach(&sock->quota);
        }
 
-       /* We don't need atomics here, it's all in single network thread */
-       if (sock->self != NULL) {
-               isc__nmsocket_detach(&sock->self);
-       } else if (sock->timer_initialized) {
-               /*
-                * We need to fire the timer callback to clean it up,
-                * it will then call us again (via detach) so that we
-                * can finally close the socket.
-                */
-               sock->timer_initialized = false;
-               uv_timer_stop(&sock->timer);
-               uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
-       } else {
-               /*
-                * At this point we're certain that there are no external
-                * references, we can close everything.
-                */
-               if (sock->outerhandle != NULL) {
-                       isc__nmsocket_clearcb(sock->outerhandle->sock);
-                       isc_nmhandle_detach(&sock->outerhandle);
-               }
-               if (sock->listener != NULL) {
-                       isc__nmsocket_detach(&sock->listener);
-               }
-               atomic_store(&sock->closed, true);
-               isc__nmsocket_prep_destroy(sock);
+       if (sock->recv_handle != NULL) {
+               isc_nmhandle_detach(&sock->recv_handle);
        }
+
+       stop_reading(sock);
+       uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
 }
 
 void
 isc__nm_tlsdns_close(isc_nmsocket_t *sock) {
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(!isc__nmsocket_active(sock));
 
        if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
                                            true)) {
@@ -703,10 +2019,12 @@ isc__nm_tlsdns_close(isc_nmsocket_t *sock) {
        if (sock->tid == isc_nm_tid()) {
                tlsdns_close_direct(sock);
        } else {
+               /*
+                * We need to create an event and pass it using async channel
+                */
                isc__netievent_tlsdnsclose_t *ievent =
-                       isc__nm_get_ievent(sock->mgr, netievent_tlsdnsclose);
+                       isc__nm_get_netievent_tlsdnsclose(sock->mgr, sock);
 
-               isc__nmsocket_attach(sock, &ievent->sock);
                isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
                                       (isc__netievent_t *)ievent);
        }
@@ -718,267 +2036,191 @@ isc__nm_async_tlsdnsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
                (isc__netievent_tlsdnsclose_t *)ev0;
        isc_nmsocket_t *sock = ievent->sock;
 
+       UNUSED(worker);
+
        REQUIRE(VALID_NMSOCK(sock));
        REQUIRE(sock->tid == isc_nm_tid());
 
-       UNUSED(worker);
-
-       tlsdns_close_direct(ievent->sock);
+       tlsdns_close_direct(sock);
 }
 
-typedef struct {
-       isc_mem_t *mctx;
-       isc_nm_cb_t cb;
-       void *cbarg;
-       size_t extrahandlesize;
-} tcpconnect_t;
-
-static void
-tlsdnsconnect_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
-       tcpconnect_t *conn = (tcpconnect_t *)arg;
-       isc_nm_cb_t cb = conn->cb;
-       void *cbarg = conn->cbarg;
-       size_t extrahandlesize = conn->extrahandlesize;
-       isc_nmsocket_t *dnssock = NULL;
-       isc_nmhandle_t *readhandle = NULL;
-
-       REQUIRE(result != ISC_R_SUCCESS || VALID_NMHANDLE(handle));
-
-       isc_mem_putanddetach(&conn->mctx, conn, sizeof(*conn));
-
-       dnssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*dnssock));
-       isc__nmsocket_init(dnssock, handle->sock->mgr, isc_nm_tlsdnssocket,
-                          handle->sock->iface);
-
-       dnssock->extrahandlesize = extrahandlesize;
-       isc_nmhandle_attach(handle, &dnssock->outerhandle);
-
-       dnssock->peer = handle->sock->peer;
-       dnssock->read_timeout = atomic_load(&handle->sock->mgr->init);
-       dnssock->tid = isc_nm_tid();
-
-       atomic_init(&dnssock->client, true);
-
-       readhandle = isc__nmhandle_get(dnssock, NULL, NULL);
+void
+isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
 
-       if (result != ISC_R_SUCCESS) {
-               cb(readhandle, result, cbarg);
-               isc__nmsocket_detach(&dnssock);
-               isc_nmhandle_detach(&readhandle);
+       /*
+        * If the socket is active, mark it inactive and
+        * continue. If it isn't active, stop now.
+        */
+       if (!isc__nmsocket_deactivate(sock)) {
                return;
        }
 
-       INSIST(dnssock->statichandle != NULL);
-       INSIST(dnssock->statichandle == readhandle);
-       INSIST(readhandle->sock == dnssock);
-       INSIST(dnssock->recv_cb == NULL);
+       if (atomic_load(&sock->connecting) || sock->accepting) {
+               return;
+       }
 
-       uv_timer_init(&dnssock->mgr->workers[isc_nm_tid()].loop,
-                     &dnssock->timer);
-       dnssock->timer.data = dnssock;
-       dnssock->timer_initialized = true;
-       uv_timer_start(&dnssock->timer, dnstcp_readtimeout,
-                      dnssock->read_timeout, 0);
-       dnssock->timer_running = true;
+       if (sock->tls.pending_req) {
+               isc__nm_uvreq_t *req = sock->tls.pending_req;
+               sock->tls.pending_req = NULL;
+               failed_connect_cb(sock, req, ISC_R_CANCELED);
+       }
 
-       /*
-        * The connection is now established; we start reading immediately,
-        * before we've been asked to. We'll read and buffer at most one
-        * packet.
-        */
-       isc_nm_read(handle, dnslisten_readcb, dnssock);
-       cb(readhandle, ISC_R_SUCCESS, cbarg);
+       if (sock->statichandle) {
+               failed_read_cb(sock, ISC_R_CANCELED);
+               return;
+       }
 
        /*
-        * The sock is now attached to the handle.
+        * Otherwise, we just send the socket to abyss...
         */
-       isc__nmsocket_detach(&dnssock);
-}
-
-isc_result_t
-isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
-                    isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
-                    size_t extrahandlesize) {
-       isc_result_t result = ISC_R_SUCCESS;
-       tcpconnect_t *conn = isc_mem_get(mgr->mctx, sizeof(tcpconnect_t));
-       SSL_CTX *ctx = NULL;
-
-       *conn = (tcpconnect_t){ .cb = cb,
-                               .cbarg = cbarg,
-                               .extrahandlesize = extrahandlesize };
-       isc_mem_attach(mgr->mctx, &conn->mctx);
-
-       ctx = SSL_CTX_new(SSLv23_client_method());
-       result = isc_nm_tlsconnect(mgr, local, peer, tlsdnsconnect_cb, conn,
-                                  ctx, timeout, 0);
-       SSL_CTX_free(ctx);
-       if (result != ISC_R_SUCCESS) {
-               isc_mem_putanddetach(&conn->mctx, conn, sizeof(*conn));
+       if (sock->parent == NULL) {
+               isc__nmsocket_prep_destroy(sock);
        }
-       return (result);
 }
 
 void
-isc__nm_tlsdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle) {
        isc_nmsocket_t *sock = NULL;
-       isc__netievent_tlsdnsread_t *ievent = NULL;
-       isc_nmhandle_t *eventhandle = NULL;
+       isc__netievent_tlsdnscancel_t *ievent = NULL;
 
        REQUIRE(VALID_NMHANDLE(handle));
 
        sock = handle->sock;
 
-       REQUIRE(sock->statichandle == handle);
        REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(sock->recv_cb == NULL);
-       REQUIRE(sock->tid == isc_nm_tid());
-       REQUIRE(atomic_load(&sock->client));
-
-       if (inactive(sock)) {
-               cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
-               return;
-       }
-
-       /*
-        * This MUST be done asynchronously, no matter which thread we're
-        * in. The callback function for isc_nm_read() often calls
-        * isc_nm_read() again; if we tried to do that synchronously
-        * we'd clash in processbuffer() and grow the stack indefinitely.
-        */
-       ievent = isc__nm_get_ievent(sock->mgr, netievent_tlsdnsread);
-       isc__nmsocket_attach(sock, &ievent->sock);
-
-       sock->recv_cb = cb;
-       sock->recv_cbarg = cbarg;
-
-       sock->read_timeout = (atomic_load(&sock->keepalive)
-                                     ? atomic_load(&sock->mgr->keepalive)
-                                     : atomic_load(&sock->mgr->idle));
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
 
-       /*
-        * Add a reference to the handle to keep it from being freed by
-        * the caller; it will be detached in in isc__nm_async_tlsdnsread().
-        */
-       isc_nmhandle_attach(handle, &eventhandle);
+       ievent = isc__nm_get_netievent_tlsdnscancel(sock->mgr, sock, handle);
        isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
                               (isc__netievent_t *)ievent);
 }
 
 void
-isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc_result_t result;
-       isc__netievent_tlsdnsread_t *ievent =
-               (isc__netievent_tlsdnsclose_t *)ev0;
+isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_tlsdnscancel_t *ievent =
+               (isc__netievent_tlsdnscancel_t *)ev0;
        isc_nmsocket_t *sock = ievent->sock;
-       isc_nmhandle_t *handle = NULL, *newhandle = NULL;
+
+       UNUSED(worker);
 
        REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(worker->id == sock->tid);
        REQUIRE(sock->tid == isc_nm_tid());
 
-       handle = sock->statichandle;
-
-       if (inactive(sock)) {
-               isc_nm_recv_cb_t cb = sock->recv_cb;
-               void *cbarg = sock->recv_cbarg;
+       failed_read_cb(sock, ISC_R_EOF);
+}
 
-               isc__nmsocket_clearcb(sock);
-               if (cb != NULL) {
-                       cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
-               }
+void
+isc__nm_tlsdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
+       isc_nmsocket_t *sock = NULL;
 
-               isc_nmhandle_detach(&handle);
-               return;
-       }
+       REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NMSOCK(handle->sock));
 
-       /*
-        * Maybe we have a packet already?
-        */
-       result = processbuffer(sock, &newhandle);
-       if (result == ISC_R_SUCCESS) {
-               if (sock->timer_initialized) {
-                       uv_timer_stop(&sock->timer);
-               }
-               isc_nmhandle_detach(&newhandle);
-       } else if (sock->outerhandle != NULL) {
-               /* Restart reading, wait for the callback */
-               if (sock->timer_initialized) {
-                       uv_timer_start(&sock->timer, dnstcp_readtimeout,
-                                      sock->read_timeout, 0);
-                       sock->timer_running = true;
-               }
-               isc_nm_resumeread(sock->outerhandle);
-       } else {
-               isc_nm_recv_cb_t cb = sock->recv_cb;
-               void *cbarg = sock->recv_cbarg;
+       sock = handle->sock;
 
-               isc__nmsocket_clearcb(sock);
-               cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
+       sock->read_timeout = timeout;
+       if (uv_is_active((uv_handle_t *)&sock->timer)) {
+               start_sock_timer(sock);
        }
-
-       isc_nmhandle_detach(&handle);
 }
 
 void
-isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle) {
+isc_nm_tlsdns_sequential(isc_nmhandle_t *handle) {
        isc_nmsocket_t *sock = NULL;
-       isc__netievent_tlsdnscancel_t *ievent = NULL;
 
        REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NMSOCK(handle->sock));
+       REQUIRE(handle->sock->type == isc_nm_tlsdnssocket);
 
        sock = handle->sock;
 
-       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       /*
+        * We don't want pipelining on this connection. That means
+        * that we need to pause after reading each request, and
+        * resume only after the request has been processed. This
+        * is done in resume_processing(), which is the socket's
+        * closehandle_cb callback, called whenever a handle
+        * is released.
+        */
 
-       ievent = isc__nm_get_ievent(sock->mgr, netievent_tlsdnscancel);
-       isc__nmsocket_attach(sock, &ievent->sock);
-       isc_nmhandle_attach(handle, &ievent->handle);
-       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
-                              (isc__netievent_t *)ievent);
+       stop_reading(sock);
+       atomic_store(&sock->sequential, true);
 }
 
 void
-isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) {
-       isc__netievent_tlsdnscancel_t *ievent =
-               (isc__netievent_tlsdnscancel_t *)ev0;
-       isc_nmsocket_t *sock = ievent->sock;
-       isc_nmhandle_t *handle = ievent->handle;
+isc_nm_tlsdns_keepalive(isc_nmhandle_t *handle, bool value) {
+       isc_nmsocket_t *sock = NULL;
 
-       REQUIRE(VALID_NMSOCK(sock));
-       REQUIRE(worker->id == sock->tid);
-       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NMSOCK(handle->sock));
+       REQUIRE(handle->sock->type != isc_nm_tlsdnssocket);
 
-       if (atomic_load(&sock->client)) {
-               isc_nm_recv_cb_t cb;
-               void *cbarg = NULL;
+       sock = handle->sock;
 
-               cb = sock->recv_cb;
-               cbarg = sock->recv_cbarg;
-               isc__nmsocket_clearcb(sock);
+       atomic_store(&sock->keepalive, value);
+}
 
-               if (cb != NULL) {
-                       cb(handle, ISC_R_EOF, NULL, cbarg);
-               }
+static void
+process_sock_buffer(isc_nmsocket_t *sock) {
+       /*
+        * 1. When process_buffer receives incomplete DNS message,
+        *    we don't touch any timers
+        *
+        * 2. When we receive at least one full DNS message, we stop the timers
+        *    until resume_processing calls this function again and restarts the
+        *    reading and the timers
+        */
 
-               isc__nm_tcp_cancelread(sock->outerhandle);
+       /*
+        * Process a DNS messages.  Stop if this is client socket, or the server
+        * socket has been set to sequential mode or the number of queries we
+        * are processing simultaneously have reached the clients-per-connection
+        * limit.
+        */
+       for (;;) {
+               isc_result_t result = processbuffer(sock);
+               switch (result) {
+               case ISC_R_NOMORE:
+                       start_reading(sock);
+                       return;
+               case ISC_R_CANCELED:
+                       stop_reading(sock);
+                       return;
+               case ISC_R_SUCCESS:
+                       if (atomic_load(&sock->client) ||
+                           atomic_load(&sock->sequential) ||
+                           atomic_load(&sock->ah) >= TLSDNS_CLIENTS_PER_CONN)
+                       {
+                               stop_reading(sock);
+                               return;
+                       }
+                       break;
+               default:
+                       INSIST(0);
+               }
        }
 }
 
-void
-isc__nm_tlsdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
-       isc_nmsocket_t *sock = NULL;
-
-       REQUIRE(VALID_NMHANDLE(handle));
+static void
+resume_processing(void *arg) {
+       isc_nmsocket_t *sock = (isc_nmsocket_t *)arg;
 
-       sock = handle->sock;
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->tid == isc_nm_tid());
+       REQUIRE(sock->type == isc_nm_tlsdnssocket);
+       REQUIRE(!atomic_load(&sock->client));
 
-       if (sock->outerhandle != NULL) {
-               isc__nm_tcp_settimeout(sock->outerhandle, timeout);
+       if (inactive(sock)) {
+               return;
        }
 
-       sock->read_timeout = timeout;
-       if (sock->timer_running) {
-               uv_timer_start(&sock->timer, dnstcp_readtimeout,
-                              sock->read_timeout, 0);
+       if (atomic_load(&sock->ah) == 0) {
+               /* Nothing is active; sockets can timeout now */
+               start_sock_timer(sock);
        }
+
+       process_sock_buffer(sock);
 }
index 7890b0cfb11f1f4c0850cb249f71851d2a1771ca..bc1baa92bbb3d0efd02e360f44efdc00e5b13891 100644 (file)
@@ -9,7 +9,7 @@
  * information regarding copyright ownership.
  */
 
-#include "openssl_shim.h"
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 #include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+
+#include "openssl_shim.h"
 
 #if !HAVE_CRYPTO_ZALLOC
 void *
-CRYPTO_zalloc(size_t size) {
-       void *ret = OPENSSL_malloc(size);
+CRYPTO_zalloc(size_t num, const char *file, int line) {
+       void *ret = CRYPTO_malloc(num, file, line);
        if (ret != NULL) {
-               memset(ret, 0, size);
+               memset(ret, 0, num);
        }
        return (ret);
 }
@@ -114,3 +117,106 @@ HMAC_CTX_get_md(const HMAC_CTX *ctx) {
        return (ctx->md);
 }
 #endif /* if !HAVE_HMAC_CTX_GET_MD */
+
+#if !HAVE_SSL_READ_EX
+int
+SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes) {
+       int rv = SSL_read(ssl, buf, num);
+       if (rv > 0) {
+               *readbytes = rv;
+               rv = 1;
+       }
+
+       return (rv);
+}
+#endif
+
+#if !HAVE_SSL_PEEK_EX
+int
+SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes) {
+       int rv = SSL_peek(ssl, buf, num);
+       if (rv > 0) {
+               *readbytes = rv;
+               rv = 1;
+       }
+
+       return (rv);
+}
+#endif
+
+#if !HAVE_SSL_WRITE_EX
+int
+SSL_write_ex(SSL *ssl, const void *buf, size_t num, size_t *written) {
+       int rv = SSL_write(ssl, buf, num);
+       if (rv > 0) {
+               *written = rv;
+               rv = 1;
+       }
+
+       return (rv);
+}
+#endif
+
+#if !HAVE_BIO_READ_EX
+int
+BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes) {
+       int rv = BIO_read(b, data, dlen);
+       if (rv > 0) {
+               *readbytes = rv;
+               rv = 1;
+       }
+
+       return (rv);
+}
+#endif
+
+#if !HAVE_BIO_WRITE_EX
+int
+BIO_write_ex(BIO *b, const void *data, size_t dlen, size_t *written) {
+       int rv = BIO_write(b, data, dlen);
+       if (rv > 0) {
+               *written = rv;
+               rv = 1;
+       }
+
+       return (rv);
+}
+#endif
+
+#if !HAVE_OPENSSL_INIT_CRYPTO
+int
+OPENSSL_init_crypto(uint64_t opts, const void *settings) {
+       (void)settings;
+
+       if ((opts & OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS) == 0) {
+               ERR_load_crypto_strings();
+       }
+
+       if ((opts & (OPENSSL_INIT_NO_ADD_ALL_CIPHERS |
+                    OPENSSL_INIT_NO_ADD_ALL_CIPHERS)) == 0)
+       {
+               OpenSSL_add_all_algorithms();
+       } else if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS) == 0) {
+               OpenSSL_add_all_digests();
+       } else if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS) == 0) {
+               OpenSSL_add_all_ciphers();
+       }
+
+       return (1);
+}
+#endif
+
+#if !HAVE_OPENSSL_INIT_SSL
+int
+OPENSSL_init_ssl(uint64_t opts, const void *settings) {
+       OPENSSL_init_crypto(opts, settings);
+
+       SSL_library_init();
+
+       if ((opts & OPENSSL_INIT_NO_LOAD_SSL_STRINGS) == 0) {
+               SSL_load_error_strings();
+       }
+
+       return (1);
+}
+#endif
index 038df12875d75e557f4200286458aaf6db122335..bd5a137e34b2061546912be15e090b584c486a21 100644 (file)
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
 #include <openssl/opensslv.h>
+#include <openssl/ssl.h>
 
 #if !HAVE_CRYPTO_ZALLOC
 void *
-CRYPTO_zalloc(size_t size);
-#define OPENSSL_zalloc(num) CRYPTO_zalloc(num)
+CRYPTO_zalloc(size_t num, const char *file, int line);
 #endif /* if !HAVE_CRYPTO_ZALLOC */
 
+#if !defined(OPENSSL_zalloc)
+#define OPENSSL_zalloc(num) CRYPTO_zalloc(num, __FILE__, __LINE__)
+#endif
+
 #if !HAVE_EVP_CIPHER_CTX_NEW
 EVP_CIPHER_CTX *
 EVP_CIPHER_CTX_new(void);
@@ -67,3 +71,58 @@ HMAC_CTX_reset(HMAC_CTX *ctx);
 const EVP_MD *
 HMAC_CTX_get_md(const HMAC_CTX *ctx);
 #endif /* if !HAVE_HMAC_CTX_GET_MD */
+
+#if !HAVE_SSL_READ_EX
+int
+SSL_read_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
+#endif
+
+#if !HAVE_SSL_PEEK_EX
+int
+SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes);
+#endif
+
+#if !HAVE_SSL_WRITE_EX
+int
+SSL_write_ex(SSL *ssl, const void *buf, size_t num, size_t *written);
+#endif
+
+#if !HAVE_BIO_READ_EX
+int
+BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes);
+#endif
+
+#if !HAVE_BIO_WRITE_EX
+int
+BIO_write_ex(BIO *b, const void *data, size_t dlen, size_t *written);
+#endif
+
+#if !HAVE_OPENSSL_INIT_CRYPTO
+
+#define OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS 0x00000001L
+#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS    0x00000002L
+#define OPENSSL_INIT_ADD_ALL_CIPHERS       0x00000004L
+#define OPENSSL_INIT_ADD_ALL_DIGESTS       0x00000008L
+#define OPENSSL_INIT_NO_ADD_ALL_CIPHERS            0x00000010L
+#define OPENSSL_INIT_NO_ADD_ALL_DIGESTS            0x00000020L
+
+int
+OPENSSL_init_crypto(uint64_t opts, const void *settings);
+#endif
+
+#if !HAVE_OPENSSL_INIT_SSL
+#define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L
+#define OPENSSL_INIT_LOAD_SSL_STRINGS   0x00200000L
+
+int
+OPENSSL_init_ssl(uint64_t opts, const void *settings);
+
+#endif
+
+#if !HAVE_TLS_SERVER_METHOD
+#define TLS_server_method SSLv23_server_method
+#endif
+
+#if !HAVE_TLS_CLIENT_METHOD
+#define TLS_client_method SSLv23_client_method
+#endif
index 3f08f1685db9d35a5ad0830729d93c8680ead9f3..c8d07aa28819acb9b036c5a69fc0d016a7a4b747 100644 (file)
@@ -15,7 +15,7 @@ libisctest_la_SOURCES =       \
        isctest.h               \
        uv_wrap.h
 
-check_PROGRAMS =       \
+TESTS =                        \
        aes_test        \
        buffer_test     \
        counter_test    \
@@ -47,11 +47,13 @@ check_PROGRAMS =    \
        tcp_test        \
        tcp_quota_test  \
        tcpdns_test     \
+       tlsdns_test     \
        time_test       \
        timer_test      \
        udp_test
 
-TESTS = $(check_PROGRAMS)
+check_PROGRAMS =       \
+       $(TESTS)
 
 hmac_test_CPPFLAGS =   \
        $(AM_CPPFLAGS)  \
@@ -100,6 +102,15 @@ tcpdns_test_LDADD =        \
        $(LDADD)        \
        $(LIBUV_LIBS)
 
+tlsdns_test_CPPFLAGS = \
+       $(AM_CPPFLAGS)  \
+       $(OPENSSL_CFLAGS)       \
+       $(LIBUV_CFLAGS)
+
+tlsdns_test_LDADD =    \
+       $(LDADD)        \
+       $(LIBUV_LIBS)
+
 udp_test_CPPFLAGS =    \
        $(AM_CPPFLAGS)  \
        $(OPENSSL_CFLAGS)       \
diff --git a/lib/isc/tests/tlsdns_test.c b/lib/isc/tests/tlsdns_test.c
new file mode 100644 (file)
index 0000000..9e1f6f5
--- /dev/null
@@ -0,0 +1,899 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#if HAVE_CMOCKA
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <uv.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/atomic.h>
+#include <isc/buffer.h>
+#include <isc/condition.h>
+#include <isc/mutex.h>
+#include <isc/netmgr.h>
+#include <isc/nonce.h>
+#include <isc/os.h>
+#include <isc/refcount.h>
+#include <isc/sockaddr.h>
+#include <isc/thread.h>
+
+#include "../netmgr/netmgr-int.h"
+#include "isctest.h"
+
+#define MAX_NM 2
+
+static isc_sockaddr_t tlsdns_listen_addr;
+static isc_tlsctx_t *tlsdns_listen_ctx = NULL;
+static isc_tlsctx_t *tlsdns_connect_ctx = NULL;
+
+static uint64_t send_magic = 0;
+static uint64_t stop_magic = 0;
+
+static uv_buf_t send_msg = { .base = (char *)&send_magic,
+                            .len = sizeof(send_magic) };
+
+static uv_buf_t stop_msg = { .base = (char *)&stop_magic,
+                            .len = sizeof(stop_magic) };
+
+static atomic_uint_fast64_t nsends;
+
+static atomic_uint_fast64_t ssends;
+static atomic_uint_fast64_t sreads;
+
+static atomic_uint_fast64_t cconnects;
+static atomic_uint_fast64_t csends;
+static atomic_uint_fast64_t creads;
+static atomic_uint_fast64_t ctimeouts;
+
+static unsigned int workers = 1;
+
+static bool reuse_supported = true;
+
+#define NSENDS 100
+#define NWRITES 10
+
+#define CHECK_RANGE_FULL(v)                                       \
+       {                                                         \
+               int __v = atomic_load(&v);                        \
+               assert_true(__v > 1);                             \
+               assert_true(__v <= NSENDS * NWRITES * 110 / 100); \
+       }
+
+#define CHECK_RANGE_HALF(v)                                      \
+       {                                                        \
+               int __v = atomic_load(&v);                       \
+               assert_true(__v > 1);                            \
+               assert_true(__v <= NSENDS * NWRITES * 60 / 100); \
+       }
+
+/* Enable this to print values while running tests */
+#undef PRINT_DEBUG
+#ifdef PRINT_DEBUG
+#define X(v) fprintf(stderr, #v " = %" PRIu64 "\n", atomic_load(&v))
+#else
+#define X(v)
+#endif
+
+static int
+setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
+       isc_result_t result;
+       socklen_t addrlen = sizeof(*addr);
+       int fd;
+       int r;
+
+       isc_sockaddr_fromin6(addr, &in6addr_loopback, 0);
+
+       fd = socket(AF_INET6, family, 0);
+       if (fd < 0) {
+               perror("setup_ephemeral_port: socket()");
+               return (-1);
+       }
+
+       r = bind(fd, (const struct sockaddr *)&addr->type.sa,
+                sizeof(addr->type.sin6));
+       if (r != 0) {
+               perror("setup_ephemeral_port: bind()");
+               close(fd);
+               return (r);
+       }
+
+       r = getsockname(fd, (struct sockaddr *)&addr->type.sa, &addrlen);
+       if (r != 0) {
+               perror("setup_ephemeral_port: getsockname()");
+               close(fd);
+               return (r);
+       }
+
+       result = isc__nm_socket_reuse(fd);
+       if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
+               fprintf(stderr,
+                       "setup_ephemeral_port: isc__nm_socket_reuse(): %s",
+                       isc_result_totext(result));
+               close(fd);
+               return (-1);
+       }
+
+       result = isc__nm_socket_reuse_lb(fd);
+       if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
+               fprintf(stderr,
+                       "setup_ephemeral_port: isc__nm_socket_reuse_lb(): %s",
+                       isc_result_totext(result));
+               close(fd);
+               return (-1);
+       }
+       if (result == ISC_R_NOTIMPLEMENTED) {
+               reuse_supported = false;
+       }
+
+#if IPV6_RECVERR
+#define setsockopt_on(socket, level, name) \
+       setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
+
+       r = setsockopt_on(fd, IPPROTO_IPV6, IPV6_RECVERR);
+       if (r != 0) {
+               perror("setup_ephemeral_port");
+               close(fd);
+               return (r);
+       }
+#endif
+
+       return (fd);
+}
+
+static int
+_setup(void **state) {
+       UNUSED(state);
+
+       /* workers = isc_os_ncpus(); */
+
+       if (isc_test_begin(NULL, true, workers) != ISC_R_SUCCESS) {
+               return (-1);
+       }
+
+       signal(SIGPIPE, SIG_IGN);
+
+       return (0);
+}
+
+static int
+_teardown(void **state) {
+       UNUSED(state);
+
+       isc_test_end();
+
+       return (0);
+}
+
+/* Generic */
+
+static void
+noop_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
+            void *cbarg) {
+       UNUSED(handle);
+       UNUSED(eresult);
+       UNUSED(region);
+       UNUSED(cbarg);
+}
+
+static unsigned int
+noop_accept_cb(isc_nmhandle_t *handle, unsigned int result, void *cbarg) {
+       UNUSED(handle);
+       UNUSED(result);
+       UNUSED(cbarg);
+
+       return (0);
+}
+
+static void
+noop_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
+       UNUSED(handle);
+       UNUSED(result);
+       UNUSED(cbarg);
+}
+
+thread_local uint8_t tlsdns_buffer_storage[4096];
+thread_local size_t tlsdns_buffer_length = 0;
+
+static int
+nm_setup(void **state) {
+       size_t nworkers = ISC_MAX(ISC_MIN(workers, 32), 1);
+       int tlsdns_listen_sock = -1;
+       isc_nm_t **nm = NULL;
+
+       isc_tlsctx_createserver(NULL, NULL, &tlsdns_listen_ctx);
+       isc_tlsctx_createclient(&tlsdns_connect_ctx);
+
+       tlsdns_listen_addr = (isc_sockaddr_t){ .length = 0 };
+       tlsdns_listen_sock = setup_ephemeral_port(&tlsdns_listen_addr,
+                                                 SOCK_STREAM);
+       if (tlsdns_listen_sock < 0) {
+               return (-1);
+       }
+       close(tlsdns_listen_sock);
+       tlsdns_listen_sock = -1;
+
+       atomic_store(&nsends, NSENDS * NWRITES);
+
+       atomic_store(&csends, 0);
+       atomic_store(&creads, 0);
+       atomic_store(&sreads, 0);
+       atomic_store(&ssends, 0);
+       atomic_store(&ctimeouts, 0);
+       atomic_store(&cconnects, 0);
+
+       isc_nonce_buf(&send_magic, sizeof(send_magic));
+       isc_nonce_buf(&stop_magic, sizeof(stop_magic));
+       if (send_magic == stop_magic) {
+               return (-1);
+       }
+
+       nm = isc_mem_get(test_mctx, MAX_NM * sizeof(nm[0]));
+       for (size_t i = 0; i < MAX_NM; i++) {
+               nm[i] = isc_nm_start(test_mctx, nworkers);
+               assert_non_null(nm[i]);
+               isc_nm_settimeouts(nm[i], 1000, 1000, 1000, 1000);
+       }
+
+       *state = nm;
+
+       return (0);
+}
+
+static int
+nm_teardown(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+
+       for (size_t i = 0; i < MAX_NM; i++) {
+               isc_nm_destroy(&nm[i]);
+               assert_null(nm[i]);
+       }
+       isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0]));
+
+       isc_tlsctx_free(&tlsdns_connect_ctx);
+       isc_tlsctx_free(&tlsdns_listen_ctx);
+
+       return (0);
+}
+
+thread_local size_t nwrites = NWRITES;
+
+/* TLSDNS */
+
+static void
+tlsdns_connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                      void *cbarg);
+
+static void
+tlsdns_connect_send(isc_nmhandle_t *handle);
+
+static void
+tlsdns_connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                      void *cbarg) {
+       assert_non_null(handle);
+
+       UNUSED(cbarg);
+
+       if (eresult == ISC_R_SUCCESS) {
+               atomic_fetch_add(&csends, 1);
+       } else {
+               /* Send failed, we need to stop reading too */
+               isc_nm_cancelread(handle);
+       }
+}
+
+static void
+tlsdns_connect_send(isc_nmhandle_t *handle) {
+       uint_fast64_t sends = atomic_load(&nsends);
+
+       /* Continue until we subtract or we are sent them all */
+       while (sends > 0) {
+               if (atomic_compare_exchange_weak(&nsends, &sends, sends - 1)) {
+                       sends--;
+                       break;
+               }
+       }
+
+       if (sends == 0) {
+               isc_nm_send(handle, (isc_region_t *)&stop_msg,
+                           tlsdns_connect_send_cb, NULL);
+       } else {
+               isc_nm_send(handle, (isc_region_t *)&send_msg,
+                           tlsdns_connect_send_cb, NULL);
+       }
+}
+
+static void
+tlsdns_connect_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                      isc_region_t *region, void *cbarg) {
+       uint64_t magic = 0;
+
+       UNUSED(cbarg);
+
+       assert_non_null(handle);
+
+       if (eresult != ISC_R_SUCCESS) {
+               goto unref;
+       }
+
+       assert_int_equal(region->length, sizeof(magic));
+
+       atomic_fetch_add(&creads, 1);
+
+       magic = *(uint64_t *)region->base;
+
+       assert_true(magic == stop_magic || magic == send_magic);
+
+unref:
+       isc_nmhandle_detach(&handle);
+}
+
+static void
+tlsdns_connect_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                         void *cbarg) {
+       isc_nmhandle_t *readhandle = NULL;
+       UNUSED(cbarg);
+
+       if (eresult != ISC_R_SUCCESS) {
+               uint_fast64_t sends = atomic_load(&nsends);
+
+               /* We failed to connect; try again */
+               while (sends > 0) {
+                       /* Continue until we subtract or we are done */
+                       if (atomic_compare_exchange_weak(&nsends, &sends,
+                                                        sends - 1)) {
+                               sends--;
+                               break;
+                       }
+               }
+               return;
+       }
+
+       atomic_fetch_add(&cconnects, 1);
+
+       isc_nmhandle_attach(handle, &readhandle);
+       isc_nm_read(handle, tlsdns_connect_read_cb, NULL);
+
+       tlsdns_connect_send(handle);
+}
+
+static void
+tlsdns_noop(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       isc_sockaddr_t tlsdns_connect_addr;
+
+       tlsdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
+       isc_sockaddr_fromin6(&tlsdns_connect_addr, &in6addr_loopback, 0);
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr, noop_recv_cb,
+               NULL, noop_accept_cb, NULL, 0, 0, NULL, tlsdns_listen_ctx,
+               &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+
+       (void)isc_nm_tlsdnsconnect(
+               connect_nm, (isc_nmiface_t *)&tlsdns_connect_addr,
+               (isc_nmiface_t *)&tlsdns_listen_addr, noop_connect_cb, NULL,
+               1000, 0, tlsdns_connect_ctx);
+       isc_nm_closedown(connect_nm);
+
+       assert_int_equal(0, atomic_load(&cconnects));
+       assert_int_equal(0, atomic_load(&csends));
+       assert_int_equal(0, atomic_load(&creads));
+       assert_int_equal(0, atomic_load(&ctimeouts));
+       assert_int_equal(0, atomic_load(&sreads));
+       assert_int_equal(0, atomic_load(&ssends));
+}
+
+static void
+tlsdns_noresponse(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       isc_sockaddr_t tlsdns_connect_addr;
+
+       tlsdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
+       isc_sockaddr_fromin6(&tlsdns_connect_addr, &in6addr_loopback, 0);
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr, noop_recv_cb,
+               NULL, noop_accept_cb, NULL, 0, 0, NULL, tlsdns_listen_ctx,
+               &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       (void)isc_nm_tlsdnsconnect(
+               connect_nm, (isc_nmiface_t *)&tlsdns_connect_addr,
+               (isc_nmiface_t *)&tlsdns_listen_addr, tlsdns_connect_connect_cb,
+               NULL, 1000, 0, tlsdns_connect_ctx);
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+       isc_nm_closedown(connect_nm);
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       assert_true(atomic_load(&cconnects) <= 1);
+       assert_true(atomic_load(&csends) <= 1);
+       assert_int_equal(0, atomic_load(&creads));
+       assert_int_equal(0, atomic_load(&ctimeouts));
+       assert_int_equal(0, atomic_load(&sreads));
+       assert_int_equal(0, atomic_load(&ssends));
+}
+
+static void
+tlsdns_listen_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                     isc_region_t *region, void *cbarg);
+
+static void
+tlsdns_listen_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                     void *cbarg) {
+       UNUSED(cbarg);
+       UNUSED(eresult);
+
+       assert_non_null(handle);
+
+       if (eresult != ISC_R_SUCCESS) {
+               return;
+       }
+
+       atomic_fetch_add(&ssends, 1);
+}
+
+static void
+tlsdns_listen_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                     isc_region_t *region, void *cbarg) {
+       uint64_t magic = 0;
+
+       UNUSED(cbarg);
+
+       assert_non_null(handle);
+
+       if (eresult != ISC_R_SUCCESS) {
+               return;
+       }
+
+       atomic_fetch_add(&sreads, 1);
+
+       assert_int_equal(region->length, sizeof(magic));
+
+       magic = *(uint64_t *)region->base;
+       assert_true(magic == stop_magic || magic == send_magic);
+
+       if (magic == send_magic) {
+               isc_nm_send(handle, region, tlsdns_listen_send_cb, NULL);
+               return;
+       } else if (magic == stop_magic) {
+               /* We are done, we don't send anything back */
+       }
+}
+
+static isc_result_t
+tlsdns_listen_accept_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                       void *cbarg) {
+       UNUSED(handle);
+       UNUSED(cbarg);
+
+       return (eresult);
+}
+
+static isc_threadresult_t
+tlsdns_connect_thread(isc_threadarg_t arg) {
+       isc_nm_t *connect_nm = (isc_nm_t *)arg;
+       isc_sockaddr_t tlsdns_connect_addr;
+
+       tlsdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
+       isc_sockaddr_fromin6(&tlsdns_connect_addr, &in6addr_loopback, 0);
+
+       while (atomic_load(&nsends) > 0) {
+               (void)isc_nm_tlsdnsconnect(
+                       connect_nm, (isc_nmiface_t *)&tlsdns_connect_addr,
+                       (isc_nmiface_t *)&tlsdns_listen_addr,
+                       tlsdns_connect_connect_cb, NULL, 1000, 0,
+                       tlsdns_connect_ctx);
+       }
+
+       return ((isc_threadresult_t)0);
+}
+
+static void
+tlsdns_recv_one(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       isc_sockaddr_t tlsdns_connect_addr;
+
+       tlsdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
+       isc_sockaddr_fromin6(&tlsdns_connect_addr, &in6addr_loopback, 0);
+
+       atomic_store(&nsends, 1);
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr,
+               tlsdns_listen_read_cb, NULL, tlsdns_listen_accept_cb, NULL, 0,
+               0, NULL, tlsdns_listen_ctx, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       (void)isc_nm_tlsdnsconnect(
+               connect_nm, (isc_nmiface_t *)&tlsdns_connect_addr,
+               (isc_nmiface_t *)&tlsdns_listen_addr, tlsdns_connect_connect_cb,
+               NULL, 1000, 0, tlsdns_connect_ctx);
+
+       while (atomic_load(&nsends) > 0) {
+               isc_thread_yield();
+       }
+
+       while (atomic_load(&cconnects) != 1 || atomic_load(&ssends) != 0 ||
+              atomic_load(&sreads) != 1 || atomic_load(&creads) != 0 ||
+              atomic_load(&csends) != 1)
+       {
+               isc_thread_yield();
+       }
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+       isc_nm_closedown(connect_nm);
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       assert_int_equal(atomic_load(&cconnects), 1);
+       assert_int_equal(atomic_load(&csends), 1);
+       assert_int_equal(atomic_load(&creads), 0);
+       assert_int_equal(atomic_load(&ctimeouts), 0);
+       assert_int_equal(atomic_load(&sreads), 1);
+       assert_int_equal(atomic_load(&ssends), 0);
+}
+
+static void
+tlsdns_recv_two(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       isc_sockaddr_t tlsdns_connect_addr;
+
+       if (!reuse_supported) {
+               skip();
+               return;
+       }
+
+       tlsdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
+       isc_sockaddr_fromin6(&tlsdns_connect_addr, &in6addr_loopback, 0);
+
+       atomic_store(&nsends, 2);
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr,
+               tlsdns_listen_read_cb, NULL, tlsdns_listen_accept_cb, NULL, 0,
+               0, NULL, tlsdns_listen_ctx, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       result = isc_nm_tlsdnsconnect(
+               connect_nm, (isc_nmiface_t *)&tlsdns_connect_addr,
+               (isc_nmiface_t *)&tlsdns_listen_addr, tlsdns_connect_connect_cb,
+               NULL, 1000, 0, tlsdns_connect_ctx);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       isc_nm_settimeouts(connect_nm, 1000, 1000, 1000, 1000);
+
+       result = isc_nm_tlsdnsconnect(
+               connect_nm, (isc_nmiface_t *)&tlsdns_connect_addr,
+               (isc_nmiface_t *)&tlsdns_listen_addr, tlsdns_connect_connect_cb,
+               NULL, 1000, 0, tlsdns_connect_ctx);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       while (atomic_load(&nsends) > 0) {
+               isc_thread_yield();
+       }
+
+       while (atomic_load(&sreads) != 2 || atomic_load(&ssends) != 1 ||
+              atomic_load(&csends) != 2 || atomic_load(&creads) != 1)
+       {
+               isc_thread_yield();
+       }
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+       isc_nm_closedown(connect_nm);
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       assert_int_equal(atomic_load(&cconnects), 2);
+       assert_int_equal(atomic_load(&csends), 2);
+       assert_int_equal(atomic_load(&creads), 1);
+       assert_int_equal(atomic_load(&ctimeouts), 0);
+       assert_int_equal(atomic_load(&sreads), 2);
+       assert_int_equal(atomic_load(&ssends), 1);
+}
+
+static void
+tlsdns_recv_send(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+       isc_thread_t threads[32] = { 0 };
+
+       if (!reuse_supported) {
+               skip();
+               return;
+       }
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr,
+               tlsdns_listen_read_cb, NULL, tlsdns_listen_accept_cb, NULL, 0,
+               0, NULL, tlsdns_listen_ctx, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_create(tlsdns_connect_thread, connect_nm,
+                                 &threads[i]);
+       }
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_join(threads[i], NULL);
+       }
+
+       isc_nm_closedown(connect_nm);
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       CHECK_RANGE_FULL(csends);
+       CHECK_RANGE_FULL(creads);
+       CHECK_RANGE_FULL(sreads);
+       CHECK_RANGE_FULL(ssends);
+}
+
+#if 0
+static void
+tlsdns_recv_half_send(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+       isc_thread_t threads[32] = { 0 };
+
+       if (!reuse_supported) {
+               skip();
+               return;
+       }
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr,
+               tlsdns_listen_read_cb, NULL, tlsdns_listen_accept_cb, NULL, 0,
+               0, NULL, tlsdns_listen_ctx, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_create(tlsdns_connect_thread, connect_nm,
+                                 &threads[i]);
+       }
+
+       while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
+               isc_thread_yield();
+       }
+
+       isc_nm_closedown(connect_nm);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_join(threads[i], NULL);
+       }
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       CHECK_RANGE_HALF(csends);
+       CHECK_RANGE_HALF(creads);
+       CHECK_RANGE_HALF(sreads);
+       CHECK_RANGE_HALF(ssends);
+}
+
+static void
+tlsdns_half_recv_send(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+       isc_thread_t threads[32] = { 0 };
+
+       if (!reuse_supported) {
+               skip();
+               return;
+       }
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr,
+               tlsdns_listen_read_cb, NULL, tlsdns_listen_accept_cb, NULL, 0,
+               0, NULL, tlsdns_listen_ctx, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_create(tlsdns_connect_thread, connect_nm,
+                                 &threads[i]);
+       }
+
+       while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
+               isc_thread_yield();
+       }
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_join(threads[i], NULL);
+       }
+
+       isc_nm_closedown(connect_nm);
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       CHECK_RANGE_HALF(csends);
+       CHECK_RANGE_HALF(creads);
+       CHECK_RANGE_HALF(sreads);
+       CHECK_RANGE_HALF(ssends);
+}
+
+static void
+tlsdns_half_recv_half_send(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
+       isc_thread_t threads[32] = { 0 };
+
+       if (!reuse_supported) {
+               skip();
+               return;
+       }
+
+       result = isc_nm_listentlsdns(
+               listen_nm, (isc_nmiface_t *)&tlsdns_listen_addr,
+               tlsdns_listen_read_cb, NULL, tlsdns_listen_accept_cb, NULL, 0,
+               0, NULL, tlsdns_listen_ctx, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_create(tlsdns_connect_thread, connect_nm,
+                                 &threads[i]);
+       }
+
+       while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
+               isc_thread_yield();
+       }
+
+       isc_nm_closedown(connect_nm);
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+
+       for (size_t i = 0; i < nthreads; i++) {
+               isc_thread_join(threads[i], NULL);
+       }
+
+       X(cconnects);
+       X(csends);
+       X(creads);
+       X(ctimeouts);
+       X(sreads);
+       X(ssends);
+
+       CHECK_RANGE_HALF(csends);
+       CHECK_RANGE_HALF(creads);
+       CHECK_RANGE_HALF(sreads);
+       CHECK_RANGE_HALF(ssends);
+}
+#endif
+
+int
+main(void) {
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test_setup_teardown(tlsdns_recv_one, nm_setup,
+                                               nm_teardown),
+               cmocka_unit_test_setup_teardown(tlsdns_recv_two, nm_setup,
+                                               nm_teardown),
+               cmocka_unit_test_setup_teardown(tlsdns_noop, nm_setup,
+                                               nm_teardown),
+               cmocka_unit_test_setup_teardown(tlsdns_noresponse, nm_setup,
+                                               nm_teardown),
+               cmocka_unit_test_setup_teardown(tlsdns_recv_send, nm_setup,
+                                               nm_teardown),
+#if 0
+               cmocka_unit_test_setup_teardown(tlsdns_recv_half_send, nm_setup,
+                                               nm_teardown),
+               cmocka_unit_test_setup_teardown(tlsdns_half_recv_send, nm_setup,
+                                               nm_teardown),
+               cmocka_unit_test_setup_teardown(tlsdns_half_recv_half_send,
+                                               nm_setup, nm_teardown),
+#endif
+       };
+
+       return (cmocka_run_group_tests(tests, _setup, _teardown));
+}
+
+#else /* HAVE_CMOCKA */
+
+#include <stdio.h>
+
+int
+main(void) {
+       printf("1..0 # Skipped: cmocka not available\n");
+       return (0);
+}
+
+#endif /* if HAVE_CMOCKA */
diff --git a/lib/isc/tls.c b/lib/isc/tls.c
new file mode 100644 (file)
index 0000000..e45678a
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <openssl/err.h>
+#include <openssl/opensslv.h>
+
+#include <isc/atomic.h>
+#include <isc/log.h>
+#include <isc/mutex.h>
+#include <isc/mutexblock.h>
+#include <isc/once.h>
+#include <isc/thread.h>
+#include <isc/tls.h>
+#include <isc/util.h>
+
+#include "openssl_shim.h"
+
+static isc_once_t init_once = ISC_ONCE_INIT;
+static atomic_bool init_done = ATOMIC_VAR_INIT(false);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static isc_mem_t *isc__tls_mctx = NULL;
+static isc_mutex_t *locks = NULL;
+static int nlocks;
+
+static void
+isc__tls_lock_callback(int mode, int type, const char *file, int line) {
+       UNUSED(file);
+       UNUSED(line);
+       if ((mode & CRYPTO_LOCK) != 0) {
+               LOCK(&locks[type]);
+       } else {
+               UNLOCK(&locks[type]);
+       }
+}
+
+static void
+isc__tls_set_thread_id(CRYPTO_THREADID *id) {
+       CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
+}
+#endif
+
+static void
+isc__tls_initialize(void) {
+       REQUIRE(!atomic_load(&init_done));
+       RUNTIME_CHECK(OPENSSL_init_ssl(0, NULL) == 1);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+       isc_mem_create(&isc__tls_mctx);
+
+       nlocks = CRYPTO_num_locks();
+       locks = isc_mem_get(isc__tls_mctx, nlocks * sizeof(locks[0]));
+       isc_mutexblock_init(locks, nlocks);
+       CRYPTO_set_locking_callback(isc__tls_lock_callback);
+       CRYPTO_THREADID_set_callback(isc__tls_set_thread_id);
+       ERR_load_crypto_strings();
+#endif
+       atomic_store(&init_done, true);
+}
+
+void
+isc_tls_initialize(void) {
+       isc_result_t result = isc_once_do(&init_once, isc__tls_initialize);
+       REQUIRE(result == ISC_R_SUCCESS);
+       REQUIRE(atomic_load(&init_done));
+}
+
+void
+isc_tls_destroy(void) {
+       REQUIRE(atomic_load(&init_done));
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+       ERR_free_strings();
+
+       ERR_remove_thread_state(NULL);
+       CRYPTO_set_locking_callback(NULL);
+
+       if (locks != NULL) {
+               INSIST(isc__tls_mctx != NULL);
+               isc_mutexblock_destroy(locks, nlocks);
+               isc_mem_put(isc__tls_mctx, locks, nlocks * sizeof(locks[0]));
+               locks = NULL;
+       }
+
+       if (isc__tls_mctx != NULL) {
+               isc_mem_detach(&isc__tls_mctx);
+       }
+#endif
+}
+
+void
+isc_tlsctx_free(isc_tlsctx_t **ctxp) {
+       SSL_CTX *ctx = NULL;
+       REQUIRE(ctxp != NULL && *ctxp != NULL);
+
+       ctx = *ctxp;
+       *ctxp = NULL;
+
+       SSL_CTX_free(ctx);
+}
+
+isc_result_t
+isc_tlsctx_createclient(isc_tlsctx_t **ctxp) {
+       unsigned long err;
+       char errbuf[256];
+       SSL_CTX *ctx = NULL;
+       const SSL_METHOD *method = NULL;
+
+       REQUIRE(ctxp != NULL && *ctxp == NULL);
+
+       method = TLS_client_method();
+       if (method == NULL) {
+               goto ssl_error;
+       }
+       ctx = SSL_CTX_new(method);
+       if (ctx == NULL) {
+               goto ssl_error;
+       }
+
+#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
+       SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+#else
+       SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+                                        SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
+#endif
+
+       *ctxp = ctx;
+
+       return (ISC_R_SUCCESS);
+
+ssl_error:
+       err = ERR_get_error();
+       ERR_error_string_n(err, errbuf, sizeof(errbuf));
+       isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+                     ISC_LOG_ERROR, "Error initializing TLS context: %s",
+                     errbuf);
+
+       return (ISC_R_TLSERROR);
+}
+
+isc_result_t
+isc_tlsctx_createserver(const char *keyfile, const char *certfile,
+                       isc_tlsctx_t **ctxp) {
+       int rv;
+       unsigned long err;
+       bool ephemeral = (keyfile == NULL && certfile == NULL);
+       X509 *cert = NULL;
+       EVP_PKEY *pkey = NULL;
+       BIGNUM *bn = NULL;
+       SSL_CTX *ctx = NULL;
+       RSA *rsa = NULL;
+       char errbuf[256];
+       const SSL_METHOD *method = NULL;
+
+       REQUIRE(ctxp != NULL && *ctxp == NULL);
+
+       if (ephemeral) {
+               INSIST(keyfile == NULL);
+               INSIST(certfile == NULL);
+       } else {
+               INSIST(keyfile != NULL);
+               INSIST(certfile != NULL);
+       }
+
+       method = TLS_server_method();
+       if (method == NULL) {
+               goto ssl_error;
+       }
+       ctx = SSL_CTX_new(method);
+       if (ctx == NULL) {
+               goto ssl_error;
+       }
+       RUNTIME_CHECK(ctx != NULL);
+
+#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
+       SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+#else
+       SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+                                        SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
+#endif
+
+       if (ephemeral) {
+               rsa = RSA_new();
+               if (rsa == NULL) {
+                       goto ssl_error;
+               }
+               bn = BN_new();
+               if (bn == NULL) {
+                       goto ssl_error;
+               }
+               BN_set_word(bn, RSA_F4);
+               rv = RSA_generate_key_ex(rsa, 4096, bn, NULL);
+               if (rv != 1) {
+                       goto ssl_error;
+               }
+               cert = X509_new();
+               if (cert == NULL) {
+                       goto ssl_error;
+               }
+               pkey = EVP_PKEY_new();
+               if (pkey == NULL) {
+                       goto ssl_error;
+               }
+
+               /*
+                * EVP_PKEY_assign_*() set the referenced key to key
+                * however these use the supplied key internally and so
+                * key will be freed when the parent pkey is freed.
+                */
+               EVP_PKEY_assign(pkey, EVP_PKEY_RSA, rsa);
+               rsa = NULL;
+               ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
+
+               X509_gmtime_adj(X509_get_notBefore(cert), 0);
+               /*
+                * We set the vailidy for 10 years.
+                */
+               X509_gmtime_adj(X509_get_notAfter(cert), 3650 * 24 * 3600);
+
+               X509_set_pubkey(cert, pkey);
+
+               X509_NAME *name = X509_get_subject_name(cert);
+
+               X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+                                          (const unsigned char *)"AQ", -1, -1,
+                                          0);
+               X509_NAME_add_entry_by_txt(
+                       name, "O", MBSTRING_ASC,
+                       (const unsigned char *)"BIND9 ephemeral "
+                                              "certificate",
+                       -1, -1, 0);
+               X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                                          (const unsigned char *)"bind9.local",
+                                          -1, -1, 0);
+
+               X509_set_issuer_name(cert, name);
+               X509_sign(cert, pkey, EVP_sha256());
+               rv = SSL_CTX_use_certificate(ctx, cert);
+               if (rv != 1) {
+                       goto ssl_error;
+               }
+               rv = SSL_CTX_use_PrivateKey(ctx, pkey);
+               if (rv != 1) {
+                       goto ssl_error;
+               }
+
+               X509_free(cert);
+               EVP_PKEY_free(pkey);
+               BN_free(bn);
+       } else {
+               rv = SSL_CTX_use_certificate_file(ctx, certfile,
+                                                 SSL_FILETYPE_PEM);
+               if (rv != 1) {
+                       goto ssl_error;
+               }
+               rv = SSL_CTX_use_PrivateKey_file(ctx, keyfile,
+                                                SSL_FILETYPE_PEM);
+               if (rv != 1) {
+                       goto ssl_error;
+               }
+       }
+
+       *ctxp = ctx;
+       return (ISC_R_SUCCESS);
+
+ssl_error:
+       err = ERR_get_error();
+       ERR_error_string_n(err, errbuf, sizeof(errbuf));
+       isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
+                     ISC_LOG_ERROR, "Error initializing TLS context: %s",
+                     errbuf);
+       if (ctx != NULL) {
+               SSL_CTX_free(ctx);
+       }
+       if (cert != NULL) {
+               X509_free(cert);
+       }
+       if (pkey != NULL) {
+               EVP_PKEY_free(pkey);
+       }
+       if (bn != NULL) {
+               BN_free(bn);
+       }
+       if (rsa != NULL) {
+               RSA_free(rsa);
+       }
+
+       return (ISC_R_TLSERROR);
+}
index 498f258783920bed402f1b2d48ca1e6f628c704f..ce691b6a78293a823c300517af9f9b96fa39c330 100644 (file)
@@ -451,7 +451,6 @@ isc_nm_detach
 isc_nm_listentcpdns
 isc_nm_listentlsdns
 isc_nm_listentcp
-isc_nm_listentls
 isc_nm_listenudp
 isc_nm_maxudp
 isc_nm_pauseread
@@ -468,8 +467,6 @@ isc_nm_settimeouts
 isc_nm_tcpdns_keepalive
 isc_nm_tcpdns_sequential
 isc_nm_tid
-isc_nm_tls_create_server_ctx
-isc_nm_tlsconnect
 isc_nm_tlsdnsconnect
 isc_nm_udpconnect
 isc_nmsocket_close
@@ -702,6 +699,11 @@ isc_timermgr_create
 isc_timermgr_createinctx
 isc_timermgr_destroy
 isc_timermgr_poke
+isc_tls_initialize
+isc_tls_destroy
+isc_tlsctx_createclient
+isc_tlsctx_createserver
+isc_tlsctx_free
 isc_tm_timegm
 isc_tm_strptime
 isc_utf8_bom
index 216be2f3a31da2524ed74f4a996800694355b2e4..fea6c87ab4325fbae1f9ec1fcf85e2c1e276ba4e 100644 (file)
     <ClInclude Include="..\include\isc\timer.h">
       <Filter>Library Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\include\isc\tls.h">
+      <Filter>Library Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\include\isc\tm.h">
       <Filter>Library Header Files</Filter>
     </ClInclude>
     <ClCompile Include="..\timer.c">
       <Filter>Library Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\tls.c">
+      <Filter>Library Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\tm.c">
       <Filter>Library Source Files</Filter>
     </ClCompile>
index 98ce0c9d3809fe91b8adaaac2c60795d53ab46f5..72e7e0cb50a69138fe07398b25b988aeb047640e 100644 (file)
 @IF PKCS11
       <PreprocessorDefinitions>BIND9;@PK11_LIB_LOCATION@WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
-      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
 @ELSE PKCS11
       <PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
-      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
 @END PKCS11
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
@@ -163,11 +163,11 @@ copy InstallFiles ..\Build\Debug\
 @IF PKCS11
       <PreprocessorDefinitions>BIND9;@PK11_LIB_LOCATION@WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
-      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
 @ELSE PKCS11
       <PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles>
-      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
 @END PKCS11
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <WholeProgramOptimization>false</WholeProgramOptimization>
@@ -334,6 +334,7 @@ copy InstallFiles ..\Build\Release\
     <ClInclude Include="..\include\isc\task.h" />
     <ClInclude Include="..\include\isc\taskpool.h" />
     <ClInclude Include="..\include\isc\timer.h" />
+    <ClInclude Include="..\include\isc\tls.h" />
     <ClInclude Include="..\include\isc\tm.h" />
     <ClInclude Include="..\include\isc\types.h" />
     <ClInclude Include="..\include\isc\utf8.h" />
@@ -413,7 +414,6 @@ copy InstallFiles ..\Build\Release\
     <ClCompile Include="..\netmgr\uverr2result.c" />
     <ClCompile Include="..\netmgr\uv-compat.c" />
     <ClCompile Include="..\netmgr\tcpdns.c" />
-    <ClCompile Include="..\netmgr\tls.c" />
     <ClCompile Include="..\netmgr\tlsdns.c" />
     <ClCompile Include="..\netscope.c" />
     <ClCompile Include="..\nonce.c" />
@@ -440,6 +440,7 @@ copy InstallFiles ..\Build\Release\
     <ClCompile Include="..\task.c" />
     <ClCompile Include="..\taskpool.c" />
     <ClCompile Include="..\timer.c" />
+    <ClCompile Include="..\tls.c" />
     <ClCompile Include="..\tm.c" />
     <ClCompile Include="..\utf8.c" />
 @IF PKCS11
index d3ced20034523a447af13d3bb72c492c6dbe0e16..fe6dffde7e01c087a62ede3c3d82a4c66572ed04 100644 (file)
 #include <stdbool.h>
 
 #include <isc/net.h>
+#include <isc/tls.h>
 
 #include <dns/types.h>
 
-#include <openssl/ssl.h>
-
 /***
  *** Types
  ***/
@@ -41,11 +40,11 @@ typedef struct ns_listenelt  ns_listenelt_t;
 typedef struct ns_listenlist ns_listenlist_t;
 
 struct ns_listenelt {
-       isc_mem_t *mctx;
-       in_port_t  port;
-       isc_dscp_t dscp; /* -1 = not set, 0..63 */
-       dns_acl_t *acl;
-       SSL_CTX *  sslctx;
+       isc_mem_t *   mctx;
+       in_port_t     port;
+       isc_dscp_t    dscp; /* -1 = not set, 0..63 */
+       dns_acl_t *   acl;
+       isc_tlsctx_t *sslctx;
        ISC_LINK(ns_listenelt_t) link;
 };
 
index c7bbc753a7f20cf9abde02bd18544957b3d630c7..8f73279263fae932d5520668ce4fdd33b0a2aa19 100644 (file)
@@ -507,9 +507,8 @@ ns_interface_listentcp(ns_interface_t *ifp) {
  * TLS related options.
  */
 static isc_result_t
-ns_interface_listentls(ns_interface_t *ifp, SSL_CTX *sslctx) {
+ns_interface_listentls(ns_interface_t *ifp, isc_tlsctx_t *sslctx) {
        isc_result_t result;
-       SSL_CTX *ctx = NULL;
 
        result = isc_nm_listentlsdns(
                ifp->mgr->nm, (isc_nmiface_t *)&ifp->addr, ns__client_request,
@@ -521,7 +520,7 @@ ns_interface_listentls(ns_interface_t *ifp, SSL_CTX *sslctx) {
                isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
                              "creating TLS socket: %s",
                              isc_result_totext(result));
-               SSL_CTX_free(ctx);
+               isc_tlsctx_free(&sslctx);
                return (result);
        }
 
index 50e71023d6886efb6b94cd3d45e8451c037246d0..c5153ca60c382c1b532bb70a8aa5c2effa54f32d 100644 (file)
@@ -39,7 +39,7 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
        elt->acl = acl;
        elt->sslctx = NULL;
        if (tls) {
-               result = isc_nm_tls_create_server_ctx(key, cert, &elt->sslctx);
+               result = isc_tlsctx_createserver(key, cert, &elt->sslctx);
                if (result != ISC_R_SUCCESS) {
                        return (result);
                }
@@ -54,8 +54,7 @@ ns_listenelt_destroy(ns_listenelt_t *elt) {
                dns_acl_detach(&elt->acl);
        }
        if (elt->sslctx != NULL) {
-               SSL_CTX_free(elt->sslctx);
-               elt->sslctx = NULL;
+               isc_tlsctx_free(&elt->sslctx);
        }
        isc_mem_put(elt->mctx, elt, sizeof(*elt));
 }
index a779c9ee8751239189206d92cb11d232d5571ab1..5595a22c57bdc278d8c2df0b0ee860243792dc55 100644 (file)
@@ -78,7 +78,7 @@ atomic_uint_fast32_t client_refs[32];
 atomic_uintptr_t client_addrs[32];
 
 void
-isc_nmhandle_attach(isc_nmhandle_t *source, isc_nmhandle_t **targetp) {
+isc__nmhandle_attach(isc_nmhandle_t *source, isc_nmhandle_t **targetp FLARG) {
        ns_client_t *client = (ns_client_t *)source;
        int i;
 
@@ -97,7 +97,7 @@ isc_nmhandle_attach(isc_nmhandle_t *source, isc_nmhandle_t **targetp) {
 }
 
 void
-isc_nmhandle_detach(isc_nmhandle_t **handlep) {
+isc__nmhandle_detach(isc_nmhandle_t **handlep FLARG) {
        isc_nmhandle_t *handle = *handlep;
        ns_client_t *client = (ns_client_t *)handle;
        int i;
index 012c2f3b43be4d3d34a200c1dac1f160c1c457dd..1721d79d27bdeeece7fdb7e1a29a0b46e8c9f885 100644 (file)
@@ -62,6 +62,15 @@ extern bool app_running;
 extern int ncpus;
 extern bool debug_mem_record;
 
+#ifdef NETMGR_TRACE
+#define FLARG                                              \
+       , const char *file __attribute__((unused)),        \
+               unsigned int line __attribute__((unused)), \
+               const char *func __attribute__((unused))
+#else
+#define FLARG
+#endif
+
 isc_result_t
 ns_test_begin(FILE *logfile, bool create_managers);
 
index 2e025daa6c0e1dffb2c34e37cb2f572d7b235ea7..7a9f9f10475c3d7b1b014b2e2b6b6ad8baeefa00 100644 (file)
 ./bin/tests/system/zonechecks/clean.sh         SH      2004,2007,2012,2014,2015,2016,2018,2019,2020,2021
 ./bin/tests/system/zonechecks/setup.sh         SH      2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
 ./bin/tests/system/zonechecks/tests.sh         SH      2004,2007,2009,2012,2013,2014,2015,2016,2018,2019,2020,2021
+./bin/tests/test_client.c                      C       2021
+./bin/tests/test_server.c                      C       2021
 ./bin/tests/testdata/wire/wire_test.data       X       1999,2000,2001,2018,2019
 ./bin/tests/testdata/wire/wire_test.data2      X       1999,2000,2001,2018,2019
 ./bin/tests/testdata/wire/wire_test.data3      X       1999,2000,2001,2018,2019
 ./lib/isc/include/isc/task.h                   C       1998,1999,2000,2001,2003,2004,2005,2006,2007,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
 ./lib/isc/include/isc/taskpool.h               C       1999,2000,2001,2004,2005,2006,2007,2011,2012,2016,2018,2019,2020,2021
 ./lib/isc/include/isc/timer.h                  C       1998,1999,2000,2001,2002,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2018,2019,2020,2021
+./lib/isc/include/isc/tls.h                    C       2021
 ./lib/isc/include/isc/tm.h                     C       2014,2016,2018,2019,2020,2021
 ./lib/isc/include/isc/types.h                  C       1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2012,2013,2014,2016,2017,2018,2019,2020,2021
 ./lib/isc/include/isc/utf8.h                   C       2020,2021
 ./lib/isc/tests/udp_test.c                     C       2020,2021
 ./lib/isc/tests/uv_wrap.h                      C       2020,2021
 ./lib/isc/timer.c                              C       1998,1999,2000,2001,2002,2004,2005,2007,2008,2009,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021
+./lib/isc/tls.c                                        C       2021
 ./lib/isc/tm.c                                 C       2014,2016,2018,2019,2020,2021
 ./lib/isc/unix/dir.c                           C       1999,2000,2001,2004,2005,2007,2008,2009,2011,2012,2016,2017,2018,2019,2020,2021
 ./lib/isc/unix/errno.c                         C       2016,2018,2019,2020,2021
index 1d896311beb76a770990456acc1506917c737fdd..7ae633d40f737a550ada56e6506b49cd23daf3ce 100644 (file)
@@ -169,11 +169,6 @@ my @substdefh = ("PACKAGE_VERSION_MAJOR",
                  "HAVE_GSSAPI_GSSAPI_KRB5_H",
                  "HAVE_KRB5_KRB5_H",
                  "HAVE_LIBXML2",
-                 "HAVE_OPENSSL_ED25519",
-                 "HAVE_OPENSSL_ED448",
-                 "HAVE_DH_GET0_KEY",
-                 "HAVE_ECDSA_SIG_GET0",
-                 "HAVE_RSA_SET0_KEY",
                  "USE_BACKTRACE",
                  "USE_OPENSSL",
                  "USE_PKCS11",
@@ -185,16 +180,6 @@ my @substdefh = ("PACKAGE_VERSION_MAJOR",
                  "WITH_IDN",
                  "CPU_RELAX",
                  "VALIDATION_DEFAULT",
-                 "HAVE_CRYPTO_ZALLOC",
-                 "HAVE_EVP_CIPHER_CTX_FREE",
-                 "HAVE_EVP_CIPHER_CTX_NEW",
-                 "HAVE_EVP_MD_CTX_FREE",
-                 "HAVE_EVP_MD_CTX_NEW",
-                 "HAVE_EVP_MD_CTX_RESET",
-                 "HAVE_HMAC_CTX_FREE",
-                 "HAVE_HMAC_CTX_GET_MD",
-                 "HAVE_HMAC_CTX_NEW",
-                 "HAVE_HMAC_CTX_RESET",
                  "HAVE_UV_HANDLE_GET_DATA",
                  "HAVE_UV_HANDLE_SET_DATA",
                  "HAVE_UV_IMPORT",
@@ -1530,12 +1515,12 @@ EOF
 #include <openssl/opensslv.h>
 
 int main() {
-        if (OPENSSL_VERSION_NUMBER >= 0x10000000L) {
+        if (OPENSSL_VERSION_NUMBER >= 0x10101000L) {
                 return (0);
         }
         printf("\n\nFound   OPENSSL_VERSION_NUMBER %#010x\n",
                OPENSSL_VERSION_NUMBER);
-        printf("Require OPENSSL_VERSION_NUMBER 0x10000000L or greater (1.0.0)\n\n");
+        printf("Require OPENSSL_VERSION_NUMBER 0x10101000L or greater (1.1.1)\n\n");
         return (1);
 }
 EOF
@@ -1551,47 +1536,6 @@ EOF
         die "can't compile OpenSSL version test: $compret\n";
     }
 
-# check OpenSSL built-in support for DH/ECDSA/RSA/CRYPTO_ZALLOC/EVP_CIPHER_CTX/EVP_MD_CTX/HMAC_CTX functions
-    if ($verbose) {
-        printf "checking OpenSSL built-in support for DH/ECDSA/RSA/CRYPTO_ZALLOC/EVP_CIPHER_CTX/EVP_MD_CTX/HMAC_CTX functions\n";
-    }
-    open F, ">testosslfunc.c" || die $!;
-    print F << 'EOF';
-#include <stdio.h>
-#include <openssl/opensslv.h>
-
-int main() {
-        if (OPENSSL_VERSION_NUMBER >= 0x10100000L) {
-                return (0);
-        }
-        printf("\n\nFound   OPENSSL_VERSION_NUMBER %#010x\n",
-               OPENSSL_VERSION_NUMBER);
-        printf("This version has no built-in support for DH/ECDSA/RSA/CRYPTO_ZALLOC/EVP_CIPHER_CTX/EVP_MD_CTX/HMAC_CTX functions.\n\n");
-        return (1);
-}
-EOF
-    close F;
-
-    $compret = `cl /nologo /MD /I "$include" testosslfunc.c "$libcrypto"`;
-    if (grep { -f and -x } "./testosslfunc.exe") {
-        `./testosslfunc.exe`;
-        if ($? == 0) {
-            $configdefh{"HAVE_DH_GET0_KEY"} = 1;
-            $configdefh{"HAVE_ECDSA_SIG_GET0"} = 1;
-            $configdefh{"HAVE_RSA_SET0_KEY"} = 1;
-            $configdefh{"HAVE_CRYPTO_ZALLOC"} = 1;
-            $configdefh{"HAVE_EVP_CIPHER_CTX_FREE"} = 1;
-            $configdefh{"HAVE_EVP_CIPHER_CTX_NEW"} = 1;
-            $configdefh{"HAVE_EVP_MD_CTX_FREE"} = 1;
-            $configdefh{"HAVE_EVP_MD_CTX_NEW"} = 1;
-            $configdefh{"HAVE_EVP_MD_CTX_RESET"} = 1;
-            $configdefh{"HAVE_HMAC_CTX_FREE"} = 1;
-            $configdefh{"HAVE_HMAC_CTX_GET_MD"} = 1;
-            $configdefh{"HAVE_HMAC_CTX_NEW"} = 1;
-            $configdefh{"HAVE_HMAC_CTX_RESET"} = 1;
-        }
-    }
-
     if ($verbose) {
         print "checking for OpenSSL Ed25519 support\n";
     }