diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index de156d59279a34bd6cc800d95918ee42e3f07c2b..2f83b38fb0af6403ecf3f7168494703eb2bf6307 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de> + * 2018 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -18,6 +19,7 @@ * * @author Kaspar Schleiser <kaspar@schleiser.de> * @author Ken Bannister <kb2ma@runbox.com> + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> */ #ifndef NET_NANOCOAP_H @@ -63,9 +65,11 @@ extern "C" { */ #define COAP_OPT_URI_HOST (3) #define COAP_OPT_OBSERVE (6) +#define COAP_OPT_LOCATION_PATH (8) #define COAP_OPT_URI_PATH (11) #define COAP_OPT_CONTENT_FORMAT (12) #define COAP_OPT_URI_QUERY (15) +#define COAP_OPT_LOCATION_QUERY (20) #define COAP_OPT_BLOCK2 (23) #define COAP_OPT_BLOCK1 (27) /** @} */ @@ -450,17 +454,87 @@ size_t coap_put_option(uint8_t *buf, uint16_t lastonum, uint16_t onum, uint8_t * size_t coap_put_option_ct(uint8_t *buf, uint16_t lastonum, uint16_t content_type); /** - * @brief Insert URI encoded option into buffer + * @brief Encode the given string as multi-part option into buffer + * + * @param[out] buf buffer to write to + * @param[in] lastonum number of previous option (for delta calculation), + * or 0 if first option + * @param[in] optnum option number to use + * @param[in] string string to encode as option + * @param[in] separator character used in @p string to separate parts + * + * @return number of bytes written to @p buf + */ +size_t coap_opt_put_string(uint8_t *buf, uint16_t lastonum, uint16_t optnum, + const char *string, char separator); + +/** + * @brief Convenience function for inserting URI_PATH option into buffer + * + * @param[out] buf buffer to write to + * @param[in] lastonum number of previous option (for delta calculation), + * or 0 if first option + * @param[in] uri ptr to source URI + * + * @returns amount of bytes written to @p buf + */ +static inline size_t coap_opt_put_uri_path(uint8_t *buf, uint16_t lastonum, + const char *uri) +{ + return coap_opt_put_string(buf, lastonum, COAP_OPT_URI_PATH, uri, '/'); +} + +/** + * @brief Convenience function for inserting URI_QUERY option into buffer * * @param[out] buf buffer to write to * @param[in] lastonum number of previous option (for delta calculation), * or 0 if first option * @param[in] uri ptr to source URI - * @param[in] optnum option number to use (e.g., COAP_OPT_URI_PATH) * * @returns amount of bytes written to @p buf */ -size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum); +static inline size_t coap_opt_put_uri_query(uint8_t *buf, uint16_t lastonum, + const char *uri) +{ + return coap_opt_put_string(buf, lastonum, COAP_OPT_URI_QUERY, uri, '&'); +} + +/** + * @brief Convenience function for inserting LOCATION_PATH option into buffer + * + * @param[out] buf buffer to write to + * @param[in] lastonum number of previous option (for delta calculation), + * or 0 if first option + * @param[in] location ptr to string holding the location + * + * @returns amount of bytes written to @p buf + */ +static inline size_t coap_opt_put_location_path(uint8_t *buf, + uint16_t lastonum, + const char *location) +{ + return coap_opt_put_string(buf, lastonum, COAP_OPT_LOCATION_PATH, + location, '/'); +} + +/** + * @brief Convenience function for inserting LOCATION_QUERY option into buffer + * + * @param[out] buf buffer to write to + * @param[in] lastonum number of previous option (for delta calculation), + * or 0 if first option + * @param[in] location ptr to string holding the location + * + * @returns amount of bytes written to @p buf + */ +static inline size_t coap_opt_put_location_query(uint8_t *buf, + uint16_t lastonum, + const char *location) +{ + return coap_opt_put_string(buf, lastonum, COAP_OPT_LOCATION_QUERY, + location, '&'); +} /** * @brief Generic block option getter @@ -585,10 +659,50 @@ ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags); unsigned coap_get_content_type(coap_pkt_t *pkt); /** - * @brief Get the packet's request URI + * @brief Read a full option as null terminated string into the target buffer * - * This function decodes the pkt's URI option into a "/"-seperated and - * NULL-terminated string. + * This function is for reading and concatenating string based, multi-part CoAP + * options like COAP_OPT_URI_PATH or COAP_OPT_LOCATION_PATH. It will write all + * parts of the given option into the target buffer, separating the parts using + * the given @p separator. The resulting string is `\0` terminated. + * + * @param[in] pkt packet to read from + * @param[in] optnum absolute option number + * @param[out] target target buffer + * @param[in] max_len size of @p target + * @param[in] separator character used for separating the option parts + * + * @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, + uint8_t *target, size_t max_len, char separator); + +/** + * @brief Convenience function for getting the packet's URI_PATH + * + * This function decodes the pkt's URI option into a "/"-separated and + * '\0'-terminated string. + * + * Caller must ensure @p target can hold at least NANOCOAP_URI_MAX bytes! + * + * @param[in] pkt pkt to work on + * @param[out] target buffer for target URI + * + * @returns -ENOSPC if URI option is larger than 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) +{ + return coap_opt_get_string(pkt, COAP_OPT_URI_PATH, target, + NANOCOAP_URI_MAX, '/'); +} + +/** + * @brief Convenience function for getting the packet's URI_QUERY option + * + * This function decodes the pkt's URI_QUERY option into a "&"-separated and + * '\0'-terminated string. * * Caller must ensure @p target can hold at least NANOCOAP_URI_MAX bytes! * @@ -598,10 +712,58 @@ unsigned coap_get_content_type(coap_pkt_t *pkt); * @returns -ENOSPC if URI option is larger than NANOCOAP_URI_MAX * @returns nr of bytes written to @p target (including '\0') */ -int coap_get_uri(coap_pkt_t *pkt, uint8_t *target); +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 Convenience function for getting the packet's LOCATION_PATH option + * + * This function decodes the pkt's LOCATION_PATH option into a '/'-separated and + * '\0'-terminated string. + * + * Caller must ensure @p target can hold at least 2 bytes! + * + * @param[in] pkt pkt to work on + * @param[out] target buffer for location path + * @param[in] max_len size of @p target in bytes + * + * @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, + uint8_t *target, size_t max_len) +{ + return coap_opt_get_string(pkt, COAP_OPT_LOCATION_PATH, + target, max_len, '/'); +} + +/** + * @brief Convenience function for getting the packet's LOCATION_QUERY option + * + * This function decodes the pkt's LOCATION_PATH option into a '&'-separated and + * '\0'-terminated string. + * + * Caller must ensure @p target can hold at least 2 bytes! + * + * @param[in] pkt pkt to work on + * @param[out] target buffer for location path + * @param[in] max_len size of @p target in bytes + * + * @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, + uint8_t *target, size_t max_len) +{ + return coap_opt_get_string(pkt, COAP_OPT_LOCATION_QUERY, + target, max_len, '&'); +} /** - * @brief Helper to decode SZX value to size in bytes + * @brief Helper to decode SZX value to size in bytes * * @param[in] szx SZX value to decode * diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 87aa1a41707e56bf0674a34ba179c3689d54bcab..9729356f1fc6d72df131fac2c0400bdb5f759bef 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -564,8 +564,8 @@ static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len) DEBUG("gcoap: _write_options: path does not start with '/'\n"); return -EINVAL; } - bufpos += coap_put_option_uri(bufpos, last_optnum, (char *)pdu->url, - COAP_OPT_URI_PATH); + bufpos += coap_opt_put_uri_path(bufpos, last_optnum, + (char *)pdu->url); last_optnum = COAP_OPT_URI_PATH; } } @@ -578,8 +578,8 @@ static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len) /* Uri-query for requests */ if (coap_get_code_class(pdu) == COAP_CLASS_REQ) { - bufpos += coap_put_option_uri(bufpos, last_optnum, (char *)pdu->qs, - COAP_OPT_URI_QUERY); + bufpos += coap_opt_put_uri_query(bufpos, last_optnum, + (char *)pdu->qs); /* uncomment when further options are added below ... */ /* last_optnum = COAP_OPT_URI_QUERY; */ } diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index be3f7482f1489678a9adba82eb184ec6be5bc753..43b0cf142269dfb16ad03d335e9af02e62061ac8 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2016-18 Kaspar Schleiser <kaspar@schleiser.de> + * 2018 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -14,6 +15,7 @@ * @brief Nanocoap implementation * * @author Kaspar Schleiser <kaspar@schleiser.de> + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> * * @} */ @@ -117,7 +119,7 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len) } #ifdef MODULE_GCOAP - coap_get_uri(pkt, pkt->url); + coap_get_uri_path(pkt, pkt->url); pkt->content_type = coap_get_content_type(pkt); if (coap_get_option_uint(pkt, COAP_OPT_OBSERVE, &pkt->observe_value) != 0) { @@ -133,9 +135,9 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len) return 0; } -uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num) +uint8_t *coap_find_option(const coap_pkt_t *pkt, unsigned opt_num) { - coap_optpos_t *optpos = pkt->options; + const coap_optpos_t *optpos = pkt->options; unsigned opt_count = pkt->options_len; while (opt_count--) { @@ -147,7 +149,8 @@ uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num) return NULL; } -static uint8_t *_parse_option(coap_pkt_t *pkt, uint8_t *pkt_pos, uint16_t *delta, int *opt_len) +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; @@ -188,7 +191,8 @@ int coap_get_option_uint(coap_pkt_t *pkt, unsigned opt_num, uint32_t *target) return -1; } -uint8_t *coap_iterate_option(coap_pkt_t *pkt, uint8_t **optpos, int *opt_len, int first) +uint8_t *coap_iterate_option(const coap_pkt_t *pkt, uint8_t **optpos, + int *opt_len, int first) { uint8_t *data_start; @@ -226,25 +230,29 @@ unsigned coap_get_content_type(coap_pkt_t *pkt) return content_type; } -int coap_get_uri(coap_pkt_t *pkt, uint8_t *target) +ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum, + uint8_t *target, size_t max_len, char separator) { - uint8_t *opt_pos = coap_find_option(pkt, COAP_OPT_URI_PATH); + assert(pkt && target && (max_len > 1)); + + uint8_t *opt_pos = coap_find_option(pkt, optnum); if (!opt_pos) { - *target++ = '/'; + *target++ = (uint8_t)separator; *target = '\0'; return 2; } - unsigned left = NANOCOAP_URI_MAX - 1; + unsigned left = max_len - 1; uint8_t *part_start = NULL; do { int opt_len; - part_start = coap_iterate_option(pkt, &opt_pos, &opt_len, part_start==NULL); + part_start = coap_iterate_option(pkt, &opt_pos, &opt_len, + (part_start == NULL)); if (part_start) { if (left < (unsigned)(opt_len + 1)) { return -ENOSPC; } - *target++ = '/'; + *target++ = (uint8_t)separator; memcpy(target, part_start, opt_len); target += opt_len; left -= (opt_len + 1); @@ -253,7 +261,7 @@ int coap_get_uri(coap_pkt_t *pkt, uint8_t *target) *target = '\0'; - return NANOCOAP_URI_MAX - left; + return (int)(max_len - left); } int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsigned *szx) @@ -296,7 +304,7 @@ ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_le uint8_t *uri = pkt->url; #else uint8_t uri[NANOCOAP_URI_MAX]; - if (coap_get_uri(pkt, uri) <= 0) { + if (coap_get_uri_path(pkt, uri) <= 0) { return -EBADMSG; } #endif @@ -569,24 +577,24 @@ size_t coap_put_block1_ok(uint8_t *pkt_pos, coap_block1_t *block1, uint16_t last } } -size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum) +size_t coap_opt_put_string(uint8_t *buf, uint16_t lastonum, uint16_t optnum, + const char *string, char separator) { - char separator = (optnum == COAP_OPT_URI_PATH) ? '/' : '&'; - size_t uri_len = strlen(uri); + size_t len = strlen(string); - if (uri_len == 0) { + if (len == 0) { return 0; } uint8_t *bufpos = buf; - char *uripos = (char *)uri; + char *uripos = (char *)string; - while (uri_len) { + while (len) { size_t part_len; uripos++; uint8_t *part_start = (uint8_t *)uripos; - while (uri_len--) { + while (len--) { if ((*uripos == separator) || (*uripos == '\0')) { break; } diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index c92be0c20f6ddb8ac6a10ff7791a21c15f89bef5..ce9eb78c9f72ad9303f1e1df97e930061d3668e7 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -95,7 +95,7 @@ ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf, size pkt.hdr = (coap_hdr_t*)buf; pktpos += coap_build_hdr(pkt.hdr, COAP_REQ, NULL, 0, COAP_METHOD_GET, 1); - pktpos += coap_put_option_uri(pktpos, 0, path, COAP_OPT_URI_PATH); + pktpos += coap_opt_put_uri_path(pktpos, 0, path); pkt.payload = pktpos; pkt.payload_len = 0; diff --git a/tests/unittests/tests-nanocoap/tests-nanocoap.c b/tests/unittests/tests-nanocoap/tests-nanocoap.c index 107f476ad3e267f6bd24e10acbcbb0f09ced5fe6..bd3e9e7a58773b88e9034bb57ec92b462f588db7 100644 --- a/tests/unittests/tests-nanocoap/tests-nanocoap.c +++ b/tests/unittests/tests-nanocoap/tests-nanocoap.c @@ -30,20 +30,27 @@ static void test_nanocoap__hdr(void) uint8_t buf[128]; uint16_t msgid = 0xABCD; char path[] = "/test/abcd/efgh"; + char loc_path[] = "/foo/bar"; unsigned char path_tmp[64] = {0}; uint8_t *pktpos = &buf[0]; - pktpos += coap_build_hdr((coap_hdr_t *)pktpos, COAP_REQ, NULL, 0, COAP_METHOD_GET, msgid); - pktpos += coap_put_option_uri(pktpos, 0, path, COAP_OPT_URI_PATH); + pktpos += coap_build_hdr((coap_hdr_t *)pktpos, COAP_REQ, NULL, 0, + COAP_METHOD_GET, msgid); + pktpos += coap_opt_put_location_path(pktpos, 0, loc_path); + pktpos += coap_opt_put_uri_path(pktpos, COAP_OPT_LOCATION_PATH, path); coap_pkt_t pkt; coap_parse(&pkt, &buf[0], pktpos - &buf[0]); TEST_ASSERT_EQUAL_INT(msgid, coap_get_id(&pkt)); - int res = coap_get_uri(&pkt, path_tmp); + int res = coap_get_uri_path(&pkt, path_tmp); TEST_ASSERT_EQUAL_INT(sizeof(path), res); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)path_tmp); + + res = coap_get_location_path(&pkt, path_tmp, 64); + TEST_ASSERT_EQUAL_INT(sizeof(loc_path), res); + TEST_ASSERT_EQUAL_STRING((char *)loc_path, (char *)path_tmp); } /* @@ -76,7 +83,7 @@ static void test_nanocoap__get_req(void) TEST_ASSERT_EQUAL_INT(total_opt_len, len); char uri[10] = {0}; - coap_get_uri(&pkt, (uint8_t *)&uri[0]); + coap_get_uri_path(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)uri); len = coap_opt_finish(&pkt, COAP_OPT_FINISH_NONE); @@ -140,7 +147,7 @@ static void test_nanocoap__get_multi_path(void) TEST_ASSERT_EQUAL_INT(uri_opt_len, len); char uri[10] = {0}; - coap_get_uri(&pkt, (uint8_t *)&uri[0]); + coap_get_uri_path(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)uri); } @@ -162,7 +169,7 @@ static void test_nanocoap__get_root_path(void) coap_pkt_init(&pkt, &buf[0], sizeof(buf), len); char uri[10] = {0}; - coap_get_uri(&pkt, (uint8_t *)&uri[0]); + coap_get_uri_path(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)uri); } @@ -188,13 +195,13 @@ static void test_nanocoap__get_max_path(void) TEST_ASSERT_EQUAL_INT(uri_opt_len, len); char uri[NANOCOAP_URI_MAX] = {0}; - coap_get_uri(&pkt, (uint8_t *)&uri[0]); + coap_get_uri_path(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_STRING((char *)path, (char *)uri); } /* * Builds on get_req test, to test path longer than NANOCOAP_URI_MAX. We - * expect coap_get_uri() to return -ENOSPC. + * expect coap_get_uri_path() to return -ENOSPC. */ static void test_nanocoap__get_path_too_long(void) { @@ -215,7 +222,7 @@ static void test_nanocoap__get_path_too_long(void) TEST_ASSERT_EQUAL_INT(uri_opt_len, len); char uri[NANOCOAP_URI_MAX] = {0}; - int get_len = coap_get_uri(&pkt, (uint8_t *)&uri[0]); + int get_len = coap_get_uri_path(&pkt, (uint8_t *)&uri[0]); TEST_ASSERT_EQUAL_INT(-ENOSPC, get_len); }