]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
netmgr: server-side TLS support
authorWitold Kręcicki <wpk@isc.org>
Wed, 13 May 2020 15:37:51 +0000 (17:37 +0200)
committerOndřej Surý <ondrej@sury.org>
Wed, 9 Dec 2020 09:46:16 +0000 (10:46 +0100)
Add server-side TLS support to netmgr - that includes moving some of the
isc_nm_ functions from tcp.c to a wrapper in netmgr.c calling a proper
tcp or tls function, and a new isc_nm_listentls() function.

Add DoT support to tcpdns - isc_nm_listentlsdns().

(cherry picked from commit b2ee0e9dc31cbe87b799e3b796165ebdf8de5f85)

lib/isc/include/isc/netmgr.h
lib/isc/include/isc/result.h
lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/tcpdns.c
lib/isc/netmgr/tls.c [new file with mode: 0644]
lib/isc/result.c
lib/isc/sockaddr.c
lib/isc/win32/libisc.def.in
lib/isc/win32/libisc.vcxproj.in
util/copyrights

index 2cd65eb99a9190423dd3cd321d59a7616eb5f6ce..349fb2f68192c2ef053461e76d361288a5db93f7 100644 (file)
@@ -16,6 +16,8 @@
 #include <isc/result.h>
 #include <isc/types.h>
 
+typedef struct ssl_ctx_st isc_ssl_ctx_t;
+
 /*
  * Replacement for isc_sockettype_t provided by socket.h.
  */
@@ -351,6 +353,16 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
  * 'quota' is passed to isc_nm_listentcp() when opening the raw TCP 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, isc_ssl_ctx_t *sslctx,
+                   isc_nmsocket_t **sockp);
+/*%<
+ * Same as isc_nm_listentcpdns but for an SSL (DoT) socket.
+ */
+
 void
 isc_nm_tcpdns_sequential(isc_nmhandle_t *handle);
 /*%<
@@ -405,6 +417,17 @@ isc_nm_tcp_gettimeouts(isc_nm_t *mgr, uint32_t *initial, uint32_t *idle,
  * \li 'mgr' is a valid netmgr.
  */
 
+isc_result_t
+isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
+                isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
+                size_t extrahandlesize, int backlog, isc_quota_t *quota,
+                isc_ssl_ctx_t *sslctx, isc_nmsocket_t **sockp);
+
+isc_result_t
+isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
+                 isc_nm_cb_t cb, void *cbarg, isc_ssl_ctx_t *ctx,
+                 unsigned int timeout, size_t extrahandlesize);
+
 void
 isc_nm_maxudp(isc_nm_t *mgr, uint32_t maxudp);
 /*%<
@@ -428,9 +451,13 @@ isc_result_t
 isc_nm_tcpdnsconnect(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);
-/*%
- * Establish a DNS client connection over a TCP socket, bound to the
- * address 'local', and connected to the address 'peer'.
+isc_result_t
+isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
+                    isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
+                    size_t extrahandlesize);
+/*%<
+ * Establish a DNS client connection via a TCP or TLS connection, bound to
+ * the address 'local' and connected to the address 'peer'.
  *
  * When the connection is complete or has timed out, call 'cb' with
  * argument 'cbarg'. Allocate 'extrahandlesize' additional bytes along
@@ -441,3 +468,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
  * The connected socket can only be accessed via the handle passed to
  * 'cb'.
  */
+
+isc_result_t
+isc_nm_tls_create_server_ctx(const char *keyfile, const char *certfile,
+                            isc_ssl_ctx_t **ctxp);
index 99c55a32f27105e4441817728afa8704fefd87e5..cfc3f03ad7ae078ab626cdef55a71a3b16b1b5f1 100644 (file)
 #define ISC_R_DISCFULL        67           /*%< disc full */
 #define ISC_R_DEFAULT         68           /*%< default */
 #define ISC_R_IPV4PREFIX       69          /*%< IPv4 prefix */
+#define ISC_R_TLSERROR        70           /*%< TLS error */
 
 /*% Not a result code: the number of results. */
-#define ISC_R_NRESULTS 70
+#define ISC_R_NRESULTS 71
 
 ISC_LANG_BEGINDECLS
 
