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:
parent
2682848a29
commit
7dc2f730d2
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user