1/* coap_block.c -- block transfer 2 * 3 * Copyright (C) 2010--2012,2015-2023 Olaf Bergmann <bergmann@tzi.org> and others 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 * 7 * This file is part of the CoAP library libcoap. Please see 8 * README for terms of use. 9 */ 10 11/** 12 * @file coap_block.c 13 * @brief CoAP Block handling 14 */ 15 16#include "coap3/coap_internal.h" 17 18#ifndef min 19#define min(a,b) ((a) < (b) ? (a) : (b)) 20#endif 21 22#define STATE_TOKEN_BASE(t) ((t) & 0xffffffffffffULL) 23#define STATE_TOKEN_RETRY(t) ((uint64_t)(t) >> 48) 24#define STATE_TOKEN_FULL(t,r) (STATE_TOKEN_BASE(t) + ((uint64_t)(r) << 48)) 25 26#if COAP_Q_BLOCK_SUPPORT 27int 28coap_q_block_is_supported(void) { 29 return 1; 30} 31#else /* ! COAP_Q_BLOCK_SUPPORT */ 32int 33coap_q_block_is_supported(void) { 34 return 0; 35} 36#endif /* ! COAP_Q_BLOCK_SUPPORT */ 37 38unsigned int 39coap_opt_block_num(const coap_opt_t *block_opt) { 40 unsigned int num = 0; 41 uint16_t len; 42 43 len = coap_opt_length(block_opt); 44 45 if (len == 0) { 46 return 0; 47 } 48 49 if (len > 1) { 50 num = coap_decode_var_bytes(coap_opt_value(block_opt), 51 coap_opt_length(block_opt) - 1); 52 } 53 54 return (num << 4) | ((COAP_OPT_BLOCK_END_BYTE(block_opt) & 0xF0) >> 4); 55} 56 57int 58coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu, 59 coap_option_num_t number, coap_block_b_t *block) { 60 coap_opt_iterator_t opt_iter; 61 coap_opt_t *option; 62 63 assert(block); 64 memset(block, 0, sizeof(coap_block_b_t)); 65 66 if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) { 67 unsigned int num; 68 69 if (COAP_OPT_BLOCK_MORE(option)) 70 block->m = 1; 71 block->aszx = block->szx = COAP_OPT_BLOCK_SZX(option); 72 if (block->szx == 7) { 73 size_t length; 74 const uint8_t *data; 75 76 if (session == NULL || COAP_PROTO_NOT_RELIABLE(session->proto) || 77 !(session->csm_bert_rem_support && session->csm_bert_loc_support)) 78 /* No BERT support */ 79 return 0; 80 81 block->szx = 6; /* BERT is 1024 block chunks */ 82 block->bert = 1; 83 if (coap_get_data(pdu, &length, &data)) { 84 if (block->m && (length % 1024) != 0) { 85 coap_log_debug("block: Oversized packet - reduced to %zu from %zu\n", 86 length - (length % 1024), length); 87 length -= length % 1024; 88 } 89 block->chunk_size = (uint32_t)length; 90 } else 91 block->chunk_size = 0; 92 } else { 93 block->chunk_size = (size_t)1 << (block->szx + 4); 94 } 95 block->defined = 1; 96 97 /* The block number is at most 20 bits, so values above 2^20 - 1 98 * are illegal. */ 99 num = coap_opt_block_num(option); 100 if (num > 0xFFFFF) { 101 return 0; 102 } 103 block->num = num; 104 return 1; 105 } 106 107 return 0; 108} 109 110int 111coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number, 112 coap_block_t *block) { 113 coap_block_b_t block_b; 114 115 assert(block); 116 memset(block, 0, sizeof(coap_block_t)); 117 118 if (coap_get_block_b(NULL, pdu, number, &block_b)) { 119 block->num = block_b.num; 120 block->m = block_b.m; 121 block->szx = block_b.szx; 122 return 1; 123 } 124 return 0; 125} 126 127static int 128setup_block_b(coap_session_t *session, coap_pdu_t *pdu, coap_block_b_t *block, 129 unsigned int num, 130 unsigned int blk_size, size_t total) { 131 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; 132 size_t avail = pdu->max_size - token_options; 133 unsigned int start = num << (blk_size + 4); 134 unsigned int can_use_bert = block->defined == 0 || block->bert; 135 136 assert(start <= total); 137 memset(block, 0, sizeof(*block)); 138 block->num = num; 139 block->szx = block->aszx = blk_size; 140 if (can_use_bert && blk_size == 6 && avail >= 1024 && session != NULL && 141 COAP_PROTO_RELIABLE(session->proto) && 142 session->csm_bert_rem_support && session->csm_bert_loc_support) { 143 block->bert = 1; 144 block->aszx = 7; 145 block->chunk_size = (uint32_t)((avail / 1024) * 1024); 146 } else { 147 block->chunk_size = (size_t)1 << (blk_size + 4); 148 if (avail < block->chunk_size && (total - start) >= avail) { 149 /* Need to reduce block size */ 150 unsigned int szx; 151 int new_blk_size; 152 153 if (avail < 16) { /* bad luck, this is the smallest block size */ 154 coap_log_debug("not enough space, even the smallest block does not fit (1)\n"); 155 return 0; 156 } 157 new_blk_size = coap_flsll((long long)avail) - 5; 158 coap_log_debug("decrease block size for %zu to %d\n", avail, new_blk_size); 159 szx = block->szx; 160 block->szx = new_blk_size; 161 block->num <<= szx - block->szx; 162 block->chunk_size = (size_t)1 << (new_blk_size + 4); 163 } 164 } 165 block->m = block->chunk_size < total - start; 166 return 1; 167} 168 169int 170coap_write_block_opt(coap_block_t *block, coap_option_num_t number, 171 coap_pdu_t *pdu, size_t data_length) { 172 size_t start; 173 unsigned char buf[4]; 174 coap_block_b_t block_b; 175 176 assert(pdu); 177 178 start = block->num << (block->szx + 4); 179 if (block->num != 0 && data_length <= start) { 180 coap_log_debug("illegal block requested\n"); 181 return -2; 182 } 183 184 assert(pdu->max_size > 0); 185 186 block_b.defined = 1; 187 block_b.bert = 0; 188 if (!setup_block_b(NULL, pdu, &block_b, block->num, 189 block->szx, data_length)) 190 return -3; 191 192 /* to re-encode the block option */ 193 coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf), 194 ((block_b.num << 4) | 195 (block_b.m << 3) | 196 block_b.szx)), 197 buf); 198 199 return 1; 200} 201 202int 203coap_write_block_b_opt(coap_session_t *session, coap_block_b_t *block, 204 coap_option_num_t number, 205 coap_pdu_t *pdu, size_t data_length) { 206 size_t start; 207 unsigned char buf[4]; 208 209 assert(pdu); 210 211 start = block->num << (block->szx + 4); 212 if (block->num != 0 && data_length <= start) { 213 coap_log_debug("illegal block requested\n"); 214 return -2; 215 } 216 217 assert(pdu->max_size > 0); 218 219 if (!setup_block_b(session, pdu, block, block->num, 220 block->szx, data_length)) 221 return -3; 222 223 /* to re-encode the block option */ 224 coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf), 225 ((block->num << 4) | 226 (block->m << 3) | 227 block->aszx)), 228 buf); 229 230 return 1; 231} 232 233int 234coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data, 235 unsigned int block_num, unsigned char block_szx) { 236 unsigned int start; 237 start = block_num << (block_szx + 4); 238 239 if (len <= start) 240 return 0; 241 242 return coap_add_data(pdu, 243 min(len - start, ((size_t)1 << (block_szx + 4))), 244 data + start); 245} 246 247int 248coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data, 249 coap_block_b_t *block) { 250 unsigned int start = block->num << (block->szx + 4); 251 size_t max_size; 252 253 if (len <= start) 254 return 0; 255 256 if (block->bert) { 257 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; 258 max_size = ((pdu->max_size - token_options) / 1024) * 1024; 259 } else { 260 max_size = (size_t)1 << (block->szx + 4); 261 } 262 block->chunk_size = (uint32_t)max_size; 263 264 return coap_add_data(pdu, 265 min(len - start, max_size), 266 data + start); 267} 268 269/* 270 * Note that the COAP_OPTION_ have to be added in the correct order 271 */ 272void 273coap_add_data_blocked_response(const coap_pdu_t *request, 274 coap_pdu_t *response, 275 uint16_t media_type, 276 int maxage, 277 size_t length, 278 const uint8_t *data 279 ) { 280 coap_key_t etag; 281 unsigned char buf[4]; 282 coap_block_t block2; 283 int block2_requested = 0; 284 285 memset(&block2, 0, sizeof(block2)); 286 /* 287 * Need to check that a valid block is getting asked for so that the 288 * correct options are put into the PDU. 289 */ 290 if (request) { 291 if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) { 292 block2_requested = 1; 293 if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) { 294 coap_log_debug("Illegal block requested (%d > last = %zu)\n", 295 block2.num, 296 length >> (block2.szx + 4)); 297 response->code = COAP_RESPONSE_CODE(400); 298 goto error; 299 } 300 } 301 } 302 response->code = COAP_RESPONSE_CODE(205); 303 304 /* add etag for the resource */ 305 memset(etag, 0, sizeof(etag)); 306 coap_hash(data, length, etag); 307 coap_insert_option(response, COAP_OPTION_ETAG, sizeof(etag), etag); 308 309 coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT, 310 coap_encode_var_safe(buf, sizeof(buf), 311 media_type), 312 buf); 313 314 if (maxage >= 0) { 315 coap_insert_option(response, 316 COAP_OPTION_MAXAGE, 317 coap_encode_var_safe(buf, sizeof(buf), maxage), buf); 318 } 319 320 if (block2_requested) { 321 int res; 322 323 res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length); 324 325 switch (res) { 326 case -2: /* illegal block (caught above) */ 327 response->code = COAP_RESPONSE_CODE(400); 328 goto error; 329 case -1: /* should really not happen */ 330 assert(0); 331 /* fall through if assert is a no-op */ 332 case -3: /* cannot handle request */ 333 response->code = COAP_RESPONSE_CODE(500); 334 goto error; 335 default: /* everything is good */ 336 ; 337 } 338 339 coap_add_option_internal(response, 340 COAP_OPTION_SIZE2, 341 coap_encode_var_safe8(buf, sizeof(buf), length), 342 buf); 343 344 coap_add_block(response, length, data, 345 block2.num, block2.szx); 346 return; 347 } 348 349 /* 350 * Block2 not requested 351 */ 352 if (!coap_add_data(response, length, data)) { 353 /* 354 * Insufficient space to add in data - use block mode 355 * set initial block size, will be lowered by 356 * coap_write_block_opt() automatically 357 */ 358 block2.num = 0; 359 block2.szx = 6; 360 coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length); 361 362 coap_add_option_internal(response, 363 COAP_OPTION_SIZE2, 364 coap_encode_var_safe8(buf, sizeof(buf), length), 365 buf); 366 367 coap_add_block(response, length, data, 368 block2.num, block2.szx); 369 } 370 return; 371 372error: 373 coap_add_data(response, 374 strlen(coap_response_phrase(response->code)), 375 (const unsigned char *)coap_response_phrase(response->code)); 376} 377 378void 379coap_context_set_block_mode(coap_context_t *context, 380 uint8_t block_mode) { 381 context->block_mode = (block_mode & (COAP_BLOCK_USE_LIBCOAP | 382 COAP_BLOCK_SINGLE_BODY | 383#if COAP_Q_BLOCK_SUPPORT 384 COAP_BLOCK_TRY_Q_BLOCK | 385 COAP_BLOCK_USE_M_Q_BLOCK | 386#endif /* COAP_Q_BLOCK_SUPPORT */ 387 COAP_BLOCK_NO_PREEMPTIVE_RTAG)); 388 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) 389 context->block_mode = 0; 390#if ! COAP_Q_BLOCK_SUPPORT 391 if (block_mode & (COAP_BLOCK_TRY_Q_BLOCK|COAP_BLOCK_USE_M_Q_BLOCK)) 392 coap_log_debug("Q-Block support not compiled in - ignored\n"); 393#endif /* ! COAP_Q_BLOCK_SUPPORT */ 394} 395 396COAP_STATIC_INLINE int 397full_match(const uint8_t *a, size_t alen, 398 const uint8_t *b, size_t blen) { 399 return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0); 400} 401 402#if COAP_CLIENT_SUPPORT 403 404int 405coap_cancel_observe(coap_session_t *session, coap_binary_t *token, 406 coap_pdu_type_t type) { 407 coap_lg_crcv_t *lg_crcv, *q; 408 409 assert(session); 410 if (!session) 411 return 0; 412 413 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { 414 coap_log_debug("** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n", 415 coap_session_str(session)); 416 return 0; 417 } 418 419 LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) { 420 if (lg_crcv->observe_set) { 421 if ((!token && !lg_crcv->app_token->length) || (token && 422 coap_binary_equal(token, lg_crcv->app_token))) { 423 uint8_t buf[8]; 424 coap_mid_t mid; 425 size_t size; 426 const uint8_t *data; 427#if COAP_Q_BLOCK_SUPPORT 428 coap_block_b_t block; 429 int using_q_block1 = coap_get_block_b(session, &lg_crcv->pdu, 430 COAP_OPTION_Q_BLOCK1, &block); 431#endif /* COAP_Q_BLOCK_SUPPORT */ 432 coap_bin_const_t *otoken = lg_crcv->obs_token ? 433 lg_crcv->obs_token[0] ? 434 lg_crcv->obs_token[0] : 435 (coap_bin_const_t *)lg_crcv->app_token : 436 (coap_bin_const_t *)lg_crcv->app_token; 437 coap_pdu_t *pdu = coap_pdu_duplicate(&lg_crcv->pdu, 438 session, 439 otoken->length, 440 otoken->s, 441 NULL); 442 443 lg_crcv->observe_set = 0; 444 if (pdu == NULL) 445 return 0; 446#if COAP_Q_BLOCK_SUPPORT 447 if (pdu->code == COAP_REQUEST_CODE_FETCH && using_q_block1) { 448 /* Have to make sure all gets through in case of packet loss */ 449 pdu->type = COAP_MESSAGE_CON; 450 } else { 451 /* Need to make sure that this is the correct requested type */ 452 pdu->type = type; 453 } 454#else /* ! COAP_Q_BLOCK_SUPPORT */ 455 /* Need to make sure that this is the correct requested type */ 456 pdu->type = type; 457#endif /* ! COAP_Q_BLOCK_SUPPORT */ 458 459 coap_update_option(pdu, COAP_OPTION_OBSERVE, 460 coap_encode_var_safe(buf, sizeof(buf), 461 COAP_OBSERVE_CANCEL), 462 buf); 463 if (coap_get_data(&lg_crcv->pdu, &size, &data)) 464 coap_add_data_large_request(session, pdu, size, data, NULL, NULL); 465 466 /* 467 * Need to fix lg_xmit stateless token as using tokens from 468 * observe setup 469 */ 470 if (pdu->lg_xmit) 471 pdu->lg_xmit->b.b1.state_token = lg_crcv->state_token; 472 473#if COAP_Q_BLOCK_SUPPORT 474 /* See if large xmit using Q-Block1 (but not testing Q-Block1) */ 475 if (using_q_block1) { 476 mid = coap_send_q_block1(session, block, pdu, COAP_SEND_INC_PDU); 477 } else { 478 mid = coap_send_internal(session, pdu); 479 } 480#else /* ! COAP_Q_BLOCK_SUPPORT */ 481 mid = coap_send_internal(session, pdu); 482#endif /* ! COAP_Q_BLOCK_SUPPORT */ 483 if (mid != COAP_INVALID_MID) 484 return 1; 485 break; 486 } 487 } 488 } 489 return 0; 490} 491 492#if COAP_OSCORE_SUPPORT 493coap_mid_t 494coap_retransmit_oscore_pdu(coap_session_t *session, 495 coap_pdu_t *pdu, 496 coap_opt_t *echo) { 497 coap_lg_crcv_t *lg_crcv; 498 uint64_t token_match = 499 STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s, 500 pdu->actual_token.length)); 501 uint8_t ltoken[8]; 502 size_t ltoken_len; 503 uint64_t token; 504 const uint8_t *data; 505 size_t data_len; 506 coap_pdu_t *resend_pdu; 507 coap_block_b_t block; 508 509 LL_FOREACH(session->lg_crcv, lg_crcv) { 510 if (token_match != STATE_TOKEN_BASE(lg_crcv->state_token) && 511 !coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) { 512 /* try out the next one */ 513 continue; 514 } 515 516 /* lg_crcv found */ 517 518 /* Re-send request with new token */ 519 token = STATE_TOKEN_FULL(lg_crcv->state_token, 520 ++lg_crcv->retry_counter); 521 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); 522 /* There could be a Block option in pdu */ 523 resend_pdu = coap_pdu_duplicate(pdu, session, ltoken_len, 524 ltoken, NULL); 525 if (!resend_pdu) 526 goto error; 527 if (echo) { 528 coap_insert_option(resend_pdu, COAP_OPTION_ECHO, coap_opt_length(echo), 529 coap_opt_value(echo)); 530 } 531 if (coap_get_data(&lg_crcv->pdu, &data_len, &data)) { 532 if (coap_get_block_b(session, resend_pdu, COAP_OPTION_BLOCK1, &block)) { 533 if (data_len > block.chunk_size && block.chunk_size != 0) { 534 data_len = block.chunk_size; 535 } 536 } 537 coap_add_data(resend_pdu, data_len, data); 538 } 539 540 return coap_send_internal(session, resend_pdu); 541 } 542error: 543 return COAP_INVALID_MID; 544} 545#endif /* COAP_OSCORE_SUPPORT */ 546#endif /* COAP_CLIENT_SUPPORT */ 547 548#if COAP_SERVER_SUPPORT 549/* 550 * Find the response lg_xmit 551 */ 552coap_lg_xmit_t * 553coap_find_lg_xmit_response(const coap_session_t *session, 554 const coap_pdu_t *request, 555 const coap_resource_t *resource, 556 const coap_string_t *query) { 557 coap_lg_xmit_t *lg_xmit; 558 coap_opt_iterator_t opt_iter; 559 coap_opt_t *rtag_opt = coap_check_option(request, 560 COAP_OPTION_RTAG, 561 &opt_iter); 562 size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0; 563 const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL; 564 565 LL_FOREACH(session->lg_xmit, lg_xmit) { 566 static coap_string_t empty = { 0, NULL}; 567 568 if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu) || 569 resource != lg_xmit->b.b2.resource || 570 request->code != lg_xmit->b.b2.request_method || 571 !coap_string_equal(query ? query : &empty, 572 lg_xmit->b.b2.query ? 573 lg_xmit->b.b2.query : &empty)) { 574 /* try out the next one */ 575 continue; 576 } 577 /* lg_xmit is a response */ 578 if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) { 579 if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1)) 580 continue; 581 if (lg_xmit->b.b2.rtag_length != rtag_length || 582 memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0) 583 continue; 584 } 585 return lg_xmit; 586 } 587 return NULL; 588} 589#endif /* COAP_SERVER_SUPPORT */ 590 591static int 592coap_add_data_large_internal(coap_session_t *session, 593 const coap_pdu_t *request, 594 coap_pdu_t *pdu, 595 coap_resource_t *resource, 596 const coap_string_t *query, 597 int maxage, 598 uint64_t etag, 599 size_t length, 600 const uint8_t *data, 601 coap_release_large_data_t release_func, 602 void *app_ptr, 603 int single_request, coap_pdu_code_t request_method) { 604 605 ssize_t avail; 606 coap_block_b_t block; 607#if COAP_Q_BLOCK_SUPPORT 608 coap_block_b_t alt_block; 609#endif /* COAP_Q_BLOCK_SUPPORT */ 610 size_t chunk; 611 coap_lg_xmit_t *lg_xmit = NULL; 612 uint8_t buf[8]; 613 int have_block_defined = 0; 614 uint8_t blk_size; 615 uint16_t option; 616 size_t token_options; 617 coap_opt_t *opt; 618 coap_opt_iterator_t opt_iter; 619#if COAP_Q_BLOCK_SUPPORT 620 uint16_t alt_option; 621#endif /* COAP_Q_BLOCK_SUPPORT */ 622 623 assert(pdu); 624 if (pdu->data) { 625 coap_log_warn("coap_add_data_large: PDU already contains data\n"); 626 if (release_func) 627 release_func(session, app_ptr); 628 return 0; 629 } 630 631 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { 632 coap_log_debug("** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n", 633 coap_session_str(session)); 634 goto add_data; 635 } 636 637 /* A lot of the reliable code assumes type is CON */ 638 if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON) 639 pdu->type = COAP_MESSAGE_CON; 640 641 /* Block NUM max 20 bits and block size is "2**(SZX + 4)" 642 and using SZX max of 6 gives maximum size = 1,073,740,800 643 CSM Max-Message-Size theoretical maximum = 4,294,967,295 644 So, if using blocks, we are limited to 1,073,740,800. 645 */ 646#define MAX_BLK_LEN (((1 << 20) - 1) * (1 << (6 + 4))) 647 648 if (length > MAX_BLK_LEN) { 649 coap_log_warn("Size of large buffer restricted to 0x%x bytes\n", MAX_BLK_LEN); 650 length = MAX_BLK_LEN; 651 } 652 653 /* Determine the block size to use, adding in sensible options if needed */ 654 if (COAP_PDU_IS_REQUEST(pdu)) { 655 coap_lg_xmit_t *q; 656 657#if COAP_Q_BLOCK_SUPPORT 658 if (session->block_mode & (COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_TRY_Q_BLOCK)) { 659 option = COAP_OPTION_Q_BLOCK1; 660 alt_option = COAP_OPTION_BLOCK1; 661 } else { 662 option = COAP_OPTION_BLOCK1; 663 alt_option = COAP_OPTION_Q_BLOCK1; 664 } 665#else /* ! COAP_Q_BLOCK_SUPPORT */ 666 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { 667 coap_remove_option(pdu, COAP_OPTION_Q_BLOCK1); 668 } 669 option = COAP_OPTION_BLOCK1; 670#endif /* ! COAP_Q_BLOCK_SUPPORT */ 671 672 /* See if this token is already in use for large bodies (unlikely) */ 673 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { 674 if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { 675 /* Unfortunately need to free this off as potential size change */ 676 LL_DELETE(session->lg_xmit, lg_xmit); 677 coap_block_delete_lg_xmit(session, lg_xmit); 678 lg_xmit = NULL; 679 coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); 680 break; 681 } 682 } 683 } else { 684 /* Have to assume that it is a response even if code is 0.00 */ 685 assert(resource); 686#if COAP_Q_BLOCK_SUPPORT 687 if (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) { 688 option = COAP_OPTION_Q_BLOCK2; 689 alt_option = COAP_OPTION_BLOCK2; 690 } else { 691 option = COAP_OPTION_BLOCK2; 692 alt_option = COAP_OPTION_Q_BLOCK2; 693 } 694#else /* ! COAP_Q_BLOCK_SUPPORT */ 695 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) { 696 coap_remove_option(pdu, COAP_OPTION_Q_BLOCK2); 697 } 698 option = COAP_OPTION_BLOCK2; 699#endif /* ! COAP_Q_BLOCK_SUPPORT */ 700#if COAP_SERVER_SUPPORT 701 /* 702 * Check if resource+query+rtag is already in use for large bodies 703 * (unlikely) 704 */ 705 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query); 706 if (lg_xmit) { 707 /* Unfortunately need to free this off as potential size change */ 708 LL_DELETE(session->lg_xmit, lg_xmit); 709 coap_block_delete_lg_xmit(session, lg_xmit); 710 lg_xmit = NULL; 711 coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); 712 } 713#endif /* COAP_SERVER_SUPPORT */ 714 } 715#if COAP_OSCORE_SUPPORT 716 if (session->oscore_encryption) { 717 /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */ 718 if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu)) 719 goto fail; 720 } 721#endif /* COAP_OSCORE_SUPPORT */ 722 723 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; 724 avail = pdu->max_size - token_options; 725 /* There may be a response with Echo option */ 726 avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); 727#if COAP_OSCORE_SUPPORT 728 avail -= coap_oscore_overhead(session, pdu); 729#endif /* COAP_OSCORE_SUPPORT */ 730 /* May need token of length 8, so account for this */ 731 avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0; 732 blk_size = coap_flsll((long long)avail) - 4 - 1; 733 if (blk_size > 6) 734 blk_size = 6; 735 736 /* see if BlockX defined - if so update blk_size as given by app */ 737 if (coap_get_block_b(session, pdu, option, &block)) { 738 if (block.szx < blk_size) 739 blk_size = block.szx; 740 have_block_defined = 1; 741 } 742#if COAP_Q_BLOCK_SUPPORT 743 /* see if alternate BlockX defined */ 744 if (coap_get_block_b(session, pdu, alt_option, &alt_block)) { 745 if (have_block_defined) { 746 /* Cannot have both options set */ 747 coap_log_warn("Both BlockX and Q-BlockX cannot be set at the same time\n"); 748 coap_remove_option(pdu, alt_option); 749 } else { 750 block = alt_block; 751 if (block.szx < blk_size) 752 blk_size = block.szx; 753 have_block_defined = 1; 754 option = alt_option; 755 } 756 } 757#endif /* COAP_Q_BLOCK_SUPPORT */ 758 759 if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) { 760 /* bad luck, this is the smallest block size */ 761 coap_log_debug("not enough space, even the smallest block does not fit (2)\n"); 762 goto fail; 763 } 764 765 chunk = (size_t)1 << (blk_size + 4); 766 if (have_block_defined && 767 (block.num != 0 || single_request)) { 768 /* App is defining a single block to send */ 769 size_t rem; 770 771 if (length >= block.num * chunk) { 772 rem = chunk; 773 if (chunk > length - block.num * chunk) 774 rem = length - block.num * chunk; 775 if (!coap_add_data(pdu, rem, &data[block.num * chunk])) 776 goto fail; 777 } 778 if (release_func) 779 release_func(session, app_ptr); 780 } else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) { 781 /* Only add in lg_xmit if more than one block needs to be handled */ 782 size_t rem; 783 784 lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t)); 785 if (!lg_xmit) 786 goto fail; 787 788 /* Set up for displaying all the data in the pdu */ 789 pdu->body_data = data; 790 pdu->body_length = length; 791 coap_log_debug("PDU presented by app.\n"); 792 coap_show_pdu(COAP_LOG_DEBUG, pdu); 793 pdu->body_data = NULL; 794 pdu->body_length = 0; 795 796 coap_log_debug("** %s: lg_xmit %p initialized\n", 797 coap_session_str(session), (void *)lg_xmit); 798 /* Update lg_xmit with large data information */ 799 memset(lg_xmit, 0, sizeof(coap_lg_xmit_t)); 800 lg_xmit->blk_size = blk_size; 801 lg_xmit->option = option; 802 lg_xmit->data = data; 803 lg_xmit->length = length; 804#if COAP_Q_BLOCK_SUPPORT 805 lg_xmit->non_timeout_random_ticks = 806 coap_get_non_timeout_random_ticks(session); 807#endif /* COAP_Q_BLOCK_SUPPORT */ 808 lg_xmit->release_func = release_func; 809 lg_xmit->app_ptr = app_ptr; 810 pdu->lg_xmit = lg_xmit; 811 coap_ticks(&lg_xmit->last_obs); 812 coap_ticks(&lg_xmit->last_sent); 813 if (COAP_PDU_IS_REQUEST(pdu)) { 814 /* Need to keep original token for updating response PDUs */ 815 lg_xmit->b.b1.app_token = coap_new_binary(pdu->actual_token.length); 816 if (!lg_xmit->b.b1.app_token) 817 goto fail; 818 memcpy(lg_xmit->b.b1.app_token->s, pdu->actual_token.s, 819 pdu->actual_token.length); 820 /* 821 * Need to set up new token for use during transmits 822 * RFC9177#section-5 823 */ 824 lg_xmit->b.b1.count = 1; 825 lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token, 826 lg_xmit->b.b1.count); 827 /* 828 * Token will be updated in pdu later as original pdu may be needed in 829 * coap_send() 830 */ 831 coap_update_option(pdu, 832 COAP_OPTION_SIZE1, 833 coap_encode_var_safe(buf, sizeof(buf), 834 (unsigned int)length), 835 buf); 836 if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter)) 837 coap_insert_option(pdu, 838 COAP_OPTION_RTAG, 839 coap_encode_var_safe(buf, sizeof(buf), 840 ++session->tx_rtag), 841 buf); 842 } else { 843 /* 844 * resource+query+rtag match is used for Block2 large body transmissions 845 * token match is used for Block1 large body transmissions 846 */ 847 lg_xmit->b.b2.resource = resource; 848 if (query) { 849 lg_xmit->b.b2.query = coap_new_string(query->length); 850 if (lg_xmit->b.b2.query) { 851 memcpy(lg_xmit->b.b2.query->s, query->s, query->length); 852 } 853 } else { 854 lg_xmit->b.b2.query = NULL; 855 } 856 opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter); 857 if (opt) { 858 lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt), 859 sizeof(lg_xmit->b.b2.rtag)); 860 memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), coap_opt_length(opt)); 861 lg_xmit->b.b2.rtag_set = 1; 862 } else { 863 lg_xmit->b.b2.rtag_set = 0; 864 } 865 lg_xmit->b.b2.etag = etag; 866 lg_xmit->b.b2.request_method = request_method; 867 if (maxage >= 0) { 868 coap_tick_t now; 869 870 coap_ticks(&now); 871 lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage; 872 } else { 873 lg_xmit->b.b2.maxage_expire = 0; 874 } 875 coap_update_option(pdu, 876 COAP_OPTION_SIZE2, 877 coap_encode_var_safe(buf, sizeof(buf), 878 (unsigned int)length), 879 buf); 880 if (etag == 0) { 881 if (++session->context->etag == 0) 882 ++session->context->etag; 883 etag = session->context->etag; 884 } 885 coap_update_option(pdu, 886 COAP_OPTION_ETAG, 887 coap_encode_var_safe8(buf, sizeof(buf), etag), 888 buf); 889 } 890 891 if (!setup_block_b(session, pdu, &block, block.num, 892 blk_size, lg_xmit->length)) 893 goto fail; 894 895 /* Add in with requested block num, more bit and block size */ 896 coap_update_option(pdu, 897 lg_xmit->option, 898 coap_encode_var_safe(buf, sizeof(buf), 899 (block.num << 4) | (block.m << 3) | block.aszx), 900 buf); 901 902 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */ 903 memcpy(&lg_xmit->pdu, pdu, sizeof(lg_xmit->pdu)); 904 lg_xmit->pdu.token = coap_malloc_type(COAP_PDU_BUF, 905 lg_xmit->pdu.used_size + lg_xmit->pdu.max_hdr_size); 906 if (!lg_xmit->pdu.token) 907 goto fail; 908 909 lg_xmit->pdu.alloc_size = lg_xmit->pdu.used_size; 910 lg_xmit->pdu.token += lg_xmit->pdu.max_hdr_size; 911 memcpy(lg_xmit->pdu.token, pdu->token, lg_xmit->pdu.used_size); 912 if (pdu->data) 913 lg_xmit->pdu.data = lg_xmit->pdu.token + (pdu->data - pdu->token); 914 lg_xmit->pdu.actual_token.s = lg_xmit->pdu.token + pdu->e_token_length - 915 pdu->actual_token.length; 916 lg_xmit->pdu.actual_token.length = pdu->actual_token.length; 917 918 /* Check we still have space after adding in some options */ 919 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; 920 avail = pdu->max_size - token_options; 921 /* There may be a response with Echo option */ 922 avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); 923 /* May need token of length 8, so account for this */ 924 avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0; 925#if COAP_OSCORE_SUPPORT 926 avail -= coap_oscore_overhead(session, pdu); 927#endif /* COAP_OSCORE_SUPPORT */ 928 if (avail < (ssize_t)chunk) { 929 /* chunk size change down */ 930 if (avail < 16) { 931 coap_log_warn("not enough space, even the smallest block does not fit (3)\n"); 932 goto fail; 933 } 934 blk_size = coap_flsll((long long)avail) - 4 - 1; 935 block.num = block.num << (lg_xmit->blk_size - blk_size); 936 lg_xmit->blk_size = blk_size; 937 chunk = (size_t)1 << (lg_xmit->blk_size + 4); 938 block.chunk_size = (uint32_t)chunk; 939 block.bert = 0; 940 coap_update_option(pdu, 941 lg_xmit->option, 942 coap_encode_var_safe(buf, sizeof(buf), 943 (block.num << 4) | (block.m << 3) | lg_xmit->blk_size), 944 buf); 945 } 946 947 rem = block.chunk_size; 948 if (rem > lg_xmit->length - block.num * chunk) 949 rem = lg_xmit->length - block.num * chunk; 950 if (!coap_add_data(pdu, rem, &data[block.num * chunk])) 951 goto fail; 952 953 if (COAP_PDU_IS_REQUEST(pdu)) 954 lg_xmit->b.b1.bert_size = rem; 955 956 lg_xmit->last_block = -1; 957 958 /* Link the new lg_xmit in */ 959 LL_PREPEND(session->lg_xmit,lg_xmit); 960 } else { 961 /* No need to use blocks */ 962 if (etag) { 963 coap_update_option(pdu, 964 COAP_OPTION_ETAG, 965 coap_encode_var_safe8(buf, sizeof(buf), etag), 966 buf); 967 } 968 if (have_block_defined) { 969 coap_update_option(pdu, 970 option, 971 coap_encode_var_safe(buf, sizeof(buf), 972 (0 << 4) | (0 << 3) | blk_size), buf); 973 } 974add_data: 975 if (!coap_add_data(pdu, length, data)) 976 goto fail; 977 978 if (release_func) 979 release_func(session, app_ptr); 980 } 981 return 1; 982 983fail: 984 if (lg_xmit) { 985 coap_block_delete_lg_xmit(session, lg_xmit); 986 } else if (release_func) { 987 release_func(session, app_ptr); 988 } 989 return 0; 990} 991 992#if COAP_CLIENT_SUPPORT 993int 994coap_add_data_large_request(coap_session_t *session, 995 coap_pdu_t *pdu, 996 size_t length, 997 const uint8_t *data, 998 coap_release_large_data_t release_func, 999 void *app_ptr) { 1000 /* 1001 * Delay if session->doing_first is set. 1002 * E.g. Reliable and CSM not in yet for checking block support 1003 */ 1004 if (coap_client_delay_first(session) == 0) { 1005 if (release_func) 1006 release_func(session, app_ptr); 1007 return 0; 1008 } 1009 return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, 1010 length, data, release_func, app_ptr, 0, 0); 1011} 1012#endif /* ! COAP_CLIENT_SUPPORT */ 1013 1014#if COAP_SERVER_SUPPORT 1015int 1016coap_add_data_large_response(coap_resource_t *resource, 1017 coap_session_t *session, 1018 const coap_pdu_t *request, 1019 coap_pdu_t *response, 1020 const coap_string_t *query, 1021 uint16_t media_type, 1022 int maxage, 1023 uint64_t etag, 1024 size_t length, 1025 const uint8_t *data, 1026 coap_release_large_data_t release_func, 1027 void *app_ptr 1028 ) { 1029 unsigned char buf[4]; 1030 coap_block_b_t block; 1031 int block_requested = 0; 1032 int single_request = 0; 1033#if COAP_Q_BLOCK_SUPPORT 1034 uint16_t block_opt = (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) ? 1035 COAP_OPTION_Q_BLOCK2 : COAP_OPTION_BLOCK2; 1036#else /* ! COAP_Q_BLOCK_SUPPORT */ 1037 uint16_t block_opt = COAP_OPTION_BLOCK2; 1038#endif /* ! COAP_Q_BLOCK_SUPPORT */ 1039 1040 memset(&block, 0, sizeof(block)); 1041 /* 1042 * Need to check that a valid block is getting asked for so that the 1043 * correct options are put into the PDU. 1044 */ 1045 if (request) { 1046 if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) { 1047 block_requested = 1; 1048 if (block.num != 0 && length <= (block.num << (block.szx + 4))) { 1049 coap_log_debug("Illegal block requested (%d > last = %zu)\n", 1050 block.num, 1051 length >> (block.szx + 4)); 1052 response->code = COAP_RESPONSE_CODE(400); 1053 goto error; 1054 } 1055 } 1056#if COAP_Q_BLOCK_SUPPORT 1057 else if (coap_get_block_b(session, request, COAP_OPTION_Q_BLOCK2, &block)) { 1058 block_requested = 1; 1059 if (block.num != 0 && length <= (block.num << (block.szx + 4))) { 1060 coap_log_debug("Illegal block requested (%d > last = %zu)\n", 1061 block.num, 1062 length >> (block.szx + 4)); 1063 response->code = COAP_RESPONSE_CODE(400); 1064 goto error; 1065 } 1066 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) { 1067 set_block_mode_has_q(session->block_mode); 1068 block_opt = COAP_OPTION_Q_BLOCK2; 1069 } 1070 if (block.m == 0) 1071 single_request = 1; 1072 } 1073#endif /* COAP_Q_BLOCK_SUPPORT */ 1074 } 1075 1076 coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT, 1077 coap_encode_var_safe(buf, sizeof(buf), 1078 media_type), 1079 buf); 1080 1081 if (maxage >= 0) { 1082 coap_insert_option(response, 1083 COAP_OPTION_MAXAGE, 1084 coap_encode_var_safe(buf, sizeof(buf), maxage), buf); 1085 } 1086 1087 if (block_requested) { 1088 int res; 1089 1090 res = coap_write_block_b_opt(session, &block, block_opt, response, 1091 length); 1092 1093 switch (res) { 1094 case -2: /* illegal block (caught above) */ 1095 response->code = COAP_RESPONSE_CODE(400); 1096 goto error; 1097 case -1: /* should really not happen */ 1098 assert(0); 1099 /* fall through if assert is a no-op */ 1100 case -3: /* cannot handle request */ 1101 response->code = COAP_RESPONSE_CODE(500); 1102 goto error; 1103 default: /* everything is good */ 1104 ; 1105 } 1106 } 1107 1108 /* add data body */ 1109 if (request && 1110 !coap_add_data_large_internal(session, request, response, resource, 1111 query, maxage, etag, length, data, 1112 release_func, app_ptr, single_request, 1113 request->code)) { 1114 response->code = COAP_RESPONSE_CODE(500); 1115 goto error_released; 1116 } 1117 1118 return 1; 1119 1120error: 1121 if (release_func) 1122 release_func(session, app_ptr); 1123error_released: 1124#if COAP_ERROR_PHRASE_LENGTH > 0 1125 coap_add_data(response, 1126 strlen(coap_response_phrase(response->code)), 1127 (const unsigned char *)coap_response_phrase(response->code)); 1128#endif /* COAP_ERROR_PHRASE_LENGTH > 0 */ 1129 return 0; 1130} 1131#endif /* ! COAP_SERVER_SUPPORT */ 1132 1133/* 1134 * return 1 if there is a future expire time, else 0. 1135 * update tim_rem with remaining value if return is 1. 1136 */ 1137int 1138coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now, 1139 coap_tick_t *tim_rem) { 1140 coap_lg_xmit_t *p; 1141 coap_lg_xmit_t *q; 1142#if COAP_Q_BLOCK_SUPPORT 1143 coap_tick_t idle_timeout = 4 * COAP_NON_TIMEOUT_TICKS(session); 1144#else /* ! COAP_Q_BLOCK_SUPPORT */ 1145 coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND; 1146#endif /* ! COAP_Q_BLOCK_SUPPORT */ 1147 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session); 1148 int ret = 0; 1149 1150 *tim_rem = -1; 1151 1152 LL_FOREACH_SAFE(session->lg_xmit, p, q) { 1153 if (p->last_all_sent) { 1154 if (p->last_all_sent + idle_timeout <= now) { 1155 /* Expire this entry */ 1156 LL_DELETE(session->lg_xmit, p); 1157 coap_block_delete_lg_xmit(session, p); 1158 } else { 1159 /* Delay until the lg_xmit needs to expire */ 1160 if (*tim_rem > p->last_all_sent + idle_timeout - now) { 1161 *tim_rem = p->last_all_sent + idle_timeout - now; 1162 ret = 1; 1163 } 1164 } 1165 } else if (p->last_sent) { 1166 if (p->last_sent + partial_timeout <= now) { 1167 /* Expire this entry */ 1168 LL_DELETE(session->lg_xmit, p); 1169 coap_block_delete_lg_xmit(session, p); 1170 coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); 1171 } else { 1172 /* Delay until the lg_xmit needs to expire */ 1173 if (*tim_rem > p->last_sent + partial_timeout - now) { 1174 *tim_rem = p->last_sent + partial_timeout - now; 1175 ret = 1; 1176 } 1177 } 1178 } 1179 } 1180 return ret; 1181} 1182 1183#if COAP_CLIENT_SUPPORT 1184#if COAP_Q_BLOCK_SUPPORT 1185static coap_pdu_t * 1186coap_build_missing_pdu(coap_session_t *session, coap_lg_crcv_t *p) { 1187 coap_pdu_t *pdu; 1188 coap_opt_filter_t drop_options; 1189 uint64_t token = STATE_TOKEN_FULL(p->state_token, ++p->retry_counter); 1190 uint8_t buf[8]; 1191 size_t len = coap_encode_var_safe8(buf, sizeof(token), token); 1192 1193 memset(&drop_options, 0, sizeof(coap_opt_filter_t)); 1194 coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK2); 1195 coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); 1196 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, 1197 &drop_options); 1198 if (!pdu) 1199 return NULL; 1200 pdu->type = p->last_type; 1201 return pdu; 1202} 1203 1204static void 1205coap_request_missing_q_block2(coap_session_t *session, coap_lg_crcv_t *lg_crcv) { 1206 uint8_t buf[8]; 1207 uint32_t i; 1208 int block = -1; /* Last one seen */ 1209 size_t sofar; 1210 size_t block_size; 1211 coap_pdu_t *pdu = NULL; 1212 int block_payload_set = -1; 1213 1214 if (session->block_mode & COAP_BLOCK_USE_M_Q_BLOCK) { 1215 /* 1216 * See if it is safe to use the single 'M' block variant of request 1217 * 1218 * If any blocks seen, then missing blocks are after range[0].end and 1219 * terminate on the last block or before range[1].begin if set. 1220 * If not defined or range[1].begin is in a different payload set then 1221 * safe to use M bit. 1222 */ 1223 if (lg_crcv->rec_blocks.used && 1224 (lg_crcv->rec_blocks.used < 2 || 1225 ((lg_crcv->rec_blocks.range[0].end + 1) / COAP_MAX_PAYLOADS(session) != 1226 (lg_crcv->rec_blocks.range[1].begin -1) / COAP_MAX_PAYLOADS(session)))) { 1227 block = lg_crcv->rec_blocks.range[0].end + 1; 1228 block_size = (size_t)1 << (lg_crcv->szx + 4); 1229 sofar = block * block_size; 1230 if (sofar < lg_crcv->total_len) { 1231 /* Ask for missing blocks */ 1232 if (pdu == NULL) { 1233 pdu = coap_build_missing_pdu(session, lg_crcv); 1234 if (!pdu) 1235 return; 1236 } 1237 coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, 1238 coap_encode_var_safe(buf, sizeof(buf), 1239 (block << 4) | (1 << 3) | lg_crcv->szx), 1240 buf); 1241 block_payload_set = block / COAP_MAX_PAYLOADS(session); 1242 goto send_it; 1243 } 1244 } 1245 } 1246 block = -1; 1247 for (i = 0; i < lg_crcv->rec_blocks.used; i++) { 1248 if (block < (int)lg_crcv->rec_blocks.range[i].begin && 1249 lg_crcv->rec_blocks.range[i].begin != 0) { 1250 /* Ask for missing blocks */ 1251 if (pdu == NULL) { 1252 pdu = coap_build_missing_pdu(session, lg_crcv); 1253 if (!pdu) 1254 continue; 1255 } 1256 block++; 1257 if (block_payload_set == -1) 1258 block_payload_set = block / COAP_MAX_PAYLOADS(session); 1259 for (; block < (int)lg_crcv->rec_blocks.range[i].begin && 1260 block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) { 1261 coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, 1262 coap_encode_var_safe(buf, sizeof(buf), 1263 (block << 4) | (0 << 3) | lg_crcv->szx), 1264 buf); 1265 } 1266 } 1267 if (block < (int)lg_crcv->rec_blocks.range[i].end) { 1268 block = lg_crcv->rec_blocks.range[i].end; 1269 } 1270 } 1271 block_size = (size_t)1 << (lg_crcv->szx + 4); 1272 sofar = (block + 1) * block_size; 1273 if (sofar < lg_crcv->total_len) { 1274 /* Ask for trailing missing blocks */ 1275 if (pdu == NULL) { 1276 pdu = coap_build_missing_pdu(session, lg_crcv); 1277 if (!pdu) 1278 return; 1279 } 1280 sofar = (lg_crcv->total_len + block_size - 1)/block_size; 1281 block++; 1282 if (block_payload_set == -1) 1283 block_payload_set = block / COAP_MAX_PAYLOADS(session); 1284 for (; block < (ssize_t)sofar && 1285 block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) { 1286 coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, 1287 coap_encode_var_safe(buf, sizeof(buf), 1288 (block << 4) | (0 << 3) | lg_crcv->szx), 1289 buf); 1290 } 1291 } 1292send_it: 1293 if (pdu) 1294 coap_send_internal(session, pdu); 1295 lg_crcv->rec_blocks.retry++; 1296 if (block_payload_set != -1) 1297 lg_crcv->rec_blocks.processing_payload_set = block_payload_set; 1298 coap_ticks(&lg_crcv->rec_blocks.last_seen); 1299} 1300#endif /* COAP_Q_BLOCK_SUPPORT */ 1301 1302/* 1303 * return 1 if there is a future expire time, else 0. 1304 * update tim_rem with remaining value if return is 1. 1305 */ 1306int 1307coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now, 1308 coap_tick_t *tim_rem) { 1309 coap_lg_crcv_t *p; 1310 coap_lg_crcv_t *q; 1311 coap_tick_t partial_timeout; 1312#if COAP_Q_BLOCK_SUPPORT 1313 coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session); 1314#endif /* COAP_Q_BLOCK_SUPPORT */ 1315 int ret = 0; 1316 1317 *tim_rem = -1; 1318#if COAP_Q_BLOCK_SUPPORT 1319 if (COAP_PROTO_NOT_RELIABLE(session->proto)) 1320 partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session); 1321 else 1322#endif /* COAP_Q_BLOCK_SUPPORT */ 1323 partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session); 1324 1325 LL_FOREACH_SAFE(session->lg_crcv, p, q) { 1326 if (COAP_PROTO_RELIABLE(session->proto) || p->last_type != COAP_MESSAGE_NON) 1327 goto check_expire; 1328 1329#if COAP_Q_BLOCK_SUPPORT 1330 if (p->block_option == COAP_OPTION_Q_BLOCK2 && p->rec_blocks.used) { 1331 size_t scaled_timeout = receive_timeout * 1332 ((size_t)1 << p->rec_blocks.retry); 1333 1334 if (p->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) { 1335 /* Done NON_MAX_RETRANSMIT retries */ 1336 coap_update_token(&p->pdu, p->app_token->length, p->app_token->s); 1337 session->context->nack_handler(session, &p->pdu, 1338 COAP_NACK_TOO_MANY_RETRIES, 1339 p->pdu.mid); 1340 goto expire; 1341 } 1342 if (p->rec_blocks.last_seen + scaled_timeout <= now) { 1343 coap_request_missing_q_block2(session, p); 1344 } else { 1345 if (*tim_rem > p->rec_blocks.last_seen + scaled_timeout - now) { 1346 *tim_rem = p->rec_blocks.last_seen + scaled_timeout - now; 1347 ret = 1; 1348 } 1349 } 1350 } 1351#endif /* COAP_Q_BLOCK_SUPPORT */ 1352 /* Used for Block2 and Q-Block2 */ 1353check_expire: 1354 if (!p->observe_set && p->last_used && 1355 p->last_used + partial_timeout <= now) { 1356#if COAP_Q_BLOCK_SUPPORT 1357expire: 1358#endif /* COAP_Q_BLOCK_SUPPORT */ 1359 /* Expire this entry */ 1360 LL_DELETE(session->lg_crcv, p); 1361 coap_block_delete_lg_crcv(session, p); 1362 } else if (!p->observe_set && p->last_used) { 1363 /* Delay until the lg_crcv needs to expire */ 1364 if (*tim_rem > p->last_used + partial_timeout - now) { 1365 *tim_rem = p->last_used + partial_timeout - now; 1366 ret = 1; 1367 } 1368 } 1369 } 1370 return ret; 1371} 1372#endif /* COAP_CLIENT_SUPPORT */ 1373 1374#if COAP_SERVER_SUPPORT 1375#if COAP_Q_BLOCK_SUPPORT 1376static coap_pdu_t * 1377pdu_408_build(coap_session_t *session, coap_lg_srcv_t *p) { 1378 coap_pdu_t *pdu; 1379 uint8_t buf[4]; 1380 1381 pdu = coap_pdu_init(COAP_MESSAGE_NON, 1382 COAP_RESPONSE_CODE(408), 1383 coap_new_message_id(session), 1384 coap_session_max_pdu_size(session)); 1385 if (!pdu) 1386 return NULL; 1387 if (p->last_token) 1388 coap_add_token(pdu, p->last_token->length, p->last_token->s); 1389 coap_add_option_internal(pdu, COAP_OPTION_CONTENT_TYPE, 1390 coap_encode_var_safe(buf, sizeof(buf), 1391 COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ), 1392 buf); 1393 pdu->token[pdu->used_size++] = COAP_PAYLOAD_START; 1394 pdu->data = pdu->token + pdu->used_size; 1395 return pdu; 1396} 1397 1398static int 1399add_408_block(coap_pdu_t *pdu, int block) { 1400 size_t len; 1401 uint8_t val[8]; 1402 1403 assert(block >= 0 && block < (1 << 20)); 1404 1405 if (block < 0 || block >= (1 << 20)) { 1406 return 0; 1407 } else if (block < 24) { 1408 len = 1; 1409 val[0] = block; 1410 } else if (block < 0x100) { 1411 len = 2; 1412 val[0] = 24; 1413 val[1] = block; 1414 } else if (block < 0x10000) { 1415 len = 3; 1416 val[0] = 25; 1417 val[1] = block >> 8; 1418 val[2] = block & 0xff; 1419 } else { /* Largest block number is 2^^20 - 1 */ 1420 len = 4; 1421 val[0] = 26; 1422 val[1] = block >> 16; 1423 val[2] = (block >> 8) & 0xff; 1424 val[3] = block & 0xff; 1425 } 1426 if (coap_pdu_check_resize(pdu, pdu->used_size + len)) { 1427 memcpy(&pdu->token[pdu->used_size], val, len); 1428 pdu->used_size += len; 1429 return 1; 1430 } 1431 return 0; 1432} 1433#endif /* COAP_Q_BLOCK_SUPPORT */ 1434#endif /* COAP_SERVER_SUPPORT */ 1435 1436static int 1437check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) { 1438 uint32_t i; 1439 1440 for (i = 0; i < rec_blocks->used; i++) { 1441 if (block_num < rec_blocks->range[i].begin) 1442 return 0; 1443 if (block_num <= rec_blocks->range[i].end) 1444 return 1; 1445 } 1446 return 0; 1447} 1448 1449static int 1450check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks) { 1451 uint32_t i; 1452 uint32_t block = 0; 1453 1454 for (i = 0; i < rec_blocks->used; i++) { 1455 if (block < rec_blocks->range[i].begin) 1456 return 0; 1457 if (block < rec_blocks->range[i].end) 1458 block = rec_blocks->range[i].end; 1459 } 1460 /* total_blocks counts from 1 */ 1461 if (block + 1 < total_blocks) 1462 return 0; 1463 1464 return 1; 1465} 1466 1467#if COAP_CLIENT_SUPPORT 1468#if COAP_Q_BLOCK_SUPPORT 1469static int 1470check_all_blocks_in_for_payload_set(coap_session_t *session, 1471 coap_rblock_t *rec_blocks) { 1472 if (rec_blocks->used && 1473 (rec_blocks->range[0].end + 1) / COAP_MAX_PAYLOADS(session) > 1474 rec_blocks->processing_payload_set) 1475 return 1; 1476 return 0; 1477} 1478 1479static int 1480check_any_blocks_next_payload_set(coap_session_t *session, 1481 coap_rblock_t *rec_blocks) { 1482 if (rec_blocks->used > 1 && 1483 rec_blocks->range[1].begin / COAP_MAX_PAYLOADS(session) == 1484 rec_blocks->processing_payload_set) 1485 return 1; 1486 return 0; 1487} 1488#endif /* COAP_Q_BLOCK_SUPPORT */ 1489#endif /* COAP_CLIENT_SUPPORT */ 1490 1491#if COAP_SERVER_SUPPORT 1492/* 1493 * return 1 if there is a future expire time, else 0. 1494 * update tim_rem with remaining value if return is 1. 1495 */ 1496int 1497coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now, 1498 coap_tick_t *tim_rem) { 1499 coap_lg_srcv_t *p; 1500 coap_lg_srcv_t *q; 1501 coap_tick_t partial_timeout; 1502#if COAP_Q_BLOCK_SUPPORT 1503 coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session); 1504#endif /* COAP_Q_BLOCK_SUPPORT */ 1505 int ret = 0; 1506 1507 *tim_rem = -1; 1508#if COAP_Q_BLOCK_SUPPORT 1509 if (COAP_PROTO_NOT_RELIABLE(session->proto)) 1510 partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session); 1511 else 1512#endif /* COAP_Q_BLOCK_SUPPORT */ 1513 partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session); 1514 1515 LL_FOREACH_SAFE(session->lg_srcv, p, q) { 1516 if (COAP_PROTO_RELIABLE(session->proto) || p->last_type != COAP_MESSAGE_NON) 1517 goto check_expire; 1518 1519#if COAP_Q_BLOCK_SUPPORT 1520 if (p->block_option == COAP_OPTION_Q_BLOCK1 && p->rec_blocks.used) { 1521 size_t scaled_timeout = receive_timeout * 1522 ((size_t)1 << p->rec_blocks.retry); 1523 1524 if (p->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) { 1525 /* Done NON_MAX_RETRANSMIT retries */ 1526 goto expire; 1527 } 1528 if (p->rec_blocks.last_seen + scaled_timeout <= now) { 1529 uint32_t i; 1530 int block = -1; /* Last one seen */ 1531 size_t block_size = (size_t)1 << (p->szx + 4); 1532 size_t final_block = (p->total_len + block_size - 1)/block_size - 1; 1533 size_t cur_payload; 1534 size_t last_payload_block; 1535 coap_pdu_t *pdu = NULL; 1536 size_t no_blocks = 0; 1537 1538 /* Need to count the number of missing blocks */ 1539 for (i = 0; i < p->rec_blocks.used; i++) { 1540 if (block < (int)p->rec_blocks.range[i].begin && 1541 p->rec_blocks.range[i].begin != 0) { 1542 block++; 1543 no_blocks += p->rec_blocks.range[i].begin - block; 1544 } 1545 if (block < (int)p->rec_blocks.range[i].end) { 1546 block = p->rec_blocks.range[i].end; 1547 } 1548 } 1549 if (no_blocks == 0 && block == (int)final_block) 1550 goto expire; 1551 1552 /* Include missing up to end of current payload or total amount */ 1553 cur_payload = block / COAP_MAX_PAYLOADS(session); 1554 last_payload_block = (cur_payload + 1) * COAP_MAX_PAYLOADS(session) - 1; 1555 if (final_block > last_payload_block) { 1556 final_block = last_payload_block; 1557 } 1558 no_blocks += final_block - block; 1559 if (no_blocks == 0) { 1560 /* Add in the blocks out of the next payload */ 1561 final_block = (p->total_len + block_size - 1)/block_size - 1; 1562 last_payload_block += COAP_MAX_PAYLOADS(session); 1563 if (final_block > last_payload_block) { 1564 final_block = last_payload_block; 1565 } 1566 no_blocks += final_block - block; 1567 } 1568 /* Ask for the missing blocks */ 1569 block = -1; 1570 for (i = 0; i < p->rec_blocks.used; i++) { 1571 if (block < (int)p->rec_blocks.range[i].begin && 1572 p->rec_blocks.range[i].begin != 0) { 1573 /* Report on missing blocks */ 1574 if (pdu == NULL) { 1575 pdu = pdu_408_build(session, p); 1576 if (!pdu) 1577 continue; 1578 } 1579 block++; 1580 for (; block < (int)p->rec_blocks.range[i].begin; block++) { 1581 if (!add_408_block(pdu, block)) { 1582 break; 1583 } 1584 } 1585 } 1586 if (block < (int)p->rec_blocks.range[i].end) { 1587 block = p->rec_blocks.range[i].end; 1588 } 1589 } 1590 block++; 1591 for (; block <= (int)final_block; block++) { 1592 if (pdu == NULL) { 1593 pdu = pdu_408_build(session, p); 1594 if (!pdu) 1595 continue; 1596 } 1597 if (!add_408_block(pdu, block)) { 1598 break; 1599 } 1600 } 1601 if (pdu) 1602 coap_send_internal(session, pdu); 1603 p->rec_blocks.retry++; 1604 coap_ticks(&p->rec_blocks.last_seen); 1605 } 1606 if (*tim_rem > p->rec_blocks.last_seen + scaled_timeout - now) { 1607 *tim_rem = p->rec_blocks.last_seen + scaled_timeout - now; 1608 ret = 1; 1609 } 1610 } 1611#endif /* COAP_Q_BLOCK_SUPPORT */ 1612 /* Used for Block1 and Q-Block1 */ 1613check_expire: 1614 if (p->last_used && p->last_used + partial_timeout <= now) { 1615#if COAP_Q_BLOCK_SUPPORT 1616expire: 1617#endif /* COAP_Q_BLOCK_SUPPORT */ 1618 /* Expire this entry */ 1619 LL_DELETE(session->lg_srcv, p); 1620 coap_block_delete_lg_srcv(session, p); 1621 } else if (p->last_used) { 1622 /* Delay until the lg_srcv needs to expire */ 1623 if (*tim_rem > p->last_used + partial_timeout - now) { 1624 *tim_rem = p->last_used + partial_timeout - now; 1625 ret = 1; 1626 } 1627 } 1628 } 1629 return ret; 1630} 1631#endif /* COAP_SERVER_SUPPORT */ 1632 1633#if COAP_Q_BLOCK_SUPPORT 1634/* 1635 * pdu is always released before return IF COAP_SEND_INC_PDU 1636 */ 1637coap_mid_t 1638coap_send_q_blocks(coap_session_t *session, 1639 coap_lg_xmit_t *lg_xmit, 1640 coap_block_b_t block, 1641 coap_pdu_t *pdu, 1642 coap_send_pdu_t send_pdu) { 1643 coap_pdu_t *block_pdu = NULL; 1644 coap_opt_filter_t drop_options; 1645 coap_mid_t mid = COAP_INVALID_MID; 1646 uint64_t token = coap_decode_var_bytes8(pdu->actual_token.s, 1647 pdu->actual_token.length); 1648 const uint8_t *ptoken; 1649 uint8_t ltoken[8]; 1650 size_t ltoken_length; 1651 uint32_t delayqueue_cnt = 0; 1652 1653 if (!lg_xmit) { 1654 if (send_pdu == COAP_SEND_INC_PDU) 1655 return coap_send_internal(session, pdu); 1656 return COAP_INVALID_MID; 1657 } 1658 1659 if (pdu->type == COAP_MESSAGE_CON) { 1660 coap_queue_t *delayqueue; 1661 1662 delayqueue_cnt = session->con_active + 1663 (send_pdu == COAP_SEND_INC_PDU ? 1 : 0); 1664 LL_FOREACH(session->delayqueue, delayqueue) { 1665 delayqueue_cnt++; 1666 } 1667 } 1668 pdu->lg_xmit = lg_xmit; 1669 if (block.m && 1670 ((pdu->type == COAP_MESSAGE_NON && 1671 ((block.num + 1) % COAP_MAX_PAYLOADS(session)) + 1 != 1672 COAP_MAX_PAYLOADS(session)) || 1673 (pdu->type == COAP_MESSAGE_ACK && 1674 lg_xmit->option == COAP_OPTION_Q_BLOCK2) || 1675 (pdu->type == COAP_MESSAGE_CON && 1676 delayqueue_cnt < COAP_NSTART(session)) || 1677 COAP_PROTO_RELIABLE(session->proto))) { 1678 /* Allocate next pdu if there is headroom */ 1679 if (COAP_PDU_IS_RESPONSE(pdu)) { 1680 ptoken = pdu->actual_token.s; 1681 ltoken_length = pdu->actual_token.length; 1682 } else { 1683 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count); 1684 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token); 1685 ptoken = ltoken; 1686 } 1687 1688 memset(&drop_options, 0, sizeof(coap_opt_filter_t)); 1689 coap_option_filter_set(&drop_options, lg_xmit->option); 1690 block_pdu = coap_pdu_duplicate(pdu, session, 1691 ltoken_length, 1692 ptoken, &drop_options); 1693 if (block_pdu->type == COAP_MESSAGE_ACK) 1694 block_pdu->type = COAP_MESSAGE_CON; 1695 } 1696 1697 /* Send initial pdu (which deletes 'pdu') */ 1698 if (send_pdu == COAP_SEND_INC_PDU && 1699 (mid = coap_send_internal(session, pdu)) == COAP_INVALID_MID) { 1700 /* Not expected, underlying issue somewhere */ 1701 coap_delete_pdu(block_pdu); 1702 return COAP_INVALID_MID; 1703 } 1704 1705 while (block_pdu) { 1706 coap_pdu_t *t_pdu = NULL; 1707 uint8_t buf[8]; 1708 size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4)); 1709 1710 block.num++; 1711 lg_xmit->offset = block.num * chunk; 1712 block.m = lg_xmit->offset + chunk < lg_xmit->length; 1713 if (block.m && ((block_pdu->type == COAP_MESSAGE_NON && 1714 (block.num % COAP_MAX_PAYLOADS(session)) + 1 != 1715 COAP_MAX_PAYLOADS(session)) || 1716 (block_pdu->type == COAP_MESSAGE_CON && 1717 delayqueue_cnt + 1 < COAP_NSTART(session)) || 1718 COAP_PROTO_RELIABLE(session->proto))) { 1719 /* 1720 * Send following block if 1721 * NON and more in MAX_PAYLOADS 1722 * CON and NSTART allows it (based on number in delayqueue) 1723 * Reliable transport 1724 */ 1725 if (COAP_PDU_IS_RESPONSE(block_pdu)) { 1726 ptoken = block_pdu->actual_token.s; 1727 ltoken_length = block_pdu->actual_token.length; 1728 } else { 1729 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count); 1730 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token); 1731 ptoken = ltoken; 1732 } 1733 t_pdu = coap_pdu_duplicate(block_pdu, session, 1734 ltoken_length, ptoken, &drop_options); 1735 } 1736 if (!coap_update_option(block_pdu, lg_xmit->option, 1737 coap_encode_var_safe(buf, 1738 sizeof(buf), 1739 ((block.num) << 4) | 1740 (block.m << 3) | 1741 block.szx), 1742 buf)) { 1743 coap_log_warn("Internal update issue option\n"); 1744 coap_delete_pdu(block_pdu); 1745 coap_delete_pdu(t_pdu); 1746 break; 1747 } 1748 1749 if (!coap_add_block(block_pdu, 1750 lg_xmit->length, 1751 lg_xmit->data, 1752 block.num, 1753 block.szx)) { 1754 coap_log_warn("Internal update issue data\n"); 1755 coap_delete_pdu(block_pdu); 1756 coap_delete_pdu(t_pdu); 1757 break; 1758 } 1759 if (COAP_PDU_IS_RESPONSE(block_pdu)) { 1760 lg_xmit->last_block = block.num; 1761 } 1762 mid = coap_send_internal(session, block_pdu); 1763 if (mid == COAP_INVALID_MID) { 1764 /* Not expected, underlying issue somewhere */ 1765 coap_delete_pdu(t_pdu); 1766 return COAP_INVALID_MID; 1767 } 1768 block_pdu = t_pdu; 1769 } 1770 if (!block.m) { 1771 lg_xmit->last_payload = 0; 1772 coap_ticks(&lg_xmit->last_all_sent); 1773 } else 1774 coap_ticks(&lg_xmit->last_payload); 1775 return mid; 1776} 1777 1778#if COAP_CLIENT_SUPPORT 1779coap_tick_t 1780coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now) { 1781 coap_lg_xmit_t *lg_xmit; 1782 coap_lg_xmit_t *q; 1783 coap_tick_t timed_out; 1784 coap_tick_t tim_rem = (coap_tick_t)-1; 1785 1786 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { 1787 coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks; 1788 1789 if (now < non_timeout) 1790 return non_timeout - now; 1791 timed_out = now - non_timeout; 1792 1793 if (lg_xmit->last_payload) { 1794 if (lg_xmit->last_payload <= timed_out) { 1795 /* Send off the next MAX_PAYLOAD set */ 1796 coap_block_b_t block; 1797 size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); 1798 1799 memset(&block, 0, sizeof(block)); 1800 block.num = (uint32_t)(lg_xmit->offset / chunk); 1801 block.m = lg_xmit->offset + chunk < lg_xmit->length; 1802 block.szx = lg_xmit->blk_size; 1803 coap_send_q_blocks(session, lg_xmit, block, &lg_xmit->pdu, COAP_SEND_SKIP_PDU); 1804 if (tim_rem > non_timeout) 1805 tim_rem = non_timeout; 1806 } else { 1807 /* Delay until the next MAX_PAYLOAD needs to be sent off */ 1808 if (tim_rem > lg_xmit->last_payload - timed_out) 1809 tim_rem = lg_xmit->last_payload - timed_out; 1810 } 1811 } else if (lg_xmit->last_all_sent) { 1812 non_timeout = COAP_NON_TIMEOUT_TICKS(session); 1813 if (lg_xmit->last_all_sent + 4 * non_timeout <= now) { 1814 /* Expire this entry */ 1815 LL_DELETE(session->lg_xmit, lg_xmit); 1816 coap_block_delete_lg_xmit(session, lg_xmit); 1817 } else { 1818 /* Delay until the lg_xmit needs to expire */ 1819 if (tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) 1820 tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now; 1821 } 1822 } 1823 } 1824 return tim_rem; 1825} 1826#endif /* COAP_CLIENT_SUPPORT */ 1827 1828#if COAP_SERVER_SUPPORT 1829coap_tick_t 1830coap_block_check_q_block2_xmit(coap_session_t *session, coap_tick_t now) { 1831 coap_lg_xmit_t *lg_xmit; 1832 coap_lg_xmit_t *q; 1833 coap_tick_t timed_out; 1834 coap_tick_t tim_rem = (coap_tick_t)-1; 1835 1836 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { 1837 coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks; 1838 1839 if (now < non_timeout) 1840 return non_timeout - now; 1841 timed_out = now - non_timeout; 1842 1843 if (lg_xmit->last_payload) { 1844 if (lg_xmit->last_payload <= timed_out) { 1845 /* Send off the next MAX_PAYLOAD set */ 1846 coap_block_b_t block; 1847 size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); 1848 1849 memset(&block, 0, sizeof(block)); 1850 block.num = (uint32_t)(lg_xmit->offset / chunk); 1851 block.m = lg_xmit->offset + chunk < lg_xmit->length; 1852 block.szx = lg_xmit->blk_size; 1853 if (block.num == (uint32_t)lg_xmit->last_block) 1854 coap_send_q_blocks(session, lg_xmit, block, &lg_xmit->pdu, COAP_SEND_SKIP_PDU); 1855 if (tim_rem > non_timeout) 1856 tim_rem = non_timeout; 1857 } else { 1858 /* Delay until the next MAX_PAYLOAD needs to be sent off */ 1859 if (tim_rem > lg_xmit->last_payload - timed_out) 1860 tim_rem = lg_xmit->last_payload - timed_out; 1861 } 1862 } else if (lg_xmit->last_all_sent) { 1863 non_timeout = COAP_NON_TIMEOUT_TICKS(session); 1864 if (lg_xmit->last_all_sent + 4 * non_timeout <= now) { 1865 /* Expire this entry */ 1866 LL_DELETE(session->lg_xmit, lg_xmit); 1867 coap_block_delete_lg_xmit(session, lg_xmit); 1868 } else { 1869 /* Delay until the lg_xmit needs to expire */ 1870 if (tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) 1871 tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now; 1872 } 1873 } 1874 } 1875 return tim_rem; 1876} 1877#endif /* COAP_SERVER_SUPPORT */ 1878#endif /* COAP_Q_BLOCK_SUPPORT */ 1879 1880#if COAP_CLIENT_SUPPORT 1881/* 1882 * If Observe = 0, save the token away and return NULL 1883 * Else If Observe = 1, return the saved token for this block 1884 * Else, return NULL 1885 */ 1886static coap_bin_const_t * 1887track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv, 1888 uint32_t block_num, coap_bin_const_t *token) { 1889 /* Need to handle Observe for large FETCH */ 1890 coap_opt_iterator_t opt_iter; 1891 coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, 1892 &opt_iter); 1893 1894 if (opt && lg_crcv) { 1895 int observe_action = -1; 1896 coap_bin_const_t **tmp; 1897 1898 observe_action = coap_decode_var_bytes(coap_opt_value(opt), 1899 coap_opt_length(opt)); 1900 if (observe_action == COAP_OBSERVE_ESTABLISH) { 1901 /* Save the token in lg_crcv */ 1902 tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token, 1903 (block_num + 1) * sizeof(lg_crcv->obs_token[0])); 1904 if (tmp == NULL) 1905 return NULL; 1906 lg_crcv->obs_token = tmp; 1907 if (block_num + 1 == lg_crcv->obs_token_cnt) 1908 coap_delete_bin_const(lg_crcv->obs_token[block_num]); 1909 1910 lg_crcv->obs_token_cnt = block_num + 1; 1911 lg_crcv->obs_token[block_num] = coap_new_bin_const(token->s, 1912 token->length); 1913 if (lg_crcv->obs_token[block_num] == NULL) 1914 return NULL; 1915 } else if (observe_action == COAP_OBSERVE_CANCEL) { 1916 /* Use the token in lg_crcv */ 1917 if (block_num < lg_crcv->obs_token_cnt) { 1918 if (lg_crcv->obs_token[block_num]) { 1919 return lg_crcv->obs_token[block_num]; 1920 } 1921 } 1922 } 1923 } 1924 return NULL; 1925} 1926 1927#if COAP_Q_BLOCK_SUPPORT 1928coap_mid_t 1929coap_send_q_block1(coap_session_t *session, 1930 coap_block_b_t block, 1931 coap_pdu_t *request, 1932 coap_send_pdu_t send_request) { 1933 /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK1 */ 1934 coap_lg_xmit_t *lg_xmit; 1935 uint64_t token_match = 1936 STATE_TOKEN_BASE(coap_decode_var_bytes8(request->actual_token.s, 1937 request->actual_token.length)); 1938 1939 LL_FOREACH(session->lg_xmit, lg_xmit) { 1940 if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 && 1941 (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) || 1942 token_match == 1943 STATE_TOKEN_BASE(coap_decode_var_bytes8(lg_xmit->b.b1.app_token->s, 1944 lg_xmit->b.b1.app_token->length)))) 1945 break; 1946 /* try out the next one */ 1947 } 1948 return coap_send_q_blocks(session, lg_xmit, block, request, send_request); 1949} 1950#endif /* COAP_Q_BLOCK_SUPPORT */ 1951#endif /* COAP_CLIENT_SUPPORT */ 1952 1953#if COAP_SERVER_SUPPORT 1954#if COAP_Q_BLOCK_SUPPORT 1955/* 1956 * response is always released before return IF COAP_SEND_INC_PDU 1957 */ 1958coap_mid_t 1959coap_send_q_block2(coap_session_t *session, 1960 coap_resource_t *resource, 1961 const coap_string_t *query, 1962 coap_pdu_code_t request_method, 1963 coap_block_b_t block, 1964 coap_pdu_t *response, 1965 coap_send_pdu_t send_response) { 1966 /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK2 */ 1967 coap_lg_xmit_t *lg_xmit; 1968 coap_string_t empty = { 0, NULL}; 1969 1970 LL_FOREACH(session->lg_xmit, lg_xmit) { 1971 if (lg_xmit->option == COAP_OPTION_Q_BLOCK2 && 1972 resource == lg_xmit->b.b2.resource && 1973 request_method == lg_xmit->b.b2.request_method && 1974 coap_string_equal(query ? query : &empty, 1975 lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) 1976 break; 1977 } 1978 return coap_send_q_blocks(session, lg_xmit, block, response, send_response); 1979} 1980#endif /* COAP_Q_BLOCK_SUPPORT */ 1981#endif /* COAP_SERVER_SUPPORT */ 1982 1983#if COAP_CLIENT_SUPPORT 1984#if COAP_Q_BLOCK_SUPPORT 1985/* 1986 * Send out a test PDU for Q-Block. 1987 */ 1988coap_mid_t 1989coap_block_test_q_block(coap_session_t *session, coap_pdu_t *actual) { 1990 coap_pdu_t *pdu; 1991 uint8_t token[8]; 1992 size_t token_len; 1993 uint8_t buf[4]; 1994 coap_mid_t mid; 1995 1996#if NDEBUG 1997 (void)actual; 1998#endif /* NDEBUG */ 1999 assert(session->block_mode & COAP_BLOCK_TRY_Q_BLOCK && 2000 session->type == COAP_SESSION_TYPE_CLIENT && 2001 COAP_PDU_IS_REQUEST(actual)); 2002 2003 coap_log_debug("Testing for Q-Block support\n"); 2004 /* RFC9177 Section 3.1 when checking if available */ 2005 pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, 2006 coap_new_message_id(session), 2007 coap_session_max_pdu_size(session)); 2008 if (!pdu) { 2009 return COAP_INVALID_MID; 2010 } 2011 2012 coap_session_new_token(session, &token_len, token); 2013 coap_add_token(pdu, token_len, token); 2014 /* M needs to be unset as 'asking' for only the first block */ 2015 coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, 2016 coap_encode_var_safe(buf, sizeof(buf), 2017 (0 << 4) | (0 << 3) | 0), 2018 buf); 2019 set_block_mode_probe_q(session->block_mode); 2020 mid = coap_send_internal(session, pdu); 2021 if (mid == COAP_INVALID_MID) 2022 return COAP_INVALID_MID; 2023 session->remote_test_mid = mid; 2024 return mid; 2025} 2026#endif /* COAP_Q_BLOCK_SUPPORT */ 2027 2028coap_lg_crcv_t * 2029coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu, 2030 coap_lg_xmit_t *lg_xmit) { 2031 coap_lg_crcv_t *lg_crcv; 2032 uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1); 2033 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : 2034 pdu->used_size; 2035 size_t data_len = lg_xmit ? lg_xmit->length : 2036 pdu->data ? 2037 pdu->used_size - (pdu->data - pdu->token) : 0; 2038 2039 lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t)); 2040 2041 if (lg_crcv == NULL) 2042 return NULL; 2043 2044 coap_log_debug("** %s: lg_crcv %p initialized - stateless token xxxx%012llx\n", 2045 coap_session_str(session), (void *)lg_crcv, 2046 STATE_TOKEN_BASE(state_token)); 2047 memset(lg_crcv, 0, sizeof(coap_lg_crcv_t)); 2048 lg_crcv->initial = 1; 2049 coap_ticks(&lg_crcv->last_used); 2050 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */ 2051 memcpy(&lg_crcv->pdu, pdu, sizeof(lg_crcv->pdu)); 2052 /* Make sure that there is space for increased token + option change */ 2053 lg_crcv->pdu.max_size = token_options + data_len + 9; 2054 lg_crcv->pdu.used_size = token_options + data_len; 2055 lg_crcv->pdu.token = coap_malloc_type(COAP_PDU_BUF, 2056 token_options + data_len + lg_crcv->pdu.max_hdr_size); 2057 if (!lg_crcv->pdu.token) { 2058 coap_block_delete_lg_crcv(session, lg_crcv); 2059 return NULL; 2060 } 2061 lg_crcv->pdu.token += lg_crcv->pdu.max_hdr_size; 2062 memcpy(lg_crcv->pdu.token, pdu->token, token_options); 2063 if (lg_crcv->pdu.data) { 2064 lg_crcv->pdu.data = lg_crcv->pdu.token + token_options; 2065 memcpy(lg_crcv->pdu.data, lg_xmit ? lg_xmit->data : pdu->data, data_len); 2066 } 2067 2068 /* Need to keep original token for updating response PDUs */ 2069 lg_crcv->app_token = coap_new_binary(pdu->actual_token.length); 2070 if (!lg_crcv->app_token) { 2071 coap_block_delete_lg_crcv(session, lg_crcv); 2072 return NULL; 2073 } 2074 memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length); 2075 2076 /* Need to set up a base token for actual communications if retries needed */ 2077 lg_crcv->retry_counter = 1; 2078 lg_crcv->state_token = state_token; 2079 2080 if (pdu->code == COAP_REQUEST_CODE_FETCH) { 2081 coap_bin_const_t *new_token; 2082 2083 /* Need to save/restore Observe Token for large FETCH */ 2084 new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token); 2085 if (new_token) 2086 coap_update_token(pdu, new_token->length, new_token->s); 2087 } 2088 2089 /* In case it is there - must not be in continuing request PDUs */ 2090 coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1); 2091 2092 return lg_crcv; 2093} 2094 2095void 2096coap_block_delete_lg_crcv(coap_session_t *session, 2097 coap_lg_crcv_t *lg_crcv) { 2098 size_t i; 2099 2100#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG) 2101 (void)session; 2102#endif 2103 if (lg_crcv == NULL) 2104 return; 2105 2106 if (lg_crcv->pdu.token) 2107 coap_free_type(COAP_PDU_BUF, lg_crcv->pdu.token - lg_crcv->pdu.max_hdr_size); 2108 coap_free_type(COAP_STRING, lg_crcv->body_data); 2109 coap_log_debug("** %s: lg_crcv %p released\n", 2110 coap_session_str(session), (void *)lg_crcv); 2111 coap_delete_binary(lg_crcv->app_token); 2112 for (i = 0; i < lg_crcv->obs_token_cnt; i++) { 2113 coap_delete_bin_const(lg_crcv->obs_token[i]); 2114 } 2115 coap_free_type(COAP_STRING, lg_crcv->obs_token); 2116 coap_free_type(COAP_LG_CRCV, lg_crcv); 2117} 2118#endif /* COAP_CLIENT_SUPPORT */ 2119 2120#if COAP_SERVER_SUPPORT 2121void 2122coap_block_delete_lg_srcv(coap_session_t *session, 2123 coap_lg_srcv_t *lg_srcv) { 2124#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG) 2125 (void)session; 2126#endif 2127 if (lg_srcv == NULL) 2128 return; 2129 2130 coap_delete_str_const(lg_srcv->uri_path); 2131#if COAP_Q_BLOCK_SUPPORT 2132 coap_delete_bin_const(lg_srcv->last_token); 2133#endif /* COAP_Q_BLOCK_SUPPORT */ 2134 coap_free_type(COAP_STRING, lg_srcv->body_data); 2135 coap_log_debug("** %s: lg_srcv %p released\n", 2136 coap_session_str(session), (void *)lg_srcv); 2137 coap_free_type(COAP_LG_SRCV, lg_srcv); 2138} 2139#endif /* COAP_SERVER_SUPPORT */ 2140 2141void 2142coap_block_delete_lg_xmit(coap_session_t *session, 2143 coap_lg_xmit_t *lg_xmit) { 2144 if (lg_xmit == NULL) 2145 return; 2146 2147 if (lg_xmit->release_func) { 2148 lg_xmit->release_func(session, lg_xmit->app_ptr); 2149 } 2150 if (lg_xmit->pdu.token) { 2151 coap_free_type(COAP_PDU_BUF, lg_xmit->pdu.token - lg_xmit->pdu.max_hdr_size); 2152 } 2153 if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu)) 2154 coap_delete_binary(lg_xmit->b.b1.app_token); 2155 else 2156 coap_delete_string(lg_xmit->b.b2.query); 2157 2158 coap_log_debug("** %s: lg_xmit %p released\n", 2159 coap_session_str(session), (void *)lg_xmit); 2160 coap_free_type(COAP_LG_XMIT, lg_xmit); 2161} 2162 2163#if COAP_SERVER_SUPPORT 2164typedef struct { 2165 uint32_t num; 2166 int is_continue; 2167} send_track; 2168 2169static int 2170add_block_send(uint32_t num, int is_continue, send_track *out_blocks, 2171 uint32_t *count, uint32_t max_count) { 2172 uint32_t i; 2173 2174 for (i = 0; i < *count && *count < max_count; i++) { 2175 if (num == out_blocks[i].num) 2176 return 0; 2177 else if (num < out_blocks[i].num) { 2178 if (*count - i > 1) 2179 memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1); 2180 out_blocks[i].num = num; 2181 out_blocks[i].is_continue = is_continue; 2182 (*count)++; 2183 return 1; 2184 } 2185 } 2186 if (*count < max_count) { 2187 out_blocks[i].num = num; 2188 out_blocks[i].is_continue = is_continue; 2189 (*count)++; 2190 return 1; 2191 } 2192 return 0; 2193} 2194 2195/* 2196 * Need to see if this is a request for the next block of a large body 2197 * transfer. If so, need to initiate the response with the next blocks 2198 * and not trouble the application. 2199 * 2200 * If additional responses needed, then these are expicitly sent out and 2201 * 'response' is updated to be the last response to be sent. There can be 2202 * multiple Q-Block2 in the request, as well as the 'Continue' Q-Block2 2203 * request. 2204 * 2205 * This is set up using coap_add_data_large_response() 2206 * 2207 * Server is sending a large data response to GET / observe (Block2) 2208 * 2209 * Return: 0 Call application handler 2210 * 1 Do not call application handler - just send the built response 2211 */ 2212int 2213coap_handle_request_send_block(coap_session_t *session, 2214 coap_pdu_t *pdu, 2215 coap_pdu_t *response, 2216 coap_resource_t *resource, 2217 coap_string_t *query) { 2218 coap_lg_xmit_t *p = NULL; 2219 coap_block_b_t block; 2220 coap_block_b_t alt_block; 2221 uint16_t block_opt = 0; 2222 send_track *out_blocks = NULL; 2223 const char *error_phrase; 2224 coap_opt_iterator_t opt_iter; 2225 size_t chunk; 2226 coap_opt_iterator_t opt_b_iter; 2227 coap_opt_t *option; 2228 uint32_t request_cnt, i; 2229 coap_opt_t *etag_opt = NULL; 2230 coap_pdu_t *out_pdu = response; 2231#if COAP_Q_BLOCK_SUPPORT 2232 size_t max_block; 2233 2234 /* Is client indicating that it supports Q_BLOCK2 ? */ 2235 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) { 2236 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) 2237 set_block_mode_has_q(session->block_mode); 2238 block_opt = COAP_OPTION_Q_BLOCK2; 2239 } 2240#endif /* COAP_Q_BLOCK_SUPPORT */ 2241 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &alt_block)) { 2242 if (block_opt) { 2243 coap_log_warn("Block2 and Q-Block2 cannot be in the same request\n"); 2244 coap_add_data(response, sizeof("Both Block2 and Q-Block2 invalid")-1, 2245 (const uint8_t *)"Both Block2 and Q-Block2 invalid"); 2246 response->code = COAP_RESPONSE_CODE(400); 2247 goto skip_app_handler; 2248 } 2249 block = alt_block; 2250 block_opt = COAP_OPTION_BLOCK2; 2251 } 2252 if (block_opt == 0) 2253 return 0; 2254 if (block.num == 0) { 2255 /* Get a fresh copy of the data */ 2256 return 0; 2257 } 2258 p = coap_find_lg_xmit_response(session, pdu, resource, query); 2259 if (p == NULL) 2260 return 0; 2261 2262#if COAP_Q_BLOCK_SUPPORT 2263 out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track) * COAP_MAX_PAYLOADS(session)); 2264#else /* ! COAP_Q_BLOCK_SUPPORT */ 2265 out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track)); 2266#endif /* ! COAP_Q_BLOCK_SUPPORT */ 2267 if (!out_blocks) { 2268 goto internal_issue; 2269 } 2270 2271 /* lg_xmit (response) found */ 2272 2273 etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter); 2274 if (etag_opt) { 2275 uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt), 2276 coap_opt_length(etag_opt)); 2277 if (etag != p->b.b2.etag) { 2278 /* Not a match - pass up to a higher level */ 2279 return 0; 2280 } 2281 out_pdu->code = COAP_RESPONSE_CODE(203); 2282 coap_ticks(&p->last_sent); 2283 goto skip_app_handler; 2284 } else { 2285 out_pdu->code = p->pdu.code; 2286 } 2287 coap_ticks(&p->last_obs); 2288 p->last_all_sent = 0; 2289 2290 chunk = (size_t)1 << (p->blk_size + 4); 2291 if (block_opt) { 2292 if (block.bert) { 2293 coap_log_debug("found Block option, block is BERT, block nr. %u, M %d\n", 2294 block.num, block.m); 2295 } else { 2296 coap_log_debug("found Block option, block size is %u, block nr. %u, M %d\n", 2297 1 << (block.szx + 4), block.num, block.m); 2298 } 2299 if (block.bert == 0 && block.szx != p->blk_size) { 2300 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) { 2301 /* 2302 * Recompute the block number of the previous packet given 2303 * the new block size 2304 */ 2305 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1); 2306 p->blk_size = block.szx; 2307 chunk = (size_t)1 << (p->blk_size + 4); 2308 p->offset = block.num * chunk; 2309 coap_log_debug("new Block size is %u, block number %u completed\n", 2310 1 << (block.szx + 4), block.num); 2311 } else { 2312 coap_log_debug("ignoring request to increase Block size, " 2313 "next block is not aligned on requested block size " 2314 "boundary. (%zu x %u mod %u = %zu (which is not 0)\n", 2315 p->offset/chunk + 1, (1 << (p->blk_size + 4)), 2316 (1 << (block.szx + 4)), 2317 (p->offset + chunk) % ((size_t)1 << (block.szx + 4))); 2318 } 2319 } 2320 } 2321 2322 /* 2323 * Need to check if there are multiple Q-Block2 requests. If so, they 2324 * need to be sent out in order of requests with the final request being 2325 * handled as per singular Block 2 request. 2326 */ 2327 request_cnt = 0; 2328#if COAP_Q_BLOCK_SUPPORT 2329 max_block = (p->length + chunk - 1)/chunk; 2330#endif /* COAP_Q_BLOCK_SUPPORT */ 2331 coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL); 2332 while ((option = coap_option_next(&opt_b_iter))) { 2333 unsigned int num; 2334 if (opt_b_iter.number != p->option) 2335 continue; 2336 num = coap_opt_block_num(option); 2337 if (num > 0xFFFFF) /* 20 bits max for num */ 2338 continue; 2339 if (block.aszx != COAP_OPT_BLOCK_SZX(option)) { 2340 coap_add_data(response, 2341 sizeof("Changing blocksize during request invalid")-1, 2342 (const uint8_t *)"Changing blocksize during request invalid"); 2343 response->code = COAP_RESPONSE_CODE(400); 2344 goto skip_app_handler; 2345 } 2346#if COAP_Q_BLOCK_SUPPORT 2347 if (COAP_OPT_BLOCK_MORE(option) && p->option == COAP_OPTION_Q_BLOCK2) { 2348 if ((num % COAP_MAX_PAYLOADS(session)) == 0) { 2349 if (num == 0) { 2350 /* This is a repeat request for everything - hmm */ 2351 goto call_app_handler; 2352 } 2353 /* 'Continue' request */ 2354 for (i = 0; i < COAP_MAX_PAYLOADS(session) && 2355 num + i < max_block; i++) { 2356 add_block_send(num + i, 1, out_blocks, &request_cnt, 2357 COAP_MAX_PAYLOADS(session)); 2358 p->last_block = num + i; 2359 } 2360 } else { 2361 /* Requesting remaining payloads in this MAX_PAYLOADS */ 2362 for (i = 0; i < COAP_MAX_PAYLOADS(session) - 2363 num % COAP_MAX_PAYLOADS(session) && 2364 num + i < max_block; i++) { 2365 add_block_send(num + i, 0, out_blocks, &request_cnt, 2366 COAP_MAX_PAYLOADS(session)); 2367 } 2368 } 2369 } else 2370 add_block_send(num, 0, out_blocks, &request_cnt, 2371 COAP_MAX_PAYLOADS(session)); 2372#else /* ! COAP_Q_BLOCK_SUPPORT */ 2373 add_block_send(num, 0, out_blocks, &request_cnt, 1); 2374 break; 2375#endif /* ! COAP_Q_BLOCK_SUPPORT */ 2376 } 2377 if (request_cnt == 0) { 2378 /* Block2 or Q-Block2 not found - give them the first block */ 2379 block.szx = p->blk_size; 2380 p->offset = 0; 2381 out_blocks[0].num = 0; 2382 out_blocks[0].is_continue = 0; 2383 request_cnt = 1; 2384 } 2385 2386 for (i = 0; i < request_cnt; i++) { 2387 uint8_t buf[8]; 2388 2389 block.num = out_blocks[i].num; 2390 p->offset = block.num * chunk; 2391 2392 if (i + 1 < request_cnt) { 2393 /* Need to set up a copy of the pdu to send */ 2394 coap_opt_filter_t drop_options; 2395 2396 memset(&drop_options, 0, sizeof(coap_opt_filter_t)); 2397 if (block.num != 0) 2398 coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); 2399 if (out_blocks[i].is_continue) { 2400 out_pdu = coap_pdu_duplicate(&p->pdu, session, p->pdu.actual_token.length, 2401 p->pdu.actual_token.s, &drop_options); 2402 } else { 2403 out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->actual_token.length, 2404 pdu->actual_token.s, &drop_options); 2405 } 2406 if (!out_pdu) { 2407 goto internal_issue; 2408 } 2409 } else { 2410 if (out_blocks[i].is_continue) 2411 coap_update_token(response, p->pdu.actual_token.length, 2412 p->pdu.actual_token.s); 2413 /* 2414 * Copy the options across and then fix the block option 2415 * 2416 * Need to drop Observe option if Block2 and block.num != 0 2417 */ 2418 coap_option_iterator_init(&p->pdu, &opt_iter, COAP_OPT_ALL); 2419 while ((option = coap_option_next(&opt_iter))) { 2420 if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0) 2421 continue; 2422 if (!coap_insert_option(response, opt_iter.number, 2423 coap_opt_length(option), 2424 coap_opt_value(option))) { 2425 goto internal_issue; 2426 } 2427 } 2428 out_pdu = response; 2429 } 2430 if (pdu->type == COAP_MESSAGE_NON) 2431 out_pdu->type = COAP_MESSAGE_NON; 2432 if (block.bert) { 2433 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; 2434 block.m = (p->length - p->offset) > 2435 ((out_pdu->max_size - token_options) /1024) * 1024; 2436 } else { 2437 block.m = (p->offset + chunk) < p->length; 2438 } 2439 if (!coap_update_option(out_pdu, p->option, 2440 coap_encode_var_safe(buf, 2441 sizeof(buf), 2442 (block.num << 4) | 2443 (block.m << 3) | 2444 block.aszx), 2445 buf)) { 2446 goto internal_issue; 2447 } 2448 if (!(p->offset + chunk < p->length)) { 2449 /* Last block - keep in cache for 4 * ACK_TIMOUT */ 2450 coap_ticks(&p->last_all_sent); 2451 } 2452 if (p->b.b2.maxage_expire) { 2453 coap_tick_t now; 2454 coap_time_t rem; 2455 2456 if (!(p->offset + chunk < p->length)) { 2457 /* Last block - keep in cache for 4 * ACK_TIMOUT */ 2458 coap_ticks(&p->last_all_sent); 2459 } 2460 coap_ticks(&now); 2461 rem = coap_ticks_to_rt(now); 2462 if (p->b.b2.maxage_expire > rem) { 2463 rem = p->b.b2.maxage_expire - rem; 2464 } else { 2465 rem = 0; 2466 /* Entry needs to be expired */ 2467 coap_ticks(&p->last_all_sent); 2468 } 2469 if (!coap_update_option(out_pdu, COAP_OPTION_MAXAGE, 2470 coap_encode_var_safe8(buf, 2471 sizeof(buf), 2472 rem), 2473 buf)) { 2474 goto internal_issue; 2475 } 2476 } 2477 2478 if (!etag_opt && !coap_add_block_b_data(out_pdu, 2479 p->length, 2480 p->data, 2481 &block)) { 2482 goto internal_issue; 2483 } 2484 if (i + 1 < request_cnt) { 2485 coap_ticks(&p->last_sent); 2486 coap_send_internal(session, out_pdu); 2487 } 2488 } 2489 coap_ticks(&p->last_payload); 2490 goto skip_app_handler; 2491#if COAP_Q_BLOCK_SUPPORT 2492call_app_handler: 2493 coap_free_type(COAP_STRING, out_blocks); 2494 return 0; 2495#endif /* COAP_Q_BLOCK_SUPPORT */ 2496 2497internal_issue: 2498 response->code = COAP_RESPONSE_CODE(500); 2499 error_phrase = coap_response_phrase(response->code); 2500 coap_add_data(response, strlen(error_phrase), 2501 (const uint8_t *)error_phrase); 2502 /* Keep in cache for 4 * ACK_TIMOUT incase of retry */ 2503 if (p) 2504 coap_ticks(&p->last_all_sent); 2505 2506skip_app_handler: 2507 coap_free_type(COAP_STRING, out_blocks); 2508 return 1; 2509} 2510#endif /* COAP_SERVER_SUPPORT */ 2511 2512static int 2513update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num) { 2514 uint32_t i; 2515 2516 /* Reset as there is activity */ 2517 rec_blocks->retry = 0; 2518 2519 for (i = 0; i < rec_blocks->used; i++) { 2520 if (block_num >= rec_blocks->range[i].begin && 2521 block_num <= rec_blocks->range[i].end) 2522 break; 2523 2524 if (block_num < rec_blocks->range[i].begin) { 2525 if (block_num + 1 == rec_blocks->range[i].begin) { 2526 rec_blocks->range[i].begin = block_num; 2527 } else { 2528 /* Need to insert a new range */ 2529 if (rec_blocks->used == COAP_RBLOCK_CNT -1) 2530 /* Too many losses */ 2531 return 0; 2532 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i], 2533 (rec_blocks->used - i) * sizeof(rec_blocks->range[0])); 2534 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num; 2535 rec_blocks->used++; 2536 } 2537 break; 2538 } 2539 if (block_num == rec_blocks->range[i].end + 1) { 2540 rec_blocks->range[i].end = block_num; 2541 if (i + 1 < rec_blocks->used) { 2542 if (rec_blocks->range[i+1].begin == block_num + 1) { 2543 /* Merge the 2 ranges */ 2544 rec_blocks->range[i].end = rec_blocks->range[i+1].end; 2545 if (i+2 < rec_blocks->used) { 2546 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i+2], 2547 (rec_blocks->used - (i+2)) * sizeof(rec_blocks->range[0])); 2548 } 2549 rec_blocks->used--; 2550 } 2551 } 2552 break; 2553 } 2554 } 2555 if (i == rec_blocks->used) { 2556 if (rec_blocks->used == COAP_RBLOCK_CNT -1) 2557 /* Too many losses */ 2558 return 0; 2559 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num; 2560 rec_blocks->used++; 2561 } 2562 coap_ticks(&rec_blocks->last_seen); 2563 return 1; 2564} 2565 2566#if COAP_SERVER_SUPPORT 2567/* 2568 * Need to check if this is a large PUT / POST using multiple blocks 2569 * 2570 * Server receiving PUT/POST etc. of a large amount of data (Block1) 2571 * 2572 * Return: 0 Call application handler 2573 * 1 Do not call application handler - just send the built response 2574 */ 2575int 2576coap_handle_request_put_block(coap_context_t *context, 2577 coap_session_t *session, 2578 coap_pdu_t *pdu, 2579 coap_pdu_t *response, 2580 coap_resource_t *resource, 2581 coap_string_t *uri_path, 2582 coap_opt_t *observe, 2583 int *added_block, 2584 coap_lg_srcv_t **pfree_lg_srcv) { 2585 size_t length = 0; 2586 const uint8_t *data = NULL; 2587 size_t offset = 0; 2588 size_t total = 0; 2589 coap_block_b_t block; 2590 coap_opt_iterator_t opt_iter; 2591 uint16_t block_option = 0; 2592 2593 *added_block = 0; 2594 *pfree_lg_srcv = NULL; 2595 coap_get_data_large(pdu, &length, &data, &offset, &total); 2596 pdu->body_offset = 0; 2597 pdu->body_total = length; 2598 2599 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) { 2600 block_option = COAP_OPTION_BLOCK1; 2601#if COAP_Q_BLOCK_SUPPORT 2602 if (coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter)) { 2603 /* Cannot handle Q-Block1 as well */ 2604 coap_add_data(response, sizeof("Block1 + Q-Block1 together")-1, 2605 (const uint8_t *)"Block1 + Q-Block1 together"); 2606 response->code = COAP_RESPONSE_CODE(402); 2607 goto skip_app_handler; 2608 } 2609#endif /* COAP_Q_BLOCK_SUPPORT */ 2610 } 2611#if COAP_Q_BLOCK_SUPPORT 2612 else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { 2613 block_option = COAP_OPTION_Q_BLOCK1; 2614 set_block_mode_has_q(session->block_mode); 2615 } 2616#endif /* COAP_Q_BLOCK_SUPPORT */ 2617 if (block_option) { 2618 coap_lg_srcv_t *p; 2619 coap_opt_t *size_opt = coap_check_option(pdu, 2620 COAP_OPTION_SIZE1, 2621 &opt_iter); 2622 coap_opt_t *fmt_opt = coap_check_option(pdu, 2623 COAP_OPTION_CONTENT_FORMAT, 2624 &opt_iter); 2625 uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt), 2626 coap_opt_length(fmt_opt)) : 2627 COAP_MEDIATYPE_TEXT_PLAIN; 2628 coap_opt_t *rtag_opt = coap_check_option(pdu, 2629 COAP_OPTION_RTAG, 2630 &opt_iter); 2631 size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0; 2632 const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL; 2633 2634 if (length > block.chunk_size) { 2635 coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n", 2636 block.chunk_size, length); 2637 length = block.chunk_size; 2638 } 2639 total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt), 2640 coap_opt_length(size_opt)) : 0; 2641 offset = block.num << (block.szx + 4); 2642 2643 LL_FOREACH(session->lg_srcv, p) { 2644 if (rtag_opt || p->rtag_set == 1) { 2645 if (!(rtag_opt && p->rtag_set == 1)) 2646 continue; 2647 if (p->rtag_length != rtag_length || 2648 memcmp(p->rtag, rtag, rtag_length) != 0) 2649 continue; 2650 } 2651 if (resource == p->resource) { 2652 break; 2653 } 2654 if ((p->resource == context->unknown_resource || 2655 resource == context->proxy_uri_resource) && 2656 coap_string_equal(uri_path, p->uri_path)) 2657 break; 2658 } 2659 if (!p && block.num != 0) { 2660 /* random access - no need to track */ 2661 pdu->body_data = data; 2662 pdu->body_length = length; 2663 pdu->body_offset = offset; 2664 pdu->body_total = length + offset + (block.m ? 1 : 0); 2665 } 2666 /* Do not do this if this is a single block */ 2667 else if (!p && !(offset == 0 && block.m == 0)) { 2668 p = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t)); 2669 if (p == NULL) { 2670 coap_add_data(response, sizeof("Memory issue")-1, 2671 (const uint8_t *)"Memory issue"); 2672 response->code = COAP_RESPONSE_CODE(500); 2673 goto skip_app_handler; 2674 } 2675 coap_log_debug("** %s: lg_srcv %p initialized\n", 2676 coap_session_str(session), (void *)p); 2677 memset(p, 0, sizeof(coap_lg_srcv_t)); 2678 coap_ticks(&p->last_used); 2679 p->resource = resource; 2680 if (resource == context->unknown_resource || 2681 resource == context->proxy_uri_resource) 2682 p->uri_path = coap_new_str_const(uri_path->s, uri_path->length); 2683 p->content_format = fmt; 2684 p->total_len = total; 2685 p->amount_so_far = length; 2686 p->szx = block.szx; 2687 p->block_option = block_option; 2688 if (observe) { 2689 p->observe_length = min(coap_opt_length(observe), 3); 2690 memcpy(p->observe, coap_opt_value(observe), p->observe_length); 2691 p->observe_set = 1; 2692 } 2693 if (rtag_opt) { 2694 p->rtag_length = coap_opt_length(rtag_opt); 2695 memcpy(p->rtag, coap_opt_value(rtag_opt), p->rtag_length); 2696 p->rtag_set = 1; 2697 } 2698 p->body_data = NULL; 2699 LL_PREPEND(session->lg_srcv, p); 2700 } 2701 if (p) { 2702 if (fmt != p->content_format) { 2703 coap_add_data(response, sizeof("Content-Format mismatch")-1, 2704 (const uint8_t *)"Content-Format mismatch"); 2705 response->code = COAP_RESPONSE_CODE(408); 2706 goto free_lg_srcv; 2707 } 2708#if COAP_Q_BLOCK_SUPPORT 2709 if (block_option == COAP_OPTION_Q_BLOCK1) { 2710 if (total != p->total_len) { 2711 coap_add_data(response, sizeof("Size1 mismatch")-1, 2712 (const uint8_t *)"Size1 mismatch"); 2713 response->code = COAP_RESPONSE_CODE(408); 2714 goto free_lg_srcv; 2715 } 2716 } 2717#endif /* COAP_Q_BLOCK_SUPPORT */ 2718 p->last_mid = pdu->mid; 2719 p->last_type = pdu->type; 2720#if COAP_Q_BLOCK_SUPPORT 2721 coap_delete_bin_const(p->last_token); 2722 p->last_token = coap_new_bin_const(pdu->actual_token.s, 2723 pdu->actual_token.length); 2724#endif /* COAP_Q_BLOCK_SUPPORT */ 2725 if (session->block_mode & 2726#if COAP_Q_BLOCK_SUPPORT 2727 (COAP_BLOCK_SINGLE_BODY|COAP_BLOCK_HAS_Q_BLOCK) || 2728#else /* ! COAP_Q_BLOCK_SUPPORT */ 2729 (COAP_BLOCK_SINGLE_BODY) || 2730#endif /* ! COAP_Q_BLOCK_SUPPORT */ 2731 block.bert) { 2732 size_t chunk = (size_t)1 << (block.szx + 4); 2733 int update_data = 0; 2734 unsigned int saved_num = block.num; 2735 size_t saved_offset = offset; 2736 2737 while (offset < saved_offset + length) { 2738 if (!check_if_received_block(&p->rec_blocks, block.num)) { 2739 /* Update list of blocks received */ 2740 if (!update_received_blocks(&p->rec_blocks, block.num)) { 2741 coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session); 2742 coap_add_data(response, sizeof("Too many missing blocks")-1, 2743 (const uint8_t *)"Too many missing blocks"); 2744 response->code = COAP_RESPONSE_CODE(408); 2745 goto free_lg_srcv; 2746 } 2747 update_data = 1; 2748 } 2749 block.num++; 2750 offset = block.num << (block.szx + 4); 2751 } 2752 block.num--; 2753 if (update_data) { 2754#if COAP_Q_BLOCK_SUPPORT 2755 p->rec_blocks.processing_payload_set = 2756 block.num / COAP_MAX_PAYLOADS(session); 2757#endif /* COAP_Q_BLOCK_SUPPORT */ 2758 /* Update saved data */ 2759 if (p->total_len < saved_offset + length) { 2760 p->total_len = saved_offset + length; 2761 } 2762 p->body_data = coap_block_build_body(p->body_data, length, data, 2763 saved_offset, p->total_len); 2764 if (!p->body_data) 2765 goto call_app_handler; 2766 2767 } 2768 if (block.m || 2769 !check_all_blocks_in(&p->rec_blocks, 2770 (uint32_t)(p->total_len + chunk -1)/chunk)) { 2771 /* Not all the payloads of the body have arrived */ 2772 if (block.m) { 2773 uint8_t buf[4]; 2774 2775#if COAP_Q_BLOCK_SUPPORT 2776 if (block_option == COAP_OPTION_Q_BLOCK1) { 2777 if (check_all_blocks_in(&p->rec_blocks, 2778 (uint32_t)(p->total_len + chunk -1)/chunk)) { 2779 goto give_app_data; 2780 } 2781 if (p->rec_blocks.used == 1 && 2782 (p->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1 2783 == COAP_MAX_PAYLOADS(session)) { 2784 /* Blocks could arrive in wrong order */ 2785 block.num = p->rec_blocks.range[0].end; 2786 } else { 2787 /* The remote end will be sending the next one unless this 2788 is a MAX_PAYLOADS and all previous have been received */ 2789 goto skip_app_handler; 2790 } 2791 if (COAP_PROTO_RELIABLE(session->proto) || 2792 pdu->type != COAP_MESSAGE_NON) 2793 goto skip_app_handler; 2794 } 2795#endif /* COAP_Q_BLOCK_SUPPORT */ 2796 /* Ask for the next block */ 2797 coap_insert_option(response, block_option, 2798 coap_encode_var_safe(buf, sizeof(buf), 2799 (saved_num << 4) | 2800 (block.m << 3) | 2801 block.aszx), 2802 buf); 2803 response->code = COAP_RESPONSE_CODE(231); 2804 goto skip_app_handler; 2805 } 2806 goto skip_app_handler; 2807 } 2808 2809 /* 2810 * Remove the Block1 option as passing all of the data to 2811 * application layer. Add back in observe option if appropriate. 2812 * Adjust all other information. 2813 */ 2814#if COAP_Q_BLOCK_SUPPORT 2815give_app_data: 2816#endif /* COAP_Q_BLOCK_SUPPORT */ 2817 if (p->observe_set) { 2818 coap_update_option(pdu, COAP_OPTION_OBSERVE, 2819 p->observe_length, p->observe); 2820 } 2821 coap_remove_option(pdu, block_option); 2822 pdu->body_data = p->body_data->s; 2823 pdu->body_length = p->total_len; 2824 pdu->body_offset = 0; 2825 pdu->body_total = p->total_len; 2826 coap_log_debug("Server app version of updated PDU\n"); 2827 coap_show_pdu(COAP_LOG_DEBUG, pdu); 2828 *pfree_lg_srcv = p; 2829 goto call_app_handler; 2830 } else { 2831 /* No need to update body_data and body_length as a single PDU */ 2832 pdu->body_offset = offset; 2833 /* Exact match if last block */ 2834 if (block.m) { 2835 uint8_t buf[4]; 2836 2837 if (total > offset + length + block.m) 2838 pdu->body_total = total; 2839 else 2840 pdu->body_total = offset + length + block.m; 2841 2842 coap_insert_option(response, block_option, 2843 coap_encode_var_safe(buf, sizeof(buf), 2844 (block.num << 4) | 2845 (block.m << 3) | 2846 block.aszx), 2847 buf); 2848 *added_block = 1; 2849 goto call_app_handler; 2850 } else { 2851 pdu->body_total = offset + length + block.m; 2852 } 2853 } 2854 2855 if (block.m == 0) { 2856 /* Last chunk - free off all */ 2857 coap_ticks(&p->last_used); 2858 } 2859 goto call_app_handler; 2860 2861free_lg_srcv: 2862 LL_DELETE(session->lg_srcv, p); 2863 coap_block_delete_lg_srcv(session, p); 2864 goto skip_app_handler; 2865 } 2866 } 2867call_app_handler: 2868 return 0; 2869 2870skip_app_handler: 2871 return 1; 2872} 2873#endif /* COAP_SERVER_SUPPORT */ 2874 2875#if COAP_CLIENT_SUPPORT 2876#if COAP_Q_BLOCK_SUPPORT 2877static uint32_t 2878derive_cbor_value(const uint8_t **bp, size_t rem_len) { 2879 uint32_t value = **bp & 0x1f; 2880 (*bp)++; 2881 if (value < 24) { 2882 return value; 2883 } else if (value == 24) { 2884 if (rem_len < 2) 2885 return (uint32_t)-1; 2886 value = **bp; 2887 (*bp)++; 2888 return value; 2889 } else if (value == 25) { 2890 if (rem_len < 3) 2891 return (uint32_t)-1; 2892 value = **bp << 8; 2893 (*bp)++; 2894 value |= **bp; 2895 (*bp)++; 2896 return value; 2897 } 2898 if (rem_len < 4) 2899 return (uint32_t)-1; 2900 value = **bp << 24; 2901 (*bp)++; 2902 value = **bp << 16; 2903 (*bp)++; 2904 value = **bp << 8; 2905 (*bp)++; 2906 value |= **bp; 2907 (*bp)++; 2908 return value; 2909} 2910#endif /* COAP_Q_BLOCK_SUPPORT */ 2911 2912static int 2913check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent, 2914 coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) { 2915 /* Check for Echo option for freshness */ 2916 coap_opt_iterator_t opt_iter; 2917 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter); 2918 2919 if (opt) { 2920 if (sent || lg_xmit || lg_crcv) { 2921 /* Need to retransmit original request with Echo option added */ 2922 coap_pdu_t *echo_pdu; 2923 coap_mid_t mid; 2924 const uint8_t *data; 2925 size_t data_len; 2926 int have_data = 0; 2927 uint8_t ltoken[8]; 2928 size_t ltoken_len; 2929 uint64_t token; 2930 2931 if (sent) { 2932 if (coap_get_data(sent, &data_len, &data)) 2933 have_data = 1; 2934 } else if (lg_xmit) { 2935 sent = &lg_xmit->pdu; 2936 if (lg_xmit->length) { 2937 size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4); 2938 size_t offset = (lg_xmit->last_block + 1) * blk_size; 2939 have_data = 1; 2940 data = &lg_xmit->data[offset]; 2941 data_len = (lg_xmit->length - offset) > blk_size ? blk_size : 2942 lg_xmit->length - offset; 2943 } 2944 } else { /* lg_crcv */ 2945 sent = &lg_crcv->pdu; 2946 if (coap_get_data(sent, &data_len, &data)) 2947 have_data = 1; 2948 } 2949 if (lg_xmit) { 2950 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, 2951 ++lg_xmit->b.b1.count); 2952 } else { 2953 token = STATE_TOKEN_FULL(lg_crcv->state_token, 2954 ++lg_crcv->retry_counter); 2955 } 2956 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); 2957 echo_pdu = coap_pdu_duplicate(sent, session, ltoken_len, ltoken, NULL); 2958 if (!echo_pdu) 2959 return 0; 2960 if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO, 2961 coap_opt_length(opt), coap_opt_value(opt))) 2962 goto not_sent; 2963 if (have_data) { 2964 coap_add_data(echo_pdu, data_len, data); 2965 } 2966 /* Need to track Observe token change if Observe */ 2967 track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token); 2968#if COAP_OSCORE_SUPPORT 2969 if (session->oscore_encryption && 2970 (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) && 2971 coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt) == 0)) { 2972 /* Need to update the base PDU's Token for closing down Observe */ 2973 if (lg_xmit) { 2974 lg_xmit->b.b1.state_token = token; 2975 } else { 2976 lg_crcv->state_token = token; 2977 } 2978 } 2979#endif /* COAP_OSCORE_SUPPORT */ 2980 mid = coap_send_internal(session, echo_pdu); 2981 if (mid == COAP_INVALID_MID) 2982 goto not_sent; 2983 return 1; 2984 } else { 2985 /* Need to save Echo option value to add to next reansmission */ 2986not_sent: 2987 coap_delete_bin_const(session->echo); 2988 session->echo = coap_new_bin_const(coap_opt_value(opt), 2989 coap_opt_length(opt)); 2990 } 2991 } 2992 return 0; 2993} 2994 2995static void 2996track_echo(coap_session_t *session, coap_pdu_t *rcvd) { 2997 coap_opt_iterator_t opt_iter; 2998 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter); 2999 3000 if (opt) { 3001 coap_delete_bin_const(session->echo); 3002 session->echo = coap_new_bin_const(coap_opt_value(opt), 3003 coap_opt_length(opt)); 3004 } 3005} 3006 3007/* 3008 * Need to see if this is a response to a large body request transfer. If so, 3009 * need to initiate the request containing the next block and not trouble the 3010 * application. Note that Token must unique per request/response. 3011 * 3012 * Client receives large data acknowledgement from server (Block1) 3013 * 3014 * This is set up using coap_add_data_large_request() 3015 * 3016 * Client is using GET etc. 3017 * 3018 * Return: 0 Call application handler 3019 * 1 Do not call application handler - just send the built response 3020 */ 3021int 3022coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, 3023 coap_pdu_t *rcvd) { 3024 coap_lg_xmit_t *p; 3025 coap_lg_xmit_t *q; 3026 uint64_t token_match = 3027 STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->actual_token.s, 3028 rcvd->actual_token.length)); 3029 coap_lg_crcv_t *lg_crcv = NULL; 3030 3031 LL_FOREACH_SAFE(session->lg_xmit, p, q) { 3032 if (!COAP_PDU_IS_REQUEST(&p->pdu) || 3033 (token_match != STATE_TOKEN_BASE(p->b.b1.state_token) && 3034 token_match != 3035 STATE_TOKEN_BASE(coap_decode_var_bytes8(p->b.b1.app_token->s, 3036 p->b.b1.app_token->length)))) { 3037 /* try out the next one */ 3038 continue; 3039 } 3040 /* lg_xmit found */ 3041 size_t chunk = (size_t)1 << (p->blk_size + 4); 3042 coap_block_b_t block; 3043 3044 if (COAP_RESPONSE_CLASS(rcvd->code) == 2 && 3045 coap_get_block_b(session, rcvd, p->option, &block)) { 3046 3047 if (block.bert) { 3048 coap_log_debug("found Block option, block is BERT, block nr. %u (%zu)\n", 3049 block.num, p->b.b1.bert_size); 3050 } else { 3051 coap_log_debug("found Block option, block size is %u, block nr. %u\n", 3052 1 << (block.szx + 4), block.num); 3053 } 3054 if (block.szx != p->blk_size) { 3055 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) { 3056 /* 3057 * Recompute the block number of the previous packet given the 3058 * new block size 3059 */ 3060 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1); 3061 p->blk_size = block.szx; 3062 chunk = (size_t)1 << (p->blk_size + 4); 3063 p->offset = block.num * chunk; 3064 coap_log_debug("new Block size is %u, block number %u completed\n", 3065 1 << (block.szx + 4), block.num); 3066 block.bert = 0; 3067 block.aszx = block.szx; 3068 } else { 3069 coap_log_debug("ignoring request to increase Block size, " 3070 "next block is not aligned on requested block size boundary. " 3071 "(%zu x %u mod %u = %zu != 0)\n", 3072 p->offset/chunk + 1, (1 << (p->blk_size + 4)), 3073 (1 << (block.szx + 4)), 3074 (p->offset + chunk) % ((size_t)1 << (block.szx + 4))); 3075 } 3076 } 3077 track_echo(session, rcvd); 3078 if (p->last_block == (int)block.num && 3079 p->option != COAP_OPTION_Q_BLOCK1) { 3080 /* 3081 * Duplicate Block1 ACK 3082 * 3083 * RFCs not clear here, but on a lossy connection, there could 3084 * be multiple Block1 ACKs, causing the client to retransmit the 3085 * same block multiple times, or the server retransmitting the 3086 * same ACK. 3087 * 3088 * Once a block has been ACKd, there is no need to retransmit it. 3089 */ 3090 return 1; 3091 } 3092 if (block.bert) 3093 block.num += (unsigned int)(p->b.b1.bert_size / 1024 - 1); 3094 p->last_block = block.num; 3095 p->offset = (block.num + 1) * chunk; 3096 if (p->offset < p->length) { 3097 /* Build the next PDU request based off the skeletal PDU */ 3098 uint8_t buf[8]; 3099 coap_pdu_t *pdu; 3100 uint64_t token = STATE_TOKEN_FULL(p->b.b1.state_token, ++p->b.b1.count); 3101 size_t len = coap_encode_var_safe8(buf, sizeof(token), token); 3102 3103 if (p->pdu.code == COAP_REQUEST_CODE_FETCH) { 3104 /* Need to handle Observe for large FETCH */ 3105 LL_FOREACH(session->lg_crcv, lg_crcv) { 3106 if (coap_binary_equal(p->b.b1.app_token, lg_crcv->app_token)) { 3107 coap_bin_const_t *new_token; 3108 coap_bin_const_t ctoken = { len, buf }; 3109 3110 /* Need to save/restore Observe Token for large FETCH */ 3111 new_token = track_fetch_observe(&p->pdu, lg_crcv, block.num + 1, 3112 &ctoken); 3113 if (new_token) { 3114 assert(len <= sizeof(buf)); 3115 len = new_token->length; 3116 memcpy(buf, new_token->s, len); 3117 } 3118 break; 3119 } 3120 } 3121 } 3122 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL); 3123 if (!pdu) 3124 goto fail_body; 3125 3126 block.num++; 3127 if (block.bert) { 3128 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : 3129 pdu->used_size; 3130 block.m = (p->length - p->offset) > 3131 ((pdu->max_size - token_options) /1024) * 1024; 3132 } else { 3133 block.m = (p->offset + chunk) < p->length; 3134 } 3135 coap_update_option(pdu, p->option, 3136 coap_encode_var_safe(buf, sizeof(buf), 3137 (block.num << 4) | 3138 (block.m << 3) | 3139 block.aszx), 3140 buf); 3141 3142 if (!coap_add_block_b_data(pdu, 3143 p->length, 3144 p->data, 3145 &block)) 3146 goto fail_body; 3147 p->b.b1.bert_size = block.chunk_size; 3148 coap_ticks(&p->last_sent); 3149#if COAP_Q_BLOCK_SUPPORT 3150 if (p->option == COAP_OPTION_Q_BLOCK1 && 3151 pdu->type == COAP_MESSAGE_NON) { 3152 if (coap_send_q_block1(session, block, pdu, 3153 COAP_SEND_INC_PDU) == COAP_INVALID_MID) 3154 goto fail_body; 3155 return 1; 3156 } else if (coap_send_internal(session, pdu) == COAP_INVALID_MID) 3157 goto fail_body; 3158#else /* ! COAP_Q_BLOCK_SUPPORT */ 3159 if (coap_send_internal(session, pdu) == COAP_INVALID_MID) 3160 goto fail_body; 3161#endif /* ! COAP_Q_BLOCK_SUPPORT */ 3162 return 1; 3163 } 3164 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { 3165 if (check_freshness(session, rcvd, sent, p, NULL)) 3166 return 1; 3167#if COAP_Q_BLOCK_SUPPORT 3168 } else if (rcvd->code == COAP_RESPONSE_CODE(402)) { 3169 /* Q-Block1 or Q-Block2 not present in p - duplicate error ? */ 3170 if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) || 3171 coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK1, &block)) 3172 return 1; 3173 } else if (rcvd->code == COAP_RESPONSE_CODE(408) && 3174 p->option == COAP_OPTION_Q_BLOCK1) { 3175 size_t length; 3176 const uint8_t *data; 3177 coap_opt_iterator_t opt_iter; 3178 coap_opt_t *fmt_opt = coap_check_option(rcvd, 3179 COAP_OPTION_CONTENT_FORMAT, 3180 &opt_iter); 3181 uint16_t fmt = fmt_opt ? 3182 coap_decode_var_bytes(coap_opt_value(fmt_opt), 3183 coap_opt_length(fmt_opt)) : 3184 COAP_MEDIATYPE_TEXT_PLAIN; 3185 3186 if (fmt != COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ) 3187 goto fail_body; 3188 3189 if (COAP_PROTO_RELIABLE(session->proto) || 3190 rcvd->type != COAP_MESSAGE_NON) { 3191 coap_log_debug("Unexpected 4.08 - protocol violation - ignore\n"); 3192 return 1; 3193 } 3194 3195 if (coap_get_data(rcvd, &length, &data)) { 3196 /* Need to decode CBOR to work out what blocks to re-send */ 3197 const uint8_t *bp = data; 3198 uint32_t i; 3199 uint8_t buf[8]; 3200 coap_pdu_t *pdu; 3201 uint64_t token = coap_decode_var_bytes8(rcvd->actual_token.s, 3202 rcvd->actual_token.length); 3203 uint8_t ltoken[8]; 3204 size_t ltoken_length; 3205 3206 for (i = 0; (bp < data + length) && 3207 i < COAP_MAX_PAYLOADS(session); i++) { 3208 if ((*bp & 0xc0) != 0x00) /* uint(value) */ 3209 goto fail_cbor; 3210 block.num = derive_cbor_value(&bp, data + length - bp); 3211 coap_log_debug("Q-Block1: Missing block %d\n", block.num); 3212 if (block.num > (1 << 20) -1) 3213 goto fail_cbor; 3214 block.m = (block.num + 1) * chunk < p->length; 3215 block.szx = p->blk_size; 3216 3217 /* Build the next PDU request based off the skeletal PDU */ 3218 token = STATE_TOKEN_FULL(p->b.b1.state_token,++p->b.b1.count); 3219 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token); 3220 pdu = coap_pdu_duplicate(&p->pdu, session, ltoken_length, 3221 ltoken, NULL); 3222 if (!pdu) 3223 goto fail_body; 3224 3225 coap_update_option(pdu, p->option, 3226 coap_encode_var_safe(buf, sizeof(buf), 3227 (block.num << 4) | 3228 (block.m << 3) | 3229 block.szx), 3230 buf); 3231 3232 if (!coap_add_block(pdu, 3233 p->length, 3234 p->data, 3235 block.num, 3236 block.szx)) 3237 goto fail_body; 3238 if (coap_send_internal(session, pdu) == COAP_INVALID_MID) 3239 goto fail_body; 3240 } 3241 return 1; 3242 } 3243fail_cbor: 3244 coap_log_info("Invalid application/missing-blocks+cbor-seq\n"); 3245#endif /* COAP_Q_BLOCK_SUPPORT */ 3246 } 3247 goto lg_xmit_finished; 3248 } /* end of LL_FOREACH_SAFE */ 3249 return 0; 3250 3251fail_body: 3252 coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); 3253 /* There has been an internal error of some sort */ 3254 rcvd->code = COAP_RESPONSE_CODE(500); 3255lg_xmit_finished: 3256 if (session->lg_crcv) { 3257 LL_FOREACH(session->lg_crcv, lg_crcv) { 3258 if (STATE_TOKEN_BASE(p->b.b1.state_token) == 3259 STATE_TOKEN_BASE(lg_crcv->state_token)) { 3260 /* In case of observe */ 3261 lg_crcv->state_token = p->b.b1.state_token; 3262 break; 3263 } 3264 } 3265 } 3266 if (!lg_crcv) { 3267 /* need to put back original token into rcvd */ 3268 if (p->b.b1.app_token) 3269 coap_update_token(rcvd, p->b.b1.app_token->length, 3270 p->b.b1.app_token->s); 3271 coap_log_debug("Client app version of updated PDU\n"); 3272 coap_show_pdu(COAP_LOG_DEBUG, rcvd); 3273 } 3274 3275 LL_DELETE(session->lg_xmit, p); 3276 coap_block_delete_lg_xmit(session, p); 3277 return 0; 3278} 3279#endif /* COAP_CLIENT_SUPPORT */ 3280 3281/* 3282 * Re-assemble payloads into a body 3283 */ 3284coap_binary_t * 3285coap_block_build_body(coap_binary_t *body_data, size_t length, 3286 const uint8_t *data, size_t offset, size_t total) { 3287 if (data == NULL) 3288 return NULL; 3289 if (body_data == NULL && total) { 3290 body_data = coap_new_binary(total); 3291 } 3292 if (body_data == NULL) 3293 return NULL; 3294 3295 /* Update saved data */ 3296 if (offset + length <= total && body_data->length >= total) { 3297 memcpy(&body_data->s[offset], data, length); 3298 } else { 3299 /* 3300 * total may be inaccurate as per 3301 * https://rfc-editor.org/rfc/rfc7959#section-4 3302 * o In a request carrying a Block1 Option, to indicate the current 3303 * estimate the client has of the total size of the resource 3304 * representation, measured in bytes ("size indication"). 3305 * o In a response carrying a Block2 Option, to indicate the current 3306 * estimate the server has of the total size of the resource 3307 * representation, measured in bytes ("size indication"). 3308 */ 3309 coap_binary_t *new = coap_resize_binary(body_data, offset + length); 3310 3311 if (new) { 3312 body_data = new; 3313 memcpy(&body_data->s[offset], data, length); 3314 } else { 3315 coap_delete_binary(body_data); 3316 return NULL; 3317 } 3318 } 3319 return body_data; 3320} 3321 3322#if COAP_CLIENT_SUPPORT 3323/* 3324 * Need to see if this is a large body response to a request. If so, 3325 * need to initiate the request for the next block and not trouble the 3326 * application. Note that Token must be unique per request/response. 3327 * 3328 * This is set up using coap_send() 3329 * Client receives large data from server ((Q-)Block2) 3330 * 3331 * Return: 0 Call application handler 3332 * 1 Do not call application handler - just sent the next request 3333 */ 3334int 3335coap_handle_response_get_block(coap_context_t *context, 3336 coap_session_t *session, 3337 coap_pdu_t *sent, 3338 coap_pdu_t *rcvd, 3339 coap_recurse_t recursive) { 3340 coap_lg_crcv_t *p; 3341 coap_block_b_t block; 3342#if COAP_Q_BLOCK_SUPPORT 3343 coap_block_b_t qblock; 3344#endif /* COAP_Q_BLOCK_SUPPORT */ 3345 int have_block = 0; 3346 uint16_t block_opt = 0; 3347 size_t offset; 3348 int ack_rst_sent = 0; 3349 uint64_t token_match = 3350 STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->actual_token.s, 3351 rcvd->actual_token.length)); 3352 3353 memset(&block, 0, sizeof(block)); 3354#if COAP_Q_BLOCK_SUPPORT 3355 memset(&qblock, 0, sizeof(qblock)); 3356#endif /* COAP_Q_BLOCK_SUPPORT */ 3357 LL_FOREACH(session->lg_crcv, p) { 3358 size_t chunk = 0; 3359 uint8_t buf[8]; 3360 coap_opt_iterator_t opt_iter; 3361 3362 if (token_match != STATE_TOKEN_BASE(p->state_token) && 3363 !coap_binary_equal(&rcvd->actual_token, p->app_token)) { 3364 /* try out the next one */ 3365 continue; 3366 } 3367 3368 /* lg_crcv found */ 3369 3370 if (COAP_RESPONSE_CLASS(rcvd->code) == 2) { 3371 size_t length; 3372 const uint8_t *data; 3373 coap_opt_t *size_opt = coap_check_option(rcvd, COAP_OPTION_SIZE2, 3374 &opt_iter); 3375 size_t size2 = size_opt ? 3376 coap_decode_var_bytes(coap_opt_value(size_opt), 3377 coap_opt_length(size_opt)) : 0; 3378 3379 /* length and data are cleared on error */ 3380 (void)coap_get_data(rcvd, &length, &data); 3381 rcvd->body_offset = 0; 3382 rcvd->body_total = length; 3383 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) { 3384 have_block = 1; 3385 block_opt = COAP_OPTION_BLOCK2; 3386 } 3387#if COAP_Q_BLOCK_SUPPORT 3388 if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) { 3389 if (have_block) { 3390 coap_log_warn("Both Block1 and Q-Block1 not supported in a response\n"); 3391 } 3392 have_block = 1; 3393 block_opt = COAP_OPTION_Q_BLOCK2; 3394 block = qblock; 3395 /* server indicating that it supports Q_BLOCK */ 3396 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) { 3397 set_block_mode_has_q(session->block_mode); 3398 } 3399 } 3400#endif /* COAP_Q_BLOCK_SUPPORT */ 3401 track_echo(session, rcvd); 3402 if (have_block && (block.m || length)) { 3403 coap_opt_t *fmt_opt = coap_check_option(rcvd, 3404 COAP_OPTION_CONTENT_FORMAT, 3405 &opt_iter); 3406 uint16_t fmt = fmt_opt ? 3407 coap_decode_var_bytes(coap_opt_value(fmt_opt), 3408 coap_opt_length(fmt_opt)) : 3409 COAP_MEDIATYPE_TEXT_PLAIN; 3410 coap_opt_t *etag_opt = coap_check_option(rcvd, 3411 COAP_OPTION_ETAG, 3412 &opt_iter); 3413 size_t saved_offset; 3414 int updated_block; 3415 3416 if (length > block.chunk_size) { 3417 coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n", 3418 block.chunk_size, length); 3419 length = block.chunk_size; 3420 } 3421 /* Possibility that Size2 not sent, or is too small */ 3422 chunk = (size_t)1 << (block.szx + 4); 3423 offset = block.num * chunk; 3424 if (size2 < (offset + length)) { 3425 if (block.m) 3426 size2 = offset + length + 1; 3427 else 3428 size2 = offset + length; 3429 } 3430 saved_offset = offset; 3431 3432 if (p->initial) { 3433#if COAP_Q_BLOCK_SUPPORT 3434reinit: 3435#endif /* COAP_Q_BLOCK_SUPPORT */ 3436 p->initial = 0; 3437 if (p->body_data) { 3438 coap_free_type(COAP_STRING, p->body_data); 3439 p->body_data = NULL; 3440 } 3441 if (etag_opt) { 3442 p->etag_length = coap_opt_length(etag_opt); 3443 memcpy(p->etag, coap_opt_value(etag_opt), p->etag_length); 3444 p->etag_set = 1; 3445 } else { 3446 p->etag_set = 0; 3447 } 3448 p->total_len = size2; 3449 p->content_format = fmt; 3450 p->szx = block.szx; 3451 p->block_option = block_opt; 3452 p->last_type = rcvd->type; 3453 p->rec_blocks.used = 0; 3454#if COAP_Q_BLOCK_SUPPORT 3455 p->rec_blocks.processing_payload_set = 0; 3456#endif /* COAP_Q_BLOCK_SUPPORT */ 3457 } 3458 if (p->total_len < size2) 3459 p->total_len = size2; 3460 3461 if (etag_opt) { 3462 if (!full_match(coap_opt_value(etag_opt), 3463 coap_opt_length(etag_opt), 3464 p->etag, p->etag_length)) { 3465 /* body of data has changed - need to restart request */ 3466 coap_pdu_t *pdu; 3467 uint64_t token = STATE_TOKEN_FULL(p->state_token, 3468 ++p->retry_counter); 3469 size_t len = coap_encode_var_safe8(buf, sizeof(token), token); 3470 coap_opt_filter_t drop_options; 3471 3472#if COAP_Q_BLOCK_SUPPORT 3473 if (block_opt == COAP_OPTION_Q_BLOCK2) 3474 goto reinit; 3475#endif /* COAP_Q_BLOCK_SUPPORT */ 3476 3477 coap_log_warn("Data body updated during receipt - new request started\n"); 3478 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY)) 3479 coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session); 3480 3481 p->initial = 1; 3482 coap_free_type(COAP_STRING, p->body_data); 3483 p->body_data = NULL; 3484 3485 coap_session_new_token(session, &len, buf); 3486 memset(&drop_options, 0, sizeof(coap_opt_filter_t)); 3487 coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); 3488 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, &drop_options); 3489 if (!pdu) 3490 goto fail_resp; 3491 3492 coap_update_option(pdu, block_opt, 3493 coap_encode_var_safe(buf, sizeof(buf), 3494 (0 << 4) | (0 << 3) | block.aszx), 3495 buf); 3496 3497 if (coap_send_internal(session, pdu) == COAP_INVALID_MID) 3498 goto fail_resp; 3499 3500 goto skip_app_handler; 3501 } 3502 } else if (p->etag_set) { 3503 /* Cannot handle this change in ETag to not being there */ 3504 coap_log_warn("Not all blocks have ETag option\n"); 3505 goto fail_resp; 3506 } 3507 3508 if (fmt != p->content_format) { 3509 coap_log_warn("Content-Format option mismatch\n"); 3510 goto fail_resp; 3511 } 3512#if COAP_Q_BLOCK_SUPPORT 3513 if (block_opt == COAP_OPTION_Q_BLOCK2 && size2 != p->total_len) { 3514 coap_log_warn("Size2 option mismatch\n"); 3515 goto fail_resp; 3516 } 3517#endif /* COAP_Q_BLOCK_SUPPORT */ 3518 if (block.num == 0) { 3519 coap_opt_t *obs_opt = coap_check_option(rcvd, 3520 COAP_OPTION_OBSERVE, 3521 &opt_iter); 3522 if (obs_opt) { 3523 p->observe_length = min(coap_opt_length(obs_opt), 3); 3524 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length); 3525 p->observe_set = 1; 3526 } else { 3527 p->observe_set = 0; 3528 } 3529 } 3530 updated_block = 0; 3531 while (offset < saved_offset + length) { 3532 if (!check_if_received_block(&p->rec_blocks, block.num)) { 3533#if COAP_Q_BLOCK_SUPPORT 3534 uint32_t this_payload_set = block.num / COAP_MAX_PAYLOADS(session); 3535#endif /* COAP_Q_BLOCK_SUPPORT */ 3536 3537 coap_log_debug("found Block option, block size is %u, block nr. %u\n", 3538 1 << (block.szx + 4), block.num); 3539#if COAP_Q_BLOCK_SUPPORT 3540 if (block_opt == COAP_OPTION_Q_BLOCK2 && p->rec_blocks.used && 3541 this_payload_set > p->rec_blocks.processing_payload_set && 3542 this_payload_set != p->rec_blocks.latest_payload_set) { 3543 coap_request_missing_q_block2(session, p); 3544 } 3545 p->rec_blocks.latest_payload_set = this_payload_set; 3546#endif /* COAP_Q_BLOCK_SUPPORT */ 3547 /* Update list of blocks received */ 3548 if (!update_received_blocks(&p->rec_blocks, block.num)) { 3549 coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session); 3550 goto fail_resp; 3551 } 3552 updated_block = 1; 3553 } 3554 block.num++; 3555 offset = block.num << (block.szx + 4); 3556 if (!block.bert && block_opt != COAP_OPTION_Q_BLOCK2) 3557 break; 3558 } 3559 block.num--; 3560 /* Only process if not duplicate block */ 3561 if (updated_block) { 3562 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) { 3563 if (size2 < saved_offset + length) { 3564 size2 = saved_offset + length; 3565 } 3566 p->body_data = coap_block_build_body(p->body_data, length, data, 3567 saved_offset, size2); 3568 if (p->body_data == NULL) { 3569 goto fail_resp; 3570 } 3571 } 3572 if (block.m || !check_all_blocks_in(&p->rec_blocks, 3573 (size2 + chunk -1) / chunk)) { 3574 /* Not all the payloads of the body have arrived */ 3575 size_t len; 3576 coap_pdu_t *pdu; 3577 uint64_t token; 3578 3579 if (block.m) { 3580#if COAP_Q_BLOCK_SUPPORT 3581 if (block_opt == COAP_OPTION_Q_BLOCK2) { 3582 /* Blocks could arrive in wrong order */ 3583 if (check_all_blocks_in(&p->rec_blocks, 3584 (size2 + chunk -1) / chunk)) { 3585 goto give_to_app; 3586 } 3587 if (check_all_blocks_in_for_payload_set(session, 3588 &p->rec_blocks)) { 3589 block.num = p->rec_blocks.range[0].end; 3590 /* Now requesting next payload */ 3591 p->rec_blocks.processing_payload_set = 3592 block.num / COAP_MAX_PAYLOADS(session) + 1; 3593 if (check_any_blocks_next_payload_set(session, 3594 &p->rec_blocks)) { 3595 /* Need to ask for them individually */ 3596 coap_request_missing_q_block2(session, p); 3597 goto skip_app_handler; 3598 } 3599 } else { 3600 /* The remote end will be sending the next one unless this 3601 is a MAX_PAYLOADS and all previous have been received */ 3602 goto skip_app_handler; 3603 } 3604 if (COAP_PROTO_RELIABLE(session->proto) || 3605 rcvd->type != COAP_MESSAGE_NON) 3606 goto skip_app_handler; 3607 3608 } else 3609#endif /* COAP_Q_BLOCK_SUPPORT */ 3610 block.m = 0; 3611 3612 /* Ask for the next block */ 3613 token = STATE_TOKEN_FULL(p->state_token, ++p->retry_counter); 3614 len = coap_encode_var_safe8(buf, sizeof(token), token); 3615 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL); 3616 if (!pdu) 3617 goto fail_resp; 3618 3619 if (rcvd->type == COAP_MESSAGE_NON) 3620 pdu->type = COAP_MESSAGE_NON; /* Server is using NON */ 3621 3622 /* Only sent with the first block */ 3623 coap_remove_option(pdu, COAP_OPTION_OBSERVE); 3624 3625 coap_update_option(pdu, block_opt, 3626 coap_encode_var_safe(buf, sizeof(buf), 3627 ((block.num + 1) << 4) | 3628 (block.m << 3) | block.aszx), 3629 buf); 3630 3631 if (coap_send_internal(session, pdu) == COAP_INVALID_MID) 3632 goto fail_resp; 3633 } 3634 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) 3635 goto skip_app_handler; 3636 3637 /* need to put back original token into rcvd */ 3638 coap_update_token(rcvd, p->app_token->length, p->app_token->s); 3639 rcvd->body_offset = saved_offset; 3640#if COAP_Q_BLOCK_SUPPORT 3641 rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ? 3642 p->total_len : size2; 3643#else /* ! COAP_Q_BLOCK_SUPPORT */ 3644 rcvd->body_total = size2; 3645#endif /* ! COAP_Q_BLOCK_SUPPORT */ 3646 coap_log_debug("Client app version of updated PDU\n"); 3647 coap_show_pdu(COAP_LOG_DEBUG, rcvd); 3648 goto call_app_handler; 3649 } 3650#if COAP_Q_BLOCK_SUPPORT 3651give_to_app: 3652#endif /* COAP_Q_BLOCK_SUPPORT */ 3653 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) { 3654 /* Pretend that there is no block */ 3655 coap_remove_option(rcvd, block_opt); 3656 if (p->observe_set) { 3657 coap_update_option(rcvd, COAP_OPTION_OBSERVE, 3658 p->observe_length, p->observe); 3659 } 3660 rcvd->body_data = p->body_data->s; 3661#if COAP_Q_BLOCK_SUPPORT 3662 rcvd->body_length = block_opt == COAP_OPTION_Q_BLOCK2 ? 3663 p->total_len : saved_offset + length; 3664#else /* ! COAP_Q_BLOCK_SUPPORT */ 3665 rcvd->body_length = saved_offset + length; 3666#endif /* ! COAP_Q_BLOCK_SUPPORT */ 3667 rcvd->body_offset = 0; 3668 rcvd->body_total = rcvd->body_length; 3669 } else { 3670 rcvd->body_offset = saved_offset; 3671#if COAP_Q_BLOCK_SUPPORT 3672 rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ? 3673 p->total_len : size2; 3674#else /* ! COAP_Q_BLOCK_SUPPORT */ 3675 rcvd->body_total = size2; 3676#endif /* ! COAP_Q_BLOCK_SUPPORT */ 3677 } 3678 if (context->response_handler) { 3679 /* need to put back original token into rcvd */ 3680 if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { 3681 coap_update_token(rcvd, p->app_token->length, p->app_token->s); 3682 coap_log_debug("Client app version of updated PDU\n"); 3683 coap_show_pdu(COAP_LOG_DEBUG, rcvd); 3684 } 3685 if (context->response_handler(session, sent, rcvd, 3686 rcvd->mid) == COAP_RESPONSE_FAIL) 3687 coap_send_rst(session, rcvd); 3688 else 3689 coap_send_ack(session, rcvd); 3690 } else { 3691 coap_send_ack(session, rcvd); 3692 } 3693 ack_rst_sent = 1; 3694 if (p->observe_set == 0) { 3695 /* Expire this entry */ 3696 LL_DELETE(session->lg_crcv, p); 3697 coap_block_delete_lg_crcv(session, p); 3698 goto skip_app_handler; 3699 } 3700 /* Set up for the next data body as observing */ 3701 p->initial = 1; 3702 if (p->body_data) { 3703 coap_free_type(COAP_STRING, p->body_data); 3704 p->body_data = NULL; 3705 } 3706 } 3707 coap_ticks(&p->last_used); 3708 goto skip_app_handler; 3709 } else { 3710 coap_opt_t *obs_opt = coap_check_option(rcvd, 3711 COAP_OPTION_OBSERVE, 3712 &opt_iter); 3713 if (obs_opt) { 3714 p->observe_length = min(coap_opt_length(obs_opt), 3); 3715 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length); 3716 p->observe_set = 1; 3717 } else { 3718 p->observe_set = 0; 3719 if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { 3720 /* need to put back original token into rcvd */ 3721 coap_update_token(rcvd, p->app_token->length, p->app_token->s); 3722 coap_log_debug("PDU presented to app.\n"); 3723 coap_show_pdu(COAP_LOG_DEBUG, rcvd); 3724 } 3725 /* Expire this entry */ 3726 goto expire_lg_crcv; 3727 } 3728 } 3729 coap_ticks(&p->last_used); 3730 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { 3731#if COAP_OSCORE_SUPPORT 3732 if (check_freshness(session, rcvd, 3733 (session->oscore_encryption == 0) ? sent : NULL, 3734 NULL, p)) 3735#else /* !COAP_OSCORE_SUPPORT */ 3736 if (check_freshness(session, rcvd, sent, NULL, p)) 3737#endif /* !COAP_OSCORE_SUPPORT */ 3738 goto skip_app_handler; 3739 goto expire_lg_crcv; 3740 } else { 3741 /* Not 2.xx or 4.01 - assume it is a failure of some sort */ 3742 goto expire_lg_crcv; 3743 } 3744 if (!block.m && !p->observe_set) { 3745fail_resp: 3746 /* lg_crcv no longer required - cache it for 1 sec */ 3747 coap_ticks(&p->last_used); 3748 p->last_used = p->last_used - COAP_MAX_TRANSMIT_WAIT_TICKS(session) + 3749 COAP_TICKS_PER_SECOND; 3750 } 3751 /* need to put back original token into rcvd */ 3752 if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { 3753 coap_update_token(rcvd, p->app_token->length, p->app_token->s); 3754 coap_log_debug("Client app version of updated PDU (3)\n"); 3755 coap_show_pdu(COAP_LOG_DEBUG, rcvd); 3756 } 3757 break; 3758 } /* LL_FOREACH() */ 3759 3760 /* Check if receiving a block response and if blocks can be set up */ 3761 if (recursive == COAP_RECURSE_OK && !p) { 3762 if (!sent) { 3763 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block) 3764#if COAP_Q_BLOCK_SUPPORT 3765 || 3766 coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) 3767#endif /* COAP_Q_BLOCK_SUPPORT */ 3768 ) { 3769 coap_log_debug("** %s: large body receive internal issue\n", 3770 coap_session_str(session)); 3771 goto skip_app_handler; 3772 } 3773 } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) { 3774 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) { 3775#if COAP_Q_BLOCK_SUPPORT 3776 if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) { 3777 set_block_mode_drop_q(session->block_mode); 3778 coap_log_debug("Q-Block support disabled\n"); 3779 } 3780#endif /* COAP_Q_BLOCK_SUPPORT */ 3781 have_block = 1; 3782 block_opt = COAP_OPTION_BLOCK2; 3783 if (block.num != 0) { 3784 /* Assume random access and just give the single response to app */ 3785 size_t length; 3786 const uint8_t *data; 3787 size_t chunk = (size_t)1 << (block.szx + 4); 3788 3789 coap_get_data(rcvd, &length, &data); 3790 rcvd->body_offset = block.num*chunk; 3791 rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0); 3792 goto call_app_handler; 3793 } 3794 } 3795#if COAP_Q_BLOCK_SUPPORT 3796 else if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)) { 3797 have_block = 1; 3798 block_opt = COAP_OPTION_Q_BLOCK2; 3799 /* server indicating that it supports Q_BLOCK2 */ 3800 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) { 3801 set_block_mode_has_q(session->block_mode); 3802 } 3803 } 3804#endif /* COAP_Q_BLOCK_SUPPORT */ 3805 if (have_block) { 3806 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent, NULL); 3807 3808 if (lg_crcv) { 3809 LL_PREPEND(session->lg_crcv, lg_crcv); 3810 return coap_handle_response_get_block(context, session, sent, rcvd, 3811 COAP_RECURSE_NO); 3812 } 3813 } 3814 track_echo(session, rcvd); 3815 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { 3816 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent, NULL); 3817 3818 if (lg_crcv) { 3819 LL_PREPEND(session->lg_crcv, lg_crcv); 3820 return coap_handle_response_get_block(context, session, sent, rcvd, 3821 COAP_RECURSE_NO); 3822 } 3823 } 3824 } 3825 return 0; 3826 3827expire_lg_crcv: 3828 /* need to put back original token into rcvd */ 3829 if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) { 3830 coap_update_token(rcvd, p->app_token->length, p->app_token->s); 3831 coap_log_debug("Client app version of updated PDU\n"); 3832 coap_show_pdu(COAP_LOG_DEBUG, rcvd); 3833 } 3834 /* Expire this entry */ 3835 LL_DELETE(session->lg_crcv, p); 3836 coap_block_delete_lg_crcv(session, p); 3837 3838call_app_handler: 3839 return 0; 3840 3841skip_app_handler: 3842 if (!ack_rst_sent) 3843 coap_send_ack(session, rcvd); 3844 return 1; 3845} 3846#endif /* COAP_CLIENT_SUPPORT */ 3847 3848#if COAP_SERVER_SUPPORT 3849/* Check if lg_xmit generated and update PDU code if so */ 3850void 3851coap_check_code_lg_xmit(const coap_session_t *session, 3852 const coap_pdu_t *request, 3853 coap_pdu_t *response, const coap_resource_t *resource, 3854 const coap_string_t *query) { 3855 coap_lg_xmit_t *lg_xmit; 3856 3857 if (response->code == 0) 3858 return; 3859 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query); 3860 if (lg_xmit && lg_xmit->pdu.code == 0) { 3861 lg_xmit->pdu.code = response->code; 3862 return; 3863 } 3864} 3865#endif /* COAP_SERVER_SUPPORT */ 3866 3867#if COAP_CLIENT_SUPPORT 3868void 3869coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu) { 3870 uint64_t token_match = 3871 STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s, 3872 pdu->actual_token.length)); 3873 coap_lg_xmit_t *lg_xmit; 3874 coap_lg_crcv_t *lg_crcv; 3875 3876 if (session->lg_crcv) { 3877 LL_FOREACH(session->lg_crcv, lg_crcv) { 3878 if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) 3879 return; 3880 if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) { 3881 coap_update_token(pdu, lg_crcv->app_token->length, 3882 lg_crcv->app_token->s); 3883 coap_log_debug("Client app version of updated PDU\n"); 3884 coap_show_pdu(COAP_LOG_DEBUG, pdu); 3885 return; 3886 } 3887 } 3888 } 3889 if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) { 3890 LL_FOREACH(session->lg_xmit, lg_xmit) { 3891 if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) 3892 return; 3893 if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) { 3894 coap_update_token(pdu, lg_xmit->b.b1.app_token->length, 3895 lg_xmit->b.b1.app_token->s); 3896 coap_log_debug("Client app version of updated PDU\n"); 3897 coap_show_pdu(COAP_LOG_DEBUG, pdu); 3898 return; 3899 } 3900 } 3901 } 3902} 3903#endif /* ! COAP_CLIENT_SUPPORT */ 3904