diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 0e329bca17..de156d5927 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -17,6 +17,7 @@ * @brief nanocoap API * * @author Kaspar Schleiser + * @author Ken Bannister */ #ifndef NET_NANOCOAP_H @@ -224,6 +225,18 @@ extern "C" { #define COAP_BLOCKWISE_SZX_MAX (7) /** @} */ +/** + * @name coap_opt_finish() flag parameter values + * + * Directs packet/buffer updates when user finishes adding options + * @{ + */ +/** @brief no special handling required */ +#define COAP_OPT_FINISH_NONE (0x0000) +/** @brief expect a payload to follow */ +#define COAP_OPT_FINISH_PAYLOAD (0x0001) +/** @} */ + /** * @brief Raw CoAP PDU header structure */ @@ -390,6 +403,22 @@ ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_le ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, size_t token_len, unsigned code, uint16_t id); +/** + * @brief Initialize a packet struct, to build a message buffer + * + * @pre buf CoAP header already initialized + * @post pkt.flags all zeroed + * @post pkt.payload points to first byte after header + * @post pkt.payload_len set to maximum space available for options + payload + * + * @param[out] pkt pkt to initialize + * @param[in] buf buffer to write for pkt, with CoAP header already + * initialized + * @param[in] len length of buf + * @param[in] header_len length of header in buf, including token + */ +void coap_pkt_init(coap_pkt_t *pkt, uint8_t *buf, size_t len, size_t header_len); + /** * @brief Insert a CoAP option into buffer * @@ -499,6 +528,52 @@ size_t coap_put_option_block1(uint8_t *buf, uint16_t lastonum, unsigned blknum, */ size_t coap_put_block1_ok(uint8_t *pkt_pos, coap_block1_t *block1, uint16_t lastonum); +/** + * @brief Encode the given string as option(s) into pkt + * + * Use separator to split string into multiple options. + * + * @post pkt.payload advanced to first byte after option(s) + * @post pkt.payload_len reduced by option(s) length + * + * @param[in,out] pkt pkt referencing target buffer + * @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 buffer + * @return -ENOSPC if no available options + */ +ssize_t coap_opt_add_string(coap_pkt_t *pkt, uint16_t optnum, const char *string, char separator); + +/** + * @brief Encode the given uint option into pkt + * + * @post pkt.payload advanced to first byte after option + * @post pkt.payload_len reduced by option length + * + * @param[in,out] pkt pkt referencing target buffer + * @param[in] optnum option number to use + * @param[in] value uint to encode + * + * @return number of bytes written to buffer + * @return <0 reserved for error but not implemented yet + */ +ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value); + +/** + * @brief Finalizes options as required and prepares for payload + * + * @post pkt.payload advanced to first available byte after options + * @post pkt.payload_len is maximum bytes available for payload + * + * @param[in,out] pkt pkt to update + * @param[in] flags see COAP_OPT_FINISH... macros + * + * @return total number of bytes written to buffer + */ +ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags); + /** * @brief Get content type from packet * diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index e72bda6059..d5b9c0c721 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -383,6 +383,15 @@ ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, size_t to return sizeof(coap_hdr_t) + token_len; } +void coap_pkt_init(coap_pkt_t *pkt, uint8_t *buf, size_t len, size_t header_len) +{ + memset(pkt, 0, sizeof(coap_pkt_t)); + pkt->hdr = (coap_hdr_t *)buf; + pkt->token = buf + sizeof(coap_hdr_t); + pkt->payload = buf + header_len; + pkt->payload_len = len - header_len; +} + static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end) { uint8_t *pkt_pos = *pkt_pos_ptr; @@ -595,6 +604,85 @@ size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uin return bufpos - buf; } +/* Common functionality for addition of an option */ +static ssize_t _add_opt_pkt(coap_pkt_t *pkt, uint16_t optnum, uint8_t *val, + size_t val_len) +{ + assert(pkt->options_len < NANOCOAP_NOPTS_MAX); + + uint16_t lastonum = (pkt->options_len) + ? pkt->options[pkt->options_len - 1].opt_num : 0; + assert(optnum >= lastonum); + + size_t optlen = coap_put_option(pkt->payload, lastonum, optnum, val, val_len); + assert(pkt->payload_len > optlen); + + pkt->options[pkt->options_len].opt_num = optnum; + pkt->options[pkt->options_len].offset = pkt->payload - (uint8_t *)pkt->hdr; + pkt->options_len++; + pkt->payload += optlen; + pkt->payload_len -= optlen; + + return optlen; +} + +ssize_t coap_opt_add_string(coap_pkt_t *pkt, uint16_t optnum, const char *string, + char separator) +{ + size_t unread_len = strlen(string); + if (!unread_len) { + return 0; + } + char *uripos = (char *)string; + size_t write_len = 0; + + while (unread_len) { + size_t part_len; + uripos++; + uint8_t *part_start = (uint8_t *)uripos; + + while (unread_len--) { + if ((*uripos == separator) || (*uripos == '\0')) { + break; + } + uripos++; + } + + part_len = (uint8_t *)uripos - part_start; + + if (part_len) { + if (pkt->options_len == NANOCOAP_NOPTS_MAX) { + return -ENOSPC; + } + write_len += _add_opt_pkt(pkt, optnum, part_start, part_len); + } + } + + return write_len; +} + +ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value) +{ + uint32_t tmp = value; + unsigned tmp_len = _encode_uint(&tmp); + return _add_opt_pkt(pkt, optnum, (uint8_t *)&tmp, tmp_len); +} + +ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags) +{ + if (flags & COAP_OPT_FINISH_PAYLOAD) { + assert(pkt->payload_len > 1); + + *pkt->payload++ = 0xFF; + pkt->payload_len--; + } + else { + pkt->payload_len = 0; + } + + return pkt->payload - (uint8_t *)pkt->hdr; +} + ssize_t coap_well_known_core_default_handler(coap_pkt_t *pkt, uint8_t *buf, \ size_t len, void *context) {