tinydtls: add sock_async support for sock_dtls
This commit is contained in:
parent
1b2303771e
commit
4013c616f7
@ -17,9 +17,15 @@
|
||||
*/
|
||||
|
||||
#include "dtls.h"
|
||||
#include "log.h"
|
||||
#include "net/sock/dtls.h"
|
||||
#include "net/credman.h"
|
||||
|
||||
#if SOCK_HAS_ASYNC
|
||||
#include "net/sock/async.h"
|
||||
#include "net/sock/async/event.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
#include "dtls_debug.h"
|
||||
@ -77,6 +83,11 @@ static int _read(struct dtls_context_t *ctx, session_t *session, uint8_t *buf,
|
||||
sock->buffer.data = buf;
|
||||
sock->buffer.datalen = len;
|
||||
sock->buffer.session = session;
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
if (sock->async_cb != NULL) {
|
||||
sock->async_cb(sock, SOCK_ASYNC_MSG_RECV, sock->async_cb_arg);
|
||||
}
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -103,7 +114,7 @@ static int _event(struct dtls_context_t *ctx, session_t *session,
|
||||
(void)session;
|
||||
|
||||
sock_dtls_t *sock = dtls_get_app_data(ctx);
|
||||
msg_t msg = { .type = code };
|
||||
msg_t msg = { .type = code, .content.ptr = session };
|
||||
#ifdef ENABLE_DEBUG
|
||||
switch (code) {
|
||||
case DTLS_EVENT_CONNECT:
|
||||
@ -120,6 +131,23 @@ static int _event(struct dtls_context_t *ctx, session_t *session,
|
||||
if (!level && (code != DTLS_EVENT_CONNECT)) {
|
||||
mbox_put(&sock->mbox, &msg);
|
||||
}
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
if (sock->async_cb != NULL) {
|
||||
switch (code) {
|
||||
case DTLS_ALERT_CLOSE_NOTIFY:
|
||||
/* peer closed their session */
|
||||
sock->async_cb(sock, SOCK_ASYNC_CONN_FIN, sock->async_cb_arg);
|
||||
break;
|
||||
case DTLS_EVENT_CONNECTED:
|
||||
/* we received a session handshake initialization */
|
||||
sock->async_cb(sock, SOCK_ASYNC_CONN_RECV,
|
||||
sock->async_cb_arg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -250,6 +278,10 @@ int sock_dtls_create(sock_dtls_t *sock, sock_udp_t *udp_sock,
|
||||
|
||||
sock->udp_sock = udp_sock;
|
||||
sock->buffer.data = NULL;
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
sock->async_cb = NULL;
|
||||
sock->buf_ctx = NULL;
|
||||
#endif /* SOCK_HAS_ASYNC */
|
||||
sock->role = role;
|
||||
sock->tag = tag;
|
||||
sock->dtls_ctx = dtls_new_context(sock);
|
||||
@ -320,14 +352,14 @@ void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote)
|
||||
ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
const void *data, size_t len, uint32_t timeout)
|
||||
{
|
||||
int res;
|
||||
|
||||
assert(sock);
|
||||
assert(remote);
|
||||
assert(data);
|
||||
|
||||
/* check if session exists, if not create session first then send */
|
||||
if (!dtls_get_peer(sock->dtls_ctx, &remote->dtls_session)) {
|
||||
int res;
|
||||
|
||||
if (timeout == 0) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
@ -366,8 +398,47 @@ ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
}
|
||||
}
|
||||
|
||||
return dtls_write(sock->dtls_ctx, &remote->dtls_session,
|
||||
(uint8_t *)data, len);
|
||||
res = dtls_write(sock->dtls_ctx, &remote->dtls_session,
|
||||
(uint8_t *)data, len);
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
if ((res >= 0) && (sock->async_cb != NULL)) {
|
||||
sock->async_cb(sock, SOCK_ASYNC_MSG_SENT, sock->async_cb_arg);
|
||||
}
|
||||
#endif /* SOCK_HAS_ASYNC */
|
||||
return res;
|
||||
}
|
||||
|
||||
#if SOCK_HAS_ASYNC
|
||||
/**
|
||||
* @brief Checks for and iterates for more data chunks within the network
|
||||
* stacks anternal packet buffer
|
||||
*
|
||||
* When no more chunks exists, `data_ctx` assures cleaning up the internal
|
||||
* buffer state and `sock_udp_recv_buf()` returns 0.
|
||||
*
|
||||
* @see @ref sock_udp_recv_buf().
|
||||
*/
|
||||
static void _check_more_chunks(sock_udp_t *udp_sock, void **data,
|
||||
void **data_ctx, sock_udp_ep_t *remote)
|
||||
{
|
||||
ssize_t res;
|
||||
|
||||
while ((res = sock_udp_recv_buf(udp_sock, data, data_ctx, 0, remote)) > 0) {
|
||||
/* TODO: remove and adapt _copy_buffer() to add remaining data when
|
||||
* tinydtls supports chunked datagram payload */
|
||||
if (IS_ACTIVE(DEVELHELP)) {
|
||||
LOG_ERROR("sock_dtls: Chunked datagram payload currently not "
|
||||
"supported yet by tinydtls\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void _copy_session(sock_dtls_t *sock, sock_dtls_session_t *remote)
|
||||
{
|
||||
memcpy(&remote->dtls_session, sock->buffer.session,
|
||||
sizeof(remote->dtls_session));
|
||||
_session_to_ep(&remote->dtls_session, &remote->ep);
|
||||
}
|
||||
|
||||
static ssize_t _copy_buffer(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
@ -380,15 +451,61 @@ static ssize_t _copy_buffer(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
if (buflen > max_len) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
#if SOCK_HAS_ASYNC
|
||||
if (sock->buf_ctx != NULL) {
|
||||
memcpy(data, buf, sock->buffer.datalen);
|
||||
_copy_session(sock, remote);
|
||||
_check_more_chunks(sock->udp_sock, (void **)&buf, &sock->buf_ctx,
|
||||
&remote->ep);
|
||||
if (sock->async_cb &&
|
||||
/* is there a message in the sock's mbox? */
|
||||
cib_avail(&sock->mbox.cib)) {
|
||||
if (sock->buffer.data) {
|
||||
sock->async_cb(sock, SOCK_ASYNC_MSG_RECV,
|
||||
sock->async_cb_arg);
|
||||
}
|
||||
else {
|
||||
sock->async_cb(sock, SOCK_ASYNC_CONN_RECV,
|
||||
sock->async_cb_arg);
|
||||
}
|
||||
}
|
||||
return buflen;
|
||||
}
|
||||
#else
|
||||
(void)remote;
|
||||
#endif
|
||||
/* use `memmove()` as tinydtls reuses `data` to store decrypted data with an
|
||||
* offset in `buf`. This prevents problems with overlapping buffers. */
|
||||
memmove(data, buf, buflen);
|
||||
memcpy(&remote->dtls_session, sock->buffer.session,
|
||||
sizeof(remote->dtls_session));
|
||||
_session_to_ep(&remote->dtls_session, &remote->ep);
|
||||
_copy_session(sock, remote);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static ssize_t _complete_handshake(sock_dtls_t *sock,
|
||||
sock_dtls_session_t *remote,
|
||||
const session_t *session)
|
||||
{
|
||||
memcpy(&remote->dtls_session, session, sizeof(remote->dtls_session));
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
if (sock->async_cb) {
|
||||
sock_async_flags_t flags = SOCK_ASYNC_CONN_RDY;
|
||||
|
||||
if (cib_avail(&sock->mbox.cib)) {
|
||||
if (sock->buffer.data) {
|
||||
flags |= SOCK_ASYNC_MSG_RECV;
|
||||
}
|
||||
else {
|
||||
flags |= SOCK_ASYNC_CONN_RECV;
|
||||
}
|
||||
}
|
||||
sock->async_cb(sock, flags, sock->async_cb_arg);
|
||||
}
|
||||
#else
|
||||
(void)sock;
|
||||
#endif
|
||||
return -SOCK_DTLS_HANDSHAKE;
|
||||
}
|
||||
|
||||
ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
void *data, size_t max_len, uint32_t timeout)
|
||||
{
|
||||
@ -396,16 +513,21 @@ ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
assert(data);
|
||||
assert(remote);
|
||||
|
||||
if (sock->buffer.data != NULL) {
|
||||
/* there is already decrypted data available */
|
||||
return _copy_buffer(sock, remote, data, max_len);
|
||||
}
|
||||
|
||||
/* loop breaks when timeout or application data read */
|
||||
while (1) {
|
||||
ssize_t res;
|
||||
uint32_t start_recv = xtimer_now_usec();
|
||||
ssize_t res = sock_udp_recv(sock->udp_sock, data, max_len, timeout,
|
||||
&remote->ep);
|
||||
msg_t msg;
|
||||
|
||||
if (sock->buffer.data != NULL) {
|
||||
return _copy_buffer(sock, remote, data, max_len);
|
||||
}
|
||||
else if (mbox_try_get(&sock->mbox, &msg) &&
|
||||
msg.type == DTLS_EVENT_CONNECTED) {
|
||||
return _complete_handshake(sock, remote, msg.content.ptr);
|
||||
}
|
||||
res = sock_udp_recv(sock->udp_sock, data, max_len, timeout,
|
||||
&remote->ep);
|
||||
if (res <= 0) {
|
||||
DEBUG("sock_dtls: error receiving UDP packet: %d\n", (int)res);
|
||||
return res;
|
||||
@ -418,21 +540,11 @@ ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||
if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) {
|
||||
timeout = _update_timeout(start_recv, timeout);
|
||||
}
|
||||
|
||||
msg_t msg;
|
||||
if (sock->buffer.data != NULL) {
|
||||
return _copy_buffer(sock, remote, data, max_len);
|
||||
}
|
||||
else if (mbox_try_get(&sock->mbox, &msg) &&
|
||||
msg.type == DTLS_EVENT_CONNECTED) {
|
||||
return -SOCK_DTLS_HANDSHAKE;
|
||||
}
|
||||
else if (timeout == 0) {
|
||||
if (timeout == 0) {
|
||||
DEBUG("sock_dtls: timed out while decrypting message\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void sock_dtls_close(sock_dtls_t *sock)
|
||||
@ -468,4 +580,64 @@ static inline uint32_t _update_timeout(uint32_t start, uint32_t timeout)
|
||||
return (diff > timeout) ? 0: timeout - diff;
|
||||
}
|
||||
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
void _udp_cb(sock_udp_t *udp_sock, sock_async_flags_t flags, void *ctx)
|
||||
{
|
||||
sock_dtls_t *sock = ctx;
|
||||
|
||||
if (flags & SOCK_ASYNC_MSG_RECV) {
|
||||
sock_dtls_session_t remote;
|
||||
void *data = NULL;
|
||||
void *data_ctx = NULL;
|
||||
|
||||
ssize_t res = sock_udp_recv_buf(udp_sock, &data, &data_ctx, 0,
|
||||
&remote.ep);
|
||||
if (res <= 0) {
|
||||
DEBUG("sock_dtls: error receiving UDP packet: %d\n", (int)res);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prevent overriding already set `buf_ctx` */
|
||||
if (sock->buf_ctx != NULL) {
|
||||
DEBUG("sock_dtls: unable to store buffer asynchronously\n");
|
||||
_check_more_chunks(udp_sock, &data, &data_ctx, &remote.ep);
|
||||
return;
|
||||
}
|
||||
_ep_to_session(&remote.ep, &remote.dtls_session);
|
||||
sock->buf_ctx = data_ctx;
|
||||
res = dtls_handle_message(sock->dtls_ctx, &remote.dtls_session,
|
||||
data, res);
|
||||
if (sock->buffer.data == NULL) {
|
||||
_check_more_chunks(udp_sock, &data, &data_ctx, &remote.ep);
|
||||
sock->buf_ctx = NULL;
|
||||
}
|
||||
}
|
||||
if ((flags & SOCK_ASYNC_PATH_PROP) && sock->async_cb) {
|
||||
/* just hand this event type up the stack */
|
||||
sock->async_cb(sock, SOCK_ASYNC_PATH_PROP, sock->async_cb_arg);
|
||||
}
|
||||
}
|
||||
|
||||
void sock_dtls_set_cb(sock_dtls_t *sock, sock_dtls_cb_t cb, void *cb_arg)
|
||||
{
|
||||
sock->async_cb = cb;
|
||||
sock->async_cb_arg = cb_arg;
|
||||
if (IS_USED(MODULE_SOCK_ASYNC_EVENT)) {
|
||||
sock_async_ctx_t *ctx = sock_dtls_get_async_ctx(sock);
|
||||
if (ctx->queue) {
|
||||
sock_udp_event_init(sock->udp_sock, ctx->queue, _udp_cb, sock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
sock_udp_set_cb(sock->udp_sock, _udp_cb, sock);
|
||||
}
|
||||
|
||||
#ifdef SOCK_HAS_ASYNC_CTX
|
||||
sock_async_ctx_t *sock_dtls_get_async_ctx(sock_dtls_t *sock)
|
||||
{
|
||||
return &sock->async_ctx;
|
||||
}
|
||||
#endif /* SOCK_HAS_ASYNC_CTX */
|
||||
#endif /* SOCK_HAS_ASYNC */
|
||||
|
||||
/** @} */
|
||||
|
||||
@ -22,6 +22,9 @@
|
||||
#include "dtls.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/credman.h"
|
||||
#ifdef SOCK_HAS_ASYNC
|
||||
#include "net/sock/async/types.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -37,6 +40,27 @@ extern "C" {
|
||||
struct sock_dtls {
|
||||
dtls_context_t *dtls_ctx; /**< TinyDTLS context for sock */
|
||||
sock_udp_t *udp_sock; /**< Underlying UDP sock to use */
|
||||
#if defined(SOCK_HAS_ASYNC) || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Network stack internal buffer context.
|
||||
*/
|
||||
void *buf_ctx;
|
||||
/**
|
||||
* @brief Asynchronous event callback
|
||||
*
|
||||
* @note Only available if @ref SOCK_HAS_ASYNC is defined
|
||||
*/
|
||||
sock_dtls_cb_t async_cb;
|
||||
void *async_cb_arg; /**< asynchronous callback arg */
|
||||
#if defined(SOCK_HAS_ASYNC_CTX) || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Asynchronous event context
|
||||
*
|
||||
* @note Only available if @ref SOCK_HAS_ASYNC_CTX is defined
|
||||
*/
|
||||
sock_async_ctx_t async_ctx;
|
||||
#endif
|
||||
#endif
|
||||
mbox_t mbox; /**< Mailbox for internal event
|
||||
handling */
|
||||
msg_t mbox_queue[SOCK_DTLS_MBOX_SIZE]; /**< Queue for struct
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user