From c9f40d7d09b2e9f24fc7d19445e90fb3ce6f0e38 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Thu, 6 Oct 2022 13:40:34 +0100 Subject: [PATCH] request-tag: Add in support for sending Request-Tag option with every request If multiple requests (apart from DELETE) are sent at the same time to the same resource, and the responses need to make use of the Block2 option, there is no way for the server to differentiate between which block set a request (if there is a resource content change) is for unless a Request-Tag is used. Disable this functionality by setting COAP_BLOCK_NO_PREEMPTIVE_RTAG when calling coap_context_set_block_mode(). Request-Tag will still be sent with any request that includes Block1 option. --- examples/coap-client.c | 2 +- include/coap3/block.h | 1 + include/coap3/coap_block_internal.h | 28 +- man/coap-client.txt.in | 5 +- man/coap_block.txt.in | 33 +- src/block.c | 459 +++++++++++++++------------- src/net.c | 12 +- src/resource.c | 3 +- 8 files changed, 310 insertions(+), 233 deletions(-) diff --git a/examples/coap-client.c b/examples/coap-client.c index 400f4a7ec6..09c05b24de 100644 --- a/examples/coap-client.c +++ b/examples/coap-client.c @@ -556,7 +556,7 @@ usage(const char *program, const char *version) { "\t-K interval\tSend a ping after interval seconds of inactivity\n" "\t-L value\tSum of one or more COAP_BLOCK_* flag valuess for block\n" "\t \t\thandling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP)\n" - "\t \t\t(Sum of one or more of 1,2,4 and 8)\n" + "\t \t\t(Sum of one or more of 1,2 and 16)\n" "\t-N \t\tSend NON-confirmable message\n" "\t-O num,text\tAdd option num with contents text to request. If the\n" "\t \t\ttext begins with 0x, then the hex text (two [0-9a-f] per\n" diff --git a/include/coap3/block.h b/include/coap3/block.h index 52fa5e1f73..1e3b8f76e0 100644 --- a/include/coap3/block.h +++ b/include/coap3/block.h @@ -60,6 +60,7 @@ typedef struct { #define COAP_BLOCK_USE_LIBCOAP 0x01 /* Use libcoap to do block requests */ #define COAP_BLOCK_SINGLE_BODY 0x02 /* Deliver the data as a single body */ +#define COAP_BLOCK_NO_PREEMPTIVE_RTAG 0x10 /* Don't use pre-emptive Request-Tags */ /** * Returns the value of the least significant byte of a Block option @p opt. diff --git a/include/coap3/coap_block_internal.h b/include/coap3/coap_block_internal.h index fe5c7d3a71..105711e959 100644 --- a/include/coap3/coap_block_internal.h +++ b/include/coap3/coap_block_internal.h @@ -70,8 +70,11 @@ typedef struct coap_l_block2_t { coap_resource_t *resource; /**< associated resource */ coap_string_t *query; /**< Associated query for the resource */ uint64_t etag; /**< ETag value */ - coap_time_t maxage_expire; /**< When this entry expires */ coap_pdu_code_t request_method; /**< Method used to request this data */ + uint8_t rtag_set; /**< Set if RTag is in receive PDU */ + uint8_t rtag_length; /**< RTag length */ + uint8_t rtag[8]; /**< RTag for block checking */ + coap_time_t maxage_expire; /**< When this entry expires */ } coap_l_block2_t; /** @@ -192,6 +195,11 @@ int coap_handle_request_put_block(coap_context_t *context, coap_string_t *query, coap_method_handler_t h, int *added_block); + +coap_lg_xmit_t * coap_find_lg_xmit_response(const coap_session_t *session, + const coap_pdu_t *request, + const coap_resource_t *resource, + const coap_string_t *query); #endif /* COAP_SERVER_SUPPORT */ #if COAP_CLIENT_SUPPORT @@ -216,15 +224,17 @@ int coap_block_check_lg_xmit_timeouts(coap_session_t *session, * The function checks that the code in a newly formed lg_xmit created by * coap_add_data_large_response() is updated. * - * @param session The session - * @param response The response PDU to to check - * @param resource The requested resource - * @param query The requested query - * @param request_method The requested method + * @param session The session. + * @param request The request PDU to to check. + * @param response The response PDU to to update with response->code. + * @param resource The requested resource. + * @param query The requested query. */ -void coap_check_code_lg_xmit(coap_session_t *session, coap_pdu_t *response, - coap_resource_t *resource, coap_string_t *query, - coap_pdu_code_t request_method); +void coap_check_code_lg_xmit(const coap_session_t *session, + const coap_pdu_t *request, + coap_pdu_t *response, + const coap_resource_t *resource, + const coap_string_t *query); /** @} */ diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index b2e6a1f769..ae4bdb4421 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -143,8 +143,9 @@ OPTIONS - General Sum of one or more COAP_BLOCK_* flag values for different block handling methods. Default is 1 (COAP_BLOCK_USE_LIBCOAP). - COAP_BLOCK_USE_LIBCOAP 1 - COAP_BLOCK_SINGLE_BODY 2 + COAP_BLOCK_USE_LIBCOAP 1 + COAP_BLOCK_SINGLE_BODY 2 + COAP_BLOCK_NO_PREEMPTIVE_RTAG 16 *-N* :: Send NON-confirmable message. If option *-N* is not specified, a diff --git a/man/coap_block.txt.in b/man/coap_block.txt.in index 4e6a999002..38109e40f2 100644 --- a/man/coap_block.txt.in +++ b/man/coap_block.txt.in @@ -138,40 +138,49 @@ session _block_mode_. [source, c] ---- -#define COAP_BLOCK_USE_LIBCOAP 0x01 /* Use libcoap to do block requests */ -#define COAP_BLOCK_SINGLE_BODY 0x02 /* Deliver the data as a single body */ +#define COAP_BLOCK_USE_LIBCOAP 0x01 /* Use libcoap to do block requests */ +#define COAP_BLOCK_SINGLE_BODY 0x02 /* Deliver the data as a single body */ +#define COAP_BLOCK_NO_PREEMPTIVE_RTAG 0x10 /* Don't use pre-emptive Request-Tags */ ---- _block_mode_ is an or'd set of zero or more COAP_BLOCK_* definitions. -If COAP_BLOCK_USE_LIBCOAP is not set, then everything works as per Option 1 +If *COAP_BLOCK_USE_LIBCOAP* is not set, then everything works as per Option 1 above. -If COAP_BLOCK_SINGLE_BODY is set, then the entire body of data is presented to +If *COAP_BLOCK_SINGLE_BODY* is set, then the entire body of data is presented to the receiving handler, otherwise each individual block is presented on arrival. To obtain the data, length and current offset, *coap_get_data_large*() must be used instead of *coap_get_data*(). It may be appropriate not to set -COAP_BLOCK_SINGLE_BODY if there are RAM limitations. +*COAP_BLOCK_SINGLE_BODY* if there are RAM limitations. *NOTE:* It is the responsibility of the receiving application to re-assemble the _data_ as appropriate (e.g., using *coap_block_build_body*()) if -COAP_BLOCK_SINGLE_BODY is not set. +*COAP_BLOCK_SINGLE_BODY* is not set. -*NOTE:* If COAP_BLOCK_SINGLE_BODY is not set, then the CoAP server on receiving +*NOTE:* If *COAP_BLOCK_SINGLE_BODY* is not set, then the CoAP server on +receiving request data that is split over multiple data blocks must respond with COAP_RESPONSE_CODE_CONTINUE 2.31 (Continue) response code if the received data is not for the final block, otherwise a COAP_RESPONSE_CODE_CREATED 2.01 (Created) or COAP_RESPONSE_CODE_CHANGED 2.04 (Changed) should be returned. -If COAP_BLOCK_USE_LIBCOAP is set, then any PDUs presented to the application +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, +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. -*NOTE:* COAP_BLOCK_USE_LIBCOAP must be set if libcoap is to do all the +*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 of this work (the default if *coap_context_set_block_mode*() is not called). +If *COAP_BLOCK_NO_PREEMPTIVE_RTAG* is set, then Request-Tag options are only +sent when a large amount of data is being sent to the server using the Block1 +option. Otherwise, a Request-Tag option is sent with any request (apart from +DELETE) on the off chance that there may be multiple Block2 based +responses for multiple requests to the same resource that need to be +differentiated between. + *Function: coap_add_data_large_request()* The *coap_add_data_large_request*() function is similar to *coap_add_data*(), @@ -517,9 +526,9 @@ FURTHER INFORMATION ------------------- See -"RFC7252: The Constrained Application Protocol (CoAP)" +"https://tools.ietf.org/html/rfc7252[RFC7252: The Constrained Application Protocol (CoAP)]" -"RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)" +"https://tools.ietf.org/html/rfc7959[RFC7959: Block-Wise Transfers in the Constrained Application Protocol (CoAP)]" for further information. diff --git a/src/block.c b/src/block.c index a9620cc67c..af5885b63b 100644 --- a/src/block.c +++ b/src/block.c @@ -364,7 +364,8 @@ void coap_context_set_block_mode(coap_context_t *context, uint8_t block_mode) { context->block_mode = block_mode &= (COAP_BLOCK_USE_LIBCOAP | - COAP_BLOCK_SINGLE_BODY); + COAP_BLOCK_SINGLE_BODY | + COAP_BLOCK_NO_PREEMPTIVE_RTAG); if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) context->block_mode = 0; } @@ -432,8 +433,53 @@ coap_cancel_observe(coap_session_t *session, coap_binary_t *token, } #endif /* COAP_CLIENT_SUPPORT */ +#if COAP_SERVER_SUPPORT +/* + * Find the response lg_xmit + */ +coap_lg_xmit_t * +coap_find_lg_xmit_response(const coap_session_t *session, + const coap_pdu_t *request, + const coap_resource_t *resource, + const coap_string_t *query) +{ + coap_lg_xmit_t *lg_xmit; + coap_opt_iterator_t opt_iter; + coap_opt_t *rtag_opt = coap_check_option(request, + 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; + + LL_FOREACH(session->lg_xmit, lg_xmit) { + static coap_string_t empty = { 0, NULL}; + + if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu) || + resource != lg_xmit->b.b2.resource || + request->code != lg_xmit->b.b2.request_method || + !coap_string_equal(query ? query : &empty, + lg_xmit->b.b2.query ? + lg_xmit->b.b2.query : &empty)) { + /* try out the next one */ + continue; + } + /* lg_xmit is a response */ + if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) { + if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1)) + continue; + if (lg_xmit->b.b2.rtag_length != rtag_length || + memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0) + continue; + } + return lg_xmit; + } + return NULL; +} +#endif /* COAP_SERVER_SUPPORT */ + static int coap_add_data_large_internal(coap_session_t *session, + const coap_pdu_t *request, coap_pdu_t *pdu, coap_resource_t *resource, const coap_string_t *query, @@ -454,6 +500,8 @@ coap_add_data_large_internal(coap_session_t *session, uint8_t blk_size; uint16_t option; size_t token_options; + coap_opt_t *opt; + coap_opt_iterator_t opt_iter; assert(pdu); if (pdu->data) { @@ -504,25 +552,22 @@ coap_add_data_large_internal(coap_session_t *session, } else { /* Have to assume that it is a response even if code is 0.00 */ - coap_lg_xmit_t *q; - coap_string_t empty = { 0, NULL}; - assert(resource); option = COAP_OPTION_BLOCK2; - /* Check if resource+query is already in use for large bodies (unlikely) */ - LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { - if (resource == lg_xmit->b.b2.resource && - request_method == lg_xmit->b.b2.request_method && - coap_string_equal(query ? query : &empty, - lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) { - /* Unfortunately need to free this off as potential size change */ - LL_DELETE(session->lg_xmit, lg_xmit); - coap_block_delete_lg_xmit(session, lg_xmit); - lg_xmit = NULL; - coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); - break; - } +#if COAP_SERVER_SUPPORT + /* + * Check if resource+query+rtag is already in use for large bodies + * (unlikely) + */ + lg_xmit = coap_find_lg_xmit_response(session, request, resource, query); + if (lg_xmit) { + /* Unfortunately need to free this off as potential size change */ + LL_DELETE(session->lg_xmit, lg_xmit); + coap_block_delete_lg_xmit(session, lg_xmit); + lg_xmit = NULL; + coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); } +#endif /* COAP_SERVER_SUPPORT */ } token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; @@ -614,14 +659,15 @@ coap_add_data_large_internal(coap_session_t *session, coap_encode_var_safe(buf, sizeof(buf), (unsigned int)length), buf); - coap_update_option(pdu, - COAP_OPTION_RTAG, - coap_encode_var_safe8(buf, sizeof(buf), - ++session->tx_rtag), - buf); + if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter)) + coap_insert_option(pdu, + COAP_OPTION_RTAG, + coap_encode_var_safe(buf, sizeof(buf), + ++session->tx_rtag), + buf); } else { /* - * resource+query match is used for Block2 large body transmissions + * resource+query+rtag match is used for Block2 large body transmissions * token match is used for Block1 large body transmissions */ lg_xmit->b.b2.resource = resource; @@ -630,9 +676,18 @@ coap_add_data_large_internal(coap_session_t *session, if (lg_xmit->b.b2.query) { memcpy(lg_xmit->b.b2.query->s, query->s, query->length); } + } else { + lg_xmit->b.b2.query = NULL; + } + opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter); + if (opt) { + lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt), + sizeof(lg_xmit->b.b2.rtag)); + memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), coap_opt_length(opt)); + lg_xmit->b.b2.rtag_set = 1; } else { - lg_xmit->b.b2.query = NULL; + lg_xmit->b.b2.rtag_set = 0; } lg_xmit->b.b2.etag = etag; lg_xmit->b.b2.request_method = request_method; @@ -769,8 +824,8 @@ coap_add_data_large_request(coap_session_t *session, release_func(session, app_ptr); return 0; } - return coap_add_data_large_internal(session, pdu, NULL, NULL, -1, 0, length, - data, release_func, app_ptr, 0); + return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, + length, data, release_func, app_ptr, 0); } #endif /* ! COAP_CLIENT_SUPPORT */ @@ -847,10 +902,9 @@ coap_add_data_large_response(coap_resource_t *resource, /* add data body */ if (request && - !coap_add_data_large_internal(session, response, resource, query, - maxage, etag, length, data, - release_func, app_ptr, - request->code)) { + !coap_add_data_large_internal(session, request, response, resource, + query, maxage, etag, length, data, + release_func, app_ptr, request->code)) { response->code = COAP_RESPONSE_CODE(500); goto error_released; } @@ -1174,6 +1228,13 @@ coap_handle_request_send_block(coap_session_t *session, uint16_t block_opt = 0; uint32_t out_blocks[1]; const char *error_phrase; + coap_opt_iterator_t opt_iter; + size_t chunk; + coap_opt_iterator_t opt_b_iter; + coap_opt_t *option; + uint32_t request_cnt, i; + coap_opt_t *etag_opt = NULL; + coap_pdu_t *out_pdu = response; if (!coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &block)) return 0; @@ -1182,170 +1243,166 @@ coap_handle_request_send_block(coap_session_t *session, return 0; } block_opt = COAP_OPTION_BLOCK2; - LL_FOREACH(session->lg_xmit, p) { - size_t chunk; - coap_opt_iterator_t opt_iter; - coap_opt_iterator_t opt_b_iter; - coap_opt_t *option; - uint32_t request_cnt, i; - coap_opt_t *etag_opt = NULL; - coap_pdu_t *out_pdu = response; - static coap_string_t empty = { 0, NULL}; + p = coap_find_lg_xmit_response(session, pdu, resource, query); + if (p == NULL) + return 0; - if (COAP_PDU_IS_REQUEST(&p->pdu) || resource != p->b.b2.resource || - pdu->code != p->b.b2.request_method || - !coap_string_equal(query ? query : &empty, - p->b.b2.query ? p->b.b2.query : &empty)) { - /* try out the next one */ - continue; - } - p->last_all_sent = 0; - etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter); - if (etag_opt) { - uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt), - coap_opt_length(etag_opt)); - if (etag != p->b.b2.etag) { - /* try out the next one */ - continue; - } - out_pdu->code = COAP_RESPONSE_CODE(203); - coap_ticks(&p->last_sent); - goto skip_app_handler; - } - else { - out_pdu->code = p->pdu.code; - } + /* lg_xmit (response) found */ - /* lg_xmit (response) found */ - coap_ticks(&p->last_obs); + etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter); + if (etag_opt) { + uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt), + coap_opt_length(etag_opt)); + if (etag != p->b.b2.etag) { + /* Not a match - pass up to a higher level */ + return 0; + } + out_pdu->code = COAP_RESPONSE_CODE(203); + coap_ticks(&p->last_sent); + goto skip_app_handler; + } + else { + out_pdu->code = p->pdu.code; + } + coap_ticks(&p->last_obs); + p->last_all_sent = 0; - chunk = (size_t)1 << (p->blk_size + 4); - if (block_opt) { - if (block.bert) { + chunk = (size_t)1 << (p->blk_size + 4); + if (block_opt) { + if (block.bert) { + coap_log(LOG_DEBUG, + "found Block option, block is BERT, block nr. %u, M %d\n", + block.num, block.m); + } else { + coap_log(LOG_DEBUG, + "found Block option, block size is %u, block nr. %u, M %d\n", + 1 << (block.szx + 4), block.num, block.m); + } + if (block.bert == 0 && block.szx != p->blk_size) { + if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) { + /* + * Recompute the block number of the previous packet given + * the new block size + */ + block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1); + p->blk_size = block.szx; + chunk = (size_t)1 << (p->blk_size + 4); + p->offset = block.num * chunk; coap_log(LOG_DEBUG, - "found Block option, block is BERT, block nr. %u\n", - block.num); + "new Block size is %u, block number %u completed\n", + 1 << (block.szx + 4), block.num); } else { coap_log(LOG_DEBUG, - "found Block option, block size is %u, block nr. %u\n", - 1 << (block.szx + 4), block.num); - } - if (block.bert == 0 && block.szx != p->blk_size) { - if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) { - /* - * Recompute the block number of the previous packet given - * the new block size - */ - block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1); - p->blk_size = block.szx; - chunk = (size_t)1 << (p->blk_size + 4); - p->offset = block.num * chunk; - coap_log(LOG_DEBUG, - "new Block size is %u, block number %u completed\n", - 1 << (block.szx + 4), block.num); - } else { - coap_log(LOG_DEBUG, - "ignoring request to increase Block size, " - "next block is not aligned on requested block size " - "boundary. (%zu x %u mod %u = %zu (which is not 0)\n", - p->offset/chunk + 1, (1 << (p->blk_size + 4)), - (1 << (block.szx + 4)), - (p->offset + chunk) % ((size_t)1 << (block.szx + 4))); - } + "ignoring request to increase Block size, " + "next block is not aligned on requested block size " + "boundary. (%zu x %u mod %u = %zu (which is not 0)\n", + p->offset/chunk + 1, (1 << (p->blk_size + 4)), + (1 << (block.szx + 4)), + (p->offset + chunk) % ((size_t)1 << (block.szx + 4))); } } + } - request_cnt = 0; - coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL); - while ((option = coap_option_next(&opt_b_iter))) { - unsigned int num; - if (opt_b_iter.number != p->option) - continue; - num = coap_opt_block_num(option); - if (num > 0xFFFFF) /* 20 bits max for num */ - continue; - if (block.aszx != COAP_OPT_BLOCK_SZX(option)) { - coap_add_data(response, - sizeof("Changing blocksize during request invalid")-1, - (const uint8_t *)"Changing blocksize during request invalid"); - response->code = COAP_RESPONSE_CODE(400); - return 1; - } - add_block_send(num, out_blocks, &request_cnt, 1); - break; - } - if (request_cnt == 0) { - /* Block2 not found - give them the first block */ - block.szx = p->blk_size; - p->offset = 0; - out_blocks[0] = 0; - request_cnt = 1; + request_cnt = 0; + coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL); + while ((option = coap_option_next(&opt_b_iter))) { + unsigned int num; + if (opt_b_iter.number != p->option) + continue; + num = coap_opt_block_num(option); + if (num > 0xFFFFF) /* 20 bits max for num */ + continue; + if (block.aszx != COAP_OPT_BLOCK_SZX(option)) { + coap_add_data(response, + sizeof("Changing blocksize during request invalid")-1, + (const uint8_t *)"Changing blocksize during request invalid"); + response->code = COAP_RESPONSE_CODE(400); + return 1; } + add_block_send(num, out_blocks, &request_cnt, 1); + break; + } + if (request_cnt == 0) { + /* Block2 not found - give them the first block */ + block.szx = p->blk_size; + p->offset = 0; + out_blocks[0] = 0; + request_cnt = 1; + } - for (i = 0; i < request_cnt; i++) { - uint8_t buf[8]; + for (i = 0; i < request_cnt; i++) { + uint8_t buf[8]; - block.num = out_blocks[i]; - p->offset = block.num * chunk; + block.num = out_blocks[i]; + p->offset = block.num * chunk; - if (i + 1 < request_cnt) { - /* Need to set up a copy of the pdu to send */ - coap_opt_filter_t drop_options; + if (i + 1 < request_cnt) { + /* Need to set up a copy of the pdu to send */ + coap_opt_filter_t drop_options; - memset(&drop_options, 0, sizeof(coap_opt_filter_t)); + memset(&drop_options, 0, sizeof(coap_opt_filter_t)); + if (block.num != 0) coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); - out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->token_length, - pdu->token, &drop_options); - if (!out_pdu) { - goto internal_issue; - } + out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->token_length, + pdu->token, &drop_options); + if (!out_pdu) { + goto internal_issue; } - else { - /* - * Copy the options across and then fix the block option - * - * Need to drop Observe option if Block2 and block.num != 0 - */ - coap_option_iterator_init(&p->pdu, &opt_iter, COAP_OPT_ALL); - while ((option = coap_option_next(&opt_iter))) { - if (opt_iter.number == COAP_OPTION_OBSERVE) - continue; - if (opt_iter.number == p->option) - continue; - if (!coap_insert_option(response, opt_iter.number, - coap_opt_length(option), - coap_opt_value(option))) { - goto internal_issue; - } + } + else { + /* + * Copy the options across and then fix the block option + * + * Need to drop Observe option if Block2 and block.num != 0 + */ + coap_option_iterator_init(&p->pdu, &opt_iter, COAP_OPT_ALL); + while ((option = coap_option_next(&opt_iter))) { + if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0) + continue; + if (!coap_insert_option(response, opt_iter.number, + coap_opt_length(option), + coap_opt_value(option))) { + goto internal_issue; } - out_pdu = response; } - if (pdu->type == COAP_MESSAGE_NON) - out_pdu->type = COAP_MESSAGE_NON; - if (block.bert) { - size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; - block.m = (p->length - p->offset) > - ((out_pdu->max_size - token_options) /1024) * 1024; - } else { - block.m = (p->offset + chunk) < p->length; - } - if (!coap_update_option(out_pdu, p->option, - coap_encode_var_safe(buf, - sizeof(buf), - (block.num << 4) | - (block.m << 3) | - block.aszx), - buf)) { - goto internal_issue; + out_pdu = response; + } + if (pdu->type == COAP_MESSAGE_NON) + out_pdu->type = COAP_MESSAGE_NON; + if (block.bert) { + size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; + block.m = (p->length - p->offset) > + ((out_pdu->max_size - token_options) /1024) * 1024; + } else { + block.m = (p->offset + chunk) < p->length; + } + if (!coap_update_option(out_pdu, p->option, + coap_encode_var_safe(buf, + sizeof(buf), + (block.num << 4) | + (block.m << 3) | + block.aszx), + buf)) { + goto internal_issue; + } + if (!(p->offset + chunk < p->length)) { + /* Last block - keep in cache for 4 * ACK_TIMOUT */ + coap_ticks(&p->last_all_sent); + } + if (p->b.b2.maxage_expire) { + coap_tick_t now; + coap_time_t rem; + + coap_ticks(&now); + rem = coap_ticks_to_rt(now); + if (p->b.b2.maxage_expire > rem) { + rem = p->b.b2.maxage_expire - rem; } if (!(p->offset + chunk < p->length)) { /* Last block - keep in cache for 4 * ACK_TIMOUT */ coap_ticks(&p->last_all_sent); } if (p->b.b2.maxage_expire) { - coap_tick_t now; - coap_time_t rem; - coap_ticks(&now); rem = coap_ticks_to_rt(now); if (p->b.b2.maxage_expire > rem) { @@ -1364,23 +1421,21 @@ coap_handle_request_send_block(coap_session_t *session, goto internal_issue; } } - - if (!etag_opt && !coap_add_block_b_data(out_pdu, - p->length, - p->data, - &block)) { - goto internal_issue; - } - if (i + 1 < request_cnt) { - coap_ticks(&p->last_sent); - coap_send_internal(session, out_pdu); - } } - coap_ticks(&p->last_payload); - goto skip_app_handler; - } /* end of LL_FOREACH() */ - return 0; + if (!etag_opt && !coap_add_block_b_data(out_pdu, + p->length, + p->data, + &block)) { + goto internal_issue; + } + if (i + 1 < request_cnt) { + coap_ticks(&p->last_sent); + coap_send_internal(session, out_pdu); + } + } + coap_ticks(&p->last_payload); + goto skip_app_handler; internal_issue: response->code = COAP_RESPONSE_CODE(500); @@ -1648,7 +1703,7 @@ coap_handle_request_put_block(coap_context_t *context, /* Need to do this here as we need to free off p */ h(resource, session, pdu, query, response); /* Check if lg_xmit generated and update PDU code if so */ - coap_check_code_lg_xmit(session, response, resource, query, pdu->code); + coap_check_code_lg_xmit(session, pdu, response, resource, query); /* Check to see if the server is doing a 4.01 + Echo response */ if (response->code == COAP_RESPONSE_CODE(401) && coap_check_option(response, COAP_OPTION_ECHO, &opt_iter)) { @@ -1682,8 +1737,7 @@ coap_handle_request_put_block(coap_context_t *context, (int)resource->uri_path->length, resource->uri_path->s); h(resource, session, pdu, query, response); /* Check if lg_xmit generated and update PDU code if so */ - coap_check_code_lg_xmit(session, response, resource, query, - pdu->code); + coap_check_code_lg_xmit(session, pdu, response, resource, query); if (COAP_RESPONSE_CLASS(response->code) == 2) { /* Just in case, as there are more to go */ response->code = COAP_RESPONSE_CODE(231); @@ -1971,8 +2025,7 @@ coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, */ coap_binary_t * coap_block_build_body(coap_binary_t *body_data, size_t length, - const uint8_t *data, size_t offset, size_t total) -{ + const uint8_t *data, size_t offset, size_t total) { if (data == NULL) return NULL; if (body_data == NULL && total) { @@ -2417,27 +2470,21 @@ coap_handle_response_get_block(coap_context_t *context, } #endif /* COAP_CLIENT_SUPPORT */ +#if COAP_SERVER_SUPPORT /* Check if lg_xmit generated and update PDU code if so */ void -coap_check_code_lg_xmit(coap_session_t *session, coap_pdu_t *response, - coap_resource_t *resource, coap_string_t *query, - coap_pdu_code_t request_method) { +coap_check_code_lg_xmit(const coap_session_t *session, + const coap_pdu_t *request, + coap_pdu_t *response, const coap_resource_t *resource, + const coap_string_t *query) { coap_lg_xmit_t *lg_xmit; - coap_string_t empty = { 0, NULL}; if (response->code == 0) return; - LL_FOREACH(session->lg_xmit, lg_xmit) { - if (!COAP_PDU_IS_REQUEST(&lg_xmit->pdu) && - lg_xmit->b.b2.resource == resource && - lg_xmit->b.b2.request_method == request_method && - coap_string_equal(query ? query : &empty, - lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) { - /* lg_xmit found */ - if (lg_xmit->pdu.code == 0) { - lg_xmit->pdu.code = response->code; - return; - } - } + lg_xmit = coap_find_lg_xmit_response(session, request, resource, query); + if (lg_xmit && lg_xmit->pdu.code == 0) { + lg_xmit->pdu.code = response->code; + return; } } +#endif /* COAP_SERVER_SUPPORT */ diff --git a/src/net.c b/src/net.c index 236f5c2579..a9631d100f 100644 --- a/src/net.c +++ b/src/net.c @@ -1105,6 +1105,8 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { } if (COAP_PDU_IS_REQUEST(pdu)) { + uint8_t buf[4]; + opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter); if (opt) { @@ -1126,6 +1128,14 @@ coap_send(coap_session_t *session, coap_pdu_t *pdu) { coap_delete_bin_const(session->last_token); session->last_token = coap_new_bin_const(token.s, token.length); } + if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter) && + (session->block_mode & COAP_BLOCK_NO_PREEMPTIVE_RTAG) == 0 && + pdu->code != COAP_REQUEST_CODE_DELETE) + coap_insert_option(pdu, + COAP_OPTION_RTAG, + coap_encode_var_safe(buf, sizeof(buf), + ++session->tx_rtag), + buf); } else { memset(&block, 0, sizeof(block)); } @@ -2907,7 +2917,7 @@ handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu h(resource, session, pdu, query, response); /* Check if lg_xmit generated and update PDU code if so */ - coap_check_code_lg_xmit(session, response, resource, query, pdu->code); + coap_check_code_lg_xmit(session, pdu, response, resource, query); skip_handler: if (send_early_empty_ack && diff --git a/src/resource.c b/src/resource.c index 487cc4337b..07c4eff18e 100644 --- a/src/resource.c +++ b/src/resource.c @@ -976,8 +976,7 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r, r->uri_path->s); h(r, obs->session, obs->pdu, query, response); /* Check if lg_xmit generated and update PDU code if so */ - coap_check_code_lg_xmit(obs->session, response, r, query, - obs->pdu->code); + coap_check_code_lg_xmit(obs->session, obs->pdu, response, r, query); coap_delete_string(query); if (COAP_RESPONSE_CLASS(response->code) != 2) { coap_remove_option(response, COAP_OPTION_OBSERVE);