From 90eae8e0a4c5df2967a1ba7c60fb4980292c1fe7 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 27 Jul 2021 23:26:10 +0200 Subject: [PATCH 1/2] gcoap: multi-transport support for clients 23a8659bdf35a0cc2758018d4dbf3c1e76c655b0 introduced DTLS support for CoAP, but did not make it possible to select the transport on request. Since switching between CoAP and CoAPS (CoAP-over-DTLS) as client is a valid use case (one might want to e.g. talk to one server over CoAP and to another over CoAPS), this change makes that possible. --- sys/include/net/gcoap.h | 95 +++++++++++------ sys/net/application_layer/gcoap/gcoap.c | 131 ++++++++++++++---------- 2 files changed, 143 insertions(+), 83 deletions(-) diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index 3bedb871787e..f164c63bd661 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -750,6 +750,33 @@ typedef struct { size_t pdu_len; /**< Length of pdu_buf */ } gcoap_resend_t; +/** + * @brief Coap socket types + */ +typedef enum { + GCOAP_SOCKET_TYPE_UNDEF = 0, + GCOAP_SOCKET_TYPE_UDP, + GCOAP_SOCKET_TYPE_DTLS +} gcoap_socket_type_t; + +/** + * @brief Coap socket to handle multiple transport types + */ +typedef struct { + gcoap_socket_type_t type; /**< Type of stored socket */ + union { + sock_udp_t *udp; +#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) + sock_dtls_t *dtls; +#endif + } socket; /**< Stored socket */ +#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) + sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket. + Used for exchanging a session between + functions. */ +#endif +} gcoap_socket_t; + /** * @brief Memo to handle a response for a request */ @@ -768,6 +795,7 @@ struct gcoap_request_memo { void *context; /**< ptr to user defined context data */ event_timeout_t resp_evt_tmout; /**< Limits wait for response */ event_callback_t resp_tmout_cb; /**< Callback for response timeout */ + gcoap_socket_t socket; /**< Transport type to remote endpoint */ }; /** @@ -778,35 +806,9 @@ typedef struct { const coap_resource_t *resource; /**< Entity being observed */ uint8_t token[GCOAP_TOKENLEN_MAX]; /**< Client token for notifications */ unsigned token_len; /**< Actual length of token attribute */ + gcoap_socket_t socket; /**< Transport type to observer */ } gcoap_observe_memo_t; -/** - * @brief Coap socket types - */ -typedef enum { - GCOAP_SOCKET_TYPE_UNDEF = 0, - GCOAP_SOCKET_TYPE_UDP, - GCOAP_SOCKET_TYPE_DTLS -} gcoap_socket_type_t; - -/** - * @brief Coap socket to handle multiple transport types - */ -typedef struct { - gcoap_socket_type_t type; /**< Type of stored socket */ - union { - sock_udp_t *udp; -#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) - sock_dtls_t *dtls; -#endif - } socket; /**< Stored socket */ -#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) - sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket. - Used for exchanging a session between - functions. */ -#endif -} gcoap_socket_t; - /** * @brief Initializes the gcoap thread and device * @@ -904,6 +906,31 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len, : -1; } +/** + * @brief Sends a buffer containing a CoAP request to the provided endpoint + * + * @deprecated Will be an alias for @ref gcoap_req_send after the 2022.01 + * release. Will be removed after the 2022.04 release. + * + * @param[in] buf Buffer containing the PDU + * @param[in] len Length of the buffer + * @param[in] remote Destination for the packet + * @param[in] resp_handler Callback when response received, may be NULL + * @param[in] context User defined context passed to the response handler + * @param[in] tl_type The transport type to use for send. When + * GCOAP_SOCKET_TYPE_UNDEF is selected, the highest + * available (by value) will be selected. + * + * @return length of the packet + * @return -ENOTCONN, if DTLS was used and session establishment failed + * @return -EINVAL, if @p tl_type is is not supported + * @return 0 if cannot send + */ +ssize_t gcoap_req_send_tl(const uint8_t *buf, size_t len, + const sock_udp_ep_t *remote, + gcoap_resp_handler_t resp_handler, void *context, + gcoap_socket_type_t tl_type); + /** * @brief Sends a buffer containing a CoAP request to the provided endpoint * @@ -913,13 +940,21 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len, * @param[in] resp_handler Callback when response received, may be NULL * @param[in] context User defined context passed to the response handler * + * @note The highest supported (by value) coap_socket_type_t will be selected + * as transport type. + * * @return length of the packet * @return -ENOTCONN, if DTLS was used and session establishment failed * @return 0 if cannot send */ -ssize_t gcoap_req_send(const uint8_t *buf, size_t len, - const sock_udp_ep_t *remote, - gcoap_resp_handler_t resp_handler, void *context); +static inline ssize_t gcoap_req_send(const uint8_t *buf, size_t len, + const sock_udp_ep_t *remote, + gcoap_resp_handler_t resp_handler, + void *context) +{ + return gcoap_req_send_tl(buf, len, remote, resp_handler, context, + GCOAP_SOCKET_TYPE_UNDEF); +} /** * @brief Initializes a CoAP response packet on a buffer diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 8d02b4fffa36..3351982f2160 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -54,15 +54,15 @@ static void *_event_loop(void *arg); static void _on_sock_udp_evt(sock_udp_t *sock, sock_async_flags_t type, void *arg); static void _process_coap_pdu(gcoap_socket_t *sock, sock_udp_ep_t *remote, uint8_t *buf, size_t len, bool truncated); -static void _tl_init_coap_socket(gcoap_socket_t *sock); +static int _tl_init_coap_socket(gcoap_socket_t *sock, gcoap_socket_type_t type); static ssize_t _tl_send(gcoap_socket_t *sock, const void *data, size_t len, const sock_udp_ep_t *remote); static ssize_t _tl_authenticate(gcoap_socket_t *sock, const sock_udp_ep_t *remote, uint32_t timeout); static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx); static void _cease_retransmission(gcoap_request_memo_t *memo); -static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len, - sock_udp_ep_t *remote); +static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf, + size_t len, sock_udp_ep_t *remote); static void _expire_request(gcoap_request_memo_t *memo); static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *pdu, const sock_udp_ep_t *remote, bool by_mid); @@ -383,7 +383,8 @@ static void _process_coap_pdu(gcoap_socket_t *sock, sock_udp_ep_t *remote, pdu_len = gcoap_response(&pdu, _listen_buf, sizeof(_listen_buf), COAP_CODE_REQUEST_ENTITY_TOO_LARGE); } else { - pdu_len = _handle_req(&pdu, _listen_buf, sizeof(_listen_buf), remote); + pdu_len = _handle_req(sock, &pdu, _listen_buf, + sizeof(_listen_buf), remote); } if (pdu_len > 0) { @@ -485,9 +486,7 @@ static void _on_resp_timeout(void *arg) { return; } - gcoap_socket_t socket; - _tl_init_coap_socket(&socket); - ssize_t bytes = _tl_send(&socket, memo->msg.data.pdu_buf, + ssize_t bytes = _tl_send(&memo->socket, memo->msg.data.pdu_buf, memo->msg.data.pdu_len, &memo->remote_ep); if (bytes <= 0) { DEBUG("gcoap: sock resend failed: %d\n", (int)bytes); @@ -529,8 +528,8 @@ static void _cease_retransmission(gcoap_request_memo_t *memo) { * * return length of response pdu, or < 0 if can't handle */ -static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len, - sock_udp_ep_t *remote) +static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf, + size_t len, sock_udp_ep_t *remote) { const coap_resource_t *resource = NULL; gcoap_listener_t *listener = NULL; @@ -567,7 +566,8 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len, } /* otherwise OK to re-register resource with the same token */ } - else if (sock_udp_ep_equal(remote, resource_memo->observer)) { + else if ((sock->type == resource_memo->socket.type) && + sock_udp_ep_equal(remote, resource_memo->observer)) { /* accept new token for resource */ memo = resource_memo; } @@ -601,6 +601,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len, /* resource may be assigned here if it is not already registered */ memo->resource = resource; memo->token_len = coap_get_token_len(pdu); + memo->socket = *sock; if (memo->token_len) { memcpy(&memo->token[0], pdu->token, memo->token_len); } @@ -924,54 +925,72 @@ static void _find_obs_memo_resource(gcoap_observe_memo_t **memo, * Transport layer functions */ -static void _tl_init_coap_socket(gcoap_socket_t *sock) +static int _tl_init_coap_socket(gcoap_socket_t *sock, gcoap_socket_type_t type) { + switch (type) { +#if !IS_USED(MODULE_GCOAP_DTLS) + case GCOAP_SOCKET_TYPE_UNDEF: +#endif + case GCOAP_SOCKET_TYPE_UDP: + sock->type = GCOAP_SOCKET_TYPE_UDP; + sock->socket.udp = &_sock_udp; + break; #if IS_USED(MODULE_GCOAP_DTLS) - sock->type = GCOAP_SOCKET_TYPE_DTLS; - sock->socket.dtls = &_sock_dtls; + case GCOAP_SOCKET_TYPE_UNDEF: + case GCOAP_SOCKET_TYPE_DTLS: + sock->type = GCOAP_SOCKET_TYPE_DTLS; + sock->socket.dtls = &_sock_dtls; + break; #else - sock->type = GCOAP_SOCKET_TYPE_UDP; - sock->socket.udp = &_sock_udp; + default: + return -1; #endif + } + return 0; } static ssize_t _tl_send(gcoap_socket_t *sock, const void *data, size_t len, - const sock_udp_ep_t *remote) + const sock_udp_ep_t *remote) { ssize_t res = -1; - if (sock->type == GCOAP_SOCKET_TYPE_DTLS) { + switch (sock->type) { + case GCOAP_SOCKET_TYPE_UDP: + res = sock_udp_send(sock->socket.udp, data, len, remote); + break; #if IS_USED(MODULE_GCOAP_DTLS) - /* prepare session */ - sock_dtls_session_set_udp_ep(&sock->ctx_dtls_session, remote); - dsm_state_t session_state = dsm_store(sock->socket.dtls, &sock->ctx_dtls_session, - SESSION_STATE_HANDSHAKE, true); - if (session_state == NO_SPACE) { - return -1; - } + case GCOAP_SOCKET_TYPE_DTLS: + /* prepare session */ + sock_dtls_session_set_udp_ep(&sock->ctx_dtls_session, remote); + dsm_state_t session_state = dsm_store(sock->socket.dtls, + &sock->ctx_dtls_session, + SESSION_STATE_HANDSHAKE, + true); + if (session_state == NO_SPACE) { + return -1; + } - /* send application data */ - res = sock_dtls_send(sock->socket.dtls, &sock->ctx_dtls_session, data, len, - SOCK_NO_TIMEOUT); - switch (res) { - case -EHOSTUNREACH: - case -ENOTCONN: - case 0: - DEBUG("gcoap: DTLS sock not connected or remote unreachable. " - "Destroying session.\n"); - dsm_remove(sock->socket.dtls, &sock->ctx_dtls_session); - sock_dtls_session_destroy(sock->socket.dtls, &sock->ctx_dtls_session); + /* send application data */ + res = sock_dtls_send(sock->socket.dtls, &sock->ctx_dtls_session, data, len, + SOCK_NO_TIMEOUT); + switch (res) { + case -EHOSTUNREACH: + case -ENOTCONN: + case 0: + DEBUG("gcoap: DTLS sock not connected or remote unreachable. " + "Destroying session.\n"); + dsm_remove(sock->socket.dtls, &sock->ctx_dtls_session); + sock_dtls_session_destroy(sock->socket.dtls, &sock->ctx_dtls_session); + break; + default: + /* Temporary error. Keeping the DTLS session */ + break; + } break; +#endif default: - /* Temporary error. Keeping the DTLS session */ + DEBUG("gcoap: undefined socket type\n"); break; - } -#endif - } else if (sock->type == GCOAP_SOCKET_TYPE_UDP) { - res = sock_udp_send(sock->socket.udp, data, len, remote); - } else { - DEBUG("gcoap: undefined socket type\n"); } - return res; } @@ -986,6 +1005,9 @@ static ssize_t _tl_authenticate(gcoap_socket_t *sock, const sock_udp_ep_t *remot #else int res; + if (sock->type != GCOAP_SOCKET_TYPE_DTLS) { + return 0; + } /* prepare session */ sock_dtls_session_set_udp_ep(&sock->ctx_dtls_session, remote); dsm_state_t session_state = dsm_store(sock->socket.dtls, &sock->ctx_dtls_session, @@ -1111,16 +1133,23 @@ int gcoap_req_init_path_buffer(coap_pkt_t *pdu, uint8_t *buf, size_t len, return (res > 0) ? 0 : res; } -ssize_t gcoap_req_send(const uint8_t *buf, size_t len, - const sock_udp_ep_t *remote, - gcoap_resp_handler_t resp_handler, void *context) +ssize_t gcoap_req_send_tl(const uint8_t *buf, size_t len, + const sock_udp_ep_t *remote, + gcoap_resp_handler_t resp_handler, void *context, + gcoap_socket_type_t tl_type) { + gcoap_socket_t socket = { 0 }; gcoap_request_memo_t *memo = NULL; unsigned msg_type = (*buf & 0x30) >> 4; uint32_t timeout = 0; + ssize_t res = 0; assert(remote != NULL); + res = _tl_init_coap_socket(&socket, tl_type); + if (res < 0) { + return -EINVAL; + } /* Only allocate memory if necessary (i.e. if user is interested in the * response or request is confirmable) */ if ((resp_handler != NULL) || (msg_type == COAP_TYPE_CON)) { @@ -1142,6 +1171,7 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len, memo->resp_handler = resp_handler; memo->context = context; memcpy(&memo->remote_ep, remote, sizeof(sock_udp_ep_t)); + memo->socket = socket; switch (msg_type) { case COAP_TYPE_CON: @@ -1186,10 +1216,7 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len, } } - ssize_t res = 0; - gcoap_socket_t socket = { 0 }; - - _tl_init_coap_socket(&socket); + _tl_init_coap_socket(&socket, tl_type); if (IS_USED(MODULE_GCOAP_DTLS) && socket.type == GCOAP_SOCKET_TYPE_DTLS) { res = _tl_authenticate(&socket, remote, CONFIG_GCOAP_DTLS_HANDSHAKE_TIMEOUT_MSEC); } @@ -1283,12 +1310,10 @@ size_t gcoap_obs_send(const uint8_t *buf, size_t len, const coap_resource_t *resource) { gcoap_observe_memo_t *memo = NULL; - gcoap_socket_t socket; - _tl_init_coap_socket(&socket); _find_obs_memo_resource(&memo, resource); if (memo) { - ssize_t bytes = _tl_send(&socket, buf, len, memo->observer); + ssize_t bytes = _tl_send(&memo->socket, buf, len, memo->observer); return (size_t)((bytes > 0) ? bytes : 0); } else { From 287bfdc10dd6b79e0ed0a00032c6e14f49f2af0e Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Wed, 28 Jul 2021 16:41:43 +0200 Subject: [PATCH 2/2] gcoap: multi-transport support for listeners --- examples/gcoap/server.c | 1 + sys/include/net/gcoap.h | 108 +++++++++++++++++------- sys/include/net/nanocoap.h | 7 ++ sys/net/application_layer/gcoap/gcoap.c | 62 ++++++++++---- 4 files changed, 129 insertions(+), 49 deletions(-) diff --git a/examples/gcoap/server.c b/examples/gcoap/server.c index 54b065b8a287..1bc3239db801 100644 --- a/examples/gcoap/server.c +++ b/examples/gcoap/server.c @@ -75,6 +75,7 @@ static const char *_link_params[] = { static gcoap_listener_t _listener = { &_resources[0], ARRAY_SIZE(_resources), + GCOAP_SOCKET_TYPE_UNDEF, _encode_link, NULL, NULL diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index f164c63bd661..331e9c01dd78 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -704,6 +704,36 @@ typedef int (*gcoap_request_matcher_t)(gcoap_listener_t *listener, const coap_resource_t **resource, coap_pkt_t *pdu); +/** + * @brief CoAP socket types + * + * May be used as flags for @ref gcoap_listener_t, but must be used numerically + * with @ref gcoap_req_send_tl(). + */ +typedef enum { + GCOAP_SOCKET_TYPE_UNDEF = 0x0, /**< undefined */ + GCOAP_SOCKET_TYPE_UDP = 0x1, /**< Unencrypted UDP transport */ + GCOAP_SOCKET_TYPE_DTLS = 0x2, /**< DTLS-over-UDP transport */ +} gcoap_socket_type_t; + +/** + * @brief CoAP socket to handle multiple transport types + */ +typedef struct { + gcoap_socket_type_t type; /**< Type of stored socket */ + union { + sock_udp_t *udp; +#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) + sock_dtls_t *dtls; +#endif + } socket; /**< Stored socket */ +#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) + sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket. + Used for exchanging a session between + functions. */ +#endif +} gcoap_socket_t; + /** * @brief A modular collection of resources for a server */ @@ -711,6 +741,16 @@ struct gcoap_listener { const coap_resource_t *resources; /**< First element in the array of * resources; must order alphabetically */ size_t resources_len; /**< Length of array */ + /** + * @brief Transport type for the listener + * + * Any transport supported by the implementation can be set as a flag. + * If @ref GCOAP_SOCKET_TYPE_UNDEF is set, the listener listens on all + * supported transports. If non of the transports beyond UDP are compiled in + * (i.e. no usage of modules `gcoap_dtls`, ...) this will be ignored and + * @ref GCOAP_SOCKET_TYPE_UDP assumed. + */ + gcoap_socket_type_t tl_type; gcoap_link_encoder_t link_encoder; /**< Writes a link for a resource */ struct gcoap_listener *next; /**< Next listener in list */ @@ -750,33 +790,6 @@ typedef struct { size_t pdu_len; /**< Length of pdu_buf */ } gcoap_resend_t; -/** - * @brief Coap socket types - */ -typedef enum { - GCOAP_SOCKET_TYPE_UNDEF = 0, - GCOAP_SOCKET_TYPE_UDP, - GCOAP_SOCKET_TYPE_DTLS -} gcoap_socket_type_t; - -/** - * @brief Coap socket to handle multiple transport types - */ -typedef struct { - gcoap_socket_type_t type; /**< Type of stored socket */ - union { - sock_udp_t *udp; -#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) - sock_dtls_t *dtls; -#endif - } socket; /**< Stored socket */ -#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN) - sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket. - Used for exchanging a session between - functions. */ -#endif -} gcoap_socket_t; - /** * @brief Memo to handle a response for a request */ @@ -918,8 +931,9 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len, * @param[in] resp_handler Callback when response received, may be NULL * @param[in] context User defined context passed to the response handler * @param[in] tl_type The transport type to use for send. When - * GCOAP_SOCKET_TYPE_UNDEF is selected, the highest - * available (by value) will be selected. + * @ref GCOAP_SOCKET_TYPE_UNDEF is selected, the highest + * available (by value) will be selected. Only single + * types are allowed, not a combination of them. * * @return length of the packet * @return -ENOTCONN, if DTLS was used and session establishment failed @@ -940,7 +954,7 @@ ssize_t gcoap_req_send_tl(const uint8_t *buf, size_t len, * @param[in] resp_handler Callback when response received, may be NULL * @param[in] context User defined context passed to the response handler * - * @note The highest supported (by value) coap_socket_type_t will be selected + * @note The highest supported (by value) gcoap_socket_type_t will be selected * as transport type. * * @return length of the packet @@ -1037,6 +1051,35 @@ uint8_t gcoap_op_state(void); * @brief Get the resource list, currently only `CoRE Link Format` * (COAP_FORMAT_LINK) supported * + * @deprecated Will be an alias for @ref gcoap_get_resource_list after the + * 2022.01 release. Will be removed after the 2022.04 release. + * + * If @p buf := NULL, nothing will be written but the size of the resulting + * resource list is computed and returned. + * + * @param[out] buf output buffer to write resource list into, my be NULL + * @param[in] maxlen length of @p buf, ignored if @p buf is NULL + * @param[in] cf content format to use for the resource list, currently + * only COAP_FORMAT_LINK supported + * @param[in] tl_type Transport type to get the list for. + * @ref GCOAP_SOCKET_TYPE_UNDEF for all transport types. + * If non of the transports beyond UDP are compiled in + * (i.e. usage of modules no `gcoap_dtls`, ...) this will + * be ignored and @ref GCOAP_SOCKET_TYPE_UDP assumed. + * + * @todo add support for `JSON CoRE Link Format` + * @todo add support for 'CBOR CoRE Link Format` + * + * @return the number of bytes written to @p buf + * @return -1 on error + */ +int gcoap_get_resource_list_tl(void *buf, size_t maxlen, uint8_t cf, + gcoap_socket_type_t tl_type); + +/** + * @brief Get the resource list for all transports, + * currently only `CoRE Link Format` (COAP_FORMAT_LINK) supported + * * If @p buf := NULL, nothing will be written but the size of the resulting * resource list is computed and returned. * @@ -1051,7 +1094,10 @@ uint8_t gcoap_op_state(void); * @return the number of bytes written to @p buf * @return -1 on error */ -int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf); +static inline int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf) +{ + return gcoap_get_resource_list_tl(buf, maxlen, cf, GCOAP_SOCKET_TYPE_UNDEF); +} /** * @brief Writes a resource in CoRE Link Format to a provided buffer. diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 057103bb550d..11e23d340435 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -208,6 +208,13 @@ typedef struct { BITFIELD(opt_crit, CONFIG_NANOCOAP_NOPTS_MAX); /**< unhandled critical option */ #ifdef MODULE_GCOAP uint32_t observe_value; /**< observe value */ + /** + * @brief transport the packet was received over + * @see @ref gcoap_socket_type_t for values. + * @note @ref gcoap_socket_type_t can not be used, as this would + * cyclically include the @ref net_gcoap header. + */ + uint32_t tl_type; #endif } coap_pkt_t; diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 3351982f2160..88940b367d28 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -66,7 +66,8 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf, static void _expire_request(gcoap_request_memo_t *memo); static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *pdu, const sock_udp_ep_t *remote, bool by_mid); -static int _find_resource(coap_pkt_t *pdu, +static int _find_resource(gcoap_socket_type_t tl_type, + coap_pkt_t *pdu, const coap_resource_t **resource_ptr, gcoap_listener_t **listener_ptr); static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote); @@ -92,6 +93,7 @@ const coap_resource_t _default_resources[] = { static gcoap_listener_t _default_listener = { &_default_resources[0], ARRAY_SIZE(_default_resources), + GCOAP_SOCKET_TYPE_UNDEF, NULL, NULL, _request_matcher_default @@ -130,7 +132,7 @@ static sock_udp_t _sock_udp; #if IS_USED(MODULE_GCOAP_DTLS) /* DTLS variables and definitions */ #define SOCK_DTLS_CLIENT_TAG (2) - +static sock_udp_t _sock_dtls_base; static sock_dtls_t _sock_dtls; static kernel_pid_t _auth_waiting_thread; @@ -147,11 +149,7 @@ static void *_event_loop(void *arg) memset(&local, 0, sizeof(sock_udp_ep_t)); local.family = AF_INET6; local.netif = SOCK_ADDR_ANY_NETIF; - if (IS_USED(MODULE_GCOAP_DTLS)) { - local.port = CONFIG_GCOAPS_PORT; - } else { - local.port = CONFIG_GCOAP_PORT; - } + local.port = CONFIG_GCOAP_PORT; int res = sock_udp_create(&_sock_udp, &local, NULL, 0); if (res < 0) { DEBUG("gcoap: cannot create sock: %d\n", res); @@ -159,20 +157,25 @@ static void *_event_loop(void *arg) } event_queue_init(&_queue); + sock_udp_event_init(&_sock_udp, &_queue, _on_sock_udp_evt, NULL); + if (IS_USED(MODULE_GCOAP_DTLS)) { #if IS_USED(MODULE_GCOAP_DTLS) - if (sock_dtls_create(&_sock_dtls, &_sock_udp, + local.port = CONFIG_GCOAPS_PORT; + if (sock_udp_create(&_sock_dtls_base, &local, NULL, 0)) { + DEBUG("gcoap: error creating DTLS transport sock\n"); + return 0; + } + if (sock_dtls_create(&_sock_dtls, &_sock_dtls_base, CREDMAN_TAG_EMPTY, SOCK_DTLS_1_2, SOCK_DTLS_SERVER) < 0) { - DEBUG("gcoap: error creating DTLS sock"); - sock_udp_close(&_sock_udp); + DEBUG("gcoap: error creating DTLS sock\n"); + sock_udp_close(&_sock_dtls_base); return 0; } sock_dtls_event_init(&_sock_dtls, &_queue, _on_sock_dtls_evt, NULL); #endif - } else { - sock_udp_event_init(&_sock_udp, &_queue, _on_sock_udp_evt, NULL); } event_loop(&_queue); @@ -537,7 +540,7 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf, gcoap_observe_memo_t *memo = NULL; gcoap_observe_memo_t *resource_memo = NULL; - switch (_find_resource(pdu, &resource, &listener)) { + switch (_find_resource(sock->type, pdu, &resource, &listener)) { case GCOAP_RESOURCE_WRONG_METHOD: return gcoap_response(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED); case GCOAP_RESOURCE_NO_PATH: @@ -631,6 +634,7 @@ static size_t _handle_req(gcoap_socket_t *sock, coap_pkt_t *pdu, uint8_t *buf, return -1; } + pdu->tl_type = (uint32_t)sock->type; ssize_t pdu_len = resource->handler(pdu, buf, len, resource->context); if (pdu_len < 0) { pdu_len = gcoap_response(pdu, buf, len, @@ -689,6 +693,7 @@ static int _request_matcher_default(gcoap_listener_t *listener, /* * Searches listener registrations for the resource matching the path in a PDU. * + * param[in] tl_type -- transport the request for the resource came over. * param[in] pdu -- the PDU to check the resource for * param[out] resource_ptr -- found resource * param[out] listener_ptr -- listener for found resource @@ -697,7 +702,8 @@ static int _request_matcher_default(gcoap_listener_t *listener, * code didn't match and `GCOAP_RESOURCE_NO_PATH` if no matching * resource was found. */ -static int _find_resource(coap_pkt_t *pdu, +static int _find_resource(gcoap_socket_type_t tl_type, + coap_pkt_t *pdu, const coap_resource_t **resource_ptr, gcoap_listener_t **listener_ptr) { @@ -708,8 +714,17 @@ static int _find_resource(coap_pkt_t *pdu, while (listener) { const coap_resource_t *resource; - int res = listener->request_matcher(listener, &resource, pdu); + int res; + /* only makes sense to check if non-UDP transports are supported, + * so check if module is used first. */ + if (IS_USED(MODULE_GCOAP_DTLS) && + (listener->tl_type != GCOAP_SOCKET_TYPE_UNDEF) && + !(listener->tl_type & tl_type)) { + listener = listener->next; + continue; + } + res = listener->request_matcher(listener, &resource, pdu); /* check next resource on mismatch */ if (res == GCOAP_RESOURCE_NO_PATH) { listener = listener->next; @@ -826,8 +841,9 @@ static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t le coap_opt_add_format(pdu, COAP_FORMAT_LINK); ssize_t plen = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); - plen += gcoap_get_resource_list(pdu->payload, (size_t)pdu->payload_len, - COAP_FORMAT_LINK); + plen += gcoap_get_resource_list_tl(pdu->payload, (size_t)pdu->payload_len, + COAP_FORMAT_LINK, + (gcoap_socket_type_t)pdu->tl_type); return plen; } @@ -1332,7 +1348,8 @@ uint8_t gcoap_op_state(void) return count; } -int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf) +int gcoap_get_resource_list_tl(void *buf, size_t maxlen, uint8_t cf, + gcoap_socket_type_t tl_type) { assert(cf == COAP_FORMAT_LINK); @@ -1351,6 +1368,15 @@ int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf) if (!listener->link_encoder) { continue; } + /* only makes sense to check if non-UDP transports are supported, + * so check if module is used first. */ + if (IS_USED(MODULE_GCOAP_DTLS) && + (tl_type != GCOAP_SOCKET_TYPE_UNDEF) && + (listener->tl_type != GCOAP_SOCKET_TYPE_UNDEF) && + ((listener->tl_type & GCOAP_SOCKET_TYPE_UDP) != (tl_type & GCOAP_SOCKET_TYPE_UDP)) && + ((listener->tl_type & GCOAP_SOCKET_TYPE_DTLS) != (tl_type & GCOAP_SOCKET_TYPE_DTLS))) { + continue; + } ctx.link_pos = 0; for (; ctx.link_pos < listener->resources_len; ctx.link_pos++) {