From 37c239be1d39c22a2980969bb60fe5801710b504 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Thu, 9 Dec 2021 11:59:50 +0000 Subject: [PATCH] RFC9175: Add in support for the Echo and Request-Tag options A unique Request-Tag is added to any PUT/POST/FETCH request that contains a large body. The server is then able to differentiate between concurrent sending of large data using the same session. If a server responds with a 4.01 and includes an Echo option, then the libcoap client retransmits the request adding in the received Echo option with the same value. If the server responds normally with an Echo option, then the next request from the client (internally added by libcoap) will contain that Echo option. It is the responsibility of the server application to add in the Echo option if required unless this is triggered by OSCORE. --- README.md | 4 +- doc/main.md | 4 +- include/coap3/block.h | 16 +-- include/coap3/coap_block_internal.h | 3 +- include/coap3/coap_session_internal.h | 2 + include/coap3/pdu.h | 2 + man/coap.txt.in | 2 + man/coap_block.txt.in | 18 +-- src/block.c | 157 +++++++++++++++++++++++--- src/coap_debug.c | 11 +- src/coap_session.c | 3 +- src/net.c | 16 ++- src/pdu.c | 44 +++++--- 13 files changed, 222 insertions(+), 60 deletions(-) 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..ba7aa1b7f5 100644 --- a/include/coap3/block.h +++ b/include/coap3/block.h @@ -167,7 +167,7 @@ coap_block_build_body(coap_binary_t *body_data, size_t length, /** * Adds the appropriate part of @p data to the @p response pdu. If blocks are * required, then the appropriate block will be added to the PDU and sent. - * Adds a ETAG option that is the hash of the entire data if the data is to be + * Adds a ETag option that is the hash of the entire data if the data is to be * split into blocks * Used by a request handler. * @@ -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 Request-Tag 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 @@ -223,7 +223,7 @@ typedef void (*coap_release_large_data_t)(coap_session_t *session, * the application not to change the contents of @p data until the data * transfer has completed. * - * There is no need for the application to include the BLOCK1 option in the + * There is no need for the application to include the Block1 option in the * @p pdu. * * coap_add_data_large_request() (or the alternative coap_add_data_large_*() @@ -260,13 +260,13 @@ int coap_add_data_large_request(coap_session_t *session, * * If all the data can be transmitted in a single PDU, this is functionally * the same as coap_add_data() except @p release_func (if not NULL) will get - * invoked after data transmission. The MEDIA_TYPE, MAXAGE and ETAG options may - * be added in as appropriate. + * invoked after data transmission. The Content-Format, Max-Age and ETag + * options may be added in as appropriate. * * Used by a server request handler to create the response. * * If the data spans multiple PDUs, then the data will get transmitted using - * BLOCK2 (response) option with the addition of the SIZE2 and ETAG + * Block2 (response) option with the addition of the Size2 and ETag * 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 @@ -274,7 +274,7 @@ int coap_add_data_large_request(coap_session_t *session, * responsibility of the application not to change the contents of @p data * until the data transfer has completed. * - * There is no need for the application to include the BLOCK2 option in the + * There is no need for the application to include the Block2 option in the * @p pdu. * * coap_add_data_large_response() (or the alternative coap_add_data_large_*() @@ -289,7 +289,7 @@ int coap_add_data_large_request(coap_session_t *session, * @param request The requesting pdu. * @param response The response pdu. * @param query The query taken from the (original) requesting pdu. - * @param media_type The format of the data. + * @param media_type The content format of the data. * @param maxage The maxmimum life of the data. If @c -1, then there * is no maxage. * @param etag ETag to use if not 0. 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..43568c07c3 100644 --- a/man/coap_block.txt.in +++ b/man/coap_block.txt.in @@ -99,7 +99,7 @@ In RAM constrained environments, option 2 may be the preferred method. This man page focuses on getting libcoap to do all the work, not how to do it all in the application. -However, if the client supplies a BLOCK1 or BLOCK2 Option in the PDU where the +However, if the client supplies a Block1 or Block2 Option in the PDU where the block number is not 0, this is assumed to be a random access request and any other blocks will not be requested by libcoap even if instructed otherwise. @@ -167,7 +167,7 @@ If COAP_BLOCK_USE_LIBCOAP is set, then any PDUs presented to the application handlers will get the tokens set back to the initiating token so that requests can be matched with responses even if different tokens had to be used for the series of packet interchanges. Furthermore, if COAP_BLOCK_SINGLE_BODY is set, -then the PDU that presents the entire body will have any BLOCKx option removed. +then the PDU that presents the entire body will have any BlockX option removed. *NOTE:* COAP_BLOCK_USE_LIBCOAP must be set if libcoap is to do all the block tracking and requesting, otherwise the application will have to do all @@ -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 Request-Tag if the data does not fit in a single PDU. When the block receipt has been acknowledged by the peer, the library @@ -216,7 +216,7 @@ NULL, the callback function is called once the final block of _data_ has been transmitted. The user-defined parameter _app_ptr_ is the same value that was passed to *coap_add_data_large_response*(). -It also adds in the appropriate CoAP options such as BLOCK2, SIZE2 and ETAG to +It also adds in the appropriate CoAP options such as Block2, Size2 and ETag to handle block-wise transfer if the data does not fit in a single PDU. _resource_, _query_, _session_, _request_, and _response_ are the same @@ -224,7 +224,7 @@ parameters as in the called resource handler that invokes *coap_add_data_large_response*(). If _etag_ is 0, then a unique ETag value will be generated, else is the ETag value to use. The _media_type_ is for the format of the _data_ and _maxage_ defines the -lifetime of the response. If _maxage_ is set to -1, then the MAXAGE option +lifetime of the response. If _maxage_ is set to -1, then the Max-Age option does not get included (which indicates the default value of 60 seconds according to RFC 7252). @@ -444,10 +444,10 @@ const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { * Define the format - COAP_MEDIATYPE_TEXT_PLAIN - to add in * Define how long this response is valid for (secs) - 1 - to add in. * - * OBSERVE Option added internally if needed within the function - * BLOCK2 Option added internally if output too large - * SIZE2 Option added internally - * ETAG Option added internally + * Observe Option added internally if needed within the function + * Block2 Option added internally if output too large + * Size2 Option added internally + * ETag Option added internally */ coap_add_data_large_response(resource, session, request, response, query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0, 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..452813cdae 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, "Request-Tag" } }; 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 cc8fcc1be7..83aeec72fa 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)) @@ -560,22 +585,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) && @@ -946,7 +956,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: ; }