diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 509f94b664..c7812cca1f 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -987,6 +987,31 @@ static inline ssize_t coap_get_uri_query_string(coap_pkt_t *pkt, char *target, bool coap_find_uri_query(coap_pkt_t *pkt, const char *key, const char **value, size_t *len); +/** + * @brief Iterate over a packet's URI Query options + * + * This expects that the Uri-Query options follow the widespread format `key=value` + * or just `key` + * + * Key and Value will be copied into the supplied buffers as a NULL-terminated + * string. + * + * @param[in] pkt packet to read from + * @param[out] ctx opaque, must be set to `NULL` on first call + * @param[out] key Output buffer for the key + * @param[in] key_len_max Size of the key output buffer + * @param[out] value Output buffer for the value + * @param[in] value_len_max Size of the value output buffer + * + * @return 2 if key and value were found + * @return 1 if key was found + * @return 0 if no key was found + * @return -E2BIG if key or value does not fit the supplied space + */ +int coap_iterate_uri_query(coap_pkt_t *pkt, void **ctx, + char *key, size_t key_len_max, + char *value, size_t value_len_max); + /** * @brief Iterate over a packet's options * diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 6e3d7e6adf..a3313ac166 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -407,6 +407,56 @@ ssize_t coap_opt_get_string(coap_pkt_t *pkt, uint16_t optnum, return (int)(max_len - left); } +int coap_iterate_uri_query(coap_pkt_t *pkt, void **opt_pos, + char *key, size_t key_len_max, + char *value, size_t value_len_max) +{ + int len; + void *key_data = coap_iterate_option(pkt, COAP_OPT_URI_QUERY, + (uint8_t **)opt_pos, &len); + if (!key_data) { + return 0; /* No key found */ + } + + const char *value_data = memchr(key_data, '=', len); + + size_t key_len, value_len; + + if (value_data) { + key_len = (uintptr_t)value_data - (uintptr_t)key_data; + value_data += 1; + value_len = len - key_len - 1; + } else { + key_len = len; + value_data = NULL; + value_len = 0; + + if (value && value_len_max) { + value[0] = 0; + } + } + + if (key_len >= key_len_max) { + return -E2BIG; + } + memcpy(key, key_data, key_len); + key[key_len] = 0; + + if (!value_data) { + return 1; /* Key was found but no values */ + } + if (!value) { + return 2; /* Key and values found */ + } + + if (value_len >= value_len_max) { + return -E2BIG; + } + memcpy(value, value_data, value_len); + value[value_len] = 0; + return 2; /* Key and values found */ +} + int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, uint8_t *szx) { uint8_t *optpos = coap_find_option(pkt, option); diff --git a/tests/unittests/tests-nanocoap/tests-nanocoap.c b/tests/unittests/tests-nanocoap/tests-nanocoap.c index 5b04de8483..d183a9738c 100644 --- a/tests/unittests/tests-nanocoap/tests-nanocoap.c +++ b/tests/unittests/tests-nanocoap/tests-nanocoap.c @@ -67,6 +67,8 @@ static void test_nanocoap__hdr_2(void) char path[] = "/test/abcd/efgh?foo=bar&baz=blub"; unsigned char path_tmp[64] = {0}; char query_tmp[64] = {0}; + char key[8]; + char value[8]; uint8_t *pktpos = &buf[0]; uint16_t lastonum = 0; @@ -86,6 +88,25 @@ static void test_nanocoap__hdr_2(void) res = coap_get_uri_query_string(&pkt, query_tmp, sizeof(query_tmp)); TEST_ASSERT_EQUAL_INT(sizeof("&foo=bar&baz=blub"), res); TEST_ASSERT_EQUAL_STRING("&foo=bar&baz=blub", (char *)query_tmp); + + void *pos = NULL; + + res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, 3); + TEST_ASSERT_EQUAL_INT(-E2BIG, res); + res = coap_iterate_uri_query(&pkt, &pos, key, 3, value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(-E2BIG, res); + + pos = NULL; + res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(2, res); + TEST_ASSERT_EQUAL_STRING("foo", key); + TEST_ASSERT_EQUAL_STRING("bar", value); + res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(2, res); + TEST_ASSERT_EQUAL_STRING("baz", key); + TEST_ASSERT_EQUAL_STRING("blub", value); + res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(0, res); } /* @@ -347,10 +368,12 @@ static void test_nanocoap__get_multi_query(void) coap_pkt_t pkt; uint16_t msgid = 0xABCD; uint8_t token[2] = {0xDA, 0xEC}; - char key1[] = "ab"; - char val1[] = "cde"; - char key2[] = "f"; - char qs[] = "ab=cde&f"; + const char key1[] = "ab"; + const char val1[] = "cde"; + const char key2[] = "f"; + const char qs[] = "ab=cde&f"; + char key[8]; + char value[8]; size_t len = coap_build_hdr((coap_hdr_t *)&buf[0], COAP_TYPE_NON, &token[0], 2, COAP_METHOD_GET, msgid); @@ -386,6 +409,18 @@ static void test_nanocoap__get_multi_query(void) TEST_ASSERT_EQUAL_INT((uintptr_t)NULL, (uintptr_t)val); TEST_ASSERT(!coap_find_uri_query(&pkt, "cde", &val, &val_len)); TEST_ASSERT(coap_find_uri_query(&pkt, "ab", NULL, 0)); + + void *pos = NULL; + int res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(2, res); + TEST_ASSERT_EQUAL_STRING("ab", key); + TEST_ASSERT_EQUAL_STRING("cde", value); + res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(1, res); + TEST_ASSERT_EQUAL_STRING("f", key); + TEST_ASSERT_EQUAL_STRING("", value); + res = coap_iterate_uri_query(&pkt, &pos, key, sizeof(key), value, sizeof(value)); + TEST_ASSERT_EQUAL_INT(0, res); } /*