mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
141ec6fb94
net/sock/dtls: introduce sock_dtls_sendv_aux()
1107 lines
42 KiB
C
1107 lines
42 KiB
C
/*
|
|
* Copyright (C) 2019 HAW Hamburg
|
|
* Freie Universität Berlin
|
|
* Inria
|
|
* Daniele Lacamera
|
|
* Ken Bannister
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @defgroup net_sock_dtls DTLS sock API
|
|
* @ingroup net_sock net_dtls
|
|
* @brief Sock submodule for DTLS
|
|
*
|
|
* DTLS sock acts as a wrapper for the underlying DTLS module to provide
|
|
* encryption for applications using the UDP sock API.
|
|
*
|
|
* How To Use
|
|
* ----------
|
|
*
|
|
* ### Summary
|
|
*
|
|
* - Include module implementing the DTLS sock API in the Makefile
|
|
* - Add credentials
|
|
* 1. Fill credman_credential_t with the credential information
|
|
* 2. Add the credential using @ref credman_add()
|
|
* - Server operation
|
|
* 1. Create UDP sock @ref sock_udp_create()
|
|
* 2. Create DTLS sock @ref sock_dtls_create() using role
|
|
* @ref SOCK_DTLS_SERVER.
|
|
* 3. Optionally:
|
|
* - when using PSK ciphersuites, set a hint @ref sock_dtls_set_server_psk_id_hint()
|
|
* - add extra credentials @ref sock_dtls_add_credential()
|
|
* - when using ECC ciphersuites, set a callback for credential selection
|
|
* @ref sock_dtls_set_rpk_cb()
|
|
* 4. Start listening with @ref sock_dtls_recv()
|
|
* - Client operation
|
|
* 1. Create UDP sock @ref sock_udp_create()
|
|
* 2. Create DTLS sock @ref sock_dtls_create() using role
|
|
* @ref SOCK_DTLS_CLIENT
|
|
* 3. Optionally:
|
|
* - add extra credentials @ref sock_dtls_add_credential()
|
|
* - when using PSK ciphersuites, set a callback for hint reception and credential
|
|
* selection @ref sock_dtls_set_client_psk_cb()
|
|
* - when using ECC ciphersuites, set a callback for credential selection
|
|
* @ref sock_dtls_set_rpk_cb()
|
|
* 4. Start handshake session to server @ref sock_dtls_session_init()
|
|
* 5. Handle incoming handshake packets with @ref sock_dtls_recv()
|
|
* 6. Send packet to server @ref sock_dtls_send()
|
|
*
|
|
* ## Makefile Includes
|
|
*
|
|
* First, we need to [include](@ref including-modules) a module that implements
|
|
* this API in our applications Makefile. For example the module that
|
|
* implements this API for [tinydtls](@ref pkg_tinydtls) is called
|
|
* `tinydtls_sock_dtls'.
|
|
*
|
|
* The corresponding [pkg](@ref pkg) providing the DTLS implementation will be
|
|
* automatically included so there is no need to use `USEPKG` to add the pkg
|
|
* manually.
|
|
*
|
|
* Each DTLS implementation may have its own configuration options and caveat.
|
|
* This can be found at @ref net_dtls.
|
|
*
|
|
* ### Adding credentials
|
|
*
|
|
* Before using this API, either as a server or a client, we first need to
|
|
* add the credentials to be used for the encryption using
|
|
* [credman](@ref net_credman). Note that credman does not copy the credentials
|
|
* given into the system, it only has information about the credentials and
|
|
* where it is located at. So it is your responsibility to make sure that the
|
|
* credential is valid during the lifetime of your application.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* #include <stdio.h>
|
|
*
|
|
* #include "net/credman.h"
|
|
*
|
|
* #define SOCK_DTLS_SERVER_TAG (10)
|
|
* #define SOCK_DTLS_CLIENT_TAG (20)
|
|
*
|
|
* static char *psk_key = "secretPSK";
|
|
* static char *psk_id = "secretID";
|
|
*
|
|
* static const unsigned char server_ecdsa_priv_key[] = {...};
|
|
* static const unsigned char server_ecdsa_pub_key_x[] = {...};
|
|
* static const unsigned char server_ecdsa_pub_key_y[] = {...};
|
|
* static const unsigned char client_pubkey_x[] = {...};
|
|
* static const unsigned char client_pubkey_y[] = {...};
|
|
*
|
|
* static ecdsa_public_key_t other_pubkeys[] = {
|
|
* { .x = client_pubkey_x, .y = client_pubkey_y },
|
|
* };
|
|
*
|
|
* int main(void)
|
|
* {
|
|
* credman_credential_t psk_credential = {
|
|
* .type = CREDMAN_TYPE_PSK,
|
|
* .tag = SOCK_DTLS_SERVER_TAG,
|
|
* .params = {
|
|
* .psk = {
|
|
* .key = { .s = psk_key, .len = sizeof(psk_key), },
|
|
* .id = { .s = psk_id, .len = sizeof(psk_id), },
|
|
* },
|
|
* },
|
|
* };
|
|
*
|
|
* if (credman_add(&psk_credential) < 0) {
|
|
* puts("Error cannot add credential");
|
|
* }
|
|
*
|
|
* credman_credential_t ecc_credential = {
|
|
* .type = CREDMAN_TYPE_ECDSA,
|
|
* .tag = SOCK_DTLS_SERVER_TAG,
|
|
* .params = {
|
|
* .ecdsa = {
|
|
* .private_key = server_ecdsa_priv_key,
|
|
* .public_key = {
|
|
* .x = server_ecdsa_pub_key_x,
|
|
* .y = server_ecdsa_pub_key_y,
|
|
* },
|
|
* .client_keys = other_pubkeys,
|
|
* .client_keys_size = ARRAY_SIZE(other_pubkeys),
|
|
* },
|
|
* },
|
|
* };
|
|
*
|
|
* if (credman_add(&ecc_credential) < 0) {
|
|
* puts("Error cannot add credential");
|
|
* }
|
|
*
|
|
* // start server/client
|
|
* // [...]
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* Above we see an example of how to register a PSK and an ECC credential.
|
|
*
|
|
* First, we need to include the header file for the API.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* #include "net/credman.h"
|
|
*
|
|
* int main(void)
|
|
* {
|
|
* credman_credential_t psk_credential = {
|
|
* .type = CREDMAN_TYPE_PSK,
|
|
* .tag = SOCK_DTLS_SERVER_TAG,
|
|
* .params = {
|
|
* .psk = {
|
|
* .key = { .s = psk_key, .len = sizeof(psk_key), },
|
|
* .id = { .s = psk_id, .len = sizeof(psk_id), },
|
|
* },
|
|
* },
|
|
* };
|
|
*
|
|
* [...]
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* We tell [credman](@ref net_credman) which credential to add by filling in
|
|
* the credentials information in a struct @ref credman_credential_t. For
|
|
* PSK credentials, we use enum @ref CREDMAN_TYPE_PSK for the
|
|
* [type](@ref credman_credential_t::type).
|
|
*
|
|
* Next, we must assign a [tag](@ref credman_tag_t) for the credential. Tags
|
|
* are unsigned integer values used to identify which DTLS sock has
|
|
* access to which credentials. Each DTLS sock will also be assigned a list of tags.
|
|
* As a result, a sock can only use credentials that have the same tag as
|
|
* the ones in the list.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* if (credman_add(&psk_credential) < 0) {
|
|
* puts("Error cannot add credential");
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* After credential information is filled, we can add it to the credential
|
|
* pool using @ref credman_add().
|
|
*
|
|
* For adding credentials of other types, you can follow the steps above except
|
|
* credman_credential_t::type and credman_credential_t::params depend on the
|
|
* type of credential used.
|
|
*
|
|
* ### Server Operation
|
|
*
|
|
* After credentials are added, we can start the server.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* #include <stdio.h>
|
|
*
|
|
* #include "net/sock/dtls.h"
|
|
*
|
|
* #define SOCK_DTLS_SERVER_TAG (10)
|
|
*
|
|
* int main(void)
|
|
* {
|
|
* // Add credentials
|
|
* // [...]
|
|
*
|
|
* // initialize server
|
|
* sock_udp_t udp_sock;
|
|
* sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
|
|
* local.port = 20220;
|
|
* if (sock_udp_create(&udp_sock, &local, NULL, 0) < 0) {
|
|
* puts("Error creating UDP sock");
|
|
* return -1;
|
|
* }
|
|
*
|
|
* sock_dtls_t dtls_sock;
|
|
* if (sock_dtls_create(&dtls_sock, &udp_sock,
|
|
* SOCK_DTLS_SERVER_TAG,
|
|
* SOCK_DTLS_1_2, SOCK_DTLS_SERVER) < 0) {
|
|
* puts("Error creating DTLS sock");
|
|
* return -1;
|
|
* }
|
|
*
|
|
* while (1) {
|
|
* int res;
|
|
* char buf[128];
|
|
* sock_dtls_session_t session;
|
|
*
|
|
* res = sock_dtls_recv(&dtls_sock, &session, buf, sizeof(buf),
|
|
* SOCK_NO_TIMEOUT);
|
|
* if (res > 0) {
|
|
* printf("Received %d bytes\n", res);
|
|
* if (sock_dtls_send(&dtls_sock, &session, buf, res) < 0) {
|
|
* puts("Error sending reply");
|
|
* }
|
|
* }
|
|
* }
|
|
* return 0;
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* This is an example of a DTLS echo server.
|
|
*
|
|
* DTLS sock uses an initialized UDP sock to send and receive encrypted
|
|
* packets. Therefore, the listening port for the server also needs to be
|
|
* set here.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* sock_udp_t udp_sock;
|
|
* sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
|
|
* local.port = 20220;
|
|
* if (sock_udp_create(&udp_sock, &local, NULL, 0) < 0) {
|
|
* puts("Error creating UDP sock");
|
|
* return -1;
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* Using the initialized UDP sock, we can then create our DTLS sock. We use
|
|
* SOCK_DTLS_SERVER_TAG, which is defined as `10` in our application code
|
|
* beforehand, as our tag. Using @ref SOCK_DTLS_1_2 and @ref SOCK_DTLS_SERVER,
|
|
* we set our DTLS endpoint to use DTLS version 1.2 and act as a DTLS server.
|
|
*
|
|
* Note that some DTLS implementation do not support earlier versions of DTLS.
|
|
* In this case, @ref sock_dtls_create() will return an error. A list of
|
|
* supported DTLS version for each DTLS implementation can be found at this
|
|
* [page](@ref net_dtls). In case of error, the program is stopped.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* #define SOCK_DTLS_SERVER_TAG (10)
|
|
*
|
|
* [...]
|
|
*
|
|
* sock_dtls_t dtls_sock;
|
|
* if (sock_dtls_create(&dtls_sock, &udp_sock,
|
|
* SOCK_DTLS_SERVER_TAG,
|
|
* SOCK_DTLS_1_2, SOCK_DTLS_SERVER) < 0) {
|
|
* puts("Error creating DTLS sock");
|
|
* return -1;
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* Now we can listen to incoming packets using @ref sock_dtls_recv(). The
|
|
* application waits indefinitely for new packets. If we want to timeout this
|
|
* wait period we could alternatively set the `timeout` parameter of the
|
|
* function to a value != @ref SOCK_NO_TIMEOUT. If an error occurs we just
|
|
* ignore it and continue looping. We can reply to an incoming message using
|
|
* its `session`.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* while (1) {
|
|
* int res;
|
|
* char buf[128];
|
|
* sock_dtls_session_t session;
|
|
*
|
|
* res = sock_dtls_recv(&dtls_sock, &session, buf, sizeof(buf),
|
|
* SOCK_NO_TIMEOUT);
|
|
* if (res > 0) {
|
|
* printf("Received %d bytes -- echo!\n", res);
|
|
* if (sock_dtls_send(&dtls_sock, &session, buf, res) < 0) {
|
|
* puts("Error sending reply");
|
|
* }
|
|
* }
|
|
* }
|
|
* return 0;
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* ### Client Operation
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* #include "net/sock/udp.h"
|
|
* #include "net/sock/dtls.h"
|
|
* #include "net/ipv6/addr.h"
|
|
* #include "net/credman.h"
|
|
*
|
|
* #define SOCK_DTLS_CLIENT_TAG (20)
|
|
*
|
|
* #ifndef SERVER_ADDR
|
|
* #define SERVER_ADDR "fe80::aa:bb:cc:dd" // replace with the server address
|
|
* #endif
|
|
*
|
|
* int main(void)
|
|
* {
|
|
* // Add credentials
|
|
* // [...]
|
|
*
|
|
* // initialize client
|
|
* char rcv[128];
|
|
* sock_udp_t udp_sock;
|
|
* sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
|
|
* local.port = 12345;
|
|
*
|
|
* sock_udp_ep_t remote = SOCK_IPV6_EP_ANY;
|
|
* remote.port = DTLS_DEFAULT_PORT;
|
|
* remote.netif = gnrc_netif_iter(NULL)->pid; // only if gnrc_netif_highlander() returns true
|
|
*
|
|
* sock_dtls_t dtls_sock;
|
|
* sock_dtls_session_t session;
|
|
*
|
|
* if (!ipv6_addr_from_str((ipv6_addr_t *)remote.addr.ipv6, SERVER_ADDR)) {
|
|
* puts("Error parsing destination address");
|
|
* return -1;
|
|
* }
|
|
*
|
|
* if (sock_udp_create(&udp_sock, &local, NULL, 0) < 0) {
|
|
* puts("Error creating UDP sock");
|
|
* return -1;
|
|
* }
|
|
*
|
|
* if (sock_dtls_create(&dtls_sock, &udp_sock,
|
|
* SOCK_DTLS_CLIENT_TAG,
|
|
* SOCK_DTLS_1_2, SOCK_DTLS_CLIENT) < 0) {
|
|
* puts("Error creating DTLS sock");
|
|
* sock_udp_close(&udp_sock);
|
|
* return -1;
|
|
* }
|
|
*
|
|
* if (sock_dtls_session_init(&dtls_sock, &remote, &session) < 0) {
|
|
* puts("Error initiating session");
|
|
* sock_dtls_close(&dtls_sock);
|
|
* sock_udp_close(&udp_sock);
|
|
* return -1;
|
|
* }
|
|
*
|
|
* if (sock_dtls_recv(&dtls_sock, &session, rcv, sizeof(rcv),
|
|
* SOCK_NO_TIMEOUT) != -SOCK_DTLS_HANDSHAKE) {
|
|
* puts("Error completing handshake");
|
|
* sock_dtls_close(&dtls_sock);
|
|
* sock_udp_close(&udp_sock);
|
|
* return -1;
|
|
* }
|
|
*
|
|
* const char data[] = "HELLO";
|
|
* int res = sock_dtls_send(&dtls_sock, &session, data, sizeof(data), 0);
|
|
* if (res >= 0) {
|
|
* printf("Sent %d bytes\n", res);
|
|
* res = sock_dtls_recv(&dtls_sock, &session, rcv, sizeof(rcv),
|
|
* SOCK_NO_TIMEOUT);
|
|
* if (res > 0) {
|
|
* printf("Received %d bytes\n", res);
|
|
* }
|
|
* }
|
|
* else {
|
|
* puts("Error sending data");
|
|
* }
|
|
*
|
|
* sock_dtls_session_destroy(&dtls_sock, &session);
|
|
* sock_dtls_close(&dtls_sock);
|
|
* sock_udp_close(&udp_sock);
|
|
* return 0;
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* This is an example of a DTLS echo client.
|
|
*
|
|
* Like the server, we must first create the UDP sock.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* sock_udp_t udp_sock;
|
|
* sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
|
|
* local.port = 12345;
|
|
* sock_udp_create(&udp_sock, &local, NULL, 0);
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* After that, we set the address of the remote endpoint and its
|
|
* listening port, which is DTLS_DEFAULT_PORT (20220).
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* sock_udp_ep_t remote = SOCK_IPV6_EP_ANY;
|
|
* remote.port = DTLS_DEFAULT_PORT;
|
|
* remote.netif = gnrc_netif_iter(NULL)->pid; // only if gnrc_netif_highlander() returns true
|
|
*
|
|
* if (!ipv6_addr_from_str((ipv6_addr_t *)remote.addr.ipv6, SERVER_ADDR)) {
|
|
* puts("Error parsing destination address");
|
|
* return -1;
|
|
* }
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* After the UDP sock is created, we can proceed with creating the DTLS sock.
|
|
* Before sending the packet, we must first initiate a session handshake with
|
|
* the remote endpoint using @ref sock_dtls_session_init(). We will need to call
|
|
* @ref sock_dtls_recv() to receive and process all the handshake packets.
|
|
* If the handshake is successful and the session is created, we send packets
|
|
* to it using @ref sock_dtls_send(). As we already know the session exists,
|
|
* we can set the timeout to `0` and listen to the reply with @ref sock_dtls_recv().
|
|
*
|
|
* Alternatively, set the timeout to of @ref sock_dtls_send() to the duration we
|
|
* want to wait for the handshake process. We can also set the timeout to
|
|
* @ref SOCK_NO_TIMEOUT to block indefinitely until handshake is complete.
|
|
* After handshake completes, the packet will be sent.
|
|
*
|
|
* @ref sock_dtls_init(), @ref sock_dtls_recv and @ref sock_dtls_close() only
|
|
* manages the DTLS layer. That means we still have to clean up the created
|
|
* UDP sock from before by calling @ref sock_udp_close() on our UDP sock
|
|
* in case of error or we reached the end of the application.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
|
|
* char rcv[128];
|
|
* sock_dtls_t dtls_sock;
|
|
* sock_dtls_session_t session;
|
|
*
|
|
* [...]
|
|
*
|
|
* if (sock_dtls_create(&dtls_sock, &udp_sock,
|
|
* SOCK_DTLS_CLIENT_TAG,
|
|
* SOCK_DTLS_1_2, SOCK_DTLS_CLIENT) < 0) {
|
|
* puts("Error creating DTLS sock");
|
|
* sock_udp_close(&udp_sock);
|
|
* return -1;
|
|
* }
|
|
*
|
|
* if (sock_dtls_session_init(&dtls_sock, &remote, &session) < 0) {
|
|
* puts("Error initiating session");
|
|
* sock_dtls_close(&dtls_sock);
|
|
* sock_udp_close(&udp_sock);
|
|
* return -1;
|
|
* }
|
|
*
|
|
* if (sock_dtls_recv(&dtls_sock, &session, rcv, sizeof(rcv),
|
|
* SOCK_NO_TIMEOUT) != -SOCK_DTLS_HANDSHAKE) {
|
|
* puts("Error completing handshake");
|
|
* sock_dtls_close(&dtls_sock);
|
|
* sock_udp_close(&udp_sock);
|
|
* return -1;
|
|
* }
|
|
*
|
|
* const char data[] = "HELLO";
|
|
* int res = sock_dtls_send(&dtls_sock, &session, data, sizeof(data), 0);
|
|
* if (res >= 0) {
|
|
* printf("Sent %d bytes: %*.s\n", res, res, data);
|
|
* res = sock_dtls_recv(&dtls_sock, &session, rcv, sizeof(rcv),
|
|
* SOCK_NO_TIMEOUT);
|
|
* if (res > 0) {
|
|
* printf("Received %d bytes: %*.s\n", res, res, rcv);
|
|
* }
|
|
* }
|
|
* else {
|
|
* puts("Error sending data");
|
|
* }
|
|
*
|
|
* sock_dtls_session_destroy(&dtls_sock, &session);
|
|
* sock_dtls_close(&dtls_sock);
|
|
* sock_udp_close(&udp_sock);
|
|
* return 0;
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* ### Multi-credential handling
|
|
*
|
|
* Each sock needs at least one credential tag to operate. `sock_dtls_create` allows to optionally
|
|
* assign an initial credential. Extra credentials can be added and removed using
|
|
* @ref sock_dtls_add_credential and @ref sock_dtls_remove_credential respectively (found in
|
|
* `net/sock/dtls/creds.h`).
|
|
*
|
|
* #### Pre-shared Keys Cipher Suites
|
|
* In the case of PSK, a server can optionally indicate a hint to help the client to decide which
|
|
* PSK Identity to use, using @ref sock_dtls_set_server_psk_id_hint
|
|
* (see https://tools.ietf.org/html/rfc4279#section-5.2). The client application can
|
|
* decide which credential to use based on the sent hint and/or the session information, by
|
|
* registering a callback with @ref sock_dtls_set_client_psk_cb. If no callback is registered, or
|
|
* fails to chose a tag (i.e. it returns @ref CREDMAN_TAG_EMPTY), the credential is chosen as
|
|
* follows: if a hint is sent by the server, all credentials registered to the sock are checked for
|
|
* a matching @ref psk_params_t::hint "hint". A credential is selected on matching hint. If no
|
|
* credential matches the hint or no hint is provided, the first PSK credential registered in the
|
|
* sock is used.
|
|
*
|
|
* #### Elliptic Curve Cryptography Cipher Suites
|
|
* When using ECC both client and server applications can register a callback to decide which of
|
|
* the registered credentials should be used, based on the session information. This is done using
|
|
* @ref sock_dtls_set_rpk_cb.
|
|
*
|
|
* In both cases, if no callbacks are registered, the sock implementation will try to find a
|
|
* registered credential in the Sock's credential list, that matches the needed type. The first
|
|
* one that matches is used.
|
|
*
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief DTLS sock definitions
|
|
*
|
|
* @author Aiman Ismail <muhammadaimanbin.ismail@haw-hamburg.de>
|
|
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
|
* @author Raul A. Fuentes Samaniego <raul.fuentes-samaniego@inria.fr>
|
|
* @author Daniele Lacamera <daniele@wolfssl.com>
|
|
* @author Ken Bannister <kb2ma@runbox.com>
|
|
*/
|
|
|
|
#ifndef NET_SOCK_DTLS_H
|
|
#define NET_SOCK_DTLS_H
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
|
|
/* net/sock/async/types.h included by net/sock.h needs to re-typedef the
|
|
* `sock_dtls_t` to prevent cyclic includes */
|
|
#if defined (__clang__)
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wtypedef-redefinition"
|
|
#endif
|
|
|
|
#include "net/sock.h"
|
|
#include "net/sock/udp.h"
|
|
#include "net/credman.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @defgroup net_sock_dtls_conf SOCK DTLS compile configuration
|
|
* @ingroup net_sock_conf
|
|
* @{
|
|
*/
|
|
/**
|
|
* @brief Default buffer size for DTLS handshake (as exponent of 2^n)
|
|
*
|
|
* As the buffer size ALWAYS needs to be power of two, this option represents
|
|
* the exponent of 2^n, which will be used as the size of the buffer
|
|
* ( @ref DTLS_HANDSHAKE_BUFSIZE ).
|
|
*
|
|
*/
|
|
#ifndef CONFIG_DTLS_HANDSHAKE_BUFSIZE_EXP
|
|
#define CONFIG_DTLS_HANDSHAKE_BUFSIZE_EXP 8
|
|
#endif
|
|
/** @} */
|
|
|
|
/**
|
|
* @brief Size buffer used in handshake to hold credentials
|
|
*/
|
|
#ifndef DTLS_HANDSHAKE_BUFSIZE
|
|
#define DTLS_HANDSHAKE_BUFSIZE (1 << CONFIG_DTLS_HANDSHAKE_BUFSIZE_EXP)
|
|
#endif
|
|
|
|
/**
|
|
* @brief Return value for a successful handshake
|
|
*/
|
|
#define SOCK_DTLS_HANDSHAKE (EXDEV)
|
|
|
|
/**
|
|
* @brief DTLS version number
|
|
* @anchor sock_dtls_prot_version
|
|
* @{
|
|
*/
|
|
enum {
|
|
SOCK_DTLS_1_0 = 1, /**< DTLS version 1.0 */
|
|
SOCK_DTLS_1_2 = 2, /**< DTLS version 1.2 */
|
|
SOCK_DTLS_1_3 = 3, /**< DTLS version 1.3 */
|
|
};
|
|
/** @} */
|
|
|
|
/**
|
|
* @brief DTLS role
|
|
* @anchor sock_dtls_role
|
|
* @{
|
|
*/
|
|
enum {
|
|
SOCK_DTLS_CLIENT = 1, /**< Endpoint client role */
|
|
SOCK_DTLS_SERVER = 2, /**< Endpoint server role */
|
|
};
|
|
/** @} */
|
|
|
|
/**
|
|
* @brief Type for a DTLS sock object
|
|
*
|
|
* @note API implementors: `struct sock_dtls` needs to be defined by
|
|
* an implementation-specific `sock_dtls_types.h`.
|
|
*/
|
|
typedef struct sock_dtls sock_dtls_t;
|
|
|
|
#if defined (__clang__)
|
|
# pragma clang diagnostic pop
|
|
#endif
|
|
|
|
/**
|
|
* @brief Information about a created session.
|
|
*/
|
|
typedef struct sock_dtls_session sock_dtls_session_t;
|
|
|
|
/**
|
|
* @brief Auxiliary data provided when receiving using an DTLS sock object
|
|
*
|
|
* @warning Implementations of this API may rely on this type to be compatible
|
|
* with @ref sock_udp_aux_rx_t. These implementations need to be
|
|
* updated, if this is no longer the case. Users of this API should
|
|
* not rely on this compatibility
|
|
*/
|
|
typedef sock_udp_aux_rx_t sock_dtls_aux_rx_t;
|
|
|
|
/**
|
|
* @brief Auxiliary data provided when sending using an DTLS sock object
|
|
*
|
|
* @warning Implementations of this API may rely on this type to be compatible
|
|
* with @ref sock_udp_aux_rx_t. These implementations need to be
|
|
* updated, if this is no longer the case. Users of this API should
|
|
* not rely on this compatibility
|
|
*/
|
|
typedef sock_udp_aux_tx_t sock_dtls_aux_tx_t;
|
|
|
|
/**
|
|
* @brief Called exactly once during `auto_init`.
|
|
*
|
|
* Calls the initialization function required by the DTLS stack used.
|
|
*/
|
|
void sock_dtls_init(void);
|
|
|
|
/**
|
|
* @brief Creates a new DTLS sock object
|
|
*
|
|
* Takes an initialized UDP sock and uses it for the transport.
|
|
* Memory allocation functions required by the underlying DTLS
|
|
* stack can be called in this function.
|
|
*
|
|
* @see net_credman.
|
|
*
|
|
* @param[out] sock The resulting DTLS sock object
|
|
* @param[in] udp_sock Existing UDP sock initialized with
|
|
* @ref sock_udp_create() to be used underneath.
|
|
* @param[in] tag Credential tag of @p sock. The sock will only use
|
|
* credentials with the tags registered to it
|
|
* (see @ref sock_dtls_add_credential). Set to @ref CREDMAN_TAG_EMPTY to
|
|
* create a sock with an empty tag list.
|
|
* @param[in] version [DTLS version](@ref sock_dtls_prot_version) to use.
|
|
* @param[in] role [Role](@ref sock_dtls_role) of the endpoint.
|
|
*
|
|
* @return 0 on success.
|
|
* @return -1 on error
|
|
*/
|
|
int sock_dtls_create(sock_dtls_t *sock, sock_udp_t *udp_sock,
|
|
credman_tag_t tag, unsigned version, unsigned role);
|
|
|
|
/**
|
|
* @brief Get underlying UDP sock.
|
|
*
|
|
* @pre `sock != NULL`.
|
|
*
|
|
* @param[in] sock DTLS sock to get UDP sock from.
|
|
*
|
|
* @return The underlying UDP sock.
|
|
*/
|
|
sock_udp_t *sock_dtls_get_udp_sock(sock_dtls_t *sock);
|
|
|
|
/**
|
|
* @brief Initialize session handshake.
|
|
*
|
|
* Sends a ClientHello message to initialize the handshake. Call
|
|
* @ref sock_dtls_recv() to finish the handshake.
|
|
*
|
|
* @param[in] sock DTLS sock to use
|
|
* @param[in] ep Remote endpoint to start a handshake with
|
|
* @param[out] remote Resulting session
|
|
*
|
|
* @return 1, if new handshake is started
|
|
* @return 0, if there is an existing session
|
|
* @return -ENOMEM, not enough memory to allocate for new peer
|
|
* @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set.
|
|
* @return -EINVAL, if @p remote is invalid or @p sock is not properly
|
|
* initialized (or closed while sock_udp_recv() blocks).
|
|
*/
|
|
int sock_dtls_session_init(sock_dtls_t *sock, const sock_udp_ep_t *ep,
|
|
sock_dtls_session_t *remote);
|
|
|
|
/**
|
|
* @brief Destroys an existing DTLS session
|
|
*
|
|
* @pre `(sock != NULL) && (ep != NULL)`
|
|
*
|
|
* @param[in] sock @ref sock_dtls_t, which the session is created on
|
|
* @param[in] remote Remote session to destroy
|
|
*
|
|
* @note For tinyDTLS this function destroys the session object right after notifying the remote
|
|
* peer about the closing. This is an interim solution, preventing endlessly blocked session
|
|
* slots, but allows as a consequence truncation attacks.
|
|
* More details in the [issue](https://github.com/eclipse/tinydtls/issues/95).
|
|
*/
|
|
void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote);
|
|
|
|
/**
|
|
* @brief Get the remote UDP endpoint from a session.
|
|
*
|
|
* @pre `(session != NULL) && (ep != NULL)`
|
|
*
|
|
* @param[in] session DTLS session
|
|
* @param[out] ep UDP endpoint
|
|
*/
|
|
void sock_dtls_session_get_udp_ep(const sock_dtls_session_t *session,
|
|
sock_udp_ep_t *ep);
|
|
|
|
/**
|
|
* @brief Set the remote UDP endpoint from a session.
|
|
*
|
|
* @pre `(session != NULL) && (ep != NULL)`
|
|
*
|
|
* @param[in] session DTLS session
|
|
* @param[in] ep UDP endpoint
|
|
*
|
|
* @note Function should only be needed when doing a blocking handshake with
|
|
* @ref sock_dtls_send() to set the remote UDP endpoint.
|
|
*/
|
|
void sock_dtls_session_set_udp_ep(sock_dtls_session_t *session,
|
|
const sock_udp_ep_t *ep);
|
|
|
|
/**
|
|
* @brief Receive handshake messages and application data from remote peer.
|
|
*
|
|
* @param[in] sock DTLS sock to use.
|
|
* @param[out] remote Remote DTLS session of the received data.
|
|
* Cannot be NULL.
|
|
* @param[out] data Pointer where the received data should be stored.
|
|
* @param[in] maxlen Maximum space available at @p data.
|
|
* @param[in] timeout Receive timeout in microseconds.
|
|
* If 0 and no data is available, the function returns
|
|
* immediately.
|
|
* May be SOCK_NO_TIMEOUT to wait until data
|
|
* is available.
|
|
* @param[out] aux Auxiliary data about the received datagram.
|
|
* May be `NULL`, if it is not required by the application.
|
|
*
|
|
* @note Function may block if data is not available and @p timeout != 0
|
|
*
|
|
* @return The number of bytes received on success
|
|
* @return -SOCK_DTLS_HANDSHAKE when new handshake is completed
|
|
* @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set.
|
|
* @return -EAGAIN, if @p timeout is `0` and no data is available.
|
|
* @return -EINVAL, if @p remote is invalid or @p sock is not properly
|
|
* initialized (or closed while sock_dtls_recv() blocks).
|
|
* @return -ENOBUFS, if buffer space is not large enough to store received
|
|
* data.
|
|
* @return -ENOMEM, if no memory was available to receive @p data.
|
|
* @return -ETIMEDOUT, if @p timeout expired.
|
|
*/
|
|
ssize_t sock_dtls_recv_aux(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
|
void *data, size_t maxlen, uint32_t timeout,
|
|
sock_dtls_aux_rx_t *aux);
|
|
|
|
/**
|
|
* @brief Receive handshake messages and application data from remote peer.
|
|
*
|
|
* @param[in] sock DTLS sock to use.
|
|
* @param[out] remote Remote DTLS session of the received data.
|
|
* Cannot be NULL.
|
|
* @param[out] data Pointer where the received data should be stored.
|
|
* @param[in] maxlen Maximum space available at @p data.
|
|
* @param[in] timeout Receive timeout in microseconds.
|
|
* If 0 and no data is available, the function returns
|
|
* immediately.
|
|
* May be SOCK_NO_TIMEOUT to wait until data
|
|
* is available.
|
|
*
|
|
* @note Function may block if data is not available and @p timeout != 0
|
|
*
|
|
* @return The number of bytes received on success
|
|
* @return -SOCK_DTLS_HANDSHAKE when new handshake is completed
|
|
* @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set.
|
|
* @return -EAGAIN, if @p timeout is `0` and no data is available.
|
|
* @return -EINVAL, if @p remote is invalid or @p sock is not properly
|
|
* initialized (or closed while sock_dtls_recv() blocks).
|
|
* @return -ENOBUFS, if buffer space is not large enough to store received
|
|
* data.
|
|
* @return -ENOMEM, if no memory was available to receive @p data.
|
|
* @return -ETIMEDOUT, if @p timeout expired.
|
|
*/
|
|
static inline ssize_t sock_dtls_recv(sock_dtls_t *sock,
|
|
sock_dtls_session_t *remote,
|
|
void *data, size_t maxlen,
|
|
uint32_t timeout)
|
|
{
|
|
return sock_dtls_recv_aux(sock, remote, data, maxlen, timeout, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Decrypts and provides stack-internal buffer space containing a
|
|
* message from a remote peer.
|
|
*
|
|
* @param[in] sock DTLS sock to use.
|
|
* @param[out] remote Remote DTLS session of the received data.
|
|
* Cannot be NULL.
|
|
* @param[out] data Pointer to a stack-internal buffer space containing the
|
|
* received data.
|
|
* @param[in,out] buf_ctx Stack-internal buffer context. If it points to a
|
|
* `NULL` pointer, the stack returns a new buffer space for
|
|
* a new packet. If it does not point to a `NULL` pointer,
|
|
* an existing context is assumed to get a next segment in
|
|
* a buffer.
|
|
* @param[in] timeout Receive timeout in microseconds.
|
|
* If 0 and no data is available, the function returns
|
|
* immediately.
|
|
* May be SOCK_NO_TIMEOUT to wait until data
|
|
* is available.
|
|
* @param[out] aux Auxiliary data about the received datagram.
|
|
* May be `NULL`, if it is not required by the application.
|
|
*
|
|
* @experimental This function is quite new, not implemented for all stacks
|
|
* yet, and may be subject to sudden API changes. Do not use in
|
|
* production if this is unacceptable.
|
|
*
|
|
* @note Function may block if data is not available and @p timeout != 0
|
|
*
|
|
* @note Function blocks if no packet is currently waiting.
|
|
*
|
|
* @return The number of bytes received on success. May not be the complete
|
|
* payload. Continue calling with the returned @p buf_ctx to get more
|
|
* buffers until result is 0 or an error.
|
|
* @return 0, if no received data is available, but everything is in order.
|
|
* If @p buf_ctx was provided, it was released.
|
|
* @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set.
|
|
* @return -EAGAIN, if @p timeout is `0` and no data is available.
|
|
* @return -EINVAL, if @p remote is invalid or @p sock is not properly
|
|
* initialized (or closed while sock_dtls_recv() blocks).
|
|
* @return -ENOMEM, if no memory was available to receive @p data.
|
|
* @return -ETIMEDOUT, if @p timeout expired.
|
|
*/
|
|
ssize_t sock_dtls_recv_buf_aux(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
|
void **data, void **buf_ctx, uint32_t timeout,
|
|
sock_dtls_aux_rx_t *aux);
|
|
|
|
/**
|
|
* @brief Decrypts and provides stack-internal buffer space containing a
|
|
* message from a remote peer.
|
|
*
|
|
* @param[in] sock DTLS sock to use.
|
|
* @param[out] remote Remote DTLS session of the received data.
|
|
* Cannot be NULL.
|
|
* @param[out] data Pointer to a stack-internal buffer space containing the
|
|
* received data.
|
|
* @param[in,out] buf_ctx Stack-internal buffer context. If it points to a
|
|
* `NULL` pointer, the stack returns a new buffer space for
|
|
* a new packet. If it does not point to a `NULL` pointer,
|
|
* an existing context is assumed to get a next segment in
|
|
* a buffer.
|
|
* @param[in] timeout Receive timeout in microseconds.
|
|
* If 0 and no data is available, the function returns
|
|
* immediately.
|
|
* May be SOCK_NO_TIMEOUT to wait until data
|
|
* is available.
|
|
*
|
|
* @experimental This function is quite new, not implemented for all stacks
|
|
* yet, and may be subject to sudden API changes. Do not use in
|
|
* production if this is unacceptable.
|
|
*
|
|
* @note Function may block if data is not available and @p timeout != 0
|
|
*
|
|
* @note Function blocks if no packet is currently waiting.
|
|
*
|
|
* @return The number of bytes received on success. May not be the complete
|
|
* payload. Continue calling with the returned @p buf_ctx to get more
|
|
* buffers until result is 0 or an error.
|
|
* @return 0, if no received data is available, but everything is in order.
|
|
* If @p buf_ctx was provided, it was released.
|
|
* @return -EADDRNOTAVAIL, if the local endpoint of @p sock is not set.
|
|
* @return -EAGAIN, if @p timeout is `0` and no data is available.
|
|
* @return -EINVAL, if @p remote is invalid or @p sock is not properly
|
|
* initialized (or closed while sock_dtls_recv() blocks).
|
|
* @return -ENOMEM, if no memory was available to receive @p data.
|
|
* @return -ETIMEDOUT, if @p timeout expired.
|
|
*/
|
|
static inline ssize_t sock_dtls_recv_buf(sock_dtls_t *sock,
|
|
sock_dtls_session_t *remote,
|
|
void **data, void **buf_ctx,
|
|
uint32_t timeout)
|
|
{
|
|
return sock_dtls_recv_buf_aux(sock, remote, data, buf_ctx, timeout, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Encrypts and sends a message to a remote peer with non-continous payload
|
|
*
|
|
* @param[in] sock DTLS sock to use
|
|
* @param[in] remote DTLS session to use. A new session will be created
|
|
* if no session exist between client and server.
|
|
* @param[in] snips List of payload chunks, will be processed in order.
|
|
* May be `NULL`.
|
|
* @param[in] timeout Handshake timeout in microseconds.
|
|
* If `timeout > 0`, will start a new handshake if no
|
|
* session exists yet. The function will block until
|
|
* handshake completed or timed out.
|
|
* May be SOCK_NO_TIMEOUT to block indefinitely until
|
|
* handshake complete.
|
|
* @param[out] aux Auxiliary data about the transmission.
|
|
* May be `NULL`, if it is not required by the application.
|
|
*
|
|
* @note When blocking, we will need an extra thread to call
|
|
* @ref sock_dtls_recv() function to handle the incoming handshake
|
|
* messages.
|
|
*
|
|
* @return The number of bytes sent on success
|
|
* @return -ENOTCONN, if `timeout == 0` and no existing session exists with
|
|
* @p remote
|
|
* @return -EADDRINUSE, if sock_dtls_t::udp_sock has no local end-point.
|
|
* @return -EAFNOSUPPORT, if `remote->ep != NULL` and
|
|
* sock_dtls_session_t::ep::family of @p remote is != AF_UNSPEC and
|
|
* not supported.
|
|
* @return -EINVAL, if sock_udp_ep_t::addr of @p remote->ep is an
|
|
* invalid address.
|
|
* @return -EINVAL, if sock_udp_ep_t::port of @p remote->ep is 0.
|
|
* @return -ENOMEM, if no memory was available to send @p data.
|
|
* @return -ETIMEDOUT, `0 < timeout < SOCK_NO_TIMEOUT` and timed out.
|
|
*/
|
|
ssize_t sock_dtls_sendv_aux(sock_dtls_t *sock, sock_dtls_session_t *remote,
|
|
const iolist_t *snips, uint32_t timeout,
|
|
sock_dtls_aux_tx_t *aux);
|
|
|
|
/**
|
|
* @brief Encrypts and sends a message to a remote peer
|
|
*
|
|
* @param[in] sock DTLS sock to use
|
|
* @param[in] remote DTLS session to use. A new session will be created
|
|
* if no session exist between client and server.
|
|
* @param[in] data Pointer where the data to be send are stored
|
|
* @param[in] len Length of @p data to be send
|
|
* @param[in] timeout Handshake timeout in microseconds.
|
|
* If `timeout > 0`, will start a new handshake if no
|
|
* session exists yet. The function will block until
|
|
* handshake completed or timed out.
|
|
* May be SOCK_NO_TIMEOUT to block indefinitely until
|
|
* handshake complete.
|
|
* @param[out] aux Auxiliary data about the transmission.
|
|
* May be `NULL`, if it is not required by the application.
|
|
*
|
|
* @note When blocking, we will need an extra thread to call
|
|
* @ref sock_dtls_recv() function to handle the incoming handshake
|
|
* messages.
|
|
*
|
|
* @return The number of bytes sent on success
|
|
* @return -ENOTCONN, if `timeout == 0` and no existing session exists with
|
|
* @p remote
|
|
* @return -EADDRINUSE, if sock_dtls_t::udp_sock has no local end-point.
|
|
* @return -EAFNOSUPPORT, if `remote->ep != NULL` and
|
|
* sock_dtls_session_t::ep::family of @p remote is != AF_UNSPEC and
|
|
* not supported.
|
|
* @return -EINVAL, if sock_udp_ep_t::addr of @p remote->ep is an
|
|
* invalid address.
|
|
* @return -EINVAL, if sock_udp_ep_t::port of @p remote->ep is 0.
|
|
* @return -ENOMEM, if no memory was available to send @p data.
|
|
* @return -ETIMEDOUT, `0 < timeout < SOCK_NO_TIMEOUT` and timed out.
|
|
*/
|
|
static inline ssize_t sock_dtls_send_aux(sock_dtls_t *sock,
|
|
sock_dtls_session_t *remote,
|
|
const void *data, size_t len,
|
|
uint32_t timeout,
|
|
sock_dtls_aux_tx_t *aux)
|
|
{
|
|
const iolist_t snip = {
|
|
.iol_base = (void *)data,
|
|
.iol_len = len,
|
|
};
|
|
|
|
return sock_dtls_sendv_aux(sock, remote, &snip, timeout, aux);
|
|
}
|
|
|
|
/**
|
|
* @brief Encrypts and sends a message to a remote peer
|
|
*
|
|
* @param[in] sock DTLS sock to use
|
|
* @param[in] remote DTLS session to use. A new session will be created
|
|
* if no session exist between client and server.
|
|
* @param[in] data Pointer where the data to be send are stored
|
|
* @param[in] len Length of @p data to be send
|
|
* @param[in] timeout Handshake timeout in microseconds.
|
|
* If `timeout > 0`, will start a new handshake if no
|
|
* session exists yet. The function will block until
|
|
* handshake completed or timed out.
|
|
* May be SOCK_NO_TIMEOUT to block indefinitely until
|
|
* handshake complete.
|
|
*
|
|
* @note When blocking, we will need an extra thread to call
|
|
* @ref sock_dtls_recv() function to handle the incoming handshake
|
|
* messages.
|
|
* An example for a blocking handshake is:
|
|
* 1. Create an empty @ref sock_dtls_session_t object.
|
|
* 2. Set the UDP endpoint of the peer you want to connect to in the
|
|
* session object with @ref sock_dtls_session_set_udp_ep().
|
|
* 3. Call @ref sock_dtls_send() with a timeout greater than 0.
|
|
* The send function blocks until the handshake completes or the
|
|
* timeout expires. If the handshake was successful the data has
|
|
* been sent.
|
|
*
|
|
* @return The number of bytes sent on success
|
|
* @return -ENOTCONN, if `timeout == 0` and no existing session exists with
|
|
* @p remote
|
|
* @return -EADDRINUSE, if sock_dtls_t::udp_sock has no local end-point.
|
|
* @return -EAFNOSUPPORT, if `remote->ep != NULL` and
|
|
* sock_dtls_session_t::ep::family of @p remote is != AF_UNSPEC and
|
|
* not supported.
|
|
* @return -EINVAL, if sock_udp_ep_t::addr of @p remote->ep is an
|
|
* invalid address.
|
|
* @return -EINVAL, if sock_udp_ep_t::port of @p remote->ep is 0.
|
|
* @return -ENOMEM, if no memory was available to send @p data.
|
|
* @return -ETIMEDOUT, `0 < timeout < SOCK_NO_TIMEOUT` and timed out.
|
|
*/
|
|
static inline ssize_t sock_dtls_send(sock_dtls_t *sock,
|
|
sock_dtls_session_t *remote,
|
|
const void *data, size_t len,
|
|
uint32_t timeout)
|
|
{
|
|
return sock_dtls_send_aux(sock, remote, data, len, timeout, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Encrypts and sends a message to a remote peer with non-continous payload
|
|
*
|
|
* @param[in] sock DTLS sock to use
|
|
* @param[in] remote DTLS session to use. A new session will be created
|
|
* if no session exist between client and server.
|
|
* @param[in] snips List of payload chunks, will be processed in order.
|
|
* May be `NULL`.
|
|
* @param[in] timeout Handshake timeout in microseconds.
|
|
* If `timeout > 0`, will start a new handshake if no
|
|
* session exists yet. The function will block until
|
|
* handshake completed or timed out.
|
|
* May be SOCK_NO_TIMEOUT to block indefinitely until
|
|
* handshake complete.
|
|
*
|
|
* @note When blocking, we will need an extra thread to call
|
|
* @ref sock_dtls_recv() function to handle the incoming handshake
|
|
* messages.
|
|
* An example for a blocking handshake is:
|
|
* 1. Create an empty @ref sock_dtls_session_t object.
|
|
* 2. Set the UDP endpoint of the peer you want to connect to in the
|
|
* session object with @ref sock_dtls_session_set_udp_ep().
|
|
* 3. Call @ref sock_dtls_send() with a timeout greater than 0.
|
|
* The send function blocks until the handshake completes or the
|
|
* timeout expires. If the handshake was successful the data has
|
|
* been sent.
|
|
*
|
|
* @return The number of bytes sent on success
|
|
* @return -ENOTCONN, if `timeout == 0` and no existing session exists with
|
|
* @p remote
|
|
* @return -EADDRINUSE, if sock_dtls_t::udp_sock has no local end-point.
|
|
* @return -EAFNOSUPPORT, if `remote->ep != NULL` and
|
|
* sock_dtls_session_t::ep::family of @p remote is != AF_UNSPEC and
|
|
* not supported.
|
|
* @return -EINVAL, if sock_udp_ep_t::addr of @p remote->ep is an
|
|
* invalid address.
|
|
* @return -EINVAL, if sock_udp_ep_t::port of @p remote->ep is 0.
|
|
* @return -ENOMEM, if no memory was available to send @p data.
|
|
* @return -ETIMEDOUT, `0 < timeout < SOCK_NO_TIMEOUT` and timed out.
|
|
*/
|
|
static inline ssize_t sock_dtls_sendv(sock_dtls_t *sock,
|
|
sock_dtls_session_t *remote,
|
|
const iolist_t *snips,
|
|
uint32_t timeout)
|
|
{
|
|
return sock_dtls_sendv_aux(sock, remote, snips, timeout, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Closes a DTLS sock
|
|
*
|
|
* Releases any memory allocated by @ref sock_dtls_create(). This function does
|
|
* NOT close the UDP sock used by the DTLS sock. After the call to this
|
|
* function, user will have to call @ref sock_udp_close() to close the UDP
|
|
* sock.
|
|
*
|
|
* @pre `(sock != NULL)`
|
|
*
|
|
* @param sock DTLS sock to close
|
|
*/
|
|
void sock_dtls_close(sock_dtls_t *sock);
|
|
|
|
#ifdef MODULE_SOCK_DTLS
|
|
#include "sock_dtls_types.h"
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* NET_SOCK_DTLS_H */
|
|
/** @} */
|