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