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 "dtls.h"
|
||||||
|
#include "log.h"
|
||||||
#include "net/sock/dtls.h"
|
#include "net/sock/dtls.h"
|
||||||
#include "net/credman.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)
|
#define ENABLE_DEBUG (0)
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "dtls_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.data = buf;
|
||||||
sock->buffer.datalen = len;
|
sock->buffer.datalen = len;
|
||||||
sock->buffer.session = session;
|
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;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +114,7 @@ static int _event(struct dtls_context_t *ctx, session_t *session,
|
|||||||
(void)session;
|
(void)session;
|
||||||
|
|
||||||
sock_dtls_t *sock = dtls_get_app_data(ctx);
|
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
|
#ifdef ENABLE_DEBUG
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case DTLS_EVENT_CONNECT:
|
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)) {
|
if (!level && (code != DTLS_EVENT_CONNECT)) {
|
||||||
mbox_put(&sock->mbox, &msg);
|
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;
|
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->udp_sock = udp_sock;
|
||||||
sock->buffer.data = NULL;
|
sock->buffer.data = NULL;
|
||||||
|
#ifdef SOCK_HAS_ASYNC
|
||||||
|
sock->async_cb = NULL;
|
||||||
|
sock->buf_ctx = NULL;
|
||||||
|
#endif /* SOCK_HAS_ASYNC */
|
||||||
sock->role = role;
|
sock->role = role;
|
||||||
sock->tag = tag;
|
sock->tag = tag;
|
||||||
sock->dtls_ctx = dtls_new_context(sock);
|
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,
|
ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||||
const void *data, size_t len, uint32_t timeout)
|
const void *data, size_t len, uint32_t timeout)
|
||||||
{
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
assert(sock);
|
assert(sock);
|
||||||
assert(remote);
|
assert(remote);
|
||||||
assert(data);
|
assert(data);
|
||||||
|
|
||||||
/* check if session exists, if not create session first then send */
|
/* check if session exists, if not create session first then send */
|
||||||
if (!dtls_get_peer(sock->dtls_ctx, &remote->dtls_session)) {
|
if (!dtls_get_peer(sock->dtls_ctx, &remote->dtls_session)) {
|
||||||
int res;
|
|
||||||
|
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
return -ENOTCONN;
|
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,
|
res = dtls_write(sock->dtls_ctx, &remote->dtls_session,
|
||||||
(uint8_t *)data, len);
|
(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,
|
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) {
|
if (buflen > max_len) {
|
||||||
return -ENOBUFS;
|
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
|
/* use `memmove()` as tinydtls reuses `data` to store decrypted data with an
|
||||||
* offset in `buf`. This prevents problems with overlapping buffers. */
|
* offset in `buf`. This prevents problems with overlapping buffers. */
|
||||||
memmove(data, buf, buflen);
|
memmove(data, buf, buflen);
|
||||||
memcpy(&remote->dtls_session, sock->buffer.session,
|
_copy_session(sock, remote);
|
||||||
sizeof(remote->dtls_session));
|
|
||||||
_session_to_ep(&remote->dtls_session, &remote->ep);
|
|
||||||
return buflen;
|
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,
|
ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
||||||
void *data, size_t max_len, uint32_t timeout)
|
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(data);
|
||||||
assert(remote);
|
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 */
|
/* loop breaks when timeout or application data read */
|
||||||
while (1) {
|
while (1) {
|
||||||
|
ssize_t res;
|
||||||
uint32_t start_recv = xtimer_now_usec();
|
uint32_t start_recv = xtimer_now_usec();
|
||||||
ssize_t res = sock_udp_recv(sock->udp_sock, data, max_len, timeout,
|
msg_t msg;
|
||||||
&remote->ep);
|
|
||||||
|
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) {
|
if (res <= 0) {
|
||||||
DEBUG("sock_dtls: error receiving UDP packet: %d\n", (int)res);
|
DEBUG("sock_dtls: error receiving UDP packet: %d\n", (int)res);
|
||||||
return 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)) {
|
if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) {
|
||||||
timeout = _update_timeout(start_recv, timeout);
|
timeout = _update_timeout(start_recv, timeout);
|
||||||
}
|
}
|
||||||
|
if (timeout == 0) {
|
||||||
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) {
|
|
||||||
DEBUG("sock_dtls: timed out while decrypting message\n");
|
DEBUG("sock_dtls: timed out while decrypting message\n");
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sock_dtls_close(sock_dtls_t *sock)
|
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;
|
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 "dtls.h"
|
||||||
#include "net/sock/udp.h"
|
#include "net/sock/udp.h"
|
||||||
#include "net/credman.h"
|
#include "net/credman.h"
|
||||||
|
#ifdef SOCK_HAS_ASYNC
|
||||||
|
#include "net/sock/async/types.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -37,6 +40,27 @@ extern "C" {
|
|||||||
struct sock_dtls {
|
struct sock_dtls {
|
||||||
dtls_context_t *dtls_ctx; /**< TinyDTLS context for sock */
|
dtls_context_t *dtls_ctx; /**< TinyDTLS context for sock */
|
||||||
sock_udp_t *udp_sock; /**< Underlying UDP sock to use */
|
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
|
mbox_t mbox; /**< Mailbox for internal event
|
||||||
handling */
|
handling */
|
||||||
msg_t mbox_queue[SOCK_DTLS_MBOX_SIZE]; /**< Queue for struct
|
msg_t mbox_queue[SOCK_DTLS_MBOX_SIZE]; /**< Queue for struct
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user