diff options
| author | Garrett D'Amore <garrett@damore.org> | 2025-04-26 18:25:48 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2025-04-27 10:53:52 -0700 |
| commit | e7977ae777ac62928041e2a07f6eddc69eb4fc40 (patch) | |
| tree | 823feb4f4661f98d3dab082961e305c3b9f9206e /src/supplemental/tls/tls_stream.c | |
| parent | 527a07c6b632f6991102d4fd08ac1f5f962ddfdf (diff) | |
| download | nng-e7977ae777ac62928041e2a07f6eddc69eb4fc40.tar.gz nng-e7977ae777ac62928041e2a07f6eddc69eb4fc40.tar.bz2 nng-e7977ae777ac62928041e2a07f6eddc69eb4fc40.zip | |
TLS: break up the TLS layer a bit to refactor for DTLS.
This allows us to break the assumption that the bottom half is
TCP, or even an nng_stream, since the DTLS layer will use a totally
different layer. Only nng_stream neeeds to support dial and listen.
Also: UDP: Make the sockaddr arguments to open const.
Also: Align the IPv6 address in the sockaddr (this allows for
efficient 64-bit or even 128-bit operations on these values.)
Diffstat (limited to 'src/supplemental/tls/tls_stream.c')
| -rw-r--r-- | src/supplemental/tls/tls_stream.c | 223 |
1 files changed, 223 insertions, 0 deletions
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)); +} |
