diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/platform.h | 2 | ||||
| -rw-r--r-- | src/platform/posix/posix_udp.c | 2 | ||||
| -rw-r--r-- | src/platform/windows/win_udp.c | 4 | ||||
| -rw-r--r-- | src/supplemental/tls/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_common.c | 917 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_common.h | 106 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_dialer.c | 190 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_listener.c | 194 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_stream.c | 223 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_stream.h | 31 | ||||
| -rw-r--r-- | src/supplemental/tls/tls_stubs.c | 199 |
11 files changed, 1079 insertions, 797 deletions
diff --git a/src/core/platform.h b/src/core/platform.h index bdb8beb4..3b6f4db5 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -357,7 +357,7 @@ typedef struct nni_plat_udp nni_plat_udp; // address specified in the AIO. The remote address is // not used. The resulting nni_plat_udp structure is returned in the // aio's a_pipe. -extern int nni_plat_udp_open(nni_plat_udp **, nni_sockaddr *); +extern int nni_plat_udp_open(nni_plat_udp **, const nni_sockaddr *); // nni_plat_udp_close closes the underlying UDP socket. extern void nni_plat_udp_close(nni_plat_udp *); diff --git a/src/platform/posix/posix_udp.c b/src/platform/posix/posix_udp.c index a1601874..14bb6c22 100644 --- a/src/platform/posix/posix_udp.c +++ b/src/platform/posix/posix_udp.c @@ -308,7 +308,7 @@ nni_posix_udp_cb(void *arg, unsigned events) } int -nni_plat_udp_open(nni_plat_udp **upp, nni_sockaddr *bindaddr) +nni_plat_udp_open(nni_plat_udp **upp, const nni_sockaddr *bindaddr) { nni_plat_udp *udp; int salen; diff --git a/src/platform/windows/win_udp.c b/src/platform/windows/win_udp.c index a280e116..0ff3ca1c 100644 --- a/src/platform/windows/win_udp.c +++ b/src/platform/windows/win_udp.c @@ -1,5 +1,5 @@ // -// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2025 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 @@ -36,7 +36,7 @@ static void udp_recv_start(nni_plat_udp *); // nni_plat_udp_open initializes a UDP socket, binding to the local // address specified specified. int -nni_plat_udp_open(nni_plat_udp **udpp, nni_sockaddr *sa) +nni_plat_udp_open(nni_plat_udp **udpp, const nni_sockaddr *sa) { nni_plat_udp *u; SOCKADDR_STORAGE ss; 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) +{ +} |
