aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/core/init.c5
-rw-r--r--src/supplemental/http/http_client.c3
-rw-r--r--src/supplemental/http/http_server.c3
-rw-r--r--src/supplemental/tls/CMakeLists.txt57
-rw-r--r--src/supplemental/tls/mbedtls/CMakeLists.txt36
-rw-r--r--src/supplemental/tls/mbedtls/tls.c1069
-rw-r--r--src/supplemental/tls/none/tls.c154
-rw-r--r--src/supplemental/tls/tls_api.h32
-rw-r--r--src/supplemental/tls/tls_common.c1216
-rw-r--r--src/supplemental/tls/tls_test.c59
-rw-r--r--src/supplemental/tls/wolfssl/CMakeLists.txt26
-rw-r--r--src/transport/tls/CMakeLists.txt5
-rw-r--r--src/transport/tls/tls.c6
-rw-r--r--src/transport/ws/websocket.c60
15 files changed, 1612 insertions, 1120 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1b177aa1..3f5f148c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -285,7 +285,6 @@ set(NNG_DEFS ${NNG_DEFS} PARENT_SCOPE)
set(NNG_SUPP_BASE64 ${NNG_SUPP_BASE64} PARENT_SCOPE)
set(NNG_SUPP_HTTP ${NNG_SUPP_HTTP} PARENT_SCOPE)
set(NNG_SUPP_SHA1 ${NNG_SUPP_SHA1} PARENT_SCOPE)
-set(NNG_SUPP_TLS ${NNG_SUPP_TLS} PARENT_SCOPE)
set(NNG_SUPP_WEBSOCKET ${NNG_SUPP_WEBSOCKET} PARENT_SCOPE)
# Configure files
diff --git a/src/core/init.c b/src/core/init.c
index 4749516f..7be879f5 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -18,6 +18,9 @@ static nni_mtx nni_init_mtx;
static nni_list nni_init_list;
static bool nni_inited = false;
+extern int nni_tls_sys_init(void);
+extern void nni_tls_sys_fini(void);
+
static int
nni_init_helper(void)
{
@@ -36,6 +39,7 @@ nni_init_helper(void)
((rv = nni_listener_sys_init()) != 0) ||
((rv = nni_dialer_sys_init()) != 0) ||
((rv = nni_pipe_sys_init()) != 0) ||
+ ((rv = nni_tls_sys_init()) != 0) ||
((rv = nni_proto_sys_init()) != 0) ||
((rv = nni_tran_sys_init()) != 0)) {
nni_fini();
@@ -71,6 +75,7 @@ nni_fini(void)
}
nni_tran_sys_fini();
nni_proto_sys_fini();
+ nni_tls_sys_fini();
nni_pipe_sys_fini();
nni_dialer_sys_fini();
nni_listener_sys_fini();
diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c
index ecba84ae..68f0f61c 100644
--- a/src/supplemental/http/http_client.c
+++ b/src/supplemental/http/http_client.c
@@ -9,13 +9,10 @@
// found online at https://opensource.org/licenses/MIT.
//
-#include <ctype.h>
#include <stdbool.h>
-#include <stdio.h>
#include <string.h>
#include "core/nng_impl.h"
-#include "supplemental/tls/tls_api.h"
#include <nng/supplemental/tls/tls.h>
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index d3b595fd..1b90c172 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 QXSoftware <lh563566994@126.com>
// Copyright 2019 Devolutions <info@devolutions.net>
@@ -19,7 +19,6 @@
#include "core/nng_impl.h"
#include "nng/supplemental/tls/tls.h"
-#include "supplemental/tls/tls_api.h"
#include "http_api.h"
diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt
index dfeb6526..61d9f2fb 100644
--- a/src/supplemental/tls/CMakeLists.txt
+++ b/src/supplemental/tls/CMakeLists.txt
@@ -1,6 +1,6 @@
#
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2018 Capitar IT Group BV <info@capitar.com>
-# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2019 Devolutions <info@devolutions.net>
#
@@ -11,38 +11,29 @@
# found online at https://opensource.org/licenses/MIT.
#
-set(_SRCS ${PROJECT_SOURCE_DIR}/include/nng/supplemental/tls/tls.h)
-
-if (NNG_SUPP_TLS)
- set(NNG_SUPP_TLS_MBEDTLS ON)
- set(_DEFS -DNNG_SUPP_TLS)
-
- list(APPEND _SRCS supplemental/tls/tls_common.c)
-endif()
-
-# For now we only support the ARM mbedTLS library.
-if (NNG_SUPP_TLS_MBEDTLS)
-
- message(WARNING "
- ************************************************************
- Linking against mbedTLS changes license terms (Apache 2.0).
- Consult a lawyer and the license files for details.
- ************************************************************")
-
- # If mbedTLS was added by a consuming project, then we should use that
- # instance of it, instead of configuring our own.
- if (TARGET mbedtls)
- set(_LIBS mbedtls)
- else()
- find_package(mbedTLS REQUIRED)
- set(_LIBS ${MBEDTLS_LIBRARIES})
- set(_INCS ${MBEDTLS_INCLUDE_DIR})
- endif()
- list(APPEND _SRCS supplemental/tls/mbedtls/tls.c)
-
-else()
- list(APPEND _SRCS supplemental/tls/none/tls.c)
-endif()
+
+if (NNG_ENABLE_TLS)
+ set(NNG_TLS_ENGINES mbed wolf none)
+ # We assume Mbed for now. (Someday replaced perhaps with Bear.)
+ set(NNG_TLS_ENGINE mbed CACHE STRING "TLS engine to use.")
+ set_property(CACHE NNG_TLS_ENGINE PROPERTY STRINGS ${NNG_TLS_ENGINES})
+else ()
+ set(NNG_TLS_ENGINE none)
+endif ()
+
+# default TLS implementation for now is Mbed.
+nng_headers(nng/supplemental/tls/tls.h)
+nng_headers(nng/supplemental/tls/engine.h)
+
+if (NOT NNG_TLS_ENGINE STREQUAL "none")
+ nng_test(tls_test)
+endif ()
+
+add_subdirectory(mbedtls)
+add_subdirectory(wolfssl)
+
+nng_sources(tls_common.c)
+nng_sources(tls_api.h)
list(APPEND NNG_DEFS ${_DEFS})
list(APPEND NNG_SRCS ${_SRCS})
diff --git a/src/supplemental/tls/mbedtls/CMakeLists.txt b/src/supplemental/tls/mbedtls/CMakeLists.txt
new file mode 100644
index 00000000..22c8e1c6
--- /dev/null
+++ b/src/supplemental/tls/mbedtls/CMakeLists.txt
@@ -0,0 +1,36 @@
+#
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+#
+# This software is supplied under the terms of the MIT License, a
+# copy of which should be located in the distribution where this
+# file was obtained (LICENSE.txt). A copy of the license may also be
+# found online at https://opensource.org/licenses/MIT.
+#
+
+if (NNG_TLS_ENGINE STREQUAL "mbed")
+ message(WARNING "
+ ************************************************************
+ Linking against Mbed TLS changes license terms (Apache 2.0).
+ Consult a lawyer and the license files for details.
+ ************************************************************")
+ nng_sources(tls.c)
+ nng_defines(NNG_TLS_ENGINE_INIT=nng_tls_engine_init_mbed)
+ nng_defines(NNG_TLS_ENGINE_FINI=nng_tls_engine_fini_mbed)
+ nng_defines(NNG_SUPP_TLS)
+
+ # If Mbed TLS was added by a consuming project, then we should use that
+ # instance of it, instead of configuring our own.
+ if (TARGET mbedtls)
+ set(_LIBS mbedtls)
+ else()
+ find_package(mbedTLS REQUIRED)
+ set(_LIBS ${MBEDTLS_LIBRARIES})
+ set(_INCS ${MBEDTLS_INCLUDE_DIR})
+ endif()
+
+ list(APPEND NNG_LIBS ${_LIBS})
+ list(APPEND NNG_INCS ${_INCS})
+
+ set(NNG_LIBS ${NNG_LIBS} PARENT_SCOPE)
+ set(NNG_INCS ${NNG_INCS} PARENT_SCOPE)
+endif()
diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c
index b0ac6ec5..e378d304 100644
--- a/src/supplemental/tls/mbedtls/tls.c
+++ b/src/supplemental/tls/mbedtls/tls.c
@@ -28,88 +28,35 @@
#include "mbedtls/ssl.h"
#include "core/nng_impl.h"
-#include "core/tcp.h"
-#include "supplemental/tls/tls_api.h"
-
-// Implementation note. This implementation buffers data between the TLS
-// encryption layer (mbedTLS) and the underlying TCP socket. As a result,
-// there may be some additional latency caused by buffer draining and
-// refilling. In the future we might want to investigate some kind of
-// double buffer policy to allow data to flow without entering a true
-// empty state.
-
-// NNG_TLS_MAX_SEND_SIZE limits the amount of data we will buffer for sending,
-// exerting backpressure if this size is exceeded. The 16K is aligned to the
-// maximum TLS record size.
-#ifndef NNG_TLS_MAX_SEND_SIZE
-#define NNG_TLS_MAX_SEND_SIZE 16384
-#endif
-
-// NNG_TLS_MAX_RECV_SIZE limits the amount of data we will receive in a single
-// operation. As we have to buffer data, this drives the size of our
-// intermediary buffer. The 16K is aligned to the maximum TLS record size.
-#ifndef NNG_TLX_MAX_RECV_SIZE
-#define NNG_TLS_MAX_RECV_SIZE 16384
-#endif
+#include <nng/supplemental/tls/engine.h>
-typedef struct nni_tls_certkey {
+// pair holds a private key and the associated certificate.
+typedef struct {
mbedtls_x509_crt crt;
mbedtls_pk_context key;
nni_list_node node;
-} nni_tls_certkey;
+} pair;
-typedef struct {
- nni_tls_common com;
- nng_stream * tcp;
- mbedtls_ssl_context ctx;
- nng_tls_config * cfg; // kept so we can release it
- nni_mtx lk;
- nni_aio * tcp_send;
- nni_aio * tcp_recv;
- bool sending;
- bool recving;
- bool closed;
- bool hsdone;
- bool tls_closed; // upper TLS layer closed
- bool tcp_closed; // underlying TCP buffer closed
- uint8_t * sendbuf; // send buffer
- size_t sendlen; // amount of data in send buffer
- size_t sendoff; // offset of start of send data
- uint8_t * recvbuf; // recv buffer
- size_t recvlen; // amount of data in recv buffer
- size_t recvoff; // offset of start of recv data
- nni_list sends; // upper side sends
- nni_list recvs; // upper recv aios
- nni_aio * handshake; // handshake aio (upper)
- nni_reap_item reap;
-} tls;
-
-struct nng_tls_config {
- mbedtls_ssl_config cfg_ctx;
- nni_mtx lk;
- bool active;
- char * server_name;
#ifdef NNG_TLS_USE_CTR_DRBG
- mbedtls_ctr_drbg_context rng_ctx;
- nni_mtx rng_lk;
+// Use a global RNG if we're going to override the builtin.
+static mbedtls_ctr_drbg_context rng_ctx;
+static nni_mtx rng_lock;
#endif
- mbedtls_x509_crt ca_certs;
- mbedtls_x509_crl crl;
- nni_atomic_u64 refcnt;
-
- nni_list certkeys;
+struct nng_tls_engine_conn {
+ void * tls; // parent conn
+ mbedtls_ssl_context ctx;
};
-static void tls_send_cb(void *);
-static void tls_recv_cb(void *);
-
-static void tls_do_send(tls *);
-static void tls_do_recv(tls *);
-static void tls_do_handshake(tls *);
-
-static int tls_net_send(void *, const unsigned char *, size_t);
-static int tls_net_recv(void *, unsigned char *, size_t);
+struct nng_tls_engine_config {
+ mbedtls_ssl_config cfg_ctx;
+ char * server_name;
+ mbedtls_x509_crt ca_certs;
+ mbedtls_x509_crl crl;
+ int min_ver;
+ int max_ver;
+ nni_list pairs;
+};
static void
tls_dbg(void *ctx, int level, const char *file, int line, const char *s)
@@ -141,115 +88,18 @@ static int
tls_random(void *arg, unsigned char *buf, size_t sz)
{
#ifdef NNG_TLS_USE_CTR_DRBG
- int rv;
- nng_tls_config *cfg = arg;
- NNI_ARG_UNUSED(arg);
+ int rv;
- nni_mtx_lock(&cfg->rng_lk);
- rv = mbedtls_ctr_drbg_random(&cfg->rng_ctx, buf, sz);
- nni_mtx_unlock(&cfg->rng_lk);
+ nni_mtx_lock(&rng_lock);
+ rv = mbedtls_ctr_drbg_random(&rng_ctx, buf, sz);
+ nni_mtx_unlock(&rng_lock);
return (rv);
#else
return (tls_get_entropy(arg, buf, sz));
#endif
}
-void
-nni_tls_config_fini(nng_tls_config *cfg)
-{
- nni_tls_certkey *ck;
-
- if (cfg != NULL) {
- if (nni_atomic_dec64_nv(&cfg->refcnt) != 0) {
- return;
- }
-
- mbedtls_ssl_config_free(&cfg->cfg_ctx);
-#ifdef NNG_TLS_USE_CTR_DRBG
- mbedtls_ctr_drbg_free(&cfg->rng_ctx);
-#endif
- mbedtls_x509_crt_free(&cfg->ca_certs);
- mbedtls_x509_crl_free(&cfg->crl);
- if (cfg->server_name) {
- nni_strfree(cfg->server_name);
- }
- while ((ck = nni_list_first(&cfg->certkeys))) {
- nni_list_remove(&cfg->certkeys, ck);
- mbedtls_x509_crt_free(&ck->crt);
- mbedtls_pk_free(&ck->key);
-
- NNI_FREE_STRUCT(ck);
- }
- nni_mtx_fini(&cfg->lk);
- NNI_FREE_STRUCT(cfg);
- }
-}
-
-int
-nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode)
-{
- nng_tls_config *cfg;
- int rv;
- int sslmode;
- int authmode;
-
- if ((cfg = NNI_ALLOC_STRUCT(cfg)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_atomic_init64(&cfg->refcnt);
- nni_atomic_inc64(&cfg->refcnt);
- nni_mtx_init(&cfg->lk);
- if (mode == NNG_TLS_MODE_SERVER) {
- sslmode = MBEDTLS_SSL_IS_SERVER;
- authmode = MBEDTLS_SSL_VERIFY_NONE;
- } else {
- sslmode = MBEDTLS_SSL_IS_CLIENT;
- authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
- }
-
- NNI_LIST_INIT(&cfg->certkeys, nni_tls_certkey, node);
- mbedtls_ssl_config_init(&cfg->cfg_ctx);
- mbedtls_x509_crt_init(&cfg->ca_certs);
- mbedtls_x509_crl_init(&cfg->crl);
-
- rv = mbedtls_ssl_config_defaults(&cfg->cfg_ctx, sslmode,
- MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
- if (rv != 0) {
- nni_tls_config_fini(cfg);
- return (rv);
- }
-
- mbedtls_ssl_conf_authmode(&cfg->cfg_ctx, authmode);
-
- // We *require* TLS v1.2 or newer, which is also known as SSL v3.3.
- mbedtls_ssl_conf_min_version(&cfg->cfg_ctx,
- MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
-
-#ifdef NNG_TLS_USE_CTR_DRBG
- mbedtls_ctr_drbg_init(&cfg->rng_ctx);
- rv = mbedtls_ctr_drbg_seed(
- &cfg->rng_ctx, tls_get_entropy, NULL, NULL, 0);
- if (rv != 0) {
- nni_tls_config_fini(cfg);
- return (rv);
- }
-#endif
- mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg);
-
- mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg);
-
- *cpp = cfg;
- return (0);
-}
-
-void
-nni_tls_config_hold(nng_tls_config *cfg)
-{
- nni_atomic_inc64(&cfg->refcnt);
-}
-
-// tls_mkerr converts an mbed error to an NNG error. In all cases
-// we just encode with NNG_ETRANERR.
+// tls_mk_err converts an mbed error to an NNG error.
static struct {
int tls;
int nng;
@@ -267,7 +117,7 @@ static struct {
};
static int
-tls_mkerr(int err)
+tls_mk_err(int err)
{
for (int i = 0; tls_errs[i].tls != 0; i++) {
if (tls_errs[i].tls == err) {
@@ -277,762 +127,433 @@ tls_mkerr(int err)
return (NNG_ECRYPTO);
}
-// The common code should call this only after it has released
-// it's upper layer stuff.
-static void
-tls_reap(void *arg)
+static int
+net_send(void *tls, const unsigned char *buf, size_t len)
{
- tls *tls = arg;
+ size_t sz = len;
+ int rv;
- // Shut it all down first.
- if (tls->tcp != NULL) {
- nng_stream_close(tls->tcp);
- }
- nni_aio_stop(tls->tcp_send);
- nni_aio_stop(tls->tcp_recv);
- nni_aio_free(tls->com.aio);
-
- // And finalize / free everything.
- nng_stream_free(tls->tcp);
- nni_aio_free(tls->tcp_send);
- nni_aio_free(tls->tcp_recv);
- mbedtls_ssl_free(&tls->ctx);
- nng_tls_config_free(tls->com.cfg);
-
- if (tls->recvbuf != NULL) {
- nni_free(tls->recvbuf, NNG_TLS_MAX_RECV_SIZE);
- }
- if (tls->sendbuf != NULL) {
- nni_free(tls->sendbuf, NNG_TLS_MAX_RECV_SIZE);
+ rv = nng_tls_engine_send(tls, buf, &sz);
+ switch (rv) {
+ case 0:
+ return ((int) sz);
+ case NNG_EAGAIN:
+ return (MBEDTLS_ERR_SSL_WANT_WRITE);
+ default:
+ return (MBEDTLS_ERR_NET_SEND_FAILED);
}
- nni_mtx_fini(&tls->lk);
- memset(tls, 0xff, sizeof(*tls));
- NNI_FREE_STRUCT(tls);
-}
-
-static void
-tls_free(void *arg)
-{
- tls *tls = arg;
- nni_reap(&tls->reap, tls_reap, tls);
}
-int
-nni_tls_start(nng_stream *arg, nng_stream *tcp)
+static int
+net_recv(void *tls, unsigned char *buf, size_t len)
{
- tls * tp = (void *) arg;
- nng_tls_config *cfg = tp->com.cfg;
- int rv;
-
- if ((tp->recvbuf = nni_zalloc(NNG_TLS_MAX_RECV_SIZE)) == NULL) {
- return (NNG_ENOMEM);
- }
- if ((tp->sendbuf = nni_zalloc(NNG_TLS_MAX_SEND_SIZE)) == NULL) {
- return (NNG_ENOMEM);
- }
-
- nni_mtx_lock(&cfg->lk);
- // No more changes allowed to config.
- cfg->active = true;
- nni_mtx_unlock(&cfg->lk);
-
- mbedtls_ssl_init(&tp->ctx);
- mbedtls_ssl_set_bio(&tp->ctx, tp, tls_net_send, tls_net_recv, NULL);
-
- if ((rv = mbedtls_ssl_setup(&tp->ctx, &cfg->cfg_ctx)) != 0) {
- return (tls_mkerr(rv));
- }
-
- if (cfg->server_name) {
- mbedtls_ssl_set_hostname(&tp->ctx, cfg->server_name);
- }
-
- tp->tcp = tcp;
+ size_t sz = len;
+ int rv;
- if (((rv = nni_aio_alloc(&tp->tcp_send, tls_send_cb, tp)) != 0) ||
- ((rv = nni_aio_alloc(&tp->tcp_recv, tls_recv_cb, tp)) != 0)) {
- return (rv);
+ rv = nng_tls_engine_recv(tls, buf, &sz);
+ switch (rv) {
+ case 0:
+ return ((int) sz);
+ case NNG_EAGAIN:
+ return (MBEDTLS_ERR_SSL_WANT_READ);
+ default:
+ return (MBEDTLS_ERR_NET_RECV_FAILED);
}
-
- nni_mtx_lock(&tp->lk);
- // Kick off a handshake operation.
- tls_do_handshake(tp);
- nni_mtx_unlock(&tp->lk);
-
- return (0);
}
static void
-tls_cancel(nni_aio *aio, void *arg, int rv)
+conn_fini(nng_tls_engine_conn *ec)
{
- tls *tp = arg;
- nni_mtx_lock(&tp->lk);
- if (nni_aio_list_active(aio)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, rv);
- }
- nni_mtx_unlock(&tp->lk);
+ mbedtls_ssl_free(&ec->ctx);
}
-// tls_send_cb is called when the underlying TCP send completes.
-static void
-tls_send_cb(void *ctx)
-{
- tls * tp = ctx;
- nni_aio *aio = tp->tcp_send;
-
- nni_mtx_lock(&tp->lk);
- if (nni_aio_result(aio) != 0) {
- nng_stream_close(tp->tcp);
- tp->tcp_closed = true;
- } else {
- size_t n = nni_aio_count(aio);
- NNI_ASSERT(tp->sendlen >= n);
- tp->sendlen -= n;
- if (tp->sendlen) {
- nni_iov iov;
- tp->sendoff += n;
- iov.iov_buf = tp->sendbuf + tp->sendoff;
- iov.iov_len = tp->sendlen;
- nni_aio_set_iov(aio, 1, &iov);
- nni_aio_set_timeout(aio, NNG_DURATION_INFINITE);
- nng_stream_send(tp->tcp, aio);
- nni_mtx_unlock(&tp->lk);
- return;
- }
- tp->sendoff = 0;
- tp->sending = false;
- }
-
- tls_do_handshake(tp);
- if (tp->hsdone) {
- tls_do_send(tp);
- tls_do_recv(tp);
- }
- nni_mtx_unlock(&tp->lk);
-}
-
-static void
-tls_recv_start(tls *tp)
+// The common code should call this only after it has released
+// it's upper layer stuff.
+int
+conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg)
{
- nni_aio *aio;
- nni_iov iov;
+ int rv;
- if (tp->recving || tp->tcp_closed) {
- return;
- }
- // If we already have data, wait for that to be consumed before
- // doing another read.
- if (tp->recvlen != 0) {
- return;
- }
+ ec->tls = tls;
- tp->recving = 1;
- tp->recvoff = 0;
- aio = tp->tcp_recv;
- iov.iov_buf = tp->recvbuf;
- iov.iov_len = NNG_TLS_MAX_RECV_SIZE;
- nni_aio_set_iov(aio, 1, &iov);
- nni_aio_set_timeout(tp->tcp_recv, NNG_DURATION_INFINITE);
- nng_stream_recv(tp->tcp, aio);
-}
+ mbedtls_ssl_init(&ec->ctx);
+ mbedtls_ssl_set_bio(&ec->ctx, tls, net_send, net_recv, NULL);
-static void
-tls_recv_cb(void *ctx)
-{
- tls * tp = ctx;
- nni_aio *aio = tp->tcp_recv;
-
- nni_mtx_lock(&tp->lk);
- tp->recving = false;
- if (nni_aio_result(aio) != 0) {
- // Close the underlying TCP channel, but permit data we
- // already received to continue to be received.
- nng_stream_close(tp->tcp);
- tp->tcp_closed = true;
- } else {
- NNI_ASSERT(tp->recvlen == 0);
- NNI_ASSERT(tp->recvoff == 0);
- tp->recvlen = nni_aio_count(aio);
+ if ((rv = mbedtls_ssl_setup(&ec->ctx, &cfg->cfg_ctx)) != 0) {
+ return (tls_mk_err(rv));
}
- // If we were closed (above), the upper layer will detect and
- // react properly. Otherwise the upper layer will consume
- // data.
- tls_do_handshake(tp);
- if (tp->hsdone) {
- tls_do_recv(tp);
- tls_do_send(tp);
+ if (cfg->server_name != NULL) {
+ mbedtls_ssl_set_hostname(&ec->ctx, cfg->server_name);
}
- nni_mtx_unlock(&tp->lk);
+ return (0);
}
-// This handles the bottom half send (i.e. sending over TCP).
-// We always accept a chunk of data, to a limit, if the bottom
-// sender is not busy. Then we handle that in the background.
-// If the sender *is* busy, we return MBEDTLS_ERR_SSL_WANT_WRITE.
-// The chunk size we accept is 64k at a time, which prevents
-// ridiculous over queueing. This is always called with the pipe
-// lock held, and never blocks.
-static int
-tls_net_send(void *ctx, const unsigned char *buf, size_t len)
+static void
+conn_close(nng_tls_engine_conn *ec)
{
- tls * tp = ctx;
- nni_iov iov;
-
- if (len > NNG_TLS_MAX_SEND_SIZE) {
- len = NNG_TLS_MAX_SEND_SIZE;
- }
-
- // We should already be running with the pipe lock held,
- // as we are running in that context.
-
- if (tp->sending) {
- return (MBEDTLS_ERR_SSL_WANT_WRITE);
- }
- if (tp->tcp_closed) {
- return (MBEDTLS_ERR_NET_SEND_FAILED);
- }
-
- tp->sending = 1;
- tp->sendlen = len;
- tp->sendoff = 0;
- memcpy(tp->sendbuf, buf, len);
- iov.iov_buf = tp->sendbuf;
- iov.iov_len = len;
- nni_aio_set_iov(tp->tcp_send, 1, &iov);
- nni_aio_set_timeout(tp->tcp_send, NNG_DURATION_INFINITE);
- nng_stream_send(tp->tcp, tp->tcp_send);
- return (len);
+ // This may succeed, or it may fail. Either way we
+ // don't care. Implementations that depend on
+ // close-notify to mean anything are broken by design,
+ // just like RFC. Note that we do *NOT* close the TCP
+ // connection at this point.
+ (void) mbedtls_ssl_close_notify(&ec->ctx);
}
static int
-tls_net_recv(void *ctx, unsigned char *buf, size_t len)
+conn_recv(nng_tls_engine_conn *ec, uint8_t *buf, size_t *szp)
{
- tls *tp = ctx;
-
- // We should already be running with the pipe lock held,
- // as we are running in that context.
-
- if (tp->recvlen == 0) {
- if (tp->tcp_closed) {
- // The underlying TCP transport has closed, and we
- // have no more data in our receive buffer.
- return (MBEDTLS_ERR_NET_RECV_FAILED);
- }
- len = MBEDTLS_ERR_SSL_WANT_READ;
- } else {
- if (len > tp->recvlen) {
- len = tp->recvlen;
+ int rv;
+ if ((rv = mbedtls_ssl_read(&ec->ctx, buf, *szp)) < 0) {
+ switch (rv) {
+ case MBEDTLS_ERR_SSL_WANT_READ:
+ case MBEDTLS_ERR_SSL_WANT_WRITE:
+ return (NNG_EAGAIN);
+ default:
+ return (tls_mk_err(rv));
}
- memcpy(buf, tp->recvbuf + tp->recvoff, len);
- tp->recvoff += len;
- tp->recvlen -= len;
- }
-
- tls_recv_start(tp);
- return ((int) len);
-}
-
-static void
-tls_send(void *arg, nni_aio *aio)
-{
- int rv;
- tls *tp = arg;
-
- if (nni_aio_begin(aio) != 0) {
- return;
- }
- nni_mtx_lock(&tp->lk);
- if (tp->tls_closed) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, NNG_ECLOSED);
- return;
- }
- if ((rv = nni_aio_schedule(aio, tls_cancel, tp)) != 0) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, rv);
- return;
}
- nni_list_append(&tp->sends, aio);
- tls_do_send(tp);
- nni_mtx_unlock(&tp->lk);
-}
-
-static void
-tls_recv(void *arg, nni_aio *aio)
-{
- int rv;
- tls *tp = arg;
-
- if (nni_aio_begin(aio) != 0) {
- return;
- }
- nni_mtx_lock(&tp->lk);
- if (tp->tls_closed) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, NNG_ECLOSED);
- return;
- }
- if ((rv = nni_aio_schedule(aio, tls_cancel, tp)) != 0) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, rv);
- return;
- }
-
- nni_list_append(&tp->recvs, aio);
- tls_do_recv(tp);
- nni_mtx_unlock(&tp->lk);
-}
-
-static int
-tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t)
-{
- tls *tp = arg;
- bool v = (mbedtls_ssl_get_verify_result(&tp->ctx) == 0);
-
- return (nni_copyout_bool(v, buf, szp, t));
+ *szp = (size_t) rv;
+ return (0);
}
-static const nni_option tls_options[] = {
- {
- .o_name = NNG_OPT_TLS_VERIFIED,
- .o_get = tls_get_verified,
- },
- {
- .o_name = NULL,
- },
-};
-
static int
-tls_setx(void *arg, const char *name, const void *buf, size_t sz, nni_type t)
+conn_send(nng_tls_engine_conn *ec, const uint8_t *buf, size_t *szp)
{
- tls * tp = arg;
- int rv;
- nng_stream *tcp;
-
- tcp = (tp != NULL) ? tp->tcp : NULL;
+ int rv;
- if ((rv = nni_stream_setx(tcp, name, buf, sz, t)) != NNG_ENOTSUP) {
- return (rv);
+ if ((rv = mbedtls_ssl_write(&ec->ctx, buf, *szp)) < 0) {
+ switch (rv) {
+ case MBEDTLS_ERR_SSL_WANT_READ:
+ case MBEDTLS_ERR_SSL_WANT_WRITE:
+ return (NNG_EAGAIN);
+ default:
+ return (tls_mk_err(rv));
+ }
}
- return (nni_setopt(tls_options, name, tp, buf, sz, t));
+ *szp = (size_t) rv;
+ return (0);
}
static int
-tls_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
-{
- tls *tp = arg;
- int rv;
-
- if ((rv = nni_stream_getx(tp->tcp, name, buf, szp, t)) !=
- NNG_ENOTSUP) {
- return (rv);
- }
- return (nni_getopt(tls_options, name, tp, buf, szp, t));
-}
-
-static void
-tls_do_handshake(tls *tp)
+conn_handshake(nng_tls_engine_conn *ec)
{
- int rv;
- nni_aio *aio;
+ int rv;
- if (tp->hsdone || tp->tls_closed) {
- return;
- }
- rv = mbedtls_ssl_handshake(&tp->ctx);
+ rv = mbedtls_ssl_handshake(&ec->ctx);
switch (rv) {
case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_WANT_READ:
// We have underlying I/O to complete first. We will
// be called again by a callback later.
- return;
+ return (NNG_EAGAIN);
case 0:
// The handshake is done, yay!
- tp->hsdone = true;
- return;
+ return (0);
default:
- // some other error occurred, this causes us to tear it down
- nng_stream_close(tp->tcp);
- tp->tls_closed = true;
- tp->tcp_closed = true;
- rv = tls_mkerr(rv);
-
- while (((aio = nni_list_first(&tp->recvs)) != NULL) ||
- ((aio = nni_list_first(&tp->sends)) != NULL)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, rv);
- }
+ return (tls_mk_err(rv));
}
}
-// tls_do_send is called to try to send more data if we have not
-// yet completed the I/O. It also completes any transactions that
-// *have* completed. It must be called with the lock held.
-static void
-tls_do_send(tls *tp)
+static bool
+conn_verified(nng_tls_engine_conn *ec)
{
- nni_aio *aio;
-
- while ((aio = nni_list_first(&tp->sends)) != NULL) {
- int n;
- uint8_t *buf = NULL;
- size_t len = 0;
- nni_iov *iov;
- unsigned niov;
-
- nni_aio_get_iov(aio, &niov, &iov);
-
- for (unsigned i = 0; i < niov; i++) {
- if (iov[i].iov_len != 0) {
- buf = iov[i].iov_buf;
- len = iov[i].iov_len;
- break;
- }
- }
- if (len == 0 || buf == NULL) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, NNG_EINVAL);
- continue;
- }
-
- n = mbedtls_ssl_write(&tp->ctx, buf, len);
-
- if ((n == MBEDTLS_ERR_SSL_WANT_WRITE) ||
- (n == MBEDTLS_ERR_SSL_WANT_READ)) {
- // Cannot send any more data right now, wait
- // for callback.
- return;
- }
- // Some other error occurred... this is not good.
- // Want better diagnostics.
- nni_aio_list_remove(aio);
- if (n < 0) {
- nni_aio_finish_error(aio, tls_mkerr(n));
- } else {
- nni_aio_finish(aio, 0, n);
- }
- }
+ return (mbedtls_ssl_get_verify_result(&ec->ctx) == 0);
}
static void
-tls_do_recv(tls *tp)
+config_fini(nng_tls_engine_config *cfg)
{
- nni_aio *aio;
-
- while ((aio = nni_list_first(&tp->recvs)) != NULL) {
- int n;
- uint8_t *buf = NULL;
- size_t len = 0;
- nni_iov *iov;
- unsigned niov;
-
- nni_aio_get_iov(aio, &niov, &iov);
-
- for (unsigned i = 0; i < niov; i++) {
- if (iov[i].iov_len != 0) {
- buf = iov[i].iov_buf;
- len = iov[i].iov_len;
- break;
- }
- }
- if (len == 0 || buf == NULL) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, NNG_EINVAL);
- continue;
- }
- n = mbedtls_ssl_read(&tp->ctx, buf, len);
-
- if ((n == MBEDTLS_ERR_SSL_WANT_READ) ||
- (n == MBEDTLS_ERR_SSL_WANT_WRITE)) {
- // Cannot receive any more data right now, wait
- // for callback.
- return;
- }
+ pair *p;
- nni_aio_list_remove(aio);
+ mbedtls_ssl_config_free(&cfg->cfg_ctx);
+#ifdef NNG_TLS_USE_CTR_DRBG
+ mbedtls_ctr_drbg_free(&cfg->rng_ctx);
+#endif
+ mbedtls_x509_crt_free(&cfg->ca_certs);
+ mbedtls_x509_crl_free(&cfg->crl);
+ if (cfg->server_name) {
+ nni_strfree(cfg->server_name);
+ }
+ while ((p = nni_list_first(&cfg->pairs))) {
+ nni_list_remove(&cfg->pairs, p);
+ mbedtls_x509_crt_free(&p->crt);
+ mbedtls_pk_free(&p->key);
- if (n < 0) {
- nni_aio_finish_error(aio, tls_mkerr(n));
- } else {
- nni_aio_finish(aio, 0, n);
- }
+ NNI_FREE_STRUCT(p);
}
}
-static void
-tls_close(void *arg)
+static int
+config_init(nng_tls_engine_config *cfg, enum nng_tls_mode mode)
{
- tls * tp = arg;
- nni_aio *aio;
+ int rv;
+ int ssl_mode;
+ int auth_mode;
- nni_aio_close(tp->tcp_send);
- nni_aio_close(tp->tcp_recv);
+ if (mode == NNG_TLS_MODE_SERVER) {
+ ssl_mode = MBEDTLS_SSL_IS_SERVER;
+ auth_mode = MBEDTLS_SSL_VERIFY_NONE;
+ } else {
+ ssl_mode = MBEDTLS_SSL_IS_CLIENT;
+ auth_mode = MBEDTLS_SSL_VERIFY_REQUIRED;
+ }
- nni_mtx_lock(&tp->lk);
- tp->tls_closed = true;
+ NNI_LIST_INIT(&cfg->pairs, pair, node);
+ mbedtls_ssl_config_init(&cfg->cfg_ctx);
+ mbedtls_x509_crt_init(&cfg->ca_certs);
+ mbedtls_x509_crl_init(&cfg->crl);
- while (((aio = nni_list_first(&tp->sends)) != NULL) ||
- ((aio = nni_list_first(&tp->recvs)) != NULL)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, NNG_ECLOSED);
+ rv = mbedtls_ssl_config_defaults(&cfg->cfg_ctx, ssl_mode,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ if (rv != 0) {
+ config_fini(cfg);
+ return (rv);
}
- if (tp->hsdone) {
- // This may succeed, or it may fail. Either way we
- // don't care. Implementations that depend on
- // close-notify to mean anything are broken by design,
- // just like RFC. Note that we do *NOT* close the TCP
- // connection at this point.
- (void) mbedtls_ssl_close_notify(&tp->ctx);
- } else {
- nng_stream_close(tp->tcp);
- }
- nni_mtx_unlock(&tp->lk);
-}
+ mbedtls_ssl_conf_authmode(&cfg->cfg_ctx, auth_mode);
-// This allocates a TLS structure, that can be used by the caller.
-// The reason we have this API is so that the base structure can be
-// embedded in the parent structure.
-int
-nni_tls_alloc(nng_stream **tlsp)
-{
- tls *tp;
+ // Default: we *require* TLS v1.2 or newer, which is also known as
+ // SSL v3.3. As of this writing, Mbed TLS still does not support
+ // version 1.3, and we would want to test it before enabling it here.
+ cfg->min_ver = MBEDTLS_SSL_MINOR_VERSION_3;
+ cfg->max_ver = MBEDTLS_SSL_MINOR_VERSION_3;
+
+ mbedtls_ssl_conf_min_version(
+ &cfg->cfg_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, cfg->min_ver);
+ mbedtls_ssl_conf_max_version(
+ &cfg->cfg_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, cfg->max_ver);
+
+ mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg);
+ mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg);
- if ((tp = NNI_ALLOC_STRUCT(tp)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_aio_list_init(&tp->sends);
- nni_aio_list_init(&tp->recvs);
- nni_mtx_init(&tp->lk);
- tp->com.ops.s_close = tls_close;
- tp->com.ops.s_free = tls_free;
- tp->com.ops.s_send = tls_send;
- tp->com.ops.s_recv = tls_recv;
- tp->com.ops.s_getx = tls_getx;
- tp->com.ops.s_setx = tls_setx;
-
- *tlsp = (void *) tp;
return (0);
}
-int
-nng_tls_config_server_name(nng_tls_config *cfg, const char *name)
+static int
+config_server_name(nng_tls_engine_config *cfg, const char *name)
{
- int rv;
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- nni_mtx_unlock(&cfg->lk);
- return (NNG_ESTATE);
+ char *dup;
+ if ((dup = strdup(name)) == NULL) {
+ return (NNG_ENOMEM);
}
if (cfg->server_name) {
nni_strfree(cfg->server_name);
}
- cfg->server_name = nni_strdup(name);
- rv = cfg->server_name == NULL ? NNG_ENOMEM : 0;
- nni_mtx_unlock(&cfg->lk);
- return (rv);
+ cfg->server_name = dup;
+ return (0);
}
-int
-nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode)
+static int
+config_auth_mode(nng_tls_engine_config *cfg, nng_tls_auth_mode mode)
{
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- nni_mtx_unlock(&cfg->lk);
- return (NNG_ESTATE);
- }
switch (mode) {
case NNG_TLS_AUTH_MODE_NONE:
mbedtls_ssl_conf_authmode(
&cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_NONE);
- break;
+ return (0);
case NNG_TLS_AUTH_MODE_OPTIONAL:
mbedtls_ssl_conf_authmode(
&cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_OPTIONAL);
- break;
+ return (0);
case NNG_TLS_AUTH_MODE_REQUIRED:
mbedtls_ssl_conf_authmode(
&cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_REQUIRED);
- break;
- default:
- nni_mtx_unlock(&cfg->lk);
- return (NNG_EINVAL);
+ return (0);
}
- nni_mtx_unlock(&cfg->lk);
- return (0);
+ return (NNG_EINVAL);
}
-int
-nng_tls_config_ca_chain(
- nng_tls_config *cfg, const char *certs, const char *crl)
+static int
+config_ca_chain(nng_tls_engine_config *cfg, const char *certs, const char *crl)
{
size_t len;
const uint8_t *pem;
int rv;
// Certs and CRL are in PEM data, with terminating NUL byte.
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- rv = NNG_ESTATE;
- goto err;
- }
pem = (const uint8_t *) certs;
len = strlen(certs) + 1;
if ((rv = mbedtls_x509_crt_parse(&cfg->ca_certs, pem, len)) != 0) {
- rv = tls_mkerr(rv);
- goto err;
+ return (tls_mk_err(rv));
}
if (crl != NULL) {
pem = (const uint8_t *) crl;
len = strlen(crl) + 1;
if ((rv = mbedtls_x509_crl_parse(&cfg->crl, pem, len)) != 0) {
- rv = tls_mkerr(rv);
- goto err;
+ return (tls_mk_err(rv));
}
}
mbedtls_ssl_conf_ca_chain(&cfg->cfg_ctx, &cfg->ca_certs, &cfg->crl);
-
-err:
- nni_mtx_unlock(&cfg->lk);
- return (rv);
+ return (0);
}
-int
-nng_tls_config_own_cert(
- nng_tls_config *cfg, const char *cert, const char *key, const char *pass)
+static int
+config_own_cert(nng_tls_engine_config *cfg, const char *cert, const char *key,
+ const char *pass)
{
- size_t len;
- const uint8_t * pem;
- nni_tls_certkey *ck;
- int rv;
+ size_t len;
+ const uint8_t *pem;
+ pair * p;
+ int rv;
- if ((ck = NNI_ALLOC_STRUCT(ck)) == NULL) {
+ if ((p = NNI_ALLOC_STRUCT(p)) == NULL) {
return (NNG_ENOMEM);
}
- mbedtls_x509_crt_init(&ck->crt);
- mbedtls_pk_init(&ck->key);
+ mbedtls_x509_crt_init(&p->crt);
+ mbedtls_pk_init(&p->key);
pem = (const uint8_t *) cert;
len = strlen(cert) + 1;
- if ((rv = mbedtls_x509_crt_parse(&ck->crt, pem, len)) != 0) {
- rv = tls_mkerr(rv);
+ if ((rv = mbedtls_x509_crt_parse(&p->crt, pem, len)) != 0) {
+ rv = tls_mk_err(rv);
goto err;
}
pem = (const uint8_t *) key;
len = strlen(key) + 1;
- rv = mbedtls_pk_parse_key(&ck->key, pem, len, (const uint8_t *) pass,
+ rv = mbedtls_pk_parse_key(&p->key, pem, len, (const uint8_t *) pass,
pass != NULL ? strlen(pass) : 0);
if (rv != 0) {
- rv = tls_mkerr(rv);
+ rv = tls_mk_err(rv);
goto err;
}
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- nni_mtx_unlock(&cfg->lk);
- rv = NNG_ESTATE;
- goto err;
- }
- rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &ck->crt, &ck->key);
+ rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &p->crt, &p->key);
if (rv != 0) {
- nni_mtx_unlock(&cfg->lk);
- rv = tls_mkerr(rv);
+ rv = tls_mk_err(rv);
goto err;
}
// Save this structure so we can free it with the context.
- nni_list_append(&cfg->certkeys, ck);
- nni_mtx_unlock(&cfg->lk);
+ nni_list_append(&cfg->pairs, p);
return (0);
err:
- mbedtls_x509_crt_free(&ck->crt);
- mbedtls_pk_free(&ck->key);
- NNI_FREE_STRUCT(ck);
+ mbedtls_x509_crt_free(&p->crt);
+ mbedtls_pk_free(&p->key);
+ NNI_FREE_STRUCT(p);
return (rv);
}
-int
-nng_tls_config_ca_file(nng_tls_config *cfg, const char *path)
+static int
+config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver,
+ nng_tls_version max_ver)
{
- int rv;
- void * fdata;
- size_t fsize;
- char * pem;
- // Note that while mbedTLS supports its own file methods, we want
- // to avoid depending on that because it might not have been
- // included, so we use our own. We have to read the file, and
- // then allocate a buffer that has an extra byte so we can
- // ensure NUL termination. The file named by path may contain
- // both a ca chain, and crl chain, or just a ca chain.
- if ((rv = nni_file_get(path, &fdata, &fsize)) != 0) {
- return (rv);
+ int v1, v2;
+ int maj = MBEDTLS_SSL_MAJOR_VERSION_3;
+
+ if (min_ver > max_ver) {
+ return (NNG_ENOTSUP);
}
- if ((pem = nni_zalloc(fsize + 1)) == NULL) {
- nni_free(fdata, fsize);
- return (NNG_ENOMEM);
+ switch (min_ver) {
+ case NNG_TLS_1_0:
+ v1 = MBEDTLS_SSL_MINOR_VERSION_1;
+ break;
+ case NNG_TLS_1_1:
+ v1 = MBEDTLS_SSL_MINOR_VERSION_2;
+ break;
+ case NNG_TLS_1_2:
+ v1 = MBEDTLS_SSL_MINOR_VERSION_3;
+ break;
+ default:
+ return (NNG_ENOTSUP);
}
- memcpy(pem, fdata, fsize);
- nni_free(fdata, fsize);
- if (strstr(pem, "-----BEGIN X509 CRL-----") != NULL) {
- rv = nng_tls_config_ca_chain(cfg, pem, pem);
- } else {
- rv = nng_tls_config_ca_chain(cfg, pem, NULL);
+
+ switch (max_ver) {
+ case NNG_TLS_1_0:
+ v2 = MBEDTLS_SSL_MINOR_VERSION_1;
+ break;
+ case NNG_TLS_1_1:
+ v2 = MBEDTLS_SSL_MINOR_VERSION_2;
+ break;
+ case NNG_TLS_1_2:
+ case NNG_TLS_1_3: // We lack support for 1.3, so treat as 1.2.
+ v2 = MBEDTLS_SSL_MINOR_VERSION_3;
+ break;
+ default:
+ // Note that this means that if we ever TLS 1.4 or 2.0,
+ // then this will break. That's sufficiently far out
+ // to justify not worrying about it.
+ return (NNG_ENOTSUP);
}
- nni_free(pem, fsize + 1);
- return (rv);
+
+ cfg->min_ver = v1;
+ cfg->max_ver = v2;
+ mbedtls_ssl_conf_min_version(&cfg->cfg_ctx, maj, cfg->min_ver);
+ mbedtls_ssl_conf_max_version(&cfg->cfg_ctx, maj, cfg->max_ver);
+ return (0);
}
+static nng_tls_engine_config_ops config_ops = {
+ .init = config_init,
+ .fini = config_fini,
+ .size = sizeof(nng_tls_engine_config),
+ .auth = config_auth_mode,
+ .ca_chain = config_ca_chain,
+ .own_cert = config_own_cert,
+ .server = config_server_name,
+ .version = config_version,
+};
+
+static nng_tls_engine_conn_ops conn_ops = {
+ .size = sizeof(nng_tls_engine_conn),
+ .init = conn_init,
+ .fini = conn_fini,
+ .close = conn_close,
+ .recv = conn_recv,
+ .send = conn_send,
+ .handshake = conn_handshake,
+ .verified = conn_verified,
+};
+
+static nng_tls_engine tls_engine_mbed = {
+ .version = NNG_TLS_ENGINE_VERSION,
+ .config_ops = &config_ops,
+ .conn_ops = &conn_ops,
+ .name = "mbed",
+ .description = MBEDTLS_VERSION_STRING_FULL,
+ .fips_mode = false,
+};
+
int
-nng_tls_config_cert_key_file(
- nng_tls_config *cfg, const char *path, const char *pass)
+nng_tls_engine_init_mbed(void)
{
- int rv;
- void * fdata;
- size_t fsize;
- char * pem;
-
- // Note that while mbedTLS supports its own file methods, we want
- // to avoid depending on that because it might not have been
- // included, so we use our own. We have to read the file, and
- // then allocate a buffer that has an extra byte so we can
- // ensure NUL termination. The file named by path must contain
- // both our certificate, and our private key. The password
- // may be NULL if the key is not encrypted.
- if ((rv = nni_file_get(path, &fdata, &fsize)) != 0) {
+ int rv;
+
+#ifdef NNG_TLS_USE_CTR_DRBG
+ nni_mtx_init(&rng_lock);
+
+ mbedtls_ctr_drbg_init(&cfg->rng_ctx);
+ rv = mbedtls_ctr_drbg_seed(&rng_ctx, tls_get_entropy, NULL, NULL, 0);
+ if (rv != 0) {
+ nni_mtx_fini(&rng_lock);
return (rv);
}
- if ((pem = nni_zalloc(fsize + 1)) == NULL) {
- nni_free(fdata, fsize);
- return (NNG_ENOMEM);
- }
- memcpy(pem, fdata, fsize);
- nni_free(fdata, fsize);
- rv = nng_tls_config_own_cert(cfg, pem, pem, pass);
- nni_free(pem, fsize + 1);
- return (rv);
-}
+#endif
+ // Uncomment the following to have noisy debug from mbedTLS.
+ // This may be useful when trying to debug failures.
+ // mbedtls_debug_set_threshold(3);
-int
-nng_tls_config_alloc(nng_tls_config **cfgp, nng_tls_mode mode)
-{
- return (nni_tls_config_init(cfgp, mode));
-}
+ rv = nng_tls_engine_register(&tls_engine_mbed);
-void
-nng_tls_config_free(nng_tls_config *cfg)
-{
- nni_tls_config_fini(cfg);
+#ifdef NNG_TLS_USE_CTR_DRBG
+ if (rv != 0) {
+ nni_mtx_fini(&rng_lock);
+ }
+#endif
+
+ return (rv);
}
void
-nng_tls_config_hold(nng_tls_config *cfg)
+nng_tls_engine_fini_mbed(void)
{
- nni_tls_config_hold(cfg);
-}
+#ifdef NNG_TLS_USE_CTR_DRBG
+ mbedtls_ctr_drbg_free(&rng_ctx);
+ nni_mtx_fini(&rng_lock);
+#endif
+} \ No newline at end of file
diff --git a/src/supplemental/tls/none/tls.c b/src/supplemental/tls/none/tls.c
deleted file mode 100644
index a1e70b73..00000000
--- a/src/supplemental/tls/none/tls.c
+++ /dev/null
@@ -1,154 +0,0 @@
-//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
-// Copyright 2018 Capitar IT Group BV <info@capitar.com>
-// Copyright 2018 Devolutions <info@devolutions.net>
-//
-// This software is supplied under the terms of the MIT License, a
-// copy of which should be located in the distribution where this
-// file was obtained (LICENSE.txt). A copy of the license may also be
-// found online at https://opensource.org/licenses/MIT.
-//
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// This file is only used when TLS support is not build into the library.
-// We provide stub functions only to satisfy linkage.
-
-#include "core/nng_impl.h"
-#include "supplemental/tls/tls_api.h"
-
-void
-nni_tls_config_fini(nng_tls_config *cfg)
-{
- NNI_ARG_UNUSED(cfg);
-}
-
-int
-nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode)
-{
- NNI_ARG_UNUSED(cpp);
- NNI_ARG_UNUSED(mode);
- return (NNG_ENOTSUP);
-}
-
-void
-nni_tls_config_hold(nng_tls_config *cfg)
-{
- NNI_ARG_UNUSED(cfg);
-}
-
-int
-nng_tls_config_server_name(nng_tls_config *cfg, const char *name)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(name);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(mode);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_ca_chain(
- nng_tls_config *cfg, const char *certs, const char *crl)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(certs);
- NNI_ARG_UNUSED(crl);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_own_cert(
- nng_tls_config *cfg, const char *cert, const char *key, const char *pass)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(cert);
- NNI_ARG_UNUSED(key);
- NNI_ARG_UNUSED(pass);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_ca_file(nng_tls_config *cfg, const char *path)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(path);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_cert_key_file(
- nng_tls_config *cfg, const char *path, const char *pass)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(path);
- NNI_ARG_UNUSED(pass);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_key(nng_tls_config *cfg, const uint8_t *key, size_t size)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(key);
- NNI_ARG_UNUSED(size);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_pass(nng_tls_config *cfg, const char *pass)
-{
- NNI_ARG_UNUSED(cfg);
- NNI_ARG_UNUSED(pass);
- return (NNG_ENOTSUP);
-}
-
-int
-nng_tls_config_alloc(nng_tls_config **cfgp, nng_tls_mode mode)
-{
-
- NNI_ARG_UNUSED(cfgp);
- NNI_ARG_UNUSED(mode);
- return (NNG_ENOTSUP);
-}
-
-void
-nng_tls_config_free(nng_tls_config *cfg)
-{
- NNI_ARG_UNUSED(cfg);
-}
-
-int
-nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
-{
- NNI_ARG_UNUSED(dp);
- NNI_ARG_UNUSED(url);
- return (NNG_ENOTSUP);
-}
-
-int
-nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url)
-{
- NNI_ARG_UNUSED(lp);
- NNI_ARG_UNUSED(url);
- return (NNG_ENOTSUP);
-}
-
-int
-nni_tls_checkopt(const char *nm, const void *buf, size_t sz, nni_type t)
-{
- NNI_ARG_UNUSED(nm);
- NNI_ARG_UNUSED(buf);
- NNI_ARG_UNUSED(sz);
- NNI_ARG_UNUSED(t);
- return (NNG_ENOTSUP);
-} \ No newline at end of file
diff --git a/src/supplemental/tls/tls_api.h b/src/supplemental/tls/tls_api.h
index 4e6146b1..642b9072 100644
--- a/src/supplemental/tls/tls_api.h
+++ b/src/supplemental/tls/tls_api.h
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
@@ -14,40 +14,10 @@
#include <nng/supplemental/tls/tls.h>
-// This nni_tls_common structure represents the "base" structure for
-// an implementation to extend. One of these must be the first member
-// of the implementation specific TLS stream struct.
-typedef struct {
- nng_stream ops;
- nni_aio * aio; // system aio for connect/accept
- nni_aio * uaio; // user aio for connect/accept
- nng_tls_config *cfg;
-} nni_tls_common;
-
// The implementation supplies this function to create the TLS connection
// object. All fields will be zeroed.
-extern int nni_tls_alloc(nng_stream **);
extern int nni_tls_dialer_alloc(nng_stream_dialer **, const nng_url *);
extern int nni_tls_listener_alloc(nng_stream_listener **, const nng_url *);
extern int nni_tls_checkopt(const char *, const void *, size_t, nni_type);
-// nni_tls_start is called by the common TLS dialer/listener completions
-// to start the TLS stream activity. This may also do allocations, etc.
-extern int nni_tls_start(nng_stream *, nng_stream *);
-
-// nni_tls_config_init creates a new TLS configuration object.
-// The object is created with a reference count of one.
-extern int nni_tls_config_init(nng_tls_config **, nng_tls_mode);
-
-// nni_tls_config_fini drops the reference on the configuration
-// object, deallocating if this was the last reference.
-extern void nni_tls_config_fini(nng_tls_config *);
-
-// nni_tls_config_hold is used to get a hold on the config
-// object, preventing it from being released inadvertently.
-// The hold is released with a call to nng_tls_config_fini().
-// Note that a hold need not be acquired at creation, since
-// the configuration object is created with a hold on it.
-extern void nni_tls_config_hold(nng_tls_config *);
-
#endif // NNG_SUPPLEMENTAL_TLS_TLS_API_H
diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c
index 7cccc1e9..7d89ea21 100644
--- a/src/supplemental/tls/tls_common.c
+++ b/src/supplemental/tls/tls_common.c
@@ -9,22 +9,90 @@
// found online at https://opensource.org/licenses/MIT.
//
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "core/nng_impl.h"
-#include "core/tcp.h"
-#include "supplemental/tls/tls_api.h"
+#include <nng/supplemental/tls/engine.h>
#include <nng/supplemental/tls/tls.h>
+// NNG_TLS_MAX_SEND_SIZE limits the amount of data we will buffer for sending,
+// exerting back-pressure if this size is exceeded. The 16K is aligned to the
+// maximum TLS record size.
+#ifndef NNG_TLS_MAX_SEND_SIZE
+#define NNG_TLS_MAX_SEND_SIZE 16384
+#endif
+
+// NNG_TLS_MAX_RECV_SIZE limits the amount of data we will receive in a single
+// operation. As we have to buffer data, this drives the size of our
+// intermediary buffer. The 16K is aligned to the maximum TLS record size.
+#ifndef NNG_TLX_MAX_RECV_SIZE
+#define NNG_TLS_MAX_RECV_SIZE 16384
+#endif
+
// This file contains common code for TLS, and is only compiled if we
// have TLS configured in the system. In particular, this provides the
// parts of TLS support that are invariant relative to different TLS
// libraries, such as dialer and listener support.
+#ifdef NNG_SUPP_TLS
+
+static const nng_tls_engine *tls_engine;
+static nni_mtx tls_engine_lock;
+
+struct nng_tls_config {
+ nng_tls_engine_config_ops ops;
+ const nng_tls_engine * engine; // store this so we can verify
+ nni_mtx lock;
+ int ref;
+ int busy;
+ size_t size;
+
+ // ... engine config data follows
+};
+
+typedef struct {
+ nng_stream stream;
+ nng_tls_engine_conn_ops ops;
+ nng_tls_config * cfg;
+ const nng_tls_engine * engine;
+ size_t size;
+ nni_aio * user_aio; // user's aio for connect/accept
+ nni_aio conn_aio; // system aio for connect/accept
+ nni_mtx lock;
+ bool closed;
+ bool hs_done;
+ nni_list send_queue;
+ nni_list recv_queue;
+ nng_stream * tcp; // lower level stream
+ nni_aio tcp_send; // lower level send pending
+ nni_aio tcp_recv; // lower level recv pending
+ uint8_t * tcp_send_buf;
+ uint8_t * tcp_recv_buf;
+ size_t tcp_recv_len;
+ size_t tcp_recv_off;
+ bool tcp_recv_pend;
+ bool tcp_send_active;
+ size_t tcp_send_len;
+ size_t tcp_send_head;
+ size_t tcp_send_tail;
+ struct nni_reap_item reap;
+
+ // ... engine connection data follows
+} tls_conn;
+
+static void tls_tcp_send_cb(void *arg);
+static void tls_tcp_recv_cb(void *arg);
+static void tls_do_send(tls_conn *);
+static void tls_do_recv(tls_conn *);
+static void tls_tcp_send_start(tls_conn *);
+static void tls_free(void *);
+static int tls_alloc(tls_conn **, nng_tls_config *, nng_aio *);
+static int tls_start(tls_conn *, nng_stream *);
+static void tls_tcp_error(tls_conn *, int);
+
typedef struct {
nng_stream_dialer ops;
nng_stream_dialer *d; // underlying TCP dialer
@@ -57,28 +125,26 @@ tls_dialer_free(void *arg)
static void
tls_conn_cb(void *arg)
{
- nng_stream * tls = arg;
- nni_tls_common *com = arg;
- nng_stream * tcp;
- int rv;
+ tls_conn * conn = arg;
+ nng_stream *tcp;
+ int rv;
- if ((rv = nni_aio_result(com->aio)) != 0) {
- nni_aio_finish_error(com->uaio, rv);
- nng_stream_free(tls);
+ if ((rv = nni_aio_result(&conn->conn_aio)) != 0) {
+ nni_aio_finish_error(conn->user_aio, rv);
+ nng_stream_free(&conn->stream);
return;
}
- tcp = nni_aio_get_output(com->aio, 0);
+ tcp = nni_aio_get_output(&conn->conn_aio, 0);
- if ((rv = nni_tls_start(tls, tcp)) != 0) {
- nni_aio_finish_error(com->uaio, rv);
- nng_stream_free(tcp);
- nng_stream_free(tls);
+ if ((rv = tls_start(conn, tcp)) != 0) {
+ nni_aio_finish_error(conn->user_aio, rv);
+ nng_stream_free(&conn->stream);
return;
}
- nni_aio_set_output(com->uaio, 0, tls);
- nni_aio_finish(com->uaio, 0, 0);
+ nni_aio_set_output(conn->user_aio, 0, &conn->stream);
+ nni_aio_finish(conn->user_aio, 0, 0);
}
// Dialer cancel is called when the user has indicated that they no longer
@@ -86,52 +152,35 @@ tls_conn_cb(void *arg)
static void
tls_conn_cancel(nni_aio *aio, void *arg, int rv)
{
- nni_tls_common *com = arg;
- NNI_ASSERT(com->uaio == aio);
+ tls_conn *conn = arg;
+ NNI_ASSERT(conn->user_aio == aio);
// Just pass this down. If the connection is already done, this
// will have no effect.
- nni_aio_abort(com->aio, rv);
+ nni_aio_abort(&conn->conn_aio, rv);
}
static void
tls_dialer_dial(void *arg, nng_aio *aio)
{
- tls_dialer * d = arg;
- int rv;
- nng_stream * tls;
- nni_tls_common *com;
+ tls_dialer *d = arg;
+ int rv;
+ tls_conn * conn;
if (nni_aio_begin(aio) != 0) {
return;
}
- if ((rv = nni_tls_alloc(&tls)) != 0) {
+ if ((rv = tls_alloc(&conn, d->cfg, aio)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
- com = (void *) tls;
- if ((rv = nni_aio_alloc(&com->aio, tls_conn_cb, tls)) != 0) {
+ if ((rv = nni_aio_schedule(aio, tls_conn_cancel, conn)) != 0) {
nni_aio_finish_error(aio, rv);
- nng_stream_free(tls);
+ tls_free(conn);
return;
}
- com->uaio = aio;
- // Save a copy of the TLS configuration. This way we don't have
- // to ensure that the dialer outlives the connection, because the
- // only shared data is the configuration which is reference counted.
- nni_mtx_lock(&d->lk);
- com->cfg = d->cfg;
- nng_tls_config_hold(com->cfg);
- nni_mtx_unlock(&d->lk);
-
- if ((rv = nni_aio_schedule(aio, tls_conn_cancel, tls)) != 0) {
- nni_aio_finish_error(aio, rv);
- nng_stream_free(tls);
- return;
- }
-
- nng_stream_dialer_dial(d->d, com->aio);
+ nng_stream_dialer_dial(d->d, &conn->conn_aio);
}
static int
@@ -307,10 +356,12 @@ nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
{
tls_dialer *d;
int rv;
- nng_url myurl;
+ nng_url my_url;
- memcpy(&myurl, url, sizeof(myurl));
- myurl.u_scheme = url->u_scheme + strlen("tls+");
+ memcpy(&my_url, url, sizeof(my_url));
+ if (strncmp(my_url.u_scheme, "tls+", 4) == 0) {
+ my_url.u_scheme += 4;
+ }
if ((rv = nni_init()) != 0) {
return (rv);
@@ -320,7 +371,7 @@ nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
}
nni_mtx_init(&d->lk);
- if ((rv = nng_stream_dialer_alloc_url(&d->d, &myurl)) != 0) {
+ if ((rv = nng_stream_dialer_alloc_url(&d->d, &my_url)) != 0) {
nni_mtx_fini(&d->lk);
NNI_FREE_STRUCT(d);
return (rv);
@@ -381,41 +432,25 @@ tls_listener_listen(void *arg)
static void
tls_listener_accept(void *arg, nng_aio *aio)
{
- tls_listener * l = arg;
- int rv;
- nng_stream * tls;
- nni_tls_common *com;
+ tls_listener *l = arg;
+ int rv;
+ tls_conn * conn;
if (nni_aio_begin(aio) != 0) {
return;
}
- if ((rv = nni_tls_alloc(&tls)) != 0) {
+ if ((rv = tls_alloc(&conn, l->cfg, aio)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
- com = (void *) tls;
- if ((rv = nni_aio_alloc(&com->aio, tls_conn_cb, tls)) != 0) {
- nni_aio_finish_error(aio, rv);
- nng_stream_free(tls);
- return;
- }
- com->uaio = aio;
-
- // Save a copy of the TLS configuration. This way we don't have
- // to ensure that the dialer outlives the connection, because the
- // only shared data is the configuration which is reference counted.
- nni_mtx_lock(&l->lk);
- com->cfg = l->cfg;
- nng_tls_config_hold(com->cfg);
- nni_mtx_unlock(&l->lk);
- if ((rv = nni_aio_schedule(aio, tls_conn_cancel, tls)) != 0) {
+ if ((rv = nni_aio_schedule(aio, tls_conn_cancel, conn)) != 0) {
nni_aio_finish_error(aio, rv);
- nng_stream_free(tls);
+ tls_free(conn);
return;
}
- nng_stream_listener_accept(l->l, com->aio);
+ nng_stream_listener_accept(l->l, &conn->conn_aio);
}
static int
@@ -581,10 +616,13 @@ nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url)
{
tls_listener *l;
int rv;
- nng_url myurl;
+ nng_url my_url;
- memcpy(&myurl, url, sizeof(myurl));
- myurl.u_scheme = url->u_scheme + strlen("tls+");
+ memcpy(&my_url, url, sizeof(my_url));
+
+ if (strncmp(my_url.u_scheme, "tls+", 4) == 0) {
+ my_url.u_scheme += 4;
+ }
if ((rv = nni_init()) != 0) {
return (rv);
@@ -594,7 +632,7 @@ nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url)
}
nni_mtx_init(&l->lk);
- if ((rv = nng_stream_listener_alloc_url(&l->l, &myurl)) != 0) {
+ if ((rv = nng_stream_listener_alloc_url(&l->l, &my_url)) != 0) {
nni_mtx_fini(&l->lk);
NNI_FREE_STRUCT(l);
return (rv);
@@ -645,7 +683,7 @@ tls_check_auth_mode(const void *buf, size_t sz, nni_type t)
return (rv);
}
-static const nni_chkoption tls_chkopts[] = {
+static const nni_chkoption tls_check_opts[] = {
{
.o_name = NNG_OPT_TLS_CONFIG,
.o_check = tls_check_config,
@@ -676,9 +714,1033 @@ nni_tls_checkopt(const char *name, const void *data, size_t sz, nni_type t)
{
int rv;
- rv = nni_chkopt(tls_chkopts, name, data, sz, t);
+ rv = nni_chkopt(tls_check_opts, name, data, sz, t);
if (rv == NNG_ENOTSUP) {
rv = nni_stream_checkopt("tcp", name, data, sz, t);
}
return (rv);
}
+
+static void
+tls_cancel(nni_aio *aio, void *arg, int rv)
+{
+ tls_conn *conn = arg;
+ nni_mtx_lock(&conn->lock);
+ if (aio == nni_list_first(&conn->recv_queue)) {
+ nni_aio_abort(&conn->tcp_recv, rv);
+ } else if (aio == nni_list_first(&conn->send_queue)) {
+ nni_aio_abort(&conn->tcp_send, rv);
+ } else if (nni_aio_list_active(aio)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, rv);
+ }
+ nni_mtx_unlock(&conn->lock);
+}
+
+// tls_send implements the upper layer stream send operation.
+static void
+tls_send(void *arg, nni_aio *aio)
+{
+ int rv;
+ tls_conn *conn = arg;
+
+ if (nni_aio_begin(aio) != 0) {
+ return;
+ }
+ nni_mtx_lock(&conn->lock);
+ if (conn->closed) {
+ nni_mtx_unlock(&conn->lock);
+ nni_aio_finish_error(aio, NNG_ECLOSED);
+ return;
+ }
+ if ((rv = nni_aio_schedule(aio, tls_cancel, conn)) != 0) {
+ nni_mtx_unlock(&conn->lock);
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+ nni_list_append(&conn->send_queue, aio);
+ tls_do_send(conn);
+ nni_mtx_unlock(&conn->lock);
+}
+
+static void
+tls_recv(void *arg, nni_aio *aio)
+{
+ int rv;
+ tls_conn *conn = arg;
+
+ if (nni_aio_begin(aio) != 0) {
+ return;
+ }
+ nni_mtx_lock(&conn->lock);
+ if (conn->closed) {
+ nni_mtx_unlock(&conn->lock);
+ nni_aio_finish_error(aio, NNG_ECLOSED);
+ return;
+ }
+ if ((rv = nni_aio_schedule(aio, tls_cancel, conn)) != 0) {
+ nni_mtx_unlock(&conn->lock);
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+
+ nni_list_append(&conn->recv_queue, aio);
+ tls_do_recv(conn);
+ nni_mtx_unlock(&conn->lock);
+}
+
+static void
+tls_close(void *arg)
+{
+ tls_conn *conn = arg;
+
+ nni_mtx_lock(&conn->lock);
+ conn->ops.close((void *) (conn + 1));
+ tls_tcp_error(conn, NNG_ECLOSED);
+ nni_mtx_unlock(&conn->lock);
+ nng_stream_close(conn->tcp);
+}
+
+static int
+tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t)
+{
+ tls_conn *conn = arg;
+ bool v;
+
+ nni_mtx_lock(&conn->lock);
+ v = conn->ops.verified((void *) (conn + 1));
+ nni_mtx_unlock(&conn->lock);
+ return (nni_copyout_bool(v, buf, szp, t));
+}
+
+static const nni_option tls_options[] = {
+ {
+ .o_name = NNG_OPT_TLS_VERIFIED,
+ .o_get = tls_get_verified,
+ },
+ {
+ .o_name = NULL,
+ },
+};
+
+static int
+tls_setx(void *arg, const char *name, const void *buf, size_t sz, nni_type t)
+{
+ tls_conn * conn = arg;
+ int rv;
+ nng_stream *tcp;
+
+ tcp = (conn != NULL) ? conn->tcp : NULL;
+
+ if ((rv = nni_stream_setx(tcp, name, buf, sz, t)) != NNG_ENOTSUP) {
+ return (rv);
+ }
+ return (nni_setopt(tls_options, name, conn, buf, sz, t));
+}
+
+static int
+tls_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
+{
+ tls_conn *conn = arg;
+ int rv;
+
+ if ((rv = nni_stream_getx(conn->tcp, name, buf, szp, t)) !=
+ NNG_ENOTSUP) {
+ return (rv);
+ }
+ return (nni_getopt(tls_options, name, conn, buf, szp, t));
+}
+
+static int
+tls_alloc(tls_conn **conn_p, nng_tls_config *cfg, nng_aio *user_aio)
+{
+ tls_conn * conn;
+ const nng_tls_engine *eng;
+ size_t size;
+
+ eng = cfg->engine;
+
+ size = NNI_ALIGN_UP(sizeof(*conn)) + eng->conn_ops->size;
+
+ if ((conn = nni_zalloc(size)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ if (((conn->tcp_send_buf = nni_alloc(NNG_TLS_MAX_SEND_SIZE)) ==
+ NULL) ||
+ ((conn->tcp_recv_buf = nni_alloc(NNG_TLS_MAX_RECV_SIZE)) ==
+ NULL)) {
+ tls_free(conn);
+ return (NNG_ENOMEM);
+ }
+ conn->size = size;
+ conn->ops = *eng->conn_ops;
+ conn->engine = eng;
+ conn->user_aio = user_aio;
+ conn->cfg = cfg;
+
+ nni_aio_init(&conn->conn_aio, tls_conn_cb, conn);
+ nni_aio_init(&conn->tcp_recv, tls_tcp_recv_cb, conn);
+ nni_aio_init(&conn->tcp_send, tls_tcp_send_cb, conn);
+ nni_aio_list_init(&conn->send_queue);
+ nni_aio_list_init(&conn->recv_queue);
+ nni_mtx_init(&conn->lock);
+ nni_aio_set_timeout(&conn->tcp_send, NNG_DURATION_INFINITE);
+ nni_aio_set_timeout(&conn->tcp_recv, NNG_DURATION_INFINITE);
+
+ conn->stream.s_close = tls_close;
+ conn->stream.s_free = tls_free;
+ conn->stream.s_send = tls_send;
+ conn->stream.s_recv = tls_recv;
+ conn->stream.s_getx = tls_getx;
+ conn->stream.s_setx = tls_setx;
+
+ nng_tls_config_hold(cfg);
+ *conn_p = conn;
+ return (0);
+}
+
+static void
+tls_reap(void *arg)
+{
+ tls_conn *conn = arg;
+
+ // Shut it all down first. We should be freed.
+ if (conn->tcp != NULL) {
+ nng_stream_close(conn->tcp);
+ }
+ nni_aio_stop(&conn->conn_aio);
+ nni_aio_stop(&conn->tcp_send);
+ nni_aio_stop(&conn->tcp_recv);
+
+ conn->ops.fini((void *) (conn + 1));
+ nni_aio_fini(&conn->conn_aio);
+ nni_aio_fini(&conn->tcp_send);
+ nni_aio_fini(&conn->tcp_recv);
+ nng_stream_free(conn->tcp);
+ if (conn->cfg != NULL) {
+ nng_tls_config_free(conn->cfg); // this drops our hold on it
+ }
+ if (conn->tcp_send_buf != NULL) {
+ nni_free(conn->tcp_send_buf, NNG_TLS_MAX_SEND_SIZE);
+ }
+ if (conn->tcp_recv_buf != NULL) {
+ nni_free(conn->tcp_recv_buf, NNG_TLS_MAX_RECV_SIZE);
+ }
+ NNI_FREE_STRUCT(conn);
+}
+
+static void
+tls_free(void *arg)
+{
+ tls_conn *conn = arg;
+
+ nni_reap(&conn->reap, tls_reap, conn);
+}
+
+static int
+tls_start(tls_conn *conn, nng_stream *tcp)
+{
+ int rv;
+
+ conn->tcp = tcp;
+ rv = conn->ops.init(
+ (void *) (conn + 1), conn, (void *) (conn->cfg + 1));
+ return (rv);
+}
+
+static void
+tls_tcp_error(tls_conn *conn, int rv)
+{
+ // An error here is fatal. Shut it all down.
+ nni_aio *aio;
+ nng_stream_close(conn->tcp);
+ nni_aio_close(&conn->tcp_send);
+ nni_aio_close(&conn->tcp_recv);
+ while (((aio = nni_list_first(&conn->send_queue)) != NULL) ||
+ ((aio = nni_list_first(&conn->recv_queue)) != NULL)) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, rv);
+ }
+}
+
+static bool
+tls_do_handshake(tls_conn *conn)
+{
+ int rv;
+ if (conn->hs_done) {
+ return (true);
+ }
+ rv = conn->ops.handshake((void *) (conn + 1));
+ if (rv == NNG_EAGAIN) {
+ // We need more data.
+ return (false);
+ }
+ if (rv == 0) {
+ conn->hs_done = true;
+ return (true);
+ }
+ tls_tcp_error(conn, rv);
+ return (true);
+}
+
+static void
+tls_do_recv(tls_conn *conn)
+{
+ nni_aio *aio;
+
+ while ((aio = nni_list_first(&conn->recv_queue)) != NULL) {
+ uint8_t *buf = NULL;
+ size_t len = 0;
+ nni_iov *iov;
+ unsigned nio;
+ int rv;
+
+ nni_aio_get_iov(aio, &nio, &iov);
+
+ for (unsigned i = 0; i < nio; i++) {
+ if (iov[i].iov_len != 0) {
+ buf = iov[i].iov_buf;
+ len = iov[i].iov_len;
+ break;
+ }
+ }
+ if (len == 0 || buf == NULL) {
+ // Caller has asked to receive "nothing".
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, NNG_EINVAL);
+ continue;
+ }
+
+ rv = conn->ops.recv((void *) (conn + 1), buf, &len);
+ if (rv == NNG_EAGAIN) {
+ // Nothing more we can do, the engine doesn't
+ // have anything else for us (yet).
+ return;
+ }
+
+ // Unlike the send side, we want to return back to the
+ // caller as *soon* as we have some data.
+ nni_aio_list_remove(aio);
+
+ if (rv != 0) {
+ nni_aio_finish_error(aio, rv);
+ } else {
+ nni_aio_finish(aio, 0, len);
+ }
+ }
+}
+
+// tls_do_send attempts to send user data.
+static void
+tls_do_send(tls_conn *conn)
+{
+ nni_aio *aio;
+
+ while ((aio = nni_list_first(&conn->send_queue)) != NULL) {
+ uint8_t *buf = NULL;
+ size_t len = 0;
+ nni_iov *iov;
+ unsigned nio;
+ int rv;
+
+ nni_aio_get_iov(aio, &nio, &iov);
+
+ for (unsigned i = 0; i < nio; i++) {
+ if (iov[i].iov_len != 0) {
+ buf = iov[i].iov_buf;
+ len = iov[i].iov_len;
+ break;
+ }
+ }
+ if (len == 0 || buf == NULL) {
+ nni_aio_list_remove(aio);
+ // Presumably this means we've completed this
+ // one, lets preserve the count, and move to the
+ // next.
+ nni_aio_finish(aio, 0, nni_aio_count(aio));
+ continue;
+ }
+
+ // Ask the engine to send.
+ rv = conn->ops.send((void *) (conn + 1), buf, &len);
+ if (rv == NNG_EAGAIN) {
+ // Can't send any more, wait for callback.
+ return;
+ }
+
+ if (rv != 0) {
+ nni_aio_list_remove(aio);
+ nni_aio_finish_error(aio, rv);
+ } else {
+ nni_aio_list_remove(aio);
+ nni_aio_finish(aio, 0, len);
+ }
+ }
+}
+
+static void
+tls_tcp_send_cb(void *arg)
+{
+ tls_conn *conn = arg;
+ nng_aio * aio = &conn->tcp_send;
+ int rv;
+ size_t count;
+
+ nni_mtx_lock(&conn->lock);
+ conn->tcp_send_active = false;
+
+ if ((rv = nni_aio_result(aio)) != 0) {
+ tls_tcp_error(conn, rv);
+ nni_mtx_unlock(&conn->lock);
+ return;
+ }
+
+ count = nni_aio_count(aio);
+ NNI_ASSERT(count <= conn->tcp_send_len);
+ conn->tcp_send_len -= count;
+ tls_tcp_send_start(conn);
+
+ if (tls_do_handshake(conn)) {
+ tls_do_send(conn);
+ tls_do_recv(conn);
+ }
+
+ nni_mtx_unlock(&conn->lock);
+}
+
+static void
+tls_tcp_recv_cb(void *arg)
+{
+ tls_conn *conn = arg;
+ nni_aio * aio = &conn->tcp_recv;
+ int rv;
+
+ nni_mtx_lock(&conn->lock);
+
+ conn->tcp_recv_pend = false;
+ if ((rv = nni_aio_result(aio)) != 0) {
+ tls_tcp_error(conn, rv);
+ nni_mtx_unlock(&conn->lock);
+ return;
+ }
+
+ NNI_ASSERT(conn->tcp_recv_len == 0);
+ NNI_ASSERT(conn->tcp_recv_off == 0);
+ conn->tcp_recv_len = nni_aio_count(aio);
+
+ if (tls_do_handshake(conn)) {
+ tls_do_recv(conn);
+ tls_do_send(conn);
+ }
+
+ nni_mtx_unlock(&conn->lock);
+}
+
+static void
+tls_tcp_recv_start(tls_conn *conn)
+{
+ nng_iov iov;
+
+ if (conn->tcp_recv_len != 0) {
+ // We already have data in the buffer.
+ return;
+ }
+ if (conn->tcp_recv_pend) {
+ // Already have a receive in flight.
+ return;
+ }
+ conn->tcp_recv_off = 0;
+ iov.iov_len = NNG_TLS_MAX_RECV_SIZE;
+ iov.iov_buf = conn->tcp_recv_buf;
+
+ conn->tcp_recv_pend = true;
+ nng_aio_set_iov(&conn->tcp_recv, 1, &iov);
+
+ nng_stream_recv(conn->tcp, &conn->tcp_recv);
+}
+
+static void
+tls_tcp_send_start(tls_conn *conn)
+{
+ nni_iov iov[2];
+ unsigned nio = 0;
+ size_t len;
+ size_t tail;
+ size_t head;
+
+ if (conn->tcp_send_active) {
+ return;
+ }
+ if (conn->tcp_send_len == 0) {
+ return;
+ }
+ len = conn->tcp_send_len;
+ head = conn->tcp_send_head;
+ tail = conn->tcp_send_tail;
+
+ while (len > 0) {
+ size_t cnt;
+ if (tail < head) {
+ cnt = head - tail;
+ } else {
+ cnt = NNG_TLS_MAX_SEND_SIZE - tail;
+ }
+ if (cnt > len) {
+ cnt = len;
+ }
+ iov[nio].iov_buf = conn->tcp_send_buf + tail;
+ iov[nio].iov_len = cnt;
+ len -= cnt;
+ tail += cnt;
+ tail %= NNG_TLS_MAX_SEND_SIZE;
+ nio++;
+ }
+ conn->tcp_send_active = true;
+ conn->tcp_send_tail = tail;
+ nni_aio_set_iov(&conn->tcp_send, nio, iov);
+ nng_stream_send(conn->tcp, &conn->tcp_send);
+}
+
+int
+nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp)
+{
+ tls_conn *conn = arg;
+ size_t len = *szp;
+ size_t head = conn->tcp_send_head;
+ size_t tail = conn->tcp_send_tail;
+ size_t space;
+ size_t cnt;
+
+ space = NNG_TLS_MAX_SEND_SIZE - conn->tcp_send_len;
+
+ if (space == 0) {
+ return (NNG_EAGAIN);
+ }
+
+ if (conn->closed) {
+ return (NNG_ECLOSED);
+ }
+
+ if (len > space) {
+ len = space;
+ }
+
+ // We are committed at this point to sending out len bytes.
+ // Update this now, so that we can use len to update.
+ *szp = len;
+
+ while (len > 0) {
+ if (head >= tail) {
+ cnt = NNG_TLS_MAX_SEND_SIZE - head;
+ } else {
+ cnt = tail - head;
+ }
+ if (cnt > len) {
+ cnt = len;
+ }
+
+ memcpy(conn->tcp_send_buf + head, buf, cnt);
+ buf += cnt;
+ head += cnt;
+ head %= NNG_TLS_MAX_SEND_SIZE;
+ conn->tcp_send_len += cnt;
+ conn->tcp_send_head = head;
+ len -= cnt;
+ }
+
+ tls_tcp_send_start(conn);
+ return (0);
+}
+
+int
+nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp)
+{
+ tls_conn *conn = arg;
+ size_t len = *szp;
+
+ if (conn->closed) {
+ return (NNG_ECLOSED);
+ }
+ if (conn->tcp_recv_len == 0) {
+ tls_tcp_recv_start(conn);
+ return (NNG_EAGAIN);
+ }
+ if (len > conn->tcp_recv_len) {
+ len = conn->tcp_recv_len;
+ }
+ memcpy(buf, conn->tcp_recv_buf + conn->tcp_recv_off, len);
+ conn->tcp_recv_off += len;
+ conn->tcp_recv_len -= len;
+
+ // If we still have data left in the buffer, then the following
+ // call is a no-op.
+ tls_tcp_recv_start(conn);
+
+ *szp = len;
+ return (0);
+}
+
+int
+nng_tls_config_cert_key_file(
+ nng_tls_config *cfg, const char *path, const char *pass)
+{
+ int rv;
+ void * data;
+ size_t size;
+ char * pem;
+
+ if ((rv = nni_file_get(path, &data, &size)) != 0) {
+ return (rv);
+ }
+ if ((pem = nni_zalloc(size + 1)) == NULL) {
+ nni_free(data, size);
+ return (NNG_ENOMEM);
+ }
+ memcpy(pem, data, size);
+ nni_free(data, size);
+ rv = nng_tls_config_own_cert(cfg, pem, pem, pass);
+ nni_free(pem, size + 1);
+ return (rv);
+}
+
+int
+nng_tls_config_ca_file(nng_tls_config *cfg, const char *path)
+{
+ int rv;
+ void * data;
+ size_t size;
+ char * pem;
+
+ if ((rv = nni_file_get(path, &data, &size)) != 0) {
+ return (rv);
+ }
+ if ((pem = nni_zalloc(size + 1)) == NULL) {
+ nni_free(data, size);
+ return (NNG_ENOMEM);
+ }
+ memcpy(pem, data, size);
+ nni_free(data, size);
+ if (strstr(pem, "-----BEGIN X509 CRL-----") != NULL) {
+ rv = nng_tls_config_ca_chain(cfg, pem, pem);
+ } else {
+ rv = nng_tls_config_ca_chain(cfg, pem, NULL);
+ }
+ nni_free(pem, size + 1);
+ return (rv);
+}
+
+int
+nng_tls_config_version(
+ nng_tls_config *cfg, nng_tls_version min_ver, nng_tls_version max_ver)
+{
+ int rv;
+
+ nni_mtx_lock(&cfg->lock);
+ if (cfg->busy != 0) {
+ rv = NNG_EBUSY;
+ } else {
+ rv = cfg->ops.version((void *) (cfg + 1), min_ver, max_ver);
+ }
+ nni_mtx_unlock(&cfg->lock);
+ return (rv);
+}
+
+int
+nng_tls_config_server_name(nng_tls_config *cfg, const char *name)
+{
+ int rv;
+
+ nni_mtx_lock(&cfg->lock);
+ if (cfg->busy != 0) {
+ rv = NNG_EBUSY;
+ } else {
+ rv = cfg->ops.server((void *) (cfg + 1), name);
+ }
+ nni_mtx_unlock(&cfg->lock);
+ return (rv);
+}
+
+int
+nng_tls_config_ca_chain(
+ nng_tls_config *cfg, const char *certs, const char *crl)
+{
+ int rv;
+
+ nni_mtx_lock(&cfg->lock);
+ if (cfg->busy != 0) {
+ rv = NNG_EBUSY;
+ } else {
+ rv = cfg->ops.ca_chain((void *) (cfg + 1), certs, crl);
+ }
+ nni_mtx_unlock(&cfg->lock);
+ return (rv);
+}
+
+int
+nng_tls_config_own_cert(
+ nng_tls_config *cfg, const char *cert, const char *key, const char *pass)
+{
+ int rv;
+ nni_mtx_lock(&cfg->lock);
+ if (cfg->busy != 0) {
+ rv = NNG_EBUSY;
+ } else {
+ rv = cfg->ops.own_cert((void *) (cfg + 1), cert, key, pass);
+ }
+ nni_mtx_unlock(&cfg->lock);
+ return (rv);
+}
+
+int
+nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode)
+{
+ int rv;
+
+ nni_mtx_lock(&cfg->lock);
+ if (cfg->busy != 0) {
+ rv = NNG_EBUSY;
+ } else {
+ rv = cfg->ops.auth((void *) (cfg + 1), mode);
+ }
+ nni_mtx_unlock(&cfg->lock);
+ return (rv);
+}
+
+int
+nng_tls_config_alloc(nng_tls_config **cfg_p, nng_tls_mode mode)
+{
+ nng_tls_config * cfg;
+ const nng_tls_engine *eng;
+ size_t size;
+ int rv;
+
+ if ((rv = nni_init()) != 0) {
+ return (rv);
+ }
+
+ nni_mtx_lock(&tls_engine_lock);
+ eng = tls_engine;
+ nni_mtx_unlock(&tls_engine_lock);
+
+ if (eng == NULL) {
+ return (NNG_ENOTSUP);
+ }
+
+ size = NNI_ALIGN_UP(sizeof(*cfg) + eng->config_ops->size);
+
+ if ((cfg = nni_zalloc(size)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+
+ cfg->ops = *eng->config_ops;
+ cfg->size = size;
+ cfg->engine = eng;
+ cfg->ref = 1;
+ cfg->busy = 0;
+ nni_mtx_init(&cfg->lock);
+
+ if ((rv = cfg->ops.init((void *) (cfg + 1), mode)) != 0) {
+ nni_free(cfg, cfg->size);
+ return (rv);
+ }
+ *cfg_p = cfg;
+ return (0);
+}
+
+void
+nng_tls_config_free(nng_tls_config *cfg)
+{
+ nni_mtx_lock(&cfg->lock);
+ cfg->ref--;
+ if (cfg->ref != 0) {
+ nni_mtx_unlock(&cfg->lock);
+ return;
+ }
+ nni_mtx_unlock(&cfg->lock);
+ nni_mtx_fini(&cfg->lock);
+ cfg->ops.fini((void *) (cfg + 1));
+ nni_free(cfg, cfg->size);
+}
+
+void
+nng_tls_config_hold(nng_tls_config *cfg)
+{
+ nni_mtx_lock(&cfg->lock);
+ cfg->ref++;
+ nni_mtx_unlock(&cfg->lock);
+}
+
+const char *
+nng_tls_engine_name(void)
+{
+ const nng_tls_engine *eng;
+
+ nni_init();
+ nni_mtx_lock(&tls_engine_lock);
+ eng = tls_engine;
+ nni_mtx_unlock(&tls_engine_lock);
+
+ return (eng == NULL ? "none" : eng->name);
+}
+
+const char *
+nng_tls_engine_description(void)
+{
+ const nng_tls_engine *eng;
+
+ nni_init();
+ nni_mtx_lock(&tls_engine_lock);
+ eng = tls_engine;
+ nni_mtx_unlock(&tls_engine_lock);
+
+ return (eng == NULL ? "" : eng->description);
+}
+
+bool
+nng_tls_engine_fips_mode(void)
+{
+ const nng_tls_engine *eng;
+
+ nni_init();
+ nni_mtx_lock(&tls_engine_lock);
+ eng = tls_engine;
+ nni_mtx_unlock(&tls_engine_lock);
+
+ return (eng == NULL ? false : eng->fips_mode);
+}
+
+int
+nng_tls_engine_register(const nng_tls_engine *engine)
+{
+ if (engine->version != NNG_TLS_ENGINE_VERSION) {
+ return (NNG_ENOTSUP);
+ }
+ nni_mtx_lock(&tls_engine_lock);
+ tls_engine = engine;
+ nni_mtx_unlock(&tls_engine_lock);
+ return (0);
+}
+
+#ifdef NNG_TLS_ENGINE_INIT
+extern int NNG_TLS_ENGINE_INIT(void);
+#else
+static int
+NNI_TLS_ENGINE_INIT(void)
+{
+ return (0);
+}
+#endif
+
+#ifdef NNG_TLS_ENGINE_FINI
+extern void NNG_TLS_ENGINE_FINI(void);
+#else
+static void
+NNG_TLS_ENGINE_FINI(void)
+{
+}
+#endif
+
+int
+nni_tls_sys_init(void)
+{
+ int rv;
+ nni_mtx_init(&tls_engine_lock);
+ tls_engine = NULL;
+
+ rv = NNG_TLS_ENGINE_INIT();
+ if (rv != 0) {
+ nni_mtx_fini(&tls_engine_lock);
+ return (rv);
+ }
+ return (0);
+}
+
+void
+nni_tls_sys_fini(void)
+{
+ NNG_TLS_ENGINE_FINI();
+}
+
+#else // NNG_SUPP_TLS
+
+// Provide stubs for the case where TLS is not enabled.
+void
+nni_tls_config_fini(nng_tls_config *cfg)
+{
+ NNI_ARG_UNUSED(cfg);
+}
+
+int
+nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode)
+{
+ NNI_ARG_UNUSED(cpp);
+ NNI_ARG_UNUSED(mode);
+ return (NNG_ENOTSUP);
+}
+
+void
+nni_tls_config_hold(nng_tls_config *cfg)
+{
+ NNI_ARG_UNUSED(cfg);
+}
+
+int
+nng_tls_config_server_name(nng_tls_config *cfg, const char *name)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(name);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(mode);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_ca_chain(
+ nng_tls_config *cfg, const char *certs, const char *crl)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(certs);
+ NNI_ARG_UNUSED(crl);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_own_cert(
+ nng_tls_config *cfg, const char *cert, const char *key, const char *pass)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(cert);
+ NNI_ARG_UNUSED(key);
+ NNI_ARG_UNUSED(pass);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_ca_file(nng_tls_config *cfg, const char *path)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(path);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_cert_key_file(
+ nng_tls_config *cfg, const char *path, const char *pass)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(path);
+ NNI_ARG_UNUSED(pass);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_key(nng_tls_config *cfg, const uint8_t *key, size_t size)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(key);
+ NNI_ARG_UNUSED(size);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_pass(nng_tls_config *cfg, const char *pass)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(pass);
+ return (NNG_ENOTSUP);
+}
+
+int
+nng_tls_config_alloc(nng_tls_config **cfgp, nng_tls_mode mode)
+{
+
+ NNI_ARG_UNUSED(cfgp);
+ NNI_ARG_UNUSED(mode);
+ return (NNG_ENOTSUP);
+}
+
+void
+nng_tls_config_free(nng_tls_config *cfg)
+{
+ NNI_ARG_UNUSED(cfg);
+}
+
+int
+nng_tls_config_version(
+ nng_tls_config *cfg, nng_tls_version min_ver, nng_tls_version max_ver)
+{
+ NNI_ARG_UNUSED(cfg);
+ NNI_ARG_UNUSED(min_ver);
+ NNI_ARG_UNUSED(max_ver);
+ return (NNG_ENOTSUP);
+}
+
+int
+nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
+{
+ NNI_ARG_UNUSED(dp);
+ NNI_ARG_UNUSED(url);
+ return (NNG_ENOTSUP);
+}
+
+int
+nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url)
+{
+ NNI_ARG_UNUSED(lp);
+ NNI_ARG_UNUSED(url);
+ return (NNG_ENOTSUP);
+}
+
+int
+nni_tls_checkopt(const char *nm, const void *buf, size_t sz, nni_type t)
+{
+ NNI_ARG_UNUSED(nm);
+ NNI_ARG_UNUSED(buf);
+ NNI_ARG_UNUSED(sz);
+ NNI_ARG_UNUSED(t);
+ return (NNG_ENOTSUP);
+}
+
+const char *
+nng_tls_engine_name(void)
+{
+ return ("none");
+}
+
+const char *
+nng_tls_engine_description(void)
+{
+ return ("");
+}
+
+bool
+nng_tls_engine_fips_mode(void)
+{
+ return (false);
+}
+
+int
+nng_tls_engine_register(const nng_tls_engine *engine)
+{
+ NNI_ARG_UNUSED(engine);
+ return (NNG_ENOTSUP);
+}
+
+int
+nni_tls_sys_init(void)
+{
+ return (0);
+}
+
+void
+nni_tls_sys_fini(void)
+{
+}
+
+#endif // !NNG_SUPP_TLS \ No newline at end of file
diff --git a/src/supplemental/tls/tls_test.c b/src/supplemental/tls/tls_test.c
new file mode 100644
index 00000000..bd96ae08
--- /dev/null
+++ b/src/supplemental/tls/tls_test.c
@@ -0,0 +1,59 @@
+//
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nng/nng.h>
+#include <nng/supplemental/tls/tls.h>
+#include <testutil.h>
+
+#include <acutest.h>
+
+void
+test_tls_config_version(void)
+{
+ nng_tls_config *cfg;
+
+ TEST_NNG_PASS(nng_tls_config_alloc(&cfg, NNG_TLS_MODE_SERVER));
+
+ // Verify that min ver < max ver
+ TEST_NNG_FAIL(nng_tls_config_version(cfg, NNG_TLS_1_3, NNG_TLS_1_0),
+ NNG_ENOTSUP);
+
+ // Verify that we cannot configure SSL 3.0 or older.
+ TEST_NNG_FAIL(
+ nng_tls_config_version(cfg, NNG_TLS_1_0 - 1, NNG_TLS_1_0),
+ NNG_ENOTSUP);
+
+ // Verify that we cannot configure TLS > 1.3.
+ TEST_NNG_FAIL(
+ nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_3 + 1),
+ NNG_ENOTSUP);
+
+ // Verify that we *can* configure some various ranges.
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_0));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_1));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_2));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_3));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_1, NNG_TLS_1_1));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_1, NNG_TLS_1_2));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_1, NNG_TLS_1_3));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_2, NNG_TLS_1_2));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_2, NNG_TLS_1_3));
+
+ nng_tls_config_free(cfg);
+}
+
+TEST_LIST = {
+ { "tls config version", test_tls_config_version },
+ { NULL, NULL },
+}; \ No newline at end of file
diff --git a/src/supplemental/tls/wolfssl/CMakeLists.txt b/src/supplemental/tls/wolfssl/CMakeLists.txt
new file mode 100644
index 00000000..f0b48d9a
--- /dev/null
+++ b/src/supplemental/tls/wolfssl/CMakeLists.txt
@@ -0,0 +1,26 @@
+#
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+#
+# This software is supplied under the terms of the MIT License, a
+# copy of which should be located in the distribution where this
+# file was obtained (LICENSE.txt). A copy of the license may also be
+# found online at https://opensource.org/licenses/MIT.
+#
+
+# Enabling wolfSSL requires that the extern/nng-wolfssl submodule
+# be populated. Note also that this action changes the licensing
+# restrictions around the resulting library!
+
+if (NNG_TLS_ENGINE STREQUAL "wolf")
+ add_subdirectory(${PROJECT_SOURCE_DIR}/extern/nng-wolfssl nng-wolfssl)
+ target_include_directories(nng-wolfssl PRIVATE )
+ target_link_libraries(nng PRIVATE nng-wolfssl)
+ if (TARGET nng_testlib)
+ target_link_libraries(nng_testlib PRIVATE nng-wolfssl)
+ endif ()
+
+ nng_defines(NNG_TLS_ENGINE_INIT=nng_tls_engine_init_wolf)
+ nng_defines(NNG_TLS_ENGINE_FINI=nng_tls_engine_fini_wolf)
+ nng_defines(NNG_SUPP_TLS)
+
+endif ()
diff --git a/src/transport/tls/CMakeLists.txt b/src/transport/tls/CMakeLists.txt
index 1603908a..d77a67cd 100644
--- a/src/transport/tls/CMakeLists.txt
+++ b/src/transport/tls/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2018 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
@@ -9,8 +9,7 @@
#
# TLS transport
-CMAKE_DEPENDENT_OPTION(NNG_TRANSPORT_TLS "Enable TLS transport" ON
- "NNG_ENABLE_TLS" OFF)
+option (NNG_TRANSPORT_TLS "Enable TLS transport." ON)
mark_as_advanced(NNG_TRANSPORT_TLS)
nng_sources_if(NNG_TRANSPORT_TLS tls.c)
diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c
index 7869c380..46ff8117 100644
--- a/src/transport/tls/tls.c
+++ b/src/transport/tls/tls.c
@@ -10,14 +10,12 @@
//
#include <stdbool.h>
-#include <stdio.h>
#include <string.h>
#include "core/nng_impl.h"
#include "nng/supplemental/tls/tls.h"
#include "nng/transport/tls/tls.h"
-#include "supplemental/tls/tls_api.h"
// TLS over TCP transport. Platform specific TCP operations must be
// supplied as well, and uses the supplemental TLS v1.2 code. It is not
@@ -49,7 +47,6 @@ struct tlstran_pipe {
size_t gotrxhead;
size_t wanttxhead;
size_t wantrxhead;
- nni_aio * useraio;
nni_aio * txaio;
nni_aio * rxaio;
nni_aio * negoaio;
@@ -821,7 +818,6 @@ error:
nni_aio_finish_error(aio, rv);
}
nni_mtx_unlock(&ep->mtx);
- return;
}
static int
@@ -868,7 +864,7 @@ tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer)
}
if ((rv = tlstran_url_parse_source(&myurl, &srcsa, url)) != 0) {
- return (NNG_EADDRINVAL);
+ return (rv);
}
if (((rv = tlstran_ep_init(&ep, url, sock)) != 0) ||
diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c
index e70b1fd4..e2578f32 100644
--- a/src/transport/ws/websocket.c
+++ b/src/transport/ws/websocket.c
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
@@ -11,12 +11,9 @@
#include <stdbool.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include "core/nng_impl.h"
-#include "supplemental/http/http_api.h"
-#include "supplemental/tls/tls_api.h"
#include "supplemental/websocket/websocket.h"
#include <nng/supplemental/tls/tls.h>
@@ -27,33 +24,27 @@ typedef struct ws_listener ws_listener;
typedef struct ws_pipe ws_pipe;
struct ws_dialer {
- uint16_t lproto; // local protocol
- uint16_t rproto; // remote protocol
+ uint16_t peer; // remote protocol
nni_list aios;
nni_mtx mtx;
nni_aio * connaio;
nng_stream_dialer *dialer;
bool started;
- nni_dialer * ndialer;
};
struct ws_listener {
- uint16_t lproto; // local protocol
- uint16_t rproto; // remote protocol
+ uint16_t peer; // remote protocol
nni_list aios;
nni_mtx mtx;
nni_aio * accaio;
nng_stream_listener *listener;
bool started;
- nni_listener * nlistener;
};
struct ws_pipe {
nni_mtx mtx;
- nni_pipe * npipe;
bool closed;
- uint16_t rproto;
- uint16_t lproto;
+ uint16_t peer;
nni_aio * user_txaio;
nni_aio * user_rxaio;
nni_aio * txaio;
@@ -193,10 +184,10 @@ wstran_pipe_stop(void *arg)
}
static int
-wstran_pipe_init(void *arg, nni_pipe *npipe)
+wstran_pipe_init(void *arg, nni_pipe *pipe)
{
- ws_pipe *p = arg;
- p->npipe = npipe;
+ NNI_ARG_UNUSED(arg);
+ NNI_ARG_UNUSED(pipe);
return (0);
}
@@ -254,7 +245,7 @@ wstran_pipe_peer(void *arg)
{
ws_pipe *p = arg;
- return (p->rproto);
+ return (p->peer);
}
static int
@@ -426,8 +417,7 @@ wstran_connect_cb(void *arg)
nng_stream_free(ws);
nni_aio_finish_error(uaio, rv);
} else {
- p->rproto = d->rproto;
- p->lproto = d->lproto;
+ p->peer = d->peer;
nni_aio_set_output(uaio, 0, p);
nni_aio_finish(uaio, 0, 0);
@@ -478,8 +468,7 @@ wstran_accept_cb(void *arg)
nng_stream_close(ws);
nni_aio_finish_error(uaio, rv);
} else {
- p->rproto = l->rproto;
- p->lproto = l->lproto;
+ p->peer = l->peer;
nni_aio_set_output(uaio, 0, p);
nni_aio_finish(uaio, 0, 0);
@@ -498,7 +487,7 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
ws_dialer *d;
nni_sock * s = nni_dialer_sock(ndialer);
int rv;
- char prname[64];
+ char name[64];
if ((d = NNI_ALLOC_STRUCT(d)) == NULL) {
return (NNG_ENOMEM);
@@ -507,11 +496,10 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
nni_aio_list_init(&d->aios);
- d->lproto = nni_sock_proto_id(s);
- d->rproto = nni_sock_peer_id(s);
- d->ndialer = ndialer;
+ d->peer = nni_sock_peer_id(s);
- snprintf(prname, sizeof(prname), "%s.sp.nanomsg.org",
+ snprintf(
+ name, sizeof(name), "%s.sp.nanomsg.org",
nni_sock_peer_name(s));
if (((rv = nni_ws_dialer_alloc(&d->dialer, url)) != 0) ||
@@ -519,7 +507,7 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
((rv = nng_stream_dialer_set_bool(
d->dialer, NNI_OPT_WS_MSGMODE, true)) != 0) ||
((rv = nng_stream_dialer_set_string(
- d->dialer, NNG_OPT_WS_PROTOCOL, prname)) != 0)) {
+ d->dialer, NNG_OPT_WS_PROTOCOL, name)) != 0)) {
wstran_dialer_fini(d);
return (rv);
}
@@ -529,12 +517,12 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
}
static int
-wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
+wstran_listener_init(void **lp, nng_url *url, nni_listener *listener)
{
ws_listener *l;
int rv;
- nni_sock * s = nni_listener_sock(nlistener);
- char prname[64];
+ nni_sock * s = nni_listener_sock(listener);
+ char name[64];
if ((l = NNI_ALLOC_STRUCT(l)) == NULL) {
return (NNG_ENOMEM);
@@ -543,11 +531,9 @@ wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
nni_aio_list_init(&l->aios);
- l->lproto = nni_sock_proto_id(s);
- l->rproto = nni_sock_peer_id(s);
- l->nlistener = nlistener;
+ l->peer = nni_sock_peer_id(s);
- snprintf(prname, sizeof(prname), "%s.sp.nanomsg.org",
+ snprintf(name, sizeof(name), "%s.sp.nanomsg.org",
nni_sock_proto_name(s));
if (((rv = nni_ws_listener_alloc(&l->listener, url)) != 0) ||
@@ -555,7 +541,7 @@ wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
((rv = nng_stream_listener_set_bool(
l->listener, NNI_OPT_WS_MSGMODE, true)) != 0) ||
((rv = nng_stream_listener_set_string(
- l->listener, NNG_OPT_WS_PROTOCOL, prname)) != 0)) {
+ l->listener, NNG_OPT_WS_PROTOCOL, name)) != 0)) {
wstran_listener_fini(l);
return (rv);
}
@@ -637,7 +623,7 @@ wstran_listener_setopt(
return (rv);
}
-static nni_chkoption wstran_checkopts[] = {
+static nni_chkoption wstran_check_opts[] = {
{
.o_name = NULL,
},
@@ -647,7 +633,7 @@ static int
wstran_checkopt(const char *name, const void *buf, size_t sz, nni_type t)
{
int rv;
- rv = nni_chkopt(wstran_checkopts, name, buf, sz, t);
+ rv = nni_chkopt(wstran_check_opts, name, buf, sz, t);
if (rv == NNG_ENOTSUP) {
rv = nni_stream_checkopt("ws", name, buf, sz, t);
}