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