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(
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));
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;
}
#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>
uint64_t byte_count;
isc_timer_t *timer;
uint8_t tries;
+ isc_tlsctx_t *tlsctx;
};
struct dig_server {
keycreate
keydelete
gssapi_krb
+/wire_test
+/test_client
+/test_server
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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
@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@
/* 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@
@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@
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])],
# 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
#
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
#include <isc/platform.h>
#include <isc/string.h>
#include <isc/thread.h>
+#include <isc/tls.h>
#include <isc/util.h>
#include <dns/log.h>
#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) */
#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;
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)
}
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);
}
#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);
}
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 \
netmgr/tcp.c \
netmgr/tcpdns.c \
netmgr/tlsdns.c \
- netmgr/tls.c \
netmgr/udp.c \
netmgr/uv-compat.c \
netmgr/uv-compat.h \
task.c \
taskpool.c \
timer.c \
+ tls.c \
tm.c \
utf8.c \
pthreads/condition.c \
#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.
*/
*/
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.
*/
* \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);
/*%<
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'.
* 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);
--- /dev/null
+/*
+ * 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.
+ */
#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
isc_refcount_t references;
atomic_int_fast64_t pktcount;
char *recvbuf;
+ char *sendbuf;
bool recvbuf_inuse;
} isc__networker_t;
netievent_tcpdnsclose,
netievent_tcpdnsstop,
- netievent_tlsclose,
- netievent_tlssend,
- netievent_tlsstartread,
- netievent_tlsconnect,
- netievent_tlsdobio,
-
netievent_tlsdnsaccept,
netievent_tlsdnsconnect,
netievent_tlsdnssend,
netievent_tlsdnscancel,
netievent_tlsdnsclose,
netievent_tlsdnsstop,
+ netievent_tlsdnscycle,
+ netievent_tlsdnsshutdown,
netievent_close,
netievent_shutdown,
netievent_udplisten,
netievent_tcplisten,
netievent_tcpdnslisten,
+ netievent_tlsdnslisten,
netievent_resume,
netievent_detach,
} isc__netievent_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;
/*% 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;
/*%
*/
-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);
* 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
* 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);
* 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
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);
* 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
* 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
*/
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);
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);
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);
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);
#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
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 };
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
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);
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 */
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);
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);
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);
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;
#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)))
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);
}
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;
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;
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();
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();
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;
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:
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:
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);
req->handle = isc__nmhandle_get(sock, NULL, NULL);
}
- return req;
+ return (req);
}
static void
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);
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);
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);
(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;
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) {
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 } };
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);
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));
(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);
}
(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)) {
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
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);
}
+++ /dev/null
-/*
- * 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
-}
* 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);
}
}
+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, "a,
+ &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("a);
+ }
+ 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)) {
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);
}
(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);
}
* 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);
}
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
#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);
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
isctest.h \
uv_wrap.h
-check_PROGRAMS = \
+TESTS = \
aes_test \
buffer_test \
counter_test \
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) \
$(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) \
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
isc_nm_listentcpdns
isc_nm_listentlsdns
isc_nm_listentcp
-isc_nm_listentls
isc_nm_listenudp
isc_nm_maxudp
isc_nm_pauseread
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
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
<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>
@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>
@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>
<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" />
<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" />
<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
#include <stdbool.h>
#include <isc/net.h>
+#include <isc/tls.h>
#include <dns/types.h>
-#include <openssl/ssl.h>
-
/***
*** Types
***/
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;
};
* 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,
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);
}
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);
}
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));
}
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;
}
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;
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);
./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
"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",
"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",
#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
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";
}