diff --git a/README.md b/README.md index 712d66e8d5..78fe633d1c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Static Analysis](https://scan.coverity.com/projects/10970/badge.svg?flat=1)](https://scan.coverity.com/projects/obgm-libcoap) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/libcoap.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:libcoap) -Copyright (C) 2010—2021 by Olaf Bergmann and others +Copyright (C) 2010—2022 by Olaf Bergmann and others ABOUT LIBCOAP ============= @@ -51,6 +51,8 @@ The following RFCs are supported * RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option +* RFC9175: CoAP: Echo, Request-Tag, and Token Processing + There is (D)TLS support for the following libraries * OpenSSL (Minimum version 1.1.0) [PKI, PSK and PKCS11] diff --git a/doc/main.md b/doc/main.md index 3ffe3d1b94..f08a6a135b 100644 --- a/doc/main.md +++ b/doc/main.md @@ -4,7 +4,7 @@ libcoap {#mainpage} A C implementation of the Constrained Application Protocol (RFC 7252) ===================================================================== -Copyright (C) 2010--2021 by Olaf Bergmann and others +Copyright (C) 2010--2022 by Olaf Bergmann and others About libcoap ============= @@ -36,6 +36,8 @@ The following RFCs are supported * RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option +* RFC9175: CoAP: Echo, Request-Tag, and Token Processing + There is (D)TLS support for the following libraries * OpenSSL (Minimum version 1.1.0) [PKI, PSK and PKCS11] diff --git a/include/coap3/block.h b/include/coap3/block.h index c093c1cd78..e6896c3bc2 100644 --- a/include/coap3/block.h +++ b/include/coap3/block.h @@ -215,7 +215,7 @@ typedef void (*coap_release_large_data_t)(coap_session_t *session, * Used for a client request. * * If the data spans multiple PDUs, then the data will get transmitted using - * BLOCK1 option with the addition of the SIZE1 option. + * BLOCK1 option with the addition of the SIZE1 and RTAG options. * The underlying library will handle the transmission of the individual blocks. * Once the body of data has been transmitted (or a failure occurred), then * @p release_func (if not NULL) will get called so the application can diff --git a/include/coap3/coap_block_internal.h b/include/coap3/coap_block_internal.h index 26a0933418..9ed7a5a29f 100644 --- a/include/coap3/coap_block_internal.h +++ b/include/coap3/coap_block_internal.h @@ -189,7 +189,8 @@ int coap_handle_request_put_block(coap_context_t *context, #endif /* COAP_SERVER_SUPPORT */ #if COAP_CLIENT_SUPPORT -int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd); +int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, + coap_pdu_t *rcvd); int coap_handle_response_get_block(coap_context_t *context, coap_session_t *session, diff --git a/include/coap3/coap_session_internal.h b/include/coap3/coap_session_internal.h index 72fd0f9728..f5e823b108 100644 --- a/include/coap3/coap_session_internal.h +++ b/include/coap3/coap_session_internal.h @@ -133,7 +133,9 @@ struct coap_session_t { int dtls_event; /**< Tracking any (D)TLS events on this sesison */ uint8_t block_mode; /**< Zero or more COAP_BLOCK_ or'd options */ + uint32_t tx_rtag; /**< Next Request-Tag number to use */ uint64_t tx_token; /**< Next token number to use */ + coap_bin_const_t *echo; /**< Echo value to send with next request */ }; #if COAP_SERVER_SUPPORT diff --git a/include/coap3/pdu.h b/include/coap3/pdu.h index c6737b6d18..7e23ca94db 100644 --- a/include/coap3/pdu.h +++ b/include/coap3/pdu.h @@ -128,7 +128,9 @@ typedef enum coap_request_t { #define COAP_OPTION_PROXY_URI 35 /* CU-___U, String, 1-1034 B, RFC7252 */ #define COAP_OPTION_PROXY_SCHEME 39 /* CU-___U, String, 1-255 B, RFC7252 */ #define COAP_OPTION_SIZE1 60 /* __N_E_U, uint, 0-4 B, RFC7252 */ +#define COAP_OPTION_ECHO 252 /* _N__E_U, opaque, 0-40 B, RFC9175 */ #define COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */ +#define COAP_OPTION_RTAG 292 /* ___RE_U, opaque, 0-8 B, RFC9175 */ #define COAP_MAX_OPT 65535 /**< the highest option number we know */ diff --git a/man/coap.txt.in b/man/coap.txt.in index d109a8751c..963310c17a 100644 --- a/man/coap.txt.in +++ b/man/coap.txt.in @@ -68,6 +68,8 @@ See "RFC8768: Constrained Application Protocol (CoAP) Hop-Limit Option" +"RFC9175: CoAP: Echo, Request-Tag, and Token Processing" + for further information. BUGS diff --git a/man/coap_block.txt.in b/man/coap_block.txt.in index 44e1a8ee17..47f12d025d 100644 --- a/man/coap_block.txt.in +++ b/man/coap_block.txt.in @@ -180,7 +180,7 @@ but supports the transmission of data that has a body size that is potentially larger than can be fitted into a single client request PDU. The specified payload _data_ of length _length_ is associated with the _session_ with the first block of data added to the PDU _pdu_ along with the appropriate CoAP -options such as BLOCK1, and SIZE1 if the data does not fit in +options such as BLOCK1, SIZE1 and RTAG if the data does not fit in a single PDU. When the block receipt has been acknowledged by the peer, the library diff --git a/man/coap_pdu_setup.txt.in b/man/coap_pdu_setup.txt.in index c54f39d88b..4d2a8d6634 100644 --- a/man/coap_pdu_setup.txt.in +++ b/man/coap_pdu_setup.txt.in @@ -282,7 +282,7 @@ The following is the current list of options with their numeric value COAP_OPTION_IF_MATCH 1 /* C__RE__, opaque, 0-8 B, RFC7252 */ COAP_OPTION_URI_HOST 3 /* CU-___U, String, 1-255 B, RFC7252 */ COAP_OPTION_ETAG 4 /* ___RE__, opaque, 1-8 B, RFC7252 */ -ION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */ +COAP_OPTION_IF_NONE_MATCH 5 /* C___E__, empty, 0 B, RFC7252 */ COAP_OPTION_OBSERVE 6 /* _U-_E_U, empty/uint, 0 B/0-3 B, RFC7641 */ COAP_OPTION_URI_PORT 7 /* CU-___U, uint, 0-2 B, RFC7252 */ COAP_OPTION_LOCATION_PATH 8 /* ___RE__, String, 0-255 B, RFC7252 */ @@ -301,7 +301,9 @@ COAP_OPTION_SIZE2 28 /* __N_E_U, uint, 0-4 B, RFC7959 */ COAP_OPTION_PROXY_URI 35 /* CU-___U, String, 1-1034 B, RFC7252 */ COAP_OPTION_PROXY_SCHEME 39 /* CU-___U, String, 1-255 B, RFC7252 */ COAP_OPTION_SIZE1 60 /* __N_E_U, uint, 0-4 B, RFC7252 */ +COAP_OPTION_ECHO 252 /* _N__E_U, opaque, 0-40 B, RFC9175 */ COAP_OPTION_NORESPONSE 258 /* _U-_E_U, uint, 0-1 B, RFC7967 */ +COAP_OPTION_RTAG 292 /* ___RE_U, opaque, 0-8 B, RFC9175 */ ---- See FURTHER INFORMATION as to how to get the latest list. @@ -570,6 +572,8 @@ See "RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)" +"RFC9175: CoAP: Echo, Request-Tag, and Token Processing" + for further information. See https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#option-numbers diff --git a/src/block.c b/src/block.c index ea1166eeac..294aa1f15c 100644 --- a/src/block.c +++ b/src/block.c @@ -411,6 +411,8 @@ coap_add_data_large_internal(coap_session_t *session, } avail = pdu->max_size - pdu->used_size - pdu->hdr_size; + /* There may be a response with Echo option */ + avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); /* May need token of length 8, so account for this */ avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0; blk_size = coap_flsll((long long)avail) - 4 - 1; @@ -494,8 +496,12 @@ coap_add_data_large_internal(coap_session_t *session, coap_encode_var_safe(buf, sizeof(buf), (unsigned int)length), buf); - } - else { + coap_update_option(pdu, + COAP_OPTION_RTAG, + coap_encode_var_safe8(buf, sizeof(buf), + ++session->tx_rtag), + buf); + } else { /* * resource+query match is used for BLOCK2 large body transmissions * token match is used for BLOCK1 large body transmissions @@ -561,6 +567,8 @@ coap_add_data_large_internal(coap_session_t *session, /* Check we still have space after adding in some options */ avail = pdu->max_size - pdu->used_size - pdu->hdr_size; + /* There may be a response with Echo option */ + avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); /* May need token of length 8, so account for this */ avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0; if (avail < (ssize_t)chunk) { @@ -839,8 +847,6 @@ coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now) { coap_lg_crcv_t * coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) { coap_lg_crcv_t *lg_crcv; - uint8_t buf[8]; - size_t length; uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1); lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t)); @@ -881,12 +887,6 @@ coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) { /* Need to set up a base token for actual communications if retries needed */ lg_crcv->retry_counter = 1; lg_crcv->state_token = state_token; - length = coap_encode_var_safe8(buf, sizeof(lg_crcv->state_token), - lg_crcv->state_token); - if (!coap_update_token(pdu, length, buf)) { - coap_block_delete_lg_crcv(session, lg_crcv); - return NULL; - } /* In case it is there - must not be in continuing request PDUs */ coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1); @@ -1301,12 +1301,24 @@ coap_handle_request_put_block(coap_context_t *context, uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt), coap_opt_length(fmt_opt)) : COAP_MEDIATYPE_TEXT_PLAIN; + coap_opt_t *rtag_opt = coap_check_option(pdu, + COAP_OPTION_RTAG, + &opt_iter); + size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0; + const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL; total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt), coap_opt_length(size_opt)) : 0; offset = block.num << (block.szx + 4); LL_FOREACH(session->lg_srcv, p) { + if (rtag_opt || p->rtag_set == 1) { + if (!(rtag_opt && p->rtag_set == 1)) + continue; + if (p->rtag_length != rtag_length || + memcmp(p->rtag, rtag, rtag_length) != 0) + continue; + } if (resource == p->resource) { break; } @@ -1348,6 +1360,11 @@ coap_handle_request_put_block(coap_context_t *context, memcpy(p->observe, coap_opt_value(observe), p->observe_length); p->observe_set = 1; } + if (rtag_opt) { + p->rtag_length = (uint8_t)rtag_length; + memcpy(p->rtag, rtag, rtag_length); + p->rtag_set = 1; + } p->body_data = NULL; LL_PREPEND(session->lg_srcv, p); } @@ -1483,6 +1500,90 @@ coap_handle_request_put_block(coap_context_t *context, #endif /* COAP_SERVER_SUPPORT */ #if COAP_CLIENT_SUPPORT +static int +check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent, + coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) +{ + /* Check for ECHO option for freshness */ + coap_opt_iterator_t opt_iter; + coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter); + + if (opt) { + if (sent || lg_xmit || lg_crcv) { + /* Need to retransmit original request with ECHO added */ + coap_pdu_t *echo_pdu; + coap_mid_t mid; + const uint8_t *data; + size_t data_len; + int have_data = 0; + uint8_t ltoken[8]; + size_t ltoken_len; + uint64_t token; + + if (sent) { + if (coap_get_data(sent, &data_len, &data)) + have_data = 1; + } else if (lg_xmit) { + sent = &lg_xmit->pdu; + if (lg_xmit->length) { + size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4); + size_t offset = (lg_xmit->last_block + 1) * blk_size; + have_data = 1; + data = &lg_xmit->data[offset]; + data_len = (lg_xmit->length - offset) > blk_size ? blk_size : + lg_xmit->length - offset; + } + } else /* lg_crcv */ { + sent = &lg_crcv->pdu; + if (coap_get_data(sent, &data_len, &data)) + have_data = 1; + } + if (lg_xmit) { + token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, + ++lg_xmit->b.b1.count); + } else { + token = STATE_TOKEN_FULL(lg_crcv->state_token, + ++lg_crcv->retry_counter); + } + ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); + echo_pdu = coap_pdu_duplicate(sent, session, ltoken_len, ltoken, NULL); + if (!echo_pdu) + return 0; + if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO, + coap_opt_length(opt), coap_opt_value(opt))) + goto no_sent; + if (have_data) { + coap_add_data(echo_pdu, data_len, data); + } + + mid = coap_send_internal(session, echo_pdu); + if (mid == COAP_INVALID_MID) + goto no_sent; + return 1; + } else { + /* Need to save ECHO value to add to next reansmission */ +no_sent: + coap_delete_bin_const(session->echo); + session->echo = coap_new_bin_const(coap_opt_value(opt), + coap_opt_length(opt)); + } + } + return 0; +} + +static void +track_echo(coap_session_t *session, coap_pdu_t *rcvd) +{ + coap_opt_iterator_t opt_iter; + coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter); + + if (opt) { + coap_delete_bin_const(session->echo); + session->echo = coap_new_bin_const(coap_opt_value(opt), + coap_opt_length(opt)); + } +} + /* * Need to see if this is a response to a large body request transfer. If so, * need to initiate the request containing the next block and not trouble the @@ -1498,7 +1599,9 @@ coap_handle_request_put_block(coap_context_t *context, * 1 Do not call application handler - just send the built response */ int -coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) { +coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, + coap_pdu_t *rcvd) +{ coap_lg_xmit_t *p; coap_lg_xmit_t *q; uint64_t token_match = STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->token, @@ -1506,7 +1609,10 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) { LL_FOREACH_SAFE(session->lg_xmit, p, q) { if (!COAP_PDU_IS_REQUEST(&p->pdu) || - token_match != STATE_TOKEN_BASE(p->b.b1.state_token)) { + (token_match != STATE_TOKEN_BASE(p->b.b1.state_token) && + token_match != + STATE_TOKEN_BASE(coap_decode_var_bytes8(p->b.b1.app_token->s, + p->b.b1.app_token->length)))) { /* try out the next one */ continue; } @@ -1542,6 +1648,7 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) { (p->offset + chunk) % ((size_t)1 << (block.szx + 4))); } } + track_echo(session, rcvd); if (p->last_block == (int)block.num) { /* * Duplicate BLOCK ACK @@ -1586,6 +1693,9 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) { goto fail_body; return 1; } + } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { + if (check_freshness(session, rcvd, sent, p, NULL)) + return 1; } fail_body: if (session->lg_crcv) { @@ -1677,10 +1787,11 @@ coap_block_build_body(coap_binary_t *body_data, size_t length, */ int coap_handle_response_get_block(coap_context_t *context, - coap_session_t *session, - coap_pdu_t *sent, - coap_pdu_t *rcvd, - coap_recurse_t recursive) { + coap_session_t *session, + coap_pdu_t *sent, + coap_pdu_t *rcvd, + coap_recurse_t recursive) +{ coap_lg_crcv_t *p; int app_has_response = 0; coap_block_t block = {0, 0, 0}; @@ -1720,6 +1831,7 @@ coap_handle_response_get_block(coap_context_t *context, have_block = 1; block_opt = COAP_OPTION_BLOCK2; } + track_echo(session, rcvd); if (have_block) { coap_opt_t *fmt_opt = coap_check_option(rcvd, COAP_OPTION_CONTENT_FORMAT, @@ -1955,6 +2067,10 @@ coap_handle_response_get_block(coap_context_t *context, goto call_app_handler; } } + } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { + if (check_freshness(session, rcvd, sent, NULL, p)) + goto skip_app_handler; + goto fail_resp; } if (!block.m && !p->observe_set) { fail_resp: @@ -2006,6 +2122,15 @@ coap_handle_response_get_block(coap_context_t *context, coap_block_delete_lg_crcv(session, lg_crcv); } } + track_echo(session, rcvd); + } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { + coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent); + + if (lg_crcv) { + LL_PREPEND(session->lg_crcv, lg_crcv); + return coap_handle_response_get_block(context, session, sent, rcvd, + COAP_RECURSE_NO); + } } } return app_has_response; diff --git a/src/coap_debug.c b/src/coap_debug.c index 1b9c922c37..a41e603989 100644 --- a/src/coap_debug.c +++ b/src/coap_debug.c @@ -364,12 +364,14 @@ msg_option_string(uint8_t code, uint16_t option_type) { { COAP_OPTION_PROXY_URI, "Proxy-Uri" }, { COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme" }, { COAP_OPTION_SIZE1, "Size1" }, - { COAP_OPTION_NORESPONSE, "No-Response" } + { COAP_OPTION_ECHO, "Echo" }, + { COAP_OPTION_NORESPONSE, "No-Response" }, + { COAP_OPTION_RTAG, "RTag" } }; static struct option_desc_t options_csm[] = { { COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size" }, - { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer" } + { COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-Wise-Transfer" } }; static struct option_desc_t options_pingpong[] = { @@ -642,6 +644,9 @@ coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu) { case COAP_OPTION_IF_MATCH: case COAP_OPTION_ETAG: + case COAP_OPTION_ECHO: + case COAP_OPTION_NORESPONSE: + case COAP_OPTION_RTAG: opt_len = coap_opt_length(option); opt_val = coap_opt_value(option); snprintf((char *)buf, sizeof(buf), "0x"); @@ -864,7 +869,7 @@ char *coap_string_tls_support(char *buffer, size_t bufsize) if (have_dtls == 0 && have_tls == 0) { snprintf(buffer, bufsize, "(No DTLS or TLS support)"); return buffer; - } + } switch (tls_version->type) { case COAP_TLS_LIBRARY_NOTLS: snprintf(buffer, bufsize, "(No DTLS or TLS support)"); diff --git a/src/coap_session.c b/src/coap_session.c index 230dc8b528..415ee16c3c 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -160,8 +160,9 @@ coap_make_session(coap_proto_t proto, coap_session_type_t type, session->dtls_event = -1; session->last_ping_mid = COAP_INVALID_MID; - /* initialize message id */ + /* Randomly initialize */ coap_prng((unsigned char *)&session->tx_mid, sizeof(session->tx_mid)); + coap_prng((unsigned char *)&session->tx_rtag, sizeof(session->tx_rtag)); return session; } diff --git a/src/net.c b/src/net.c index a7179f54fc..f3fe31acdf 100644 --- a/src/net.c +++ b/src/net.c @@ -1248,6 +1248,14 @@ coap_send_internal(coap_session_t *session, coap_pdu_t *pdu) { } } + if (session->echo) { + if (!coap_insert_option(pdu, COAP_OPTION_ECHO, session->echo->length, + session->echo->s)) + goto error; + coap_delete_bin_const(session->echo); + session->echo = NULL; + } + if (!coap_pdu_encode_header(pdu, session->proto)) { goto error; } @@ -2852,7 +2860,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu if (observe) coap_delete_observer(resource, session, &token); if (added_block) - coap_remove_option(pdu, COAP_OPTION_BLOCK1); + coap_remove_option(response, COAP_OPTION_BLOCK1); } /* If original request contained a token, and the registered @@ -2919,8 +2927,8 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu #if COAP_CLIENT_SUPPORT static void handle_response(coap_context_t *context, coap_session_t *session, - coap_pdu_t *sent, coap_pdu_t *rcvd) { - + coap_pdu_t *sent, coap_pdu_t *rcvd) +{ /* In a lossy context, the ACK of a separate response may have * been lost, so we need to stop retransmitting requests with the * same token. @@ -2930,7 +2938,7 @@ handle_response(coap_context_t *context, coap_session_t *session, if (session->block_mode & COAP_BLOCK_USE_LIBCOAP) { /* See if need to send next block to server */ - if (coap_handle_response_send_block(session, rcvd)) { + if (coap_handle_response_send_block(session, sent, rcvd)) { /* Next block transmitted, no need to inform app */ coap_send_ack(session, rcvd); return; diff --git a/src/pdu.c b/src/pdu.c index ea49cbb649..38eac5345f 100644 --- a/src/pdu.c +++ b/src/pdu.c @@ -417,6 +417,28 @@ coap_remove_option(coap_pdu_t *pdu, coap_option_num_t number) { return 1; } +static void +check_repeatable(coap_option_num_t number) +{ + /* Validate that the option is repeatable */ + switch (number) { + /* Ignore list of genuine repeatable */ + case COAP_OPTION_IF_MATCH: + case COAP_OPTION_ETAG: + case COAP_OPTION_LOCATION_PATH: + case COAP_OPTION_URI_PATH: + case COAP_OPTION_URI_QUERY: + case COAP_OPTION_LOCATION_QUERY: + case COAP_OPTION_RTAG: + break; + default: + coap_log(LOG_INFO, "Option number %d is not defined as repeatable\n", + number); + /* Accepting it after warning as there may be user defineable options */ + break; + } +} + size_t coap_insert_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data) { @@ -448,6 +470,9 @@ coap_insert_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token), &decode)) return 0; opt_delta = opt_iter.number - number; + if (opt_delta == 0) { + check_repeatable(number); + } if (!coap_pdu_check_resize(pdu, pdu->used_size + shift - shrink)) @@ -565,22 +590,7 @@ coap_add_option_internal(coap_pdu_t *pdu, coap_option_num_t number, size_t len, assert(pdu); if (number == pdu->max_opt) { - /* Validate that the option is repeatable */ - switch (number) { - /* Ignore list of genuine repeatable */ - case COAP_OPTION_IF_MATCH: - case COAP_OPTION_ETAG: - case COAP_OPTION_LOCATION_PATH: - case COAP_OPTION_URI_PATH: - case COAP_OPTION_URI_QUERY: - case COAP_OPTION_LOCATION_QUERY: - break; - default: - coap_log(LOG_INFO, "Option number %d is not defined as repeatable\n", - number); - /* Accepting it after warning as there may be user defineable options */ - break; - } + check_repeatable(number); } if (COAP_PDU_IS_REQUEST(pdu) && @@ -951,7 +961,9 @@ coap_pdu_parse_opt_base(coap_pdu_t *pdu, uint16_t len) { case COAP_OPTION_PROXY_URI: if (len < 1 || len > 1034) res = 0; break; case COAP_OPTION_PROXY_SCHEME: if (len < 1 || len > 255) res = 0; break; case COAP_OPTION_SIZE1: if (len > 4) res = 0; break; + case COAP_OPTION_ECHO: if (len > 40) res = 0; break; case COAP_OPTION_NORESPONSE: if (len > 1) res = 0; break; + case COAP_OPTION_RTAG: if (len > 8) res = 0; break; default: ; }