aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/tls
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplemental/tls')
-rw-r--r--src/supplemental/tls/CMakeLists.txt8
-rw-r--r--src/supplemental/tls/tls_common.c917
-rw-r--r--src/supplemental/tls/tls_common.h106
-rw-r--r--src/supplemental/tls/tls_dialer.c190
-rw-r--r--src/supplemental/tls/tls_listener.c194
-rw-r--r--src/supplemental/tls/tls_stream.c223
-rw-r--r--src/supplemental/tls/tls_stream.h31
-rw-r--r--src/supplemental/tls/tls_stubs.c199
8 files changed, 1075 insertions, 793 deletions
diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt
index 78fb10b7..400b1354 100644
--- a/src/supplemental/tls/CMakeLists.txt
+++ b/src/supplemental/tls/CMakeLists.txt
@@ -27,5 +27,9 @@ endif ()
add_subdirectory(mbedtls)
add_subdirectory(wolfssl)
-nng_sources(tls_common.c)
-nng_sources(tls_api.h tls_engine.h)
+if (NNG_ENABLE_TLS)
+ nng_sources(tls_common.c tls_dialer.c tls_listener.c tls_stream.c)
+ nng_sources(tls_api.h tls_engine.h)
+else()
+ nng_sources(tls_stubs.c)
+endif()
diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c
index fca0b420..c068109d 100644
--- a/src/supplemental/tls/tls_common.c
+++ b/src/supplemental/tls/tls_common.c
@@ -15,6 +15,7 @@
#include "core/nng_impl.h"
+#include "tls_common.h"
#include "tls_engine.h"
// NNG_TLS_MAX_SEND_SIZE limits the amount of data we will buffer for sending,
@@ -36,426 +37,14 @@
// parts of TLS support that are invariant relative to different TLS
// libraries, such as dialer and listener support.
-#ifdef NNG_SUPP_TLS
-
static nni_atomic_ptr tls_engine;
-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;
- bool busy;
- bool key_is_set;
- 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;
- nni_atomic_flag did_close;
- 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;
- nni_reap_node 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_bio_send_cb(void *arg);
+static void tls_bio_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 void tls_reap(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);
-
-static nni_reap_list tls_conn_reap_list = {
- .rl_offset = offsetof(tls_conn, reap),
- .rl_func = tls_reap,
-};
-
-typedef struct {
- nng_stream_dialer ops;
- nng_stream_dialer *d; // underlying TCP dialer
- nng_tls_config *cfg;
- bool started;
- nni_mtx lk; // protects the config
-} tls_dialer;
-
-static void
-tls_dialer_close(void *arg)
-{
- tls_dialer *d = arg;
- nng_stream_dialer_close(d->d);
-}
-
-static void
-tls_dialer_free(void *arg)
-{
- tls_dialer *d;
- if ((d = arg) != NULL) {
- nng_stream_dialer_free(d->d);
- nng_tls_config_free(d->cfg);
- nni_mtx_fini(&d->lk);
- NNI_FREE_STRUCT(d);
- }
-}
-
-static void
-tls_dialer_stop(void *arg)
-{
- tls_dialer *d = arg;
-
- nng_stream_dialer_stop(d->d);
-}
-
-// For dialing, we need to have our own completion callback, instead of
-// the user's completion callback.
-
-static void
-tls_conn_cb(void *arg)
-{
- tls_conn *conn = arg;
- nng_stream *tcp;
- int rv;
-
- 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(&conn->conn_aio, 0);
-
- 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(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
-// want to wait for the connection to establish.
-static void
-tls_conn_cancel(nni_aio *aio, void *arg, int rv)
-{
- tls_conn *conn = arg;
-
- NNI_ARG_UNUSED(aio);
-
- // Just pass this down. If the connection is already done, this
- // will have no effect.
- nni_aio_abort(&conn->conn_aio, rv);
-}
-
-static void
-tls_dialer_dial(void *arg, nng_aio *aio)
-{
- tls_dialer *d = arg;
- tls_conn *conn;
- int rv;
-
- nni_aio_reset(aio);
- if ((rv = tls_alloc(&conn, d->cfg, aio)) != 0) {
- nni_aio_finish_error(aio, rv);
- return;
- }
-
- if (!nni_aio_start(aio, tls_conn_cancel, conn)) {
- tls_free(conn);
- return;
- }
- nni_mtx_lock(&d->lk);
- d->started = true;
- nni_mtx_unlock(&d->lk);
-
- nng_stream_dialer_dial(d->d, &conn->conn_aio);
-}
-
-static int
-tls_dialer_set_tls(void *arg, nng_tls_config *cfg)
-{
- tls_dialer *d = arg;
- nng_tls_config *old;
- if (cfg == NULL) {
- return (NNG_EINVAL);
- }
-
- nng_tls_config_hold(cfg);
-
- nni_mtx_lock(&d->lk);
- if (d->started) {
- nni_mtx_unlock(&d->lk);
- nng_tls_config_free(cfg);
- return (NNG_EBUSY);
- }
- old = d->cfg;
- d->cfg = cfg;
- nni_mtx_unlock(&d->lk);
-
- nng_tls_config_free(old);
- return (0);
-}
-
-static int
-tls_dialer_get_tls(void *arg, nng_tls_config **cfg)
-{
- tls_dialer *d = arg;
- nni_mtx_lock(&d->lk);
- *cfg = d->cfg;
- nni_mtx_unlock(&d->lk);
- return (0);
-}
-
-static int
-tls_dialer_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
-{
- tls_dialer *d = arg;
-
- return (nni_stream_dialer_get(d->d, name, buf, szp, t));
-}
-
-static int
-tls_dialer_set(
- void *arg, const char *name, const void *buf, size_t sz, nni_type t)
-{
- tls_dialer *d = arg;
-
- return (nni_stream_dialer_set(d->d, name, buf, sz, t));
-}
-
-int
-nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
-{
- tls_dialer *d;
- int rv;
- nng_url my_url;
-
- memcpy(&my_url, url, sizeof(my_url));
- if (strncmp(my_url.u_scheme, "tls+", 4) == 0) {
- my_url.u_scheme += 4;
- }
-
- if ((d = NNI_ALLOC_STRUCT(d)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_mtx_init(&d->lk);
-
- if ((rv = nng_stream_dialer_alloc_url(&d->d, &my_url)) != 0) {
- nni_mtx_fini(&d->lk);
- NNI_FREE_STRUCT(d);
- return (rv);
- }
- if ((rv = nng_tls_config_alloc(&d->cfg, NNG_TLS_MODE_CLIENT)) != 0) {
- nng_stream_dialer_free(d->d);
- nni_mtx_fini(&d->lk);
- NNI_FREE_STRUCT(d);
- return (rv);
- }
-
- // Set the expected outbound hostname
- nng_tls_config_server_name(d->cfg, url->u_hostname);
-
- d->ops.sd_close = tls_dialer_close;
- d->ops.sd_free = tls_dialer_free;
- d->ops.sd_stop = tls_dialer_stop;
- d->ops.sd_dial = tls_dialer_dial;
- d->ops.sd_get = tls_dialer_get;
- d->ops.sd_set = tls_dialer_set;
- d->ops.sd_get_tls = tls_dialer_get_tls;
- d->ops.sd_set_tls = tls_dialer_set_tls;
-
- *dp = (void *) d;
- return (rv);
-}
-
-typedef struct {
- nng_stream_listener ops;
- nng_stream_listener *l;
- nng_tls_config *cfg;
- bool started;
- nni_mtx lk;
-} tls_listener;
-
-static void
-tls_listener_close(void *arg)
-{
- tls_listener *l = arg;
- nng_stream_listener_close(l->l);
-}
-
-static void
-tls_listener_stop(void *arg)
-{
- tls_listener *l = arg;
- nng_stream_listener_close(l->l);
-}
-
-static void
-tls_listener_free(void *arg)
-{
- tls_listener *l;
- if ((l = arg) != NULL) {
- tls_listener_close(l);
- nng_tls_config_free(l->cfg);
- nng_stream_listener_free(l->l);
- nni_mtx_fini(&l->lk);
- NNI_FREE_STRUCT(l);
- }
-}
-
-static int
-tls_listener_listen(void *arg)
-{
- tls_listener *l = arg;
- nni_mtx_lock(&l->lk);
- l->started = true;
- nni_mtx_unlock(&l->lk);
- return (nng_stream_listener_listen(l->l));
-}
-
-static void
-tls_listener_accept(void *arg, nng_aio *aio)
-{
- tls_listener *l = arg;
- int rv;
- tls_conn *conn;
-
- nni_aio_reset(aio);
- if ((rv = tls_alloc(&conn, l->cfg, aio)) != 0) {
- nni_aio_finish_error(aio, rv);
- return;
- }
-
- if (!nni_aio_start(aio, tls_conn_cancel, conn)) {
- tls_free(conn);
- return;
- }
-
- nng_stream_listener_accept(l->l, &conn->conn_aio);
-}
-
-static int
-tls_listener_set_tls(void *arg, nng_tls_config *cfg)
-{
- tls_listener *l = arg;
- nng_tls_config *old;
- if (cfg == NULL) {
- return (NNG_EINVAL);
- }
- nng_tls_config_hold(cfg);
-
- nni_mtx_lock(&l->lk);
- if (l->started) {
- nni_mtx_unlock(&l->lk);
- nng_tls_config_free(cfg);
- return (NNG_EBUSY);
- }
- old = l->cfg;
- l->cfg = cfg;
- nni_mtx_unlock(&l->lk);
-
- nng_tls_config_free(old);
- return (0);
-}
-
-static int
-tls_listener_get_tls(void *arg, nng_tls_config **cfg)
-{
- tls_listener *l = arg;
- nni_mtx_lock(&l->lk);
- *cfg = l->cfg;
- nni_mtx_unlock(&l->lk);
- return (0);
-}
-
-static int
-tls_listener_get(
- void *arg, const char *name, void *buf, size_t *szp, nni_type t)
-{
- tls_listener *l = arg;
-
- return (nni_stream_listener_get(l->l, name, buf, szp, t));
-}
-
-static int
-tls_listener_set(
- void *arg, const char *name, const void *buf, size_t sz, nni_type t)
-{
- tls_listener *l = arg;
-
- return (nni_stream_listener_set(l->l, name, buf, sz, t));
-}
-
-int
-nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url)
-{
- tls_listener *l;
- int rv;
- nng_url my_url;
-
- memcpy(&my_url, url, sizeof(my_url));
-
- if (strncmp(my_url.u_scheme, "tls+", 4) == 0) {
- my_url.u_scheme += 4;
- }
-
- if ((l = NNI_ALLOC_STRUCT(l)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_mtx_init(&l->lk);
-
- if ((rv = nng_stream_listener_alloc_url(&l->l, &my_url)) != 0) {
- nni_mtx_fini(&l->lk);
- NNI_FREE_STRUCT(l);
- return (rv);
- }
- if ((rv = nng_tls_config_alloc(&l->cfg, NNG_TLS_MODE_SERVER)) != 0) {
- nng_stream_listener_free(l->l);
- nni_mtx_fini(&l->lk);
- NNI_FREE_STRUCT(l);
- return (rv);
- }
- l->ops.sl_free = tls_listener_free;
- l->ops.sl_close = tls_listener_close;
- l->ops.sl_stop = tls_listener_stop;
- l->ops.sl_accept = tls_listener_accept;
- l->ops.sl_listen = tls_listener_listen;
- l->ops.sl_get = tls_listener_get;
- l->ops.sl_set = tls_listener_set;
- l->ops.sl_get_tls = tls_listener_get_tls;
- l->ops.sl_set_tls = tls_listener_set_tls;
- *lp = (void *) l;
- return (0);
-}
+static void tls_bio_send_start(tls_conn *);
+static void tls_bio_error(tls_conn *, int);
static void
tls_cancel(nni_aio *aio, void *arg, int rv)
@@ -463,9 +52,9 @@ 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);
+ nni_aio_abort(&conn->bio_recv, rv);
} else if (aio == nni_list_first(&conn->send_queue)) {
- nni_aio_abort(&conn->tcp_send, rv);
+ nni_aio_abort(&conn->bio_send, rv);
} else if (nni_aio_list_active(aio)) {
nni_aio_list_remove(aio);
nni_aio_finish_error(aio, rv);
@@ -473,12 +62,10 @@ tls_cancel(nni_aio *aio, void *arg, int rv)
nni_mtx_unlock(&conn->lock);
}
-// tls_send implements the upper layer stream send operation.
-static void
-tls_send(void *arg, nni_aio *aio)
+// tls_send implements the upper layer send operation.
+void
+nni_tls_send(tls_conn *conn, nni_aio *aio)
{
- tls_conn *conn = arg;
-
nni_aio_reset(aio);
nni_mtx_lock(&conn->lock);
if (!nni_aio_start(aio, tls_cancel, conn)) {
@@ -495,11 +82,9 @@ tls_send(void *arg, nni_aio *aio)
nni_mtx_unlock(&conn->lock);
}
-static void
-tls_recv(void *arg, nni_aio *aio)
+void
+nni_tls_recv(tls_conn *conn, nni_aio *aio)
{
- tls_conn *conn = arg;
-
nni_aio_reset(aio);
nni_mtx_lock(&conn->lock);
if (!nni_aio_start(aio, tls_cancel, conn)) {
@@ -517,93 +102,55 @@ tls_recv(void *arg, nni_aio *aio)
nni_mtx_unlock(&conn->lock);
}
-static void
-tls_close(void *arg)
+void
+nni_tls_close(tls_conn *conn)
{
- tls_conn *conn = arg;
-
if (!nni_atomic_flag_test_and_set(&conn->did_close)) {
nni_mtx_lock(&conn->lock);
conn->ops.close((void *) (conn + 1));
- tls_tcp_error(conn, NNG_ECLOSED);
+ tls_bio_error(conn, NNG_ECLOSED);
nni_mtx_unlock(&conn->lock);
- nng_stream_close(conn->tcp);
+ if (conn->bio != NULL) {
+ conn->bio_ops.bio_close(conn->bio);
+ }
}
}
-static void
-tls_stop(void *arg)
+void
+nni_tls_stop(tls_conn *conn)
{
- tls_conn *conn = arg;
-
- tls_close(conn);
- nng_stream_stop(conn->tcp);
- nni_aio_stop(&conn->conn_aio);
- nni_aio_stop(&conn->tcp_send);
- nni_aio_stop(&conn->tcp_recv);
+ nni_tls_close(conn);
+ if (conn->bio != NULL) {
+ conn->bio_ops.bio_stop(conn->bio);
+ }
+ nni_aio_stop(&conn->bio_send);
+ nni_aio_stop(&conn->bio_recv);
}
-static int
-tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t)
+bool
+nni_tls_verified(tls_conn *conn)
{
- tls_conn *conn = arg;
- bool v;
-
+ bool result;
nni_mtx_lock(&conn->lock);
- v = conn->ops.verified((void *) (conn + 1));
+ result = conn->ops.verified((void *) (conn + 1));
nni_mtx_unlock(&conn->lock);
- return (nni_copyout_bool(v, buf, szp, t));
+ return result;
}
-static int
-tls_get_peer_cn(void *arg, void *buf, size_t *szp, nni_type t)
+const char *
+nni_tls_peer_cn(tls_conn *conn)
{
- NNI_ARG_UNUSED(szp);
-
- if (t != NNI_TYPE_STRING) {
- return (NNG_EBADTYPE);
- }
-
- tls_conn *conn = arg;
+ const char *result;
nni_mtx_lock(&conn->lock);
- *(char **) buf = conn->ops.peer_cn((void *) (conn + 1));
+ result = conn->ops.peer_cn((void *) (conn + 1));
nni_mtx_unlock(&conn->lock);
- return (0);
+ return result;
}
-static const nni_option tls_options[] = {
- {
- .o_name = NNG_OPT_TLS_VERIFIED,
- .o_get = tls_get_verified,
- },
- {
- .o_name = NNG_OPT_TLS_PEER_CN,
- .o_get = tls_get_peer_cn,
- },
- {
- .o_name = NULL,
- },
-};
-
-static int
-tls_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
-{
- tls_conn *conn = arg;
- int rv;
-
- if ((rv = nni_stream_get(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)
+int
+nni_tls_init(tls_conn *conn, nng_tls_config *cfg)
{
- tls_conn *conn;
const nng_tls_engine *eng;
- size_t size;
eng = cfg->engine;
@@ -611,98 +158,76 @@ tls_alloc(tls_conn **conn_p, nng_tls_config *cfg, nng_aio *user_aio)
cfg->busy = true;
nni_mtx_unlock(&cfg->lock);
- 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)) ==
+ if (((conn->bio_send_buf = nni_alloc(NNG_TLS_MAX_SEND_SIZE)) ==
NULL) ||
- ((conn->tcp_recv_buf = nni_alloc(NNG_TLS_MAX_RECV_SIZE)) ==
+ ((conn->bio_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);
+ conn->ops = *eng->conn_ops;
+ conn->engine = eng;
+ conn->cfg = cfg;
+
+ nni_aio_init(&conn->bio_recv, tls_bio_recv_cb, conn);
+ nni_aio_init(&conn->bio_send, tls_bio_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->conn_aio, NNG_DURATION_INFINITE);
- nni_aio_set_timeout(&conn->tcp_send, NNG_DURATION_INFINITE);
- nni_aio_set_timeout(&conn->tcp_recv, NNG_DURATION_INFINITE);
+ nni_aio_set_timeout(&conn->bio_send, NNG_DURATION_INFINITE);
+ nni_aio_set_timeout(&conn->bio_recv, NNG_DURATION_INFINITE);
nni_atomic_flag_reset(&conn->did_close);
- conn->stream.s_close = tls_close;
- conn->stream.s_free = tls_free;
- conn->stream.s_stop = tls_stop;
- conn->stream.s_send = tls_send;
- conn->stream.s_recv = tls_recv;
- conn->stream.s_get = tls_get;
-
nng_tls_config_hold(cfg);
- *conn_p = conn;
return (0);
}
-static void
-tls_reap(void *arg)
+void
+nni_tls_fini(tls_conn *conn)
{
- tls_conn *conn = arg;
-
- tls_stop(conn);
+ nni_tls_stop(conn);
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);
+ nni_aio_fini(&conn->bio_send);
+ nni_aio_fini(&conn->bio_recv);
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->bio_send_buf != NULL) {
+ nni_free(conn->bio_send_buf, NNG_TLS_MAX_SEND_SIZE);
}
- if (conn->tcp_recv_buf != NULL) {
- nni_free(conn->tcp_recv_buf, NNG_TLS_MAX_RECV_SIZE);
+ if (conn->bio_recv_buf != NULL) {
+ nni_free(conn->bio_recv_buf, NNG_TLS_MAX_RECV_SIZE);
+ }
+ if (conn->bio != NULL) {
+ conn->bio_ops.bio_free(conn->bio);
}
nni_mtx_fini(&conn->lock);
- NNI_FREE_STRUCT(conn);
}
-static void
-tls_free(void *arg)
+int
+nni_tls_start(tls_conn *conn, const nni_tls_bio_ops *biops, void *bio)
{
- tls_conn *conn = arg;
+ nng_tls_engine_config *cfg;
+ nng_tls_engine_conn *econ;
- nni_reap(&tls_conn_reap_list, conn);
-}
+ cfg = (void *) (conn->cfg + 1);
+ econ = (void *) (conn + 1);
-static int
-tls_start(tls_conn *conn, nng_stream *tcp)
-{
- int rv;
+ conn->bio_ops = *biops;
+ conn->bio = bio;
- conn->tcp = tcp;
- rv = conn->ops.init(
- (void *) (conn + 1), conn, (void *) (conn->cfg + 1));
- return (rv);
+ return (conn->ops.init(econ, conn, cfg));
}
static void
-tls_tcp_error(tls_conn *conn, int rv)
+tls_bio_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);
+ if (conn->bio != NULL) {
+ conn->bio_ops.bio_close(conn->bio);
+ }
+ nni_aio_close(&conn->bio_send);
+ nni_aio_close(&conn->bio_recv);
while (((aio = nni_list_first(&conn->send_queue)) != NULL) ||
((aio = nni_list_first(&conn->recv_queue)) != NULL)) {
nni_aio_list_remove(aio);
@@ -726,7 +251,7 @@ tls_do_handshake(tls_conn *conn)
conn->hs_done = true;
return (true);
}
- tls_tcp_error(conn, rv);
+ tls_bio_error(conn, rv);
return (true);
}
@@ -826,28 +351,28 @@ tls_do_send(tls_conn *conn)
}
static void
-tls_tcp_send_cb(void *arg)
+tls_bio_send_cb(void *arg)
{
tls_conn *conn = arg;
- nng_aio *aio = &conn->tcp_send;
+ nng_aio *aio = &conn->bio_send;
int rv;
size_t count;
nni_mtx_lock(&conn->lock);
- conn->tcp_send_active = false;
+ conn->bio_send_active = false;
if ((rv = nni_aio_result(aio)) != 0) {
- tls_tcp_error(conn, rv);
+ tls_bio_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;
- conn->tcp_send_tail += count;
- conn->tcp_send_tail %= NNG_TLS_MAX_SEND_SIZE;
- tls_tcp_send_start(conn);
+ NNI_ASSERT(count <= conn->bio_send_len);
+ conn->bio_send_len -= count;
+ conn->bio_send_tail += count;
+ conn->bio_send_tail %= NNG_TLS_MAX_SEND_SIZE;
+ tls_bio_send_start(conn);
if (tls_do_handshake(conn)) {
tls_do_send(conn);
@@ -858,24 +383,24 @@ tls_tcp_send_cb(void *arg)
}
static void
-tls_tcp_recv_cb(void *arg)
+tls_bio_recv_cb(void *arg)
{
tls_conn *conn = arg;
- nni_aio *aio = &conn->tcp_recv;
+ nni_aio *aio = &conn->bio_recv;
int rv;
nni_mtx_lock(&conn->lock);
- conn->tcp_recv_pend = false;
+ conn->bio_recv_pend = false;
if ((rv = nni_aio_result(aio)) != 0) {
- tls_tcp_error(conn, rv);
+ tls_bio_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);
+ NNI_ASSERT(conn->bio_recv_len == 0);
+ NNI_ASSERT(conn->bio_recv_off == 0);
+ conn->bio_recv_len = nni_aio_count(aio);
if (tls_do_handshake(conn)) {
tls_do_recv(conn);
@@ -886,30 +411,30 @@ tls_tcp_recv_cb(void *arg)
}
static void
-tls_tcp_recv_start(tls_conn *conn)
+tls_bio_recv_start(tls_conn *conn)
{
nng_iov iov;
- if (conn->tcp_recv_len != 0) {
+ if (conn->bio_recv_len != 0) {
// We already have data in the buffer.
return;
}
- if (conn->tcp_recv_pend) {
+ if (conn->bio_recv_pend) {
// Already have a receive in flight.
return;
}
- conn->tcp_recv_off = 0;
+ conn->bio_recv_off = 0;
iov.iov_len = NNG_TLS_MAX_RECV_SIZE;
- iov.iov_buf = conn->tcp_recv_buf;
+ iov.iov_buf = conn->bio_recv_buf;
- conn->tcp_recv_pend = true;
- nng_aio_set_iov(&conn->tcp_recv, 1, &iov);
+ conn->bio_recv_pend = true;
+ nng_aio_set_iov(&conn->bio_recv, 1, &iov);
- nng_stream_recv(conn->tcp, &conn->tcp_recv);
+ conn->bio_ops.bio_recv(conn->bio, &conn->bio_recv);
}
static void
-tls_tcp_send_start(tls_conn *conn)
+tls_bio_send_start(tls_conn *conn)
{
nni_iov iov[2];
unsigned nio = 0;
@@ -917,15 +442,15 @@ tls_tcp_send_start(tls_conn *conn)
size_t tail;
size_t head;
- if (conn->tcp_send_active) {
+ if (conn->bio_send_active) {
return;
}
- if (conn->tcp_send_len == 0) {
+ if (conn->bio_send_len == 0) {
return;
}
- len = conn->tcp_send_len;
- head = conn->tcp_send_head;
- tail = conn->tcp_send_tail;
+ len = conn->bio_send_len;
+ head = conn->bio_send_head;
+ tail = conn->bio_send_tail;
while (len > 0) {
size_t cnt;
@@ -938,16 +463,16 @@ tls_tcp_send_start(tls_conn *conn)
if (cnt > len) {
cnt = len;
}
- iov[nio].iov_buf = conn->tcp_send_buf + tail;
+ iov[nio].iov_buf = conn->bio_send_buf + tail;
iov[nio].iov_len = cnt;
len -= cnt;
tail += cnt;
tail %= NNG_TLS_MAX_SEND_SIZE;
nio++;
}
- conn->tcp_send_active = true;
- nni_aio_set_iov(&conn->tcp_send, nio, iov);
- nng_stream_send(conn->tcp, &conn->tcp_send);
+ conn->bio_send_active = true;
+ nni_aio_set_iov(&conn->bio_send, nio, iov);
+ conn->bio_ops.bio_send(conn->bio, &conn->bio_send);
}
int
@@ -955,12 +480,12 @@ 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 head = conn->bio_send_head;
+ size_t tail = conn->bio_send_tail;
size_t space;
size_t cnt;
- space = NNG_TLS_MAX_SEND_SIZE - conn->tcp_send_len;
+ space = NNG_TLS_MAX_SEND_SIZE - conn->bio_send_len;
if (space == 0) {
return (NNG_EAGAIN);
@@ -977,8 +502,8 @@ nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp)
// We are committed at this point to sending out len bytes.
// Update this now, so that we can use len to update.
*szp = len;
- conn->tcp_send_len += len;
- NNI_ASSERT(conn->tcp_send_len <= NNG_TLS_MAX_SEND_SIZE);
+ conn->bio_send_len += len;
+ NNI_ASSERT(conn->bio_send_len <= NNG_TLS_MAX_SEND_SIZE);
while (len > 0) {
if (head >= tail) {
@@ -990,16 +515,16 @@ nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp)
cnt = len;
}
- memcpy(conn->tcp_send_buf + head, buf, cnt);
+ memcpy(conn->bio_send_buf + head, buf, cnt);
buf += cnt;
head += cnt;
head %= NNG_TLS_MAX_SEND_SIZE;
len -= cnt;
}
- conn->tcp_send_head = head;
+ conn->bio_send_head = head;
- tls_tcp_send_start(conn);
+ tls_bio_send_start(conn);
return (0);
}
@@ -1012,20 +537,20 @@ nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp)
if (conn->closed) {
return (NNG_ECLOSED);
}
- if (conn->tcp_recv_len == 0) {
- tls_tcp_recv_start(conn);
+ if (conn->bio_recv_len == 0) {
+ tls_bio_recv_start(conn);
return (NNG_EAGAIN);
}
- if (len > conn->tcp_recv_len) {
- len = conn->tcp_recv_len;
+ if (len > conn->bio_recv_len) {
+ len = conn->bio_recv_len;
}
- memcpy(buf, conn->tcp_recv_buf + conn->tcp_recv_off, len);
- conn->tcp_recv_off += len;
- conn->tcp_recv_len -= len;
+ memcpy(buf, conn->bio_recv_buf + conn->bio_recv_off, len);
+ conn->bio_recv_off += len;
+ conn->bio_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);
+ tls_bio_recv_start(conn);
*szp = len;
return (0);
@@ -1316,193 +841,3 @@ 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_hold(nng_tls_config *cfg)
-{
- NNI_ARG_UNUSED(cfg);
-}
-
-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
diff --git a/src/supplemental/tls/tls_common.h b/src/supplemental/tls/tls_common.h
new file mode 100644
index 00000000..3e703785
--- /dev/null
+++ b/src/supplemental/tls/tls_common.h
@@ -0,0 +1,106 @@
+//
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core/nng_impl.h"
+
+#include "tls_engine.h"
+
+#ifndef NNG_TLS_TLS_COMMON_H
+#define NNG_TLS_TLS_COMMON_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_TLS_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.
+
+static nni_atomic_ptr tls_engine;
+
+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;
+ bool busy;
+ bool key_is_set;
+ size_t size;
+
+ // ... engine config data follows
+};
+
+typedef struct nni_tls_bio_ops_s {
+ void (*bio_send)(void *, nng_aio *);
+ void (*bio_recv)(void *, nng_aio *);
+ void (*bio_stop)(void *);
+ void (*bio_close)(void *);
+ void (*bio_free)(void *);
+} nni_tls_bio_ops;
+
+typedef struct {
+ nng_stream stream;
+ nng_tls_engine_conn_ops ops;
+ nng_tls_config *cfg;
+ const nng_tls_engine *engine;
+ size_t size;
+ nni_mtx lock;
+ bool closed;
+ nni_atomic_flag did_close;
+ bool hs_done;
+ nni_list send_queue;
+ nni_list recv_queue;
+
+ void *bio; // lower level transport object
+ nni_tls_bio_ops bio_ops; // lower level ops vector
+ nni_aio bio_send; // lower level send pending
+ nni_aio bio_recv; // lower level recv pending
+ uint8_t *bio_send_buf;
+ uint8_t *bio_recv_buf;
+ size_t bio_recv_len;
+ size_t bio_recv_off;
+ bool bio_recv_pend;
+ bool bio_send_active;
+ size_t bio_send_len;
+ size_t bio_send_head;
+ size_t bio_send_tail;
+ nni_reap_node reap;
+
+ // ... engine connection data follows
+} tls_conn;
+
+extern void nni_tls_fini(tls_conn *conn);
+extern int nni_tls_init(tls_conn *conn, nng_tls_config *cfg);
+extern int nni_tls_start(
+ tls_conn *conn, const nni_tls_bio_ops *biops, void *bio);
+extern void nni_tls_stop(tls_conn *conn);
+extern void nni_tls_close(tls_conn *conn);
+extern void nni_tls_recv(tls_conn *conn, nni_aio *aio);
+extern void nni_tls_send(tls_conn *conn, nni_aio *aio);
+extern bool nni_tls_verified(tls_conn *conn);
+extern const char *nni_tls_peer_cn(tls_conn *conn);
+
+#endif // NNG_TLS_TLS_COMMON_H
diff --git a/src/supplemental/tls/tls_dialer.c b/src/supplemental/tls/tls_dialer.c
new file mode 100644
index 00000000..d0fee0ac
--- /dev/null
+++ b/src/supplemental/tls/tls_dialer.c
@@ -0,0 +1,190 @@
+//
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core/nng_impl.h"
+
+#include "tls_common.h"
+#include "tls_engine.h"
+#include "tls_stream.h"
+
+typedef struct {
+ nng_stream_dialer ops;
+ nng_stream_dialer *d; // underlying TCP dialer
+ nng_tls_config *cfg;
+ bool started;
+ nni_mtx lk; // protects the config
+} tls_dialer;
+
+static void
+tls_dialer_close(void *arg)
+{
+ tls_dialer *d = arg;
+ nng_stream_dialer_close(d->d);
+}
+
+static void
+tls_dialer_free(void *arg)
+{
+ tls_dialer *d;
+ if ((d = arg) != NULL) {
+ nng_stream_dialer_free(d->d);
+ nng_tls_config_free(d->cfg);
+ nni_mtx_fini(&d->lk);
+ NNI_FREE_STRUCT(d);
+ }
+}
+
+static void
+tls_dialer_stop(void *arg)
+{
+ tls_dialer *d = arg;
+
+ nng_stream_dialer_stop(d->d);
+}
+
+// Dialer cancel is called when the user has indicated that they no longer
+// want to wait for the connection to establish.
+static void
+tls_dialer_cancel(nni_aio *aio, void *arg, int rv)
+{
+ tls_stream *ts = arg;
+
+ NNI_ARG_UNUSED(aio);
+
+ // Just pass this down. If the connection is already done, this
+ // will have no effect.
+ nni_aio_abort(&ts->conn_aio, rv);
+}
+
+static void
+tls_dialer_dial(void *arg, nng_aio *aio)
+{
+ tls_dialer *d = arg;
+ tls_stream *ts;
+ int rv;
+
+ nni_aio_reset(aio);
+ if ((rv = nni_tls_stream_alloc(&ts, d->cfg, aio)) != 0) {
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+
+ if (!nni_aio_start(aio, tls_dialer_cancel, ts)) {
+ nni_tls_stream_free(ts);
+ return;
+ }
+ nni_mtx_lock(&d->lk);
+ d->started = true;
+ nni_mtx_unlock(&d->lk);
+
+ nng_stream_dialer_dial(d->d, &ts->conn_aio);
+}
+
+static int
+tls_dialer_set_tls(void *arg, nng_tls_config *cfg)
+{
+ tls_dialer *d = arg;
+ nng_tls_config *old;
+ if (cfg == NULL) {
+ return (NNG_EINVAL);
+ }
+
+ nng_tls_config_hold(cfg);
+
+ nni_mtx_lock(&d->lk);
+ if (d->started) {
+ nni_mtx_unlock(&d->lk);
+ nng_tls_config_free(cfg);
+ return (NNG_EBUSY);
+ }
+ old = d->cfg;
+ d->cfg = cfg;
+ nni_mtx_unlock(&d->lk);
+
+ nng_tls_config_free(old);
+ return (0);
+}
+
+static int
+tls_dialer_get_tls(void *arg, nng_tls_config **cfg)
+{
+ tls_dialer *d = arg;
+ nni_mtx_lock(&d->lk);
+ *cfg = d->cfg;
+ nni_mtx_unlock(&d->lk);
+ return (0);
+}
+
+static int
+tls_dialer_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
+{
+ tls_dialer *d = arg;
+
+ return (nni_stream_dialer_get(d->d, name, buf, szp, t));
+}
+
+static int
+tls_dialer_set(
+ void *arg, const char *name, const void *buf, size_t sz, nni_type t)
+{
+ tls_dialer *d = arg;
+
+ return (nni_stream_dialer_set(d->d, name, buf, sz, t));
+}
+
+int
+nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url)
+{
+ tls_dialer *d;
+ int rv;
+ nng_url my_url;
+
+ memcpy(&my_url, url, sizeof(my_url));
+ if (strncmp(my_url.u_scheme, "tls+", 4) == 0) {
+ my_url.u_scheme += 4;
+ }
+
+ if ((d = NNI_ALLOC_STRUCT(d)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ nni_mtx_init(&d->lk);
+
+ if ((rv = nng_stream_dialer_alloc_url(&d->d, &my_url)) != 0) {
+ nni_mtx_fini(&d->lk);
+ NNI_FREE_STRUCT(d);
+ return (rv);
+ }
+ if ((rv = nng_tls_config_alloc(&d->cfg, NNG_TLS_MODE_CLIENT)) != 0) {
+ nng_stream_dialer_free(d->d);
+ nni_mtx_fini(&d->lk);
+ NNI_FREE_STRUCT(d);
+ return (rv);
+ }
+
+ // Set the expected outbound hostname
+ nng_tls_config_server_name(d->cfg, url->u_hostname);
+
+ d->ops.sd_close = tls_dialer_close;
+ d->ops.sd_free = tls_dialer_free;
+ d->ops.sd_stop = tls_dialer_stop;
+ d->ops.sd_dial = tls_dialer_dial;
+ d->ops.sd_get = tls_dialer_get;
+ d->ops.sd_set = tls_dialer_set;
+ d->ops.sd_get_tls = tls_dialer_get_tls;
+ d->ops.sd_set_tls = tls_dialer_set_tls;
+
+ *dp = (void *) d;
+ return (rv);
+}
diff --git a/src/supplemental/tls/tls_listener.c b/src/supplemental/tls/tls_listener.c
new file mode 100644
index 00000000..5e884165
--- /dev/null
+++ b/src/supplemental/tls/tls_listener.c
@@ -0,0 +1,194 @@
+//
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core/nng_impl.h"
+
+#include "tls_common.h"
+#include "tls_engine.h"
+#include "tls_stream.h"
+
+typedef struct {
+ nng_stream_listener ops;
+ nng_stream_listener *l;
+ nng_tls_config *cfg;
+ bool started;
+ nni_mtx lk;
+} tls_listener;
+
+static void
+tls_listener_close(void *arg)
+{
+ tls_listener *l = arg;
+ nng_stream_listener_close(l->l);
+}
+
+static void
+tls_listener_stop(void *arg)
+{
+ tls_listener *l = arg;
+ nng_stream_listener_close(l->l);
+}
+
+static void
+tls_listener_free(void *arg)
+{
+ tls_listener *l;
+ if ((l = arg) != NULL) {
+ tls_listener_close(l);
+ nng_tls_config_free(l->cfg);
+ nng_stream_listener_free(l->l);
+ nni_mtx_fini(&l->lk);
+ NNI_FREE_STRUCT(l);
+ }
+}
+
+static int
+tls_listener_listen(void *arg)
+{
+ tls_listener *l = arg;
+ nni_mtx_lock(&l->lk);
+ l->started = true;
+ nni_mtx_unlock(&l->lk);
+ return (nng_stream_listener_listen(l->l));
+}
+
+// Listener cancel is called when the user has indicated that they no longer
+// want to wait for the connection to establish.
+static void
+tls_listener_cancel(nni_aio *aio, void *arg, int rv)
+{
+ tls_stream *ts = arg;
+
+ NNI_ARG_UNUSED(aio);
+
+ // Just pass this down. If the connection is already done, this
+ // will have no effect.
+ nni_aio_abort(&ts->conn_aio, rv);
+}
+
+static void
+tls_listener_accept(void *arg, nng_aio *aio)
+{
+ tls_listener *l = arg;
+ int rv;
+ tls_stream *ts;
+
+ nni_aio_reset(aio);
+ if ((rv = nni_tls_stream_alloc(&ts, l->cfg, aio)) != 0) {
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+
+ if (!nni_aio_start(aio, tls_listener_cancel, ts)) {
+ nni_tls_stream_free(ts);
+ return;
+ }
+
+ nng_stream_listener_accept(l->l, &ts->conn_aio);
+}
+
+static int
+tls_listener_set_tls(void *arg, nng_tls_config *cfg)
+{
+ tls_listener *l = arg;
+ nng_tls_config *old;
+ if (cfg == NULL) {
+ return (NNG_EINVAL);
+ }
+ nng_tls_config_hold(cfg);
+
+ nni_mtx_lock(&l->lk);
+ if (l->started) {
+ nni_mtx_unlock(&l->lk);
+ nng_tls_config_free(cfg);
+ return (NNG_EBUSY);
+ }
+ old = l->cfg;
+ l->cfg = cfg;
+ nni_mtx_unlock(&l->lk);
+
+ nng_tls_config_free(old);
+ return (0);
+}
+
+static int
+tls_listener_get_tls(void *arg, nng_tls_config **cfg)
+{
+ tls_listener *l = arg;
+ nni_mtx_lock(&l->lk);
+ *cfg = l->cfg;
+ nni_mtx_unlock(&l->lk);
+ return (0);
+}
+
+static int
+tls_listener_get(
+ void *arg, const char *name, void *buf, size_t *szp, nni_type t)
+{
+ tls_listener *l = arg;
+
+ return (nni_stream_listener_get(l->l, name, buf, szp, t));
+}
+
+static int
+tls_listener_set(
+ void *arg, const char *name, const void *buf, size_t sz, nni_type t)
+{
+ tls_listener *l = arg;
+
+ return (nni_stream_listener_set(l->l, name, buf, sz, t));
+}
+
+int
+nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url)
+{
+ tls_listener *l;
+ int rv;
+ nng_url my_url;
+
+ memcpy(&my_url, url, sizeof(my_url));
+
+ if (strncmp(my_url.u_scheme, "tls+", 4) == 0) {
+ my_url.u_scheme += 4;
+ }
+
+ if ((l = NNI_ALLOC_STRUCT(l)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ nni_mtx_init(&l->lk);
+
+ if ((rv = nng_stream_listener_alloc_url(&l->l, &my_url)) != 0) {
+ nni_mtx_fini(&l->lk);
+ NNI_FREE_STRUCT(l);
+ return (rv);
+ }
+ if ((rv = nng_tls_config_alloc(&l->cfg, NNG_TLS_MODE_SERVER)) != 0) {
+ nng_stream_listener_free(l->l);
+ nni_mtx_fini(&l->lk);
+ NNI_FREE_STRUCT(l);
+ return (rv);
+ }
+ l->ops.sl_free = tls_listener_free;
+ l->ops.sl_close = tls_listener_close;
+ l->ops.sl_stop = tls_listener_stop;
+ l->ops.sl_accept = tls_listener_accept;
+ l->ops.sl_listen = tls_listener_listen;
+ l->ops.sl_get = tls_listener_get;
+ l->ops.sl_set = tls_listener_set;
+ l->ops.sl_get_tls = tls_listener_get_tls;
+ l->ops.sl_set_tls = tls_listener_set_tls;
+ *lp = (void *) l;
+ return (0);
+}
diff --git a/src/supplemental/tls/tls_stream.c b/src/supplemental/tls/tls_stream.c
new file mode 100644
index 00000000..7ac8d5b9
--- /dev/null
+++ b/src/supplemental/tls/tls_stream.c
@@ -0,0 +1,223 @@
+//
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core/nng_impl.h"
+
+#include "tls_common.h"
+#include "tls_engine.h"
+#include "tls_stream.h"
+
+static void
+tls_bio_stream_free(void *bio)
+{
+ nng_stream_free(bio);
+}
+
+static void
+tls_bio_stream_stop(void *bio)
+{
+ nng_stream_stop(bio);
+}
+
+static void
+tls_bio_stream_close(void *bio)
+{
+ nng_stream_close(bio);
+}
+
+static void
+tls_bio_stream_send(void *bio, nng_aio *aio)
+{
+ nng_stream_send(bio, aio);
+}
+
+static void
+tls_bio_stream_recv(void *bio, nng_aio *aio)
+{
+ nng_stream_recv(bio, aio);
+}
+
+static const nni_tls_bio_ops tls_stream_bio = {
+ .bio_send = tls_bio_stream_send,
+ .bio_recv = tls_bio_stream_recv,
+ .bio_free = tls_bio_stream_free,
+ .bio_stop = tls_bio_stream_stop,
+ .bio_close = tls_bio_stream_close,
+};
+
+static void
+tls_stream_reap(void *arg)
+{
+ tls_stream *ts = arg;
+
+ nni_tls_fini(&ts->conn);
+ NNI_FREE_STRUCT(ts);
+}
+
+static nni_reap_list tls_stream_reap_list = {
+ .rl_offset = offsetof(tls_stream, reap),
+ .rl_func = tls_stream_reap,
+};
+
+void
+nni_tls_stream_free(void *arg)
+{
+ tls_stream *ts = arg;
+
+ nni_reap(&tls_stream_reap_list, ts);
+}
+
+static void
+tls_stream_stop(void *arg)
+{
+ tls_stream *ts = arg;
+ nni_tls_stop(&ts->conn);
+}
+
+static void
+tls_stream_close(void *arg)
+{
+ tls_stream *ts = arg;
+ nni_tls_close(&ts->conn);
+}
+
+static void
+tls_stream_send(void *arg, nng_aio *aio)
+{
+ tls_stream *ts = arg;
+ nni_tls_send(&ts->conn, aio);
+}
+
+static void
+tls_stream_recv(void *arg, nng_aio *aio)
+{
+ tls_stream *ts = arg;
+ nni_tls_recv(&ts->conn, aio);
+}
+
+static void
+tls_stream_conn_cb(void *arg)
+{
+ tls_stream *ts = arg;
+ nng_stream *bio;
+ int rv;
+
+ if ((rv = nni_aio_result(&ts->conn_aio)) != 0) {
+ nni_aio_finish_error(ts->user_aio, rv);
+ nni_tls_stream_free(ts);
+ return;
+ }
+
+ bio = nni_aio_get_output(&ts->conn_aio, 0);
+
+ if ((rv = nni_tls_start(&ts->conn, &tls_stream_bio, bio)) != 0) {
+ // NB: if this fails, it *will* have set the bio either way.
+ // So nni_tls_stream_free will also free the bio.
+ nni_aio_finish_error(ts->user_aio, rv);
+ nni_tls_stream_free(ts);
+ return;
+ }
+
+ nni_aio_set_output(ts->user_aio, 0, &ts->stream);
+ nni_aio_finish(ts->user_aio, 0, 0);
+}
+
+static int tls_stream_get(
+ void *arg, const char *name, void *buf, size_t *szp, nni_type t);
+
+int
+nni_tls_stream_alloc(tls_stream **tsp, nng_tls_config *cfg, nng_aio *user_aio)
+{
+ tls_stream *ts;
+ const nng_tls_engine *eng;
+ size_t size;
+ int rv;
+
+ eng = cfg->engine;
+ size = NNI_ALIGN_UP(sizeof(*ts)) + eng->conn_ops->size;
+
+ if ((ts = nni_zalloc(size)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+
+ ts->user_aio = user_aio;
+
+ // NB: free is exposed for benefit of dialer/listener
+ ts->stream.s_free = nni_tls_stream_free;
+ ts->stream.s_close = tls_stream_close;
+ ts->stream.s_stop = tls_stream_stop;
+ ts->stream.s_send = tls_stream_send;
+ ts->stream.s_recv = tls_stream_recv;
+ ts->stream.s_get = tls_stream_get;
+
+ nni_aio_init(&ts->conn_aio, tls_stream_conn_cb, ts);
+
+ if ((rv = nni_tls_init(&ts->conn, cfg)) != 0) {
+ nni_tls_stream_free(ts);
+ return (rv);
+ }
+
+ *tsp = ts;
+ return (0);
+}
+
+static int
+tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t)
+{
+ tls_stream *ts = arg;
+
+ return (nni_copyout_bool(nni_tls_verified(&ts->conn), buf, szp, t));
+}
+
+static int
+tls_get_peer_cn(void *arg, void *buf, size_t *szp, nni_type t)
+{
+ NNI_ARG_UNUSED(szp);
+
+ if (t != NNI_TYPE_STRING) {
+ return (NNG_EBADTYPE);
+ }
+
+ tls_stream *ts = arg;
+ *(char **) buf = (char *) nni_tls_peer_cn(&ts->conn);
+ return (0);
+}
+
+static const nni_option tls_stream_options[] = {
+ {
+ .o_name = NNG_OPT_TLS_VERIFIED,
+ .o_get = tls_get_verified,
+ },
+ {
+ .o_name = NNG_OPT_TLS_PEER_CN,
+ .o_get = tls_get_peer_cn,
+ },
+ {
+ .o_name = NULL,
+ },
+};
+
+static int
+tls_stream_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
+{
+ tls_stream *ts = arg;
+ int rv;
+
+ if ((rv = nni_stream_get(ts->conn.bio, name, buf, szp, t)) !=
+ NNG_ENOTSUP) {
+ return (rv);
+ }
+ return (nni_getopt(tls_stream_options, name, ts, buf, szp, t));
+}
diff --git a/src/supplemental/tls/tls_stream.h b/src/supplemental/tls/tls_stream.h
new file mode 100644
index 00000000..78760f82
--- /dev/null
+++ b/src/supplemental/tls/tls_stream.h
@@ -0,0 +1,31 @@
+//
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 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.
+//
+
+#ifndef NNG_TLS_TLS_STREAM_H
+#define NNG_TLS_TLS_STREAM_H
+
+#include "core/nng_impl.h"
+#include "tls_common.h"
+
+typedef struct tls_stream_s {
+ nng_stream stream;
+ size_t size;
+ nni_reap_node reap;
+ nni_aio conn_aio;
+ nni_aio *user_aio;
+ tls_conn conn; // NB: must be last!
+} tls_stream;
+
+extern void nni_tls_stream_free(void *arg);
+extern int nni_tls_stream_alloc(
+ tls_stream **tsp, nng_tls_config *cfg, nng_aio *user_aio);
+
+#endif
diff --git a/src/supplemental/tls/tls_stubs.c b/src/supplemental/tls/tls_stubs.c
new file mode 100644
index 00000000..fc16afb5
--- /dev/null
+++ b/src/supplemental/tls/tls_stubs.c
@@ -0,0 +1,199 @@
+//
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2019 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 "core/nng_impl.h"
+#include "tls_engine.h"
+
+// 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_hold(nng_tls_config *cfg)
+{
+ NNI_ARG_UNUSED(cfg);
+}
+
+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)
+{
+}