libcoap 4.3.5-develop-8c1e09a
Loading...
Searching...
No Matches
coap_block.c
Go to the documentation of this file.
1/* coap_block.c -- block transfer
2 *
3 * Copyright (C) 2010--2012,2015-2025 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
17
18#ifndef min
19#define min(a,b) ((a) < (b) ? (a) : (b))
20#endif
21
22/* Can be 1 - 8 bytes long */
23#ifndef COAP_ETAG_MAX_BYTES
24#define COAP_ETAG_MAX_BYTES 4
25#endif
26#if COAP_ETAG_MAX_BYTES < 1 || COAP_ETAG_MAX_BYTES > 8
27#error COAP_ETAG_MAX_BYTES byte size invalid
28#endif
29
30#if COAP_Q_BLOCK_SUPPORT
31int
33 return 1;
34}
35#else /* ! COAP_Q_BLOCK_SUPPORT */
36int
38 return 0;
39}
40#endif /* ! COAP_Q_BLOCK_SUPPORT */
41
42unsigned int
43coap_opt_block_num(const coap_opt_t *block_opt) {
44 unsigned int num = 0;
45 uint16_t len;
46
47 len = coap_opt_length(block_opt);
48
49 if (len == 0) {
50 return 0;
51 }
52
53 if (len > 1) {
55 coap_opt_length(block_opt) - 1);
56 }
57
58 return (num << 4) | ((COAP_OPT_BLOCK_END_BYTE(block_opt) & 0xF0) >> 4);
59}
60
61int
62coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu,
63 coap_option_num_t number, coap_block_b_t *block) {
64 coap_opt_iterator_t opt_iter;
65 coap_opt_t *option;
66
67 assert(block);
68 memset(block, 0, sizeof(coap_block_b_t));
69
70 if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) {
71 uint32_t num;
72
73 if (COAP_OPT_BLOCK_MORE(option))
74 block->m = 1;
75 block->aszx = block->szx = COAP_OPT_BLOCK_SZX(option);
76 if (block->szx == 7) {
77 size_t length;
78 const uint8_t *data;
79
80 if (session == NULL || COAP_PROTO_NOT_RELIABLE(session->proto) ||
81 !(session->csm_bert_rem_support && session->csm_bert_loc_support))
82 /* No BERT support */
83 return 0;
84
85 block->szx = 6; /* BERT is 1024 block chunks */
86 block->bert = 1;
87 if (coap_get_data(pdu, &length, &data)) {
88 if (block->m && (length % 1024) != 0) {
89 coap_log_debug("block: Oversized packet - reduced to %zu from %zu\n",
90 length - (length % 1024), length);
91 length -= length % 1024;
92 }
93 block->chunk_size = (uint32_t)length;
94 } else
95 block->chunk_size = 0;
96 } else {
97 block->chunk_size = (size_t)1 << (block->szx + 4);
98 }
99 block->defined = 1;
100
101 /* The block number is at most 20 bits, so values above 2^20 - 1
102 * are illegal. */
103 num = coap_opt_block_num(option);
104 if (num > 0xFFFFF) {
105 return 0;
106 }
107 block->num = num;
108 return 1;
109 }
110
111 return 0;
112}
113
114int
116 coap_block_t *block) {
117 coap_block_b_t block_b;
118
119 assert(block);
120 memset(block, 0, sizeof(coap_block_t));
121
122 if (coap_get_block_b(NULL, pdu, number, &block_b)) {
123 block->num = block_b.num;
124 block->m = block_b.m;
125 block->szx = block_b.szx;
126 return 1;
127 }
128 return 0;
129}
130
131static int
133 unsigned int num,
134 unsigned int blk_size, size_t total) {
135 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
136 size_t avail = pdu->max_size - token_options;
137 unsigned int start = num << (blk_size + 4);
138 unsigned int can_use_bert = block->defined == 0 || block->bert;
139
140 assert(start <= total);
141 memset(block, 0, sizeof(*block));
142 block->num = num;
143 block->szx = block->aszx = blk_size;
144 if (can_use_bert && blk_size == 6 && avail >= 1024 && session != NULL &&
145 COAP_PROTO_RELIABLE(session->proto) &&
146 session->csm_bert_rem_support && session->csm_bert_loc_support) {
147 block->bert = 1;
148 block->aszx = 7;
149 block->chunk_size = (uint32_t)((avail / 1024) * 1024);
150 } else {
151 block->chunk_size = (size_t)1 << (blk_size + 4);
152 if (avail < block->chunk_size && (total - start) >= avail) {
153 /* Need to reduce block size */
154 unsigned int szx;
155 int new_blk_size;
156
157 if (avail < 16) { /* bad luck, this is the smallest block size */
158 coap_log_debug("not enough space, even the smallest block does not fit (1)\n");
159 return 0;
160 }
161 new_blk_size = coap_flsll((long long)avail) - 5;
162 coap_log_debug("decrease block size for %zu to %d\n", avail, new_blk_size);
163 szx = block->szx;
164 block->szx = new_blk_size;
165 block->num <<= szx - block->szx;
166 block->chunk_size = (size_t)1 << (new_blk_size + 4);
167 }
168 }
169 block->m = block->chunk_size < total - start;
170 return 1;
171}
172
173int
175 coap_pdu_t *pdu, size_t data_length) {
176 size_t start;
177 unsigned char buf[4];
178 coap_block_b_t block_b;
179
180 assert(pdu);
181
182 start = block->num << (block->szx + 4);
183 if (block->num != 0 && data_length <= start) {
184 coap_log_debug("illegal block requested\n");
185 return -2;
186 }
187
188 assert(pdu->max_size > 0);
189
190 block_b.defined = 1;
191 block_b.bert = 0;
192 if (!setup_block_b(NULL, pdu, &block_b, block->num,
193 block->szx, data_length))
194 return -3;
195
196 /* to re-encode the block option */
197 coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
198 ((block_b.num << 4) |
199 (block_b.m << 3) |
200 block_b.szx)),
201 buf);
202
203 return 1;
204}
205
206int
208 coap_option_num_t number,
209 coap_pdu_t *pdu, size_t data_length) {
210 size_t start;
211 unsigned char buf[4];
212
213 assert(pdu);
214
215 start = block->num << (block->szx + 4);
216 if (block->num != 0 && data_length <= start) {
217 coap_log_debug("illegal block requested\n");
218 return -2;
219 }
220
221 assert(pdu->max_size > 0);
222
223 if (!setup_block_b(session, pdu, block, block->num,
224 block->szx, data_length))
225 return -3;
226
227 /* to re-encode the block option */
228 coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
229 ((block->num << 4) |
230 (block->m << 3) |
231 block->aszx)),
232 buf);
233
234 return 1;
235}
236
237int
238coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data,
239 unsigned int block_num, unsigned char block_szx) {
240 unsigned int start;
241 start = block_num << (block_szx + 4);
242
243 if (len <= start)
244 return 0;
245
246 return coap_add_data(pdu,
247 min(len - start, ((size_t)1 << (block_szx + 4))),
248 data + start);
249}
250
251int
252coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data,
253 coap_block_b_t *block) {
254 unsigned int start = block->num << (block->szx + 4);
255 size_t max_size;
256
257 if (len <= start)
258 return 0;
259
260 if (block->bert) {
261 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
262 max_size = ((pdu->max_size - token_options) / 1024) * 1024;
263 } else {
264 max_size = (size_t)1 << (block->szx + 4);
265 }
266 block->chunk_size = (uint32_t)max_size;
267
268 return coap_add_data(pdu,
269 min(len - start, max_size),
270 data + start);
271}
272
273/*
274 * Note that the COAP_OPTION_ have to be added in the correct order
275 */
276void
278 coap_pdu_t *response,
279 uint16_t media_type,
280 int maxage,
281 size_t length,
282 const uint8_t *data
283 ) {
284 unsigned char buf[4];
285 coap_block_t block2;
286 int block2_requested = 0;
287#if COAP_SERVER_SUPPORT
288 uint64_t etag = 0;
289 coap_digest_t digest;
290 coap_digest_ctx_t *dctx = NULL;
291#endif /* COAP_SERVER_SUPPORT */
292
293 memset(&block2, 0, sizeof(block2));
294 /*
295 * Need to check that a valid block is getting asked for so that the
296 * correct options are put into the PDU.
297 */
298 if (request) {
299 if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
300 block2_requested = 1;
301 if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) {
302 coap_log_debug("Illegal block requested (%d > last = %zu)\n",
303 block2.num,
304 length >> (block2.szx + 4));
305 response->code = COAP_RESPONSE_CODE(400);
306 goto error;
307 }
308 }
309 }
310 response->code = COAP_RESPONSE_CODE(205);
311
312#if COAP_SERVER_SUPPORT
313 /* add ETag for the resource data */
314 if (length) {
315 dctx = coap_digest_setup();
316 if (!dctx)
317 goto error;
318 if (request && request->session &&
319 coap_is_mcast(&request->session->addr_info.local)) {
321 }
322 if (!coap_digest_update(dctx, data, length))
323 goto error;
324 if (!coap_digest_final(dctx, &digest))
325 goto error;
326 dctx = NULL;
327 memcpy(&etag, digest.key, sizeof(etag));
328#if COAP_ETAG_MAX_BYTES != 8
329 etag = etag >> 8*(8 - COAP_ETAG_MAX_BYTES);
330#endif
331 if (!etag)
332 etag = 1;
333 coap_update_option(response,
335 coap_encode_var_safe8(buf, sizeof(buf), etag),
336 buf);
337 }
338#endif /* COAP_SERVER_SUPPORT */
339
341 coap_encode_var_safe(buf, sizeof(buf),
342 media_type),
343 buf);
344
345 if (maxage >= 0) {
346 coap_insert_option(response,
348 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
349 }
350
351 if (block2_requested) {
352 int res;
353
354 res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
355
356 switch (res) {
357 case -2: /* illegal block (caught above) */
358 response->code = COAP_RESPONSE_CODE(400);
359 goto error;
360 case -1: /* should really not happen */
361 assert(0);
362 /* fall through if assert is a no-op */
363 case -3: /* cannot handle request */
364 response->code = COAP_RESPONSE_CODE(500);
365 goto error;
366 default: /* everything is good */
367 ;
368 }
369
372 coap_encode_var_safe8(buf, sizeof(buf), length),
373 buf);
374
375 coap_add_block(response, length, data,
376 block2.num, block2.szx);
377 return;
378 }
379
380 /*
381 * Block2 not requested
382 */
383 if (!coap_add_data(response, length, data)) {
384 /*
385 * Insufficient space to add in data - use block mode
386 * set initial block size, will be lowered by
387 * coap_write_block_opt() automatically
388 */
389 block2.num = 0;
390 block2.szx = 6;
391 coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
392
395 coap_encode_var_safe8(buf, sizeof(buf), length),
396 buf);
397
398 coap_add_block(response, length, data,
399 block2.num, block2.szx);
400 }
401 return;
402
403error:
404#if COAP_SERVER_SUPPORT
405 coap_digest_free(dctx);
406#endif /* COAP_SERVER_SUPPORT */
407 coap_add_data(response,
408 strlen(coap_response_phrase(response->code)),
409 (const unsigned char *)coap_response_phrase(response->code));
410}
411
412COAP_API void
414 uint32_t block_mode) {
415 coap_lock_lock(return);
416 coap_context_set_block_mode_lkd(context, block_mode);
418}
419
420void
422 uint32_t block_mode) {
424 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP))
425 block_mode = 0;
426 context->block_mode &= ~COAP_BLOCK_SET_MASK;
427 context->block_mode |= block_mode & COAP_BLOCK_SET_MASK;
428#if ! COAP_Q_BLOCK_SUPPORT
430 coap_log_debug("Q-Block support not compiled in - ignored\n");
431#endif /* ! COAP_Q_BLOCK_SUPPORT */
432}
433
434COAP_API int
436 size_t max_block_size) {
437 int ret;
438
439 coap_lock_lock(return 0);
440 ret = coap_context_set_max_block_size_lkd(context, max_block_size);
442 return ret;
443}
444
445int
446coap_context_set_max_block_size_lkd(coap_context_t *context, size_t max_block_size) {
447 switch (max_block_size) {
448 case 0:
449 case 16:
450 case 32:
451 case 64:
452 case 128:
453 case 256:
454 case 512:
455 case 1024:
456 break;
457 default:
458 coap_log_info("coap_context_set_max_block_size: Invalid max block size (%zu)\n",
459 max_block_size);
460 return 0;
461 }
463 max_block_size = (coap_fls((uint32_t)max_block_size >> 4) - 1) & 0x07;
464 context->block_mode &= ~COAP_BLOCK_MAX_SIZE_MASK;
465 context->block_mode |= COAP_BLOCK_MAX_SIZE_SET((uint32_t)max_block_size);
466 return 1;
467}
468
470full_match(const uint8_t *a, size_t alen,
471 const uint8_t *b, size_t blen) {
472 return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
473}
474
477 coap_lg_xmit_t *lg_xmit = NULL;
478 coap_lg_xmit_t *m_lg_xmit = NULL;
479 uint64_t token_match =
481 pdu->actual_token.length));
482
483 LL_FOREACH(session->lg_xmit, lg_xmit) {
484 if (token_match != STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) &&
485 !coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) {
486 /* try out the next one */
487 continue;
488 }
489#if COAP_CLIENT_SUPPORT
490 if (COAP_PDU_IS_RESPONSE(pdu)) {
491 if (coap_is_mcast(&lg_xmit->b.b1.upstream)) {
492 m_lg_xmit = lg_xmit;
493 }
494 if (!coap_address_equals(&lg_xmit->b.b1.upstream, &session->addr_info.remote)) {
495 /* try out the next one */
496 continue;
497 }
498 }
499 /* Have a match */
500 return lg_xmit;
501#endif /* COAP_CLIENT_SUPPORT */
502 }
503 if (m_lg_xmit && (session->sock.flags & COAP_SOCKET_MULTICAST)) {
504 /* Need to set up unicast version of mcast lg_xmit entry */
505 lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
506 if (!lg_xmit)
507 return NULL;
508 memcpy(lg_xmit, m_lg_xmit, sizeof(coap_lg_xmit_t));
509 lg_xmit->next = NULL;
510 lg_xmit->b.b1.app_token = NULL;
511 lg_xmit->data_info->ref++;
512 lg_xmit->sent_pdu = coap_pdu_reference_lkd(m_lg_xmit->sent_pdu);
513 coap_address_copy(&lg_xmit->b.b1.upstream, &session->addr_info.remote);
514 lg_xmit->b.b1.app_token = coap_new_binary(m_lg_xmit->b.b1.app_token->length);
515 if (!lg_xmit->b.b1.app_token)
516 goto fail;
517 LL_PREPEND(session->lg_xmit, lg_xmit);
518 coap_log_debug("** %s: lg_xmit %p mcast slave initialized\n",
519 coap_session_str(session), (void *)lg_xmit);
520 /* Allow the mcast lg_xmit to time out earlier */
521 coap_ticks(&m_lg_xmit->last_all_sent);
522#if COAP_CLIENT_SUPPORT
523 if (COAP_PDU_IS_RESPONSE(pdu)) {
524 coap_lg_crcv_t *lg_crcv;
525
526 lg_crcv = coap_find_lg_crcv(session, pdu);
527 if (lg_crcv) {
528 lg_xmit->b.b1.state_token = lg_crcv->state_token;
529 }
530 }
531#endif /* COAP_CLIENT_SUPPORT */
532 }
533 return lg_xmit;
534
535fail:
536 coap_block_delete_lg_xmit(session, lg_xmit);
537 return NULL;
538}
539
540#if COAP_CLIENT_SUPPORT
541
542COAP_API int
544 coap_pdu_type_t type) {
545 int ret;
546
547 coap_lock_lock(return 0);
548 ret = coap_cancel_observe_lkd(session, token, type);
550 return ret;
551}
552
553int
555 coap_pdu_type_t type) {
556 coap_lg_crcv_t *lg_crcv, *q;
557
558 assert(session);
559 if (!session)
560 return 0;
561
563 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
564 coap_log_debug("** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n",
565 coap_session_str(session));
566 return 0;
567 }
568
569 LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) {
570 if (lg_crcv->observe_set) {
571 if ((!token && !lg_crcv->app_token->length) || (token &&
572 coap_binary_equal(token, lg_crcv->app_token))) {
573 uint8_t buf[8];
574 coap_mid_t mid;
575 size_t size;
576 const uint8_t *data;
577#if COAP_Q_BLOCK_SUPPORT
578 coap_block_b_t block;
579 int using_q_block1 = coap_get_block_b(session, lg_crcv->sent_pdu,
580 COAP_OPTION_Q_BLOCK1, &block);
581#endif /* COAP_Q_BLOCK_SUPPORT */
582 coap_bin_const_t *otoken = lg_crcv->obs_token ?
583 lg_crcv->obs_token[0] ?
584 lg_crcv->obs_token[0] :
585 (coap_bin_const_t *)lg_crcv->app_token :
586 (coap_bin_const_t *)lg_crcv->app_token;
588 session,
589 otoken->length,
590 otoken->s,
591 NULL);
592
593 lg_crcv->observe_set = 0;
594 if (pdu == NULL)
595 return 0;
596 /* Need to make sure that this is the correct requested type */
597 pdu->type = type;
598
600 coap_encode_var_safe(buf, sizeof(buf),
602 buf);
603 if (lg_crcv->o_block_option) {
605 coap_encode_var_safe(buf, sizeof(buf),
606 lg_crcv->o_blk_size),
607 buf);
608 }
609 if (lg_crcv->obs_data) {
611 lg_crcv->obs_data->length,
612 lg_crcv->obs_data->data, NULL, NULL);
613 } else if (coap_get_data(lg_crcv->sent_pdu, &size, &data)) {
614 coap_add_data_large_request_lkd(session, pdu, size, data, NULL, NULL);
615 }
616
617 /*
618 * Need to fix lg_xmit stateless token as using tokens from
619 * observe setup
620 */
621 if (pdu->lg_xmit)
622 pdu->lg_xmit->b.b1.state_token = lg_crcv->state_token;
623
624 coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream);
625#if COAP_Q_BLOCK_SUPPORT
626 /* See if large xmit using Q-Block1 (but not testing Q-Block1) */
627 if (using_q_block1) {
628 mid = coap_send_q_block1(session, block, pdu, COAP_SEND_INC_PDU);
629 } else {
630 mid = coap_send_internal(session, pdu, NULL);
631 }
632#else /* ! COAP_Q_BLOCK_SUPPORT */
633 mid = coap_send_internal(session, pdu, NULL);
634#endif /* ! COAP_Q_BLOCK_SUPPORT */
635 if (mid == COAP_INVALID_MID)
636 break;
637 }
638 }
639 }
640 return 1;
641}
642
645 coap_lg_crcv_t *lg_crcv;
646 coap_lg_crcv_t *m_lg_crcv = NULL;
647 uint64_t token_match =
649 pdu->actual_token.length));
650
651 LL_FOREACH(session->lg_crcv, lg_crcv) {
652 if (token_match != STATE_TOKEN_BASE(lg_crcv->state_token) &&
653 !coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) {
654 /* try out the next one */
655 continue;
656 }
657 if (coap_is_mcast(&lg_crcv->upstream)) {
658 m_lg_crcv = lg_crcv;
659 }
660 if (!coap_address_equals(&lg_crcv->upstream, &session->addr_info.remote)) {
661 /* try out the next one */
662 continue;
663 }
664 /* Have a match */
665 return lg_crcv;
666 }
667 if (m_lg_crcv && (session->sock.flags & COAP_SOCKET_MULTICAST)) {
668 /* Need to set up unicast version of mcast lg_crcv entry */
669 lg_crcv = coap_block_new_lg_crcv(session, m_lg_crcv->sent_pdu, NULL);
670 if (lg_crcv) {
671 if (m_lg_crcv->obs_data) {
672 m_lg_crcv->obs_data->ref++;
673 lg_crcv->obs_data = m_lg_crcv->obs_data;
674 }
675 LL_PREPEND(session->lg_crcv, lg_crcv);
676 }
677 }
678 return lg_crcv;
679}
680
681#if COAP_OSCORE_SUPPORT
684 coap_pdu_t *pdu,
685 coap_opt_t *echo) {
686 coap_lg_crcv_t *lg_crcv;
687 uint8_t ltoken[8];
688 size_t ltoken_len;
689 uint64_t token;
690 const uint8_t *data;
691 size_t data_len;
692 coap_pdu_t *resend_pdu;
693 coap_block_b_t block;
694
695 lg_crcv = coap_find_lg_crcv(session, pdu);
696 if (lg_crcv) {
697 /* Re-send request with new token */
698 token = STATE_TOKEN_FULL(lg_crcv->state_token,
699 ++lg_crcv->retry_counter);
700 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
701 /* There could be a Block option in pdu */
702 resend_pdu = coap_pdu_duplicate_lkd(pdu, session, ltoken_len,
703 ltoken, NULL);
704 if (!resend_pdu)
705 goto error;
706 if (echo) {
708 coap_opt_value(echo));
709 }
710 if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) {
711 if (coap_get_block_b(session, resend_pdu, COAP_OPTION_BLOCK1, &block)) {
712 if (data_len > block.chunk_size && block.chunk_size != 0) {
713 data_len = block.chunk_size;
714 }
715 }
716 coap_add_data(resend_pdu, data_len, data);
717 }
718
719 return coap_send_internal(session, resend_pdu, NULL);
720 }
721error:
722 return COAP_INVALID_MID;
723}
724#endif /* COAP_OSCORE_SUPPORT */
725#endif /* COAP_CLIENT_SUPPORT */
726
727#if COAP_SERVER_SUPPORT
728/*
729 * Find the response lg_xmit
730 */
733 const coap_pdu_t *request,
734 const coap_resource_t *resource,
735 const coap_string_t *query) {
736 coap_lg_xmit_t *lg_xmit;
737 coap_opt_iterator_t opt_iter;
738 coap_opt_t *rtag_opt = coap_check_option(request,
740 &opt_iter);
741 size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
742 const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
743
744 LL_FOREACH(session->lg_xmit, lg_xmit) {
745 static coap_string_t empty = { 0, NULL};
746
747 if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) ||
748 resource != lg_xmit->b.b2.resource ||
749 request->code != lg_xmit->b.b2.request_method ||
750 !coap_string_equal(query ? query : &empty,
751 lg_xmit->b.b2.query ?
752 lg_xmit->b.b2.query : &empty)) {
753 /* try out the next one */
754 continue;
755 }
756 /* lg_xmit is a response */
757 if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) {
758 if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1))
759 continue;
760 if (lg_xmit->b.b2.rtag_length != rtag_length ||
761 memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0)
762 continue;
763 }
764 return lg_xmit;
765 }
766 return NULL;
767}
768#endif /* COAP_SERVER_SUPPORT */
769
770static int
772 const coap_pdu_t *request,
773 coap_pdu_t *pdu,
774 coap_resource_t *resource,
775 const coap_string_t *query,
776 int maxage,
777 uint64_t etag,
778 size_t length,
779 const uint8_t *data,
780 coap_release_large_data_t release_func,
781 coap_get_large_data_t get_func,
782 void *app_ptr,
783 int single_request, coap_pdu_code_t request_method) {
784
785 ssize_t avail;
786 coap_block_b_t block;
787#if COAP_Q_BLOCK_SUPPORT
788 coap_block_b_t alt_block;
789#endif /* COAP_Q_BLOCK_SUPPORT */
790 size_t chunk;
791 coap_lg_xmit_t *lg_xmit = NULL;
792 uint8_t buf[8];
793 int have_block_defined = 0;
794 uint8_t blk_size;
795 uint8_t max_blk_size;
796 uint16_t option;
797 size_t token_options;
798 coap_opt_t *opt;
799 coap_opt_iterator_t opt_iter;
800#if COAP_Q_BLOCK_SUPPORT
801 uint16_t alt_option;
802#endif /* COAP_Q_BLOCK_SUPPORT */
803
804#if !COAP_SERVER_SUPPORT
805 (void)etag;
806#endif /* COAP_SERVER_SUPPORT */
807
808 assert(pdu);
809 if (pdu->data) {
810 coap_log_warn("coap_add_data_large: PDU already contains data\n");
811 if (release_func) {
812 coap_lock_callback(release_func(session, app_ptr));
813 }
814 return 0;
815 }
816
817 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
818 coap_log_debug("** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n",
819 coap_session_str(session));
820 goto add_data;
821 }
822
823 /* A lot of the reliable code assumes type is CON */
824 if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON)
825 pdu->type = COAP_MESSAGE_CON;
826
827 /* Block NUM max 20 bits and block size is "2**(SZX + 4)"
828 and using SZX max of 6 gives maximum size = 1,073,740,800
829 CSM Max-Message-Size theoretical maximum = 4,294,967,295
830 So, if using blocks, we are limited to 1,073,740,800.
831 */
832#define MAX_BLK_LEN (((1UL << 20) - 1) * (1 << (6 + 4)))
833
834#if UINT_MAX > MAX_BLK_LEN
835 if (length > MAX_BLK_LEN) {
836 coap_log_warn("Size of large buffer restricted to 0x%lx bytes\n", MAX_BLK_LEN);
837 length = MAX_BLK_LEN;
838 }
839#endif /* UINT_MAX > MAX_BLK_LEN */
840
841#if COAP_SERVER_SUPPORT
842 /* Possible response code not yet set, so check if not request */
843 if (!COAP_PDU_IS_REQUEST(pdu) && length) {
844 coap_opt_t *etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
845
846 if (etag_opt) {
847 /* Have to use ETag as supplied in the response PDU */
848 etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
849 coap_opt_length(etag_opt));
850 } else {
851 if (!etag) {
852 /* calculate ETag for the response */
853 coap_digest_t digest;
855
856 if (dctx) {
857 if (coap_is_mcast(&session->addr_info.local)) {
859 }
860 if (coap_digest_update(dctx, data, length)) {
861 if (coap_digest_final(dctx, &digest)) {
862 memcpy(&etag, digest.key, sizeof(etag));
863#if COAP_ETAG_MAX_BYTES != 8
864 etag = etag >> 8*(8 - COAP_ETAG_MAX_BYTES);
865#endif
866 dctx = NULL;
867 }
868 }
869 coap_digest_free(dctx);
870 }
871 if (!etag)
872 etag = 1;
873 }
876 coap_encode_var_safe8(buf, sizeof(buf), etag),
877 buf);
878 }
879 if (request) {
880 etag_opt = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter);
881 if (etag_opt) {
882 /* There may be multiple ETag - need to check each one */
883 coap_option_iterator_init(request, &opt_iter, COAP_OPT_ALL);
884 while ((etag_opt = coap_option_next(&opt_iter))) {
885 if (opt_iter.number == COAP_OPTION_ETAG) {
886 uint64_t etag_r = coap_decode_var_bytes8(coap_opt_value(etag_opt),
887 coap_opt_length(etag_opt));
888
889 if (etag == etag_r) {
890 pdu->code = COAP_RESPONSE_CODE(203);
891 return 1;
892 }
893 }
894 }
895 }
896 }
897 }
898#endif /* COAP_SERVER_SUPPORT */
899
900 /* Determine the block size to use, adding in sensible options if needed */
901 if (COAP_PDU_IS_REQUEST(pdu)) {
903
904#if COAP_Q_BLOCK_SUPPORT
905 if (session->block_mode & (COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_TRY_Q_BLOCK)) {
906 option = COAP_OPTION_Q_BLOCK1;
907 alt_option = COAP_OPTION_BLOCK1;
908 } else {
909 option = COAP_OPTION_BLOCK1;
910 alt_option = COAP_OPTION_Q_BLOCK1;
911 }
912#else /* ! COAP_Q_BLOCK_SUPPORT */
913 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
915 }
916 option = COAP_OPTION_BLOCK1;
917#endif /* ! COAP_Q_BLOCK_SUPPORT */
918
919 /* See if this token is already in use for large bodies (unlikely) */
920 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
921 if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) {
922 /* Unfortunately need to free this off as potential size change */
923 int is_mcast = 0;
924#if COAP_CLIENT_SUPPORT
925 is_mcast = coap_is_mcast(&lg_xmit->b.b1.upstream);
926#endif /* COAP_CLIENT_SUPPORT */
927 LL_DELETE(session->lg_xmit, lg_xmit);
928 coap_block_delete_lg_xmit(session, lg_xmit);
929 lg_xmit = NULL;
930 if (!is_mcast)
932 break;
933 }
934 }
935 } else {
936 /* Have to assume that it is a response even if code is 0.00 */
937 assert(resource);
938#if COAP_Q_BLOCK_SUPPORT
939 if (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) {
940 option = COAP_OPTION_Q_BLOCK2;
941 alt_option = COAP_OPTION_BLOCK2;
942 } else {
943 option = COAP_OPTION_BLOCK2;
944 alt_option = COAP_OPTION_Q_BLOCK2;
945 }
946#else /* ! COAP_Q_BLOCK_SUPPORT */
947 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
949 }
950 option = COAP_OPTION_BLOCK2;
951#endif /* ! COAP_Q_BLOCK_SUPPORT */
952#if COAP_SERVER_SUPPORT
953 /*
954 * Check if resource+query+rtag is already in use for large bodies
955 * (unlikely)
956 */
957 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
958 if (lg_xmit) {
959 /* Unfortunately need to free this off as potential size change */
960 LL_DELETE(session->lg_xmit, lg_xmit);
961 coap_block_delete_lg_xmit(session, lg_xmit);
962 lg_xmit = NULL;
964 }
965#endif /* COAP_SERVER_SUPPORT */
966 }
967#if COAP_OSCORE_SUPPORT
968 if (session->oscore_encryption) {
969 /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */
971 goto fail;
972 }
973#endif /* COAP_OSCORE_SUPPORT */
974
975 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
976 avail = pdu->max_size - token_options;
977 /* There may be a response with Echo option */
979#if COAP_OSCORE_SUPPORT
980 avail -= coap_oscore_overhead(session, pdu);
981#endif /* COAP_OSCORE_SUPPORT */
982 /* May need token of length 8, so account for this */
983 avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0;
984 blk_size = coap_flsll((long long)avail) - 4 - 1;
985 if (blk_size > 6)
986 blk_size = 6;
987
988 max_blk_size = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
989 if (max_blk_size && blk_size > max_blk_size)
990 blk_size = max_blk_size;
991
992 /* see if BlockX defined - if so update blk_size as given by app */
993 if (coap_get_block_b(session, pdu, option, &block)) {
994 if (block.szx < blk_size)
995 blk_size = block.szx;
996 have_block_defined = 1;
997 }
998#if COAP_Q_BLOCK_SUPPORT
999 /* see if alternate BlockX defined */
1000 if (coap_get_block_b(session, pdu, alt_option, &alt_block)) {
1001 if (have_block_defined) {
1002 /* Cannot have both options set */
1003 coap_log_warn("Both BlockX and Q-BlockX cannot be set at the same time\n");
1004 coap_remove_option(pdu, alt_option);
1005 } else {
1006 block = alt_block;
1007 if (block.szx < blk_size)
1008 blk_size = block.szx;
1009 have_block_defined = 1;
1010 option = alt_option;
1011 }
1012 }
1013#endif /* COAP_Q_BLOCK_SUPPORT */
1014
1015 if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) {
1016 /* bad luck, this is the smallest block size */
1017 coap_log_debug("not enough space, even the smallest block does not fit (2)\n");
1018 goto fail;
1019 }
1020
1021 chunk = (size_t)1 << (blk_size + 4);
1022 if ((have_block_defined && block.num != 0) || single_request ||
1023 ((session->block_mode & COAP_BLOCK_STLESS_BLOCK2) && session->type != COAP_SESSION_TYPE_CLIENT)) {
1024 /* App is defining a single block to send or we are stateless */
1025 size_t rem;
1026
1027 if (length >= block.num * chunk) {
1028#if COAP_SERVER_SUPPORT
1029 if (session->block_mode & COAP_BLOCK_STLESS_BLOCK2 && session->type != COAP_SESSION_TYPE_CLIENT) {
1030 /* We are running server stateless */
1033 coap_encode_var_safe(buf, sizeof(buf),
1034 (unsigned int)length),
1035 buf);
1036 if (request) {
1037 if (!coap_get_block_b(session, request, option, &block))
1038 block.num = 0;
1039 }
1040 if (!setup_block_b(session, pdu, &block, block.num,
1041 blk_size, length))
1042 goto fail;
1043
1044 /* Add in with requested block num, more bit and block size */
1046 option,
1047 coap_encode_var_safe(buf, sizeof(buf),
1048 (block.num << 4) | (block.m << 3) | block.aszx),
1049 buf);
1050 }
1051#endif /* COAP_SERVER_SUPPORT */
1052 rem = chunk;
1053 if (chunk > length - block.num * chunk)
1054 rem = length - block.num * chunk;
1055 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
1056 goto fail;
1057 }
1058 if (release_func) {
1059 coap_lock_callback(release_func(session, app_ptr));
1060 }
1061 } else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) {
1062 /* Only add in lg_xmit if more than one block needs to be handled */
1063 size_t rem;
1064
1065 lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
1066 if (!lg_xmit)
1067 goto fail;
1068
1069 /* Set up for displaying all the data in the pdu */
1070 if (!get_func) {
1071 pdu->body_data = data;
1072 pdu->body_length = length;
1073 coap_log_debug("PDU presented by app.\n");
1075 pdu->body_data = NULL;
1076 pdu->body_length = 0;
1077 } else {
1078 coap_log_debug("PDU presented by app without data.\n");
1080 }
1081
1082 coap_log_debug("** %s: lg_xmit %p initialized\n",
1083 coap_session_str(session), (void *)lg_xmit);
1084 /* Update lg_xmit with large data information */
1085 memset(lg_xmit, 0, sizeof(coap_lg_xmit_t));
1086 lg_xmit->blk_size = blk_size;
1087 lg_xmit->option = option;
1088 lg_xmit->data_info = coap_malloc_type(COAP_STRING, sizeof(coap_lg_xmit_data_t));
1089 if (!lg_xmit->data_info)
1090 goto fail;
1091 lg_xmit->data_info->ref = 0;
1092 lg_xmit->data_info->data = data;
1093 lg_xmit->data_info->length = length;
1094#if COAP_Q_BLOCK_SUPPORT
1095 lg_xmit->non_timeout_random_ticks =
1097#endif /* COAP_Q_BLOCK_SUPPORT */
1098 lg_xmit->data_info->get_func = get_func;
1099 lg_xmit->data_info->release_func = release_func;
1100 lg_xmit->data_info->app_ptr = app_ptr;
1101 pdu->lg_xmit = lg_xmit;
1102 coap_ticks(&lg_xmit->last_obs);
1103 coap_ticks(&lg_xmit->last_sent);
1104 if (COAP_PDU_IS_REQUEST(pdu)) {
1105 /* Need to keep original token for updating response PDUs */
1106 lg_xmit->b.b1.app_token = coap_new_binary(pdu->actual_token.length);
1107 if (!lg_xmit->b.b1.app_token)
1108 goto fail;
1109 memcpy(lg_xmit->b.b1.app_token->s, pdu->actual_token.s,
1110 pdu->actual_token.length);
1111 /*
1112 * Need to set up new token for use during transmits
1113 * RFC9177#section-5
1114 */
1115 lg_xmit->b.b1.count = 1;
1116 lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token,
1117 lg_xmit->b.b1.count);
1118 coap_address_copy(&lg_xmit->b.b1.upstream, &session->addr_info.remote);
1119 /*
1120 * Token will be updated in pdu later as original pdu may be needed in
1121 * coap_send()
1122 */
1125 coap_encode_var_safe(buf, sizeof(buf),
1126 (unsigned int)length),
1127 buf);
1128 if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter))
1131 coap_encode_var_safe(buf, sizeof(buf),
1132 ++session->tx_rtag),
1133 buf);
1134 } else {
1135 /*
1136 * resource+query+rtag match is used for Block2 large body transmissions
1137 * token match is used for Block1 large body transmissions
1138 */
1139 lg_xmit->b.b2.resource = resource;
1140 if (query) {
1141 lg_xmit->b.b2.query = coap_new_string(query->length);
1142 if (lg_xmit->b.b2.query) {
1143 memcpy(lg_xmit->b.b2.query->s, query->s, query->length);
1144 }
1145 } else {
1146 lg_xmit->b.b2.query = NULL;
1147 }
1148 opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter);
1149 if (opt) {
1150 lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt),
1151 sizeof(lg_xmit->b.b2.rtag));
1152 memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), coap_opt_length(opt));
1153 lg_xmit->b.b2.rtag_set = 1;
1154 } else {
1155 lg_xmit->b.b2.rtag_set = 0;
1156 }
1157 lg_xmit->b.b2.etag = etag;
1158 lg_xmit->b.b2.request_method = request_method;
1159 if (maxage >= 0) {
1160 coap_tick_t now;
1161
1162 coap_ticks(&now);
1163 lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage;
1164 } else {
1165 lg_xmit->b.b2.maxage_expire = 0;
1166 }
1169 coap_encode_var_safe(buf, sizeof(buf),
1170 (unsigned int)length),
1171 buf);
1172 }
1173
1174 if (!setup_block_b(session, pdu, &block, block.num,
1175 blk_size, lg_xmit->data_info->length))
1176 goto fail;
1177
1178 /* Add in with requested block num, more bit and block size */
1180 lg_xmit->option,
1181 coap_encode_var_safe(buf, sizeof(buf),
1182 (block.num << 4) | (block.m << 3) | block.aszx),
1183 buf);
1184
1185 /* Reference PDU to use as a basis for all the subsequent blocks */
1186 lg_xmit->sent_pdu = coap_pdu_reference_lkd(pdu);
1187
1188 /* Check we still have space after adding in some options */
1189 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
1190 avail = pdu->max_size - token_options;
1191 /* There may be a response with Echo option */
1193 /* May need token of length 8, so account for this */
1194 avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0;
1195#if COAP_OSCORE_SUPPORT
1196 avail -= coap_oscore_overhead(session, pdu);
1197#endif /* COAP_OSCORE_SUPPORT */
1198 if (avail < (ssize_t)chunk) {
1199 /* chunk size change down */
1200 if (avail < 16) {
1201 coap_log_warn("not enough space, even the smallest block does not fit (3)\n");
1202 goto fail;
1203 }
1204 blk_size = coap_flsll((long long)avail) - 4 - 1;
1205 block.num = block.num << (lg_xmit->blk_size - blk_size);
1206 lg_xmit->blk_size = blk_size;
1207 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
1208 block.chunk_size = (uint32_t)chunk;
1209 block.bert = 0;
1211 lg_xmit->option,
1212 coap_encode_var_safe(buf, sizeof(buf),
1213 (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
1214 buf);
1215 }
1216
1217 rem = block.chunk_size;
1218 if (rem > lg_xmit->data_info->length - block.num * chunk)
1219 rem = lg_xmit->data_info->length - block.num * chunk;
1220 if (get_func) {
1221#if COAP_CONSTRAINED_STACK
1222 /* Protected by global_lock if needed */
1223 static uint8_t l_data[1024];
1224#else /* ! COAP_CONSTRAINED_STACK */
1225 uint8_t l_data[1024];
1226#endif /* ! COAP_CONSTRAINED_STACK */
1227 size_t l_length;
1228
1229 assert(rem <= 1024);
1230 if (get_func(session, rem, block.num * chunk, l_data, &l_length, lg_xmit->data_info->app_ptr)) {
1231 if (!coap_add_data(pdu, l_length, l_data)) {
1232 goto fail;
1233 }
1234 }
1235 } else {
1236 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
1237 goto fail;
1238 }
1239
1240 if (COAP_PDU_IS_REQUEST(pdu))
1241 lg_xmit->b.b1.bert_size = rem;
1242
1243 lg_xmit->last_block = -1;
1244
1245 /* Link the new lg_xmit in */
1246 LL_PREPEND(session->lg_xmit,lg_xmit);
1247 } else {
1248 /* No need to use blocks */
1249 if (have_block_defined) {
1251 option,
1252 coap_encode_var_safe(buf, sizeof(buf),
1253 (0 << 4) | (0 << 3) | blk_size), buf);
1254 }
1255add_data:
1256 if (get_func) {
1257 uint8_t *l_data = coap_malloc_type(COAP_STRING, length);
1258 size_t l_length;
1259
1260 if (get_func(session, length, 0, l_data, &l_length, app_ptr)) {
1261 if (!coap_add_data(pdu, l_length, l_data)) {
1262 coap_free_type(COAP_STRING, l_data);
1263 goto fail;
1264 }
1265 coap_free_type(COAP_STRING, l_data);
1266 }
1267 } else {
1268 if (!coap_add_data(pdu, length, data))
1269 goto fail;
1270 }
1271
1272 if (release_func) {
1273 coap_lock_callback(release_func(session, app_ptr));
1274 }
1275 }
1276 return 1;
1277
1278fail:
1279 if (lg_xmit) {
1280 coap_block_delete_lg_xmit(session, lg_xmit);
1281 } else if (release_func) {
1282 coap_lock_callback(release_func(session, app_ptr));
1283 }
1284 return 0;
1285}
1286
1287#if COAP_CLIENT_SUPPORT
1288COAP_API int
1290 coap_pdu_t *pdu,
1291 size_t length,
1292 const uint8_t *data,
1293 coap_release_large_data_t release_func,
1294 void *app_ptr
1295 ) {
1296 int ret;
1297
1298 coap_lock_lock(return 0);
1299 ret = coap_add_data_large_request_lkd(session, pdu, length, data,
1300 release_func, app_ptr);
1302 return ret;
1303}
1304
1305int
1307 coap_pdu_t *pdu,
1308 size_t length,
1309 const uint8_t *data,
1310 coap_release_large_data_t release_func,
1311 void *app_ptr) {
1312 /*
1313 * Delay if session->doing_first is set.
1314 * E.g. Reliable and CSM not in yet for checking block support
1315 */
1316 if (coap_client_delay_first(session) == 0) {
1317 if (release_func) {
1318 coap_lock_callback(release_func(session, app_ptr));
1319 }
1320 return 0;
1321 }
1322 return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0,
1323 length, data, release_func, NULL, app_ptr, 0, 0);
1324}
1325
1326COAP_API int
1328 coap_pdu_t *pdu,
1329 size_t length,
1330 coap_release_large_data_t release_func,
1331 coap_get_large_data_t get_func,
1332 void *app_ptr) {
1333 int ret;
1334
1335 coap_lock_lock(return 0);
1336 ret = coap_add_data_large_request_app_lkd(session, pdu, length,
1337 release_func, get_func, app_ptr);
1339 return ret;
1340}
1341
1342int
1344 coap_pdu_t *pdu,
1345 size_t length,
1346 coap_release_large_data_t release_func,
1347 coap_get_large_data_t get_func,
1348 void *app_ptr) {
1349 /*
1350 * Delay if session->doing_first is set.
1351 * E.g. Reliable and CSM not in yet for checking block support
1352 */
1353 if (coap_client_delay_first(session) == 0) {
1354 return 0;
1355 }
1356 return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0,
1357 length, NULL, release_func, get_func,
1358 app_ptr, 0, 0);
1359}
1360#endif /* ! COAP_CLIENT_SUPPORT */
1361
1362#if COAP_SERVER_SUPPORT
1363COAP_API int
1365 coap_session_t *session,
1366 const coap_pdu_t *request,
1367 coap_pdu_t *response,
1368 const coap_string_t *query,
1369 uint16_t media_type,
1370 int maxage,
1371 uint64_t etag,
1372 size_t length,
1373 const uint8_t *data,
1374 coap_release_large_data_t release_func,
1375 void *app_ptr) {
1376 int ret;
1377
1378 coap_lock_lock(return 0);
1379 ret = coap_add_data_large_response_lkd(resource, session, request,
1380 response, query, media_type, maxage, etag,
1381 length, data, release_func, app_ptr);
1383 return ret;
1384}
1385
1386int
1388 coap_session_t *session,
1389 const coap_pdu_t *request,
1390 coap_pdu_t *response,
1391 const coap_string_t *query,
1392 uint16_t media_type,
1393 int maxage,
1394 uint64_t etag,
1395 size_t length,
1396 const uint8_t *data,
1397 coap_release_large_data_t release_func,
1398 void *app_ptr
1399 ) {
1400 unsigned char buf[4];
1401 coap_block_b_t block;
1402 int block_requested = 0;
1403 int single_request = 0;
1404#if COAP_Q_BLOCK_SUPPORT
1405 uint32_t block_opt = (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) ?
1407#else /* ! COAP_Q_BLOCK_SUPPORT */
1408 uint16_t block_opt = COAP_OPTION_BLOCK2;
1409#endif /* ! COAP_Q_BLOCK_SUPPORT */
1410
1411 memset(&block, 0, sizeof(block));
1412 /*
1413 * Need to check that a valid block is getting asked for so that the
1414 * correct options are put into the PDU.
1415 */
1416 if (request) {
1417 if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) {
1418 block_requested = 1;
1419 if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
1420 coap_log_debug("Illegal block requested (%d > last = %zu)\n",
1421 block.num,
1422 length >> (block.szx + 4));
1423 response->code = COAP_RESPONSE_CODE(400);
1424 goto error;
1425 }
1426 }
1427#if COAP_Q_BLOCK_SUPPORT
1428 else if (coap_get_block_b(session, request, COAP_OPTION_Q_BLOCK2, &block)) {
1429 block_requested = 1;
1430 if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
1431 coap_log_debug("Illegal block requested (%d > last = %zu)\n",
1432 block.num,
1433 length >> (block.szx + 4));
1434 response->code = COAP_RESPONSE_CODE(400);
1435 goto error;
1436 }
1437 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
1438 set_block_mode_has_q(session->block_mode);
1439 block_opt = COAP_OPTION_Q_BLOCK2;
1440 }
1441 if (block.m == 0)
1442 single_request = 1;
1443 }
1444#endif /* COAP_Q_BLOCK_SUPPORT */
1445 }
1446
1448 coap_encode_var_safe(buf, sizeof(buf),
1449 media_type),
1450 buf);
1451
1452 if (maxage >= 0) {
1453 coap_update_option(response,
1455 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
1456 }
1457
1458 if (block_requested) {
1459 int res;
1460
1461 res = coap_write_block_b_opt(session, &block, block_opt, response,
1462 length);
1463
1464 switch (res) {
1465 case -2: /* illegal block (caught above) */
1466 response->code = COAP_RESPONSE_CODE(400);
1467 goto error;
1468 case -1: /* should really not happen */
1469 assert(0);
1470 /* fall through if assert is a no-op */
1471 case -3: /* cannot handle request */
1472 response->code = COAP_RESPONSE_CODE(500);
1473 goto error;
1474 default: /* everything is good */
1475 ;
1476 }
1477 }
1478
1479 /* add data body */
1480 if (request &&
1481 !coap_add_data_large_internal(session, request, response, resource,
1482 query, maxage, etag, length, data,
1483 release_func, NULL, app_ptr, single_request,
1484 request->code)) {
1485 response->code = COAP_RESPONSE_CODE(500);
1486 goto error_released;
1487 }
1488
1489 return 1;
1490
1491error:
1492 if (release_func) {
1493 coap_lock_callback(release_func(session, app_ptr));
1494 }
1495error_released:
1496#if COAP_ERROR_PHRASE_LENGTH > 0
1497 coap_add_data(response,
1498 strlen(coap_response_phrase(response->code)),
1499 (const unsigned char *)coap_response_phrase(response->code));
1500#endif /* COAP_ERROR_PHRASE_LENGTH > 0 */
1501 return 0;
1502}
1503#endif /* ! COAP_SERVER_SUPPORT */
1504
1505/*
1506 * return 1 if there is a future expire time, else 0.
1507 * update tim_rem with remaining value if return is 1.
1508 */
1509int
1511 coap_tick_t *tim_rem) {
1512 coap_lg_xmit_t *lg_xmit;
1513 coap_lg_xmit_t *q;
1514#if COAP_Q_BLOCK_SUPPORT
1515 coap_tick_t idle_timeout = 4 * COAP_NON_TIMEOUT_TICKS(session);
1516#else /* ! COAP_Q_BLOCK_SUPPORT */
1517 coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND;
1518#endif /* ! COAP_Q_BLOCK_SUPPORT */
1519 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1520 int ret = 0;
1521
1522 *tim_rem = COAP_MAX_DELAY_TICKS;
1523
1524 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
1525 if (lg_xmit->last_all_sent) {
1526 if (lg_xmit->last_all_sent + idle_timeout <= now) {
1527 /* Expire this entry */
1528 LL_DELETE(session->lg_xmit, lg_xmit);
1529 coap_block_delete_lg_xmit(session, lg_xmit);
1530 } else {
1531 /* Delay until the lg_xmit needs to expire */
1532 if (*tim_rem > lg_xmit->last_all_sent + idle_timeout - now) {
1533 *tim_rem = lg_xmit->last_all_sent + idle_timeout - now;
1534 ret = 1;
1535 }
1536 }
1537 } else if (lg_xmit->last_sent) {
1538 if (lg_xmit->last_sent + partial_timeout <= now) {
1539 /* Expire this entry */
1540 int is_mcast = 0;
1541#if COAP_CLIENT_SUPPORT
1542 is_mcast = COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) &&
1543 coap_is_mcast(&lg_xmit->b.b1.upstream);
1544#endif /* COAP_CLIENT_SUPPORT */
1545 LL_DELETE(session->lg_xmit, lg_xmit);
1546
1547 coap_block_delete_lg_xmit(session, lg_xmit);
1548 if (!is_mcast)
1550 } else {
1551 /* Delay until the lg_xmit needs to expire */
1552 if (*tim_rem > lg_xmit->last_sent + partial_timeout - now) {
1553 *tim_rem = lg_xmit->last_sent + partial_timeout - now;
1554 ret = 1;
1555 }
1556 }
1557 }
1558 }
1559 return ret;
1560}
1561
1562#if COAP_CLIENT_SUPPORT
1563#if COAP_Q_BLOCK_SUPPORT
1564static coap_pdu_t *
1565coap_build_missing_pdu(coap_session_t *session, coap_lg_crcv_t *lg_crcv) {
1566 coap_pdu_t *pdu;
1567 coap_opt_filter_t drop_options;
1568 uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter);
1569 uint8_t buf[8];
1570 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
1571
1572 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1576 pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf,
1577 &drop_options);
1578 if (!pdu)
1579 return NULL;
1580 pdu->type = lg_crcv->last_type;
1581 return pdu;
1582}
1583
1584static void
1585coap_request_missing_q_block2(coap_session_t *session, coap_lg_crcv_t *lg_crcv) {
1586 uint8_t buf[8];
1587 uint32_t i;
1588 int block = -1; /* Last one seen */
1589 size_t sofar;
1590 size_t block_size;
1591 coap_pdu_t *pdu = NULL;
1592 int block_payload_set = -1;
1593
1594 if (session->block_mode & COAP_BLOCK_USE_M_Q_BLOCK) {
1595 /*
1596 * See if it is safe to use the single 'M' block variant of request
1597 *
1598 * If any blocks seen, then missing blocks are after range[0].end and
1599 * terminate on the last block or before range[1].begin if set.
1600 * If not defined or range[1].begin is in a different payload set then
1601 * safe to use M bit.
1602 */
1603 if (lg_crcv->rec_blocks.used &&
1604 (lg_crcv->rec_blocks.used < 2 ||
1605 ((lg_crcv->rec_blocks.range[0].end + 1) / COAP_MAX_PAYLOADS(session) !=
1606 (lg_crcv->rec_blocks.range[1].begin -1) / COAP_MAX_PAYLOADS(session)))) {
1607 block = lg_crcv->rec_blocks.range[0].end + 1;
1608 block_size = (size_t)1 << (lg_crcv->szx + 4);
1609 sofar = block * block_size;
1610 if (sofar < lg_crcv->total_len) {
1611 /* Ask for missing blocks */
1612 if (pdu == NULL) {
1613 pdu = coap_build_missing_pdu(session, lg_crcv);
1614 if (!pdu)
1615 return;
1616 }
1618 coap_encode_var_safe(buf, sizeof(buf),
1619 (block << 4) | (1 << 3) | lg_crcv->szx),
1620 buf);
1621 block_payload_set = block / COAP_MAX_PAYLOADS(session);
1622 goto send_it;
1623 }
1624 }
1625 }
1626 block = -1;
1627 for (i = 0; i < lg_crcv->rec_blocks.used; i++) {
1628 if (block < (int)lg_crcv->rec_blocks.range[i].begin &&
1629 lg_crcv->rec_blocks.range[i].begin != 0) {
1630 /* Ask for missing blocks */
1631 if (pdu == NULL) {
1632 pdu = coap_build_missing_pdu(session, lg_crcv);
1633 if (!pdu)
1634 continue;
1635 }
1636 block++;
1637 if (block_payload_set == -1)
1638 block_payload_set = block / COAP_MAX_PAYLOADS(session);
1639 for (; block < (int)lg_crcv->rec_blocks.range[i].begin &&
1640 block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) {
1642 coap_encode_var_safe(buf, sizeof(buf),
1643 (block << 4) | (0 << 3) | lg_crcv->szx),
1644 buf);
1645 }
1646 }
1647 if (block < (int)lg_crcv->rec_blocks.range[i].end) {
1648 block = lg_crcv->rec_blocks.range[i].end;
1649 }
1650 }
1651 block_size = (size_t)1 << (lg_crcv->szx + 4);
1652 sofar = (block + 1) * block_size;
1653 if (sofar < lg_crcv->total_len) {
1654 /* Ask for trailing missing blocks */
1655 if (pdu == NULL) {
1656 pdu = coap_build_missing_pdu(session, lg_crcv);
1657 if (!pdu)
1658 return;
1659 }
1660 sofar = (lg_crcv->total_len + block_size - 1)/block_size;
1661 block++;
1662 if (block_payload_set == -1)
1663 block_payload_set = block / COAP_MAX_PAYLOADS(session);
1664 for (; block < (ssize_t)sofar &&
1665 block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) {
1667 coap_encode_var_safe(buf, sizeof(buf),
1668 (block << 4) | (0 << 3) | lg_crcv->szx),
1669 buf);
1670 }
1671 }
1672send_it:
1673 if (pdu)
1674 coap_send_internal(session, pdu, NULL);
1675 lg_crcv->rec_blocks.retry++;
1676 if (block_payload_set != -1)
1677 lg_crcv->rec_blocks.processing_payload_set = block_payload_set;
1678 coap_ticks(&lg_crcv->rec_blocks.last_seen);
1679}
1680#endif /* COAP_Q_BLOCK_SUPPORT */
1681
1682/*
1683 * return 1 if there is a future expire time, else 0.
1684 * update tim_rem with remaining value if return is 1.
1685 */
1686int
1688 coap_tick_t *tim_rem) {
1689 coap_lg_crcv_t *lg_crcv;
1690 coap_lg_crcv_t *q;
1691 coap_tick_t partial_timeout;
1692#if COAP_Q_BLOCK_SUPPORT
1693 coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session);
1694#endif /* COAP_Q_BLOCK_SUPPORT */
1695 int ret = 0;
1696
1697 *tim_rem = COAP_MAX_DELAY_TICKS;
1698#if COAP_Q_BLOCK_SUPPORT
1699 if (COAP_PROTO_NOT_RELIABLE(session->proto) &&
1700 session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)
1701 partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session);
1702 else
1703#endif /* COAP_Q_BLOCK_SUPPORT */
1704 partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1705
1706 LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) {
1707 if (COAP_PROTO_RELIABLE(session->proto) || lg_crcv->last_type != COAP_MESSAGE_NON)
1708 goto check_expire;
1709
1710#if COAP_Q_BLOCK_SUPPORT
1711 if (lg_crcv->block_option == COAP_OPTION_Q_BLOCK2 && lg_crcv->rec_blocks.used) {
1712 size_t scaled_timeout = receive_timeout *
1713 ((size_t)1 << lg_crcv->rec_blocks.retry);
1714
1715 if (lg_crcv->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) {
1716 /* Done NON_MAX_RETRANSMIT retries */
1717 coap_handle_nack(session, lg_crcv->sent_pdu,
1719 goto expire;
1720 }
1721 if (lg_crcv->rec_blocks.last_seen + scaled_timeout <= now) {
1722 coap_request_missing_q_block2(session, lg_crcv);
1723 } else {
1724 if (*tim_rem > lg_crcv->rec_blocks.last_seen + scaled_timeout - now) {
1725 *tim_rem = lg_crcv->rec_blocks.last_seen + scaled_timeout - now;
1726 ret = 1;
1727 }
1728 }
1729 }
1730#endif /* COAP_Q_BLOCK_SUPPORT */
1731 /* Used for Block2 and Q-Block2 */
1732check_expire:
1733 if (!lg_crcv->observe_set && lg_crcv->last_used &&
1734 lg_crcv->last_used + partial_timeout <= now) {
1735#if COAP_Q_BLOCK_SUPPORT
1736expire:
1737#endif /* COAP_Q_BLOCK_SUPPORT */
1738 /* Expire this entry */
1739 LL_DELETE(session->lg_crcv, lg_crcv);
1740 coap_block_delete_lg_crcv(session, lg_crcv);
1741 } else if (!lg_crcv->observe_set && lg_crcv->last_used) {
1742 /* Delay until the lg_crcv needs to expire */
1743 if (*tim_rem > lg_crcv->last_used + partial_timeout - now) {
1744 *tim_rem = lg_crcv->last_used + partial_timeout - now;
1745 ret = 1;
1746 }
1747 }
1748 }
1749 return ret;
1750}
1751#endif /* COAP_CLIENT_SUPPORT */
1752
1753#if COAP_SERVER_SUPPORT
1754#if COAP_Q_BLOCK_SUPPORT
1755static coap_pdu_t *
1756pdu_408_build(coap_session_t *session, coap_lg_srcv_t *lg_srcv) {
1757 coap_pdu_t *pdu;
1758 uint8_t buf[4];
1759
1761 COAP_RESPONSE_CODE(408),
1762 coap_new_message_id_lkd(session),
1764 if (!pdu)
1765 return NULL;
1766 if (lg_srcv->last_token)
1767 coap_add_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
1769 coap_encode_var_safe(buf, sizeof(buf),
1771 buf);
1772 pdu->token[pdu->used_size++] = COAP_PAYLOAD_START;
1773 pdu->data = pdu->token + pdu->used_size;
1774 return pdu;
1775}
1776
1777static int
1778add_408_block(coap_pdu_t *pdu, int block) {
1779 size_t len;
1780 uint8_t val[8];
1781
1782 assert(block >= 0 && block < (1 << 20));
1783
1784 if (block < 0 || block >= (1 << 20)) {
1785 return 0;
1786 } else if (block < 24) {
1787 len = 1;
1788 val[0] = block;
1789 } else if (block < 0x100) {
1790 len = 2;
1791 val[0] = 24;
1792 val[1] = block;
1793 } else if (block < 0x10000) {
1794 len = 3;
1795 val[0] = 25;
1796 val[1] = block >> 8;
1797 val[2] = block & 0xff;
1798 } else { /* Largest block number is 2^^20 - 1 */
1799 len = 4;
1800 val[0] = 26;
1801 val[1] = block >> 16;
1802 val[2] = (block >> 8) & 0xff;
1803 val[3] = block & 0xff;
1804 }
1805 if (coap_pdu_check_resize(pdu, pdu->used_size + len)) {
1806 memcpy(&pdu->token[pdu->used_size], val, len);
1807 pdu->used_size += len;
1808 return 1;
1809 }
1810 return 0;
1811}
1812#endif /* COAP_Q_BLOCK_SUPPORT */
1813#endif /* COAP_SERVER_SUPPORT */
1814
1815static int
1816check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
1817 uint32_t i;
1818
1819 for (i = 0; i < rec_blocks->used; i++) {
1820 if (block_num < rec_blocks->range[i].begin)
1821 return 0;
1822 if (block_num <= rec_blocks->range[i].end)
1823 return 1;
1824 }
1825 return 0;
1826}
1827
1828#if COAP_SERVER_SUPPORT
1829static int
1830check_if_next_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
1831 if (rec_blocks->used == 0) {
1832 return block_num == 0 ? 1 : 0;
1833 }
1834 if (rec_blocks->range[rec_blocks->used-1].end + 1 == block_num)
1835 return 1;
1836
1837 return 0;
1838}
1839#endif /* COAP_SERVER_SUPPORT */
1840
1841static int
1843 uint32_t i;
1844 uint32_t block = 0;
1845
1846 if (rec_blocks->total_blocks == 0) {
1847 /* Not seen block with More bit unset yet */
1848 return 0;
1849 }
1850
1851 for (i = 0; i < rec_blocks->used; i++) {
1852 if (block < rec_blocks->range[i].begin)
1853 return 0;
1854 if (block < rec_blocks->range[i].end)
1855 block = rec_blocks->range[i].end;
1856 }
1857 return 1;
1858}
1859
1860#if COAP_CLIENT_SUPPORT
1861#if COAP_Q_BLOCK_SUPPORT
1862static int
1863check_all_blocks_in_for_payload_set(coap_session_t *session,
1864 coap_rblock_t *rec_blocks) {
1865 if (rec_blocks->used &&
1866 (rec_blocks->range[0].end + 1) / COAP_MAX_PAYLOADS(session) >
1867 rec_blocks->processing_payload_set)
1868 return 1;
1869 return 0;
1870}
1871
1872static int
1873check_any_blocks_next_payload_set(coap_session_t *session,
1874 coap_rblock_t *rec_blocks) {
1875 if (rec_blocks->used > 1 &&
1876 rec_blocks->range[1].begin / COAP_MAX_PAYLOADS(session) ==
1877 rec_blocks->processing_payload_set)
1878 return 1;
1879 return 0;
1880}
1881#endif /* COAP_Q_BLOCK_SUPPORT */
1882#endif /* COAP_CLIENT_SUPPORT */
1883
1884#if COAP_SERVER_SUPPORT
1885/*
1886 * return 1 if there is a future expire time, else 0.
1887 * update tim_rem with remaining value if return is 1.
1888 */
1889int
1891 coap_tick_t *tim_rem) {
1892 coap_lg_srcv_t *lg_srcv;
1893 coap_lg_srcv_t *q;
1894 coap_tick_t partial_timeout;
1895#if COAP_Q_BLOCK_SUPPORT
1896 coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session);
1897#endif /* COAP_Q_BLOCK_SUPPORT */
1898 int ret = 0;
1899
1900 *tim_rem = COAP_MAX_DELAY_TICKS;
1901#if COAP_Q_BLOCK_SUPPORT
1902 if (COAP_PROTO_NOT_RELIABLE(session->proto) &&
1903 session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)
1904 partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session);
1905 else
1906#endif /* COAP_Q_BLOCK_SUPPORT */
1907 partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1908
1909 LL_FOREACH_SAFE(session->lg_srcv, lg_srcv, q) {
1910 if (lg_srcv->dont_timeout) {
1911 /* Not safe to timeout at present */
1912 continue;
1913 }
1914 if (COAP_PROTO_RELIABLE(session->proto) || lg_srcv->last_type != COAP_MESSAGE_NON)
1915 goto check_expire;
1916
1917#if COAP_Q_BLOCK_SUPPORT
1918 if (lg_srcv->block_option == COAP_OPTION_Q_BLOCK1 && lg_srcv->rec_blocks.used) {
1919 size_t scaled_timeout = receive_timeout *
1920 ((size_t)1 << lg_srcv->rec_blocks.retry);
1921
1922 if (lg_srcv->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) {
1923 /* Done NON_MAX_RETRANSMIT retries */
1924 goto expire;
1925 }
1926 if (lg_srcv->rec_blocks.last_seen + scaled_timeout <= now) {
1927 uint32_t i;
1928 int block = -1; /* Last one seen */
1929 size_t block_size = (size_t)1 << (lg_srcv->szx + 4);
1930 size_t final_block = (lg_srcv->total_len + block_size - 1)/block_size - 1;
1931 size_t cur_payload;
1932 size_t last_payload_block;
1933 coap_pdu_t *pdu = NULL;
1934 size_t no_blocks = 0;
1935
1936 /* Need to count the number of missing blocks */
1937 for (i = 0; i < lg_srcv->rec_blocks.used; i++) {
1938 if (block < (int)lg_srcv->rec_blocks.range[i].begin &&
1939 lg_srcv->rec_blocks.range[i].begin != 0) {
1940 block++;
1941 no_blocks += lg_srcv->rec_blocks.range[i].begin - block;
1942 }
1943 if (block < (int)lg_srcv->rec_blocks.range[i].end) {
1944 block = lg_srcv->rec_blocks.range[i].end;
1945 }
1946 }
1947 if (no_blocks == 0 && block == (int)final_block)
1948 goto expire;
1949
1950 /* Include missing up to end of current payload or total amount */
1951 cur_payload = block / COAP_MAX_PAYLOADS(session);
1952 last_payload_block = (cur_payload + 1) * COAP_MAX_PAYLOADS(session) - 1;
1953 if (final_block > last_payload_block) {
1954 final_block = last_payload_block;
1955 }
1956 no_blocks += final_block - block;
1957 if (no_blocks == 0) {
1958 /* Add in the blocks out of the next payload */
1959 final_block = (lg_srcv->total_len + block_size - 1)/block_size - 1;
1960 last_payload_block += COAP_MAX_PAYLOADS(session);
1961 if (final_block > last_payload_block) {
1962 final_block = last_payload_block;
1963 }
1964 }
1965 /* Ask for the missing blocks */
1966 block = -1;
1967 for (i = 0; i < lg_srcv->rec_blocks.used; i++) {
1968 if (block < (int)lg_srcv->rec_blocks.range[i].begin &&
1969 lg_srcv->rec_blocks.range[i].begin != 0) {
1970 /* Report on missing blocks */
1971 if (pdu == NULL) {
1972 pdu = pdu_408_build(session, lg_srcv);
1973 if (!pdu)
1974 continue;
1975 }
1976 block++;
1977 for (; block < (int)lg_srcv->rec_blocks.range[i].begin; block++) {
1978 if (!add_408_block(pdu, block)) {
1979 break;
1980 }
1981 }
1982 }
1983 if (block < (int)lg_srcv->rec_blocks.range[i].end) {
1984 block = lg_srcv->rec_blocks.range[i].end;
1985 }
1986 }
1987 block++;
1988 for (; block <= (int)final_block; block++) {
1989 if (pdu == NULL) {
1990 pdu = pdu_408_build(session, lg_srcv);
1991 if (!pdu)
1992 continue;
1993 }
1994 if (!add_408_block(pdu, block)) {
1995 break;
1996 }
1997 }
1998 if (pdu)
1999 coap_send_internal(session, pdu, NULL);
2000 lg_srcv->rec_blocks.retry++;
2001 coap_ticks(&lg_srcv->rec_blocks.last_seen);
2002 }
2003 if (*tim_rem > lg_srcv->rec_blocks.last_seen + scaled_timeout - now) {
2004 *tim_rem = lg_srcv->rec_blocks.last_seen + scaled_timeout - now;
2005 ret = 1;
2006 }
2007 }
2008#endif /* COAP_Q_BLOCK_SUPPORT */
2009 /* Used for Block1 and Q-Block1 */
2010check_expire:
2011 if (lg_srcv->no_more_seen)
2012 partial_timeout = 10 * COAP_TICKS_PER_SECOND;
2013 if (lg_srcv->last_used && lg_srcv->last_used + partial_timeout <= now) {
2014#if COAP_Q_BLOCK_SUPPORT
2015expire:
2016#endif /* COAP_Q_BLOCK_SUPPORT */
2017 /* Expire this entry */
2018 if (lg_srcv->no_more_seen && lg_srcv->block_option == COAP_OPTION_BLOCK1) {
2019 /*
2020 * Need to send a separate 4.08 to indicate missing blocks
2021 * Using NON is permissable as per
2022 * https://datatracker.ietf.org/doc/html/rfc7252#section-5.2.3
2023 */
2024 coap_pdu_t *pdu;
2025
2027 COAP_RESPONSE_CODE(408),
2028 coap_new_message_id_lkd(session),
2030 if (pdu) {
2031 if (lg_srcv->last_token)
2032 coap_add_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
2033 coap_add_data(pdu, sizeof("Missing interim block")-1,
2034 (const uint8_t *)"Missing interim block");
2035 coap_send_internal(session, pdu, NULL);
2036 }
2037 }
2038 LL_DELETE(session->lg_srcv, lg_srcv);
2039 coap_block_delete_lg_srcv(session, lg_srcv);
2040 } else if (lg_srcv->last_used) {
2041 /* Delay until the lg_srcv needs to expire */
2042 if (*tim_rem > lg_srcv->last_used + partial_timeout - now) {
2043 *tim_rem = lg_srcv->last_used + partial_timeout - now;
2044 ret = 1;
2045 }
2046 }
2047 }
2048 return ret;
2049}
2050#endif /* COAP_SERVER_SUPPORT */
2051
2052#if COAP_Q_BLOCK_SUPPORT
2053/*
2054 * pdu is always released before return IF COAP_SEND_INC_PDU
2055 */
2057coap_send_q_blocks(coap_session_t *session,
2058 coap_lg_xmit_t *lg_xmit,
2059 coap_block_b_t block,
2060 coap_pdu_t *pdu,
2061 coap_send_pdu_t send_pdu) {
2062 coap_pdu_t *block_pdu = NULL;
2063 coap_opt_filter_t drop_options;
2065 uint64_t token;
2066 const uint8_t *ptoken;
2067 uint8_t ltoken[8];
2068 size_t ltoken_length;
2069 uint32_t delayqueue_cnt = 0;
2070
2071 if (!lg_xmit) {
2072 if (send_pdu == COAP_SEND_INC_PDU)
2073 return coap_send_internal(session, pdu, NULL);
2074 return COAP_INVALID_MID;
2075 }
2076
2077 if (pdu->type == COAP_MESSAGE_CON) {
2078 coap_queue_t *delayqueue;
2079
2080 delayqueue_cnt = session->con_active +
2081 (send_pdu == COAP_SEND_INC_PDU ? 1 : 0);
2082 LL_FOREACH(session->delayqueue, delayqueue) {
2083 delayqueue_cnt++;
2084 }
2085 }
2086 pdu->lg_xmit = lg_xmit;
2087 if (block.m &&
2088 ((pdu->type == COAP_MESSAGE_NON &&
2089 ((block.num + 1) % COAP_MAX_PAYLOADS(session)) + 1 !=
2090 COAP_MAX_PAYLOADS(session)) ||
2091 (pdu->type == COAP_MESSAGE_ACK &&
2092 lg_xmit->option == COAP_OPTION_Q_BLOCK2) ||
2093 (pdu->type == COAP_MESSAGE_CON &&
2094 delayqueue_cnt < COAP_NSTART(session)) ||
2095 COAP_PROTO_RELIABLE(session->proto))) {
2096 /* Allocate next pdu if there is headroom */
2097 if (COAP_PDU_IS_RESPONSE(pdu)) {
2098 ptoken = pdu->actual_token.s;
2099 ltoken_length = pdu->actual_token.length;
2100 } else {
2101 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count);
2102 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
2103 ptoken = ltoken;
2104 }
2105
2106 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
2107 coap_option_filter_set(&drop_options, lg_xmit->option);
2108 block_pdu = coap_pdu_duplicate_lkd(pdu, session,
2109 ltoken_length,
2110 ptoken, &drop_options);
2111 if (block_pdu->type == COAP_MESSAGE_ACK)
2112 block_pdu->type = COAP_MESSAGE_CON;
2113 }
2114
2115 /* Send initial pdu (which deletes 'pdu') */
2116 if (send_pdu == COAP_SEND_INC_PDU &&
2117 (mid = coap_send_internal(session, pdu, NULL)) == COAP_INVALID_MID) {
2118 /* Not expected, underlying issue somewhere */
2119 coap_delete_pdu_lkd(block_pdu);
2120 return COAP_INVALID_MID;
2121 }
2122
2123 while (block_pdu) {
2124 coap_pdu_t *t_pdu = NULL;
2125 uint8_t buf[8];
2126 size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4));
2127
2128 block.num++;
2129 lg_xmit->offset = block.num * chunk;
2130 block.m = lg_xmit->offset + chunk < lg_xmit->data_info->length;
2131 if (block.m && ((block_pdu->type == COAP_MESSAGE_NON &&
2132 (block.num % COAP_MAX_PAYLOADS(session)) + 1 !=
2133 COAP_MAX_PAYLOADS(session)) ||
2134 (block_pdu->type == COAP_MESSAGE_CON &&
2135 delayqueue_cnt + 1 < COAP_NSTART(session)) ||
2136 COAP_PROTO_RELIABLE(session->proto))) {
2137 /*
2138 * Send following block if
2139 * NON and more in MAX_PAYLOADS
2140 * CON and NSTART allows it (based on number in delayqueue)
2141 * Reliable transport
2142 */
2143 if (COAP_PDU_IS_RESPONSE(block_pdu)) {
2144 ptoken = block_pdu->actual_token.s;
2145 ltoken_length = block_pdu->actual_token.length;
2146 } else {
2147 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count);
2148 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
2149 ptoken = ltoken;
2150 }
2151 t_pdu = coap_pdu_duplicate_lkd(block_pdu, session,
2152 ltoken_length, ptoken, &drop_options);
2153 }
2154 if (!coap_update_option(block_pdu, lg_xmit->option,
2156 sizeof(buf),
2157 ((block.num) << 4) |
2158 (block.m << 3) |
2159 block.szx),
2160 buf)) {
2161 coap_log_warn("Internal update issue option\n");
2162 coap_delete_pdu_lkd(block_pdu);
2163 coap_delete_pdu_lkd(t_pdu);
2164 break;
2165 }
2166
2167 if (lg_xmit->data_info->get_func) {
2168#if COAP_CONSTRAINED_STACK
2169 /* Protected by global_lock if needed */
2170 static uint8_t l_data[1024];
2171#else /* ! COAP_CONSTRAINED_STACK */
2172 uint8_t l_data[1024];
2173#endif /* ! COAP_CONSTRAINED_STACK */
2174 size_t l_length;
2175
2176 assert(chunk <= 1024);
2177 if (lg_xmit->data_info->get_func(session, chunk,
2178 block.num * chunk, l_data, &l_length,
2179 lg_xmit->data_info->app_ptr)) {
2180 if (!coap_add_data(block_pdu, l_length, l_data)) {
2181 coap_log_warn("Internal update issue data (1)\n");
2182 coap_delete_pdu_lkd(block_pdu);
2183 coap_delete_pdu_lkd(t_pdu);
2184 break;
2185 }
2186 }
2187 } else {
2188 if (!coap_add_block(block_pdu,
2189 lg_xmit->data_info->length,
2190 lg_xmit->data_info->data,
2191 block.num,
2192 block.szx)) {
2193 coap_log_warn("Internal update issue data (2)\n");
2194 coap_delete_pdu_lkd(block_pdu);
2195 coap_delete_pdu_lkd(t_pdu);
2196 break;
2197 }
2198 }
2199 if (COAP_PDU_IS_RESPONSE(block_pdu)) {
2200 lg_xmit->last_block = block.num;
2201 }
2202 mid = coap_send_internal(session, block_pdu, NULL);
2203 if (mid == COAP_INVALID_MID) {
2204 /* Not expected, underlying issue somewhere */
2205 coap_delete_pdu_lkd(t_pdu);
2206 return COAP_INVALID_MID;
2207 }
2208 block_pdu = t_pdu;
2209 }
2210 if (!block.m) {
2211 lg_xmit->last_payload = 0;
2212 coap_ticks(&lg_xmit->last_all_sent);
2213 } else
2214 coap_ticks(&lg_xmit->last_payload);
2215 return mid;
2216}
2217
2218#if COAP_CLIENT_SUPPORT
2219/*
2220 * Return 1 if there is a future expire time, else 0.
2221 * Update tim_rem with remaining value if return is 1.
2222 */
2223int
2224coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem) {
2225 coap_lg_xmit_t *lg_xmit;
2226 coap_lg_xmit_t *q;
2227 coap_tick_t timed_out;
2228 int ret = 0;
2229
2230 *tim_rem = COAP_MAX_DELAY_TICKS;
2231 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
2232 coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks;
2233
2234 if (now <= non_timeout) {
2235 /* Too early in the startup cycle to have an accurate response */
2236 *tim_rem = non_timeout - now;
2237 return 1;
2238 }
2239 timed_out = now - non_timeout;
2240
2241 if (lg_xmit->last_payload) {
2242 if (lg_xmit->last_payload <= timed_out) {
2243 /* Send off the next MAX_PAYLOAD set */
2244 coap_block_b_t block;
2245 size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2246
2247 memset(&block, 0, sizeof(block));
2248 block.num = (uint32_t)(lg_xmit->offset / chunk);
2249 block.m = lg_xmit->offset + chunk < lg_xmit->data_info->length;
2250 block.szx = lg_xmit->blk_size;
2251 coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
2252 if (*tim_rem > non_timeout) {
2253 *tim_rem = non_timeout;
2254 ret = 1;
2255 }
2256 } else {
2257 /* Delay until the next MAX_PAYLOAD needs to be sent off */
2258 if (*tim_rem > lg_xmit->last_payload - timed_out) {
2259 *tim_rem = lg_xmit->last_payload - timed_out;
2260 ret = 1;
2261 }
2262 }
2263 } else if (lg_xmit->last_all_sent) {
2264 non_timeout = COAP_NON_TIMEOUT_TICKS(session);
2265 if (lg_xmit->last_all_sent + 4 * non_timeout <= now) {
2266 /* Expire this entry */
2267 LL_DELETE(session->lg_xmit, lg_xmit);
2268 coap_block_delete_lg_xmit(session, lg_xmit);
2269 } else {
2270 /* Delay until the lg_xmit needs to expire */
2271 if (*tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) {
2272 *tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now;
2273 ret = 1;
2274 }
2275 }
2276 }
2277 }
2278 return ret;
2279}
2280#endif /* COAP_CLIENT_SUPPORT */
2281
2282#if COAP_SERVER_SUPPORT
2283/*
2284 * Return 1 if there is a future expire time, else 0.
2285 * Update tim_rem with remaining value if return is 1.
2286 */
2287int
2288coap_block_check_q_block2_xmit(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem) {
2289 coap_lg_xmit_t *lg_xmit;
2290 coap_lg_xmit_t *q;
2291 coap_tick_t timed_out;
2292 int ret = 0;
2293
2294 *tim_rem = (coap_tick_t)-1;
2295 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
2296 coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks;
2297
2298 if (now <= non_timeout) {
2299 /* Too early in the startup cycle to have an accurate response */
2300 *tim_rem = non_timeout - now;
2301 return 1;
2302 }
2303 timed_out = now - non_timeout;
2304
2305 if (lg_xmit->last_payload) {
2306 if (lg_xmit->last_payload <= timed_out) {
2307 /* Send off the next MAX_PAYLOAD set */
2308 coap_block_b_t block;
2309 size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2310
2311 memset(&block, 0, sizeof(block));
2312 block.num = (uint32_t)(lg_xmit->offset / chunk);
2313 block.m = lg_xmit->offset + chunk < lg_xmit->data_info->length;
2314 block.szx = lg_xmit->blk_size;
2315 if (block.num == (uint32_t)lg_xmit->last_block)
2316 coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
2317 if (*tim_rem > non_timeout) {
2318 *tim_rem = non_timeout;
2319 ret = 1;
2320 }
2321 } else {
2322 /* Delay until the next MAX_PAYLOAD needs to be sent off */
2323 if (*tim_rem > lg_xmit->last_payload - timed_out) {
2324 *tim_rem = lg_xmit->last_payload - timed_out;
2325 ret = 1;
2326 }
2327 }
2328 } else if (lg_xmit->last_all_sent) {
2329 non_timeout = COAP_NON_TIMEOUT_TICKS(session);
2330 if (lg_xmit->last_all_sent + 4 * non_timeout <= now) {
2331 /* Expire this entry */
2332 LL_DELETE(session->lg_xmit, lg_xmit);
2333 coap_block_delete_lg_xmit(session, lg_xmit);
2334 } else {
2335 /* Delay until the lg_xmit needs to expire */
2336 if (*tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) {
2337 *tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now;
2338 ret = 1;
2339 }
2340 }
2341 }
2342 }
2343 return ret;
2344}
2345#endif /* COAP_SERVER_SUPPORT */
2346#endif /* COAP_Q_BLOCK_SUPPORT */
2347
2348#if COAP_CLIENT_SUPPORT
2349/*
2350 * If Observe = 0, save the token away and return NULL
2351 * Else If Observe = 1, return the saved token for this block
2352 * Else, return NULL
2353 */
2354static coap_bin_const_t *
2355track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv,
2356 uint32_t block_num, coap_bin_const_t *token) {
2357 /* Need to handle Observe for large FETCH */
2358 coap_opt_iterator_t opt_iter;
2360 &opt_iter);
2361
2362 if (opt && lg_crcv) {
2363 int observe_action = -1;
2364 coap_bin_const_t **tmp;
2365
2366 observe_action = coap_decode_var_bytes(coap_opt_value(opt),
2367 coap_opt_length(opt));
2368 if (observe_action == COAP_OBSERVE_ESTABLISH) {
2369 /* Save the token in lg_crcv */
2370 if (lg_crcv->obs_token_cnt <= block_num) {
2371 size_t i;
2372
2373 tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token,
2374 (block_num + 1) * sizeof(lg_crcv->obs_token[0]));
2375 if (tmp == NULL)
2376 return NULL;
2377 lg_crcv->obs_token = tmp;
2378 for (i = lg_crcv->obs_token_cnt; i < block_num + 1; i++) {
2379 lg_crcv->obs_token[i] = NULL;
2380 }
2381 }
2382 coap_delete_bin_const(lg_crcv->obs_token[block_num]);
2383
2384 lg_crcv->obs_token_cnt = block_num + 1;
2385 lg_crcv->obs_token[block_num] = coap_new_bin_const(token->s,
2386 token->length);
2387 if (lg_crcv->obs_token[block_num] == NULL)
2388 return NULL;
2389 } else if (observe_action == COAP_OBSERVE_CANCEL) {
2390 /* Use the token in lg_crcv */
2391 if (block_num < lg_crcv->obs_token_cnt) {
2392 return lg_crcv->obs_token[block_num];
2393 }
2394 }
2395 }
2396 return NULL;
2397}
2398
2399#if COAP_Q_BLOCK_SUPPORT
2401coap_send_q_block1(coap_session_t *session,
2402 coap_block_b_t block,
2403 coap_pdu_t *request,
2404 coap_send_pdu_t send_request) {
2405 /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK1 */
2406 coap_lg_xmit_t *lg_xmit;
2407 uint64_t token_match =
2409 request->actual_token.length));
2410
2411 LL_FOREACH(session->lg_xmit, lg_xmit) {
2412 if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 &&
2413 (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) ||
2414 token_match ==
2416 lg_xmit->b.b1.app_token->length))))
2417 break;
2418 /* try out the next one */
2419 }
2420 return coap_send_q_blocks(session, lg_xmit, block, request, send_request);
2421}
2422#endif /* COAP_Q_BLOCK_SUPPORT */
2423#endif /* COAP_CLIENT_SUPPORT */
2424
2425#if COAP_SERVER_SUPPORT
2426#if COAP_Q_BLOCK_SUPPORT
2427/*
2428 * response is always released before return IF COAP_SEND_INC_PDU
2429 */
2431coap_send_q_block2(coap_session_t *session,
2432 coap_resource_t *resource,
2433 const coap_string_t *query,
2434 coap_pdu_code_t request_method,
2435 coap_block_b_t block,
2436 coap_pdu_t *response,
2437 coap_send_pdu_t send_response) {
2438 /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK2 */
2439 coap_lg_xmit_t *lg_xmit;
2440 coap_string_t empty = { 0, NULL};
2441
2442 LL_FOREACH(session->lg_xmit, lg_xmit) {
2443 if (lg_xmit->option == COAP_OPTION_Q_BLOCK2 &&
2444 resource == lg_xmit->b.b2.resource &&
2445 request_method == lg_xmit->b.b2.request_method &&
2446 coap_string_equal(query ? query : &empty,
2447 lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty))
2448 break;
2449 }
2450 return coap_send_q_blocks(session, lg_xmit, block, response, send_response);
2451}
2452#endif /* COAP_Q_BLOCK_SUPPORT */
2453#endif /* COAP_SERVER_SUPPORT */
2454
2455static void
2457 coap_lg_xmit_data_t *data_info) {
2458 if (!data_info)
2459 return;
2460 if (data_info->ref > 0) {
2461 data_info->ref--;
2462 return;
2463 }
2464 if (data_info->release_func) {
2465 coap_lock_callback(data_info->release_func(session,
2466 data_info->app_ptr));
2467 data_info->release_func = NULL;
2468 }
2469 coap_free_type(COAP_STRING, data_info);
2470}
2471
2472#if COAP_CLIENT_SUPPORT
2473#if COAP_Q_BLOCK_SUPPORT
2474/*
2475 * Send out a test PDU for Q-Block.
2476 */
2478coap_block_test_q_block(coap_session_t *session, coap_pdu_t *actual) {
2479 coap_pdu_t *pdu;
2480 uint8_t token[8];
2481 size_t token_len;
2482 uint8_t buf[4];
2483 coap_mid_t mid;
2484
2485#if NDEBUG
2486 (void)actual;
2487#endif /* NDEBUG */
2488 assert(session->block_mode & COAP_BLOCK_TRY_Q_BLOCK &&
2489 session->type == COAP_SESSION_TYPE_CLIENT &&
2490 COAP_PDU_IS_REQUEST(actual));
2491
2492 coap_log_debug("Testing for Q-Block support\n");
2493 /* RFC9177 Section 4.1 when checking if available */
2495 coap_new_message_id_lkd(session),
2497 if (!pdu) {
2498 return COAP_INVALID_MID;
2499 }
2500
2501 coap_session_new_token(session, &token_len, token);
2502 coap_add_token(pdu, token_len, token);
2503 /* Use a resource that the server MUST support (.well-known/core) */
2505 11, (const uint8_t *)".well-known");
2507 4, (const uint8_t *)"core");
2508 /*
2509 * M needs to be unset as 'asking' for only the first block using
2510 * Q-Block2 as a test for server support.
2511 * See RFC9177 Section 4.4 Using the Q-Block2 Option.
2512 *
2513 * As the client is asking for 16 byte chunks, it is unlikely that
2514 * the .well-known/core response will be 16 bytes or less, so
2515 * if the server supports Q-Block, it will be forced to respond with
2516 * a Q-Block2, so the client can detect the server Q-Block support.
2517 */
2519 coap_encode_var_safe(buf, sizeof(buf),
2520 (0 << 4) | (0 << 3) | 0),
2521 buf);
2522 set_block_mode_probe_q(session->block_mode);
2523 mid = coap_send_internal(session, pdu, NULL);
2524 if (mid == COAP_INVALID_MID)
2525 return COAP_INVALID_MID;
2526 session->remote_test_mid = mid;
2527 return mid;
2528}
2529#endif /* COAP_Q_BLOCK_SUPPORT */
2530
2533 coap_lg_xmit_t *lg_xmit) {
2534 coap_block_b_t block;
2535 coap_lg_crcv_t *lg_crcv;
2536 uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);
2537
2538 lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
2539
2540 if (lg_crcv == NULL)
2541 return NULL;
2542
2543 coap_log_debug("** %s: lg_crcv %p initialized - stateless token xxxxx%011llx\n",
2544 coap_session_str(session), (void *)lg_crcv,
2545 STATE_TOKEN_BASE(state_token));
2546 memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
2547 lg_crcv->initial = 1;
2548 coap_ticks(&lg_crcv->last_used);
2549 /* Keep a copy of the sent pdu */
2550 lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu);
2551 if (lg_xmit) {
2552 coap_opt_iterator_t opt_iter;
2553 coap_opt_t *opt;
2554
2555 opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter);
2556
2557 if (opt) {
2558 int observe_action;
2559
2560 observe_action = coap_decode_var_bytes(coap_opt_value(opt),
2561 coap_opt_length(opt));
2562 if (observe_action == COAP_OBSERVE_ESTABLISH) {
2563 /* Need to keep information for Observe Cancel */
2564 size_t data_len;
2565 const uint8_t *data;
2566
2567 if (coap_get_data(pdu, &data_len, &data)) {
2568 if (data_len < lg_xmit->data_info->length) {
2569 lg_xmit->data_info->ref++;
2570 lg_crcv->obs_data = lg_xmit->data_info;
2571 }
2572 }
2573 }
2574 }
2575 }
2576
2577 /* Need to keep original token for updating response PDUs */
2578 lg_crcv->app_token = coap_new_binary(pdu->actual_token.length);
2579 if (!lg_crcv->app_token) {
2580 coap_block_delete_lg_crcv(session, lg_crcv);
2581 return NULL;
2582 }
2583 memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length);
2584
2585 /* Need to set up a base token for actual communications if retries needed */
2586 lg_crcv->retry_counter = 1;
2587 lg_crcv->state_token = state_token;
2588 coap_address_copy(&lg_crcv->upstream, &session->addr_info.remote);
2589
2590 if (pdu->code == COAP_REQUEST_CODE_FETCH) {
2591 coap_bin_const_t *new_token;
2592
2593 /* Need to save/restore Observe Token for large FETCH */
2594 new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token);
2595 if (new_token)
2596 coap_update_token(pdu, new_token->length, new_token->s);
2597 }
2598
2599 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
2600 /* In case it is there - must not be in continuing request PDUs */
2601 lg_crcv->o_block_option = COAP_OPTION_BLOCK1;
2602 lg_crcv->o_blk_size = block.aszx;
2603 }
2604
2605 return lg_crcv;
2606}
2607
2608void
2610 coap_lg_crcv_t *lg_crcv) {
2611 size_t i;
2612
2613#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2614 (void)session;
2615#endif
2616 if (lg_crcv == NULL)
2617 return;
2618
2620 if (lg_crcv->obs_data) {
2621 coap_block_release_lg_xmit_data(session, lg_crcv->obs_data);
2622 lg_crcv->obs_data = NULL;
2623 }
2624 coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream);
2625 coap_log_debug("** %s: lg_crcv %p released\n",
2626 coap_session_str(session), (void *)lg_crcv);
2627 coap_delete_binary(lg_crcv->app_token);
2628 for (i = 0; i < lg_crcv->obs_token_cnt; i++) {
2629 coap_delete_bin_const(lg_crcv->obs_token[i]);
2630 }
2632 coap_delete_pdu_lkd(lg_crcv->sent_pdu);
2633 coap_free_type(COAP_LG_CRCV, lg_crcv);
2634}
2635#endif /* COAP_CLIENT_SUPPORT */
2636
2637#if COAP_SERVER_SUPPORT
2638void
2640 coap_lg_srcv_t *lg_srcv) {
2641#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2642 (void)session;
2643#endif
2644 if (lg_srcv == NULL)
2645 return;
2646
2650 coap_log_debug("** %s: lg_srcv %p released\n",
2651 coap_session_str(session), (void *)lg_srcv);
2652 coap_free_type(COAP_LG_SRCV, lg_srcv);
2653}
2654#endif /* COAP_SERVER_SUPPORT */
2655
2656void
2658 coap_lg_xmit_t *lg_xmit) {
2659 if (lg_xmit == NULL)
2660 return;
2661
2662 coap_block_release_lg_xmit_data(session, lg_xmit->data_info);
2663 if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu))
2664 coap_delete_binary(lg_xmit->b.b1.app_token);
2665 else
2666 coap_delete_string(lg_xmit->b.b2.query);
2667 coap_delete_pdu_lkd(lg_xmit->sent_pdu);
2668
2669 coap_log_debug("** %s: lg_xmit %p released\n",
2670 coap_session_str(session), (void *)lg_xmit);
2671 coap_free_type(COAP_LG_XMIT, lg_xmit);
2672}
2673
2674#if COAP_SERVER_SUPPORT
2675typedef struct {
2676 uint32_t num;
2677 int is_continue;
2678} send_track;
2679
2680static int
2681add_block_send(uint32_t num, int is_continue, send_track *out_blocks,
2682 uint32_t *count, uint32_t max_count) {
2683 uint32_t i;
2684
2685 for (i = 0; i < *count && *count < max_count; i++) {
2686 if (num == out_blocks[i].num)
2687 return 0;
2688 else if (num < out_blocks[i].num) {
2689 if (*count - i > 1)
2690 memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
2691 out_blocks[i].num = num;
2692 out_blocks[i].is_continue = is_continue;
2693 (*count)++;
2694 return 1;
2695 }
2696 }
2697 if (*count < max_count) {
2698 out_blocks[i].num = num;
2699 out_blocks[i].is_continue = is_continue;
2700 (*count)++;
2701 return 1;
2702 }
2703 return 0;
2704}
2705
2706/*
2707 * Need to see if this is a request for the next block of a large body
2708 * transfer. If so, need to initiate the response with the next blocks
2709 * and not trouble the application.
2710 *
2711 * If additional responses needed, then these are expicitly sent out and
2712 * 'response' is updated to be the last response to be sent. There can be
2713 * multiple Q-Block2 in the request, as well as the 'Continue' Q-Block2
2714 * request.
2715 *
2716 * This is set up using coap_add_data_large_response_lkd()
2717 *
2718 * Server is sending a large data response to GET / observe (Block2)
2719 *
2720 * Return: 0 Call application handler
2721 * 1 Do not call application handler - just send the built response
2722 */
2723int
2725 coap_pdu_t *pdu,
2726 coap_pdu_t *response,
2727 coap_resource_t *resource,
2728 coap_string_t *query) {
2729 coap_lg_xmit_t *lg_xmit = NULL;
2730 coap_block_b_t block;
2731 coap_block_b_t alt_block;
2732 uint16_t block_opt = 0;
2733 send_track *out_blocks = NULL;
2734 const char *error_phrase;
2735 coap_opt_iterator_t opt_iter;
2736 size_t chunk;
2737 coap_opt_iterator_t opt_b_iter;
2738 coap_opt_t *option;
2739 uint32_t request_cnt, i;
2740 coap_opt_t *etag_opt = NULL;
2741 coap_pdu_t *out_pdu = response;
2742#if COAP_Q_BLOCK_SUPPORT
2743 size_t max_block;
2744
2745 /* Is client indicating that it supports Q_BLOCK2 ? */
2746 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
2747 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK))
2748 set_block_mode_has_q(session->block_mode);
2749 block_opt = COAP_OPTION_Q_BLOCK2;
2750 }
2751#endif /* COAP_Q_BLOCK_SUPPORT */
2752 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &alt_block)) {
2753 if (block_opt) {
2754 coap_log_warn("Block2 and Q-Block2 cannot be in the same request\n");
2755 coap_add_data(response, sizeof("Both Block2 and Q-Block2 invalid")-1,
2756 (const uint8_t *)"Both Block2 and Q-Block2 invalid");
2757 response->code = COAP_RESPONSE_CODE(400);
2758 goto skip_app_handler;
2759 }
2760 block = alt_block;
2761 block_opt = COAP_OPTION_BLOCK2;
2762 }
2763 if (block_opt == 0)
2764 return 0;
2765 if (block.num == 0) {
2766 /* Get a fresh copy of the data */
2767 return 0;
2768 }
2769 lg_xmit = coap_find_lg_xmit_response(session, pdu, resource, query);
2770 if (lg_xmit == NULL)
2771 return 0;
2772
2773#if COAP_Q_BLOCK_SUPPORT
2774 out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track) * COAP_MAX_PAYLOADS(session));
2775#else /* ! COAP_Q_BLOCK_SUPPORT */
2776 out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track));
2777#endif /* ! COAP_Q_BLOCK_SUPPORT */
2778 if (!out_blocks) {
2779 goto internal_issue;
2780 }
2781
2782 /* lg_xmit (response) found */
2783
2784 etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
2785 if (etag_opt) {
2786 /* There may be multiple ETag - need to check each one */
2787 coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
2788 while ((etag_opt = coap_option_next(&opt_iter))) {
2789 if (opt_iter.number == COAP_OPTION_ETAG) {
2790 uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
2791 coap_opt_length(etag_opt));
2792 if (etag == lg_xmit->b.b2.etag) {
2793 break;
2794 }
2795 }
2796 }
2797 if (!etag_opt) {
2798 /* Not a match - pass up to a higher level */
2799 return 0;
2800 }
2801 }
2802 out_pdu->code = lg_xmit->sent_pdu->code;
2803 coap_ticks(&lg_xmit->last_obs);
2804 lg_xmit->last_all_sent = 0;
2805
2806 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2807 if (block_opt) {
2808 if (block.bert) {
2809 coap_log_debug("found Block option, block is BERT, block nr. %u, M %d\n",
2810 block.num, block.m);
2811 } else {
2812 coap_log_debug("found Block option, block size is %u, block nr. %u, M %d\n",
2813 1 << (block.szx + 4), block.num, block.m);
2814 }
2815 if (block.bert == 0 && block.szx != lg_xmit->blk_size) {
2816 if (block.num == 0) {
2817 if ((lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
2818 /*
2819 * Recompute the block number of the previous packet given
2820 * the new block size
2821 */
2822 block.num = (uint32_t)(((lg_xmit->offset + chunk) >> (block.szx + 4)) - 1);
2823 lg_xmit->blk_size = block.szx;
2824 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2825 lg_xmit->offset = block.num * chunk;
2826 coap_log_debug("new Block size is %u, block number %u completed\n",
2827 1 << (block.szx + 4), block.num);
2828 } else {
2829 coap_log_debug("ignoring request to increase Block size, "
2830 "next block is not aligned on requested block size "
2831 "boundary. (%zu x %u mod %u = %zu (which is not 0)\n",
2832 lg_xmit->offset/chunk + 1, (1 << (lg_xmit->blk_size + 4)),
2833 (1 << (block.szx + 4)),
2834 (lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4)));
2835 }
2836 } else {
2837 coap_log_debug("ignoring request to change Block size from %u to %u\n",
2838 (1 << (lg_xmit->blk_size + 4)), (1 << (block.szx + 4)));
2839 block.szx = block.aszx = lg_xmit->blk_size;
2840 }
2841 }
2842 }
2843
2844 /*
2845 * Need to check if there are multiple Q-Block2 requests. If so, they
2846 * need to be sent out in order of requests with the final request being
2847 * handled as per singular Block 2 request.
2848 */
2849 request_cnt = 0;
2850#if COAP_Q_BLOCK_SUPPORT
2851 max_block = (lg_xmit->data_info->length + chunk - 1)/chunk;
2852#endif /* COAP_Q_BLOCK_SUPPORT */
2853 coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
2854 while ((option = coap_option_next(&opt_b_iter))) {
2855 uint32_t num;
2856 if (opt_b_iter.number != lg_xmit->option)
2857 continue;
2858 num = coap_opt_block_num(option);
2859 if (num > 0xFFFFF) /* 20 bits max for num */
2860 continue;
2861 if (block.aszx != COAP_OPT_BLOCK_SZX(option)) {
2862 coap_add_data(response,
2863 sizeof("Changing blocksize during request invalid")-1,
2864 (const uint8_t *)"Changing blocksize during request invalid");
2865 response->code = COAP_RESPONSE_CODE(400);
2866 goto skip_app_handler;
2867 }
2868#if COAP_Q_BLOCK_SUPPORT
2869 if (COAP_OPT_BLOCK_MORE(option) && lg_xmit->option == COAP_OPTION_Q_BLOCK2) {
2870 if ((num % COAP_MAX_PAYLOADS(session)) == 0) {
2871 if (num == 0) {
2872 /* This is a repeat request for everything - hmm */
2873 goto call_app_handler;
2874 }
2875 /* 'Continue' request */
2876 for (i = 0; i < COAP_MAX_PAYLOADS(session) &&
2877 num + i < max_block; i++) {
2878 add_block_send(num + i, 1, out_blocks, &request_cnt,
2879 COAP_MAX_PAYLOADS(session));
2880 lg_xmit->last_block = num + i;
2881 }
2882 } else {
2883 /* Requesting remaining payloads in this MAX_PAYLOADS */
2884 for (i = 0; i < COAP_MAX_PAYLOADS(session) -
2885 num % COAP_MAX_PAYLOADS(session) &&
2886 num + i < max_block; i++) {
2887 add_block_send(num + i, 0, out_blocks, &request_cnt,
2888 COAP_MAX_PAYLOADS(session));
2889 }
2890 }
2891 } else
2892 add_block_send(num, 0, out_blocks, &request_cnt,
2893 COAP_MAX_PAYLOADS(session));
2894#else /* ! COAP_Q_BLOCK_SUPPORT */
2895 add_block_send(num, 0, out_blocks, &request_cnt, 1);
2896 break;
2897#endif /* ! COAP_Q_BLOCK_SUPPORT */
2898 }
2899 if (request_cnt == 0) {
2900 /* Block2 or Q-Block2 not found - give them the first block */
2901 block.szx = lg_xmit->blk_size;
2902 lg_xmit->offset = 0;
2903 out_blocks[0].num = 0;
2904 out_blocks[0].is_continue = 0;
2905 request_cnt = 1;
2906 }
2907
2908 for (i = 0; i < request_cnt; i++) {
2909 uint8_t buf[8];
2910
2911 block.num = out_blocks[i].num;
2912 lg_xmit->offset = block.num * chunk;
2913
2914 if (i + 1 < request_cnt) {
2915 /* Need to set up a copy of the pdu to send */
2916 coap_opt_filter_t drop_options;
2917
2918 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
2919 if (block.num != 0)
2921 if (out_blocks[i].is_continue) {
2922 out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
2923 lg_xmit->sent_pdu->actual_token.length,
2924 lg_xmit->sent_pdu->actual_token.s, &drop_options);
2925 } else {
2926 out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, pdu->actual_token.length,
2927 pdu->actual_token.s, &drop_options);
2928 }
2929 if (!out_pdu) {
2930 goto internal_issue;
2931 }
2932 } else {
2933 if (out_blocks[i].is_continue)
2934 coap_update_token(response, lg_xmit->sent_pdu->actual_token.length,
2935 lg_xmit->sent_pdu->actual_token.s);
2936 /*
2937 * Copy the options across and then fix the block option
2938 *
2939 * Need to drop Observe option if Block2 and block.num != 0
2940 */
2941 coap_option_iterator_init(lg_xmit->sent_pdu, &opt_iter, COAP_OPT_ALL);
2942 while ((option = coap_option_next(&opt_iter))) {
2943 if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0)
2944 continue;
2945 if (!coap_insert_option(response, opt_iter.number,
2946 coap_opt_length(option),
2947 coap_opt_value(option))) {
2948 goto internal_issue;
2949 }
2950 }
2951 out_pdu = response;
2952 }
2953 if (pdu->type == COAP_MESSAGE_NON)
2954 out_pdu->type = COAP_MESSAGE_NON;
2955 if (block.bert) {
2956 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
2957 block.m = (lg_xmit->data_info->length - lg_xmit->offset) >
2958 ((out_pdu->max_size - token_options) /1024) * 1024;
2959 } else {
2960 block.m = (lg_xmit->offset + chunk) < lg_xmit->data_info->length;
2961 }
2962 if (!coap_update_option(out_pdu, lg_xmit->option,
2964 sizeof(buf),
2965 (block.num << 4) |
2966 (block.m << 3) |
2967 block.aszx),
2968 buf)) {
2969 goto internal_issue;
2970 }
2971 if (!(lg_xmit->offset + chunk < lg_xmit->data_info->length)) {
2972 /* Last block - keep in cache for 4 * ACK_TIMOUT */
2973 coap_ticks(&lg_xmit->last_all_sent);
2974 }
2975 if (lg_xmit->b.b2.maxage_expire) {
2976 coap_tick_t now;
2977 coap_time_t rem;
2978
2979 if (!(lg_xmit->offset + chunk < lg_xmit->data_info->length)) {
2980 /* Last block - keep in cache for 4 * ACK_TIMOUT */
2981 coap_ticks(&lg_xmit->last_all_sent);
2982 }
2983 coap_ticks(&now);
2984 rem = coap_ticks_to_rt(now);
2985 if (lg_xmit->b.b2.maxage_expire > rem) {
2986 rem = lg_xmit->b.b2.maxage_expire - rem;
2987 } else {
2988 rem = 0;
2989 /* Entry needs to be expired */
2990 coap_ticks(&lg_xmit->last_all_sent);
2991 }
2994 sizeof(buf),
2995 rem),
2996 buf)) {
2997 goto internal_issue;
2998 }
2999 }
3000
3001 if (!coap_add_block_b_data(out_pdu,
3002 lg_xmit->data_info->length,
3003 lg_xmit->data_info->data,
3004 &block)) {
3005 goto internal_issue;
3006 }
3007 if (i + 1 < request_cnt) {
3008 coap_ticks(&lg_xmit->last_sent);
3009 coap_send_internal(session, out_pdu, NULL);
3010 }
3011 }
3012 coap_ticks(&lg_xmit->last_payload);
3013 goto skip_app_handler;
3014#if COAP_Q_BLOCK_SUPPORT
3015call_app_handler:
3016 coap_free_type(COAP_STRING, out_blocks);
3017 return 0;
3018#endif /* COAP_Q_BLOCK_SUPPORT */
3019
3020internal_issue:
3021 response->code = COAP_RESPONSE_CODE(500);
3022 error_phrase = coap_response_phrase(response->code);
3023 coap_add_data(response, strlen(error_phrase),
3024 (const uint8_t *)error_phrase);
3025 /* Keep in cache for 4 * ACK_TIMOUT incase of retry */
3026 if (lg_xmit)
3027 coap_ticks(&lg_xmit->last_all_sent);
3028
3029skip_app_handler:
3030 coap_free_type(COAP_STRING, out_blocks);
3031 return 1;
3032}
3033#endif /* COAP_SERVER_SUPPORT */
3034
3035static int
3036update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m) {
3037 uint32_t i;
3038
3039 if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) {
3040 /* received block number greater than Block No defined when More bit unset */
3041 return 0;
3042 }
3043
3044 /* Reset as there is activity */
3045 rec_blocks->retry = 0;
3046
3047 for (i = 0; i < rec_blocks->used; i++) {
3048 if (block_num >= rec_blocks->range[i].begin &&
3049 block_num <= rec_blocks->range[i].end)
3050 break;
3051
3052 if (block_num < rec_blocks->range[i].begin) {
3053 if (block_num + 1 == rec_blocks->range[i].begin) {
3054 rec_blocks->range[i].begin = block_num;
3055 } else {
3056 /* Need to insert a new range */
3057 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
3058 /* Too many losses */
3059 return 0;
3060 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
3061 (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3062 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
3063 rec_blocks->used++;
3064 }
3065 break;
3066 }
3067 if (block_num == rec_blocks->range[i].end + 1) {
3068 rec_blocks->range[i].end = block_num;
3069 if (i + 1 < rec_blocks->used) {
3070 if (rec_blocks->range[i+1].begin == block_num + 1) {
3071 /* Merge the 2 ranges */
3072 rec_blocks->range[i].end = rec_blocks->range[i+1].end;
3073 if (i+2 < rec_blocks->used) {
3074 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i+2],
3075 (rec_blocks->used - (i+2)) * sizeof(rec_blocks->range[0]));
3076 }
3077 rec_blocks->used--;
3078 }
3079 }
3080 break;
3081 }
3082 }
3083 if (i == rec_blocks->used) {
3084 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
3085 /* Too many losses */
3086 return 0;
3087 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
3088 rec_blocks->used++;
3089 }
3090 if (!block_m)
3091 rec_blocks->total_blocks = block_num + 1;
3092
3093 coap_ticks(&rec_blocks->last_seen);
3094 return 1;
3095}
3096
3097#if COAP_SERVER_SUPPORT
3098/*
3099 * Need to check if this is a large PUT / POST etc. using multiple blocks
3100 *
3101 * Server receiving PUT/POST etc. of a large amount of data (Block1)
3102 *
3103 * Return: 0 Call application handler
3104 * 1 Do not call application handler - just send the built response
3105 */
3106int
3108 coap_session_t *session,
3109 coap_pdu_t *pdu,
3110 coap_pdu_t *response,
3111 coap_resource_t *resource,
3112 coap_string_t *uri_path,
3113 coap_opt_t *observe,
3114 int *added_block,
3115 coap_lg_srcv_t **pfree_lg_srcv) {
3116 size_t length = 0;
3117 const uint8_t *data = NULL;
3118 size_t offset = 0;
3119 size_t total = 0;
3120 coap_block_b_t block;
3121 coap_opt_iterator_t opt_iter;
3122 uint16_t block_option = 0;
3123 coap_lg_srcv_t *lg_srcv;
3124 coap_opt_t *size_opt;
3125 coap_opt_t *fmt_opt;
3126 uint16_t fmt;
3127 coap_opt_t *rtag_opt;
3128 size_t rtag_length;
3129 const uint8_t *rtag;
3130 uint32_t max_block_szx;
3131 int update_data;
3132 unsigned int saved_num;
3133 size_t saved_offset;
3134
3135 *added_block = 0;
3136 *pfree_lg_srcv = NULL;
3137 coap_get_data_large(pdu, &length, &data, &offset, &total);
3138 pdu->body_offset = 0;
3139 pdu->body_total = length;
3140
3141 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
3142 block_option = COAP_OPTION_BLOCK1;
3143#if COAP_Q_BLOCK_SUPPORT
3144 if (coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter)) {
3145 /* Cannot handle Q-Block1 as well */
3146 coap_add_data(response, sizeof("Block1 + Q-Block1 together")-1,
3147 (const uint8_t *)"Block1 + Q-Block1 together");
3148 response->code = COAP_RESPONSE_CODE(402);
3149 goto skip_app_handler;
3150 }
3151#endif /* COAP_Q_BLOCK_SUPPORT */
3152 }
3153#if COAP_Q_BLOCK_SUPPORT
3154 else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
3155 block_option = COAP_OPTION_Q_BLOCK1;
3156 set_block_mode_has_q(session->block_mode);
3157 }
3158#endif /* COAP_Q_BLOCK_SUPPORT */
3159 if (!block_option ||
3160 (block_option == COAP_OPTION_BLOCK1 && block.num == 0 && block.m == 0)) {
3161 /* Not blocked, or a single block */
3162 goto call_app_handler;
3163 }
3164
3165 size_opt = coap_check_option(pdu,
3167 &opt_iter);
3168 fmt_opt = coap_check_option(pdu,
3170 &opt_iter);
3171 fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
3172 coap_opt_length(fmt_opt)) :
3174 rtag_opt = coap_check_option(pdu,
3176 &opt_iter);
3177 rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
3178 rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
3179
3180 if (length > block.chunk_size) {
3181 coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n",
3182 block.chunk_size, length);
3183 length = block.chunk_size;
3184 } else if (!block.bert && block.m && length != block.chunk_size) {
3185 coap_log_info("block: Undersized packet chunk %"PRIu32" got %zu\n",
3186 block.chunk_size, length);
3187 response->code = COAP_RESPONSE_CODE(400);
3188 goto skip_app_handler;
3189 }
3190 total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
3191 coap_opt_length(size_opt)) : 0;
3192 offset = block.num << (block.szx + 4);
3193
3194 if (!(session->block_mode &
3195#if COAP_Q_BLOCK_SUPPORT
3197#else /* COAP_Q_BLOCK_SUPPORT */
3199#endif /* COAP_Q_BLOCK_SUPPORT */
3200 && !block.bert) {
3201 uint8_t buf[4];
3202
3203 /* Ask for the next block */
3204 coap_insert_option(response, block_option,
3205 coap_encode_var_safe(buf, sizeof(buf),
3206 (block.num << 4) |
3207 (block.m << 3) |
3208 block.aszx),
3209 buf);
3210 /* Not re-assembling or checking for receipt order */
3211 pdu->body_data = data;
3212 pdu->body_length = length;
3213 pdu->body_offset = offset;
3214 if (total < (length + offset + (block.m ? 1 : 0)))
3215 total = length + offset + (block.m ? 1 : 0);
3216 pdu->body_total = total;
3217 *added_block = block.m;
3218 /* The application is responsible for returning the correct 2.01/2.04/2.31 etc. */
3219 goto call_app_handler;
3220 }
3221
3222 /*
3223 * locate the lg_srcv
3224 */
3225 LL_FOREACH(session->lg_srcv, lg_srcv) {
3226 if (rtag_opt || lg_srcv->rtag_set == 1) {
3227 if (!(rtag_opt && lg_srcv->rtag_set == 1))
3228 continue;
3229 if (lg_srcv->rtag_length != rtag_length ||
3230 memcmp(lg_srcv->rtag, rtag, rtag_length) != 0)
3231 continue;
3232 }
3233 if (resource == lg_srcv->resource) {
3234 break;
3235 }
3236 if ((lg_srcv->resource == context->unknown_resource ||
3237 resource == context->proxy_uri_resource) &&
3238 coap_string_equal(uri_path, lg_srcv->uri_path))
3239 break;
3240 }
3241
3242 if (!lg_srcv && block.num != 0 && session->block_mode & COAP_BLOCK_NOT_RANDOM_BLOCK1) {
3243 coap_add_data(response, sizeof("Missing block 0")-1,
3244 (const uint8_t *)"Missing block 0");
3245 response->code = COAP_RESPONSE_CODE(408);
3246 goto skip_app_handler;
3247 }
3248
3249 if (!lg_srcv) {
3250 /* Allocate lg_srcv to use for tracking */
3251 lg_srcv = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t));
3252 if (lg_srcv == NULL) {
3253 coap_add_data(response, sizeof("Memory issue")-1,
3254 (const uint8_t *)"Memory issue");
3255 response->code = COAP_RESPONSE_CODE(500);
3256 goto skip_app_handler;
3257 }
3258 coap_log_debug("** %s: lg_srcv %p initialized\n",
3259 coap_session_str(session), (void *)lg_srcv);
3260 memset(lg_srcv, 0, sizeof(coap_lg_srcv_t));
3261 lg_srcv->resource = resource;
3262 if (resource == context->unknown_resource ||
3263 resource == context->proxy_uri_resource)
3264 lg_srcv->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
3265 lg_srcv->content_format = fmt;
3266 lg_srcv->total_len = total;
3267 max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3268 if (!block.bert && block.num == 0 && max_block_szx != 0 &&
3269 max_block_szx < block.szx) {
3270 lg_srcv->szx = max_block_szx;
3271 } else {
3272 lg_srcv->szx = block.szx;
3273 }
3274 lg_srcv->block_option = block_option;
3275 if (observe) {
3276 lg_srcv->observe_length = min(coap_opt_length(observe), 3);
3277 memcpy(lg_srcv->observe, coap_opt_value(observe), lg_srcv->observe_length);
3278 lg_srcv->observe_set = 1;
3279 }
3280 if (rtag_opt) {
3281 lg_srcv->rtag_length = coap_opt_length(rtag_opt);
3282 memcpy(lg_srcv->rtag, coap_opt_value(rtag_opt), lg_srcv->rtag_length);
3283 lg_srcv->rtag_set = 1;
3284 }
3285 lg_srcv->body_data = NULL;
3286 LL_PREPEND(session->lg_srcv, lg_srcv);
3287 }
3288 coap_ticks(&lg_srcv->last_used);
3289
3290 if (block_option == COAP_OPTION_BLOCK1 &&
3292 !check_if_next_block(&lg_srcv->rec_blocks, block.num)) {
3293 coap_add_data(response, sizeof("Missing interim block")-1,
3294 (const uint8_t *)"Missing interim block");
3295 response->code = COAP_RESPONSE_CODE(408);
3296 goto skip_app_handler;
3297 }
3298
3299 if (fmt != lg_srcv->content_format) {
3300 coap_add_data(response, sizeof("Content-Format mismatch")-1,
3301 (const uint8_t *)"Content-Format mismatch");
3302 response->code = COAP_RESPONSE_CODE(408);
3303 goto free_lg_srcv;
3304 }
3305
3306#if COAP_Q_BLOCK_SUPPORT
3307 if (block_option == COAP_OPTION_Q_BLOCK1) {
3308 if (total != lg_srcv->total_len) {
3309 coap_add_data(response, sizeof("Size1 mismatch")-1,
3310 (const uint8_t *)"Size1 mismatch");
3311 response->code = COAP_RESPONSE_CODE(408);
3312 goto free_lg_srcv;
3313 }
3316 pdu->actual_token.length);
3317 }
3318#endif /* COAP_Q_BLOCK_SUPPORT */
3319
3320 lg_srcv->last_mid = pdu->mid;
3321 lg_srcv->last_type = pdu->type;
3322
3323 update_data = 0;
3324 saved_num = block.num;
3325 saved_offset = offset;
3326
3327 while (offset < saved_offset + length) {
3328 if (!check_if_received_block(&lg_srcv->rec_blocks, block.num)) {
3329 /* Update list of blocks received */
3330 if (!update_received_blocks(&lg_srcv->rec_blocks, block.num, block.m)) {
3332 coap_add_data(response, sizeof("Too many missing blocks")-1,
3333 (const uint8_t *)"Too many missing blocks");
3334 response->code = COAP_RESPONSE_CODE(408);
3335 goto free_lg_srcv;
3336 }
3337 update_data = 1;
3338 }
3339 block.num++;
3340 offset = block.num << (block.szx + 4);
3341 }
3342 block.num--;
3343
3344 if (update_data) {
3345 /* Update saved data */
3346#if COAP_Q_BLOCK_SUPPORT
3347 lg_srcv->rec_blocks.processing_payload_set =
3348 block.num / COAP_MAX_PAYLOADS(session);
3349#endif /* COAP_Q_BLOCK_SUPPORT */
3350 if (lg_srcv->total_len < saved_offset + length) {
3351 lg_srcv->total_len = saved_offset + length;
3352 }
3353 if (context && context->block_data_handler && !resource->is_proxy_uri &&
3354 !resource->is_reverse_proxy &&
3355 ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) &&
3357 coap_response_t resp;
3358 resp = context->block_data_handler(session, pdu, resource, &lg_srcv->body_data,
3359 length, data, saved_offset,
3360 lg_srcv->total_len);
3361 if (resp != COAP_RESPONSE_OK) {
3362 response->code = COAP_RESPONSE_CODE(500);
3363 goto skip_app_handler;
3364 }
3365 } else {
3366 lg_srcv->body_data = coap_block_build_body(lg_srcv->body_data, length, data,
3367 saved_offset, lg_srcv->total_len);
3368 if (!lg_srcv->body_data) {
3369 coap_add_data(response, sizeof("Memory issue")-1,
3370 (const uint8_t *)"Memory issue");
3371 response->code = COAP_RESPONSE_CODE(500);
3372 goto skip_app_handler;
3373 }
3374 }
3375 }
3376
3377 if (block.m ||
3378 !check_all_blocks_in(&lg_srcv->rec_blocks)) {
3379 /* Not all the payloads of the body have arrived */
3380 if (block.m) {
3381 uint8_t buf[4];
3382
3383#if COAP_Q_BLOCK_SUPPORT
3384 if (block_option == COAP_OPTION_Q_BLOCK1) {
3385 if (check_all_blocks_in(&lg_srcv->rec_blocks)) {
3386 goto give_app_data;
3387 }
3388 if (lg_srcv->rec_blocks.used == 1 &&
3389 (lg_srcv->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1
3390 == COAP_MAX_PAYLOADS(session)) {
3391 /* Blocks could arrive in wrong order */
3392 block.num = lg_srcv->rec_blocks.range[0].end;
3393 } else {
3394 /* The remote end will be sending the next one unless this
3395 is a MAX_PAYLOADS and all previous have been received */
3396 goto skip_app_handler;
3397 }
3398 if (COAP_PROTO_RELIABLE(session->proto) ||
3399 pdu->type != COAP_MESSAGE_NON)
3400 goto skip_app_handler;
3401 }
3402#endif /* COAP_Q_BLOCK_SUPPORT */
3403
3404 /* Check to see if block size is getting forced down */
3405 max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3406 if (!block.bert && saved_num == 0 && max_block_szx != 0 &&
3407 max_block_szx < block.aszx) {
3408 block.aszx = max_block_szx;
3409 }
3410
3411 /*
3412 * If the last block has been seen, packets are coming in in
3413 * random order. If all blocks are now in, then need to send
3414 * complete payload to application and acknowledge this current
3415 * block.
3416 */
3417 if ((total == 0 && block.m) || !check_all_blocks_in(&lg_srcv->rec_blocks)) {
3418 /* Ask for the next block */
3419 coap_insert_option(response, block_option,
3420 coap_encode_var_safe(buf, sizeof(buf),
3421 (saved_num << 4) |
3422 (block.m << 3) |
3423 block.aszx),
3424 buf);
3425 response->code = COAP_RESPONSE_CODE(231);
3426 } else {
3427 /* Need to separately respond to this request */
3428 coap_pdu_t *tmp_pdu = coap_pdu_duplicate_lkd(response,
3429 session,
3430 response->actual_token.length,
3431 response->actual_token.s,
3432 NULL);
3433 if (tmp_pdu) {
3434 tmp_pdu->code = COAP_RESPONSE_CODE(231);
3435 coap_send_internal(session, tmp_pdu, NULL);
3436 }
3437 if (lg_srcv->last_token) {
3438 coap_update_token(response, lg_srcv->last_token->length, lg_srcv->last_token->s);
3439 coap_update_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
3440 }
3441 /* Pass the assembled pdu and body to the application */
3442 goto give_app_data;
3443 }
3444 } else {
3445 /* block.m Block More option not set. Some outstanding blocks */
3446#if COAP_Q_BLOCK_SUPPORT
3447 if (block_option != COAP_OPTION_Q_BLOCK1) {
3448#endif /* COAP_Q_BLOCK_SUPPORT */
3449 /* Last chunk - but not all in */
3450 coap_ticks(&lg_srcv->last_used);
3451 lg_srcv->no_more_seen = 1;
3454 pdu->actual_token.length);
3455
3456 /*
3457 * Need to just ACK (no response code) to handle client's NSTART.
3458 * When final missing block comes in, we will pass all the data
3459 * for processing so a 2.01, 2.04 etc. code can be generated
3460 * and responded to as a separate response "RFC7252 5.2.2. Separate"
3461 * If missing block(s) do not come in, then will generate a 4.08
3462 * when lg_srcv times out.
3463 * Fall through to skip_app_handler.
3464 */
3465#if COAP_Q_BLOCK_SUPPORT
3466 }
3467#endif /* COAP_Q_BLOCK_SUPPORT */
3468 }
3469 goto skip_app_handler;
3470 }
3471
3472 /*
3473 * Entire payload received.
3474 * Remove the Block1 option as passing all of the data to
3475 * application layer. Add back in observe option if appropriate.
3476 * Adjust all other information.
3477 */
3478give_app_data:
3479 if (lg_srcv->observe_set) {
3481 lg_srcv->observe_length, lg_srcv->observe);
3482 }
3483 coap_remove_option(pdu, block_option);
3484 if (lg_srcv->body_data) {
3485 pdu->body_data = lg_srcv->body_data->s;
3486 pdu->body_length = lg_srcv->total_len;
3487 } else {
3488 pdu->body_data = NULL;
3489 pdu->body_length = 0;
3490 }
3491 pdu->body_offset = 0;
3492 pdu->body_total = lg_srcv->total_len;
3493 if (context && context->block_data_handler && !resource->is_proxy_uri &&
3494 !resource->is_reverse_proxy &&
3495 ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) &&
3497 /* Data has already been provided - do not duplicate */
3498 if (pdu->data) {
3499 pdu->used_size = pdu->data - pdu->token - 1;
3500 pdu->data = NULL;
3501 }
3502 }
3503 coap_log_debug("Server app version of updated PDU\n");
3505 lg_srcv->dont_timeout = 1;
3506 *pfree_lg_srcv = lg_srcv;
3507
3508call_app_handler:
3509 return 0;
3510
3511free_lg_srcv:
3512 LL_DELETE(session->lg_srcv, lg_srcv);
3513 coap_block_delete_lg_srcv(session, lg_srcv);
3514
3515skip_app_handler:
3516 return 1;
3517}
3518#endif /* COAP_SERVER_SUPPORT */
3519
3520#if COAP_CLIENT_SUPPORT
3521#if COAP_Q_BLOCK_SUPPORT
3522static uint32_t
3523derive_cbor_value(const uint8_t **bp, size_t rem_len) {
3524 uint32_t value = **bp & 0x1f;
3525 (*bp)++;
3526 if (value < 24) {
3527 return value;
3528 } else if (value == 24) {
3529 if (rem_len < 2)
3530 return (uint32_t)-1;
3531 value = **bp;
3532 (*bp)++;
3533 return value;
3534 } else if (value == 25) {
3535 if (rem_len < 3)
3536 return (uint32_t)-1;
3537 value = **bp << 8;
3538 (*bp)++;
3539 value |= **bp;
3540 (*bp)++;
3541 return value;
3542 }
3543 if (rem_len < 4)
3544 return (uint32_t)-1;
3545 value = **bp << 24;
3546 (*bp)++;
3547 value |= **bp << 16;
3548 (*bp)++;
3549 value |= **bp << 8;
3550 (*bp)++;
3551 value |= **bp;
3552 (*bp)++;
3553 return value;
3554}
3555#endif /* COAP_Q_BLOCK_SUPPORT */
3556
3557static int
3558check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
3559 coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) {
3560 /* Check for Echo option for freshness */
3561 coap_opt_iterator_t opt_iter;
3562 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
3563
3564 if (opt) {
3565 if (sent || lg_xmit || lg_crcv) {
3566 /* Need to retransmit original request with Echo option added */
3567 coap_pdu_t *echo_pdu;
3568 coap_mid_t mid;
3569 const uint8_t *data;
3570 size_t data_len;
3571 int have_data = 0;
3572 uint8_t ltoken[8];
3573 size_t ltoken_len;
3574 uint64_t token;
3575
3576 if (sent) {
3577 if (coap_get_data(sent, &data_len, &data))
3578 have_data = 1;
3579 } else if (lg_xmit) {
3580 sent = lg_xmit->sent_pdu;
3581 if (lg_xmit->data_info->length) {
3582 size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
3583 size_t offset = (lg_xmit->last_block + 1) * blk_size;
3584 have_data = 1;
3585 data = &lg_xmit->data_info->data[offset];
3586 data_len = (lg_xmit->data_info->length - offset) > blk_size ? blk_size :
3587 lg_xmit->data_info->length - offset;
3588 }
3589 } else { /* lg_crcv */
3590 sent = lg_crcv->sent_pdu;
3591 if (coap_get_data(sent, &data_len, &data))
3592 have_data = 1;
3593 }
3594 if (lg_xmit) {
3595 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
3596 ++lg_xmit->b.b1.count);
3597 } else {
3598 token = STATE_TOKEN_FULL(lg_crcv->state_token,
3599 ++lg_crcv->retry_counter);
3600 }
3601 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
3602 echo_pdu = coap_pdu_duplicate_lkd(sent, session, ltoken_len, ltoken, NULL);
3603 if (!echo_pdu)
3604 return 0;
3605 if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
3606 coap_opt_length(opt), coap_opt_value(opt)))
3607 goto not_sent;
3608 if (have_data) {
3609 coap_add_data(echo_pdu, data_len, data);
3610 }
3611 /* Need to track Observe token change if Observe */
3612 track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token);
3613#if COAP_OSCORE_SUPPORT
3614 if (session->oscore_encryption &&
3615 (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) &&
3617 /* Need to update the base PDU's Token for closing down Observe */
3618 if (lg_xmit) {
3619 lg_xmit->b.b1.state_token = token;
3620 } else {
3621 lg_crcv->state_token = token;
3622 }
3623 }
3624#endif /* COAP_OSCORE_SUPPORT */
3625 mid = coap_send_internal(session, echo_pdu, NULL);
3626 if (mid == COAP_INVALID_MID)
3627 goto not_sent;
3628 return 1;
3629 } else {
3630 /* Need to save Echo option value to add to next reansmission */
3631not_sent:
3632 coap_delete_bin_const(session->echo);
3633 session->echo = coap_new_bin_const(coap_opt_value(opt),
3634 coap_opt_length(opt));
3635 }
3636 }
3637 return 0;
3638}
3639
3640static void
3641track_echo(coap_session_t *session, coap_pdu_t *rcvd) {
3642 coap_opt_iterator_t opt_iter;
3643 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
3644
3645 if (opt) {
3646 coap_delete_bin_const(session->echo);
3647 session->echo = coap_new_bin_const(coap_opt_value(opt),
3648 coap_opt_length(opt));
3649 }
3650}
3651
3652/*
3653 * Need to see if this is a response to a large body request transfer. If so,
3654 * need to initiate the request containing the next block and not trouble the
3655 * application. Note that Token must unique per request/response.
3656 *
3657 * Client receives large data acknowledgement from server (Block1)
3658 *
3659 * This is set up using coap_add_data_large_request_lkd()
3660 *
3661 * Client is using GET etc.
3662 *
3663 * Return: 0 Call application handler
3664 * 1 Do not call application handler - just send the built response
3665 */
3666int
3668 coap_pdu_t *rcvd) {
3669 coap_lg_xmit_t *lg_xmit;
3670 coap_lg_crcv_t *lg_crcv = NULL;
3671
3672 lg_xmit = coap_find_lg_xmit(session, rcvd);
3673 if (lg_xmit) {
3674 /* lg_xmit found */
3675 size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
3676 coap_block_b_t block;
3677
3678 lg_crcv = coap_find_lg_crcv(session, rcvd);
3679 if (lg_crcv)
3680 coap_ticks(&lg_crcv->last_used);
3681
3682 if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
3683 coap_get_block_b(session, rcvd, lg_xmit->option, &block)) {
3684
3685 if (block.bert) {
3686 coap_log_debug("found Block option, block is BERT, block nr. %u (%zu)\n",
3687 block.num, lg_xmit->b.b1.bert_size);
3688 } else {
3689 coap_log_debug("found Block option, block size is %u, block nr. %u\n",
3690 1 << (block.szx + 4), block.num);
3691 }
3692 if (block.szx != lg_xmit->blk_size) {
3693 if (block.szx > lg_xmit->blk_size) {
3694 coap_log_info("ignoring request to increase Block size, "
3695 "(%u > %u)\n",
3696 1 << (block.szx + 4), 1 << (lg_xmit->blk_size + 4));
3697 } else if ((lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
3698 /*
3699 * Recompute the block number of the previous packet given the
3700 * new block size
3701 */
3702 block.num = (uint32_t)(((lg_xmit->offset + chunk) >> (block.szx + 4)) - 1);
3703 lg_xmit->blk_size = block.szx;
3704 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
3705 lg_xmit->offset = block.num * chunk;
3706 coap_log_debug("new Block size is %u, block number %u completed\n",
3707 1 << (block.szx + 4), block.num);
3708 block.bert = 0;
3709 block.aszx = block.szx;
3710 } else {
3711 coap_log_debug("ignoring request to increase Block size, "
3712 "next block is not aligned on requested block size boundary. "
3713 "(%zu x %u mod %u = %zu != 0)\n",
3714 lg_xmit->offset/chunk + 1, (1 << (lg_xmit->blk_size + 4)),
3715 (1 << (block.szx + 4)),
3716 (lg_xmit->offset + chunk) % ((size_t)1 << (block.szx + 4)));
3717 }
3718 }
3719 track_echo(session, rcvd);
3720 if (lg_xmit->last_block == (int)block.num &&
3721 lg_xmit->option != COAP_OPTION_Q_BLOCK1) {
3722 /*
3723 * Duplicate Block1 ACK
3724 *
3725 * RFCs not clear here, but on a lossy connection, there could
3726 * be multiple Block1 ACKs, causing the client to retransmit the
3727 * same block multiple times, or the server retransmitting the
3728 * same ACK.
3729 *
3730 * Once a block has been ACKd, there is no need to retransmit it.
3731 */
3732 return 1;
3733 }
3734 if (block.bert)
3735 block.num += (unsigned int)(lg_xmit->b.b1.bert_size / 1024 - 1);
3736 lg_xmit->last_block = block.num;
3737 lg_xmit->offset = (block.num + 1) * chunk;
3738 if (lg_xmit->offset < lg_xmit->data_info->length) {
3739 /* Build the next PDU request based off the skeletal PDU */
3740 uint8_t buf[8];
3741 coap_pdu_t *pdu;
3742 uint64_t token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, ++lg_xmit->b.b1.count);
3743 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
3744
3745 if (lg_xmit->sent_pdu->code == COAP_REQUEST_CODE_FETCH) {
3746 /* Need to handle Observe for large FETCH */
3747 if (lg_crcv) {
3748 if (coap_binary_equal(lg_xmit->b.b1.app_token, lg_crcv->app_token)) {
3749 coap_bin_const_t *new_token;
3750 coap_bin_const_t ctoken = { len, buf };
3751
3752 /* Need to save/restore Observe Token for large FETCH */
3753 new_token = track_fetch_observe(lg_xmit->sent_pdu, lg_crcv, block.num + 1,
3754 &ctoken);
3755 if (new_token) {
3756 assert(len <= sizeof(buf));
3757 len = new_token->length;
3758 memcpy(buf, new_token->s, len);
3759 }
3760 }
3761 }
3762 }
3763 pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, len, buf, NULL);
3764 if (!pdu)
3765 goto fail_body;
3766
3767 /*
3768 * If initial transmit was multicast, that would have been NON.
3769 * Make subsequent traffic CON for reliability.
3770 */
3771 if (session->sock.flags & COAP_SOCKET_MULTICAST) {
3772 pdu->type = COAP_MESSAGE_CON;
3773 }
3774
3775 block.num++;
3776 if (block.bert) {
3777 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
3778 pdu->used_size;
3779 block.m = (lg_xmit->data_info->length - lg_xmit->offset) >
3780 ((pdu->max_size - token_options) /1024) * 1024;
3781 } else {
3782 block.m = (lg_xmit->offset + chunk) < lg_xmit->data_info->length;
3783 }
3784 coap_update_option(pdu, lg_xmit->option,
3785 coap_encode_var_safe(buf, sizeof(buf),
3786 (block.num << 4) |
3787 (block.m << 3) |
3788 block.aszx),
3789 buf);
3790
3791 if (lg_xmit->data_info->get_func) {
3792#if COAP_CONSTRAINED_STACK
3793 /* Protected by global_lock if needed */
3794 static uint8_t l_data[1024];
3795#else /* ! COAP_CONSTRAINED_STACK */
3796 uint8_t l_data[1024];
3797#endif /* ! COAP_CONSTRAINED_STACK */
3798 size_t l_length;
3799
3800 assert(chunk <= 1024);
3801 if (lg_xmit->data_info->get_func(session, chunk,
3802 block.num * chunk, l_data, &l_length,
3803 lg_xmit->data_info->app_ptr)) {
3804 if (!coap_add_data(pdu, l_length, l_data)) {
3805 goto fail_body;
3806 }
3807 }
3808 } else {
3809 if (!coap_add_block_b_data(pdu,
3810 lg_xmit->data_info->length,
3811 lg_xmit->data_info->data,
3812 &block))
3813 goto fail_body;
3814 }
3815 lg_xmit->b.b1.bert_size = block.chunk_size;
3816 coap_ticks(&lg_xmit->last_sent);
3817#if COAP_Q_BLOCK_SUPPORT
3818 if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 &&
3819 pdu->type == COAP_MESSAGE_NON) {
3820 if (coap_send_q_block1(session, block, pdu,
3821 COAP_SEND_INC_PDU) == COAP_INVALID_MID)
3822 goto fail_body;
3823 return 1;
3824 } else if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
3825 goto fail_body;
3826#else /* ! COAP_Q_BLOCK_SUPPORT */
3827 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
3828 goto fail_body;
3829#endif /* ! COAP_Q_BLOCK_SUPPORT */
3830 return 1;
3831 }
3832 } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
3833 /*
3834 * Not a block response asking for the next block.
3835 * Could be an Observe response overlapping with block FETCH doing
3836 * Observe cancellation.
3837 */
3838 coap_opt_iterator_t opt_iter;
3839 coap_opt_t *obs_opt;
3840 int observe_action = -1;
3841
3842 if (lg_xmit->sent_pdu->code != COAP_REQUEST_CODE_FETCH) {
3843 goto lg_xmit_finished;
3844 }
3845 obs_opt = coap_check_option(lg_xmit->sent_pdu,
3847 &opt_iter);
3848 if (obs_opt) {
3849 observe_action = coap_decode_var_bytes(coap_opt_value(obs_opt),
3850 coap_opt_length(obs_opt));
3851 }
3852 if (observe_action != COAP_OBSERVE_CANCEL) {
3853 goto lg_xmit_finished;
3854 }
3855 obs_opt = coap_check_option(rcvd,
3857 &opt_iter);
3858 if (obs_opt) {
3859 return 0;
3860 }
3861 goto lg_xmit_finished;
3862 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
3863 if (check_freshness(session, rcvd, sent, lg_xmit, NULL))
3864 return 1;
3865#if COAP_Q_BLOCK_SUPPORT
3866 } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
3867 /* Q-Block1 or Q-Block2 not present in p - duplicate error ? */
3868 if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) ||
3869 coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK1, &block))
3870 return 1;
3871 } else if (rcvd->code == COAP_RESPONSE_CODE(408) &&
3872 lg_xmit->option == COAP_OPTION_Q_BLOCK1) {
3873 size_t length;
3874 const uint8_t *data;
3875 coap_opt_iterator_t opt_iter;
3876 coap_opt_t *fmt_opt = coap_check_option(rcvd,
3878 &opt_iter);
3879 uint16_t fmt = fmt_opt ?
3881 coap_opt_length(fmt_opt)) :
3883
3885 goto fail_body;
3886
3887 if (COAP_PROTO_RELIABLE(session->proto) ||
3888 rcvd->type != COAP_MESSAGE_NON) {
3889 coap_log_debug("Unexpected 4.08 - protocol violation - ignore\n");
3890 return 1;
3891 }
3892
3893 if (coap_get_data(rcvd, &length, &data)) {
3894 /* Need to decode CBOR to work out what blocks to re-send */
3895 const uint8_t *bp = data;
3896 uint32_t i;
3897 uint8_t buf[8];
3898 coap_pdu_t *pdu;
3899 uint64_t token;
3900 uint8_t ltoken[8];
3901 size_t ltoken_length;
3902
3903 for (i = 0; (bp < data + length) &&
3904 i < COAP_MAX_PAYLOADS(session); i++) {
3905 if ((*bp & 0xc0) != 0x00) /* uint(value) */
3906 goto fail_cbor;
3907 block.num = derive_cbor_value(&bp, data + length - bp);
3908 coap_log_debug("Q-Block1: Missing block %d\n", block.num);
3909 if (block.num > (1 << 20) -1)
3910 goto fail_cbor;
3911 block.m = (block.num + 1) * chunk < lg_xmit->data_info->length;
3912 block.szx = lg_xmit->blk_size;
3913
3914 /* Build the next PDU request based off the skeletal PDU */
3915 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count);
3916 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
3917 pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, ltoken_length,
3918 ltoken, NULL);
3919 if (!pdu)
3920 goto fail_body;
3921
3922 coap_update_option(pdu, lg_xmit->option,
3923 coap_encode_var_safe(buf, sizeof(buf),
3924 (block.num << 4) |
3925 (block.m << 3) |
3926 block.szx),
3927 buf);
3928
3929 if (!coap_add_block(pdu,
3930 lg_xmit->data_info->length,
3931 lg_xmit->data_info->data,
3932 block.num,
3933 block.szx))
3934 goto fail_body;
3935 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
3936 goto fail_body;
3937 }
3938 return 1;
3939 }
3940fail_cbor:
3941 coap_log_info("Invalid application/missing-blocks+cbor-seq\n");
3942#endif /* COAP_Q_BLOCK_SUPPORT */
3943 }
3944 goto lg_xmit_finished;
3945 }
3946 return 0;
3947
3948fail_body:
3950 /* There has been an internal error of some sort */
3951 rcvd->code = COAP_RESPONSE_CODE(500);
3952lg_xmit_finished:
3953 if (lg_crcv) {
3954 if (STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) ==
3955 STATE_TOKEN_BASE(lg_crcv->state_token)) {
3956 /* In case of observe */
3957 lg_crcv->state_token = lg_xmit->b.b1.state_token;
3958 lg_crcv->retry_counter = lg_xmit->b.b1.count;
3959 }
3960 }
3961 if (!lg_crcv) {
3962 /* need to put back original token into rcvd */
3963 if (lg_xmit->b.b1.app_token)
3964 coap_update_token(rcvd, lg_xmit->b.b1.app_token->length,
3965 lg_xmit->b.b1.app_token->s);
3966 coap_log_debug("Client app version of updated PDU (1)\n");
3968 } else {
3969 lg_crcv->sent_pdu->lg_xmit = 0;
3970 }
3971
3972 if (sent) {
3973 /* need to put back original token into sent */
3974 if (lg_xmit->b.b1.app_token)
3975 coap_update_token(sent, lg_xmit->b.b1.app_token->length,
3976 lg_xmit->b.b1.app_token->s);
3977 if (sent->lg_xmit)
3978 coap_remove_option(sent, sent->lg_xmit->option);
3979 sent->lg_xmit = NULL;
3980 }
3981 LL_DELETE(session->lg_xmit, lg_xmit);
3982 coap_block_delete_lg_xmit(session, lg_xmit);
3983 return 0;
3984}
3985#endif /* COAP_CLIENT_SUPPORT */
3986
3987void
3989 coap_block_data_handler_t block_data_handler) {
3990 context->block_data_handler = block_data_handler;
3991}
3992
3993/*
3994 * Re-assemble payloads into a body
3995 */
3997coap_block_build_body(coap_binary_t *body_data, size_t length,
3998 const uint8_t *data, size_t offset, size_t total) {
3999 if (data == NULL)
4000 return NULL;
4001 if (body_data == NULL && total) {
4002 body_data = coap_new_binary(total);
4003 }
4004 if (body_data == NULL)
4005 return NULL;
4006
4007 /* Update saved data */
4008 if (offset + length <= total && body_data->length >= total) {
4009 memcpy(&body_data->s[offset], data, length);
4010 } else {
4011 /*
4012 * total may be inaccurate as per
4013 * https://rfc-editor.org/rfc/rfc7959#section-4
4014 * o In a request carrying a Block1 Option, to indicate the current
4015 * estimate the client has of the total size of the resource
4016 * representation, measured in bytes ("size indication").
4017 * o In a response carrying a Block2 Option, to indicate the current
4018 * estimate the server has of the total size of the resource
4019 * representation, measured in bytes ("size indication").
4020 */
4021 coap_binary_t *new = coap_resize_binary(body_data, offset + length);
4022
4023 if (new) {
4024 body_data = new;
4025 memcpy(&body_data->s[offset], data, length);
4026 } else {
4027 coap_delete_binary(body_data);
4028 return NULL;
4029 }
4030 }
4031 return body_data;
4032}
4033
4034#if COAP_CLIENT_SUPPORT
4035/*
4036 * Need to see if this is a large body response to a request. If so,
4037 * need to initiate the request for the next block and not trouble the
4038 * application. Note that Token must be unique per request/response.
4039 *
4040 * This is set up using coap_send()
4041 * Client receives large data from server ((Q-)Block2)
4042 *
4043 * Return: 0 Call application handler
4044 * 1 Do not call application handler - just sent the next request
4045 */
4046int
4048 coap_session_t *session,
4049 coap_pdu_t *sent,
4050 coap_pdu_t *rcvd,
4051 coap_recurse_t recursive) {
4052 coap_lg_crcv_t *lg_crcv;
4053 coap_block_b_t block;
4054#if COAP_Q_BLOCK_SUPPORT
4055 coap_block_b_t qblock;
4056#endif /* COAP_Q_BLOCK_SUPPORT */
4057 int have_block = 0;
4058 uint16_t block_opt = 0;
4059 size_t offset;
4060 int ack_rst_sent = 0;
4061
4063 memset(&block, 0, sizeof(block));
4064#if COAP_Q_BLOCK_SUPPORT
4065 memset(&qblock, 0, sizeof(qblock));
4066#endif /* COAP_Q_BLOCK_SUPPORT */
4067 lg_crcv = coap_find_lg_crcv(session, rcvd);
4068 if (lg_crcv) {
4069 size_t chunk = 0;
4070 uint8_t buf[8];
4071 coap_opt_iterator_t opt_iter;
4072
4073 if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4074 size_t length;
4075 const uint8_t *data;
4077 &opt_iter);
4078 size_t size2 = size_opt ?
4080 coap_opt_length(size_opt)) : 0;
4081
4082 /* length and data are cleared on error */
4083 (void)coap_get_data(rcvd, &length, &data);
4084 rcvd->body_offset = 0;
4085 rcvd->body_total = length;
4086 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
4087 have_block = 1;
4088 block_opt = COAP_OPTION_BLOCK2;
4089 }
4090#if COAP_Q_BLOCK_SUPPORT
4091 if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) {
4092 if (have_block) {
4093 coap_log_warn("Both Block1 and Q-Block1 not supported in a response\n");
4094 }
4095 have_block = 1;
4096 block_opt = COAP_OPTION_Q_BLOCK2;
4097 block = qblock;
4098 /* server indicating that it supports Q_BLOCK */
4099 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
4100 set_block_mode_has_q(session->block_mode);
4101 }
4102 }
4103#endif /* COAP_Q_BLOCK_SUPPORT */
4104 track_echo(session, rcvd);
4105 if (have_block && (block.m || length)) {
4106 coap_opt_t *fmt_opt = coap_check_option(rcvd,
4108 &opt_iter);
4109 uint16_t fmt = fmt_opt ?
4111 coap_opt_length(fmt_opt)) :
4113 coap_opt_t *etag_opt = coap_check_option(rcvd,
4115 &opt_iter);
4116 size_t saved_offset;
4117 int updated_block;
4118
4119 if (length > block.chunk_size) {
4120 coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n",
4121 block.chunk_size, length);
4122 length = block.chunk_size;
4123 }
4124 if (block.m && length != block.chunk_size) {
4125 coap_log_warn("block: Undersized packet - expected %"PRIu32", got %zu\n",
4126 block.chunk_size, length);
4127 /* Unclear how to properly handle this */
4128 rcvd->code = COAP_RESPONSE_CODE(402);
4129 goto expire_lg_crcv;
4130 }
4131 /* Possibility that Size2 not sent, or is too small */
4132 chunk = (size_t)1 << (block.szx + 4);
4133 offset = block.num * chunk;
4134 if (size2 < (offset + length)) {
4135 if (block.m)
4136 size2 = offset + length + 1;
4137 else
4138 size2 = offset + length;
4139 }
4140 saved_offset = offset;
4141
4142 if (lg_crcv->initial) {
4143#if COAP_Q_BLOCK_SUPPORT
4144reinit:
4145#endif /* COAP_Q_BLOCK_SUPPORT */
4146 lg_crcv->initial = 0;
4147 if (lg_crcv->body_data) {
4149 lg_crcv->body_data = NULL;
4150 }
4151 if (etag_opt) {
4152 lg_crcv->etag_length = coap_opt_length(etag_opt);
4153 memcpy(lg_crcv->etag, coap_opt_value(etag_opt), lg_crcv->etag_length);
4154 lg_crcv->etag_set = 1;
4155 } else {
4156 lg_crcv->etag_set = 0;
4157 }
4158 lg_crcv->total_len = size2;
4159 lg_crcv->content_format = fmt;
4160 lg_crcv->szx = block.szx;
4161 lg_crcv->block_option = block_opt;
4162 lg_crcv->last_type = rcvd->type;
4163 lg_crcv->rec_blocks.used = 0;
4164 lg_crcv->rec_blocks.total_blocks = 0;
4165#if COAP_Q_BLOCK_SUPPORT
4166 lg_crcv->rec_blocks.processing_payload_set = 0;
4167#endif /* COAP_Q_BLOCK_SUPPORT */
4168 }
4169 if (lg_crcv->total_len < size2)
4170 lg_crcv->total_len = size2;
4171
4172 if (etag_opt) {
4173 if (!full_match(coap_opt_value(etag_opt),
4174 coap_opt_length(etag_opt),
4175 lg_crcv->etag, lg_crcv->etag_length)) {
4176 /* body of data has changed - need to restart request */
4177 coap_pdu_t *pdu;
4178 uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token,
4179 ++lg_crcv->retry_counter);
4180 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
4181 coap_opt_filter_t drop_options;
4182
4183#if COAP_Q_BLOCK_SUPPORT
4184 if (block_opt == COAP_OPTION_Q_BLOCK2)
4185 goto reinit;
4186#endif /* COAP_Q_BLOCK_SUPPORT */
4187
4188 coap_log_warn("Data body updated during receipt - new request started\n");
4189 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
4191
4192 lg_crcv->initial = 1;
4194 lg_crcv->body_data = NULL;
4195
4196 coap_session_new_token(session, &len, buf);
4197 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
4200 pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, &drop_options);
4201 if (!pdu)
4202 goto fail_resp;
4203
4204 coap_update_option(pdu, block_opt,
4205 coap_encode_var_safe(buf, sizeof(buf),
4206 (0 << 4) | (0 << 3) | block.aszx),
4207 buf);
4208
4209 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4210 goto fail_resp;
4211
4212 goto skip_app_handler;
4213 }
4214 } else if (lg_crcv->etag_set) {
4215 /* Cannot handle this change in ETag to not being there */
4216 coap_log_warn("Not all blocks have ETag option\n");
4217 goto fail_resp;
4218 }
4219
4220 if (fmt != lg_crcv->content_format) {
4221 coap_log_warn("Content-Format option mismatch\n");
4222 goto fail_resp;
4223 }
4224#if COAP_Q_BLOCK_SUPPORT
4225 if (block_opt == COAP_OPTION_Q_BLOCK2 && size2 != lg_crcv->total_len) {
4226 coap_log_warn("Size2 option mismatch\n");
4227 goto fail_resp;
4228 }
4229#endif /* COAP_Q_BLOCK_SUPPORT */
4230 if (block.num == 0) {
4231 coap_opt_t *obs_opt = coap_check_option(rcvd,
4233 &opt_iter);
4234 if (obs_opt) {
4235 lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3);
4236 memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length);
4237 lg_crcv->observe_set = 1;
4238 } else {
4239 lg_crcv->observe_set = 0;
4240 }
4241 }
4242 updated_block = 0;
4243 while (offset < saved_offset + length) {
4244 if (!check_if_received_block(&lg_crcv->rec_blocks, block.num)) {
4245#if COAP_Q_BLOCK_SUPPORT
4246 uint32_t this_payload_set = block.num / COAP_MAX_PAYLOADS(session);
4247#endif /* COAP_Q_BLOCK_SUPPORT */
4248
4249 coap_log_debug("found Block option, block size is %u, block nr. %u\n",
4250 1 << (block.szx + 4), block.num);
4251#if COAP_Q_BLOCK_SUPPORT
4252 if (block_opt == COAP_OPTION_Q_BLOCK2 && lg_crcv->rec_blocks.used &&
4253 this_payload_set > lg_crcv->rec_blocks.processing_payload_set &&
4254 this_payload_set != lg_crcv->rec_blocks.latest_payload_set) {
4255 coap_request_missing_q_block2(session, lg_crcv);
4256 }
4257 lg_crcv->rec_blocks.latest_payload_set = this_payload_set;
4258#endif /* COAP_Q_BLOCK_SUPPORT */
4259 /* Update list of blocks received */
4260 if (!update_received_blocks(&lg_crcv->rec_blocks, block.num, block.m)) {
4262 goto fail_resp;
4263 }
4264 updated_block = 1;
4265 }
4266 block.num++;
4267 offset = block.num << (block.szx + 4);
4268 if (!block.bert && block_opt != COAP_OPTION_Q_BLOCK2)
4269 break;
4270 }
4271 block.num--;
4272 /* Only process if not duplicate block */
4273 if (updated_block) {
4274 void *body_free;
4275
4276 /* Update last_used to prevent premature timeout during long transfers */
4277 coap_ticks(&lg_crcv->last_used);
4278
4279 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
4280 if (size2 < saved_offset + length) {
4281 size2 = saved_offset + length;
4282 }
4283 if (context && context->block_data_handler) {
4284 coap_response_t resp;
4285 resp = context->block_data_handler(session, rcvd, 0,
4286 &lg_crcv->body_data, length,
4287 data, saved_offset, size2);
4288 if (resp != COAP_RESPONSE_OK) {
4289 goto fail_resp;
4290 }
4291 } else {
4292 lg_crcv->body_data = coap_block_build_body(lg_crcv->body_data, length, data,
4293 saved_offset, size2);
4294 if (lg_crcv->body_data == NULL) {
4295 goto fail_resp;
4296 }
4297 }
4298 }
4299 if (block.m || !check_all_blocks_in(&lg_crcv->rec_blocks)) {
4300 /* Not all the payloads of the body have arrived */
4301 size_t len;
4302 coap_pdu_t *pdu;
4303 uint64_t token;
4304 coap_opt_filter_t drop_options;
4305
4306 if (block.m) {
4307#if COAP_Q_BLOCK_SUPPORT
4308 if (block_opt == COAP_OPTION_Q_BLOCK2) {
4309 /* Blocks could arrive in wrong order */
4310 if (check_all_blocks_in(&lg_crcv->rec_blocks)) {
4311 goto give_to_app;
4312 }
4313 if (check_all_blocks_in_for_payload_set(session,
4314 &lg_crcv->rec_blocks)) {
4315 block.num = lg_crcv->rec_blocks.range[0].end;
4316 /* Now requesting next payload */
4317 lg_crcv->rec_blocks.processing_payload_set =
4318 block.num / COAP_MAX_PAYLOADS(session) + 1;
4319 if (check_any_blocks_next_payload_set(session,
4320 &lg_crcv->rec_blocks)) {
4321 /* Need to ask for them individually */
4322 coap_request_missing_q_block2(session, lg_crcv);
4323 goto skip_app_handler;
4324 }
4325 } else {
4326 /* The remote end will be sending the next one unless this
4327 is a MAX_PAYLOADS and all previous have been received */
4328 goto skip_app_handler;
4329 }
4330 if (COAP_PROTO_RELIABLE(session->proto) ||
4331 rcvd->type != COAP_MESSAGE_NON)
4332 goto skip_app_handler;
4333
4334 } else
4335#endif /* COAP_Q_BLOCK_SUPPORT */
4336 block.m = 0;
4337
4338 /* Ask for the next block */
4339 token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter);
4340 len = coap_encode_var_safe8(buf, sizeof(token), token);
4341 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
4343 pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, &drop_options);
4344 if (!pdu)
4345 goto fail_resp;
4346
4347 if (rcvd->type == COAP_MESSAGE_NON)
4348 pdu->type = COAP_MESSAGE_NON; /* Server is using NON */
4349
4350 /* Only sent with the first block */
4352
4353 coap_update_option(pdu, block_opt,
4354 coap_encode_var_safe(buf, sizeof(buf),
4355 ((block.num + 1) << 4) |
4356 (block.m << 3) | block.aszx),
4357 buf);
4358
4360 (void)coap_get_data(lg_crcv->sent_pdu, &length, &data);
4361 coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, length, data, NULL, NULL, NULL,
4362 0, 0);
4363 }
4364 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4365 /* Session could now be disconnected, so no lg_crcv */
4366 goto skip_app_handler;
4367 }
4368 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert)
4369 goto skip_app_handler;
4370
4371 /* need to put back original token into rcvd */
4372 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4373 rcvd->body_offset = saved_offset;
4374#if COAP_Q_BLOCK_SUPPORT
4375 rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
4376 lg_crcv->total_len : size2;
4377#else /* ! COAP_Q_BLOCK_SUPPORT */
4378 rcvd->body_total = size2;
4379#endif /* ! COAP_Q_BLOCK_SUPPORT */
4380 coap_log_debug("Client app version of updated PDU (2)\n");
4382
4383 if (sent) {
4384 /* need to put back original token into sent */
4385 if (lg_crcv->app_token)
4386 coap_update_token(sent, lg_crcv->app_token->length,
4387 lg_crcv->app_token->s);
4388 coap_remove_option(sent, lg_crcv->block_option);
4389 }
4390 goto call_app_handler;
4391 }
4392#if COAP_Q_BLOCK_SUPPORT
4393give_to_app:
4394#endif /* COAP_Q_BLOCK_SUPPORT */
4395 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
4396 /* Pretend that there is no block */
4397 coap_remove_option(rcvd, block_opt);
4398 if (lg_crcv->observe_set) {
4400 lg_crcv->observe_length, lg_crcv->observe);
4401 }
4402 rcvd->body_data = lg_crcv->body_data ? lg_crcv->body_data->s : NULL;
4403#if COAP_Q_BLOCK_SUPPORT
4404 if (context && context->block_data_handler) {
4405 /* Data has already been provided - do not duplicate */
4406 if (rcvd->data) {
4407 rcvd->used_size = rcvd->data - rcvd->token - 1;
4408 rcvd->data = NULL;
4409 }
4410 }
4411 rcvd->body_length = block_opt == COAP_OPTION_Q_BLOCK2 ?
4412 lg_crcv->total_len : saved_offset + length;
4413#else /* ! COAP_Q_BLOCK_SUPPORT */
4414 rcvd->body_length = saved_offset + length;
4415#endif /* ! COAP_Q_BLOCK_SUPPORT */
4416 rcvd->body_offset = 0;
4417 rcvd->body_total = rcvd->body_length;
4418 } else {
4419 rcvd->body_offset = saved_offset;
4420#if COAP_Q_BLOCK_SUPPORT
4421 rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
4422 lg_crcv->total_len : size2;
4423#else /* ! COAP_Q_BLOCK_SUPPORT */
4424 rcvd->body_total = size2;
4425#endif /* ! COAP_Q_BLOCK_SUPPORT */
4426 }
4427 /* need to put back original token into rcvd */
4428 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4429 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4430 coap_log_debug("Client app version of updated PDU (3)\n");
4432 }
4433 if (sent) {
4434 /* need to put back original token into sent */
4435 if (lg_crcv->app_token)
4436 coap_update_token(sent, lg_crcv->app_token->length,
4437 lg_crcv->app_token->s);
4438 coap_remove_option(sent, lg_crcv->block_option);
4439 }
4440 body_free = lg_crcv->body_data;
4441 lg_crcv->body_data = NULL;
4442 coap_call_response_handler(session, sent, rcvd, body_free);
4443
4444 ack_rst_sent = 1;
4445 if (lg_crcv->observe_set == 0) {
4446 /* Expire this entry */
4447 LL_DELETE(session->lg_crcv, lg_crcv);
4448 coap_block_delete_lg_crcv(session, lg_crcv);
4449 goto skip_app_handler;
4450 }
4451 /* Set up for the next data body as observing */
4452 lg_crcv->initial = 1;
4453 }
4454 coap_ticks(&lg_crcv->last_used);
4455 goto skip_app_handler;
4456 } else {
4457 coap_opt_t *obs_opt = coap_check_option(rcvd,
4459 &opt_iter);
4460 if (obs_opt) {
4461 lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3);
4462 memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length);
4463 lg_crcv->observe_set = 1;
4464 } else {
4465 lg_crcv->observe_set = 0;
4466 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4467 /* need to put back original token into rcvd */
4468 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4470 coap_log_debug("PDU presented to app.\n");
4472 }
4473 /* Expire this entry */
4474 goto expire_lg_crcv;
4475 }
4476 }
4477 coap_ticks(&lg_crcv->last_used);
4478 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4479#if COAP_OSCORE_SUPPORT
4480 if (check_freshness(session, rcvd,
4481 (session->oscore_encryption == 0) ? sent : NULL,
4482 NULL, lg_crcv))
4483#else /* !COAP_OSCORE_SUPPORT */
4484 if (check_freshness(session, rcvd, sent, NULL, lg_crcv))
4485#endif /* !COAP_OSCORE_SUPPORT */
4486 goto skip_app_handler;
4487 goto expire_lg_crcv;
4488 } else {
4489 /* Not 2.xx or 4.01 - assume it is a failure of some sort */
4490 goto expire_lg_crcv;
4491 }
4492 if (!block.m && !lg_crcv->observe_set) {
4493fail_resp:
4494 /* lg_crcv no longer required - cache it for 1 sec */
4495 coap_ticks(&lg_crcv->last_used);
4496 lg_crcv->last_used = lg_crcv->last_used - COAP_MAX_TRANSMIT_WAIT_TICKS(session) +
4498 }
4499 /* need to put back original token into rcvd */
4500 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4501 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4502 coap_log_debug("Client app version of updated PDU (4)\n");
4504 }
4505 }
4506
4507 /* Check if receiving a block response and if blocks can be set up */
4508 if (recursive == COAP_RECURSE_OK && !lg_crcv) {
4509 if (!sent) {
4510 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)
4511#if COAP_Q_BLOCK_SUPPORT
4512 ||
4513 coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)
4514#endif /* COAP_Q_BLOCK_SUPPORT */
4515 ) {
4516 coap_log_debug("** %s: large body receive internal issue\n",
4517 coap_session_str(session));
4518 goto skip_app_handler;
4519 }
4520 } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4521 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
4522#if COAP_Q_BLOCK_SUPPORT
4523 if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) {
4524 set_block_mode_drop_q(session->block_mode);
4525 coap_log_debug("Q-Block support disabled\n");
4526 }
4527#endif /* COAP_Q_BLOCK_SUPPORT */
4528 have_block = 1;
4529 if (block.num != 0) {
4530 /* Assume random access and just give the single response to app */
4531 size_t length;
4532 const uint8_t *data;
4533 size_t chunk = (size_t)1 << (block.szx + 4);
4534
4535 coap_get_data(rcvd, &length, &data);
4536 rcvd->body_offset = block.num*chunk;
4537 rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
4538 goto call_app_handler;
4539 }
4540 }
4541#if COAP_Q_BLOCK_SUPPORT
4542 else if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)) {
4543 have_block = 1;
4544 /* server indicating that it supports Q_BLOCK2 */
4545 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
4546 set_block_mode_has_q(session->block_mode);
4547 }
4548 }
4549#endif /* COAP_Q_BLOCK_SUPPORT */
4550 if (have_block) {
4551 lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
4552
4553 if (lg_crcv) {
4554 LL_PREPEND(session->lg_crcv, lg_crcv);
4555 return coap_handle_response_get_block(context, session, sent, rcvd,
4557 }
4558 }
4559 track_echo(session, rcvd);
4560 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4561 lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
4562
4563 if (lg_crcv) {
4564 LL_PREPEND(session->lg_crcv, lg_crcv);
4565 return coap_handle_response_get_block(context, session, sent, rcvd,
4567 }
4568 }
4569 }
4570 return 0;
4571
4572expire_lg_crcv:
4573 /* need to put back original token into rcvd */
4574 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4575 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4576 coap_log_debug("Client app version of updated PDU (5)\n");
4578 }
4579
4580 if (sent) {
4581 /* need to put back original token into sent */
4582 if (lg_crcv->app_token)
4583 coap_update_token(sent, lg_crcv->app_token->length,
4584 lg_crcv->app_token->s);
4585 coap_remove_option(sent, lg_crcv->block_option);
4586 }
4587 /* Expire this entry */
4588 LL_DELETE(session->lg_crcv, lg_crcv);
4589 coap_block_delete_lg_crcv(session, lg_crcv);
4590
4591call_app_handler:
4592 return 0;
4593
4594skip_app_handler:
4595 if (!ack_rst_sent)
4596 coap_send_ack_lkd(session, rcvd);
4597 return 1;
4598}
4599#endif /* COAP_CLIENT_SUPPORT */
4600
4601#if COAP_SERVER_SUPPORT
4602/* Check if lg_xmit generated and update PDU code if so */
4603void
4605 const coap_pdu_t *request,
4606 coap_pdu_t *response, const coap_resource_t *resource,
4607 const coap_string_t *query) {
4608 coap_lg_xmit_t *lg_xmit;
4609
4610 if (response->code == 0)
4611 return;
4612 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
4613 if (lg_xmit && lg_xmit->sent_pdu && lg_xmit->sent_pdu->code == 0) {
4614 lg_xmit->sent_pdu->code = response->code;
4615 return;
4616 }
4617}
4618#endif /* COAP_SERVER_SUPPORT */
4619
4620#if COAP_CLIENT_SUPPORT
4621void
4623 uint64_t token_match =
4625 pdu->actual_token.length));
4626 coap_lg_xmit_t *lg_xmit;
4627 coap_lg_crcv_t *lg_crcv;
4628
4629 if (session->lg_crcv) {
4630 LL_FOREACH(session->lg_crcv, lg_crcv) {
4631 if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token))
4632 return;
4633 if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) {
4634 coap_update_token(pdu, lg_crcv->app_token->length,
4635 lg_crcv->app_token->s);
4636 coap_log_debug("Client app version of updated PDU (6)\n");
4638 return;
4639 }
4640 }
4641 }
4642 if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) {
4643 LL_FOREACH(session->lg_xmit, lg_xmit) {
4644 if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token))
4645 return;
4646 if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) {
4647 coap_update_token(pdu, lg_xmit->b.b1.app_token->length,
4648 lg_xmit->b.b1.app_token->s);
4649 coap_log_debug("Client app version of updated PDU (7)\n");
4651 return;
4652 }
4653 }
4654 }
4655}
4656#endif /* ! COAP_CLIENT_SUPPORT */
int coap_is_mcast(const coap_address_t *a)
Checks if given address a denotes a multicast address.
void coap_address_copy(coap_address_t *dst, const coap_address_t *src)
int coap_address_equals(const coap_address_t *a, const coap_address_t *b)
Compares given address objects a and b.
static void coap_block_release_lg_xmit_data(coap_session_t *session, coap_lg_xmit_data_t *data_info)
#define COAP_ETAG_MAX_BYTES
Definition coap_block.c:24
COAP_STATIC_INLINE int full_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition coap_block.c:470
#define MAX_BLK_LEN
static int coap_add_data_large_internal(coap_session_t *session, const coap_pdu_t *request, coap_pdu_t *pdu, coap_resource_t *resource, const coap_string_t *query, int maxage, uint64_t etag, size_t length, const uint8_t *data, coap_release_large_data_t release_func, coap_get_large_data_t get_func, void *app_ptr, int single_request, coap_pdu_code_t request_method)
Definition coap_block.c:771
static int update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m)
static int check_all_blocks_in(coap_rblock_t *rec_blocks)
static int setup_block_b(coap_session_t *session, coap_pdu_t *pdu, coap_block_b_t *block, unsigned int num, unsigned int blk_size, size_t total)
Definition coap_block.c:132
#define min(a, b)
Definition coap_block.c:19
static int check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num)
int coap_flsll(long long j)
Definition coap_encode.c:28
int coap_fls(unsigned int i)
Definition coap_encode.c:21
#define PRIu32
@ COAP_NACK_TOO_MANY_RETRIES
Definition coap_io.h:67
#define COAP_SOCKET_MULTICAST
socket is used for multicast communication
Library specific build wrapper for coap_internal.h.
#define COAP_API
@ COAP_LG_XMIT
Definition coap_mem.h:50
@ COAP_LG_CRCV
Definition coap_mem.h:51
@ COAP_LG_SRCV
Definition coap_mem.h:52
@ COAP_STRING
Definition coap_mem.h:34
void * coap_realloc_type(coap_memory_tag_t type, void *p, size_t size)
Reallocates a chunk p of bytes created by coap_malloc_type() or coap_realloc_type() and returns a poi...
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
uint8_t coap_unique_id[8]
Definition coap_net.c:5072
uint16_t coap_option_num_t
Definition coap_option.h:24
uint8_t coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition coap_option.h:30
void coap_call_response_handler(coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd, void *body_free)
coap_mid_t coap_send_ack_lkd(coap_session_t *session, const coap_pdu_t *request)
Sends an ACK message with code 0 for the specified request to dst.
Definition coap_net.c:1082
int coap_add_data_large_response_lkd(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, coap_pdu_t *response, const coap_string_t *query, uint16_t media_type, int maxage, uint64_t etag, size_t length, const uint8_t *data, coap_release_large_data_t release_func, void *app_ptr)
Associates given data with the response pdu that is passed as fourth parameter.
int coap_context_set_max_block_size_lkd(coap_context_t *context, size_t max_block_size)
Set the context level maximum block size that the server supports when sending or receiving packets w...
Definition coap_block.c:446
void coap_block_delete_lg_srcv(coap_session_t *session, coap_lg_srcv_t *lg_srcv)
void coap_context_set_block_mode_lkd(coap_context_t *context, uint32_t block_mode)
Set the context level CoAP block handling bits for handling RFC7959.
Definition coap_block.c:421
int coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
int coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
#define COAP_BLOCK_MAX_SIZE_SET(a)
#define COAP_RBLOCK_CNT
int coap_add_data_large_request_app_lkd(coap_session_t *session, coap_pdu_t *pdu, size_t length, coap_release_large_data_t release_func, coap_get_large_data_t get_func, void *app_ptr)
Associates given data callback with the pdu that is passed as second parameter.
void coap_block_delete_lg_crcv(coap_session_t *session, coap_lg_crcv_t *lg_crcv)
int coap_handle_response_get_block(coap_context_t *context, coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd, coap_recurse_t recursive)
void coap_check_code_lg_xmit(const coap_session_t *session, const coap_pdu_t *request, coap_pdu_t *response, const coap_resource_t *resource, const coap_string_t *query)
The function checks that the code in a newly formed lg_xmit created by coap_add_data_large_response_l...
int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd)
coap_mid_t coap_retransmit_oscore_pdu(coap_session_t *session, coap_pdu_t *pdu, coap_opt_t *echo)
coap_lg_crcv_t * coap_find_lg_crcv(coap_session_t *session, coap_pdu_t *pdu)
Find the current lg_crcv for the session that matches the pdu.
int coap_add_data_large_request_lkd(coap_session_t *session, coap_pdu_t *pdu, size_t length, const uint8_t *data, coap_release_large_data_t release_func, void *app_ptr)
Associates given data with the pdu that is passed as second parameter.
void coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu)
The function checks if the token needs to be updated before PDU is presented to the application (only...
void coap_block_delete_lg_xmit(coap_session_t *session, coap_lg_xmit_t *lg_xmit)
#define STATE_TOKEN_FULL(t, r)
#define COAP_SINGLE_BLOCK_OR_Q
int coap_handle_request_put_block(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *uri_path, coap_opt_t *observe, int *added_block, coap_lg_srcv_t **free_lg_srcv)
#define STATE_TOKEN_BASE(t)
#define COAP_BLOCK_SET_MASK
coap_lg_xmit_t * coap_find_lg_xmit_response(const coap_session_t *session, const coap_pdu_t *request, const coap_resource_t *resource, const coap_string_t *query)
coap_lg_crcv_t * coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu, coap_lg_xmit_t *lg_xmit)
coap_lg_xmit_t * coap_find_lg_xmit(coap_session_t *session, coap_pdu_t *pdu)
Find the current lg_xmit for the session that matches the pdu.
Definition coap_block.c:476
int coap_handle_request_send_block(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *query)
#define COAP_BLOCK_MAX_SIZE_GET(a)
int coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
@ COAP_RECURSE_OK
@ COAP_RECURSE_NO
COAP_API void coap_context_set_block_mode(coap_context_t *context, uint32_t block_mode)
Set the context level CoAP block handling bits for handling RFC7959.
Definition coap_block.c:413
COAP_API int coap_add_data_large_response(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, coap_pdu_t *response, const coap_string_t *query, uint16_t media_type, int maxage, uint64_t etag, size_t length, const uint8_t *data, coap_release_large_data_t release_func, void *app_ptr)
Associates given data with the response pdu that is passed as fourth parameter.
int(* coap_get_large_data_t)(coap_session_t *session, size_t max, size_t offset, uint8_t *data, size_t *length, void *app_ptr)
Callback handler for getting the data based on app_ptr provided to coap_add_data_large_request_app() ...
Definition coap_block.h:361
#define COAP_BLOCK_USE_M_Q_BLOCK
Definition coap_block.h:68
#define COAP_OPT_BLOCK_SZX(opt)
Returns the value of the SZX-field of a Block option opt.
Definition coap_block.h:95
#define COAP_BLOCK_STLESS_BLOCK2
Definition coap_block.h:71
COAP_API int coap_add_data_large_request(coap_session_t *session, coap_pdu_t *pdu, size_t length, const uint8_t *data, coap_release_large_data_t release_func, void *app_ptr)
Associates given data with the pdu that is passed as second parameter.
#define COAP_BLOCK_TRY_Q_BLOCK
Definition coap_block.h:67
#define COAP_BLOCK_STLESS_FETCH
Definition coap_block.h:70
COAP_API int coap_context_set_max_block_size(coap_context_t *context, size_t max_block_size)
Set the context level maximum block size that the server supports when sending or receiving packets w...
Definition coap_block.c:435
int coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data, coap_block_b_t *block)
Adds the appropriate payload data of the body to the pdu.
Definition coap_block.c:252
#define COAP_BLOCK_SINGLE_BODY
Definition coap_block.h:66
int coap_write_block_b_opt(coap_session_t *session, coap_block_b_t *block, coap_option_num_t number, coap_pdu_t *pdu, size_t data_length)
Writes a block option of type number to message pdu.
Definition coap_block.c:207
int coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data, unsigned int block_num, unsigned char block_szx)
Adds the block_num block of size 1 << (block_szx + 4) from source data to pdu.
Definition coap_block.c:238
void(* coap_release_large_data_t)(coap_session_t *session, void *app_ptr)
Callback handler for de-allocating the data based on app_ptr provided to coap_add_data_large_*() func...
Definition coap_block.h:292
void coap_add_data_blocked_response(const coap_pdu_t *request, coap_pdu_t *response, uint16_t media_type, int maxage, size_t length, const uint8_t *data)
Adds the appropriate part of data to the response pdu.
Definition coap_block.c:277
int coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu, coap_option_num_t number, coap_block_b_t *block)
Initializes block from pdu.
Definition coap_block.c:62
#define COAP_OPT_BLOCK_MORE(opt)
Returns the value of the More-bit of a Block option opt.
Definition coap_block.h:91
unsigned int coap_opt_block_num(const coap_opt_t *block_opt)
Returns the value of field num in the given block option block_opt.
Definition coap_block.c:43
int coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number, coap_block_t *block)
Initializes block from pdu.
Definition coap_block.c:115
#define COAP_BLOCK_NOT_RANDOM_BLOCK1
Definition coap_block.h:72
#define COAP_OPT_BLOCK_END_BYTE(opt)
Returns the value of the last byte of opt.
Definition coap_block.h:86
int coap_write_block_opt(coap_block_t *block, coap_option_num_t number, coap_pdu_t *pdu, size_t data_length)
Writes a block option of type number to message pdu.
Definition coap_block.c:174
COAP_API int coap_add_data_large_request_app(coap_session_t *session, coap_pdu_t *pdu, size_t length, coap_release_large_data_t release_func, coap_get_large_data_t get_func, void *app_ptr)
Associates given data callback with the pdu that is passed as second parameter.
#define COAP_BLOCK_FORCE_Q_BLOCK
Definition coap_block.h:74
coap_binary_t * coap_block_build_body(coap_binary_t *body_data, size_t length, const uint8_t *data, size_t offset, size_t total)
Re-assemble payloads into a body.
#define COAP_BLOCK_USE_LIBCOAP
Definition coap_block.h:65
void coap_digest_free(coap_digest_ctx_t *digest_ctx)
Free off coap_digest_ctx_t.
int coap_digest_final(coap_digest_ctx_t *digest_ctx, coap_digest_t *digest_buffer)
Finalize the coap_digest information into the provided digest_buffer.
int coap_digest_update(coap_digest_ctx_t *digest_ctx, const uint8_t *data, size_t data_len)
Update the coap_digest information with the next chunk of data.
void coap_digest_ctx_t
coap_digest_ctx_t * coap_digest_setup(void)
Initialize a coap_digest.
time_t coap_time_t
CoAP time in seconds since epoch.
Definition coap_time.h:156
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition coap_time.h:151
coap_time_t coap_ticks_to_rt(coap_tick_t t)
Helper function that converts coap ticks to wallclock time.
Definition coap_time.c:123
#define COAP_TICKS_PER_SECOND
Use ms resolution on POSIX systems.
Definition coap_time.h:166
#define COAP_MAX_DELAY_TICKS
Definition coap_time.h:230
#define COAP_RESOURCE_USE_BLOCK_DATA_HANDLER
Call the block data handler for this resource if one is registered for the context.
int coap_handle_event_lkd(coap_context_t *context, coap_event_t event, coap_session_t *session)
Invokes the event handler of context for the given event and data.
Definition coap_net.c:4919
uint16_t coap_new_message_id_lkd(coap_session_t *session)
Returns a new message id and updates session->tx_mid accordingly.
int coap_client_delay_first(coap_session_t *session)
Delay the sending of the first client request until some other negotiation has completed.
Definition coap_net.c:1335
coap_mid_t coap_send_internal(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *request_pdu)
Sends a CoAP message to given peer.
Definition coap_net.c:1836
void coap_register_block_data_handler(coap_context_t *context, coap_block_data_handler_t block_data_handler)
Sets up a handler that is called for each received block during a block-wise transfer when COAP_BLOCK...
coap_response_t
Definition coap_net.h:54
coap_response_t(* coap_block_data_handler_t)(coap_session_t *session, coap_pdu_t *pdu, coap_resource_t *resource, coap_binary_t **body_data, size_t length, const uint8_t *data, size_t offset, size_t total)
Definition of the block data handler function.
Definition coap_net.h:136
void coap_ticks(coap_tick_t *t)
Returns the current value of an internal tick counter.
Definition coap_time.c:90
@ COAP_RESPONSE_OK
Response is fine.
Definition coap_net.h:56
unsigned int coap_encode_var_safe(uint8_t *buf, size_t length, unsigned int val)
Encodes multiple-length byte sequences.
Definition coap_encode.c:47
unsigned int coap_decode_var_bytes(const uint8_t *buf, size_t len)
Decodes multiple-length byte sequences.
Definition coap_encode.c:38
uint64_t coap_decode_var_bytes8(const uint8_t *buf, size_t len)
Decodes multiple-length byte sequences.
Definition coap_encode.c:67
unsigned int coap_encode_var_safe8(uint8_t *buf, size_t length, uint64_t val)
Encodes multiple-length byte sequences.
Definition coap_encode.c:77
@ COAP_EVENT_PARTIAL_BLOCK
Triggered when not all of a large body has been received.
Definition coap_event.h:75
@ COAP_EVENT_XMIT_BLOCK_FAIL
Triggered when not all of a large body has been transmitted.
Definition coap_event.h:77
#define coap_lock_callback(func)
Dummy for no thread-safe code.
#define coap_lock_unlock()
Dummy for no thread-safe code.
#define coap_lock_check_locked()
Dummy for no thread-safe code.
#define coap_lock_lock(failed)
Dummy for no thread-safe code.
#define coap_log_debug(...)
Definition coap_debug.h:126
void coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu)
Display the contents of the specified pdu.
Definition coap_debug.c:786
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define coap_log_info(...)
Definition coap_debug.h:114
#define coap_log_warn(...)
Definition coap_debug.h:108
@ COAP_LOG_DEBUG
Definition coap_debug.h:64
#define COAP_OBSERVE_CANCEL
The value COAP_OBSERVE_CANCEL in a GET/FETCH request option COAP_OPTION_OBSERVE indicates that the ob...
#define COAP_OBSERVE_ESTABLISH
The value COAP_OBSERVE_ESTABLISH in a GET/FETCH request option COAP_OPTION_OBSERVE indicates a new ob...
COAP_API int coap_cancel_observe(coap_session_t *session, coap_binary_t *token, coap_pdu_type_t message_type)
Cancel an observe that is being tracked by the client large receive logic.
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
coap_opt_iterator_t * coap_option_iterator_init(const coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t *filter)
Initializes the given option iterator oi to point to the beginning of the pdu's option list.
size_t coap_opt_encode_size(uint16_t delta, size_t length)
Compute storage bytes needed for an option with given delta and length.
#define COAP_OPT_ALL
Pre-defined filter that includes all options.
coap_opt_t * coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number, coap_opt_iterator_t *oi)
Retrieves the first option of number number from pdu.
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option)
Sets the corresponding entry for number in filter.
int coap_rebuild_pdu_for_proxy(coap_pdu_t *pdu)
Convert PDU to use Proxy-Scheme option if Proxy-Uri option is present.
size_t coap_oscore_overhead(coap_session_t *session, coap_pdu_t *pdu)
Determine the additional data size requirements for adding in OSCORE.
#define COAP_PDU_IS_RESPONSE(pdu)
coap_pdu_t * coap_pdu_reference_lkd(coap_pdu_t *pdu)
Increment reference counter on a pdu to stop it prematurely getting freed off when coap_delete_pdu() ...
Definition coap_pdu.c:1655
void coap_delete_pdu_lkd(coap_pdu_t *pdu)
Dispose of an CoAP PDU and free off associated storage.
Definition coap_pdu.c:194
size_t coap_insert_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Inserts option of given number in the pdu with the appropriate data.
Definition coap_pdu.c:633
int coap_remove_option(coap_pdu_t *pdu, coap_option_num_t number)
Removes (first) option of given number from the pdu.
Definition coap_pdu.c:493
int coap_update_token(coap_pdu_t *pdu, size_t len, const uint8_t *data)
Updates token in pdu with length len and data.
Definition coap_pdu.c:417
size_t coap_update_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Updates existing first option of given number in the pdu with the new data.
Definition coap_pdu.c:727
coap_pdu_t * coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu, coap_session_t *session, size_t token_length, const uint8_t *token, coap_opt_filter_t *drop_options)
Duplicate an existing PDU.
Definition coap_pdu.c:234
#define COAP_PAYLOAD_START
int coap_pdu_check_resize(coap_pdu_t *pdu, size_t size)
Dynamically grows the size of pdu to new_size if needed.
Definition coap_pdu.c:343
#define COAP_PDU_IS_REQUEST(pdu)
size_t coap_add_option_internal(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Adds option of given number to pdu that is passed as first parameter.
Definition coap_pdu.c:783
#define COAP_OPTION_BLOCK2
Definition coap_pdu.h:142
const char * coap_response_phrase(unsigned char code)
Returns a human-readable response phrase for the specified CoAP response code.
Definition coap_pdu.c:954
#define COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ
Definition coap_pdu.h:259
#define COAP_OPTION_CONTENT_FORMAT
Definition coap_pdu.h:132
#define COAP_OPTION_SIZE2
Definition coap_pdu.h:144
#define COAP_OPTION_BLOCK1
Definition coap_pdu.h:143
#define COAP_OPTION_Q_BLOCK1
Definition coap_pdu.h:139
int coap_mid_t
coap_mid_t is used to store the CoAP Message ID of a CoAP PDU.
Definition coap_pdu.h:268
#define COAP_OPTION_URI_PATH
Definition coap_pdu.h:131
#define COAP_RESPONSE_CODE(N)
Definition coap_pdu.h:165
#define COAP_RESPONSE_CLASS(C)
Definition coap_pdu.h:168
coap_pdu_code_t
Set of codes available for a PDU.
Definition coap_pdu.h:332
#define COAP_OPTION_SIZE1
Definition coap_pdu.h:148
coap_pdu_type_t
CoAP PDU message type definitions.
Definition coap_pdu.h:72
#define COAP_MEDIATYPE_TEXT_PLAIN
Definition coap_pdu.h:218
int coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data)
Adds token of length len to pdu.
Definition coap_pdu.c:360
#define COAP_OPTION_CONTENT_TYPE
Definition coap_pdu.h:133
size_t coap_add_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Adds option of given number to pdu that is passed as first parameter.
Definition coap_pdu.c:773
#define COAP_OPTION_Q_BLOCK2
Definition coap_pdu.h:145
int coap_get_data(const coap_pdu_t *pdu, size_t *len, const uint8_t **data)
Retrieves the length and data pointer of specified PDU.
Definition coap_pdu.c:879
#define COAP_OPTION_RTAG
Definition coap_pdu.h:151
coap_pdu_t * coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid, size_t size)
Creates a new CoAP PDU with at least enough storage space for the given size maximum message size.
Definition coap_pdu.c:102
int coap_get_data_large(const coap_pdu_t *pdu, size_t *len, const uint8_t **data, size_t *offset, size_t *total)
Retrieves the data from a PDU, with support for large bodies of data that spans multiple PDUs.
Definition coap_pdu.c:887
#define COAP_INVALID_MID
Indicates an invalid message id.
Definition coap_pdu.h:271
#define COAP_OPTION_MAXAGE
Definition coap_pdu.h:135
#define COAP_OPTION_ETAG
Definition coap_pdu.h:125
#define COAP_OPTION_OBSERVE
Definition coap_pdu.h:127
#define COAP_OPTION_ECHO
Definition coap_pdu.h:149
int coap_add_data(coap_pdu_t *pdu, size_t len, const uint8_t *data)
Adds given data to the pdu that is passed as first parameter.
Definition coap_pdu.c:848
@ COAP_REQUEST_CODE_GET
Definition coap_pdu.h:335
@ COAP_REQUEST_CODE_FETCH
Definition coap_pdu.h:339
@ COAP_MESSAGE_NON
Definition coap_pdu.h:74
@ COAP_MESSAGE_ACK
Definition coap_pdu.h:75
@ COAP_MESSAGE_CON
Definition coap_pdu.h:73
#define COAP_NON_RECEIVE_TIMEOUT_TICKS(s)
The NON_RECEIVE_TIMEOUT definition for the session (s).
#define COAP_NON_TIMEOUT_TICKS(s)
void coap_handle_nack(coap_session_t *session, coap_pdu_t *sent, const coap_nack_reason_t reason, const coap_mid_t mid)
#define COAP_MAX_TRANSMIT_WAIT_TICKS(s)
#define COAP_NON_PARTIAL_TIMEOUT_TICKS(s)
The NON_PARTIAL_TIMEOUT definition for the session (s).
coap_tick_t coap_get_non_timeout_random_ticks(coap_session_t *session)
#define COAP_NSTART(s)
#define COAP_MAX_PAYLOADS(s)
size_t coap_session_max_pdu_size_lkd(const coap_session_t *session)
Get maximum acceptable PDU size.
#define COAP_NON_MAX_RETRANSMIT(s)
#define COAP_PROTO_NOT_RELIABLE(p)
#define COAP_PROTO_RELIABLE(p)
void coap_session_new_token(coap_session_t *session, size_t *len, uint8_t *data)
Creates a new token for use.
@ COAP_SESSION_TYPE_CLIENT
client-side
void coap_delete_bin_const(coap_bin_const_t *s)
Deletes the given const binary data and releases any memory allocated.
Definition coap_str.c:120
void coap_delete_str_const(coap_str_const_t *s)
Deletes the given const string and releases any memory allocated.
Definition coap_str.c:61
coap_binary_t * coap_new_binary(size_t size)
Returns a new binary object with at least size bytes storage allocated.
Definition coap_str.c:77
coap_bin_const_t * coap_new_bin_const(const uint8_t *data, size_t size)
Take the specified byte array (text) and create a coap_bin_const_t * Returns a new const binary objec...
Definition coap_str.c:110
coap_binary_t * coap_resize_binary(coap_binary_t *s, size_t size)
Resizes the given coap_binary_t object.
Definition coap_str.c:82
void coap_delete_binary(coap_binary_t *s)
Deletes the given coap_binary_t object and releases any memory allocated.
Definition coap_str.c:105
#define coap_binary_equal(binary1, binary2)
Compares the two binary data for equality.
Definition coap_str.h:214
#define coap_string_equal(string1, string2)
Compares the two strings for equality.
Definition coap_str.h:200
coap_string_t * coap_new_string(size_t size)
Returns a new string object with at least size+1 bytes storage allocated.
Definition coap_str.c:21
coap_str_const_t * coap_new_str_const(const uint8_t *data, size_t size)
Returns a new const string object with at least size+1 bytes storage allocated, and the provided data...
Definition coap_str.c:51
void coap_delete_string(coap_string_t *s)
Deletes the given string and releases any memory allocated.
Definition coap_str.c:46
int coap_cancel_observe_lkd(coap_session_t *session, coap_binary_t *token, coap_pdu_type_t message_type)
Cancel an observe that is being tracked by the client large receive logic.
int coap_q_block_is_supported(void)
Check whether Q-BlockX is available.
Definition coap_block.c:37
#define COAP_STATIC_INLINE
Definition libcoap.h:57
coap_address_t remote
remote address and port
Definition coap_io.h:60
coap_address_t local
local address and port
Definition coap_io.h:61
CoAP binary data definition with const data.
Definition coap_str.h:67
size_t length
length of binary data
Definition coap_str.h:68
const uint8_t * s
read-only binary data
Definition coap_str.h:69
CoAP binary data definition.
Definition coap_str.h:59
size_t length
length of binary data
Definition coap_str.h:60
uint8_t * s
binary data
Definition coap_str.h:61
Structure of Block options with BERT support.
Definition coap_block.h:55
unsigned int num
block number
Definition coap_block.h:56
uint32_t chunk_size
‍1024 if BERT
Definition coap_block.h:62
unsigned int bert
Operating as BERT.
Definition coap_block.h:61
unsigned int aszx
block size (0-7 including BERT
Definition coap_block.h:59
unsigned int defined
Set if block found.
Definition coap_block.h:60
unsigned int m
1 if more blocks follow, 0 otherwise
Definition coap_block.h:57
unsigned int szx
block size (0-6)
Definition coap_block.h:58
Structure of Block options.
Definition coap_block.h:46
unsigned int num
block number
Definition coap_block.h:47
unsigned int szx
block size
Definition coap_block.h:49
unsigned int m
1 if more blocks follow, 0 otherwise
Definition coap_block.h:48
The CoAP stack's global state is stored in a coap_context_t object.
coap_block_data_handler_t block_data_handler
Called with each block data during block transfers.
uint32_t block_mode
Zero or more COAP_BLOCK_ or'd options.
coap_resource_t * proxy_uri_resource
can be used for handling proxy URI resources
coap_resource_t * unknown_resource
can be used for handling unknown resources
uint64_t state_token
state token
size_t bert_size
size of last BERT block
coap_address_t upstream
uint32_t count
the number of packets sent for payload
coap_binary_t * app_token
original PDU token
coap_pdu_code_t request_method
Method used to request this data.
uint8_t rtag_length
RTag length.
coap_string_t * query
Associated query for the resource.
uint64_t etag
ETag value.
coap_resource_t * resource
associated resource
coap_time_t maxage_expire
When this entry expires.
uint8_t rtag_set
Set if RTag is in receive PDU.
uint8_t rtag[8]
RTag for block checking.
Structure to hold large body (many blocks) client receive information.
uint16_t block_option
Block option in use.
uint8_t etag[8]
ETag for block checking.
uint8_t etag_length
ETag length.
uint8_t last_type
Last request type (CON/NON)
coap_lg_xmit_data_t * obs_data
lg_xmit data info if large observe request
uint8_t observe_length
Length of observe data.
uint8_t observe[3]
Observe data (if observe_set) (only 24 bits)
uint8_t etag_set
Set if ETag is in receive PDU.
coap_rblock_t rec_blocks
list of received blocks
coap_address_t upstream
Unicast IP of server if mcast client session.
uint8_t initial
If set, has not been used yet.
uint8_t szx
size of individual blocks
uint16_t o_block_option
Block CoAP option used when initiating Observe.
uint16_t content_format
Content format for the set of blocks.
uint8_t o_blk_size
Block size used when initiating Observe.
coap_tick_t last_used
Last time all data sent or 0.
coap_bin_const_t ** obs_token
Tokens used in setting up Observe (to handle large FETCH)
uint64_t state_token
state token
coap_binary_t * app_token
app requesting PDU token
coap_pdu_t * sent_pdu
The sent pdu with all the data.
uint16_t retry_counter
Retry counter (part of state token)
coap_binary_t * body_data
Used for re-assembling entire body.
size_t obs_token_cnt
number of tokens used to set up Observe
uint8_t observe_set
Set if this is an observe receive PDU.
size_t total_len
Length as indicated by SIZE2 option.
Structure to hold large body (many blocks) server receive information.
uint8_t rtag[8]
RTag for block checking.
coap_mid_t last_mid
Last received mid for this set of packets.
uint8_t rtag_set
Set if RTag is in receive PDU.
uint16_t block_option
Block option in use.
size_t total_len
Length as indicated by SIZE1 option.
uint8_t dont_timeout
Set if app handler in use.
uint8_t observe_length
Length of observe data.
coap_rblock_t rec_blocks
set to uri_path if unknown resource
uint8_t no_more_seen
Set if block with more not set seen.
coap_binary_t * body_data
Used for re-assembling entire body.
coap_resource_t * resource
associated resource
uint8_t observe_set
Set if this is an observe receive PDU.
uint8_t rtag_length
RTag length.
uint8_t last_type
Last request type (CON/NON)
uint8_t szx
size of individual blocks
coap_tick_t last_used
Last time data sent or 0.
uint8_t observe[3]
Observe data (if set) (only 24 bits)
uint16_t content_format
Content format for the set of blocks.
coap_bin_const_t * last_token
< list of received blocks
coap_str_const_t * uri_path
coap_get_large_data_t get_func
Where to get data id needed.
void * app_ptr
applicaton provided ptr for de-alloc function
uint32_t ref
Reference count.
const uint8_t * data
large data ptr
size_t length
large data length
coap_release_large_data_t release_func
large data de-alloc function
Structure to hold large body (many blocks) transmission information.
coap_tick_t last_all_sent
Last time all data sent or 0.
uint8_t blk_size
large block transmission size
coap_tick_t last_sent
Last time any data sent.
union coap_lg_xmit_t::@1 b
coap_lg_xmit_data_t * data_info
Pointer to large data information.
int last_block
last acknowledged block number Block1 last transmitted Q-Block2
coap_tick_t last_payload
Last time MAX_PAYLOAD was sent or 0.
size_t offset
large data next offset to transmit
coap_pdu_t * sent_pdu
The sent pdu with all the data.
coap_l_block1_t b1
coap_l_block2_t b2
uint16_t option
large block transmisson CoAP option
struct coap_lg_xmit_t * next
coap_tick_t last_obs
Last time used (Observe tracking) or 0.
Iterator to run through PDU options.
coap_option_num_t number
decoded option number
structure for CoAP PDUs
uint8_t * token
first byte of token (or extended length bytes prefix), if any, or options
coap_lg_xmit_t * lg_xmit
Holds ptr to lg_xmit if sending a set of blocks.
size_t body_length
Holds body data length.
size_t max_size
maximum size for token, options and payload, or zero for variable size pdu
const uint8_t * body_data
Holds ptr to re-assembled data or NULL.
size_t body_offset
Holds body data offset.
coap_pdu_code_t code
request method (value 1–31) or response code (value 64-255)
coap_bin_const_t actual_token
Actual token in pdu.
uint8_t * data
first byte of payload, if any
coap_mid_t mid
message id, if any, in regular host byte order
size_t used_size
used bytes of storage for token, options and payload
coap_session_t * session
Session responsible for PDU or NULL.
size_t body_total
Holds body data total size.
coap_pdu_type_t type
message type
Queue entry.
Structure to keep track of received blocks.
uint32_t total_blocks
Set to block no + 1 when More bit unset.
uint32_t used
Number of range blocks in use.
struct coap_lg_range range[COAP_RBLOCK_CNT]
Abstraction of resource that can be attached to coap_context_t.
unsigned int is_proxy_uri
resource created for proxy URI handler
unsigned int is_reverse_proxy
resource created for reverse proxy URI handler
int flags
zero or more COAP_RESOURCE_FLAGS_* or'd together
Abstraction of virtual session that can be attached to coap_context_t (client) or coap_endpoint_t (se...
coap_lg_xmit_t * lg_xmit
list of large transmissions
uint32_t block_mode
Zero or more COAP_BLOCK_ or'd options.
coap_socket_t sock
socket object for the session, if any
uint8_t csm_bert_rem_support
CSM TCP BERT blocks supported (remote)
uint64_t tx_token
Next token number to use.
coap_mid_t remote_test_mid
mid used for checking remote support
uint8_t csm_bert_loc_support
CSM TCP BERT blocks supported (local)
coap_addr_tuple_t addr_info
remote/local address info
coap_proto_t proto
protocol used
uint8_t con_active
Active CON request sent.
coap_queue_t * delayqueue
list of delayed messages waiting to be sent
uint32_t tx_rtag
Next Request-Tag number to use.
coap_lg_srcv_t * lg_srcv
Server list of expected large receives.
coap_lg_crcv_t * lg_crcv
Client list of expected large receives.
coap_session_type_t type
client or server side socket
coap_context_t * context
session's context
coap_bin_const_t * echo
last token used to make a request
coap_socket_flags_t flags
1 or more of COAP_SOCKET* flag values
CoAP string data definition.
Definition coap_str.h:41
uint8_t * s
string data
Definition coap_str.h:43
size_t length
length of string
Definition coap_str.h:42