From ba788b532c20f1fa23b2c81387a145afe83a4951 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 24 Apr 2019 12:10:33 +0200 Subject: [PATCH 1/7] nanocoap: Allow access to arbitrary options The coap_opt_by_index function is added to allow reading and modifying the options written to a message. This is the reading counterpart to a5146098. --- sys/include/net/nanocoap.h | 21 +++++++++++++++++++ sys/net/application_layer/nanocoap/nanocoap.c | 15 +++++++++++++ 2 files changed, 36 insertions(+) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 9313a4f37a9d..286e8ba92746 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -748,6 +748,27 @@ size_t coap_opt_put_block2(uint8_t *buf, uint16_t lastonum, coap_block_slicer_t */ unsigned coap_get_content_type(coap_pkt_t *pkt); +/** + * @brief Access an option by its struct offset + * + * This function is for accessing arbitrary options in a message that was + * received, or was built using the struct-based API. It is useful for reading + * opaque options, updating values after they have been added (e.g. putting a + * value into a pre-allocated ETag after the payload was calculate), and when + * iterating over a message's options. + * + * It is up to the caller to ensure that optnum is valid, i.e. that `0 <= optnum + * < pkt->options_len`. + * + * @param[in] pkt packet to read from + * @param[in] index absolute option number + * @param[out] start pointer to the start of the option value + * + * @return number of bytes available at @p start + */ +size_t coap_opt_by_index(const coap_pkt_t *pkt, uint16_t index, + uint8_t **start); + /** * @brief Read a full option as null terminated string into the target buffer * diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index b1d2085c88c5..b763a0b869dd 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -256,6 +256,21 @@ unsigned coap_get_content_type(coap_pkt_t *pkt) return content_type; } +size_t coap_opt_by_index(const coap_pkt_t *pkt, uint16_t index, + uint8_t **start) +{ + assert(pkt && index < pkt->options_len); + + const coap_optpos_t *optslot = &pkt->options[index]; + uint8_t *opt_pos = (uint8_t*)pkt->hdr + optslot->offset; + int opt_len; + uint16_t delta; + + *start = _parse_option(pkt, opt_pos, &delta, &opt_len); + + return opt_len; +} + ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum, uint8_t *target, size_t max_len, char separator) { From 3e07d77b7b7eb0f18f28c90526af6e104d5faf9a Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 24 Apr 2019 15:33:15 +0200 Subject: [PATCH 2/7] nanocoap: Add test for coap_opt_by_index --- tests/nanocoap_cli/README.md | 3 +++ tests/nanocoap_cli/request_handlers.c | 34 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/tests/nanocoap_cli/README.md b/tests/nanocoap_cli/README.md index 27f9d1aa4824..b815c9678dcd 100644 --- a/tests/nanocoap_cli/README.md +++ b/tests/nanocoap_cli/README.md @@ -10,6 +10,9 @@ which to listen. Provides these resources: `/value`
Reads/Writes an unsigned 8-bit integer. +`/req-opts`
+Hex-dumps all options in the request message into a response. + `/.well-known/core`
Reads the list of resources. Expects a block2 based request or else returns at most the first 16 bytes of the list. diff --git a/tests/nanocoap_cli/request_handlers.c b/tests/nanocoap_cli/request_handlers.c index f8686daf7ec3..45d5009fd552 100644 --- a/tests/nanocoap_cli/request_handlers.c +++ b/tests/nanocoap_cli/request_handlers.c @@ -71,9 +71,43 @@ static ssize_t _value_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *c COAP_FORMAT_TEXT, (uint8_t*)rsp, p); } +static ssize_t _reqopts_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context) +{ + (void) context; + + ssize_t p = 0; + char rsp[128]; + + for (uint16_t i = 0; i < pkt->options_len; ++i) { + uint16_t optnum = pkt->options[i].opt_num; + p += snprintf(&rsp[p], sizeof(rsp) - p, "%d: ", optnum); + if ((size_t)p >= sizeof(rsp)) + goto error; + uint8_t *optcursor; + size_t length = coap_opt_by_index(pkt, i, &optcursor); + for (size_t j = 0; j < length; ++j) { + p += snprintf(&rsp[p], sizeof(rsp) - p, "%02x", optcursor[j]); + if ((size_t)p >= sizeof(rsp)) + goto error; + } + p += snprintf(&rsp[p], sizeof(rsp) - p, "\n"); + if ((size_t)p >= sizeof(rsp)) + goto error; + } + + return coap_reply_simple(pkt, COAP_CODE_205, buf, len, + COAP_FORMAT_TEXT, (uint8_t*)rsp, p); + +error: + rsp[sizeof(rsp) - 1] = '_'; + return coap_reply_simple(pkt, COAP_CODE_205, buf, len, + COAP_FORMAT_TEXT, (uint8_t*)rsp, sizeof(rsp)); +} + /* must be sorted by path (ASCII order) */ const coap_resource_t coap_resources[] = { COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER, + { "/req-opts", COAP_GET, _reqopts_handler, NULL }, { "/value", COAP_GET | COAP_PUT | COAP_POST, _value_handler, NULL }, }; From e32d16a88c1a9038bbbf12f92c52e6a6da90557d Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 14 May 2019 20:26:30 +0200 Subject: [PATCH 3/7] nanocoap: const-ify the coap_get_token_len helper --- sys/include/net/nanocoap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 286e8ba92746..f825128c8337 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -996,7 +996,7 @@ static inline unsigned coap_get_type(coap_pkt_t *pkt) * * @returns length of token in the given message (0-8 byte) */ -static inline unsigned coap_get_token_len(coap_pkt_t *pkt) +static inline unsigned coap_get_token_len(const coap_pkt_t *pkt) { return (pkt->hdr->ver_t_tkl & 0xf); } From 2f82da4ef6c2eba363ac477dc89bb48f5b695d5c Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 14 May 2019 20:35:39 +0200 Subject: [PATCH 4/7] nanocoap: replace coap_opt_by_index with coap_opt_get_next See https://github.com/RIOT-OS/RIOT/pull/11437, to be squashed before merging. --- sys/include/net/nanocoap.h | 29 +++++++++++-------- sys/net/application_layer/nanocoap/nanocoap.c | 15 ++++++---- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index f825128c8337..f22c72b68e7e 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -749,24 +749,29 @@ size_t coap_opt_put_block2(uint8_t *buf, uint16_t lastonum, coap_block_slicer_t unsigned coap_get_content_type(coap_pkt_t *pkt); /** - * @brief Access an option by its struct offset + * @brief Iterate over a packet's options * - * This function is for accessing arbitrary options in a message that was - * received, or was built using the struct-based API. It is useful for reading - * opaque options, updating values after they have been added (e.g. putting a - * value into a pre-allocated ETag after the payload was calculate), and when - * iterating over a message's options. + * This function is for accessing arbitrary options in a message in sequence. + * It is useful for reading opaque options, updating values after they have + * been added (e.g. putting a value into a pre-allocated ETag after the + * payload was calculate), and when iterating over a message's options. * - * It is up to the caller to ensure that optnum is valid, i.e. that `0 <= optnum - * < pkt->options_len`. + * The caller must provide a zeroed-out *opt to start iterating, and pass the + * same opt to subsequent calls. The function will, on each invocation, set + * *start to point to the beginning of the next option, and return the + * to-be-read length. The caller can read that option's number from + * opt.opt_num, and must ignore opt.offset. * - * @param[in] pkt packet to read from - * @param[in] index absolute option number - * @param[out] start pointer to the start of the option value + * The end of the options is indicated by *start having a value of NULL; the + * return value is unspecified in that case. + * + * @param[in] pkt packet to read from + * @param[in,out] opt iteration counter + * @param[out] start pointer to the start of the option value * * @return number of bytes available at @p start */ -size_t coap_opt_by_index(const coap_pkt_t *pkt, uint16_t index, +size_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt, uint8_t **start); /** diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index b763a0b869dd..508b24a6c49e 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -256,19 +256,22 @@ unsigned coap_get_content_type(coap_pkt_t *pkt) return content_type; } -size_t coap_opt_by_index(const coap_pkt_t *pkt, uint16_t index, +size_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt, uint8_t **start) { - assert(pkt && index < pkt->options_len); - - const coap_optpos_t *optslot = &pkt->options[index]; - uint8_t *opt_pos = (uint8_t*)pkt->hdr + optslot->offset; + uint8_t *opt_pos = (uint8_t*)pkt->hdr + (opt->offset == 0 ? sizeof(coap_hdr_t) + coap_get_token_len(pkt) : opt->offset); int opt_len; uint16_t delta; *start = _parse_option(pkt, opt_pos, &delta, &opt_len); - return opt_len; + if (start != NULL) { + opt->opt_num += delta; + opt->offset = *start + opt_len - (uint8_t*)pkt->hdr; + return opt_len; + } else { + return 0; + } } ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum, From 4db18ba5732b2a86f7820f0198e4a122201fb805 Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 14 May 2019 20:40:33 +0200 Subject: [PATCH 5/7] nanocoap: adapt cli example To be squashed before merging. --- tests/nanocoap_cli/request_handlers.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/nanocoap_cli/request_handlers.c b/tests/nanocoap_cli/request_handlers.c index 45d5009fd552..8fcae4a7386a 100644 --- a/tests/nanocoap_cli/request_handlers.c +++ b/tests/nanocoap_cli/request_handlers.c @@ -78,15 +78,21 @@ static ssize_t _reqopts_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void ssize_t p = 0; char rsp[128]; - for (uint16_t i = 0; i < pkt->options_len; ++i) { - uint16_t optnum = pkt->options[i].opt_num; - p += snprintf(&rsp[p], sizeof(rsp) - p, "%d: ", optnum); + uint8_t *start; + size_t length; + coap_optpos_t cursor = {0}; + while (1) { + length = coap_opt_get_next(pkt, &cursor, &start); + if (start == NULL) { + break; + } + + p += snprintf(&rsp[p], sizeof(rsp) - p, "%d: ", cursor.opt_num); if ((size_t)p >= sizeof(rsp)) goto error; - uint8_t *optcursor; - size_t length = coap_opt_by_index(pkt, i, &optcursor); + for (size_t j = 0; j < length; ++j) { - p += snprintf(&rsp[p], sizeof(rsp) - p, "%02x", optcursor[j]); + p += snprintf(&rsp[p], sizeof(rsp) - p, "%02x", start[j]); if ((size_t)p >= sizeof(rsp)) goto error; } From 690df8f063c6ddbdb50d294be411f2d233dc6b7b Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 28 May 2019 10:41:16 +0200 Subject: [PATCH 6/7] fixup! nanocoap: replace coap_opt_by_index with coap_opt_get_next --- sys/net/application_layer/nanocoap/nanocoap.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 508b24a6c49e..9e558f2c61d1 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -260,16 +260,20 @@ size_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt, uint8_t **start) { uint8_t *opt_pos = (uint8_t*)pkt->hdr + (opt->offset == 0 ? sizeof(coap_hdr_t) + coap_get_token_len(pkt) : opt->offset); - int opt_len; + int opt_len = -1; uint16_t delta; *start = _parse_option(pkt, opt_pos, &delta, &opt_len); - if (start != NULL) { + if (opt_len >= 0) { opt->opt_num += delta; opt->offset = *start + opt_len - (uint8_t*)pkt->hdr; return opt_len; } else { + // Terminal condition that can either be caused by reaching the end of + // the options (if there is no payload marker), or by reaching the + // payload marker + *start = 0; return 0; } } From bd888cc474843aeb5734e109b868be765ba6c02c Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 28 May 2019 10:42:05 +0200 Subject: [PATCH 7/7] fixup! nanocoap: adapt cli example This allows actually testing the error fixed by the previous fixup, as a GET never has a payload but a POST may. --- tests/nanocoap_cli/request_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nanocoap_cli/request_handlers.c b/tests/nanocoap_cli/request_handlers.c index 8fcae4a7386a..c99e9f0c7794 100644 --- a/tests/nanocoap_cli/request_handlers.c +++ b/tests/nanocoap_cli/request_handlers.c @@ -113,7 +113,7 @@ static ssize_t _reqopts_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void /* must be sorted by path (ASCII order) */ const coap_resource_t coap_resources[] = { COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER, - { "/req-opts", COAP_GET, _reqopts_handler, NULL }, + { "/req-opts", COAP_GET | COAP_POST, _reqopts_handler, NULL }, { "/value", COAP_GET | COAP_PUT | COAP_POST, _value_handler, NULL }, };