index 8efe84f30d41037bffe6a36f35d188bfdd39a6c5..624bfb224491ab96fe3d497fd3991f49308e6712 100644 (file)
@@ -14,6 +14,9 @@
 #include <unistd.h>
 #include <uv.h>
 
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
 #include <isc/astack.h>
 #include <isc/atomic.h>
 #include <isc/buffer.h>
@@ -158,6 +161,12 @@ typedef enum isc__netievent_type {
        netievent_tcpdnsclose,
        netievent_tcpdnsstop,
 
+       netievent_tlsclose,
+       netievent_tlssend,
+       netievent_tlsstartread,
+       netievent_tlsconnect,
+       netievent_tlsdobio,
+
        netievent_closecb,
        netievent_shutdown,
        netievent_stop,
@@ -188,7 +197,8 @@ typedef union {
 #define UVREQ_MAGIC    ISC_MAGIC('N', 'M', 'U', 'R')
 #define VALID_UVREQ(t) ISC_MAGIC_VALID(t, UVREQ_MAGIC)
 
-typedef struct isc__nm_uvreq {
+typedef struct isc__nm_uvreq isc__nm_uvreq_t;
+struct isc__nm_uvreq {
        int magic;
        isc_nmsocket_t *sock;
        isc_nmhandle_t *handle;
@@ -212,7 +222,8 @@ typedef struct isc__nm_uvreq {
                uv_fs_t fs;
                uv_work_t work;
        } uv_req;
-} isc__nm_uvreq_t;
+       ISC_LINK(isc__nm_uvreq_t) link;
+};
 
 typedef struct isc__netievent__socket {
        isc__netievent_type type;
@@ -224,14 +235,19 @@ typedef isc__netievent__socket_t isc__netievent_udpread_t;
 typedef isc__netievent__socket_t isc__netievent_udpstop_t;
 typedef isc__netievent__socket_t isc__netievent_udpclose_t;
 typedef isc__netievent__socket_t isc__netievent_tcpstop_t;
+
 typedef isc__netievent__socket_t isc__netievent_tcpclose_t;
 typedef isc__netievent__socket_t isc__netievent_startread_t;
 typedef isc__netievent__socket_t isc__netievent_pauseread_t;
 typedef isc__netievent__socket_t isc__netievent_closecb_t;
+
 typedef isc__netievent__socket_t isc__netievent_tcpdnsclose_t;
 typedef isc__netievent__socket_t isc__netievent_tcpdnsread_t;
 typedef isc__netievent__socket_t isc__netievent_tcpdnsstop_t;
 
+typedef isc__netievent__socket_t isc__netievent_tlsclose_t;
+typedef isc__netievent__socket_t isc__netievent_tlsdobio_t;
+
 typedef struct isc__netievent__socket_req {
        isc__netievent_type type;
        isc_nmsocket_t *sock;
@@ -280,6 +296,14 @@ typedef struct isc__netievent_udpsend {
        isc__nm_uvreq_t *req;
 } isc__netievent_udpsend_t;
 
+typedef struct isc__netievent_tlsconnect {
+       isc__netievent_type type;
+       isc_nmsocket_t *sock;
+       SSL_CTX *ctx;
+       isc_sockaddr_t local; /* local address */
+       isc_sockaddr_t peer;  /* peer address */
+} isc__netievent_tlsconnect_t;
+
 typedef struct isc__netievent {
        isc__netievent_type type;
 } isc__netievent_t;
@@ -294,6 +318,7 @@ typedef union {
        isc__netievent_udpsend_t nius;
        isc__netievent__socket_quota_t nisq;
        isc__netievent__socket_streaminfo_quota_t nissq;
+       isc__netievent_tlsconnect_t nitc;
 } isc__netievent_storage_t;
 
 /*
@@ -361,6 +386,8 @@ typedef enum isc_nmsocket_type {
        isc_nm_tcplistener,
        isc_nm_tcpdnslistener,
        isc_nm_tcpdnssocket,
+       isc_nm_tlslistener,
+       isc_nm_tlssocket
 } isc_nmsocket_type;
 
 /*%
@@ -400,6 +427,26 @@ struct isc_nmsocket {
        /*% Self, for self-contained unreferenced sockets (tcpdns) */
        isc_nmsocket_t *self;
 
+       /*% TLS stuff */
+       struct tls {
+               bool server;
+               BIO *app_bio;
+               SSL *ssl;
+               SSL_CTX *ctx;
+               BIO *ssl_bio;
+               enum {
+                       TLS_INIT,
+                       TLS_HANDSHAKE,
+                       TLS_IO,
+                       TLS_ERROR,
+                       TLS_CLOSING
+               } state;
+               isc_region_t senddata;
+               bool sending;
+               /* List of active send requests. */
+               ISC_LIST(isc__nm_uvreq_t) sends;
+       } tls;
+
        /*%
         * quota is the TCP client, attached when a TCP connection
         * is established. pquota is a non-attached pointer to the
@@ -863,6 +910,25 @@ isc__nm_async_tcpclose(isc__networker_t *worker, isc__netievent_t *ev0);
  * stoplisten, send, read, pause, close).
  */
 
+void
+isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tls_startread(isc__networker_t *worker, isc__netievent_t *ev0);
+
+void
+isc__nm_async_tls_do_bio(isc__networker_t *worker, isc__netievent_t *ev0);
+
+/*%<
+ * Callback handlers for asynchronouse TLS events.
+ */
+
 void
 isc__nm_tcpdns_send(isc_nmhandle_t *handle, isc_region_t *region,
                    isc_nm_cb_t cb, void *cbarg);
@@ -910,6 +976,35 @@ isc__nm_tcpdns_cancelread(isc_nmhandle_t *handle);
  * Stop reading on a connected TCPDNS 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_nmsocket_t *sock);
+/*%<
+ * Pause reading on this socket, while still remembering the callback.
+ */
+
+void
+isc__nm_tls_resumeread(isc_nmsocket_t *sock);
+/*%<
+ * Resume reading from socket.
+ *
+ */
+
+void
+isc__nm_tls_stoplistening(isc_nmsocket_t *sock);
+
 #define isc__nm_uverr2result(x) \
        isc___nm_uverr2result(x, true, __FILE__, __LINE__)
 isc_result_t
@@ -987,3 +1082,9 @@ isc__nm_socket_dontfrag(uv_os_sock_t fd, sa_family_t sa_family);
 /*%<
  * Set the SO_IP_DONTFRAG (or equivalent) socket option of the fd if available
  */
+
+void
+isc__nm_tls_initialize(void);
+/*%<
+ * Initialize OpenSSL library, idempotent.
+ */
index 5d9cb9992ef9fcf5fa419c08b5d7a54463183f24..404b7d1636d6d60065bdda331c3b8dab64c871f3 100644 (file)
@@ -168,6 +168,8 @@ isc_nm_start(isc_mem_t *mctx, uint32_t workers) {
        isc_nm_t *mgr = NULL;
        char name[32];
 
+       isc__nm_tls_initialize();
+
        mgr = isc_mem_get(mctx, sizeof(*mgr));
        *mgr = (isc_nm_t){ .nworkers = workers };
 
@@ -669,7 +671,6 @@ process_queue(isc__networker_t *worker, isc_queue_t *queue) {
                case netievent_tcpclose:
                        isc__nm_async_tcpclose(worker, ievent);
                        break;
-
                case netievent_tcpdnscancel:
                        isc__nm_async_tcpdnscancel(worker, ievent);
                        break;
@@ -683,6 +684,22 @@ process_queue(isc__networker_t *worker, isc_queue_t *queue) {
                        isc__nm_async_tcpdnsstop(worker, ievent);
                        break;
 
+               case netievent_tlsstartread:
+                       isc__nm_async_tls_startread(worker, ievent);
+                       break;
+               case netievent_tlssend:
+                       isc__nm_async_tlssend(worker, ievent);
+                       break;
+               case netievent_tlsclose:
+                       isc__nm_async_tlsclose(worker, ievent);
+                       break;
+               case netievent_tlsconnect:
+                       isc__nm_async_tlsconnect(worker, ievent);
+                       break;
+               case netievent_tlsdobio:
+                       isc__nm_async_tls_do_bio(worker, ievent);
+                       break;
+
                case netievent_closecb:
                        isc__nm_async_closecb(worker, ievent);
                        break;
@@ -966,6 +983,9 @@ isc__nmsocket_prep_destroy(isc_nmsocket_t *sock) {
                case isc_nm_tcpdnssocket:
                        isc__nm_tcpdns_close(sock);
                        return;
+               case isc_nm_tlssocket:
+                       isc__nm_tls_close(sock);
+                       break;
                default:
                        break;
                }
@@ -1163,16 +1183,16 @@ isc__nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
 #endif
 
        if (peer != NULL) {
-               memcpy(&handle->peer, peer, sizeof(isc_sockaddr_t));
+               memmove(&handle->peer, peer, sizeof(isc_sockaddr_t));
        } else {
-               memcpy(&handle->peer, &sock->peer, sizeof(isc_sockaddr_t));
+               memmove(&handle->peer, &sock->peer, sizeof(isc_sockaddr_t));
        }
 
        if (local != NULL) {
-               memcpy(&handle->local, local, sizeof(isc_sockaddr_t));
+               memmove(&handle->local, local, sizeof(isc_sockaddr_t));
        } else if (sock->iface != NULL) {
-               memcpy(&handle->local, &sock->iface->addr,
-                      sizeof(isc_sockaddr_t));
+               memmove(&handle->local, &sock->iface->addr,
+                       sizeof(isc_sockaddr_t));
        } else {
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1207,7 +1227,7 @@ isc__nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
 #endif
        UNLOCK(&sock->lock);
 
-       if (sock->type == isc_nm_tcpsocket ||
+       if (sock->type == isc_nm_tcpsocket || sock->type == isc_nm_tlssocket ||
            (sock->type == isc_nm_udpsocket && atomic_load(&sock->client)) ||
            (sock->type == isc_nm_tcpdnssocket && atomic_load(&sock->client)))
        {
@@ -1453,6 +1473,7 @@ isc__nm_uvreq_get(isc_nm_t *mgr, isc_nmsocket_t *sock) {
        }
 
        *req = (isc__nm_uvreq_t){ .magic = 0 };
+       ISC_LINK_INIT(req, link);
        req->uv_req.req.data = req;
        isc__nmsocket_attach(sock, &req->sock);
        req->magic = UVREQ_MAGIC;
@@ -1510,6 +1531,9 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
        case isc_nm_tcpdnssocket:
                isc__nm_tcpdns_send(handle, region, cb, cbarg);
                break;
+       case isc_nm_tlssocket:
+               isc__nm_tls_send(handle, region, cb, cbarg);
+               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1530,6 +1554,9 @@ isc_nm_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
        case isc_nm_tcpdnssocket:
                isc__nm_tcpdns_read(handle, cb, cbarg);
                break;
+       case isc_nm_tlssocket:
+               isc__nm_tls_read(handle, cb, cbarg);
+               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1566,6 +1593,9 @@ isc_nm_pauseread(isc_nmhandle_t *handle) {
        case isc_nm_tcpsocket:
                isc__nm_tcp_pauseread(sock);
                break;
+       case isc_nm_tlssocket:
+               isc__nm_tls_pauseread(sock);
+               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1582,6 +1612,9 @@ isc_nm_resumeread(isc_nmhandle_t *handle) {
        case isc_nm_tcpsocket:
                isc__nm_tcp_resumeread(sock);
                break;
+       case isc_nm_tlssocket:
+               isc__nm_tls_resumeread(sock);
+               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1602,6 +1635,9 @@ isc_nm_stoplistening(isc_nmsocket_t *sock) {
        case isc_nm_tcplistener:
                isc__nm_tcp_stoplistening(sock);
                break;
+       case isc_nm_tlslistener:
+               isc__nm_tls_stoplistening(sock);
+               break;
        default:
                INSIST(0);
                ISC_UNREACHABLE();
@@ -1948,6 +1984,10 @@ nmsocket_type_totext(isc_nmsocket_type type) {
                return ("isc_nm_tcpdnslistener");
        case isc_nm_tcpdnssocket:
                return ("isc_nm_tcpdnssocket");
+       case isc_nm_tlssocket:
+               return ("isc_nm_tlssocket");
+       case isc_nm_tlslistener:
+               return ("isc_nm_tlslistener");
        default:
                INSIST(0);
                ISC_UNREACHABLE();
index aa2003ea7956b5e3313b993d060c8e177681720c..8004124a850a0edfbc1ec3056b73c126a1d9a475 100644 (file)
@@ -393,11 +393,6 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
        dnslistensock->accept_cbarg = accept_cbarg;
        dnslistensock->extrahandlesize = extrahandlesize;
 
-       /*
-        * dnslistensock will be a DNS 'wrapper' around a connected
-        * stream. We set dnslistensock->outer to a socket listening
-        * for a TCP connection.
-        */
        result = isc_nm_listentcp(mgr, iface, dnslisten_acceptcb, dnslistensock,
                                  extrahandlesize, backlog, quota,
                                  &dnslistensock->outer);
@@ -412,6 +407,43 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_nmiface_t *iface, isc_nm_recv_cb_t cb,
        }
 }
 
+/*
+ * isc_nm_listentlsdns works exactly as listentcpdns 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;
+
+       REQUIRE(VALID_NM(mgr));
+       REQUIRE(sslctx != NULL);
+
+       isc__nmsocket_init(dnslistensock, mgr, isc_nm_tcpdnslistener, 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);
+       if (result == ISC_R_SUCCESS) {
+               atomic_store(&dnslistensock->listening, true);
+               *sockp = dnslistensock;
+               return (ISC_R_SUCCESS);
+       } else {
+               atomic_store(&dnslistensock->closed, true);
+               isc__nmsocket_detach(&dnslistensock);
+               return (result);
+       }
+}
+
 void
 isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) {
        isc__netievent_tcpstop_t *ievent = (isc__netievent_tcpdnsstop_t *)ev0;
@@ -430,7 +462,16 @@ isc__nm_async_tcpdnsstop(isc__networker_t *worker, isc__netievent_t *ev0) {
        isc__nmsocket_clearcb(sock);
 
        if (sock->outer != NULL) {
-               isc__nm_tcp_stoplistening(sock->outer);
+               switch (sock->outer->type) {
+               case isc_nm_tcplistener:
+                       isc__nm_tcp_stoplistening(sock->outer);
+                       break;
+               case isc_nm_tlslistener:
+                       isc__nm_tls_stoplistening(sock->outer);
+                       break;
+               default:
+                       INSIST(0);
+               }
                isc__nmsocket_detach(&sock->outer);
        }
 
@@ -559,6 +600,7 @@ resume_processing(void *arg) {
 static void
 tcpdnssend_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);
 
@@ -805,6 +847,25 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
                                  timeout, 0));
 }
 
+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) {
+       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());
+       isc_result_t result = isc_nm_tlsconnect(
+               mgr, local, peer, tcpdnsconnect_cb, conn, ctx, timeout, 0);
+       SSL_CTX_free(ctx);
+       return (result);
+}
+
 void
 isc__nm_tcpdns_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
        isc_nmsocket_t *sock = handle->sock;
diff --git a/lib/isc/netmgr/tls.c b/lib/isc/netmgr/tls.c
new file mode 100644 (file)
index 0000000..df0a1a1
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ * 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);
+
+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_ievent(sock->mgr, netievent_tlsdobio);
+       ievent->sock = 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 (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;
+       }
+
+       if (tls_err == 0) {
+               return;
+       }
+
+       if (tls_err == 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;
+               }
+       } else if (tls_err == SSL_ERROR_WANT_READ) {
+               isc_nm_resumeread(sock->outerhandle);
+       } else if (tls_err != 0) {
+               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) {
+       isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg;
+       isc_nmsocket_t *tlssock = NULL;
+
+       REQUIRE(VALID_NMSOCK(tlslistensock));
+       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 = 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);
+       }
+
+       uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
+                     &tlssock->timer);
+       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__nm_uvreq_t *req = ievent->req;
+       ievent->req = NULL;
+       REQUIRE(VALID_UVREQ(req));
+       REQUIRE(worker->id == ievent->sock->tid);
+
+       if (!atomic_load(&ievent->sock->active)) {
+               return;
+       }
+       if (!ISC_LIST_EMPTY(ievent->sock->tls.sends)) {
+               /* We're not the first */
+               ISC_LIST_APPEND(ievent->sock->tls.sends, req, link);
+               tls_do_bio(ievent->sock);
+               return;
+       }
+
+       rv = SSL_write(ievent->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(ievent->sock->tls.sends, req, link);
+               tls_do_bio(ievent->sock);
+               return;
+       }
+       if (rv != (int)req->uvbuf.len) {
+               ievent->sock->tls.state = TLS_ERROR;
+               async_tls_do_bio(ievent->sock);
+               return;
+       }
+       req->cb.send(ievent->sock->statichandle, ISC_R_SUCCESS, req->cbarg);
+       isc__nm_uvreq_put(&req, ievent->sock);
+       tls_do_bio(ievent->sock);
+       return;
+}
+
+void
+isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
+                void *cbarg) {
+       isc_nmsocket_t *sock = handle->sock;
+       isc__netievent_tcpsend_t *ievent = NULL;
+       isc__nm_uvreq_t *uvreq = NULL;
+
+       REQUIRE(sock->type == isc_nm_tlssocket);
+
+       uvreq = isc__nm_uvreq_get(sock->mgr, sock);
+       uvreq->uvbuf.base = (char *)region->base;
+       uvreq->uvbuf.len = region->length;
+       isc_nmhandle_attach(handle, &uvreq->handle);
+       uvreq->cb.send = cb;
+       uvreq->cbarg = cbarg;
+
+       /*
+        * We need to create an event and pass it using async channel
+        */
+       ievent = isc__nm_get_ievent(sock->mgr, netievent_tlssend);
+       ievent->sock = sock;
+       ievent->req = uvreq;
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_async_tls_startread(isc__networker_t *worker, isc__netievent_t *ev0) {
+       isc__netievent_startread_t *ievent = (isc__netievent_startread_t *)ev0;
+       isc_nmsocket_t *sock = ievent->sock;
+
+       REQUIRE(worker->id == isc_nm_tid());
+
+       tls_do_bio(sock);
+}
+
+void
+isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
+       isc_nmsocket_t *sock = NULL;
+       isc__netievent_startread_t *ievent = NULL;
+
+       REQUIRE(VALID_NMHANDLE(handle));
+       REQUIRE(VALID_NMSOCK(handle->sock));
+
+       sock = handle->sock;
+       sock->recv_cb = cb;
+       sock->recv_cbarg = cbarg;
+
+       ievent = isc__nm_get_ievent(sock->mgr, netievent_tlsstartread);
+       ievent->sock = sock;
+
+       isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
+                              (isc__netievent_t *)ievent);
+}
+
+void
+isc__nm_tls_pauseread(isc_nmsocket_t *sock) {
+       atomic_store(&sock->readpaused, true);
+}
+
+void
+isc__nm_tls_resumeread(isc_nmsocket_t *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);
+       INSIST(VALID_NMSOCK(sock));
+       isc__nmsocket_detach(&sock);
+}
+
+static void
+tls_close_direct(isc_nmsocket_t *sock) {
+       REQUIRE(sock->tid == isc_nm_tid());
+
+       /* 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);
+       }
+}
+
+void
+isc__nm_tls_close(isc_nmsocket_t *sock) {
+       REQUIRE(VALID_NMSOCK(sock));
+       REQUIRE(sock->type == isc_nm_tlssocket);
+
+       if (sock->tid == isc_nm_tid()) {
+               tls_close_direct(sock);
+       } else {
+               isc__netievent_tlsclose_t *ievent =
+                       isc__nm_get_ievent(sock->mgr, netievent_tlsclose);
+
+               ievent->sock = 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(worker->id == ievent->sock->tid);
+
+       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, *tmp = 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;
+       atomic_init(&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_ievent(mgr, netievent_tlsconnect);
+       ievent->sock = nsock;
+       ievent->local = local->addr;
+       ievent->peer = peer->addr;
+       ievent->ctx = ctx;
+
+       /*
+        * Async callbacks can dereference the socket in the meantime,
+        * we need to hold an additional reference to it.
+        */
+       isc__nmsocket_attach(nsock, &tmp);
+
+       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_ievent(mgr, ievent);
+       } else {
+               nsock->tid = isc_random_uniform(mgr->nworkers);
+               isc__nm_enqueue_ievent(&mgr->workers[nsock->tid],
+                                      (isc__netievent_t *)ievent);
+       }
+
+       isc__nmsocket_detach(&tmp);
+
+       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(NULL, result, tlssock->connect_cbarg);
+               atomic_store(&tlssock->result, result);
+               atomic_store(&tlssock->connect_error, true);
+               tls_close_direct(tlssock);
+               return;
+       }
+
+       INSIST(VALID_NMHANDLE(handle));
+
+       isc_nmhandle_attach(handle, &tlssock->outerhandle);
+       result = initialize_tls(tlssock, false);
+       if (result != ISC_R_SUCCESS) {
+               tlssock->connect_cb(NULL, result, tlssock->connect_cbarg);
+               atomic_store(&tlssock->result, result);
+               atomic_store(&tlssock->connect_error, true);
+               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;
+
+       UNUSED(worker);
+
+       tlssock->tid = isc_nm_tid();
+       uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
+                     &tlssock->timer);
+       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) {
+               tlssock->connect_cb(NULL, result, tlssock->connect_cbarg);
+               atomic_store(&tlssock->result, result);
+               atomic_store(&tlssock->connect_error, true);
+               tls_close_direct(tlssock);
+               return;
+       }
+}
+
+void
+isc__nm_async_tls_do_bio(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;
+
+#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);
+       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);
+       SSL_CTX_free(ctx);
+       return (ISC_R_TLSERROR);
+}
+
+void
+isc__nm_tls_initialize() {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+       SSL_library_init();
+#else
+       OPENSSL_init_ssl(0, NULL);
+#endif
+}
index 8b2258c036a6f86af187343cd09d02105f45beb2..e5f8c1f5ee1b6d6ef1366487d07a426e525d18c6 100644 (file)
@@ -101,6 +101,7 @@ static const char *description[ISC_R_NRESULTS] = {
        "disc full",                        /*%< 67 */
        "default",                          /*%< 68 */
        "IPv4 prefix",                      /*%< 69 */
+       "TLS error",                        /*%< 70 */
 };
 
 static const char *identifier[ISC_R_NRESULTS] = {
@@ -174,6 +175,7 @@ static const char *identifier[ISC_R_NRESULTS] = {
        "ISC_R_DISCFULL",
        "ISC_R_DEFAULT",
        "ISC_R_IPV4PREFIX",
+       "ISC_R_TLSERROR",
 };
 
 #define ISC_RESULT_RESULTSET     2
index 6789c33cebed50b19f33235e9bb0cc419a8e460b..8ee4412a6c6f69d55b180dcb6ee9a0b5dfd55143 100644 (file)
@@ -506,7 +506,7 @@ isc_sockaddr_fromsockaddr(isc_sockaddr_t *isa, const struct sockaddr *sa) {
        }
 
        memset(isa, 0, sizeof(isc_sockaddr_t));
-       memcpy(isa, sa, length);
+       memmove(isa, sa, length);
        isa->length = length;
 
        return (ISC_R_SUCCESS);
index 99cfb966ec7d200beb29c0ea32d78d20a5db902a..711e95c5d1d613e6a777039026d233077ea57e30 100644 (file)
@@ -459,7 +459,9 @@ isc_nm_closedown
 isc_nm_destroy
 isc_nm_detach
 isc_nm_listentcpdns
+isc_nm_listentlsdns
 isc_nm_listentcp
+isc_nm_listentls
 isc_nm_listenudp
 isc_nm_maxudp
 isc_nm_pauseread
@@ -476,6 +478,9 @@ isc_nm_tcp_settimeouts
 isc_nm_tcpdns_keepalive
 isc_nm_tcpdns_sequential
 isc_nm_tid
+isc_nm_tls_create_server_ctx
+isc_nm_tlsconnect
+isc_nm_tlsdnsconnect
 isc_nm_udpconnect
 isc_nmsocket_close
 isc__nm_acquire_interlocked
index 3221ab0c2d5a4c1b3f293cc0322c9a5d465858a1..eb05b500e915a554b8d653105a3d83d3e2be0ea6 100644 (file)
@@ -413,6 +413,7 @@ copy InstallFiles ..\Build\Release\
     <ClCompile Include="..\netmgr\uverr2result.c" />
     <ClCompile Include="..\netmgr\uv-compat.c" />
     <ClCompile Include="..\netmgr\tcpdns.c" />
+    <ClCompile Include="..\netmgr\tls.c" />
     <ClCompile Include="..\netscope.c" />
     <ClCompile Include="..\nonce.c" />
     <ClCompile Include="..\openssl_shim.c" />
index b2e9cad9daf989628305b4535e92581eaff3d8c5..57d3e89bcc204f1618141aa074e29864038b2245 100644 (file)
 ./lib/isc/netmgr/netmgr.c                      C       2019,2020
 ./lib/isc/netmgr/tcp.c                         C       2019,2020
 ./lib/isc/netmgr/tcpdns.c                      C       2019,2020
+./lib/isc/netmgr/tls.c                         C       2020
 ./lib/isc/netmgr/udp.c                         C       2019,2020
 ./lib/isc/netmgr/uv-compat.c                   C       2020
 ./lib/isc/netmgr/uv-compat.h                   C       2019,2020