diff --git a/sys/include/net/coap.h b/sys/include/net/coap.h index 358ebf7a072b..7e83216e7e99 100644 --- a/sys/include/net/coap.h +++ b/sys/include/net/coap.h @@ -535,6 +535,11 @@ typedef enum { #define COAP_ETAG_LENGTH_MAX (8U) /**< maximum length of the ETag option */ /** @} */ +/** + * @brief Marks the boundary between header and payload + */ +#define COAP_PAYLOAD_MARKER (0xFF) + /** * @defgroup net_coap_conf CoAP compile configurations * @ingroup net_coap diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 439a069858dd..3655fd7bd2a9 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -2115,6 +2115,36 @@ ssize_t coap_payload_put_bytes(coap_pkt_t *pkt, const void *data, size_t len); */ ssize_t coap_payload_put_char(coap_pkt_t *pkt, char c); +/** + * @brief Create CoAP reply header (convenience function) + * + * This function generates the reply CoAP header and sets + * the payload pointer inside the response buffer to point to + * the start of the payload, so that it can be written directly + * after the header. + * + * @param[in] pkt packet to reply to + * @param[in] code reply code (e.g., COAP_CODE_204) + * @param[out] buf buffer to write reply to + * @param[in] len size of @p buf + * @param[in] ct content type of payload + * if ct < 0 this will be ignored + * @param[out] payload Will be set to the start of the payload inside + * @p buf. + * May be set to NULL if no payload response is + * wanted (no-reply option) + * @param[out] payload_len_max max length of payload left in @p buf + * + * @returns size of reply header on success + * @returns 0 if no reply should be sent + * @returns <0 on error + * @returns -ENOSPC if @p buf too small + */ +ssize_t coap_build_reply_header(coap_pkt_t *pkt, unsigned code, + void *buf, size_t len, + int ct, + void **payload, size_t *payload_len_max); + /** * @brief Create CoAP reply (convenience function) * diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index bbba262e2d69..86ea74019f8e 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -119,7 +119,7 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len) while (pkt_pos < pkt_end) { uint8_t *option_start = pkt_pos; uint8_t option_byte = *pkt_pos++; - if (option_byte == 0xff) { + if (option_byte == COAP_PAYLOAD_MARKER) { pkt->payload = pkt_pos; pkt->payload_len = buf + len - pkt_pos; DEBUG("payload len = %u\n", pkt->payload_len); @@ -232,7 +232,7 @@ static uint8_t *_parse_option(const coap_pkt_t *pkt, uint8_t *hdr_end = pkt->payload; if ((pkt_pos >= hdr_end) - || (((pkt_pos + 1) == hdr_end) && (*pkt_pos == 0xFF))) { + || (((pkt_pos + 1) == hdr_end) && (*pkt_pos == COAP_PAYLOAD_MARKER))) { return NULL; } @@ -545,29 +545,85 @@ ssize_t coap_tree_handler(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_ return coap_build_reply(pkt, COAP_CODE_404, resp_buf, resp_buf_len, 0); } +ssize_t coap_build_reply_header(coap_pkt_t *pkt, unsigned code, + void *buf, size_t len, + int ct, + void **payload, size_t *payload_len_max) +{ + uint8_t *bufpos = buf; + uint32_t no_response; + unsigned tkl = coap_get_token_len(pkt); + size_t hdr_len = sizeof(coap_hdr_t) + tkl; + uint8_t type = coap_get_type(pkt) == COAP_TYPE_CON + ? COAP_TYPE_ACK + : COAP_TYPE_NON; + + if (hdr_len > len) { + return -ENOBUFS; + } + + bufpos += coap_build_hdr(buf, type, coap_get_token(pkt), tkl, + code, ntohs(pkt->hdr->id)); + + if (coap_opt_get_uint(pkt, COAP_OPT_NO_RESPONSE, &no_response) == 0) { + const uint8_t no_response_index = (code >> 5) - 1; + /* If the handler code misbehaved here, we'd face UB otherwise */ + assume(no_response_index < 7); + + const uint8_t mask = 1 << no_response_index; + if (no_response & mask) { + if (payload) { + *payload = NULL; + *payload_len_max = 0; + payload = NULL; + } + + /* no-response requested, only send empty ACK or nothing */ + if (type != COAP_TYPE_ACK) { + return 0; + } + } + } + + if (payload) { + if (ct >= 0) { + bufpos += coap_put_option_ct(bufpos, 0, ct); + } + *bufpos++ = COAP_PAYLOAD_MARKER; + *payload = bufpos; + hdr_len = bufpos - (uint8_t *)buf; + *payload_len_max = len - hdr_len; + } + + /* with the nanoCoAP API we can't detect the overflow before it happens */ + assert(hdr_len <= len); + + return hdr_len; +} + ssize_t coap_reply_simple(coap_pkt_t *pkt, unsigned code, uint8_t *buf, size_t len, unsigned ct, const void *payload, size_t payload_len) { - uint8_t *payload_start = buf + coap_get_total_hdr_len(pkt); - uint8_t *bufpos = payload_start; + void *payload_start; + size_t payload_len_max; - if (payload_len) { - bufpos += coap_put_option_ct(bufpos, 0, ct); - *bufpos++ = 0xff; + ssize_t header_len = coap_build_reply_header(pkt, code, buf, len, ct, + payload ? &payload_start : NULL, + &payload_len_max); + if (payload == NULL || header_len <= 0) { + return header_len; } - ssize_t res = coap_build_reply(pkt, code, buf, len, - bufpos - payload_start + payload_len); - - if (payload_len && (res > 0)) { - assert(payload); - memcpy(bufpos, payload, payload_len); + if (payload_len > payload_len_max) { + return -ENOBUFS; } - return res; + memcpy(payload_start, payload, payload_len); + + return header_len + payload_len; } ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, @@ -1112,7 +1168,7 @@ ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags) return -ENOSPC; } - *pkt->payload++ = 0xFF; + *pkt->payload++ = COAP_PAYLOAD_MARKER; pkt->payload_len--; } else { @@ -1337,7 +1393,7 @@ ssize_t coap_well_known_core_default_handler(coap_pkt_t *pkt, uint8_t *buf, \ bufpos += coap_put_option_ct(bufpos, 0, COAP_FORMAT_LINK); bufpos += coap_opt_put_block2(bufpos, COAP_OPT_CONTENT_FORMAT, &slicer, 1); - *bufpos++ = 0xff; + *bufpos++ = COAP_PAYLOAD_MARKER; for (unsigned i = 0; i < coap_resources_numof; i++) { if (i) {