1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

nanocoap: check if all critical options were handled

This commit is contained in:
Benjamin Valentin 2022-01-25 17:35:42 +01:00
parent 2682848a29
commit 7dc2f730d2
5 changed files with 85 additions and 20 deletions

View File

@ -702,7 +702,7 @@ typedef struct gcoap_listener gcoap_listener_t;
*/
typedef int (*gcoap_request_matcher_t)(gcoap_listener_t *listener,
const coap_resource_t **resource,
const coap_pkt_t *pdu);
coap_pkt_t *pdu);
/**
* @brief A modular collection of resources for a server

View File

@ -86,6 +86,7 @@
#include <unistd.h>
#ifdef RIOT_VERSION
#include "bitfield.h"
#include "byteorder.h"
#include "net/coap.h"
#else
@ -204,6 +205,7 @@ typedef struct {
uint16_t payload_len; /**< length of payload */
uint16_t options_len; /**< length of options array */
coap_optpos_t options[CONFIG_NANOCOAP_NOPTS_MAX]; /**< option offset array */
BITFIELD(opt_crit, CONFIG_NANOCOAP_NOPTS_MAX); /**< unhandled critical option */
#ifdef MODULE_GCOAP
uint32_t observe_value; /**< observe value */
#endif
@ -462,8 +464,30 @@ static inline void coap_hdr_set_type(coap_hdr_t *hdr, unsigned type)
* @name Functions -- Options Read
*
* Read options from a parsed packet.
*
* Packets accessed through @ref coap_find_option or any of the `coap_opt_get_*` functions
* track their access in bit field created at parsing time to enable checking for critical
* options in @ref coap_has_unprocessed_critical_options.
* These functions thus have a side effect, and code that calls them on critical options
* needs to ensure that failure to process the accessed option is propagated into failure
* to process the message.
* For example, a server helper that tries to read the If-None-Match option (which is critical)
* and finds it to be longer than it can process must not return as if no If-None-Match option
* was present, as it has already triggered the side effect of marking the option as processed.
*/
/**@{*/
/**
* @brief Get pointer to an option field by type
*
* @param[in] pkt packet to work on
* @param[in] opt_num the option number to search for
*
* @returns pointer to the option data
* NULL if option number was not found
*/
uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num);
/**
* @brief Get content type from packet
*
@ -486,7 +510,7 @@ unsigned coap_get_content_type(coap_pkt_t *pkt);
* @return -ENOSPC if option length is greater than 4 octets
* @return -EBADMSG if option value is invalid
*/
int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t optnum, uint32_t *value);
int coap_opt_get_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t *value);
/**
* @brief Read a full option as null terminated string into the target buffer
@ -505,7 +529,7 @@ int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t optnum, uint32_t *value);
* @return -ENOSPC if the complete option does not fit into @p target
* @return nr of bytes written to @p target (including '\0')
*/
ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
ssize_t coap_opt_get_string(coap_pkt_t *pkt, uint16_t optnum,
uint8_t *target, size_t max_len, char separator);
/**
@ -523,7 +547,7 @@ ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
* @returns -ENOSPC if URI option is larger than @p max_len
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_location_path(const coap_pkt_t *pkt,
static inline ssize_t coap_get_location_path(coap_pkt_t *pkt,
uint8_t *target, size_t max_len)
{
return coap_opt_get_string(pkt, COAP_OPT_LOCATION_PATH,
@ -545,7 +569,7 @@ static inline ssize_t coap_get_location_path(const coap_pkt_t *pkt,
* @returns -ENOSPC if URI option is larger than @p max_len
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_location_query(const coap_pkt_t *pkt,
static inline ssize_t coap_get_location_query(coap_pkt_t *pkt,
uint8_t *target, size_t max_len)
{
return coap_opt_get_string(pkt, COAP_OPT_LOCATION_QUERY,
@ -566,7 +590,7 @@ static inline ssize_t coap_get_location_query(const coap_pkt_t *pkt,
* @returns -ENOSPC if URI option is larger than CONFIG_NANOCOAP_URI_MAX
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_uri_path(const coap_pkt_t *pkt, uint8_t *target)
static inline ssize_t coap_get_uri_path(coap_pkt_t *pkt, uint8_t *target)
{
return coap_opt_get_string(pkt, COAP_OPT_URI_PATH, target,
CONFIG_NANOCOAP_URI_MAX, '/');
@ -586,7 +610,7 @@ static inline ssize_t coap_get_uri_path(const coap_pkt_t *pkt, uint8_t *target)
* @returns -ENOSPC if URI option is larger than CONFIG_NANOCOAP_URI_MAX
* @returns nr of bytes written to @p target (including '\0')
*/
static inline ssize_t coap_get_uri_query(const coap_pkt_t *pkt, uint8_t *target)
static inline ssize_t coap_get_uri_query(coap_pkt_t *pkt, uint8_t *target)
{
return coap_opt_get_string(pkt, COAP_OPT_URI_QUERY, target,
CONFIG_NANOCOAP_URI_MAX, '&');
@ -640,7 +664,7 @@ ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt,
* @return -ENOENT if option not found
* @return -EINVAL if option cannot be parsed
*/
ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **value);
ssize_t coap_opt_get_opaque(coap_pkt_t *pkt, unsigned opt_num, uint8_t **value);
/**@}*/
/**
@ -655,7 +679,7 @@ ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **v
* @return -ENOENT if Proxy-Uri option not found
* @return -EINVAL if Proxy-Uri option cannot be parsed
*/
static inline ssize_t coap_get_proxy_uri(const coap_pkt_t *pkt, char **target)
static inline ssize_t coap_get_proxy_uri(coap_pkt_t *pkt, char **target)
{
return coap_opt_get_opaque(pkt, COAP_OPT_PROXY_URI, (uint8_t **)target);
}
@ -860,6 +884,25 @@ static inline int coap_get_block2(coap_pkt_t *pkt, coap_block1_t *block)
*/
int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsigned *szx);
/**
* @brief Check whether any of the packet's options that are critical
*
* (i.e must be understood by the receiver, indicated by a 1 in the option number's least
* significant bit) were not accessed since the packet was parsed.)
*
* Call this in a server on requests after all their option processing has happened,
* and stop processing the request if it returns true, returning a 4.02 Bad Option response.
*
* Call this in a client when receiving a response before acting on it;
* consider the response unprocessable if it returns true.
*
* @param[in] pkt pkt to work on
*
* @returns true if any of the options marked as critical at parse time have not been accessed.
* @returns false if there are no critical options, or all have been accessed.
*/
bool coap_has_unprocessed_critical_options(const coap_pkt_t *pkt);
/**
* @brief Helper to decode SZX value to size in bytes
*

View File

@ -66,7 +66,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
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(const coap_pkt_t *pdu,
static int _find_resource(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);
@ -77,7 +77,7 @@ static void _find_obs_memo_resource(gcoap_observe_memo_t **memo,
static int _request_matcher_default(gcoap_listener_t *listener,
const coap_resource_t **resource,
const coap_pkt_t *pdu);
coap_pkt_t *pdu);
#if IS_USED(MODULE_GCOAP_DTLS)
static void _on_sock_dtls_evt(sock_dtls_t *sock, sock_async_flags_t type, void *arg);
@ -538,7 +538,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
gcoap_observe_memo_t *memo = NULL;
gcoap_observe_memo_t *resource_memo = NULL;
switch (_find_resource((const coap_pkt_t *)pdu, &resource, &listener)) {
switch (_find_resource(pdu, &resource, &listener)) {
case GCOAP_RESOURCE_WRONG_METHOD:
return gcoap_response(pdu, buf, len, COAP_CODE_METHOD_NOT_ALLOWED);
case GCOAP_RESOURCE_NO_PATH:
@ -640,7 +640,7 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
static int _request_matcher_default(gcoap_listener_t *listener,
const coap_resource_t **resource,
const coap_pkt_t *pdu)
coap_pkt_t *pdu)
{
uint8_t uri[CONFIG_NANOCOAP_URI_MAX];
int ret = GCOAP_RESOURCE_NO_PATH;
@ -696,7 +696,7 @@ 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(const coap_pkt_t *pdu,
static int _find_resource(coap_pkt_t *pdu,
const coap_resource_t **resource_ptr,
gcoap_listener_t **listener_ptr)
{

View File

@ -68,6 +68,7 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
pkt->payload = NULL;
pkt->payload_len = 0;
memset(pkt->opt_crit, 0, sizeof(pkt->opt_crit));
if (len < sizeof(coap_hdr_t)) {
DEBUG("msg too short\n");
@ -126,6 +127,10 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
return -ENOMEM;
}
/* check if option is critical */
if (option_nr & 1) {
bf_set(pkt->opt_crit, option_count);
}
optpos->opt_num = option_nr;
optpos->offset = (uintptr_t)option_start - (uintptr_t)hdr;
DEBUG("optpos option_nr=%u %u\n", (unsigned)option_nr, (unsigned)optpos->offset);
@ -176,13 +181,15 @@ int coap_match_path(const coap_resource_t *resource, uint8_t *uri)
return res;
}
uint8_t *coap_find_option(const coap_pkt_t *pkt, unsigned opt_num)
uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num)
{
const coap_optpos_t *optpos = pkt->options;
unsigned opt_count = pkt->options_len;
while (opt_count--) {
if (optpos->opt_num == opt_num) {
unsigned idx = index_of(pkt->options, optpos);
bf_unset(pkt->opt_crit, idx);
return (uint8_t*)pkt->hdr + optpos->offset;
}
optpos++;
@ -219,7 +226,7 @@ static uint8_t *_parse_option(const coap_pkt_t *pkt,
return pkt_pos;
}
ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **value)
ssize_t coap_opt_get_opaque(coap_pkt_t *pkt, unsigned opt_num, uint8_t **value)
{
uint8_t *start = coap_find_option(pkt, opt_num);
if (!start) {
@ -237,7 +244,7 @@ ssize_t coap_opt_get_opaque(const coap_pkt_t *pkt, unsigned opt_num, uint8_t **v
return len;
}
int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t opt_num, uint32_t *target)
int coap_opt_get_uint(coap_pkt_t *pkt, uint16_t opt_num, uint32_t *target)
{
assert(target);
@ -262,7 +269,7 @@ int coap_opt_get_uint(const coap_pkt_t *pkt, uint16_t opt_num, uint32_t *target)
return -ENOENT;
}
uint8_t *coap_iterate_option(const coap_pkt_t *pkt, uint8_t **optpos,
uint8_t *coap_iterate_option(coap_pkt_t *pkt, uint8_t **optpos,
int *opt_len, int first)
{
uint8_t *data_start;
@ -325,7 +332,7 @@ ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt,
return len;
}
ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
ssize_t coap_opt_get_string(coap_pkt_t *pkt, uint16_t optnum,
uint8_t *target, size_t max_len, char separator)
{
assert(pkt && target && (max_len > 1));
@ -392,6 +399,17 @@ int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsign
return (blkopt & 0x8) ? 1 : 0;
}
bool coap_has_unprocessed_critical_options(const coap_pkt_t *pkt)
{
for (unsigned i = 0; i < sizeof(pkt->opt_crit); ++i){
if (pkt->opt_crit[i]) {
return true;
}
}
return false;
}
ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_len)
{
if (coap_get_code_class(pkt) != COAP_REQ) {

View File

@ -188,12 +188,16 @@ int nanocoap_get_blockwise(sock_udp_t *sock, const char *path,
return -1;
}
/* no block option in response - direcly use paylaod */
/* no block option in response - directly use paylaod */
if (!coap_get_block2(&pkt, &block2)) {
block2.more = 0;
block2.offset = 0;
}
if (coap_has_unprocessed_critical_options(&pkt)) {
return -ENOTSUP;
}
res = callback(arg, block2.offset, pkt.payload, pkt.payload_len,
block2.more);
if (res) {