From 78ea07d5e752b730f2f07a9d98a17af7f2b521d6 Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Wed, 21 Aug 2019 06:15:30 -0400 Subject: [PATCH 1/4] net/nanocoap: make coap_pkt_t param const --- sys/include/net/nanocoap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 213121fdf34e..415df82314c4 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -325,7 +325,7 @@ static inline unsigned coap_get_id(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); } @@ -337,7 +337,7 @@ static inline unsigned coap_get_token_len(coap_pkt_t *pkt) * * @returns total header length */ -static inline unsigned coap_get_total_hdr_len(coap_pkt_t *pkt) +static inline unsigned coap_get_total_hdr_len(const coap_pkt_t *pkt) { return sizeof(coap_hdr_t) + coap_get_token_len(pkt); } From 6c8e646b839fdc85c4f69c256141c9e70424855f Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Sat, 24 Aug 2019 06:33:56 -0400 Subject: [PATCH 2/4] net/nanocoap: check for payload marker when parse option --- sys/net/application_layer/nanocoap/nanocoap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index e5f23a412658..46c5e3bbcbfa 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -185,14 +185,15 @@ uint8_t *coap_find_option(const coap_pkt_t *pkt, unsigned opt_num) * opt_len[out] length of option value * * return next byte after option header, usually the option value - * return NULL if initial pkt_pos is past options + * return NULL if initial pkt_pos is payload marker or past options */ static uint8_t *_parse_option(const coap_pkt_t *pkt, uint8_t *pkt_pos, uint16_t *delta, int *opt_len) { uint8_t *hdr_end = pkt->payload; - if (pkt_pos == hdr_end) { + if ((pkt_pos >= hdr_end) + || (((pkt_pos + 1) == hdr_end) && (*pkt_pos == 0xFF))) { return NULL; } From 4e89741b7916d7d9f566861a78df88a886c44a46 Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Mon, 15 Jul 2019 00:45:05 -0400 Subject: [PATCH 3/4] net/nanocoap: add function to iterate over options --- sys/include/net/nanocoap.h | 32 +++++++++++++++++++ sys/net/application_layer/nanocoap/nanocoap.c | 24 ++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 415df82314c4..9078d1a5044d 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -530,6 +530,38 @@ static inline ssize_t coap_get_uri_query(const coap_pkt_t *pkt, uint8_t *target) return coap_opt_get_string(pkt, COAP_OPT_URI_QUERY, target, NANOCOAP_URI_MAX, '&'); } + +/** + * @brief Iterate over a packet's options + * + * To start iteration from the first option, set @p init_opt to true. To start + * iteration from a specific option, set @p init_opt to false, set + * @p opt->offset to the offset of the desired option from pkt->hdr, and + * @p opt->opt_num as required. See below for how @p opt->opt_num is modified. + * + * With each invocation, this function returns the length of the option value + * and sets @p value to point to the start of the value. The value for + * @p opt->opt_num is increased by the delta in the option number value over + * the preceding option in the packet. So, @p opt->opt_num is accurate if + * iteration started with the first option. Otherwise, it is useful for + * identification of repeated options. Finally, @p opt->offset is set to the + * offset for any following option, to prepare for the next iteration. + * + * The end of the options is indicated by a -ENOENT return value. In this case + * @p value and @p opt are unchanged from their input values. + * + * @param[in] pkt packet to read from + * @param[in,out] opt option attributes; read on input if @p init_opt + * is false + * @param[out] value start of the option value + * @param[in] init_opt true to retrieve first option; false to retrieve + * option at opt->offset + * + * @return length of option value + * @return -ENOENT if option not found + */ +ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt, + uint8_t **value, bool init_opt); /**@}*/ diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 46c5e3bbcbfa..aa5bf1fe866c 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -269,6 +269,30 @@ unsigned coap_get_content_type(coap_pkt_t *pkt) return content_type; } +ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt, + uint8_t **value, bool init_opt) +{ + if (init_opt) { + opt->opt_num = 0; + opt->offset = coap_get_total_hdr_len(pkt); + } + uint8_t *start = (uint8_t*)pkt->hdr + opt->offset; + + /* Find start of option value and value length. */ + uint16_t delta; + int len; + + start = _parse_option(pkt, start, &delta, &len); + if (!start) { + return -ENOENT; + } + + *value = start; + opt->opt_num += delta; + opt->offset = start + len - (uint8_t*)pkt->hdr; + return 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 47ef5c165684d6415221f32d488644fb73b7b1b0 Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Sat, 24 Aug 2019 06:38:36 -0400 Subject: [PATCH 4/4] net/nanocoap: add unit test for iterate options --- .../unittests/tests-nanocoap/tests-nanocoap.c | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/unittests/tests-nanocoap/tests-nanocoap.c b/tests/unittests/tests-nanocoap/tests-nanocoap.c index e6dab029361e..c5d630b9d4a2 100644 --- a/tests/unittests/tests-nanocoap/tests-nanocoap.c +++ b/tests/unittests/tests-nanocoap/tests-nanocoap.c @@ -547,6 +547,79 @@ static void test_nanocoap__server_option_count_overflow(void) TEST_ASSERT(res < 0); } +/* + * Helper for options tests below. + * POST request to a CoRE RD server to update the entries for a node + * from RIOT cord_ep example. Generated by RIOT. + * Includes 4 options: + * Uri-Path: resourcedirectory + * Content-Format: 40 (0x28) + * Uri-Query: ep-RIOT-0C49232323232323 + * Uri-Query: lt=60 + * Payload: (absent if omit_payload) + */ +static int _read_rd_post_req(coap_pkt_t *pkt, bool omit_payload) +{ + static uint8_t pkt_data[] = { + 0x42, 0x02, 0x20, 0x92, 0xb9, 0x27, 0xbd, 0x04, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x11, 0x28, 0x3d, 0x0b, 0x65, 0x70, 0x3d, + 0x52, 0x49, 0x4f, 0x54, 0x2d, 0x30, 0x43, 0x34, + 0x39, 0x32, 0x33, 0x32, 0x33, 0x32, 0x33, 0x32, + 0x33, 0x32, 0x33, 0x32, 0x33, 0x05, 0x6c, 0x74, + 0x3d, 0x36, 0x30, 0xff, 0x3c, 0x2f, 0x6e, 0x6f, + 0x64, 0x65, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x3e + }; + + size_t len = omit_payload ? 59 : sizeof(pkt_data); + return coap_parse(pkt, pkt_data, len); +} + +/* + * Tests use of coap_opt_get_next() to iterate over options. + */ +static void test_nanocoap__options_iterate(void) +{ + coap_pkt_t pkt; + int res = _read_rd_post_req(&pkt, true); + TEST_ASSERT_EQUAL_INT(0, res); + + /* read all options */ + coap_optpos_t opt = {0, 0}; + uint8_t *value; + ssize_t exp_len[] = {17, 1, 24, 5, -ENOENT}; + ssize_t exp_optnum[] = {COAP_OPT_URI_PATH, COAP_OPT_CONTENT_FORMAT, + COAP_OPT_URI_QUERY, COAP_OPT_URI_QUERY}; + + for (int i = 0; i < 5; i++) { + ssize_t optlen = coap_opt_get_next(&pkt, &opt, &value, !i); + TEST_ASSERT_EQUAL_INT(exp_len[i], optlen); + if (optlen >= 0) { + TEST_ASSERT_EQUAL_INT(exp_optnum[i], opt.opt_num); + } + else { + TEST_ASSERT_EQUAL_INT(4, i); + } + } + + /* test with no payload to verify end of options handling */ + memset(&pkt, 0, sizeof(pkt)); + res = _read_rd_post_req(&pkt, false); + TEST_ASSERT_EQUAL_INT(0, res); + + for (int i = 0; i < 5; i++) { + ssize_t optlen = coap_opt_get_next(&pkt, &opt, &value, !i); + TEST_ASSERT_EQUAL_INT(exp_len[i], optlen); + if (optlen >= 0) { + TEST_ASSERT_EQUAL_INT(exp_optnum[i], opt.opt_num); + } + else { + TEST_ASSERT_EQUAL_INT(4, i); + } + } +} + Test *tests_nanocoap_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -561,6 +634,7 @@ Test *tests_nanocoap_tests(void) new_TestFixture(test_nanocoap__get_query), new_TestFixture(test_nanocoap__get_multi_query), new_TestFixture(test_nanocoap__option_add_buffer_max), + new_TestFixture(test_nanocoap__options_iterate), new_TestFixture(test_nanocoap__server_get_req), new_TestFixture(test_nanocoap__server_reply_simple), new_TestFixture(test_nanocoap__server_get_req_con),