diff options
Diffstat (limited to 'src')
40 files changed, 2582 insertions, 183 deletions
diff --git a/src/core/aio.c b/src/core/aio.c index 4c4c78b6..76ca7726 100644 --- a/src/core/aio.c +++ b/src/core/aio.c @@ -499,7 +499,7 @@ nni_aio_finish_sync(nni_aio *aio, nng_err result, size_t count) void nni_aio_finish_error(nni_aio *aio, nng_err result) { - nni_aio_finish_impl(aio, result, NNG_OK, NULL, false); + nni_aio_finish_impl(aio, result, 0, NULL, false); } void diff --git a/src/core/defs.h b/src/core/defs.h index 419f5ba7..432c0be7 100644 --- a/src/core/defs.h +++ b/src/core/defs.h @@ -151,25 +151,25 @@ typedef void (*nni_cb)(void *); (ptr)[0] = (uint8_t) ((uint64_t) (u)); \ } while (0) -#define NNI_GET16LE(ptr, v) \ - v = (((uint16_t) ((uint8_t) (ptr)[1])) << 8u) + \ - (((uint16_t) (uint8_t) (ptr)[0])) - -#define NNI_GET32LE(ptr, v) \ - v = (((uint32_t) ((uint8_t) (ptr)[3])) << 24u) + \ - (((uint32_t) ((uint8_t) (ptr)[2])) << 16u) + \ - (((uint32_t) ((uint8_t) (ptr)[1])) << 8u) + \ - (((uint32_t) (uint8_t) (ptr)[0])) - -#define NNI_GET64LE(ptr, v) \ - v = (((uint64_t) ((uint8_t) (ptr)[7])) << 56u) + \ - (((uint64_t) ((uint8_t) (ptr)[6])) << 48u) + \ - (((uint64_t) ((uint8_t) (ptr)[5])) << 40u) + \ - (((uint64_t) ((uint8_t) (ptr)[4])) << 32u) + \ - (((uint64_t) ((uint8_t) (ptr)[3])) << 24u) + \ - (((uint64_t) ((uint8_t) (ptr)[2])) << 16u) + \ - (((uint64_t) ((uint8_t) (ptr)[1])) << 8u) + \ - (((uint64_t) (uint8_t) (ptr)[0])) +#define NNI_GET16LE(ptr, v) \ + v = (((uint16_t) (((uint8_t *) (ptr))[1])) << 8u) + \ + ((uint16_t) ((uint8_t *) (ptr))[0]) + +#define NNI_GET32LE(ptr, v) \ + v = (((uint32_t) (((uint8_t *) (ptr))[3])) << 24u) + \ + (((uint32_t) (((uint8_t *) (ptr))[2])) << 16u) + \ + (((uint32_t) (((uint8_t *) (ptr))[1])) << 8u) + \ + (((uint32_t) ((uint8_t *) (ptr))[0])) + +#define NNI_GET64LE(ptr, v) \ + v = (((uint64_t) (((uint8_t *) (ptr))[7])) << 56u) + \ + (((uint64_t) (((uint8_t *) (ptr))[6])) << 48u) + \ + (((uint64_t) (((uint8_t *) (ptr))[5])) << 40u) + \ + (((uint64_t) (((uint8_t *) (ptr))[4])) << 32u) + \ + (((uint64_t) (((uint8_t *) (ptr))[3])) << 24u) + \ + (((uint64_t) (((uint8_t *) (ptr))[2])) << 16u) + \ + (((uint64_t) (((uint8_t *) (ptr))[1])) << 8u) + \ + (((uint64_t) ((uint8_t *) (ptr))[0])) // This increments a pointer a fixed number of byte cells. #define NNI_INCPTR(ptr, n) ((ptr) = (void *) ((char *) (ptr) + (n))) diff --git a/src/core/pipe.c b/src/core/pipe.c index c57a8d43..5ce85420 100644 --- a/src/core/pipe.c +++ b/src/core/pipe.c @@ -250,7 +250,7 @@ pipe_create(nni_pipe **pp, nni_sock *sock, nni_sp_tran *tran, nni_dialer *d, size_t sz; sz = NNI_ALIGN_UP(sizeof(*p)) + NNI_ALIGN_UP(pops->pipe_size) + - NNI_ALIGN_UP(tops->p_size); + NNI_ALIGN_UP(tops->p_size()); if ((p = nni_zalloc(sz)) == NULL) { return (NNG_ENOMEM); diff --git a/src/core/platform.h b/src/core/platform.h index a13ae9f2..cdb0d887 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -359,6 +359,10 @@ typedef struct nni_plat_udp nni_plat_udp; // aio's a_pipe. extern int nni_plat_udp_open(nni_plat_udp **, const nni_sockaddr *); +// nni_plat_udp_stop stops I/O on the socket, but does not close it +// or free the underlying data. May block for callbacks to complete. +extern void nni_plat_udp_stop(nni_plat_udp *); + // nni_plat_udp_close closes the underlying UDP socket. extern void nni_plat_udp_close(nni_plat_udp *); diff --git a/src/core/url.c b/src/core/url.c index 9db92992..fb13ee59 100644 --- a/src/core/url.c +++ b/src/core/url.c @@ -273,6 +273,9 @@ static const char *nni_schemes[] = { "udp", "udp4", "udp6", + "dtls", + "dtls4", + "dtls6", // we don't support these "file", "mailto", @@ -2148,6 +2148,12 @@ nng_udp_open(nng_udp **udp, nng_sockaddr *sa) } void +nng_udp_stop(nng_udp *udp) +{ + nni_plat_udp_stop((nni_plat_udp *) udp); +} + +void nng_udp_close(nng_udp *udp) { nni_plat_udp_close((nni_plat_udp *) udp); diff --git a/src/platform/posix/posix_udp.c b/src/platform/posix/posix_udp.c index b643d0b2..99460721 100644 --- a/src/platform/posix/posix_udp.c +++ b/src/platform/posix/posix_udp.c @@ -58,6 +58,7 @@ struct nni_plat_udp { nni_list udp_recvq; nni_list udp_sendq; nni_mtx udp_mtx; + bool udp_stopped; }; static void @@ -361,15 +362,20 @@ nni_plat_udp_open(nni_plat_udp **upp, const nni_sockaddr *bindaddr) } void -nni_plat_udp_close(nni_plat_udp *udp) +nni_plat_udp_stop(nni_plat_udp *udp) { - nni_posix_pfd_stop(&udp->udp_pfd); - nni_mtx_lock(&udp->udp_mtx); + udp->udp_stopped = true; nni_posix_udp_doclose(udp); nni_mtx_unlock(&udp->udp_mtx); nni_posix_pfd_stop(&udp->udp_pfd); +} + +void +nni_plat_udp_close(nni_plat_udp *udp) +{ + nni_plat_udp_stop(udp); nni_posix_pfd_fini(&udp->udp_pfd); (void) close(udp->udp_fd); nni_mtx_fini(&udp->udp_mtx); @@ -399,6 +405,11 @@ nni_plat_udp_recv(nni_plat_udp *udp, nni_aio *aio) nni_mtx_unlock(&udp->udp_mtx); return; } + if (udp->udp_stopped) { + nni_mtx_unlock(&udp->udp_mtx); + nni_aio_finish_error(aio, NNG_ECLOSED); + return; + } nni_list_append(&udp->udp_recvq, aio); if (nni_list_first(&udp->udp_recvq) == aio) { if ((rv = nni_posix_pfd_arm(&udp->udp_pfd, NNI_POLL_IN)) != @@ -420,6 +431,11 @@ nni_plat_udp_send(nni_plat_udp *udp, nni_aio *aio) nni_mtx_unlock(&udp->udp_mtx); return; } + if (udp->udp_stopped) { + nni_mtx_unlock(&udp->udp_mtx); + nni_aio_finish_error(aio, NNG_ECLOSED); + return; + } nni_list_append(&udp->udp_sendq, aio); if (nni_list_first(&udp->udp_sendq) == aio) { if ((rv = nni_posix_pfd_arm(&udp->udp_pfd, NNI_POLL_OUT)) != diff --git a/src/platform/windows/win_udp.c b/src/platform/windows/win_udp.c index 709ef82e..79720223 100644 --- a/src/platform/windows/win_udp.c +++ b/src/platform/windows/win_udp.c @@ -85,9 +85,8 @@ nni_plat_udp_open(nni_plat_udp **udpp, const nni_sockaddr *sa) return (rv); } -// nni_plat_udp_close closes the underlying UDP socket. void -nni_plat_udp_close(nni_plat_udp *u) +nni_plat_udp_stop(nni_plat_udp *u) { nni_mtx_lock(&u->lk); u->closed = true; @@ -98,6 +97,13 @@ nni_plat_udp_close(nni_plat_udp *u) nni_cv_wait(&u->cv); } nni_mtx_unlock(&u->lk); +} + +// nni_plat_udp_close closes the underlying UDP socket. +void +nni_plat_udp_close(nni_plat_udp *u) +{ + nni_plat_udp_stop(u); if (u->s != INVALID_SOCKET) { closesocket(u->s); diff --git a/src/sp/transport.c b/src/sp/transport.c index e1c2737e..1f2e0021 100644 --- a/src/sp/transport.c +++ b/src/sp/transport.c @@ -105,6 +105,9 @@ extern void nni_sp_wss_register(void); #ifdef NNG_TRANSPORT_FDC extern void nni_sp_sfd_register(void); #endif +#ifdef NNG_TRANSPORT_DTLS +extern void nni_sp_dtls_register(void); +#endif void nni_sp_tran_sys_init(void) @@ -133,6 +136,9 @@ nni_sp_tran_sys_init(void) #ifdef NNG_TRANSPORT_FDC nni_sp_sfd_register(); #endif +#ifdef NNG_TRANSPORT_DTLS + nni_sp_dtls_register(); +#endif } // nni_sp_tran_sys_fini finalizes the entire transport system, including all diff --git a/src/sp/transport.h b/src/sp/transport.h index 9d67b7c2..6aa2086b 100644 --- a/src/sp/transport.h +++ b/src/sp/transport.h @@ -139,7 +139,12 @@ struct nni_sp_pipe_ops { // p_init initializes the pipe data structures. The main // purpose of this is so that the pipe will see the upper // layer nni_pipe and get a chance to register stats and such. - size_t p_size; + // size_t p_size; + + // p_size returns the size of the transport data needed for a pipe. + // This allows for dynamic registration of context size to allow for + // different tunings or different runtimes. + size_t (*p_size)(void); // p_init initializes the transport's pipe data structure. // The pipe MUST be left in a state that p_fini can be safely diff --git a/src/sp/transport/CMakeLists.txt b/src/sp/transport/CMakeLists.txt index d0875e57..5b1c9b17 100644 --- a/src/sp/transport/CMakeLists.txt +++ b/src/sp/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2024 Staysail Systems, Inc. <info@staystail.tech> +# Copyright 2025 Staysail Systems, Inc. <info@staystail.tech> # # This software is supplied under the terms of the MIT License, a # copy of which should be located in the distribution where this @@ -15,5 +15,6 @@ add_subdirectory(inproc) add_subdirectory(ipc) add_subdirectory(tcp) add_subdirectory(tls) +add_subdirectory(dtls) add_subdirectory(udp) add_subdirectory(ws) diff --git a/src/sp/transport/dtls/CMakeLists.txt b/src/sp/transport/dtls/CMakeLists.txt new file mode 100644 index 00000000..e1472345 --- /dev/null +++ b/src/sp/transport/dtls/CMakeLists.txt @@ -0,0 +1,17 @@ +# +# Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> +# +# This software is supplied under the terms of the MIT License, a +# copy of which should be located in the distribution where this +# file was obtained (LICENSE.txt). A copy of the license may also be +# found online at https://opensource.org/licenses/MIT. +# + +# DTLS transport +nng_directory(dtls) + +if (NNG_TRANSPORT_DTLS) + nng_sources(dtls.c) + nng_defines(NNG_TRANSPORT_DTLS) + nng_test(dtls_tran_test) +endif() diff --git a/src/sp/transport/dtls/dtls.c b/src/sp/transport/dtls/dtls.c new file mode 100644 index 00000000..02961a56 --- /dev/null +++ b/src/sp/transport/dtls/dtls.c @@ -0,0 +1,1802 @@ +// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "core/aio.h" +#include "core/defs.h" +#include "core/idhash.h" +#include "core/message.h" +#include "core/nng_impl.h" +#include "core/options.h" +#include "core/pipe.h" +#include "core/platform.h" +#include "core/socket.h" +#include "core/stats.h" +#include "nng/nng.h" +#include "supplemental/tls/tls_common.h" + +#include <string.h> + +// Experimental DTLS transport. Unicast only. +// +typedef struct dtls_pipe dtls_pipe; +typedef struct dtls_ep dtls_ep; +typedef struct dtls_conn dtls_conn; + +const uint8_t PROTO_VERSION = 1; + +// OP code, 8 bits +enum dtls_opcode { + OPCODE_DATA = 0, + OPCODE_CREQ = 1, + OPCODE_CACK = 2, + OPCODE_DISC = 3, +}; + +// Disconnect reason, must be 16 bits +typedef enum dtls_disc_reason { + DISC_CLOSED = 0, // normal close + DISC_TYPE = 1, // bad SP type + DISC_NOTCONN = 2, // no such connection + DISC_REFUSED = 3, // refused by policy + DISC_MSGSIZE = 4, // message too large + DISC_NEGO = 5, // neogtiation failed + DISC_INACTIVE = 6, // closed due to inactivity + DISC_PROTO = 7, // other protocol error + DISC_NOBUF = 8, // resources exhausted +} dtls_disc_reason; + +#ifndef NNG_DTLS_TXQUEUE_LEN +#define NNG_DTLS_TXQUEUE_LEN 32 +#endif + +#ifndef NNG_DTLS_RXQUEUE_LEN +#define NNG_DTLS_RXQUEUE_LEN 16 +#endif + +// The maximum TLS record size +#define DTLS_MAX_RECORD 16384 + +// For DTLS we use a maximum record size of 16384, +// but we reserve some space for headers. DTLS needs +// 13 bytes, and the transport layer needs 8 bytes. +// To leave some room for the future, we just trim to 64 bytes. +#ifndef NNG_DTLS_RECVMAX +#define NNG_DTLS_RECVMAX (DTLS_MAX_RECORD - 64) +#endif + +#ifndef NNG_DTLS_REFRESH +#define NNG_DTLS_REFRESH (5 * NNI_SECOND) +#endif + +#ifndef NNG_DTLS_CONNRETRY +#define NNG_DTLS_CONNRETRY (NNI_SECOND / 5) +#endif + +// 64-bit protocol header +typedef struct dtls_sp_hdr { + uint8_t us_ver; + uint8_t us_op_code; + uint16_t us_type; + uint16_t us_params[2]; +} dtls_sp_hdr; + +// DTLS pipe resend (CREQ) in msec (nng_duration) +#define DTLS_PIPE_REFRESH(p) ((p)->refresh) + +// DTLS pipe timeout in msec (nng_duration) +#define DTLS_PIPE_TIMEOUT(p) ((p)->refresh * 5) + +struct dtls_pipe { + dtls_ep *ep; + nni_pipe *npipe; + nng_sockaddr peer_addr; + uint64_t id; // hash of peer address + uint16_t peer; + uint16_t proto; + bool matched; // true if have matched and given this to SP + bool closed; // true if we are closed (no more send or recv!) + bool dialer; // true if we are dialer + nng_duration refresh; // seconds, for the protocol + nng_time next_wake; + nng_time expire; // inactivity expiration time + nng_time next_refresh; + nni_list_node node; + nni_lmq rx_mq; + + // Upper layer queues. These are between the PIPE and SP. + bool send_busy; // true if send is in process + uint16_t send_max; // peer's max recv size + nni_list send_aios; + uint8_t *send_buf; + size_t send_bufsz; + nng_aio send_tls_aio; + + bool recv_busy; + bool recv_rdy; // receive is done and data in recvbuf + uint16_t recv_max; // max recv size + nni_list recv_aios; + uint8_t *recv_buf; + size_t recv_bufsz; + nng_aio recv_tls_aio; + + // Lower layer queues. These are between the + + uint8_t send_op; // usually OPCODE_DATA + uint8_t last_op; // last op code we sent + uint16_t reason; // only for disconnect + + nni_mtx lower_mtx; // protects the lower rx_q, etc. + + // This is the lower level RX buffer, which contains only + // received ciphertext (content before passed to TLS layer for + // decrypt). The actual pointers may change, as we "swap" + // buffers between the endpoint and the pipe to avoid copying. + nni_list rx_q; // lower aio from the TLS layer + + nni_tls_conn tls; +}; + +struct dtls_ep { + nng_udp *udp; + nni_mtx mtx; + uint16_t proto; + uint16_t peer; + uint16_t af; // address family + bool fini; + bool started; + bool closed; + nng_url *url; + const char *host; // for dialers + nni_aio *useraio; + nni_aio *connaio; + nni_aio timeaio; + nni_aio resaio; + bool dialer; + nni_listener *nlistener; + nni_dialer *ndialer; + nni_msg *rx_payload; // current receive message + nng_sockaddr rx_sa; // addr for last message + nni_aio tx_aio; // aio for TX handling + nni_aio rx_aio; // aio for RX handling + nni_id_map pipes; // pipes (indexed by id) + nni_sockaddr self_sa; // our address + nni_sockaddr peer_sa; // peer address, only for dialer; + nni_list connaios; // aios from accept waiting for a client peer + nni_list connpipes; // pipes waiting to be connected + nng_duration refresh; // refresh interval for connections in seconds + uint16_t rcvmax; // max payload, trimmed to uint16_t + nni_resolv_item resolv; + + nng_tls_config *tlscfg; + + size_t rx_size; // size of the rx buffer + void *rx_buf; + + nni_stat_item st_rcv_max; + nni_stat_item st_rcv_reorder; + nni_stat_item st_rcv_toobig; + nni_stat_item st_rcv_nomatch; + nni_stat_item st_rcv_copy; + nni_stat_item st_rcv_nocopy; + nni_stat_item st_rcv_nobuf; + nni_stat_item st_snd_toobig; + nni_stat_item st_snd_nobuf; + nni_stat_item st_peer_inactive; + nni_stat_item st_copy_max; +}; + +static void dtls_ep_start(dtls_ep *); +static void dtls_resolv_cb(void *); +static void dtls_rx_cb(void *); + +static void dtls_ep_match(dtls_ep *ep); +static void dtls_remove_pipe(dtls_pipe *p); + +// BIO send/recv functions for use by the common TLS layer. + +static void +dtls_bio_cancel(nng_aio *aio, void *arg, nng_err rv) +{ + dtls_pipe *p = arg; + nni_mtx_lock(&p->lower_mtx); + if (nni_aio_list_active(aio)) { + nni_aio_list_remove(aio); + } + nni_aio_finish_error(aio, rv); + nni_mtx_unlock(&p->lower_mtx); +} + +static void +dtls_bio_recv_done(dtls_pipe *p) +{ + nng_aio *aio; + uint8_t *ptr; + size_t resid; + nni_msg *msg; + + while ((!nni_lmq_empty(&p->rx_mq)) && + ((aio = nni_list_first(&p->rx_q)) != NULL)) { + + nni_aio_list_remove(aio); + nni_lmq_get(&p->rx_mq, &msg); + + // assumption we only have a body, because we don't bother to + // fill in the header for raw UDP. + + resid = nni_msg_len(msg); + ptr = nni_msg_body(msg); + + for (unsigned i = 0; i < aio->a_nio && resid > 0; i++) { + size_t num = resid > aio->a_iov[i].iov_len + ? aio->a_iov[i].iov_len + : resid; + memcpy(aio->a_iov[i].iov_buf, ptr, num); + ptr += num; + resid -= num; + } + nni_aio_finish(aio, NNG_OK, nni_msg_len(msg)); + nni_msg_free(msg); + } +} + +static void +dtls_bio_recv(void *arg, nng_aio *aio) +{ + dtls_pipe *p = arg; + + nni_mtx_lock(&p->lower_mtx); + if (!nni_aio_start(aio, dtls_bio_cancel, p)) { + nni_mtx_unlock(&p->lower_mtx); + return; + } + + nni_aio_list_append(&p->rx_q, aio); + dtls_bio_recv_done(p); + nni_mtx_unlock(&p->lower_mtx); +} + +static void +dtls_bio_send(void *arg, nng_aio *aio) +{ + dtls_pipe *p = arg; + + nni_mtx_lock(&p->lower_mtx); + if (!p->closed) { + nni_aio_set_input(aio, 0, &p->peer_addr); + nng_udp_send(p->ep->udp, aio); + } + nni_mtx_unlock(&p->lower_mtx); +} + +static void +dtls_bio_free(void *arg) +{ + NNI_ARG_UNUSED(arg); +} + +static void +dtls_bio_close(void *arg) +{ + NNI_ARG_UNUSED(arg); +} + +static void +dtls_bio_stop(void *arg) +{ + dtls_pipe *p = arg; + nni_aio_stop(&p->recv_tls_aio); + nni_aio_stop(&p->send_tls_aio); +} + +static nni_tls_bio_ops dtls_bio_ops = { + .bio_send = dtls_bio_send, + .bio_recv = dtls_bio_recv, + .bio_close = dtls_bio_close, + .bio_stop = dtls_bio_stop, + .bio_free = dtls_bio_free, +}; + +static void +dtls_tran_init(void) +{ +} + +static void +dtls_tran_fini(void) +{ +} + +// +// Upper layer functions - moving data between TLS and SP. +// TLS acts as kind of a stream for us, so we only see the +// data that is meant for us, but we will send and receive +// control messages that are not just data payloads. +// + +static void dtls_pipe_send_cancel(nng_aio *, void *, nng_err); +static void dtls_pipe_send_tls(dtls_pipe *); +static void dtls_pipe_send_tls_cb(void *arg); + +static void +dtls_pipe_send(void *arg, nni_aio *aio) +{ + dtls_pipe *p = arg; + dtls_ep *ep; + nng_msg *msg; + size_t count = 0; + size_t sndmax; + + msg = nni_aio_get_msg(aio); + ep = p->ep; + + if (msg != NULL) { + count = nni_msg_len(msg) + nni_msg_header_len(msg); + } + + nni_mtx_lock(&ep->mtx); + sndmax = p->send_max; + if (!nni_aio_start(aio, dtls_pipe_send_cancel, p)) { + nni_mtx_unlock(&ep->mtx); + return; + } + + nni_aio_reset(aio); + if ((nni_msg_len(msg) + nni_msg_header_len(msg)) > sndmax) { + // rather failing this with an error, we just drop it + // on the floor. this is on the sender, so there isn't + // a compelling need to disconnect the pipe, since it + // we're not being "ill-behaved" to our peer. + nni_stat_inc(&ep->st_snd_toobig, 1); + nni_mtx_unlock(&ep->mtx); + nni_aio_finish(aio, 0, count); + nni_msg_free(msg); + return; + } + + nni_aio_list_append(&p->send_aios, aio); + dtls_pipe_send_tls(p); + nni_mtx_unlock(&ep->mtx); +} + +static void +dtls_pipe_send_cancel(nng_aio *aio, void *arg, nng_err err) +{ + dtls_pipe *p = arg; + nni_mtx_lock(&p->ep->mtx); + if (nni_aio_list_active(aio)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, err); + } + nni_mtx_unlock(&p->ep->mtx); +} + +// Lower layer send/recv functions, used by the pipe layer. + +static void +dtls_pipe_send_tls(dtls_pipe *p) +{ + nni_aio *aio; + nng_msg *msg; + uint8_t opcode; + nng_iov iov; + dtls_sp_hdr *hdr = (void *) p->send_buf; + + if (p->send_busy || p->closed) { + return; + } + + opcode = p->send_op; + // reset the last op + p->send_op = OPCODE_DATA; + + hdr->us_ver = PROTO_VERSION; + hdr->us_op_code = opcode; + NNI_PUT16LE(&hdr->us_type, p->proto); + hdr->us_params[0] = 0; + hdr->us_params[1] = 0; + + iov.iov_buf = hdr; + iov.iov_len = sizeof(*hdr); + + switch (opcode) { + case OPCODE_DATA: + for (;;) { + if ((aio = nni_list_first(&p->send_aios)) == NULL) { + // no work for us! + return; + } + nni_aio_list_remove(aio); + msg = nni_aio_get_msg(aio); + if (nni_msg_header_len(msg) + nni_msg_len(msg) + + sizeof(*hdr) > + p->send_bufsz) { + nng_msg_free(msg); + nni_aio_finish_error(aio, NNG_EMSGSIZE); + continue; + } + break; // for loop + } + + size_t len = nni_msg_header_len(msg); + uint8_t *data = (void *) (hdr + 1); + memcpy(data, nni_msg_header(msg), len); + data += len; + memcpy(data, nni_msg_body(msg), nni_msg_len(msg)); + len += nni_msg_len(msg); + + NNI_PUT16LE(&hdr->us_params[0], (uint16_t) len); + iov.iov_len += len; + + nni_msg_free(msg); + nni_aio_set_msg(aio, NULL); + nni_aio_finish(aio, 0, len); + break; + + case OPCODE_CREQ: + case OPCODE_CACK: + NNI_PUT16LE(&hdr->us_params[0], p->recv_max); + NNI_PUT16LE(&hdr->us_params[1], p->refresh); + break; + + case OPCODE_DISC: + NNI_PUT16LE(&hdr->us_params[0], p->reason); + p->closed = true; + break; + default: + NNI_ASSERT(false); // this should never happen! + // fall back to sending a disconnect + hdr->us_op_code = OPCODE_DISC; + NNI_PUT16LE(&hdr->us_params[0], DISC_PROTO); + } + + p->last_op = opcode; + p->send_busy = true; + nni_aio_set_iov(&p->send_tls_aio, 1, &iov); + nni_tls_send(&p->tls, &p->send_tls_aio); +} + +static void +dtls_pipe_send_tls_cb(void *arg) +{ + dtls_pipe *p = arg; + + nni_mtx_lock(&p->ep->mtx); + + p->send_busy = false; + if (nni_aio_result(&p->send_tls_aio) != NNG_OK || + p->last_op == OPCODE_DISC) { + nni_pipe_close(p->npipe); + if (p->matched == 0) { + dtls_remove_pipe(p); + } + nni_mtx_unlock(&p->ep->mtx); + return; + } + dtls_pipe_send_tls(p); + nni_mtx_unlock(&p->ep->mtx); +} + +// RECV SIDE + +static void dtls_pipe_recv_cancel(nni_aio *, void *, nng_err); +static void dtls_pipe_recv_tls(dtls_pipe *); + +static void +dtls_pipe_recv(void *arg, nni_aio *aio) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + + nni_aio_reset(aio); + nni_mtx_lock(&ep->mtx); + if (p->closed) { + nni_mtx_unlock(&ep->mtx); + nni_aio_finish_error(aio, NNG_ECLOSED); + return; + } + if (!nni_aio_start(aio, dtls_pipe_recv_cancel, p)) { + nni_mtx_unlock(&ep->mtx); + return; + } + + nni_list_append(&p->recv_aios, aio); + dtls_pipe_recv_tls(p); + nni_mtx_unlock(&ep->mtx); +} + +static void +dtls_pipe_recv_cancel(nni_aio *aio, void *arg, nng_err rv) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + + nni_mtx_lock(&ep->mtx); + if (!nni_aio_list_active(aio)) { + nni_mtx_unlock(&ep->mtx); + return; + } + nni_aio_list_remove(aio); + nni_mtx_unlock(&ep->mtx); + nni_aio_finish_error(aio, rv); +} + +static void +dtls_pipe_recv_tls_start(dtls_pipe *p) +{ + nng_iov iov; + if (p->recv_busy || p->closed) { + return; + } + p->recv_busy = true; + iov.iov_buf = p->recv_buf; + iov.iov_len = p->recv_bufsz; + + nni_aio_set_iov(&p->recv_tls_aio, 1, &iov); + nni_tls_recv(&p->tls, &p->recv_tls_aio); +} + +static void +dtls_pipe_recv_tls(dtls_pipe *p) +{ + nng_aio *aio = nni_list_first(&p->recv_aios); + size_t len; + nng_msg *msg; + int rv; + + if (aio == NULL) { + return; + } + if (!p->recv_rdy) { + dtls_pipe_recv_tls_start(p); + return; + } + + p->recv_rdy = false; + + nni_aio_list_remove(aio); + len = nng_aio_count(&p->recv_tls_aio); + NNI_ASSERT(len >= sizeof(dtls_sp_hdr)); + len -= sizeof(dtls_sp_hdr); + + if ((rv = nni_msg_alloc(&msg, len)) != NNG_OK) { + nni_aio_finish_error(aio, rv); + return; + } + + memcpy(nng_msg_body(msg), p->recv_buf + sizeof(dtls_sp_hdr), len); + nni_aio_finish_msg(aio, msg); +} + +static void +dtls_pipe_recv_tls_cb(void *arg) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + dtls_sp_hdr *hdr = (void *) p->recv_buf; + nng_aio *aio = &p->recv_tls_aio; + uint16_t proto; + uint16_t refresh; + uint16_t rcvmax; + nng_err rv; + + nni_mtx_lock(&ep->mtx); + p->recv_busy = false; + + if ((rv = nni_aio_result(aio)) != NNG_OK) { + + // If we didn't connect yet, issue an error so the peer can see + // a connection failure (e.g. if we failed the TLS handshake.) + if (p->dialer && !p->matched) { + nni_aio *caio; + if ((caio = nni_list_first(&ep->connaios)) != NULL) { + nni_aio_list_remove(caio); + nni_aio_finish_error(caio, rv); + } + } + + // Bump a bad receive stat (e.g. someone may have sent us + // garbage.) We do not acknowledge or handle garbage frames + // sent to an open session. + nni_pipe_close(p->npipe); + nni_mtx_unlock(&ep->mtx); + return; + } + + // We had a "good" receive (TLS passed at least) from the peer. + + if (nni_aio_count(aio) < sizeof(*hdr)) { + // Runt frame. + p->send_op = OPCODE_DISC; + p->reason = DISC_PROTO; + goto bad; + } + + if (nni_aio_count(aio) > sizeof(*hdr) + p->recv_max) { + p->send_op = OPCODE_DISC; + p->reason = DISC_MSGSIZE; + goto bad; + } + + if (hdr->us_ver != PROTO_VERSION) { + // Bad protocol version + p->send_op = OPCODE_DISC; + p->reason = DISC_PROTO; + goto bad; + } + NNI_GET16LE(&hdr->us_type, proto); + if (proto != p->peer) { + // Bad SP protocol type + p->send_op = OPCODE_DISC; + p->reason = DISC_TYPE; + goto bad; + } + + p->expire = nni_clock() + DTLS_PIPE_TIMEOUT(p); + + if (!p->matched) { + p->matched = true; + nni_list_append(&p->ep->connpipes, p); + dtls_ep_match(p->ep); + } + + switch (hdr->us_op_code) { + case OPCODE_CREQ: + if (p->dialer) { + // dialers don't accept requests + goto bad; + } + NNI_GET16LE(&hdr->us_params[0], rcvmax); + NNI_GET16LE(&hdr->us_params[1], refresh); + if ((refresh > 0) && ((refresh * NNI_SECOND) < p->refresh)) { + p->refresh = refresh * NNI_SECOND; + } + if ((rcvmax > 0) && (rcvmax < NNG_DTLS_RECVMAX)) { + p->send_max = rcvmax; + } + // schedule the CACK reply + p->send_op = OPCODE_CACK; + break; + + case OPCODE_CACK: + if (!p->dialer) { + goto bad; + } + NNI_GET16LE(&hdr->us_params[0], rcvmax); + NNI_GET16LE(&hdr->us_params[0], refresh); + + if ((refresh > 0) && ((refresh * NNI_SECOND) < p->refresh)) { + p->refresh = refresh * NNI_SECOND; + } + if ((rcvmax > 0) && (rcvmax < NNG_DTLS_RECVMAX)) { + p->send_max = rcvmax; + } + break; + + case OPCODE_DISC: + p->closed = true; + nni_mtx_unlock(&ep->mtx); + nni_pipe_close(p->npipe); + return; + + case OPCODE_DATA: + p->recv_rdy = true; + dtls_pipe_recv_tls(p); + nni_mtx_unlock(&ep->mtx); + return; + } +bad: + if (p->send_op != OPCODE_DATA) { + dtls_pipe_send_tls(p); + } + dtls_pipe_recv_tls_start(p); + nni_mtx_unlock(&ep->mtx); +} + +static void +dtls_pipe_close(void *arg) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + nni_aio *aio; + + nni_mtx_lock(&ep->mtx); + while ((aio = nni_list_first(&p->recv_aios)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } + while ((aio = nni_list_first(&p->send_aios)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } + if (!p->matched) { + dtls_remove_pipe(p); + } else { + p->send_op = OPCODE_DISC; + p->reason = DISC_CLOSED; + dtls_pipe_send_tls(p); + } + nni_mtx_unlock(&ep->mtx); +} + +static nng_err dtls_add_pipe(dtls_ep *ep, dtls_pipe *p); + +static void +dtls_pipe_stop(void *arg) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + + dtls_pipe_close(arg); + + nni_mtx_lock(&ep->mtx); + dtls_remove_pipe(p); + nni_list_node_remove(&p->node); + nni_mtx_unlock(&ep->mtx); +} + +static int +dtls_pipe_alloc(dtls_ep *ep, dtls_pipe **pp, const nng_sockaddr *sa) +{ + dtls_pipe *p; + nng_err rv; + + if (ep->dialer) { + rv = nni_pipe_alloc_dialer((void **) &p, ep->ndialer); + } else { + rv = nni_pipe_alloc_listener((void *) &p, ep->nlistener); + } + if (rv != NNG_OK) { + nng_log_err("NNG-DTLS-PIPE-ALLOC-FAIL", + "Failed allocating pipe for DTLS: %s", nng_strerror(rv)); + return (rv); + } + p->dialer = ep->dialer; + p->ep = ep; + p->proto = ep->proto; + p->peer = ep->peer; + p->peer_addr = *sa; + p->id = nng_sockaddr_hash(sa); + p->refresh = ep->refresh; + p->send_max = NNG_DTLS_RECVMAX; + p->recv_max = ep->rcvmax; + *pp = p; + + if (((rv = dtls_add_pipe(ep, p)) != NNG_OK) || + ((rv = nni_tls_init(&p->tls, ep->tlscfg)) != NNG_OK) || + ((rv = nni_tls_start(&p->tls, &dtls_bio_ops, p, sa)) != NNG_OK)) { + nni_pipe_close(p->npipe); + nng_log_err("NNG-DTLS-PIPE-ADD-FAIL", + "Failed adding pipe for DTLS: %s", nng_strerror(rv)); + return (rv); + } + + // We need to start a receiver on the pipe. + dtls_pipe_recv_tls_start(p); + + // Also start TLS up and running. + switch (nni_tls_run(&p->tls)) { + case NNG_OK: + case NNG_EAGAIN: + break; + default: + nni_pipe_close(p->npipe); + break; + } + + // wake the timer so it knows to resubmit + nni_aio_abort(&ep->timeaio, NNG_ETIMEDOUT); + + return (NNG_OK); +} + +static size_t +dtls_pipe_size(void) +{ + return (NNI_ALIGN_UP(sizeof(dtls_pipe)) + + NNI_ALIGN_UP(nni_tls_engine_conn_size())); +} + +static int +dtls_pipe_init(void *arg, nni_pipe *npipe) +{ + dtls_pipe *p = arg; + p->npipe = npipe; + + size_t bufsz = DTLS_MAX_RECORD; // TODO: Make this a tunable. + + if ((p->recv_buf = nni_alloc(bufsz)) == NULL) { + return (NNG_ENOMEM); + } + if ((p->send_buf = nni_alloc(bufsz)) == NULL) { + return (NNG_ENOMEM); + } + p->recv_bufsz = bufsz; + p->send_bufsz = bufsz; + nni_mtx_init(&p->lower_mtx); + nni_aio_init(&p->recv_tls_aio, dtls_pipe_recv_tls_cb, p); + nni_aio_init(&p->send_tls_aio, dtls_pipe_send_tls_cb, p); + nni_aio_list_init(&p->rx_q); + nni_aio_list_init(&p->recv_aios); + nni_aio_list_init(&p->send_aios); + nni_lmq_init(&p->rx_mq, NNG_DTLS_RXQUEUE_LEN); + + return (0); +} + +static void +dtls_pipe_fini(void *arg) +{ + dtls_pipe *p = arg; + nng_msg *m; + + nni_tls_fini(&p->tls); + nni_aio_fini(&p->recv_tls_aio); + nni_aio_fini(&p->send_tls_aio); + if (p->recv_buf != NULL) { + nni_free(p->recv_buf, p->recv_bufsz); + } + if (p->send_buf != NULL) { + nni_free(p->send_buf, p->send_bufsz); + } + nni_mtx_lock(&p->lower_mtx); + while (!nni_lmq_empty(&p->rx_mq)) { + nni_lmq_get(&p->rx_mq, &m); + nni_msg_free(m); + } + nni_mtx_unlock(&p->lower_mtx); + nni_mtx_fini(&p->lower_mtx); + nni_lmq_fini(&p->rx_mq); + NNI_ASSERT(nni_list_empty(&p->recv_aios)); + NNI_ASSERT(nni_list_empty(&p->send_aios)); +} + +static dtls_pipe * +dtls_find_pipe(dtls_ep *ep, const nng_sockaddr *peer_addr) +{ + uint64_t id = nng_sockaddr_hash(peer_addr); + dtls_pipe *p; + + // we'll keep incrementing id until we conclusively match + // or we get a NULL. This is another level of rehashing, but + // it keeps us from having to look up. + for (;;) { + if ((p = nni_id_get(&ep->pipes, id)) == NULL) { + return (NULL); + } + if (nng_sockaddr_equal(&p->peer_addr, peer_addr)) { + return (p); + } + id++; + if (id == 0) { + id = 1; + } + } +} + +static void +dtls_remove_pipe(dtls_pipe *p) +{ + // ep locked + dtls_ep *ep = p->ep; + uint64_t id = p->id; + bool matched = p->matched; + if (id == 0) { + return; + } + p->id = 0; + for (;;) { + dtls_pipe *srch; + if ((srch = nni_id_get(&ep->pipes, id)) == NULL) { + break; + } + if (srch == p) { + nni_id_remove(&ep->pipes, id); + break; + } + id++; + if (id == 0) { + id = 1; + } + } + if (!matched) { + nni_pipe_rele(p->npipe); + } +} + +static nng_err +dtls_add_pipe(dtls_ep *ep, dtls_pipe *p) +{ + // Id must be part of the hash + uint64_t id = p->id; + while (nni_id_get(&ep->pipes, id) != NULL) { + id++; + if (id == 0) { + id = 1; + } + } + return (nni_id_set(&ep->pipes, id, p)); +} + +static void +dtls_start_rx(dtls_ep *ep) +{ + nni_iov iov; + + iov.iov_buf = ep->rx_buf; + iov.iov_len = ep->rx_size; + + nni_aio_reset(&ep->rx_aio); + nni_aio_set_input(&ep->rx_aio, 0, &ep->rx_sa); + nni_aio_set_iov(&ep->rx_aio, 1, &iov); + nng_udp_recv(ep->udp, &ep->rx_aio); +} + +static void +dtls_rx_cb(void *arg) +{ + dtls_ep *ep = arg; + dtls_pipe *p; + nni_aio *aio = &ep->rx_aio; + int rv; + nni_msg *msg; + + nni_mtx_lock(&ep->mtx); + if ((rv = nni_aio_result(aio)) != 0) { + // something bad happened on RX... which is unexpected. + // sleep a little bit and hope for recovery. + switch (nni_aio_result(aio)) { + case NNG_ECLOSED: + case NNG_ECANCELED: + case NNG_ESTOPPED: + nni_mtx_unlock(&ep->mtx); + return; + case NNG_ETIMEDOUT: + case NNG_EAGAIN: + case NNG_EINTR: + default: + goto fail; + } + } + + // If this came from another host, and we are a dialer, we discard. + // Dialers only talk to the party they explicitly dialed. + if (ep->dialer && !nng_sockaddr_equal(&ep->rx_sa, &ep->peer_sa)) { + goto fail; + } + + if ((p = dtls_find_pipe(ep, &ep->rx_sa)) == NULL) { + if (dtls_pipe_alloc(ep, &p, &ep->rx_sa) != NNG_OK) { + goto fail; + } + } + if (p->closed) { + goto fail; + } + NNI_ASSERT(p != NULL); + + if (nni_msg_alloc(&msg, nni_aio_count(aio)) != NNG_OK) { + // TODO BUMP A NO RECV ALLOC STAT + goto fail; + } + memcpy(nni_msg_body(msg), ep->rx_buf, nni_aio_count(aio)); + dtls_start_rx(ep); + nni_mtx_unlock(&ep->mtx); + + nni_mtx_lock(&p->lower_mtx); + + if (nni_lmq_put(&p->rx_mq, msg) != NNG_OK) { + // TODO: BUMP TXQ FULL STAT + nng_msg_free(msg); + } + dtls_bio_recv_done(p); + nni_mtx_unlock(&p->lower_mtx); + + // Run the TLS state machine. + switch (nni_tls_run(&p->tls)) { + case NNG_OK: + case NNG_EAGAIN: + break; + default: + nni_pipe_close(p->npipe); + } + return; + +fail: + // start another receive + dtls_start_rx(ep); + + nni_mtx_unlock(&ep->mtx); +} + +static uint16_t +dtls_pipe_peer(void *arg) +{ + dtls_pipe *p = arg; + + return (p->peer); +} + +static nng_err +dtls_pipe_get_recvmax(void *arg, void *v, size_t *szp, nni_type t) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + nng_err rv; + nni_mtx_lock(&ep->mtx); + rv = nni_copyout_size(p->recv_max, v, szp, t); + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static nng_err +dtls_pipe_get_remaddr(void *arg, void *v, size_t *szp, nni_type t) +{ + dtls_pipe *p = arg; + dtls_ep *ep = p->ep; + nng_err rv; + nni_mtx_lock(&ep->mtx); + rv = nni_copyout_sockaddr(&p->peer_addr, v, szp, t); + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static nni_option dtls_pipe_options[] = { + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_get = dtls_pipe_get_recvmax, + }, + { + .o_name = NNG_OPT_REMADDR, + .o_get = dtls_pipe_get_remaddr, + }, + { + .o_name = NULL, + }, +}; + +static nng_err +dtls_pipe_getopt( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + dtls_pipe *p = arg; + int rv; + + rv = nni_getopt(dtls_pipe_options, name, p, buf, szp, t); + return (rv); +} + +static void +dtls_ep_fini(void *arg) +{ + dtls_ep *ep = arg; + + nni_aio_fini(&ep->timeaio); + nni_aio_fini(&ep->resaio); + nni_aio_fini(&ep->tx_aio); + nni_aio_fini(&ep->rx_aio); + + if (ep->udp != NULL) { + nng_udp_close(ep->udp); + } + if (ep->rx_size != 0) { + nni_free(ep->rx_buf, ep->rx_size); + } + + nni_msg_free(ep->rx_payload); // safe even if msg is null + nni_id_map_fini(&ep->pipes); + nni_mtx_fini(&ep->mtx); +} + +static void +dtls_ep_close(void *arg) +{ + dtls_ep *ep = arg; + nni_aio *aio; + dtls_pipe *p; + uint64_t key; + uint32_t cursor; + + nni_aio_close(&ep->resaio); + nni_aio_close(&ep->rx_aio); + nni_aio_close(&ep->timeaio); + + // leave tx open so we can send disconnects + + nni_mtx_lock(&ep->mtx); + ep->closed = true; + while ((aio = nni_list_first(&ep->connaios)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECONNABORTED); + } + cursor = 0; + key = 0; + while (nni_id_visit(&ep->pipes, &key, (void **) &p, &cursor)) { + nni_pipe_close(p->npipe); + } + nni_mtx_unlock(&ep->mtx); +} + +static void +dtls_ep_stop(void *arg) +{ + dtls_ep *ep = arg; + + nni_aio_stop(&ep->resaio); + nni_aio_stop(&ep->rx_aio); + nni_aio_stop(&ep->timeaio); + + nni_mtx_lock(&ep->mtx); + ep->fini = true; + nni_mtx_unlock(&ep->mtx); +} + +// timer handler - sends out additional creqs as needed, +// reaps stale connections, and handles linger. +static void +dtls_timer_cb(void *arg) +{ + dtls_ep *ep = arg; + dtls_pipe *p; + int rv; + + nni_mtx_lock(&ep->mtx); + rv = nni_aio_result(&ep->timeaio); + switch (rv) { + case NNG_ECLOSED: + case NNG_ECANCELED: + case NNG_ESTOPPED: + nni_mtx_unlock(&ep->mtx); + return; + default: + if (ep->closed) { + nni_mtx_unlock(&ep->mtx); + return; + } + break; + } + + uint32_t cursor = 0; + nni_time now = nni_clock(); + nng_duration refresh = NNG_DURATION_INFINITE; + + while (nni_id_visit(&ep->pipes, NULL, (void **) &p, &cursor)) { + + if (p->closed) { + continue; + } + NNI_ASSERT(p->refresh > 0); + if (p->expire > 0 && now > p->expire) { + char buf[128]; + nng_log_info("NNG-DTLS-INACTIVE", + "Pipe peer %s timed out due to inactivity", + nng_str_sockaddr(&p->peer_addr, buf, sizeof(buf))); + + nni_stat_inc(&ep->st_peer_inactive, 1); + nni_pipe_close(p->npipe); + continue; + } + + if (p->dialer && now > p->next_refresh) { + p->send_op = OPCODE_CREQ; + p->next_refresh = p->expire + p->refresh; + dtls_pipe_send_tls(p); + } + if (refresh == NNG_DURATION_INFINITE && p->refresh > 0) { + refresh = p->refresh; + } else if ((p->refresh > 0) && (p->refresh < refresh)) { + refresh = p->refresh; + } + } + nni_sleep_aio(refresh, &ep->timeaio); + + nni_mtx_unlock(&ep->mtx); +} + +static nng_err +dtls_ep_init( + dtls_ep *ep, nng_url *url, nni_sock *sock, nni_dialer *d, nni_listener *l) +{ + nni_mtx_init(&ep->mtx); + nni_id_map_init(&ep->pipes, 1, 0xFFFFFFFF, true); + NNI_LIST_INIT(&ep->connpipes, dtls_pipe, node); + nni_aio_list_init(&ep->connaios); + + nni_aio_init(&ep->rx_aio, dtls_rx_cb, ep); + nni_aio_init(&ep->timeaio, dtls_timer_cb, ep); + nni_aio_init(&ep->resaio, dtls_resolv_cb, ep); + + if (strcmp(url->u_scheme, "dtls") == 0) { + ep->af = NNG_AF_UNSPEC; + } else if (strcmp(url->u_scheme, "dtls4") == 0) { + ep->af = NNG_AF_INET; + } else if (strcmp(url->u_scheme, "dtls6") == 0) { + ep->af = NNG_AF_INET6; + } else { + return (NNG_EADDRINVAL); + } + + ep->self_sa.s_family = ep->af; + ep->proto = nni_sock_proto_id(sock); + ep->peer = nni_sock_peer_id(sock); + ep->url = url; + ep->refresh = NNG_DTLS_REFRESH; // one minute by default + ep->rcvmax = NNG_DTLS_RECVMAX; + + // receive buffer plus some extra for UDP and TLS headers + if ((ep->rx_buf = nni_alloc(DTLS_MAX_RECORD)) == NULL) { + return (NNG_ENOMEM); + } + ep->rx_size = DTLS_MAX_RECORD; + + NNI_STAT_LOCK(rcv_max_info, "rcv_max", "maximum receive size", + NNG_STAT_LEVEL, NNG_UNIT_BYTES); + NNI_STAT_LOCK(rcv_nomatch_info, "rcv_nomatch", + "messages without a matching connection", NNG_STAT_COUNTER, + NNG_UNIT_MESSAGES); + NNI_STAT_LOCK(rcv_toobig_info, "rcv_toobig", + "received messages rejected because too big", NNG_STAT_COUNTER, + NNG_UNIT_MESSAGES); + NNI_STAT_LOCK(rcv_nobuf_info, "rcv_nobuf", + "received messages dropped no buffer", NNG_STAT_COUNTER, + NNG_UNIT_MESSAGES); + NNI_STAT_LOCK(snd_toobig_info, "snd_toobig", + "sent messages rejected because too big", NNG_STAT_COUNTER, + NNG_UNIT_MESSAGES); + NNI_STAT_LOCK(snd_nobuf_info, "snd_nobuf", + "sent messages dropped no buffer", NNG_STAT_COUNTER, + NNG_UNIT_MESSAGES); + NNI_STAT_LOCK(peer_inactive_info, "peer_inactive", + "connections closed due to inactive peer", NNG_STAT_COUNTER, + NNG_UNIT_EVENTS); + + nni_stat_init_lock(&ep->st_rcv_max, &rcv_max_info, &ep->mtx); + nni_stat_init_lock(&ep->st_rcv_toobig, &rcv_toobig_info, &ep->mtx); + nni_stat_init_lock(&ep->st_rcv_nomatch, &rcv_nomatch_info, &ep->mtx); + nni_stat_init_lock(&ep->st_rcv_nobuf, &rcv_nobuf_info, &ep->mtx); + nni_stat_init_lock(&ep->st_snd_toobig, &snd_toobig_info, &ep->mtx); + nni_stat_init_lock(&ep->st_snd_nobuf, &snd_nobuf_info, &ep->mtx); + nni_stat_init_lock( + &ep->st_peer_inactive, &peer_inactive_info, &ep->mtx); + + if (l) { + NNI_ASSERT(d == NULL); + nni_listener_add_stat(l, &ep->st_rcv_max); + + nni_listener_add_stat(l, &ep->st_rcv_toobig); + nni_listener_add_stat(l, &ep->st_rcv_nomatch); + nni_listener_add_stat(l, &ep->st_rcv_nobuf); + nni_listener_add_stat(l, &ep->st_snd_toobig); + nni_listener_add_stat(l, &ep->st_snd_nobuf); + } + if (d) { + NNI_ASSERT(l == NULL); + nni_dialer_add_stat(d, &ep->st_rcv_max); + nni_dialer_add_stat(d, &ep->st_rcv_toobig); + nni_dialer_add_stat(d, &ep->st_rcv_nomatch); + nni_dialer_add_stat(d, &ep->st_rcv_nobuf); + nni_dialer_add_stat(d, &ep->st_snd_toobig); + nni_dialer_add_stat(d, &ep->st_snd_nobuf); + } + + // schedule our timer callback - forever for now + // adjusted automatically as we add pipes or other + // actions which require earlier wakeup. + nni_sleep_aio(NNG_DURATION_INFINITE, &ep->timeaio); + // nni_sleep_aio(100, &ep->timeaio); + + return (NNG_OK); +} + +static nng_err +dtls_check_url(nng_url *url, bool listen) +{ + // Check for invalid URL components. + if ((strlen(url->u_path) != 0) && (strcmp(url->u_path, "/") != 0)) { + return (NNG_EADDRINVAL); + } + if ((url->u_fragment != NULL) || (url->u_userinfo != NULL) || + (url->u_query != NULL)) { + return (NNG_EADDRINVAL); + } + if (!listen) { + if ((strlen(url->u_hostname) == 0) || (url->u_port == 0)) { + return (NNG_EADDRINVAL); + } + } + return (NNG_OK); +} + +static nng_err +dtls_dialer_init(void *arg, nng_url *url, nni_dialer *ndialer) +{ + dtls_ep *ep = arg; + nng_err rv; + nni_sock *sock = nni_dialer_sock(ndialer); + + if ((rv = dtls_check_url(url, false)) != NNG_OK) { + return (rv); + } + + ep->ndialer = ndialer; + if ((rv = dtls_ep_init(ep, url, sock, ndialer, NULL)) != NNG_OK) { + return (rv); + } + + return (NNG_OK); +} + +static nng_err +dtls_listener_init(void *arg, nng_url *url, nni_listener *nlistener) +{ + dtls_ep *ep = arg; + nng_err rv; + nni_sock *sock = nni_listener_sock(nlistener); + + ep->nlistener = nlistener; + if ((rv = dtls_ep_init(ep, url, sock, NULL, nlistener)) != NNG_OK) { + return (rv); + } + // Check for invalid URL components. + if (((rv = dtls_check_url(url, true)) != NNG_OK) || + ((rv = nni_url_to_address(&ep->self_sa, url)) != NNG_OK)) { + return (rv); + } + + return (NNG_OK); +} + +static void +dtls_ep_cancel(nni_aio *aio, void *arg, nng_err rv) +{ + dtls_ep *ep = arg; + nni_mtx_lock(&ep->mtx); + if (nni_aio_list_active(aio)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + nni_aio_abort(&ep->resaio, rv); + } + nni_mtx_unlock(&ep->mtx); +} + +static void +dtls_resolv_cb(void *arg) +{ + dtls_ep *ep = arg; + dtls_pipe *p; + nni_aio *aio; + int rv; + + nni_mtx_lock(&ep->mtx); + if ((aio = nni_list_first(&ep->connaios)) == NULL) { + nni_mtx_unlock(&ep->mtx); + return; + } + if (ep->closed) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + nni_mtx_unlock(&ep->mtx); + return; + } + if ((rv = nni_aio_result(&ep->resaio)) != 0) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + nni_mtx_unlock(&ep->mtx); + nng_log_warn("NNG-RESOLV", "Failed resolving IP address: %s", + nng_strerror(rv)); + return; + } + + // Choose the right port to bind to. The family must match. + if (ep->self_sa.s_family == NNG_AF_UNSPEC) { + ep->self_sa.s_family = ep->peer_sa.s_family; + } + + if (ep->udp == NULL) { + if ((rv = nng_udp_open(&ep->udp, &ep->self_sa)) != NNG_OK) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + nni_mtx_unlock(&ep->mtx); + return; + } + } + + if ((rv = dtls_pipe_alloc(ep, &p, &ep->peer_sa)) != NNG_OK) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + nni_mtx_unlock(&ep->mtx); + return; + } + dtls_ep_start(ep); + + // Send out the connection request. We don't complete + // the user aio until we confirm a connection, so that + // we can supply details like maximum receive message size + // and the protocol the peer is using. + p->send_op = OPCODE_CREQ; + dtls_pipe_send_tls(p); + nni_mtx_unlock(&ep->mtx); +} + +static void +dtls_ep_connect(void *arg, nni_aio *aio) +{ + dtls_ep *ep = arg; + + nni_mtx_lock(&ep->mtx); + if (!nni_aio_start(aio, dtls_ep_cancel, ep)) { + nni_mtx_unlock(&ep->mtx); + return; + } + if (ep->closed) { + nni_aio_finish_error(aio, NNG_ECLOSED); + nni_mtx_unlock(&ep->mtx); + return; + } + if (ep->started) { + nni_aio_finish_error(aio, NNG_EBUSY); + nni_mtx_unlock(&ep->mtx); + return; + } + NNI_ASSERT(nni_list_empty(&ep->connaios)); + ep->dialer = true; + + nni_list_append(&ep->connaios, aio); + + // lookup the IP address + + memset(&ep->resolv, 0, sizeof(ep->resolv)); + ep->resolv.ri_family = ep->af; + ep->resolv.ri_host = ep->url->u_hostname; + ep->resolv.ri_port = ep->url->u_port; + ep->resolv.ri_passive = false; + ep->resolv.ri_sa = &ep->peer_sa; + nni_aio_set_timeout(&ep->resaio, NNI_SECOND * 5); + nni_resolv(&ep->resolv, &ep->resaio); + + // wake up for retries + nni_aio_abort(&ep->timeaio, NNG_EINTR); + + nni_mtx_unlock(&ep->mtx); +} + +static nng_err +dtls_ep_get_port(void *arg, void *buf, size_t *szp, nni_type t) +{ + dtls_ep *ep = arg; + nng_sockaddr sa; + int port; + uint8_t *paddr; + + nni_mtx_lock(&ep->mtx); + if (ep->udp != NULL) { + (void) nng_udp_sockname(ep->udp, &sa); + } else { + sa = ep->self_sa; + } + switch (sa.s_family) { + case NNG_AF_INET: + paddr = (void *) &sa.s_in.sa_port; + break; + + case NNG_AF_INET6: + paddr = (void *) &sa.s_in6.sa_port; + break; + + default: + paddr = NULL; + break; + } + nni_mtx_unlock(&ep->mtx); + + if (paddr == NULL) { + return (NNG_ESTATE); + } + + NNI_GET16(paddr, port); + return (nni_copyout_int(port, buf, szp, t)); +} + +static nng_err +dtls_ep_get_locaddr(void *arg, void *v, size_t *szp, nni_opt_type t) +{ + dtls_ep *ep = arg; + nng_sockaddr sa; + + nni_mtx_lock(&ep->mtx); + if (ep->udp != NULL) { + (void) nng_udp_sockname(ep->udp, &sa); + } else { + sa = ep->self_sa; + } + nni_mtx_unlock(&ep->mtx); + + return (nni_copyout_sockaddr(&sa, v, szp, t)); +} + +static nng_err +dtls_ep_get_remaddr(void *arg, void *v, size_t *szp, nni_opt_type t) +{ + dtls_ep *ep = arg; + nng_err rv; + + if (!ep->dialer) { + return (NNG_ENOTSUP); + } + nni_mtx_lock(&ep->mtx); + rv = nni_copyout_sockaddr(&ep->peer_sa, v, szp, t); + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static nng_err +dtls_ep_get_recvmaxsz(void *arg, void *v, size_t *szp, nni_opt_type t) +{ + dtls_ep *ep = arg; + nng_err rv; + + nni_mtx_lock(&ep->mtx); + rv = nni_copyout_size(ep->rcvmax, v, szp, t); + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static nng_err +dtls_ep_set_recvmaxsz(void *arg, const void *v, size_t sz, nni_opt_type t) +{ + dtls_ep *ep = arg; + size_t val; + nng_err rv; + if ((rv = nni_copyin_size(&val, v, sz, 0, NNG_DTLS_RECVMAX, t)) == 0) { + if ((val == 0) || (val > NNG_DTLS_RECVMAX)) { + val = NNG_DTLS_RECVMAX; + } + nni_mtx_lock(&ep->mtx); + if (ep->started) { + nni_mtx_unlock(&ep->mtx); + return (NNG_EBUSY); + } + ep->rcvmax = (uint16_t) val; + nni_stat_set_value(&ep->st_rcv_max, val); + nni_mtx_unlock(&ep->mtx); + } + return (rv); +} + +static nng_err +dtls_ep_set_tls(void *arg, nng_tls_config *cfg) +{ + dtls_ep *ep = arg; + nni_mtx_lock(&ep->mtx); + if (ep->started) { + nni_mtx_unlock(&ep->mtx); + return (NNG_EBUSY); + } + ep->tlscfg = cfg; + nni_mtx_unlock(&ep->mtx); + return (NNG_OK); +} + +static nng_err +dtls_ep_get_tls(void *arg, nng_tls_config **cfgp) +{ + dtls_ep *ep = arg; + nni_mtx_lock(&ep->mtx); + *cfgp = ep->tlscfg; + nni_mtx_unlock(&ep->mtx); + return (NNG_OK); +} + +// this just looks for pipes waiting for an aio, and aios waiting for +// a connection, and matches them together. +static void +dtls_ep_match(dtls_ep *ep) +{ + nng_aio *aio = nni_list_first(&ep->connaios); + dtls_pipe *p = nni_list_first(&ep->connpipes); + + if ((aio == NULL) || (p == NULL)) { + return; + } + + nni_aio_list_remove(aio); + nni_list_remove(&ep->connpipes, p); + nni_aio_set_output(aio, 0, p->npipe); + nni_aio_finish(aio, 0, 0); +} + +static void +dtls_ep_start(dtls_ep *ep) +{ + ep->started = true; + dtls_start_rx(ep); +} + +static nng_err +dtls_ep_bind(void *arg, nng_url *url) +{ + dtls_ep *ep = arg; + nng_err rv; + + nni_mtx_lock(&ep->mtx); + if (ep->started) { + nni_mtx_unlock(&ep->mtx); + return (NNG_EBUSY); + } + + rv = nng_udp_open(&ep->udp, &ep->self_sa); + if (rv != NNG_OK) { + nni_mtx_unlock(&ep->mtx); + return (rv); + } + nng_sockaddr sa; + nng_udp_sockname(ep->udp, &sa); + url->u_port = nng_sockaddr_port(&sa); + dtls_ep_start(ep); + nni_mtx_unlock(&ep->mtx); + + return (rv); +} + +static void +dtls_ep_accept(void *arg, nni_aio *aio) +{ + dtls_ep *ep = arg; + + nni_aio_reset(aio); + nni_mtx_lock(&ep->mtx); + if (ep->closed) { + nni_aio_finish_error(aio, NNG_ECLOSED); + nni_mtx_unlock(&ep->mtx); + return; + } + if (!nni_aio_start(aio, dtls_ep_cancel, ep)) { + nni_mtx_unlock(&ep->mtx); + return; + } + nni_aio_list_append(&ep->connaios, aio); + dtls_ep_match(ep); + nni_mtx_unlock(&ep->mtx); +} + +static nni_sp_pipe_ops dtls_pipe_ops = { + .p_size = dtls_pipe_size, + .p_init = dtls_pipe_init, + .p_fini = dtls_pipe_fini, + .p_stop = dtls_pipe_stop, + .p_send = dtls_pipe_send, + .p_recv = dtls_pipe_recv, + .p_close = dtls_pipe_close, + .p_peer = dtls_pipe_peer, + .p_getopt = dtls_pipe_getopt, +}; + +static const nni_option dtls_ep_opts[] = { + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_get = dtls_ep_get_recvmaxsz, + .o_set = dtls_ep_set_recvmaxsz, + }, + { + .o_name = NNG_OPT_LOCADDR, + .o_get = dtls_ep_get_locaddr, + }, + { + .o_name = NNG_OPT_REMADDR, + .o_get = dtls_ep_get_remaddr, + }, + { + .o_name = NNG_OPT_TCP_BOUND_PORT, + .o_get = dtls_ep_get_port, + }, + // terminate list + { + .o_name = NULL, + }, +}; + +static nng_err +dtls_dialer_getopt( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + dtls_ep *ep = arg; + + return (nni_getopt(dtls_ep_opts, name, ep, buf, szp, t)); +} + +static nng_err +dtls_dialer_setopt( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + dtls_ep *ep = arg; + + return (nni_setopt(dtls_ep_opts, name, ep, buf, sz, t)); +} + +static nng_err +dtls_listener_getopt( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + dtls_ep *ep = arg; + + return (nni_getopt(dtls_ep_opts, name, ep, buf, szp, t)); +} + +static nng_err +dtls_listener_setopt( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + dtls_ep *ep = arg; + + return (nni_setopt(dtls_ep_opts, name, ep, buf, sz, t)); +} + +static nni_sp_dialer_ops dtls_dialer_ops = { + .d_size = sizeof(dtls_ep), + .d_init = dtls_dialer_init, + .d_fini = dtls_ep_fini, + .d_connect = dtls_ep_connect, + .d_close = dtls_ep_close, + .d_stop = dtls_ep_stop, + .d_set_tls = dtls_ep_set_tls, + .d_get_tls = dtls_ep_get_tls, + .d_getopt = dtls_dialer_getopt, + .d_setopt = dtls_dialer_setopt, +}; + +static nni_sp_listener_ops dtls_listener_ops = { + .l_size = sizeof(dtls_ep), + .l_init = dtls_listener_init, + .l_fini = dtls_ep_fini, + .l_bind = dtls_ep_bind, + .l_accept = dtls_ep_accept, + .l_close = dtls_ep_close, + .l_stop = dtls_ep_stop, + .l_set_tls = dtls_ep_set_tls, + .l_get_tls = dtls_ep_get_tls, + .l_getopt = dtls_listener_getopt, + .l_setopt = dtls_listener_setopt, +}; + +static nni_sp_tran dtls_tran = { + .tran_scheme = "dtls", + .tran_dialer = &dtls_dialer_ops, + .tran_listener = &dtls_listener_ops, + .tran_pipe = &dtls_pipe_ops, + .tran_init = dtls_tran_init, + .tran_fini = dtls_tran_fini, +}; + +static nni_sp_tran dtls4_tran = { + .tran_scheme = "dtls4", + .tran_dialer = &dtls_dialer_ops, + .tran_listener = &dtls_listener_ops, + .tran_pipe = &dtls_pipe_ops, + .tran_init = dtls_tran_init, + .tran_fini = dtls_tran_fini, +}; + +#ifdef NNG_ENABLE_IPV6 +static nni_sp_tran dtls6_tran = { + .tran_scheme = "dtls6", + .tran_dialer = &dtls_dialer_ops, + .tran_listener = &dtls_listener_ops, + .tran_pipe = &dtls_pipe_ops, + .tran_init = dtls_tran_init, + .tran_fini = dtls_tran_fini, +}; +#endif + +void +nni_sp_dtls_register(void) +{ + nni_sp_tran_register(&dtls_tran); + nni_sp_tran_register(&dtls4_tran); +#ifdef NNG_ENABLE_IPV6 + nni_sp_tran_register(&dtls6_tran); +#endif +} diff --git a/src/sp/transport/dtls/dtls_tran_test.c b/src/sp/transport/dtls/dtls_tran_test.c new file mode 100644 index 00000000..1dcfeb49 --- /dev/null +++ b/src/sp/transport/dtls/dtls_tran_test.c @@ -0,0 +1,345 @@ +// +// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2018 Cody Piersall <cody.piersall@gmail.com> +// +// 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 "nng/nng.h" +#include <nuts.h> + +// TLS tests. + +static nng_tls_config * +tls_server_config(void) +{ + nng_tls_config *c; + NUTS_PASS(nng_tls_config_alloc(&c, NNG_TLS_MODE_SERVER)); + NUTS_PASS(nng_tls_config_own_cert( + c, nuts_server_crt, nuts_server_key, NULL)); + return (c); +} + +static nng_tls_config * +tls_server_config_ecdsa(void) +{ + nng_tls_config *c; + NUTS_PASS(nng_tls_config_alloc(&c, NNG_TLS_MODE_SERVER)); + NUTS_PASS(nng_tls_config_own_cert( + c, nuts_ecdsa_server_crt, nuts_ecdsa_server_key, NULL)); + return (c); +} + +static nng_tls_config * +tls_config_psk(nng_tls_mode mode, const char *name, uint8_t *key, size_t len) +{ + nng_tls_config *c; + NUTS_PASS(nng_tls_config_alloc(&c, mode)); + NUTS_PASS(nng_tls_config_psk(c, name, key, len)); + return (c); +} + +static nng_tls_config * +tls_client_config(void) +{ + nng_tls_config *c; + NUTS_PASS(nng_tls_config_alloc(&c, NNG_TLS_MODE_CLIENT)); + NUTS_PASS(nng_tls_config_own_cert( + c, nuts_client_crt, nuts_client_key, NULL)); + NUTS_PASS(nng_tls_config_ca_chain(c, nuts_server_crt, NULL)); + NUTS_PASS(nng_tls_config_server_name(c, "localhost")); + return (c); +} + +static nng_tls_config * +tls_client_config_ecdsa(void) +{ + nng_tls_config *c; + NUTS_PASS(nng_tls_config_alloc(&c, NNG_TLS_MODE_CLIENT)); + NUTS_PASS(nng_tls_config_own_cert( + c, nuts_ecdsa_client_crt, nuts_ecdsa_client_key, NULL)); + NUTS_PASS(nng_tls_config_ca_chain(c, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_tls_config_server_name(c, "localhost")); + return (c); +} + +void +test_dtls_port_zero_bind(void) +{ + nng_socket s1; + nng_socket s2; + nng_tls_config *c1, *c2; + nng_sockaddr sa; + nng_listener l; + nng_dialer d; + const nng_url *url; + + NUTS_ENABLE_LOG(NNG_LOG_DEBUG); + c1 = tls_server_config(); + c2 = tls_client_config(); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_listener_create(&l, s1, "dtls://127.0.0.1:0")); + NUTS_PASS(nng_listener_set_tls(l, c1)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + NUTS_MATCH(nng_url_scheme(url), "dtls"); + NUTS_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET); + NUTS_TRUE(sa.s_in.sa_port != 0); + NUTS_TRUE(sa.s_in.sa_addr = nuts_be32(0x7f000001)); + NUTS_PASS(nng_dialer_create_url(&d, s2, url)); + NUTS_PASS(nng_dialer_set_tls(d, c2)); + // NUTS_PASS(nng_dialer_start(d, NNG_FLAG_NONBLOCK)); + NUTS_PASS(nng_dialer_start(d, 0)); + nng_msleep(1000); + NUTS_CLOSE(s2); + NUTS_CLOSE(s1); + nng_tls_config_free(c1); + nng_tls_config_free(c2); +} + +void +test_dtls_bad_cert_mutual(void) +{ + nng_socket s1; + nng_socket s2; + nng_tls_config *c1, *c2; + nng_sockaddr sa; + nng_listener l; + nng_dialer d; + const nng_url *url; + + c1 = tls_server_config(); + c2 = tls_client_config(); + + NUTS_ENABLE_LOG(NNG_LOG_DEBUG); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_tls_config_auth_mode(c1, NNG_TLS_AUTH_MODE_REQUIRED)); + // a valid cert, but not the one that signed the config! + NUTS_PASS(nng_tls_config_ca_chain(c1, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_listener_create(&l, s1, "dtls://127.0.0.1:0")); + NUTS_PASS(nng_listener_set_tls(l, c1)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + NUTS_MATCH(nng_url_scheme(url), "dtls"); + NUTS_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET); + NUTS_TRUE(sa.s_in.sa_port != 0); + NUTS_TRUE(sa.s_in.sa_addr = nuts_be32(0x7f000001)); + NUTS_PASS(nng_dialer_create_url(&d, s2, url)); + NUTS_PASS(nng_dialer_set_tls(d, c2)); + // With DTLS we are not guaranteed to get the connection failure. + nng_dialer_start(d, NNG_FLAG_NONBLOCK); + nng_msleep(500); + NUTS_CLOSE(s2); + NUTS_CLOSE(s1); + nng_tls_config_free(c1); + nng_tls_config_free(c2); +} + +void +test_dtls_cert_mutual(void) +{ + nng_socket s1; + nng_socket s2; + nng_tls_config *c1, *c2; + nng_sockaddr sa; + nng_listener l; + nng_dialer d; + const nng_url *url; + + c1 = tls_server_config_ecdsa(); + c2 = tls_client_config_ecdsa(); + + NUTS_ENABLE_LOG(NNG_LOG_DEBUG); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_tls_config_auth_mode(c1, NNG_TLS_AUTH_MODE_REQUIRED)); + NUTS_PASS(nng_tls_config_ca_chain(c1, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_tls_config_ca_chain(c2, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_listener_create(&l, s1, "dtls://127.0.0.1:0")); + NUTS_PASS(nng_listener_set_tls(l, c1)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + NUTS_MATCH(nng_url_scheme(url), "dtls"); + NUTS_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET); + NUTS_TRUE(sa.s_in.sa_port != 0); + NUTS_TRUE(sa.s_in.sa_addr = nuts_be32(0x7f000001)); + NUTS_PASS(nng_dialer_create_url(&d, s2, url)); + NUTS_PASS(nng_dialer_set_tls(d, c2)); + NUTS_PASS(nng_dialer_start(d, 0)); + nng_msleep(50); + NUTS_CLOSE(s2); + NUTS_CLOSE(s1); + nng_tls_config_free(c1); + nng_tls_config_free(c2); +} + +void +test_dtls_malformed_address(void) +{ + nng_socket s1; + + NUTS_OPEN(s1); + NUTS_FAIL(nng_dial(s1, "dtls://127.0.0.1", NULL, 0), NNG_EADDRINVAL); + NUTS_FAIL( + nng_dial(s1, "dtls://127.0.0.1.32", NULL, 0), NNG_EADDRINVAL); + NUTS_FAIL( + nng_dial(s1, "dtls://127.0.x.1.32", NULL, 0), NNG_EADDRINVAL); + NUTS_FAIL( + nng_listen(s1, "dtls://127.0.0.1.32", NULL, 0), NNG_EADDRINVAL); + NUTS_FAIL( + nng_listen(s1, "dtls://127.0.x.1.32", NULL, 0), NNG_EADDRINVAL); + NUTS_CLOSE(s1); +} + +// DTLS does not support TCP_NODELAY because it's based on UDP. +void +test_dtls_no_delay_option(void) +{ + nng_socket s; + nng_dialer d; + nng_listener l; + bool v; + char *addr; + nng_tls_config *dc, *lc; + + NUTS_ADDR(addr, "dtls"); + dc = tls_client_config(); + lc = tls_server_config(); + + NUTS_OPEN(s); + NUTS_PASS(nng_dialer_create(&d, s, addr)); + NUTS_PASS(nng_dialer_set_tls(d, dc)); + NUTS_FAIL( + nng_dialer_get_bool(d, NNG_OPT_TCP_NODELAY, &v), NNG_ENOTSUP); + NUTS_FAIL(nng_dialer_set_bool(d, NNG_OPT_TCP_NODELAY, v), NNG_ENOTSUP); + + NUTS_PASS(nng_listener_create(&l, s, addr)); + NUTS_PASS(nng_listener_set_tls(l, lc)); + NUTS_FAIL( + nng_listener_get_bool(l, NNG_OPT_TCP_NODELAY, &v), NNG_ENOTSUP); + NUTS_FAIL( + nng_listener_set_bool(l, NNG_OPT_TCP_NODELAY, v), NNG_ENOTSUP); + + NUTS_PASS(nng_dialer_close(d)); + NUTS_PASS(nng_listener_close(l)); + + NUTS_CLOSE(s); + nng_tls_config_free(lc); + nng_tls_config_free(dc); +} + +void +test_dtls_recv_max(void) +{ + char msg[256]; + char buf[256]; + nng_socket s0; + nng_socket s1; + nng_tls_config *c0, *c1; + nng_listener l; + nng_dialer d; + size_t sz; + char *addr; + const nng_url *url; + + NUTS_ADDR_ZERO(addr, "dtls"); + + c0 = tls_server_config(); + c1 = tls_client_config(); + NUTS_OPEN(s0); + NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_size(s0, NNG_OPT_RECVMAXSZ, 200)); + NUTS_PASS(nng_listener_create(&l, s0, addr)); + NUTS_PASS(nng_listener_set_tls(l, c0)); + NUTS_PASS(nng_socket_get_size(s0, NNG_OPT_RECVMAXSZ, &sz)); + NUTS_TRUE(sz == 200); + NUTS_PASS(nng_listener_set_size(l, NNG_OPT_RECVMAXSZ, 100)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + + NUTS_OPEN(s1); + NUTS_PASS(nng_dialer_create_url(&d, s1, url)); + NUTS_PASS(nng_dialer_set_tls(d, c1)); + NUTS_PASS(nng_dialer_start(d, 0)); + NUTS_PASS(nng_send(s1, msg, 95, 0)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_recv(s0, buf, &sz, 0)); + NUTS_TRUE(sz == 95); + NUTS_PASS(nng_send(s1, msg, 150, 0)); + NUTS_FAIL(nng_recv(s0, buf, &sz, 0), NNG_ETIMEDOUT); + NUTS_CLOSE(s0); + NUTS_CLOSE(s1); + nng_tls_config_free(c0); + nng_tls_config_free(c1); +} + +void +test_dtls_psk(void) +{ +#ifdef NNG_SUPP_TLS_PSK + char msg[256]; + char buf[256]; + nng_socket s0; + nng_socket s1; + nng_tls_config *c0, *c1; + nng_listener l; + nng_dialer d; + size_t sz; + char *addr; + uint8_t key[32]; + const nng_url *url; + + for (unsigned i = 0; i < sizeof(key); i++) { + key[i] = rand() % 0xff; + } + + NUTS_ADDR_ZERO(addr, "dtls"); + NUTS_ENABLE_LOG(NNG_LOG_DEBUG); + + c0 = tls_config_psk(NNG_TLS_MODE_SERVER, "identity", key, sizeof key); + c1 = tls_config_psk(NNG_TLS_MODE_CLIENT, "identity", key, sizeof key); + NUTS_OPEN(s0); + NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_listener_create(&l, s0, addr)); + NUTS_PASS(nng_listener_set_tls(l, c0)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + + NUTS_OPEN(s1); + NUTS_PASS(nng_dialer_create_url(&d, s1, url)); + NUTS_PASS(nng_dialer_set_tls(d, c1)); + NUTS_PASS(nng_dialer_start(d, 0)); + NUTS_SLEEP(1000); // make sure connection has time to form! + NUTS_PASS(nng_send(s1, msg, 95, 0)); + NUTS_PASS(nng_recv(s0, buf, &sz, 0)); + NUTS_TRUE(sz == 95); + NUTS_CLOSE(s0); + NUTS_CLOSE(s1); + nng_tls_config_free(c0); + nng_tls_config_free(c1); +#else + NUTS_SKIP("no PSK support"); +#endif +} + +NUTS_TESTS = { + + { "dtls port zero bind", test_dtls_port_zero_bind }, + { "dtls malformed address", test_dtls_malformed_address }, + { "dtls no delay option", test_dtls_no_delay_option }, + { "dtls recv max", test_dtls_recv_max }, + { "dtls pre-shared key", test_dtls_psk }, + { "dtls bad cert mutual", test_dtls_bad_cert_mutual }, + { "dtls cert mutual", test_dtls_cert_mutual }, + { NULL, NULL }, +}; diff --git a/src/sp/transport/inproc/CMakeLists.txt b/src/sp/transport/inproc/CMakeLists.txt index 2132e8d7..42f4d824 100644 --- a/src/sp/transport/inproc/CMakeLists.txt +++ b/src/sp/transport/inproc/CMakeLists.txt @@ -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 @@ -11,6 +11,8 @@ # inproc protocol nng_directory(inproc) -nng_sources_if(NNG_TRANSPORT_INPROC inproc.c) -nng_defines_if(NNG_TRANSPORT_INPROC NNG_TRANSPORT_INPROC) -nng_test_if(NNG_TRANSPORT_INPROC inproc_test) +if (NNG_TRANSPORT_INPROC) + nng_sources(inproc.c) + nng_defines(NNG_TRANSPORT_INPROC) + nng_test(inproc_test) +endif() diff --git a/src/sp/transport/inproc/inproc.c b/src/sp/transport/inproc/inproc.c index 24c0f1ad..22fe619b 100644 --- a/src/sp/transport/inproc/inproc.c +++ b/src/sp/transport/inproc/inproc.c @@ -584,8 +584,14 @@ inproc_pipe_getopt( return (nni_getopt(inproc_pipe_options, name, arg, v, szp, t)); } +static size_t +inproc_pipe_size(void) +{ + return (sizeof(inproc_pipe)); +} + static nni_sp_pipe_ops inproc_pipe_ops = { - .p_size = sizeof(inproc_pipe), + .p_size = inproc_pipe_size, .p_init = inproc_pipe_init, .p_fini = inproc_pipe_fini, .p_send = inproc_pipe_send, diff --git a/src/sp/transport/ipc/CMakeLists.txt b/src/sp/transport/ipc/CMakeLists.txt index 7353c4f3..8cd78941 100644 --- a/src/sp/transport/ipc/CMakeLists.txt +++ b/src/sp/transport/ipc/CMakeLists.txt @@ -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 @@ -11,6 +11,8 @@ # ipc protocol nng_directory(ipc) -nng_sources_if(NNG_TRANSPORT_IPC ipc.c) -nng_defines_if(NNG_TRANSPORT_IPC NNG_TRANSPORT_IPC) -nng_test_if(NNG_TRANSPORT_IPC ipc_test) +if (NNG_TRANSPORT_IPC) + nng_sources(ipc.c) + nng_defines(NNG_TRANSPORT_IPC) + nng_test(ipc_test) +endif() diff --git a/src/sp/transport/ipc/ipc.c b/src/sp/transport/ipc/ipc.c index 67038e2e..6bf4445b 100644 --- a/src/sp/transport/ipc/ipc.c +++ b/src/sp/transport/ipc/ipc.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> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -956,8 +956,14 @@ ipc_pipe_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t) return (nni_stream_get(p->conn, name, buf, szp, t)); } +static size_t +ipc_pipe_size(void) +{ + return (sizeof(ipc_pipe)); +} + static nni_sp_pipe_ops ipc_tran_pipe_ops = { - .p_size = sizeof(ipc_pipe), + .p_size = ipc_pipe_size, .p_init = ipc_pipe_init, .p_fini = ipc_pipe_fini, .p_stop = ipc_pipe_stop, diff --git a/src/sp/transport/socket/CMakeLists.txt b/src/sp/transport/socket/CMakeLists.txt index d79b261e..4c7e8b58 100644 --- a/src/sp/transport/socket/CMakeLists.txt +++ b/src/sp/transport/socket/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2023 Staysail Systems, Inc. <info@staysail.tech> +# Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> # # This software is supplied under the terms of the MIT License, a # copy of which should be located in the distribution where this @@ -10,6 +10,8 @@ # File Descriptor (or Handle) based connections nng_directory(socket) -nng_sources_if(NNG_TRANSPORT_FDC sockfd.c) -nng_defines_if(NNG_TRANSPORT_FDC NNG_TRANSPORT_FDC) -nng_test(sockfd_test)
\ No newline at end of file +if (NNG_TRANSPORT_FDC) + nng_sources(sockfd.c) + nng_defines(NNG_TRANSPORT_FDC) + nng_test(sockfd_test) +endif() diff --git a/src/sp/transport/socket/sockfd.c b/src/sp/transport/socket/sockfd.c index 37debc85..57693088 100644 --- a/src/sp/transport/socket/sockfd.c +++ b/src/sp/transport/socket/sockfd.c @@ -807,8 +807,14 @@ sfd_tran_ep_accept(void *arg, nni_aio *aio) nni_mtx_unlock(&ep->mtx); } +static size_t +sfd_tran_pipe_size(void) +{ + return (sizeof(sfd_tran_pipe)); +} + static nni_sp_pipe_ops sfd_tran_pipe_ops = { - .p_size = sizeof(sfd_tran_pipe), + .p_size = sfd_tran_pipe_size, .p_init = sfd_tran_pipe_init, .p_fini = sfd_tran_pipe_fini, .p_stop = sfd_tran_pipe_stop, diff --git a/src/sp/transport/tcp/CMakeLists.txt b/src/sp/transport/tcp/CMakeLists.txt index fea821c2..e611a502 100644 --- a/src/sp/transport/tcp/CMakeLists.txt +++ b/src/sp/transport/tcp/CMakeLists.txt @@ -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 @@ -11,6 +11,8 @@ # TCP protocol nng_directory(tcp) -nng_sources_if(NNG_TRANSPORT_TCP tcp.c) -nng_defines_if(NNG_TRANSPORT_TCP NNG_TRANSPORT_TCP) -nng_test(tcp_test) +if (NNG_TRANSPORT_TCP) + nng_sources(tcp.c) + nng_defines(NNG_TRANSPORT_TCP) + nng_test(tcp_test) +endif() diff --git a/src/sp/transport/tcp/tcp.c b/src/sp/transport/tcp/tcp.c index d77e7b2f..096d2e24 100644 --- a/src/sp/transport/tcp/tcp.c +++ b/src/sp/transport/tcp/tcp.c @@ -964,8 +964,14 @@ tcptran_ep_accept(void *arg, nni_aio *aio) nni_mtx_unlock(&ep->mtx); } +static size_t +tcptran_pipe_size(void) +{ + return (sizeof(tcptran_pipe)); +} + static nni_sp_pipe_ops tcptran_pipe_ops = { - .p_size = sizeof(tcptran_pipe), + .p_size = tcptran_pipe_size, .p_init = tcptran_pipe_init, .p_fini = tcptran_pipe_fini, .p_stop = tcptran_pipe_stop, diff --git a/src/sp/transport/tls/CMakeLists.txt b/src/sp/transport/tls/CMakeLists.txt index f55340a9..0ba9a235 100644 --- a/src/sp/transport/tls/CMakeLists.txt +++ b/src/sp/transport/tls/CMakeLists.txt @@ -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 @@ -11,6 +11,8 @@ # TLS transport nng_directory(tls) -nng_sources_if(NNG_TRANSPORT_TLS tls.c) -nng_defines_if(NNG_TRANSPORT_TLS NNG_TRANSPORT_TLS) -nng_test_if(NNG_ENABLE_TLS tls_tran_test) +if (NNG_TRANSPORT_TLS) + nng_sources(tls.c) + nng_defines(NNG_TRANSPORT_TLS) + nng_test(tls_tran_test) +endif() diff --git a/src/sp/transport/tls/tls.c b/src/sp/transport/tls/tls.c index 5c567692..fd983c67 100644 --- a/src/sp/transport/tls/tls.c +++ b/src/sp/transport/tls/tls.c @@ -952,8 +952,14 @@ tlstran_pipe_getopt( return (rv); } +static size_t +tlstran_pipe_size(void) +{ + return (sizeof(tlstran_pipe)); // TODO add engine data size +} + static nni_sp_pipe_ops tlstran_pipe_ops = { - .p_size = sizeof(tlstran_pipe), + .p_size = tlstran_pipe_size, .p_init = tlstran_pipe_init, .p_fini = tlstran_pipe_fini, .p_stop = tlstran_pipe_stop, diff --git a/src/sp/transport/tls/tls_tran_test.c b/src/sp/transport/tls/tls_tran_test.c index 5b38d733..3c43b36e 100644 --- a/src/sp/transport/tls/tls_tran_test.c +++ b/src/sp/transport/tls/tls_tran_test.c @@ -35,6 +35,7 @@ tls_server_config_ecdsa(void) return (c); } +#ifdef NNG_SUPP_TLS_PSK static nng_tls_config * tls_config_psk(nng_tls_mode mode, const char *name, uint8_t *key, size_t len) { @@ -43,6 +44,7 @@ tls_config_psk(nng_tls_mode mode, const char *name, uint8_t *key, size_t len) NUTS_PASS(nng_tls_config_psk(c, name, key, len)); return (c); } +#endif static nng_tls_config * tls_client_config(void) diff --git a/src/sp/transport/udp/CMakeLists.txt b/src/sp/transport/udp/CMakeLists.txt index b08cd861..391888fe 100644 --- a/src/sp/transport/udp/CMakeLists.txt +++ b/src/sp/transport/udp/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2024 Staysail Systems, Inc. <info@staysail.tech> +# Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> # # This software is supplied under the terms of the MIT License, a # copy of which should be located in the distribution where this @@ -10,6 +10,8 @@ # UDP transport nng_directory(udp) -nng_sources_if(NNG_TRANSPORT_UDP udp.c) -nng_defines_if(NNG_TRANSPORT_UDP NNG_TRANSPORT_UDP) -nng_test_if(NNG_TRANSPORT_UDP udp_tran_test) +if (NNG_TRANSPORT_UDP) + nng_sources(udp.c) + nng_defines(NNG_TRANSPORT_UDP) + nng_test(udp_tran_test) +endif() diff --git a/src/sp/transport/udp/udp.c b/src/sp/transport/udp/udp.c index 0aa46767..e19a5684 100644 --- a/src/sp/transport/udp/udp.c +++ b/src/sp/transport/udp/udp.c @@ -1756,8 +1756,14 @@ udp_ep_accept(void *arg, nni_aio *aio) nni_mtx_unlock(&ep->mtx); } +static size_t +udp_pipe_size(void) +{ + return (sizeof(udp_pipe)); +} + static nni_sp_pipe_ops udp_pipe_ops = { - .p_size = sizeof(udp_pipe), + .p_size = udp_pipe_size, .p_init = udp_pipe_init, .p_fini = udp_pipe_fini, .p_stop = udp_pipe_stop, diff --git a/src/sp/transport/ws/CMakeLists.txt b/src/sp/transport/ws/CMakeLists.txt index 437d0919..2b477c27 100644 --- a/src/sp/transport/ws/CMakeLists.txt +++ b/src/sp/transport/ws/CMakeLists.txt @@ -12,11 +12,14 @@ nng_directory(ws) if (NNG_TRANSPORT_WS OR NNG_TRANSPORT_WSS) - set(WS_ON ON) + nng_sources(websocket.c) endif() -nng_defines_if(NNG_TRANSPORT_WS NNG_TRANSPORT_WS) -nng_defines_if(NNG_TRANSPORT_WSS NNG_TRANSPORT_WSS) -nng_sources_if(WS_ON websocket.c) -nng_test_if(WS_ON ws_test) -nng_test_if(NNG_TRANSPORT_WSS wss_test) +if (NNG_TRANSPORT_WS) + nng_defines(NNG_TRANSPORT_WS) + nng_test(ws_test) +endif() +if (NNG_TRANSPORT_WSS) + nng_defines(NNG_TRANSPORT_WSS) + nng_test(wss_test) +endif() diff --git a/src/sp/transport/ws/websocket.c b/src/sp/transport/ws/websocket.c index b6045306..515f7b65 100644 --- a/src/sp/transport/ws/websocket.c +++ b/src/sp/transport/ws/websocket.c @@ -328,8 +328,14 @@ wstran_pipe_getopt( return (rv); } +static size_t +wstran_pipe_size(void) +{ + return (sizeof(ws_pipe)); +} + static nni_sp_pipe_ops ws_pipe_ops = { - .p_size = sizeof(ws_pipe), + .p_size = wstran_pipe_size, .p_init = wstran_pipe_init, .p_fini = wstran_pipe_fini, .p_stop = wstran_pipe_stop, diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c index 53da78c2..2273e3be 100644 --- a/src/supplemental/http/http_server.c +++ b/src/supplemental/http/http_server.c @@ -99,9 +99,15 @@ static nni_reap_list http_sc_reap_list = { static void http_server_fini(nni_http_server *); +static void +http_server_fini_cb(void *arg) +{ + http_server_fini((nni_http_server *) arg); +} + static nni_reap_list http_server_reap_list = { .rl_offset = offsetof(nni_http_server, reap), - .rl_func = (nni_cb) http_server_fini, + .rl_func = http_server_fini_cb, }; nng_err diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt index 400b1354..41587915 100644 --- a/src/supplemental/tls/CMakeLists.txt +++ b/src/supplemental/tls/CMakeLists.txt @@ -12,8 +12,10 @@ # if (NNG_ENABLE_TLS) + # List of TLS engines we support. TLS engines must support TLS 1.2 or better, + # and must also support DTLS. Support for PSK is preferred. set(NNG_TLS_ENGINES mbed wolf none) - # We assume Mbed for now. (Someday replaced perhaps with Bear.) + # We assume Mbed for now. set(NNG_TLS_ENGINE mbed CACHE STRING "TLS engine to use.") set_property(CACHE NNG_TLS_ENGINE PROPERTY STRINGS ${NNG_TLS_ENGINES}) else () @@ -29,7 +31,7 @@ add_subdirectory(wolfssl) 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) + nng_sources(tls_api.h tls_common.h tls_engine.h tls_stream.h) else() nng_sources(tls_stubs.c) endif() diff --git a/src/supplemental/tls/mbedtls/CMakeLists.txt b/src/supplemental/tls/mbedtls/CMakeLists.txt index acf852bd..466f0a1b 100644 --- a/src/supplemental/tls/mbedtls/CMakeLists.txt +++ b/src/supplemental/tls/mbedtls/CMakeLists.txt @@ -13,7 +13,7 @@ if (NNG_TLS_ENGINE STREQUAL "mbed") Linking against Mbed TLS may change license terms. Consult a lawyer and the license files for details. ************************************************************") - nng_sources(tls.c) + nng_sources(mbedtls.c) nng_defines(NNG_TLS_ENGINE_INIT=nng_tls_engine_init_mbed) nng_defines(NNG_TLS_ENGINE_FINI=nng_tls_engine_fini_mbed) nng_defines(NNG_SUPP_TLS) diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/mbedtls.c index 7764bbbf..8250740f 100644 --- a/src/supplemental/tls/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/mbedtls.c @@ -23,6 +23,9 @@ #include "nng/nng.h" +// We use a common cookie for our application. +#include "mbedtls/ssl_cookie.h" + #include "../tls_engine.h" // mbedTLS renamed this header for 2.4.0. @@ -82,6 +85,8 @@ static nni_mtx rng_lock; struct nng_tls_engine_conn { void *tls; // parent conn mbedtls_ssl_context ctx; + nng_time exp1; + nng_time exp2; }; struct nng_tls_engine_config { @@ -96,6 +101,8 @@ struct nng_tls_engine_config { nni_list psks; }; +static mbedtls_ssl_cookie_ctx mbed_ssl_cookie_ctx; + static void tls_dbg(void *ctx, int level, const char *file, int line, const char *s) { @@ -238,15 +245,44 @@ conn_fini(nng_tls_engine_conn *ec) mbedtls_ssl_free(&ec->ctx); } +static void +conn_set_timer(void *arg, unsigned int t1, unsigned int t2) +{ + nng_time now = nng_clock(); + nng_tls_engine_conn *ec = arg; + ec->exp1 = t1 ? now + t1 : 0; + ec->exp2 = t2 ? now + t2 : 0; +} + static int -conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg) +conn_get_timer(void *arg) { - int rv; + nng_tls_engine_conn *ec = arg; + nng_time now = nng_clock(); + if (ec->exp2 == 0) { + return -1; + } + if (now > ec->exp2) { + return 2; + } + if (now > ec->exp1) { + return 1; + } + return (0); +} + +static int +conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg, + const nng_sockaddr *sa) +{ + int rv; + char buf[NNG_MAXADDRSTRLEN]; ec->tls = tls; mbedtls_ssl_init(&ec->ctx); mbedtls_ssl_set_bio(&ec->ctx, tls, net_send, net_recv, NULL); + mbedtls_ssl_set_timer_cb(&ec->ctx, ec, conn_set_timer, conn_get_timer); if ((rv = mbedtls_ssl_setup(&ec->ctx, &cfg->cfg_ctx)) != 0) { tls_log_warn( @@ -258,6 +294,12 @@ conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg) mbedtls_ssl_set_hostname(&ec->ctx, cfg->server_name); } + if (cfg->mode == NNG_TLS_MODE_SERVER) { + nng_str_sockaddr(sa, buf, sizeof(buf)); + mbedtls_ssl_set_client_transport_id( + &ec->ctx, (const void *) buf, strlen(buf)); + } + return (0); } @@ -484,6 +526,12 @@ config_init(nng_tls_engine_config *cfg, enum nng_tls_mode mode) mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg); mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg); + if (cfg->mode == NNG_TLS_MODE_SERVER) { + mbedtls_ssl_conf_dtls_cookies(&cfg->cfg_ctx, + mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, + &mbed_ssl_cookie_ctx); + } + return (0); } @@ -793,9 +841,14 @@ nng_tls_engine_init_mbed(void) #endif // Uncomment the following to have noisy debug from mbedTLS. // This may be useful when trying to debug failures. - // mbedtls_debug_set_threshold(9); + // mbedtls_debug_set_threshold(9); + + mbedtls_ssl_cookie_init(&mbed_ssl_cookie_ctx); + rv = mbedtls_ssl_cookie_setup(&mbed_ssl_cookie_ctx, tls_random, NULL); - rv = nng_tls_engine_register(&tls_engine_mbed); + if (rv == 0) { + rv = nng_tls_engine_register(&tls_engine_mbed); + } #ifdef NNG_TLS_USE_CTR_DRBG if (rv != 0) { @@ -809,6 +862,7 @@ nng_tls_engine_init_mbed(void) void nng_tls_engine_fini_mbed(void) { + mbedtls_ssl_cookie_free(&mbed_ssl_cookie_ctx); #ifdef NNG_TLS_USE_CTR_DRBG mbedtls_ctr_drbg_free(&rng_ctx); nni_mtx_fini(&rng_lock); diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c index c31e5fe9..40689f69 100644 --- a/src/supplemental/tls/tls_common.c +++ b/src/supplemental/tls/tls_common.c @@ -41,15 +41,15 @@ static nni_atomic_ptr tls_engine; 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_bio_send_start(tls_conn *); -static void tls_bio_error(tls_conn *, int); +static void tls_do_send(nni_tls_conn *); +static void tls_do_recv(nni_tls_conn *); +static void tls_bio_send_start(nni_tls_conn *); +static void tls_bio_error(nni_tls_conn *, nng_err); static void tls_cancel(nni_aio *aio, void *arg, nng_err rv) { - tls_conn *conn = arg; + nni_tls_conn *conn = arg; nni_mtx_lock(&conn->lock); if (aio == nni_list_first(&conn->recv_queue)) { nni_aio_abort(&conn->bio_recv, rv); @@ -64,7 +64,7 @@ tls_cancel(nni_aio *aio, void *arg, nng_err rv) // tls_send implements the upper layer send operation. void -nni_tls_send(tls_conn *conn, nni_aio *aio) +nni_tls_send(nni_tls_conn *conn, nni_aio *aio) { nni_aio_reset(aio); nni_mtx_lock(&conn->lock); @@ -83,7 +83,7 @@ nni_tls_send(tls_conn *conn, nni_aio *aio) } void -nni_tls_recv(tls_conn *conn, nni_aio *aio) +nni_tls_recv(nni_tls_conn *conn, nni_aio *aio) { nni_aio_reset(aio); nni_mtx_lock(&conn->lock); @@ -103,21 +103,20 @@ nni_tls_recv(tls_conn *conn, nni_aio *aio) } void -nni_tls_close(tls_conn *conn) +nni_tls_close(nni_tls_conn *conn) { if (!nni_atomic_flag_test_and_set(&conn->did_close)) { nni_mtx_lock(&conn->lock); conn->ops.close((void *) (conn + 1)); - tls_bio_error(conn, NNG_ECLOSED); nni_mtx_unlock(&conn->lock); - if (conn->bio != NULL) { - conn->bio_ops.bio_close(conn->bio); - } + nni_mtx_lock(&conn->bio_lock); + tls_bio_error(conn, NNG_ECLOSED); + nni_mtx_unlock(&conn->bio_lock); } } void -nni_tls_stop(tls_conn *conn) +nni_tls_stop(nni_tls_conn *conn) { nni_tls_close(conn); if (conn->bio != NULL) { @@ -128,7 +127,7 @@ nni_tls_stop(tls_conn *conn) } bool -nni_tls_verified(tls_conn *conn) +nni_tls_verified(nni_tls_conn *conn) { bool result; nni_mtx_lock(&conn->lock); @@ -138,7 +137,7 @@ nni_tls_verified(tls_conn *conn) } const char * -nni_tls_peer_cn(tls_conn *conn) +nni_tls_peer_cn(nni_tls_conn *conn) { const char *result; nni_mtx_lock(&conn->lock); @@ -148,7 +147,7 @@ nni_tls_peer_cn(tls_conn *conn) } int -nni_tls_init(tls_conn *conn, nng_tls_config *cfg) +nni_tls_init(nni_tls_conn *conn, nng_tls_config *cfg) { const nng_tls_engine *eng; @@ -158,9 +157,9 @@ nni_tls_init(tls_conn *conn, nng_tls_config *cfg) cfg->busy = true; nni_mtx_unlock(&cfg->lock); - if (((conn->bio_send_buf = nni_alloc(NNG_TLS_MAX_SEND_SIZE)) == + if (((conn->bio_send_buf = nni_zalloc(NNG_TLS_MAX_SEND_SIZE)) == NULL) || - ((conn->bio_recv_buf = nni_alloc(NNG_TLS_MAX_RECV_SIZE)) == + ((conn->bio_recv_buf = nni_zalloc(NNG_TLS_MAX_RECV_SIZE)) == NULL)) { return (NNG_ENOMEM); } @@ -173,6 +172,7 @@ nni_tls_init(tls_conn *conn, nng_tls_config *cfg) nni_aio_list_init(&conn->send_queue); nni_aio_list_init(&conn->recv_queue); nni_mtx_init(&conn->lock); + nni_mtx_init(&conn->bio_lock); 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); @@ -182,7 +182,7 @@ nni_tls_init(tls_conn *conn, nng_tls_config *cfg) } void -nni_tls_fini(tls_conn *conn) +nni_tls_fini(nni_tls_conn *conn) { nni_tls_stop(conn); conn->ops.fini((void *) (conn + 1)); @@ -200,11 +200,13 @@ nni_tls_fini(tls_conn *conn) if (conn->bio != NULL) { conn->bio_ops.bio_free(conn->bio); } + nni_mtx_fini(&conn->bio_lock); nni_mtx_fini(&conn->lock); } int -nni_tls_start(tls_conn *conn, const nni_tls_bio_ops *biops, void *bio) +nni_tls_start(nni_tls_conn *conn, const nni_tls_bio_ops *biops, void *bio, + const nng_sockaddr *sa) { nng_tls_engine_config *cfg; nng_tls_engine_conn *econ; @@ -215,48 +217,62 @@ nni_tls_start(tls_conn *conn, const nni_tls_bio_ops *biops, void *bio) conn->bio_ops = *biops; conn->bio = bio; - return (conn->ops.init(econ, conn, cfg)); + return (conn->ops.init(econ, conn, cfg, sa)); } static void -tls_bio_error(tls_conn *conn, int rv) +tls_conn_err(nni_tls_conn *conn, nng_err rv) { - // An error here is fatal. Shut it all down. nni_aio *aio; - if (conn->bio != NULL) { - conn->bio_ops.bio_close(conn->bio); - } - nni_aio_close(&conn->bio_send); - nni_aio_close(&conn->bio_recv); + nni_mtx_lock(&conn->lock); while (((aio = nni_list_first(&conn->send_queue)) != NULL) || ((aio = nni_list_first(&conn->recv_queue)) != NULL)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); } + nni_mtx_unlock(&conn->lock); +} +static void +tls_bio_error(nni_tls_conn *conn, nng_err rv) +{ + // An error here is fatal. Shut it all down. + if (!conn->bio_closed) { + conn->bio_closed = true; + conn->bio_err = rv; + if (conn->bio_send_active) + nni_aio_abort(&conn->bio_send, conn->bio_err); + if (conn->bio_recv_pend) + nni_aio_abort(&conn->bio_recv, conn->bio_err); + if (conn->bio != NULL) { + conn->bio_ops.bio_close(conn->bio); + } + + nni_aio_close(&conn->bio_send); + nni_aio_close(&conn->bio_recv); + } } -static bool -tls_do_handshake(tls_conn *conn) +static nng_err +tls_handshake(nni_tls_conn *conn) { int rv; if (conn->hs_done) { - return (true); + return (NNG_OK); } rv = conn->ops.handshake((void *) (conn + 1)); if (rv == NNG_EAGAIN) { // We need more data. - return (false); + return (rv); } - if (rv == 0) { + if (rv == NNG_OK) { conn->hs_done = true; - return (true); + return (rv); } - tls_bio_error(conn, rv); - return (true); + return (rv); } static void -tls_do_recv(tls_conn *conn) +tls_do_recv(nni_tls_conn *conn) { nni_aio *aio; @@ -294,7 +310,7 @@ tls_do_recv(tls_conn *conn) // caller as *soon* as we have some data. nni_aio_list_remove(aio); - if (rv != 0) { + if (rv != NNG_OK) { nni_aio_finish_error(aio, rv); } else { nni_aio_finish(aio, 0, len); @@ -304,7 +320,7 @@ tls_do_recv(tls_conn *conn) // tls_do_send attempts to send user data. static void -tls_do_send(tls_conn *conn) +tls_do_send(nni_tls_conn *conn) { nni_aio *aio; @@ -350,20 +366,47 @@ tls_do_send(tls_conn *conn) } } +nng_err +nni_tls_run(nni_tls_conn *conn) +{ + nni_aio *aio; + nng_err rv; + nni_mtx_lock(&conn->lock); + switch ((rv = tls_handshake(conn))) { + case NNG_OK: + tls_do_recv(conn); + tls_do_send(conn); + break; + case NNG_EAGAIN: + break; + default: + while (((aio = nni_list_first(&conn->send_queue)) != NULL) || + ((aio = nni_list_first(&conn->recv_queue)) != NULL)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + } + break; + } + nni_mtx_unlock(&conn->lock); + return (rv); +} + static void tls_bio_send_cb(void *arg) { - tls_conn *conn = arg; - nng_aio *aio = &conn->bio_send; - int rv; - size_t count; + nni_tls_conn *conn = arg; + nng_aio *aio = &conn->bio_send; + int rv; + size_t count; - nni_mtx_lock(&conn->lock); + nni_mtx_lock(&conn->bio_lock); conn->bio_send_active = false; if ((rv = nni_aio_result(aio)) != 0) { tls_bio_error(conn, rv); - nni_mtx_unlock(&conn->lock); + nni_mtx_unlock(&conn->bio_lock); + + tls_conn_err(conn, rv); return; } @@ -373,45 +416,37 @@ tls_bio_send_cb(void *arg) conn->bio_send_tail += count; conn->bio_send_tail %= NNG_TLS_MAX_SEND_SIZE; tls_bio_send_start(conn); + nni_mtx_unlock(&conn->bio_lock); - if (tls_do_handshake(conn)) { - tls_do_send(conn); - tls_do_recv(conn); - } - - nni_mtx_unlock(&conn->lock); + nni_tls_run(conn); } static void tls_bio_recv_cb(void *arg) { - tls_conn *conn = arg; - nni_aio *aio = &conn->bio_recv; - int rv; - - nni_mtx_lock(&conn->lock); + nni_tls_conn *conn = arg; + nni_aio *aio = &conn->bio_recv; + int rv; + nni_mtx_lock(&conn->bio_lock); conn->bio_recv_pend = false; if ((rv = nni_aio_result(aio)) != 0) { tls_bio_error(conn, rv); - nni_mtx_unlock(&conn->lock); + nni_mtx_unlock(&conn->bio_lock); + tls_conn_err(conn, rv); return; } NNI_ASSERT(conn->bio_recv_len == 0); NNI_ASSERT(conn->bio_recv_off == 0); conn->bio_recv_len = nni_aio_count(aio); + nni_mtx_unlock(&conn->bio_lock); - if (tls_do_handshake(conn)) { - tls_do_recv(conn); - tls_do_send(conn); - } - - nni_mtx_unlock(&conn->lock); + nni_tls_run(conn); } static void -tls_bio_recv_start(tls_conn *conn) +tls_bio_recv_start(nni_tls_conn *conn) { nng_iov iov; @@ -423,6 +458,9 @@ tls_bio_recv_start(tls_conn *conn) // Already have a receive in flight. return; } + if (conn->bio_closed) { + return; + } conn->bio_recv_off = 0; iov.iov_len = NNG_TLS_MAX_RECV_SIZE; iov.iov_buf = conn->bio_recv_buf; @@ -434,7 +472,7 @@ tls_bio_recv_start(tls_conn *conn) } static void -tls_bio_send_start(tls_conn *conn) +tls_bio_send_start(nni_tls_conn *conn) { nni_iov iov[2]; unsigned nio = 0; @@ -448,6 +486,9 @@ tls_bio_send_start(tls_conn *conn) if (conn->bio_send_len == 0) { return; } + if (conn->bio_closed) { + return; + } len = conn->bio_send_len; head = conn->bio_send_head; tail = conn->bio_send_tail; @@ -478,23 +519,23 @@ tls_bio_send_start(tls_conn *conn) int nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp) { - tls_conn *conn = arg; - size_t len = *szp; - size_t head = conn->bio_send_head; - size_t tail = conn->bio_send_tail; - size_t space; - size_t cnt; + nni_tls_conn *conn = arg; + size_t len = *szp; + size_t head; + size_t tail; + size_t space; + size_t cnt; + nni_mtx_lock(&conn->bio_lock); + head = conn->bio_send_head; + tail = conn->bio_send_tail; space = NNG_TLS_MAX_SEND_SIZE - conn->bio_send_len; if (space == 0) { + nni_mtx_unlock(&conn->bio_lock); return (NNG_EAGAIN); } - if (conn->closed) { - return (NNG_ECLOSED); - } - if (len > space) { len = space; } @@ -525,20 +566,20 @@ nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp) conn->bio_send_head = head; tls_bio_send_start(conn); + nni_mtx_unlock(&conn->bio_lock); return (0); } int nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp) { - tls_conn *conn = arg; - size_t len = *szp; + nni_tls_conn *conn = arg; + size_t len = *szp; - if (conn->closed) { - return (NNG_ECLOSED); - } + nni_mtx_lock(&conn->bio_lock); if (conn->bio_recv_len == 0) { tls_bio_recv_start(conn); + nni_mtx_unlock(&conn->bio_lock); return (NNG_EAGAIN); } if (len > conn->bio_recv_len) { @@ -551,6 +592,7 @@ nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp) // If we still have data left in the buffer, then the following // call is a no-op. tls_bio_recv_start(conn); + nni_mtx_unlock(&conn->bio_lock); *szp = len; return (0); @@ -805,6 +847,16 @@ nng_tls_engine_register(const nng_tls_engine *engine) return (0); } +size_t +nni_tls_engine_conn_size(void) +{ + const nng_tls_engine *eng; + + eng = nni_atomic_get_ptr(&tls_engine); + + return (eng == NULL ? false : eng->conn_ops->size); +} + #ifdef NNG_TLS_ENGINE_INIT extern int NNG_TLS_ENGINE_INIT(void); #else diff --git a/src/supplemental/tls/tls_common.h b/src/supplemental/tls/tls_common.h index 3e703785..14bb0cf7 100644 --- a/src/supplemental/tls/tls_common.h +++ b/src/supplemental/tls/tls_common.h @@ -39,8 +39,6 @@ // 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 @@ -78,29 +76,34 @@ typedef struct { 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 + nni_mtx bio_lock; // lock protecting lower layer operations 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; + bool bio_closed; + nng_err bio_err; 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); +} nni_tls_conn; + +extern void nni_tls_fini(nni_tls_conn *conn); +extern int nni_tls_init(nni_tls_conn *conn, nng_tls_config *cfg); +extern int nni_tls_start(nni_tls_conn *conn, const nni_tls_bio_ops *biops, + void *bio, const nng_sockaddr *sa); +extern void nni_tls_stop(nni_tls_conn *conn); +extern void nni_tls_close(nni_tls_conn *conn); +extern void nni_tls_recv(nni_tls_conn *conn, nni_aio *aio); +extern void nni_tls_send(nni_tls_conn *conn, nni_aio *aio); +extern bool nni_tls_verified(nni_tls_conn *conn); +extern const char *nni_tls_peer_cn(nni_tls_conn *conn); +extern nng_err nni_tls_run(nni_tls_conn *conn); +extern size_t nni_tls_engine_conn_size(void); #endif // NNG_TLS_TLS_COMMON_H diff --git a/src/supplemental/tls/tls_engine.h b/src/supplemental/tls/tls_engine.h index bbc5a944..66d40826 100644 --- a/src/supplemental/tls/tls_engine.h +++ b/src/supplemental/tls/tls_engine.h @@ -44,7 +44,10 @@ typedef struct nng_tls_engine_conn_ops_s { // init is used to initialize a connection object. // The passed in connection state will be aligned naturally, // and zeroed. On success this returns 0, else an NNG error code. - int (*init)(nng_tls_engine_conn *, void *, nng_tls_engine_config *); + // The sockaddr is the peer's socket adress (needed for DTLS or + // possibly session resumption.) + int (*init)(nng_tls_engine_conn *, void *, nng_tls_engine_config *, + const nng_sockaddr *); // fini destroys a connection object. This will // be called only when no other external use of the connection @@ -175,7 +178,7 @@ typedef enum nng_tls_engine_version_e { } nng_tls_engine_version; typedef struct nng_tls_engine_s { - // _version is the engine version. This for now must + // version is the engine version. This for now must // be NNG_TLS_ENGINE_VERSION. If the version does not match // then registration of the engine will fail. nng_tls_engine_version version; @@ -212,7 +215,7 @@ extern int nng_tls_engine_register(const nng_tls_engine *); // is the context structure passed in when starting the engine. extern int nng_tls_engine_send(void *, const uint8_t *, size_t *); -// nng_tls_engine_recv is called byu the engine to receive data over +// nng_tls_engine_recv is called by the engine to receive data over // the underlying connection. It returns zero on success, NNG_EAGAIN // if the operation can't be completed yet (there is no data available // for reading), or some other error. On success the count is updated diff --git a/src/supplemental/tls/tls_stream.c b/src/supplemental/tls/tls_stream.c index 8a7f26d8..cd248686 100644 --- a/src/supplemental/tls/tls_stream.c +++ b/src/supplemental/tls/tls_stream.c @@ -110,9 +110,10 @@ tls_stream_recv(void *arg, nng_aio *aio) static void tls_stream_conn_cb(void *arg) { - tls_stream *ts = arg; - nng_stream *bio; - int rv; + tls_stream *ts = arg; + nng_stream *bio; + int rv; + nng_sockaddr sa; if ((rv = nni_aio_result(&ts->conn_aio)) != 0) { nni_aio_finish_error(ts->user_aio, rv); @@ -121,8 +122,13 @@ tls_stream_conn_cb(void *arg) } bio = nni_aio_get_output(&ts->conn_aio, 0); + if ((rv = nng_stream_get_addr(bio, NNG_OPT_REMADDR, &sa)) != 0) { + nni_aio_finish_error(ts->user_aio, rv); + nni_tls_stream_free(ts); + return; + }; - if ((rv = nni_tls_start(&ts->conn, &tls_stream_bio, bio)) != 0) { + if ((rv = nni_tls_start(&ts->conn, &tls_stream_bio, bio, &sa)) != 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); @@ -140,13 +146,12 @@ static nng_err tls_stream_get( 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; + tls_stream *ts; + size_t size; + int rv; - eng = cfg->engine; - size = NNI_ALIGN_UP(sizeof(*ts)) + eng->conn_ops->size; + size = NNI_ALIGN_UP(sizeof(*ts)) + + NNI_ALIGN_UP(nni_tls_engine_conn_size()); if ((ts = nni_zalloc(size)) == NULL) { return (NNG_ENOMEM); diff --git a/src/supplemental/tls/tls_stream.h b/src/supplemental/tls/tls_stream.h index 78760f82..dca439af 100644 --- a/src/supplemental/tls/tls_stream.h +++ b/src/supplemental/tls/tls_stream.h @@ -21,7 +21,7 @@ typedef struct tls_stream_s { nni_reap_node reap; nni_aio conn_aio; nni_aio *user_aio; - tls_conn conn; // NB: must be last! + nni_tls_conn conn; // NB: must be last! } tls_stream; extern void nni_tls_stream_free(void *arg); diff --git a/src/supplemental/tls/wolfssl/wolfssl.c b/src/supplemental/tls/wolfssl/wolfssl.c index 1510a02a..3eab0ada 100644 --- a/src/supplemental/tls/wolfssl/wolfssl.c +++ b/src/supplemental/tls/wolfssl/wolfssl.c @@ -155,8 +155,10 @@ wolf_conn_fini(nng_tls_engine_conn *ec) } static int -wolf_conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg) +wolf_conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg, + const nng_sockaddr *sa) { + NNI_ARG_UNUSED(sa); // for now... revisit if we support DTLS ? ec->tls = tls; ec->auth_mode = cfg->auth_mode; diff --git a/src/testing/marry.c b/src/testing/marry.c index 6f2ec109..a197fb01 100644 --- a/src/testing/marry.c +++ b/src/testing/marry.c @@ -47,6 +47,7 @@ nuts_scratch_addr(const char *scheme, size_t sz, char *addr) if ((strncmp(scheme, "tcp", 3) == 0) || (strncmp(scheme, "tls", 3) == 0) || + (strncmp(scheme, "dtls", 4) == 0) || (strncmp(scheme, "udp", 3) == 0)) { const char *ip = strchr(scheme, '6') != NULL ? "[::1]" : "127.0.0.1"; @@ -104,6 +105,7 @@ nuts_scratch_addr_zero(const char *scheme, size_t sz, char *addr) if ((strncmp(scheme, "tcp", 3) == 0) || (strncmp(scheme, "tls", 3) == 0) || + (strncmp(scheme, "dtls", 4) == 0) || (strncmp(scheme, "udp", 3) == 0)) { const char *ip = strchr(scheme, '6') != NULL ? "[::1]" : "127.0.0.1"; |
