diff --git a/examples/gcoap/server.c b/examples/gcoap/server.c index 54b065b8a2..1bc3239db8 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 f164c63bd6..331e9c01dd 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 057103bb55..11e23d3404 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 3351982f21..88940b367d 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++) {