diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/core/init.c | 5 | ||||
| -rw-r--r-- | src/supplemental/http/http_client.c | 3 | ||||
| -rw-r--r-- | src/supplemental/http/http_server.c | 3 | ||||
| -rw-r--r-- | src/supplemental/tls/CMakeLists.txt | 57 | ||||
| -rw-r--r-- | src/supplemental/tls/mbedtls/CMakeLists.txt | 36 | ||||
| -rw-r--r-- | src/supplemental/tls/mbedtls/tls.c | 1069 | ||||
| -rw-r--r-- | src/supplemental/tls/none/tls.c | 154 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_api.h | 32 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_common.c | 1216 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_test.c | 59 | ||||
| -rw-r--r-- | src/supplemental/tls/wolfssl/CMakeLists.txt | 26 | ||||
| -rw-r--r-- | src/transport/tls/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/transport/tls/tls.c | 6 | ||||
| -rw-r--r-- | src/transport/ws/websocket.c | 60 |
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); } |
