diff --git a/examples/nanocoap_server/Makefile b/examples/nanocoap_server/Makefile index ec2b6e1b0e099d8a926bf280e2318fd7baa6f285..9030ca052de70fae292311825dc6e82481b306f0 100644 --- a/examples/nanocoap_server/Makefile +++ b/examples/nanocoap_server/Makefile @@ -27,6 +27,9 @@ USEMODULE += nanocoap_sock # include this for nicely formatting the returned internal value USEMODULE += fmt +# include sha256 (used by example blockwise handler) +USEMODULE += hashes + # include this for printing IP addresses USEMODULE += shell_commands diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 06bd294f0b873a22fae2f13348933d68bee653a4..9a254ac2da22807eed89105df408c7a9a3d2af4d 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -12,6 +12,7 @@ #include "fmt.h" #include "net/nanocoap.h" +#include "hashes/sha256.h" /* internal value that can be read/written via CoAP */ static uint8_t internal_value = 0; @@ -55,9 +56,58 @@ static ssize_t _riot_value_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, vo COAP_FORMAT_TEXT, (uint8_t*)rsp, p); } +ssize_t _sha256_handler(coap_pkt_t* pkt, uint8_t *buf, size_t len, void *context) +{ + (void)context; + + /* using a shared sha256 context *will* break if two requests are handled + * at the same time. doing it anyways, as this is meant to showcase block1 + * support, not proper synchronisation. */ + static sha256_context_t sha256; + + uint8_t digest[SHA256_DIGEST_LENGTH]; + + uint32_t result = COAP_CODE_204; + + coap_block1_t block1; + int blockwise = coap_get_block1(pkt, &block1); + + printf("_sha256_handler(): received data: offset=%u len=%u blockwise=%i more=%i\n", \ + (unsigned)block1.offset, pkt->payload_len, blockwise, block1.more); + + if (block1.offset == 0) { + puts("_sha256_handler(): init"); + sha256_init(&sha256); + } + + sha256_update(&sha256, pkt->payload, pkt->payload_len); + + if (block1.more == 1) { + result = COAP_CODE_231; + } + + size_t result_len = 0; + if (!blockwise || !block1.more) { + puts("_sha256_handler(): finish"); + sha256_final(&sha256, digest); + result_len = SHA256_DIGEST_LENGTH * 2; + } + + ssize_t reply_len = coap_build_reply(pkt, result, buf, len, 0); + uint8_t *pkt_pos = (uint8_t*)pkt->hdr + reply_len; + pkt_pos += coap_put_block1_ok(pkt_pos, &block1, 0); + if (result_len) { + *pkt_pos++ = 0xFF; + pkt_pos += fmt_bytes_hex((char *)pkt_pos, digest, sizeof(digest)); + } + + return pkt_pos - (uint8_t*)pkt->hdr; +} + /* must be sorted by path (alphabetically) */ const coap_resource_t coap_resources[] = { COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER, + { "/sha256", COAP_POST, _sha256_handler, NULL }, { "/riot/board", COAP_GET, _riot_board_handler, NULL }, { "/riot/value", COAP_GET | COAP_PUT | COAP_POST, _riot_value_handler, NULL }, }; diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 80d7fa6e371fb29711899044931fdeb7583d0346..e0af70a155a4b9ebf45928109e4c02a89680f780 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -65,6 +65,8 @@ extern "C" { #define COAP_OPT_URI_PATH (11) #define COAP_OPT_CONTENT_FORMAT (12) #define COAP_OPT_URI_QUERY (15) +#define COAP_OPT_BLOCK2 (23) +#define COAP_OPT_BLOCK1 (27) /** @} */ /** @@ -211,6 +213,16 @@ extern "C" { #define COAP_DEFAULT_LEISURE (5) /** @} */ +/** + * @name Blockwise transfer (RFC7959) + * @{ + */ +#define COAP_BLOCKWISE_NUM_OFF (4) +#define COAP_BLOCKWISE_MORE_OFF (3) +#define COAP_BLOCKWISE_SZX_MASK (0x07) +#define COAP_BLOCKWISE_SZX_MAX (7) +/** @} */ + /** * @brief Raw CoAP PDU header structure */ @@ -262,6 +274,17 @@ typedef struct { void *context; /**< ptr to user defined context data */ } coap_resource_t; +/** + * @brief Block1 helper struct + */ +typedef struct { + size_t offset; /**< offset of received data */ + uint32_t blknum; /**< block number */ + unsigned szx; /**< szx value */ + int more; /**< -1 for no option, 0 for last block, + 1 for more blocks coming */ +} coap_block1_t; + /** * @brief Global CoAP resource list */ @@ -409,6 +432,71 @@ size_t coap_put_option_ct(uint8_t *buf, uint16_t lastonum, uint16_t content_type */ size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum); +/** + * @brief Generic block option getter + * + * @param[in] pkt pkt to work on + * @param[in] option actual block option number to get + * @param[out] blknum block number + * @param[out] szx SZX value + * + * @returns -1 if option not found + * @returns 0 if more flag is not set + * @returns 1 if more flag is set + */ +int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsigned *szx); + +/** + * @brief Block1 option getter + * + * This function gets a CoAP packet's block1 option and parses it into a helper + * structure. + * + * If no block1 option is present in @p pkt, the values in @p block1 will be + * initialized with zero. That implies both block1->offset and block1->more are + * also valid in that case, as packet with offset==0 and more==0 means it contains + * all the payload for the corresponding request. + * + * @param[in] pkt pkt to work on + * @param[out] block1 ptr to preallocated coap_block1_t structure + * + * @returns 0 if block1 option not present + * @returns 1 if structure has been filled + */ +int coap_get_block1(coap_pkt_t *pkt, coap_block1_t *block1); + +/** + * @brief Insert block1 option into buffer + * + * @param[out] buf buffer to write to + * @param[in] lastonum number of previous option (for delta calculation), + * must be < 27 + * @param[in] blknum block number + * @param[in] szx SXZ value + * @param[in] more more flag (1 or 0) + * + * @returns amount of bytes written to @p buf + */ +size_t coap_put_option_block1(uint8_t *buf, uint16_t lastonum, unsigned blknum, unsigned szx, int more); + +/** + * @brief Insert block1 option into buffer (from coap_block1_t) + * + * This function is wrapper around @ref coap_put_option_block1(), + * taking its arguments from a coap_block1_t struct. + * + * It will write option Nr. 27 (COAP_OPT_BLOCK1). + * + * It is safe to be called when @p block1 was generated for a non-blockwise + * request. + * + * @param[in] pkt_pos buffer to write to + * @param[in] block1 ptr to block1 struct (created by coap_get_block1()) + * @param[in] lastonum last option number (must be < 27) + * + * @returns amount of bytes written to @p pkt_pos + */ +size_t coap_put_block1_ok(uint8_t *pkt_pos, coap_block1_t *block1, uint16_t lastonum); /** * @brief Get content type from packet @@ -436,6 +524,18 @@ unsigned coap_get_content_type(coap_pkt_t *pkt); */ int coap_get_uri(coap_pkt_t *pkt, uint8_t *target); +/** + * @brief Helper to decode SZX value to size in bytes + * + * @param[in] szx SZX value to decode + * + * @returns SZX value decoded to bytes + */ +static inline unsigned coap_szx2size(unsigned szx) +{ + return (1 << (szx + 4)); +} + /** * @brief Get the CoAP version number * diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 0f04395e3d1bcde9c3d4db24c26331c032185adc..e72bda605981b250c4304226e063405c8f4d944b 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -30,6 +30,8 @@ static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end); int coap_get_option_uint(coap_pkt_t *pkt, unsigned opt_num, uint32_t *target); +static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes); +static size_t _encode_uint(uint32_t *val); /* http://tools.ietf.org/html/rfc7252#section-3 * 0 1 2 3 @@ -161,17 +163,6 @@ static uint8_t *_parse_option(coap_pkt_t *pkt, uint8_t *pkt_pos, uint16_t *delta return pkt_pos; } -static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes) -{ - assert(nbytes <= 4); - - uint32_t res = 0; - if (nbytes) { - memcpy(((uint8_t *)&res) + (4 - nbytes), pkt_pos, nbytes); - } - return ntohl(res); -} - int coap_get_option_uint(coap_pkt_t *pkt, unsigned opt_num, uint32_t *target) { assert(target); @@ -265,6 +256,29 @@ int coap_get_uri(coap_pkt_t *pkt, uint8_t *target) return NANOCOAP_URI_MAX - left; } +int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsigned *szx) +{ + uint8_t *optpos = coap_find_option(pkt, option); + if (!optpos) { + *blknum = 0; + *szx = 0; + return -1; + } + + int option_len; + uint16_t delta; + + uint8_t *data_start = _parse_option(pkt, optpos, &delta, &option_len); + uint32_t blkopt = _decode_uint(data_start, option_len); + + DEBUG("nanocoap: blkopt len: %i\n", option_len); + DEBUG("nanocoap: blkopt: 0x%08x\n", (unsigned)blkopt); + *blknum = blkopt >> COAP_BLOCKWISE_NUM_OFF; + *szx = blkopt & COAP_BLOCKWISE_SZX_MASK; + + return (blkopt & 0x8) ? 1 : 0; +} + 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) { @@ -416,6 +430,43 @@ static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end) return res; } +static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes) +{ + assert(nbytes <= 4); + + uint32_t res = 0; + if (nbytes) { + memcpy(((uint8_t *)&res) + (4 - nbytes), pkt_pos, nbytes); + } + return ntohl(res); +} + +static size_t _encode_uint(uint32_t *val) +{ + uint8_t *tgt = (uint8_t *)val; + size_t size = 0; + + /* count number of used bytes */ + uint32_t tmp = *val; + while(tmp) { + size++; + tmp >>= 8; + } + + /* convert to network byte order */ + tmp = htonl(*val); + + /* copy bytewise, starting with first actually used byte */ + *val = 0; + uint8_t *tmp_u8 = (uint8_t *)&tmp; + tmp_u8 += (4 - size); + for (unsigned n = 0; n < size; n++) { + *tgt++ = *tmp_u8++; + } + + return size; +} + static unsigned _put_delta_optlen(uint8_t *buf, unsigned offset, unsigned shift, unsigned val) { if (val < 13) { @@ -469,6 +520,46 @@ size_t coap_put_option_ct(uint8_t *buf, uint16_t lastonum, uint16_t content_type } } +static size_t coap_put_option_block(uint8_t *buf, uint16_t lastonum, unsigned blknum, unsigned szx, int more, uint16_t option) +{ + uint32_t blkopt = (blknum << 4) | szx | (more ? 0x8 : 0); + size_t olen = _encode_uint(&blkopt); + return coap_put_option(buf, lastonum, option, (uint8_t*)&blkopt, olen); +} + +size_t coap_put_option_block1(uint8_t *buf, uint16_t lastonum, unsigned blknum, unsigned szx, int more) +{ + return coap_put_option_block(buf, lastonum, blknum, szx, more, COAP_OPT_BLOCK1); +} + +int coap_get_block1(coap_pkt_t *pkt, coap_block1_t *block1) +{ + uint32_t blknum; + unsigned szx; + block1->more = coap_get_blockopt(pkt, COAP_OPT_BLOCK1, &blknum, &szx); + if (block1->more >= 0) { + block1->offset = blknum << (szx + 4); + } + else { + block1->offset = 0; + } + + block1->blknum = blknum; + block1->szx = szx; + + return (block1->more >= 0); +} + +size_t coap_put_block1_ok(uint8_t *pkt_pos, coap_block1_t *block1, uint16_t lastonum) +{ + if (block1->more >= 1) { + return coap_put_option_block1(pkt_pos, lastonum, block1->blknum, block1->szx, block1->more); + } + else { + return 0; + } +} + size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum) { char separator = (optnum == COAP_OPT_URI_PATH) ? '/' : '&';