libcoap 4.3.5-develop-f4a8fe1
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 coap_bin_const_t *k_token;
2572
2573#if NDEBUG
2574 (void)actual;
2575#endif /* NDEBUG */
2576 assert(session->block_mode & COAP_BLOCK_TRY_Q_BLOCK &&
2577 session->type == COAP_SESSION_TYPE_CLIENT &&
2578 COAP_PDU_IS_REQUEST(actual));
2579
2580 coap_log_debug("Testing for Q-Block support\n");
2581 /* RFC9177 Section 4.1 when checking if available */
2583 coap_new_message_id_lkd(session),
2585 if (!pdu) {
2586 return COAP_INVALID_MID;
2587 }
2588
2589 coap_session_new_token(session, &token_len, token);
2590 coap_add_token(pdu, token_len, token);
2591 /* Use a resource that the server MUST support (.well-known/core) */
2593 11, (const uint8_t *)".well-known");
2595 4, (const uint8_t *)"core");
2596 /*
2597 * M needs to be unset as 'asking' for only the first block using
2598 * Q-Block2 as a test for server support.
2599 * See RFC9177 Section 4.4 Using the Q-Block2 Option.
2600 *
2601 * As the client is asking for 16 byte chunks, it is unlikely that
2602 * the .well-known/core response will be 16 bytes or less, so
2603 * if the server supports Q-Block, it will be forced to respond with
2604 * a Q-Block2, so the client can detect the server Q-Block support.
2605 */
2607 coap_encode_var_safe(buf, sizeof(buf),
2608 (0 << 4) | (0 << 3) | 0),
2609 buf);
2610 k_token = coap_new_bin_const(pdu->actual_token.s, pdu->actual_token.length);
2611 set_block_mode_probe_q(session->block_mode);
2612 mid = coap_send_internal(session, pdu, NULL);
2613 if (mid == COAP_INVALID_MID) {
2614 coap_delete_bin_const(k_token);
2615 return COAP_INVALID_MID;
2616 }
2617 session->remote_test_mid = mid;
2619 session->last_token = k_token;
2620 return mid;
2621}
2622#endif /* COAP_Q_BLOCK_SUPPORT */
2623
2625coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu,
2626 coap_lg_xmit_t *lg_xmit) {
2627 coap_block_b_t block;
2628 coap_lg_crcv_t *lg_crcv;
2629 uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);
2630
2631 lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
2632
2633 if (lg_crcv == NULL)
2634 return NULL;
2635
2636 coap_log_debug("** %s: lg_crcv %p initialized - stateless token xxxxx%011llx\n",
2637 coap_session_str(session), (void *)lg_crcv,
2638 STATE_TOKEN_BASE(state_token));
2639 memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
2640 lg_crcv->initial = 1;
2641 coap_ticks(&lg_crcv->last_used);
2642 /* Keep a copy of the sent pdu */
2643 lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu);
2644 if (lg_xmit) {
2645 coap_opt_iterator_t opt_iter;
2646 coap_opt_t *opt;
2647
2648 opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter);
2649
2650 if (opt) {
2651 int observe_action;
2652
2653 observe_action = coap_decode_var_bytes(coap_opt_value(opt),
2654 coap_opt_length(opt));
2655 if (observe_action == COAP_OBSERVE_ESTABLISH) {
2656 /* Need to keep information for Observe Cancel */
2657 size_t data_len;
2658 const uint8_t *data;
2659
2660 if (coap_get_data(pdu, &data_len, &data)) {
2661 if (data_len < lg_xmit->data_info->length) {
2662 lg_xmit->data_info->ref++;
2663 lg_crcv->obs_data = lg_xmit->data_info;
2664 }
2665 }
2666 }
2667 }
2668 }
2669
2670 /* Need to keep original token for updating response PDUs */
2671 lg_crcv->app_token = coap_new_binary(pdu->actual_token.length);
2672 if (!lg_crcv->app_token) {
2673 coap_block_delete_lg_crcv(session, lg_crcv);
2674 return NULL;
2675 }
2676 memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length);
2677
2678 /* Need to set up a base token for actual communications if retries needed */
2679 lg_crcv->retry_counter = 1;
2680 lg_crcv->state_token = state_token;
2681 coap_address_copy(&lg_crcv->upstream, &session->addr_info.remote);
2682
2683 if (pdu->code == COAP_REQUEST_CODE_FETCH) {
2684 coap_bin_const_t *new_token;
2685
2686 /* Need to save/restore Observe Token for large FETCH */
2687 new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token);
2688 if (new_token)
2689 coap_update_token(pdu, new_token->length, new_token->s);
2690 }
2691
2692 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
2693 /* In case it is there - must not be in continuing request PDUs */
2694 lg_crcv->o_block_option = COAP_OPTION_BLOCK1;
2695 lg_crcv->o_blk_size = block.aszx;
2696 }
2697
2698 return lg_crcv;
2699}
2700
2701void
2702coap_block_delete_lg_crcv(coap_session_t *session,
2703 coap_lg_crcv_t *lg_crcv) {
2704 size_t i;
2705
2706#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2707 (void)session;
2708#endif
2709 if (lg_crcv == NULL)
2710 return;
2711
2712 if (lg_crcv->ref > 0) {
2713 lg_crcv->ref--;
2714 return;
2715 }
2716
2717 coap_free_type(COAP_STRING, lg_crcv->body_data);
2718 if (lg_crcv->obs_data) {
2719 coap_block_release_lg_xmit_data(session, lg_crcv->obs_data);
2720 lg_crcv->obs_data = NULL;
2721 }
2722 coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream);
2723 coap_log_debug("** %s: lg_crcv %p released\n",
2724 coap_session_str(session), (void *)lg_crcv);
2725 coap_delete_binary(lg_crcv->app_token);
2726 for (i = 0; i < lg_crcv->obs_token_cnt; i++) {
2727 coap_delete_bin_const(lg_crcv->obs_token[i]);
2728 }
2729 coap_free_type(COAP_STRING, lg_crcv->obs_token);
2730 coap_delete_pdu_lkd(lg_crcv->sent_pdu);
2731 coap_free_type(COAP_LG_CRCV, lg_crcv);
2732}
2733#endif /* COAP_CLIENT_SUPPORT */
2734
2735#if COAP_SERVER_SUPPORT
2736void
2737coap_block_delete_lg_srcv(coap_session_t *session,
2738 coap_lg_srcv_t *lg_srcv) {
2739#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2740 (void)session;
2741#endif
2742 if (lg_srcv == NULL)
2743 return;
2744
2745 if (lg_srcv->ref > 0) {
2746 lg_srcv->ref--;
2747 return;
2748 }
2749
2750 coap_delete_str_const(lg_srcv->uri_path);
2751 coap_delete_bin_const(lg_srcv->last_token);
2752 coap_free_type(COAP_STRING, lg_srcv->body_data);
2753 coap_log_debug("** %s: lg_srcv %p released\n",
2754 coap_session_str(session), (void *)lg_srcv);
2755 coap_free_type(COAP_LG_SRCV, lg_srcv);
2756}
2757#endif /* COAP_SERVER_SUPPORT */
2758
2759void
2761 coap_lg_xmit_t *lg_xmit) {
2762 if (lg_xmit == NULL)
2763 return;
2764
2765 if (lg_xmit->ref > 0) {
2766 lg_xmit->ref--;
2767 return;
2768 }
2769
2770 coap_block_release_lg_xmit_data(session, lg_xmit->data_info);
2771 if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu))
2772 coap_delete_binary(lg_xmit->b.b1.app_token);
2773 else
2774 coap_delete_string(lg_xmit->b.b2.query);
2775 coap_delete_pdu_lkd(lg_xmit->sent_pdu);
2776
2777 coap_log_debug("** %s: lg_xmit %p released\n",
2778 coap_session_str(session), (void *)lg_xmit);
2779 coap_free_type(COAP_LG_XMIT, lg_xmit);
2780}
2781
2782#if COAP_SERVER_SUPPORT
2783typedef struct {
2784 uint32_t num;
2785 int is_continue;
2786} send_track;
2787
2788static int
2789add_block_send(uint32_t num, int is_continue, send_track *out_blocks,
2790 uint32_t *count, uint32_t max_count) {
2791 uint32_t i;
2792
2793 for (i = 0; i < *count && *count < max_count; i++) {
2794 if (num == out_blocks[i].num)
2795 return 0;
2796 else if (num < out_blocks[i].num) {
2797 if (*count - i > 1)
2798 memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
2799 out_blocks[i].num = num;
2800 out_blocks[i].is_continue = is_continue;
2801 (*count)++;
2802 return 1;
2803 }
2804 }
2805 if (*count < max_count) {
2806 out_blocks[i].num = num;
2807 out_blocks[i].is_continue = is_continue;
2808 (*count)++;
2809 return 1;
2810 }
2811 return 0;
2812}
2813
2814/*
2815 * Need to see if this is a request for the next block of a large body
2816 * transfer. If so, need to initiate the response with the next blocks
2817 * and not trouble the application.
2818 *
2819 * If additional responses needed, then these are expicitly sent out and
2820 * 'response' is updated to be the last response to be sent. There can be
2821 * multiple Q-Block2 in the request, as well as the 'Continue' Q-Block2
2822 * request.
2823 *
2824 * This is set up using coap_add_data_large_response_lkd()
2825 *
2826 * Server is sending a large data response to GET / observe (Block2)
2827 *
2828 * Return: 0 Call application handler
2829 * 1 Do not call application handler - just send the built response
2830 */
2831int
2832coap_handle_request_send_block(coap_session_t *session,
2833 coap_pdu_t *pdu,
2834 coap_pdu_t *response,
2835 coap_resource_t *resource,
2836 coap_string_t *query) {
2837 coap_lg_xmit_t *lg_xmit = NULL;
2838 coap_block_b_t block;
2839 coap_block_b_t alt_block;
2840 uint16_t block_opt = 0;
2841 send_track *out_blocks = NULL;
2842 const char *error_phrase;
2843 coap_opt_iterator_t opt_iter;
2844 size_t chunk;
2845 coap_opt_iterator_t opt_b_iter;
2846 coap_opt_t *option;
2847 uint32_t request_cnt, i;
2848 coap_opt_t *etag_opt = NULL;
2849 coap_pdu_t *out_pdu = response;
2850#if COAP_Q_BLOCK_SUPPORT
2851 size_t max_block;
2852
2853 /* Is client indicating that it supports Q_BLOCK2 ? */
2854 if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
2855 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK))
2856 set_block_mode_has_q(session->block_mode);
2857 block_opt = COAP_OPTION_Q_BLOCK2;
2858 }
2859#endif /* COAP_Q_BLOCK_SUPPORT */
2860 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &alt_block)) {
2861 if (block_opt) {
2862 coap_log_warn("Block2 and Q-Block2 cannot be in the same request\n");
2863 coap_add_data(response, sizeof("Both Block2 and Q-Block2 invalid")-1,
2864 (const uint8_t *)"Both Block2 and Q-Block2 invalid");
2865 response->code = COAP_RESPONSE_CODE(400);
2866 goto skip_app_handler;
2867 }
2868 block = alt_block;
2869 block_opt = COAP_OPTION_BLOCK2;
2870 }
2871 if (block_opt == 0)
2872 return 0;
2873 if (block.num == 0) {
2874 /* Get a fresh copy of the data */
2875 return 0;
2876 }
2877 lg_xmit = coap_find_lg_xmit_response(session, pdu, resource, query);
2878 if (lg_xmit == NULL)
2879 return 0;
2880
2881#if COAP_Q_BLOCK_SUPPORT
2882 out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track) * COAP_MAX_PAYLOADS(session));
2883#else /* ! COAP_Q_BLOCK_SUPPORT */
2884 out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track));
2885#endif /* ! COAP_Q_BLOCK_SUPPORT */
2886 if (!out_blocks) {
2887 goto internal_issue;
2888 }
2889
2890 /* lg_xmit (response) found */
2891
2892 etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
2893 if (etag_opt) {
2894 /* There may be multiple ETag - need to check each one */
2895 coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
2896 while ((etag_opt = coap_option_next(&opt_iter))) {
2897 if (opt_iter.number == COAP_OPTION_ETAG) {
2898 uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
2899 coap_opt_length(etag_opt));
2900 if (etag == lg_xmit->b.b2.etag) {
2901 break;
2902 }
2903 }
2904 }
2905 if (!etag_opt) {
2906 /* Not a match - pass up to a higher level */
2907 return 0;
2908 }
2909 }
2910 out_pdu->code = lg_xmit->sent_pdu->code;
2911 coap_ticks(&lg_xmit->last_obs);
2912 lg_xmit->last_all_sent = 0;
2913
2914 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2915 if (block_opt) {
2916 if (block.bert) {
2917 coap_log_debug("found Block option, block is BERT, block nr. %u, M %d\n",
2918 block.num, block.m);
2919 } else {
2920 coap_log_debug("found Block option, block size is %u, block nr. %u, M %d\n",
2921 1 << (block.szx + 4), block.num, block.m);
2922 }
2923 if (block.bert == 0 && block.szx != lg_xmit->blk_size) {
2924 if (block.num == 0) {
2925 if (block.szx < lg_xmit->blk_size) {
2926 /*
2927 * Recompute the block number of the previous packet given
2928 * the new block size
2929 */
2930 block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1);
2931 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2932#if COAP_Q_BLOCK_SUPPORT
2933 lg_xmit->send_blocks.range[0].begin = block.num;
2934 lg_xmit->send_blocks.range[0].end = (uint32_t)(lg_xmit->data_info->length / chunk);
2935#endif /* COAP_Q_BLOCK_SUPPORT */
2936 lg_xmit->blk_size = block.szx;
2937 coap_log_debug("new Block size is %u, block number %u completed\n",
2938 (1 << (block.szx + 4)), block.num);
2939 } else {
2940 coap_log_debug("ignoring request to increase Block size from %u to %u\n",
2941 (1 << (lg_xmit->blk_size + 4)), (1 << (lg_xmit->blk_size + 4)));
2942 }
2943 } else {
2944 coap_log_debug("ignoring request to change Block size from %u to %u\n",
2945 (1 << (lg_xmit->blk_size + 4)), (1 << (block.szx + 4)));
2946 block.szx = block.aszx = lg_xmit->blk_size;
2947 }
2948 }
2949 }
2950
2951 /*
2952 * Need to check if there are multiple Q-Block2 requests. If so, they
2953 * need to be sent out in order of requests with the final request being
2954 * handled as per singular Block 2 request.
2955 */
2956 request_cnt = 0;
2957#if COAP_Q_BLOCK_SUPPORT
2958 max_block = (lg_xmit->data_info->length + chunk - 1)/chunk;
2959#endif /* COAP_Q_BLOCK_SUPPORT */
2960 coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
2961 while ((option = coap_option_next(&opt_b_iter))) {
2962 uint32_t num;
2963 if (opt_b_iter.number != lg_xmit->option)
2964 continue;
2965 num = coap_opt_block_num(option);
2966 if (num > 0xFFFFF) /* 20 bits max for num */
2967 continue;
2968 if (block.aszx != COAP_OPT_BLOCK_SZX(option)) {
2969 coap_add_data(response,
2970 sizeof("Changing blocksize during request invalid")-1,
2971 (const uint8_t *)"Changing blocksize during request invalid");
2972 response->code = COAP_RESPONSE_CODE(400);
2973 goto skip_app_handler;
2974 }
2975#if COAP_Q_BLOCK_SUPPORT
2976 if (COAP_OPT_BLOCK_MORE(option) && lg_xmit->option == COAP_OPTION_Q_BLOCK2) {
2977 if ((num % COAP_MAX_PAYLOADS(session)) == 0) {
2978 if (num == 0) {
2979 /* This is a repeat request for everything - hmm */
2980 goto call_app_handler;
2981 }
2982 /* 'Continue' request */
2983 for (i = 0; i < COAP_MAX_PAYLOADS(session) &&
2984 num + i < max_block; i++) {
2985 add_block_send(num + i, 1, out_blocks, &request_cnt,
2986 COAP_MAX_PAYLOADS(session));
2987 lg_xmit->last_block = num + i;
2988 }
2989 } else {
2990 /* Requesting remaining payloads in this MAX_PAYLOADS */
2991 for (i = 0; i < COAP_MAX_PAYLOADS(session) -
2992 num % COAP_MAX_PAYLOADS(session) &&
2993 num + i < max_block; i++) {
2994 add_block_send(num + i, 0, out_blocks, &request_cnt,
2995 COAP_MAX_PAYLOADS(session));
2996 }
2997 }
2998 } else
2999 add_block_send(num, 0, out_blocks, &request_cnt,
3000 COAP_MAX_PAYLOADS(session));
3001#else /* ! COAP_Q_BLOCK_SUPPORT */
3002 add_block_send(num, 0, out_blocks, &request_cnt, 1);
3003 break;
3004#endif /* ! COAP_Q_BLOCK_SUPPORT */
3005 }
3006 if (request_cnt == 0) {
3007 /* Block2 or Q-Block2 not found - give them the first block */
3008 block.szx = lg_xmit->blk_size;
3009 out_blocks[0].num = 0;
3010 out_blocks[0].is_continue = 0;
3011 request_cnt = 1;
3012 }
3013
3014 for (i = 0; i < request_cnt; i++) {
3015 uint8_t buf[8];
3016 size_t offset;
3017
3018 block.num = out_blocks[i].num;
3019
3020 if (i + 1 < request_cnt) {
3021 /* Need to set up a copy of the pdu to send */
3022 coap_opt_filter_t drop_options;
3023
3024 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
3025 if (block.num != 0)
3027 if (out_blocks[i].is_continue) {
3028 out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
3029 lg_xmit->sent_pdu->actual_token.length,
3030 lg_xmit->sent_pdu->actual_token.s,
3031 &drop_options, COAP_BOOL_FALSE);
3032 } else {
3033 out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
3034 pdu->actual_token.length,
3035 pdu->actual_token.s,
3036 &drop_options, COAP_BOOL_FALSE);
3037 }
3038 if (!out_pdu) {
3039 goto internal_issue;
3040 }
3041 } else {
3042 if (out_blocks[i].is_continue)
3043 coap_update_token(response, lg_xmit->sent_pdu->actual_token.length,
3044 lg_xmit->sent_pdu->actual_token.s);
3045 /*
3046 * Copy the options across and then fix the block option
3047 *
3048 * Need to drop Observe option if Block2 and block.num != 0
3049 */
3050 coap_option_iterator_init(lg_xmit->sent_pdu, &opt_iter, COAP_OPT_ALL);
3051 while ((option = coap_option_next(&opt_iter))) {
3052 if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0)
3053 continue;
3054 if (!coap_insert_option(response, opt_iter.number,
3055 coap_opt_length(option),
3056 coap_opt_value(option))) {
3057 goto internal_issue;
3058 }
3059 }
3060 out_pdu = response;
3061 }
3062 if (pdu->type == COAP_MESSAGE_NON)
3063 out_pdu->type = COAP_MESSAGE_NON;
3064 offset = block.num * chunk;
3065 if (block.bert) {
3066 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
3067 block.m = (lg_xmit->data_info->length - offset) >
3068 ((out_pdu->max_size - token_options) /1024) * 1024;
3069 } else {
3070 block.m = (offset + chunk) < lg_xmit->data_info->length;
3071 }
3072 if (!coap_update_option(out_pdu, lg_xmit->option,
3074 sizeof(buf),
3075 (block.num << 4) |
3076 (block.m << 3) |
3077 block.aszx),
3078 buf)) {
3079 goto internal_issue;
3080 }
3081 if (!(offset + chunk < lg_xmit->data_info->length)) {
3082 /* Last block - keep in cache for 4 * ACK_TIMOUT */
3083 coap_ticks(&lg_xmit->last_all_sent);
3084 }
3085 if (lg_xmit->b.b2.maxage_expire) {
3086 coap_tick_t now;
3087 coap_time_t rem;
3088
3089 if (!(offset + chunk < lg_xmit->data_info->length)) {
3090 /* Last block - keep in cache for 4 * ACK_TIMOUT */
3091 coap_ticks(&lg_xmit->last_all_sent);
3092 }
3093 coap_ticks(&now);
3094 rem = coap_ticks_to_rt(now);
3095 if (lg_xmit->b.b2.maxage_expire > rem) {
3096 rem = lg_xmit->b.b2.maxage_expire - rem;
3097 } else {
3098 rem = 0;
3099 /* Entry needs to be expired */
3100 coap_ticks(&lg_xmit->last_all_sent);
3101 }
3104 sizeof(buf),
3105 rem),
3106 buf)) {
3107 goto internal_issue;
3108 }
3109 }
3110
3111 if (!coap_add_block_b_data(out_pdu,
3112 lg_xmit->data_info->length,
3113 lg_xmit->data_info->data,
3114 &block)) {
3115 goto internal_issue;
3116 }
3117 if (i + 1 < request_cnt) {
3118 coap_ticks(&lg_xmit->last_sent);
3119 coap_send_internal(session, out_pdu, NULL);
3120 }
3121 }
3122 coap_ticks(&lg_xmit->last_payload);
3123 goto skip_app_handler;
3124#if COAP_Q_BLOCK_SUPPORT
3125call_app_handler:
3126 coap_free_type(COAP_STRING, out_blocks);
3127 return 0;
3128#endif /* COAP_Q_BLOCK_SUPPORT */
3129
3130internal_issue:
3131 response->code = COAP_RESPONSE_CODE(500);
3132 error_phrase = coap_response_phrase(response->code);
3133 coap_add_data(response, strlen(error_phrase),
3134 (const uint8_t *)error_phrase);
3135 /* Keep in cache for 4 * ACK_TIMOUT incase of retry */
3136 if (lg_xmit)
3137 coap_ticks(&lg_xmit->last_all_sent);
3138
3139skip_app_handler:
3140 coap_free_type(COAP_STRING, out_blocks);
3141 return 1;
3142}
3143#endif /* COAP_SERVER_SUPPORT */
3144
3145#if COAP_Q_BLOCK_SUPPORT
3146static int
3147blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num) {
3148 uint32_t i;
3149
3150 if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) {
3151 /* received block number greater than Block No defined when More bit unset */
3152 return 0;
3153 }
3154
3155 for (i = 0; i < rec_blocks->used; i++) {
3156 if (block_num >= rec_blocks->range[i].begin &&
3157 block_num <= rec_blocks->range[i].end) {
3158 /* In this block */
3159 if (block_num == rec_blocks->range[i].begin) {
3160 if (block_num == rec_blocks->range[i].end) {
3161 /* Need to delete this range */
3162 if (i + 1 < rec_blocks->used) {
3163 memmove(&rec_blocks->range[i], &rec_blocks->range[i+1],
3164 (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3165 }
3166 rec_blocks->used--;
3167 break;
3168 }
3169 rec_blocks->range[i].begin++;
3170 } else if (block_num == rec_blocks->range[i].end) {
3171 rec_blocks->range[i].end--;
3172 if (rec_blocks->range[i].begin == rec_blocks->range[i].end) {
3173 /* Need to delete this range */
3174 rec_blocks->used--;
3175 if (i == rec_blocks->used)
3176 break;
3177 memmove(&rec_blocks->range[i], &rec_blocks->range[i+1],
3178 sizeof(rec_blocks->range[i]) * (rec_blocks->used - i));
3179 }
3180 } else {
3181 /* Need to split the range */
3182 if (rec_blocks->used == COAP_RBLOCK_CNT)
3183 /* Too many losses */
3184 return 0;
3185 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
3186 (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3187 rec_blocks->range[i].end = block_num - 1;
3188 rec_blocks->range[i+1].begin = block_num + 1;
3189 rec_blocks->used++;
3190 }
3191 break;
3192 }
3193 }
3194 coap_ticks(&rec_blocks->last_seen);
3195 return 1;
3196}
3197#endif /* COAP_Q_BLOCK_SUPPORT */
3198
3199static int
3200blocks_add_entry(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m) {
3201 uint32_t i;
3202
3203 if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) {
3204 /* received block number greater than Block No defined when More bit unset */
3205 return 0;
3206 }
3207
3208 /* Reset as there is activity */
3209 rec_blocks->retry = 0;
3210
3211 for (i = 0; i < rec_blocks->used; i++) {
3212 if (block_num >= rec_blocks->range[i].begin &&
3213 block_num <= rec_blocks->range[i].end)
3214 break;
3215
3216 if (block_num < rec_blocks->range[i].begin) {
3217 if (block_num + 1 == rec_blocks->range[i].begin) {
3218 rec_blocks->range[i].begin = block_num;
3219 } else {
3220 /* Need to insert a new range */
3221 if (rec_blocks->used == COAP_RBLOCK_CNT)
3222 /* Too many losses */
3223 return 0;
3224 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
3225 (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3226 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
3227 rec_blocks->used++;
3228 }
3229 break;
3230 }
3231 if (block_num == rec_blocks->range[i].end + 1) {
3232 rec_blocks->range[i].end = block_num;
3233 if (i + 1 < rec_blocks->used) {
3234 if (rec_blocks->range[i+1].begin == block_num + 1) {
3235 /* Merge the 2 ranges */
3236 rec_blocks->range[i].end = rec_blocks->range[i+1].end;
3237 if (i+2 < rec_blocks->used) {
3238 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i+2],
3239 (rec_blocks->used - (i+2)) * sizeof(rec_blocks->range[0]));
3240 }
3241 rec_blocks->used--;
3242 }
3243 }
3244 break;
3245 }
3246 }
3247 if (i == rec_blocks->used) {
3248 if (rec_blocks->used == COAP_RBLOCK_CNT)
3249 /* Too many losses */
3250 return 0;
3251 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
3252 rec_blocks->used++;
3253 }
3254 if (!block_m)
3255 rec_blocks->total_blocks = block_num + 1;
3256
3257 coap_ticks(&rec_blocks->last_seen);
3258 return 1;
3259}
3260
3261#if COAP_SERVER_SUPPORT
3262/*
3263 * Need to check if this is a large PUT / POST etc. using multiple blocks
3264 *
3265 * Server receiving PUT/POST etc. of a large amount of data (Block1)
3266 *
3267 * Return: 0 Call application handler
3268 * 1 Do not call application handler - just send the built response
3269 */
3270int
3271coap_handle_request_put_block(coap_context_t *context,
3272 coap_session_t *session,
3273 coap_pdu_t *pdu,
3274 coap_pdu_t *response,
3275 coap_resource_t *resource,
3276 coap_string_t *uri_path,
3277 coap_opt_t *observe,
3278 int *added_block,
3279 coap_lg_srcv_t **pfree_lg_srcv) {
3280 size_t length = 0;
3281 const uint8_t *data = NULL;
3282 size_t offset = 0;
3283 size_t total = 0;
3284 coap_block_b_t block;
3285 coap_opt_iterator_t opt_iter;
3286 uint16_t block_option = 0;
3287 coap_lg_srcv_t *lg_srcv;
3288 coap_opt_t *size_opt;
3289 coap_opt_t *fmt_opt;
3290 uint16_t fmt;
3291 coap_opt_t *rtag_opt;
3292 size_t rtag_length;
3293 const uint8_t *rtag;
3294 uint32_t max_block_szx;
3295 int update_data;
3296 unsigned int saved_num;
3297 size_t saved_offset;
3298 int lg_srcv_is_refed = 0;
3299
3300 *added_block = 0;
3301 *pfree_lg_srcv = NULL;
3302 coap_get_data_large(pdu, &length, &data, &offset, &total);
3303 pdu->body_offset = 0;
3304 pdu->body_total = length;
3305
3306 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
3307 block_option = COAP_OPTION_BLOCK1;
3308#if COAP_Q_BLOCK_SUPPORT
3309 if (coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter)) {
3310 /* Cannot handle Q-Block1 as well */
3311 coap_add_data(response, sizeof("Block1 + Q-Block1 together")-1,
3312 (const uint8_t *)"Block1 + Q-Block1 together");
3313 response->code = COAP_RESPONSE_CODE(402);
3314 goto skip_app_handler;
3315 }
3316#endif /* COAP_Q_BLOCK_SUPPORT */
3317 }
3318#if COAP_Q_BLOCK_SUPPORT
3319 else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
3320 block_option = COAP_OPTION_Q_BLOCK1;
3321 set_block_mode_has_q(session->block_mode);
3322 }
3323#endif /* COAP_Q_BLOCK_SUPPORT */
3324 if (!block_option ||
3325 (block_option == COAP_OPTION_BLOCK1 && block.num == 0 && block.m == 0)) {
3326 /* Not blocked, or a single block */
3327 if (context->max_body_size && total > context->max_body_size) {
3328 uint8_t buf[4];
3329
3330 coap_update_option(response,
3332 coap_encode_var_safe((uint8_t *)buf, sizeof(buf),
3333 context->max_body_size),
3334 (uint8_t *)buf);
3335 response->code = COAP_RESPONSE_CODE(413);
3336 coap_log_warn("Unable to handle data size %" PRIuS " (max %" PRIu32 ")\n", total,
3337 context->max_body_size);
3338 goto skip_app_handler;
3339 }
3340 goto call_app_handler;
3341 }
3342
3343 size_opt = coap_check_option(pdu,
3345 &opt_iter);
3346 fmt_opt = coap_check_option(pdu,
3348 &opt_iter);
3349 fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
3350 coap_opt_length(fmt_opt)) :
3352 rtag_opt = coap_check_option(pdu,
3354 &opt_iter);
3355 rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
3356 rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
3357
3358 if (length > block.chunk_size) {
3359 coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %" PRIuS "\n",
3360 block.chunk_size, length);
3361 length = block.chunk_size;
3362 } else if (!block.bert && block.m && length != block.chunk_size) {
3363 coap_log_info("block: Undersized packet chunk %"PRIu32" got %" PRIuS "\n",
3364 block.chunk_size, length);
3365 response->code = COAP_RESPONSE_CODE(400);
3366 goto skip_app_handler;
3367 }
3368 total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
3369 coap_opt_length(size_opt)) : 0;
3370 if (total) {
3371 uint32_t max_body;
3372
3373 max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3374 if (max_block_szx == 0 || max_block_szx > block.szx) {
3375 max_block_szx = block.szx;
3376 }
3377 max_body = ((1UL << 20) * (1 << (max_block_szx + 4)));
3378 if (max_body > MAX_BLK_LEN)
3379 max_body = MAX_BLK_LEN;
3380 if ((context->max_body_size && total > context->max_body_size) ||
3381 (total > max_body)) {
3382 /* Suggested body size larger than allowed */
3383 char buf[32];
3384 uint32_t max_body_size = context->max_body_size;
3385
3386 if (max_body_size == 0 || max_body < max_body_size) {
3387 max_body_size = max_body;
3388 }
3389 coap_update_option(response,
3391 coap_encode_var_safe((uint8_t *)buf, sizeof(buf),
3392 max_body_size),
3393 (uint8_t *)buf);
3394 snprintf(buf, sizeof(buf), "Max body size %" PRIu32, max_body_size);
3395 coap_add_data(response, strlen(buf), (uint8_t *)buf);
3396 response->code = COAP_RESPONSE_CODE(413);
3397 coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", total, max_body_size);
3398 goto skip_app_handler;
3399 }
3400 }
3401 offset = block.num << (block.szx + 4);
3402
3403 if (!(session->block_mode &
3404#if COAP_Q_BLOCK_SUPPORT
3406#else /* COAP_Q_BLOCK_SUPPORT */
3408#endif /* COAP_Q_BLOCK_SUPPORT */
3409 && !block.bert) {
3410 uint8_t buf[4];
3411
3412 /* Ask for the next block */
3413 coap_insert_option(response, block_option,
3414 coap_encode_var_safe(buf, sizeof(buf),
3415 (block.num << 4) |
3416 (block.m << 3) |
3417 block.aszx),
3418 buf);
3419 /* Not re-assembling or checking for receipt order */
3420 pdu->body_data = data;
3421 pdu->body_length = length;
3422 pdu->body_offset = offset;
3423 if (total < (length + offset + (block.m ? 1 : 0)))
3424 total = length + offset + (block.m ? 1 : 0);
3425 pdu->body_total = total;
3426 *added_block = block.m;
3427 /* The application is responsible for returning the correct 2.01/2.04/2.31 etc. */
3428 goto call_app_handler;
3429 }
3430
3431 /*
3432 * locate the lg_srcv
3433 */
3434 LL_FOREACH(session->lg_srcv, lg_srcv) {
3435 if (rtag_opt || lg_srcv->rtag_set == 1) {
3436 if (!(rtag_opt && lg_srcv->rtag_set == 1))
3437 continue;
3438 if (lg_srcv->rtag_length != rtag_length ||
3439 memcmp(lg_srcv->rtag, rtag, rtag_length) != 0)
3440 continue;
3441 }
3442 if (resource == lg_srcv->resource) {
3443 break;
3444 }
3445 if ((lg_srcv->resource == context->unknown_resource ||
3446 resource == context->proxy_uri_resource) &&
3447 coap_string_equal(uri_path, lg_srcv->uri_path))
3448 break;
3449 }
3450
3451 if (!lg_srcv && block.num != 0 && session->block_mode & COAP_BLOCK_NOT_RANDOM_BLOCK1) {
3452 coap_add_data(response, sizeof("Missing block 0")-1,
3453 (const uint8_t *)"Missing block 0");
3454 response->code = COAP_RESPONSE_CODE(408);
3455 goto skip_app_handler;
3456 }
3457
3458 if (!lg_srcv) {
3459 /* Allocate lg_srcv to use for tracking */
3460 lg_srcv = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t));
3461 if (lg_srcv == NULL) {
3462 coap_add_data(response, sizeof("Memory issue")-1,
3463 (const uint8_t *)"Memory issue");
3464 response->code = COAP_RESPONSE_CODE(500);
3465 goto skip_app_handler;
3466 }
3467 coap_log_debug("** %s: lg_srcv %p initialized\n",
3468 coap_session_str(session), (void *)lg_srcv);
3469 memset(lg_srcv, 0, sizeof(coap_lg_srcv_t));
3470 lg_srcv->resource = resource;
3471 if (resource == context->unknown_resource ||
3472 resource == context->proxy_uri_resource)
3473 lg_srcv->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
3474 lg_srcv->content_format = fmt;
3475 lg_srcv->total_len = total;
3476 max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3477 if (!block.bert && block.num == 0 && max_block_szx != 0 &&
3478 max_block_szx < block.szx) {
3479 lg_srcv->szx = max_block_szx;
3480 } else {
3481 lg_srcv->szx = block.szx;
3482 }
3483 lg_srcv->block_option = block_option;
3484 if (observe) {
3485 lg_srcv->observe_length = min(coap_opt_length(observe), 3);
3486 memcpy(lg_srcv->observe, coap_opt_value(observe), lg_srcv->observe_length);
3487 lg_srcv->observe_set = 1;
3488 }
3489 if (rtag_opt) {
3490 lg_srcv->rtag_length = coap_opt_length(rtag_opt);
3491 memcpy(lg_srcv->rtag, coap_opt_value(rtag_opt), lg_srcv->rtag_length);
3492 lg_srcv->rtag_set = 1;
3493 }
3494 lg_srcv->body_data = NULL;
3495 LL_PREPEND(session->lg_srcv, lg_srcv);
3496 }
3497 coap_ticks(&lg_srcv->last_used);
3498 coap_lg_srcv_reference_lkd(lg_srcv);
3499 lg_srcv_is_refed = 1;
3500
3501 if (block_option == COAP_OPTION_BLOCK1 &&
3503 !check_if_next_block(&lg_srcv->rec_blocks, block.num)) {
3504 coap_add_data(response, sizeof("Missing interim block")-1,
3505 (const uint8_t *)"Missing interim block");
3506 response->code = COAP_RESPONSE_CODE(408);
3507 goto skip_app_handler;
3508 }
3509
3510 if (fmt != lg_srcv->content_format) {
3511 coap_add_data(response, sizeof("Content-Format mismatch")-1,
3512 (const uint8_t *)"Content-Format mismatch");
3513 response->code = COAP_RESPONSE_CODE(408);
3514 goto free_lg_srcv;
3515 }
3516
3517#if COAP_Q_BLOCK_SUPPORT
3518 if (block_option == COAP_OPTION_Q_BLOCK1) {
3519 if (total != lg_srcv->total_len) {
3520 coap_add_data(response, sizeof("Size1 mismatch")-1,
3521 (const uint8_t *)"Size1 mismatch");
3522 response->code = COAP_RESPONSE_CODE(408);
3523 goto free_lg_srcv;
3524 }
3525 coap_delete_bin_const(lg_srcv->last_token);
3526 lg_srcv->last_token = coap_new_bin_const(pdu->actual_token.s,
3527 pdu->actual_token.length);
3528 }
3529#endif /* COAP_Q_BLOCK_SUPPORT */
3530
3531 lg_srcv->last_mid = pdu->mid;
3532 lg_srcv->last_type = pdu->type;
3533
3534 update_data = 0;
3535 saved_num = block.num;
3536 saved_offset = offset;
3537
3538 while (offset < saved_offset + length) {
3539 if (!check_if_received_block(&lg_srcv->rec_blocks, block.num)) {
3540 /* Update list of blocks received */
3541 if (blocks_add_entry(&lg_srcv->rec_blocks, block.num, block.m)) {
3542 update_data = 1;
3543 } else {
3544 coap_log_debug("Block nr %u ignored (too many missing blocks)\n", block.num);
3545 }
3546 } else {
3547 coap_log_debug("Duplicate block nr %u\n", block.num);
3548 }
3549 block.num++;
3550 offset = block.num << (block.szx + 4);
3551 }
3552 block.num--;
3553
3554 if (update_data) {
3555 /* Update saved data */
3556#if COAP_Q_BLOCK_SUPPORT
3557 lg_srcv->rec_blocks.processing_payload_set =
3558 block.num / COAP_MAX_PAYLOADS(session);
3559#endif /* COAP_Q_BLOCK_SUPPORT */
3560 if (lg_srcv->total_len < saved_offset + length) {
3561 lg_srcv->total_len = saved_offset + length;
3562 }
3563
3564#define USE_BLOCK_DATA_HANDLER (context && context->block_data_cb && \
3565 !resource->is_proxy_uri && \
3566 !resource->is_reverse_proxy && \
3567 ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) && \
3568 (resource->flags & COAP_RESOURCE_USE_BLOCK_DATA_HANDLER))
3569
3570 if (USE_BLOCK_DATA_HANDLER) {
3571 coap_response_t resp;
3572
3574 context->block_data_cb(session, pdu, resource,
3575 &lg_srcv->body_data,
3576 length, data, saved_offset,
3577 lg_srcv->total_len));
3578 if (resp != COAP_RESPONSE_OK) {
3579 response->code = COAP_RESPONSE_CODE(500);
3580 goto skip_app_handler;
3581 }
3582 } else {
3583 lg_srcv->body_data = coap_block_build_body_lkd(lg_srcv->body_data, length, data,
3584 saved_offset, lg_srcv->total_len);
3585 if (!lg_srcv->body_data) {
3586 coap_add_data(response, sizeof("Memory issue")-1,
3587 (const uint8_t *)"Memory issue");
3588 response->code = COAP_RESPONSE_CODE(500);
3589 goto skip_app_handler;
3590 }
3591 }
3592 } else {
3593 goto skip_app_handler;
3594 }
3595
3596 if (block.m ||
3597 !check_all_blocks_in(&lg_srcv->rec_blocks)) {
3598 /* Not all the payloads of the body have arrived */
3599 if (block.m) {
3600 uint8_t buf[4];
3601
3602#if COAP_Q_BLOCK_SUPPORT
3603 if (block_option == COAP_OPTION_Q_BLOCK1) {
3604 if (check_all_blocks_in(&lg_srcv->rec_blocks)) {
3605 goto give_app_data;
3606 }
3607 if (lg_srcv->rec_blocks.used == 1 &&
3608 (lg_srcv->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1
3609 == COAP_MAX_PAYLOADS(session)) {
3610 if (block.num != lg_srcv->rec_blocks.range[0].end) {
3611 /* Blocks could arrive in wrong order */
3612 block.num = lg_srcv->rec_blocks.range[0].end;
3613 goto skip_app_handler;
3614 }
3615 } else if (lg_srcv->rec_blocks.used > 1 &&
3616 (block.num / COAP_MAX_PAYLOADS(session)) >
3617 (lg_srcv->rec_blocks.range[0].end / COAP_MAX_PAYLOADS(session)) &&
3618 (lg_srcv->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1 !=
3619 COAP_MAX_PAYLOADS(session)) {
3620 /*
3621 * Current MAX_PAYLOAD chunk is different to last MAX_PAYLOAD chunk
3622 * with some missing packets in last MAX_PAYLOAD chunk
3623 */
3624 request_missing_blocks(session, lg_srcv, block.num);
3625 goto skip_app_handler;
3626 } else {
3627 /* The remote end will be sending the next one unless this
3628 is a MAX_PAYLOADS and all previous have been received */
3629 goto skip_app_handler;
3630 }
3631 if (COAP_PROTO_RELIABLE(session->proto) ||
3632 pdu->type != COAP_MESSAGE_NON)
3633 goto skip_app_handler;
3634 }
3635#endif /* COAP_Q_BLOCK_SUPPORT */
3636
3637 /* Check to see if block size is getting forced down */
3638 max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3639 if (!block.bert && saved_num == 0 && max_block_szx != 0 &&
3640 max_block_szx < block.aszx) {
3641 block.aszx = max_block_szx;
3642 }
3643
3644 /*
3645 * If the last block has been seen, packets are coming in in
3646 * random order. If all blocks are now in, then need to send
3647 * complete payload to application and acknowledge this current
3648 * block.
3649 */
3650 if ((total == 0 && block.m) || !check_all_blocks_in(&lg_srcv->rec_blocks)) {
3651 /* Ask for the next block */
3652 coap_insert_option(response, block_option,
3653 coap_encode_var_safe(buf, sizeof(buf),
3654 (saved_num << 4) |
3655 (block.m << 3) |
3656 block.aszx),
3657 buf);
3658 response->code = COAP_RESPONSE_CODE(231);
3659 } else {
3660 /* Need to separately respond to this request */
3661 coap_pdu_t *tmp_pdu = coap_pdu_duplicate_lkd(response,
3662 session,
3663 response->actual_token.length,
3664 response->actual_token.s,
3666 if (tmp_pdu) {
3667 tmp_pdu->code = COAP_RESPONSE_CODE(231);
3668 if (coap_send_internal(session, tmp_pdu, NULL) == COAP_INVALID_MID) {
3669 /* lg_srcv may have got deleted */
3670 coap_lg_srcv_t *sg;
3671
3672 LL_FOREACH(session->lg_srcv, sg) {
3673 if (lg_srcv == sg) {
3674 /* Still there */
3675 break;
3676 }
3677 }
3678 if (!sg)
3679 goto skip_app_handler;
3680 }
3681 }
3682 if (lg_srcv->last_token) {
3683 coap_update_token(response, lg_srcv->last_token->length, lg_srcv->last_token->s);
3684 coap_update_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
3685 }
3686 /* Pass the assembled pdu and body to the application */
3687 goto give_app_data;
3688 }
3689 } else {
3690 /* block.m Block More option not set. Some outstanding blocks */
3691#if COAP_Q_BLOCK_SUPPORT
3692 if (block_option != COAP_OPTION_Q_BLOCK1) {
3693#endif /* COAP_Q_BLOCK_SUPPORT */
3694 /* Last chunk - but not all in */
3695 coap_ticks(&lg_srcv->last_used);
3696 lg_srcv->no_more_seen = 1;
3697 coap_delete_bin_const(lg_srcv->last_token);
3698 lg_srcv->last_token = coap_new_bin_const(pdu->actual_token.s,
3699 pdu->actual_token.length);
3700
3701 /*
3702 * Need to just ACK (no response code) to handle client's NSTART.
3703 * When final missing block comes in, we will pass all the data
3704 * for processing so a 2.01, 2.04 etc. code can be generated
3705 * and responded to as a separate response "RFC7252 5.2.2. Separate"
3706 * If missing block(s) do not come in, then will generate a 4.08
3707 * when lg_srcv times out.
3708 * Fall through to skip_app_handler.
3709 */
3710#if COAP_Q_BLOCK_SUPPORT
3711 } else {
3712 request_missing_blocks(session, lg_srcv, block.num);
3713 }
3714#endif /* COAP_Q_BLOCK_SUPPORT */
3715 }
3716 goto skip_app_handler;
3717 }
3718
3719 /*
3720 * Entire payload received.
3721 * Remove the Block1 option as passing all of the data to
3722 * application layer. Add back in observe option if appropriate.
3723 * Adjust all other information.
3724 */
3725give_app_data:
3726 if (lg_srcv->observe_set) {
3728 lg_srcv->observe_length, lg_srcv->observe);
3729 }
3730 coap_remove_option(pdu, block_option);
3731 if (lg_srcv->body_data) {
3732 pdu->body_data = lg_srcv->body_data->s;
3733 pdu->body_length = lg_srcv->total_len;
3734 } else {
3735 pdu->body_data = NULL;
3736 pdu->body_length = 0;
3737 }
3738 pdu->body_offset = 0;
3739 pdu->body_total = lg_srcv->total_len;
3740 if (USE_BLOCK_DATA_HANDLER) {
3741 /* Data has already been provided - do not duplicate */
3742 if (pdu->data) {
3743 pdu->used_size = pdu->data - pdu->token - 1;
3744 pdu->data = NULL;
3745 }
3746 }
3747 coap_log_debug("Server app version of updated PDU\n");
3749 lg_srcv->dont_timeout = 1;
3750 *pfree_lg_srcv = lg_srcv;
3751
3752call_app_handler:
3753 if (lg_srcv_is_refed)
3754 coap_lg_srcv_release_lkd(session, lg_srcv);
3755 return 0;
3756
3757free_lg_srcv:
3758 LL_DELETE(session->lg_srcv, lg_srcv);
3759 coap_block_delete_lg_srcv(session, lg_srcv);
3760
3761skip_app_handler:
3762 if (lg_srcv_is_refed)
3763 coap_lg_srcv_release_lkd(session, lg_srcv);
3764 return 1;
3765}
3766#endif /* COAP_SERVER_SUPPORT */
3767
3768#if COAP_CLIENT_SUPPORT
3769#if COAP_Q_BLOCK_SUPPORT
3770static uint32_t
3771derive_cbor_value(const uint8_t **bp, size_t rem_len) {
3772 uint32_t value = **bp & 0x1f;
3773 (*bp)++;
3774 if (value < 24) {
3775 return value;
3776 } else if (value == 24) {
3777 if (rem_len < 2)
3778 return (uint32_t)-1;
3779 value = **bp;
3780 (*bp)++;
3781 return value;
3782 } else if (value == 25) {
3783 if (rem_len < 3)
3784 return (uint32_t)-1;
3785 value = **bp << 8;
3786 (*bp)++;
3787 value |= **bp;
3788 (*bp)++;
3789 return value;
3790 }
3791 if (rem_len < 4)
3792 return (uint32_t)-1;
3793 value = (uint32_t)(**bp) << 24;
3794 (*bp)++;
3795 value |= **bp << 16;
3796 (*bp)++;
3797 value |= **bp << 8;
3798 (*bp)++;
3799 value |= **bp;
3800 (*bp)++;
3801 return value;
3802}
3803#endif /* COAP_Q_BLOCK_SUPPORT */
3804
3805static int
3806check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
3807 coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) {
3808 /* Check for Echo option for freshness */
3809 coap_opt_iterator_t opt_iter;
3810 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
3811
3812 if (opt) {
3813 if (sent || lg_xmit || lg_crcv) {
3814 /* Need to retransmit original request with Echo option added */
3815 coap_pdu_t *echo_pdu;
3816 coap_mid_t mid;
3817 const uint8_t *data;
3818 size_t data_len;
3819 int have_data = 0;
3820 uint8_t ltoken[8];
3821 size_t ltoken_len;
3822 uint64_t token;
3823
3824 if (sent) {
3825 if (coap_get_data(sent, &data_len, &data))
3826 have_data = 1;
3827 } else if (lg_xmit) {
3828 sent = lg_xmit->sent_pdu;
3829 if (lg_xmit->data_info->length) {
3830 size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
3831 size_t offset = (lg_xmit->last_block + 1) * blk_size;
3832 have_data = 1;
3833 data = &lg_xmit->data_info->data[offset];
3834 data_len = (lg_xmit->data_info->length - offset) > blk_size ? blk_size :
3835 lg_xmit->data_info->length - offset;
3836 }
3837 } else { /* lg_crcv */
3838 sent = lg_crcv->sent_pdu;
3839 if (coap_get_data(sent, &data_len, &data))
3840 have_data = 1;
3841 }
3842 if (lg_xmit) {
3843 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
3844 ++lg_xmit->b.b1.count);
3845 } else {
3846 token = STATE_TOKEN_FULL(lg_crcv->state_token,
3847 ++lg_crcv->retry_counter);
3848 }
3849 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
3850 echo_pdu = coap_pdu_duplicate_lkd(sent, session, ltoken_len, ltoken,
3852 if (!echo_pdu)
3853 return 0;
3854 if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
3855 coap_opt_length(opt), coap_opt_value(opt)))
3856 goto not_sent;
3857 if (have_data) {
3858 coap_add_data(echo_pdu, data_len, data);
3859 }
3860 /* Need to track Observe token change if Observe */
3861 track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token);
3862#if COAP_OSCORE_SUPPORT
3863 if (session->oscore_encryption &&
3864 (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) &&
3866 /* Need to update the base PDU's Token for closing down Observe */
3867 if (lg_xmit) {
3868 lg_xmit->b.b1.state_token = token;
3869 } else {
3870 lg_crcv->state_token = token;
3871 }
3872 }
3873#endif /* COAP_OSCORE_SUPPORT */
3874 mid = coap_send_internal(session, echo_pdu, NULL);
3875 if (mid == COAP_INVALID_MID)
3876 goto not_sent;
3877 return 1;
3878 } else {
3879 /* Need to save Echo option value to add to next reansmission */
3880not_sent:
3881 coap_delete_bin_const(session->echo);
3882 session->echo = coap_new_bin_const(coap_opt_value(opt),
3883 coap_opt_length(opt));
3884 }
3885 }
3886 return 0;
3887}
3888
3889static void
3890track_echo(coap_session_t *session, coap_pdu_t *rcvd) {
3891 coap_opt_iterator_t opt_iter;
3892 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
3893
3894 if (opt) {
3895 coap_delete_bin_const(session->echo);
3896 session->echo = coap_new_bin_const(coap_opt_value(opt),
3897 coap_opt_length(opt));
3898 }
3899}
3900
3901/*
3902 * Need to see if this is a response to a large body request transfer. If so,
3903 * need to initiate the request containing the next block and not trouble the
3904 * application. Note that Token must unique per request/response.
3905 *
3906 * Client receives large data acknowledgement from server (Block1)
3907 *
3908 * This is set up using coap_add_data_large_request_lkd()
3909 *
3910 * Client is using GET etc.
3911 *
3912 * Return: 0 Call application handler
3913 * 1 Do not call application handler - just send the built response
3914 */
3915int
3916coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent,
3917 coap_pdu_t *rcvd) {
3918 coap_lg_xmit_t *lg_xmit;
3919 coap_lg_crcv_t *lg_crcv = NULL;
3920 int lg_crcv_is_refed = 0;
3921
3922 lg_xmit = coap_find_lg_xmit(session, rcvd);
3923 if (lg_xmit) {
3924 /* lg_xmit found */
3925 size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
3926 coap_block_b_t block;
3927
3928 lg_crcv = coap_find_lg_crcv(session, rcvd);
3929 if (lg_crcv) {
3930 coap_ticks(&lg_crcv->last_used);
3931 coap_lg_crcv_reference_lkd(lg_crcv);
3932 lg_crcv_is_refed = 1;
3933 }
3934
3935 if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
3936 coap_get_block_b(session, rcvd, lg_xmit->option, &block)) {
3937
3938 if (block.bert) {
3939 coap_log_debug("found Block option, block is BERT, block nr. %u (%" PRIuS ")\n",
3940 block.num, lg_xmit->b.b1.bert_size);
3941 } else {
3942 coap_log_debug("found Block option, block size is %u, block nr. %u\n",
3943 1 << (block.szx + 4), block.num);
3944 }
3945 if (block.szx != lg_xmit->blk_size) {
3946 if (block.szx > lg_xmit->blk_size) {
3947 coap_log_info("ignoring request to increase Block size, "
3948 "(%u > %u)\n",
3949 1 << (block.szx + 4), 1 << (lg_xmit->blk_size + 4));
3950 } else {
3951 /*
3952 * Recompute the block number of the previous packet given the
3953 * new block size
3954 */
3955 block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1);
3956 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
3957#if COAP_Q_BLOCK_SUPPORT
3958 lg_xmit->send_blocks.range[0].begin = block.num;
3959 lg_xmit->send_blocks.range[0].end = (uint32_t)(lg_xmit->data_info->length / chunk);
3960#endif /* COAP_Q_BLOCK_SUPPORT */
3961 lg_xmit->blk_size = block.szx;
3962 coap_log_debug("new Block size is %u, block number %u completed\n",
3963 1 << (block.szx + 4), block.num);
3964 block.bert = 0;
3965 block.aszx = block.szx;
3966 }
3967 }
3968 track_echo(session, rcvd);
3969#if COAP_Q_BLOCK_SUPPORT
3970 if (rcvd->code == COAP_RESPONSE_CLASS(231) &&
3971 lg_xmit->option == COAP_OPTION_Q_BLOCK1) {
3972 coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
3973 goto skip_app_handler;
3974 }
3975#endif /* COAP_Q_BLOCK_SUPPORT */
3976 if (lg_xmit->last_block == (int)block.num &&
3977 lg_xmit->option != COAP_OPTION_Q_BLOCK1) {
3978 /*
3979 * Duplicate Block1 ACK
3980 *
3981 * RFCs not clear here, but on a lossy connection, there could
3982 * be multiple Block1 ACKs, causing the client to retransmit the
3983 * same block multiple times, or the server retransmitting the
3984 * same ACK.
3985 *
3986 * Once a block has been ACKd, there is no need to retransmit it.
3987 */
3988 goto skip_app_handler;
3989 }
3990 if (block.bert)
3991 block.num += (unsigned int)(lg_xmit->b.b1.bert_size / 1024 - 1);
3992 lg_xmit->last_block = block.num;
3993 if ((block.num + 1) * chunk < lg_xmit->data_info->length) {
3994 /* Build the next PDU request based off the skeletal PDU */
3995 uint8_t buf[8];
3996 coap_pdu_t *pdu;
3997 uint64_t token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, ++lg_xmit->b.b1.count);
3998 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
3999 size_t offset;
4000
4001 if (lg_xmit->sent_pdu->code == COAP_REQUEST_CODE_FETCH) {
4002 /* Need to handle Observe for large FETCH */
4003 if (lg_crcv) {
4004 if (coap_binary_equal(lg_xmit->b.b1.app_token, lg_crcv->app_token)) {
4005 coap_bin_const_t *new_token;
4006 coap_bin_const_t ctoken = { len, buf };
4007
4008 /* Need to save/restore Observe Token for large FETCH */
4009 new_token = track_fetch_observe(lg_xmit->sent_pdu, lg_crcv, block.num + 1,
4010 &ctoken);
4011 if (new_token) {
4012 assert(len <= sizeof(buf));
4013 len = new_token->length;
4014 memcpy(buf, new_token->s, len);
4015 }
4016 }
4017 }
4018 }
4019 pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
4020 len, buf, NULL, COAP_BOOL_FALSE);
4021 if (!pdu)
4022 goto fail_body;
4023
4024 /*
4025 * If initial transmit was multicast, that would have been NON.
4026 * Make subsequent traffic CON for reliability.
4027 */
4028 if (session->sock.flags & COAP_SOCKET_MULTICAST) {
4029 pdu->type = COAP_MESSAGE_CON;
4030 }
4031
4032 block.num++;
4033 offset = block.num * chunk;
4034 if (block.bert) {
4035 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
4036 pdu->used_size;
4037 block.m = (lg_xmit->data_info->length - offset) >
4038 ((pdu->max_size - token_options) /1024) * 1024;
4039 } else {
4040 block.m = (offset + chunk) < lg_xmit->data_info->length;
4041 }
4042 coap_update_option(pdu, lg_xmit->option,
4043 coap_encode_var_safe(buf, sizeof(buf),
4044 (block.num << 4) |
4045 (block.m << 3) |
4046 block.aszx),
4047 buf);
4048
4049 if (lg_xmit->data_info->get_func) {
4050#if COAP_CONSTRAINED_STACK
4051 /* Protected by global_lock if needed */
4052 static uint8_t l_data[1024];
4053#else /* ! COAP_CONSTRAINED_STACK */
4054 uint8_t l_data[1024];
4055#endif /* ! COAP_CONSTRAINED_STACK */
4056 size_t l_length;
4057
4058 assert(chunk <= 1024);
4059 if (lg_xmit->data_info->get_func(session, chunk,
4060 block.num * chunk, l_data, &l_length,
4061 lg_xmit->data_info->app_ptr)) {
4062 if (!coap_add_data(pdu, l_length, l_data)) {
4063 goto fail_body;
4064 }
4065 }
4066 } else {
4067 if (!coap_add_block_b_data(pdu,
4068 lg_xmit->data_info->length,
4069 lg_xmit->data_info->data,
4070 &block))
4071 goto fail_body;
4072 }
4073 lg_xmit->b.b1.bert_size = block.chunk_size;
4074 coap_ticks(&lg_xmit->last_sent);
4075#if COAP_Q_BLOCK_SUPPORT
4076 if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 &&
4077 pdu->type == COAP_MESSAGE_NON) {
4078 if (coap_send_q_block1(session, block, pdu,
4079 COAP_SEND_INC_PDU) == COAP_INVALID_MID)
4080 goto fail_body;
4081 goto skip_app_handler;
4082 } else if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4083 goto fail_body;
4084#else /* ! COAP_Q_BLOCK_SUPPORT */
4085 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4086 goto fail_body;
4087#endif /* ! COAP_Q_BLOCK_SUPPORT */
4088 goto skip_app_handler;
4089 }
4090 } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4091 /*
4092 * Not a block response asking for the next block.
4093 * Could be an Observe response overlapping with block FETCH doing
4094 * Observe cancellation.
4095 */
4096 coap_opt_iterator_t opt_iter;
4097 coap_opt_t *obs_opt;
4098 int observe_action = -1;
4099
4100 if (lg_xmit->sent_pdu->code != COAP_REQUEST_CODE_FETCH) {
4101 goto lg_xmit_finished;
4102 }
4103 obs_opt = coap_check_option(lg_xmit->sent_pdu,
4105 &opt_iter);
4106 if (obs_opt) {
4107 observe_action = coap_decode_var_bytes(coap_opt_value(obs_opt),
4108 coap_opt_length(obs_opt));
4109 }
4110 if (observe_action != COAP_OBSERVE_CANCEL) {
4111 goto lg_xmit_finished;
4112 }
4113 obs_opt = coap_check_option(rcvd,
4115 &opt_iter);
4116 if (obs_opt) {
4117 goto call_app_handler;
4118 }
4119 goto lg_xmit_finished;
4120 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4121 if (check_freshness(session, rcvd, sent, lg_xmit, NULL))
4122 goto skip_app_handler;
4123#if COAP_Q_BLOCK_SUPPORT
4124 } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
4125 /* Q-Block1 or Q-Block2 not present in p - duplicate error ? */
4126 if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) ||
4127 coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK1, &block))
4128 goto skip_app_handler;
4129 } else if (rcvd->code == COAP_RESPONSE_CODE(408) &&
4130 lg_xmit->option == COAP_OPTION_Q_BLOCK1) {
4131 size_t length;
4132 const uint8_t *data;
4133 coap_opt_iterator_t opt_iter;
4134 coap_opt_t *fmt_opt = coap_check_option(rcvd,
4136 &opt_iter);
4137 uint16_t fmt = fmt_opt ?
4139 coap_opt_length(fmt_opt)) :
4141
4143 goto fail_body;
4144
4145 if (COAP_PROTO_RELIABLE(session->proto) ||
4146 rcvd->type != COAP_MESSAGE_NON) {
4147 coap_log_debug("Unexpected 4.08 - protocol violation - ignore\n");
4148 goto skip_app_handler;
4149 }
4150
4151 if (coap_get_data(rcvd, &length, &data)) {
4152 /* Need to decode CBOR to work out what blocks to re-send */
4153 const uint8_t *bp = data;
4154 uint32_t i;
4155
4156 for (i = 0; (bp < data + length) &&
4157 i < COAP_MAX_PAYLOADS(session); i++) {
4158 if ((*bp & 0xc0) != 0x00) /* uint(value) */
4159 goto fail_cbor;
4160 block.num = derive_cbor_value(&bp, data + length - bp);
4161 coap_log_debug("Q-Block1: Missing block %d\n", block.num);
4162 if (block.num > (1 << 20) -1)
4163 goto fail_cbor;
4164 block.m = (block.num + 1) * chunk < lg_xmit->data_info->length;
4165 block.szx = lg_xmit->blk_size;
4166
4167 blocks_add_entry(&lg_xmit->send_blocks, block.num, block.m);
4168 }
4169 if (lg_xmit->send_blocks.used) {
4170 /* Flush out the first one */
4171 block.num = lg_xmit->send_blocks.range[0].begin;
4172 block.m = (block.num + 1) * chunk < lg_xmit->data_info->length;
4173 coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
4174 }
4175 goto skip_app_handler;
4176 }
4177fail_cbor:
4178 coap_log_info("Invalid application/missing-blocks+cbor-seq\n");
4179#endif /* COAP_Q_BLOCK_SUPPORT */
4180 }
4181 goto lg_xmit_finished;
4182 }
4183 goto call_app_handler;
4184
4185fail_body:
4187 /* There has been an internal error of some sort */
4188 rcvd->code = COAP_RESPONSE_CODE(500);
4189lg_xmit_finished:
4190 if (lg_crcv) {
4191 if (STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) ==
4192 STATE_TOKEN_BASE(lg_crcv->state_token)) {
4193 /* In case of observe */
4194 lg_crcv->state_token = lg_xmit->b.b1.state_token;
4195 lg_crcv->retry_counter = lg_xmit->b.b1.count;
4196 }
4197 }
4198 if (!lg_crcv) {
4199 /* need to put back original token into rcvd */
4200 if (lg_xmit->b.b1.app_token)
4201 coap_update_token(rcvd, lg_xmit->b.b1.app_token->length,
4202 lg_xmit->b.b1.app_token->s);
4203 coap_log_debug("Client app version of updated PDU (1)\n");
4205 } else {
4206 lg_crcv->sent_pdu->lg_xmit = 0;
4207 }
4208
4209 if (sent) {
4210 /* need to put back original token into sent */
4211 if (lg_xmit->b.b1.app_token)
4212 coap_update_token(sent, lg_xmit->b.b1.app_token->length,
4213 lg_xmit->b.b1.app_token->s);
4214 if (sent->lg_xmit)
4215 coap_remove_option(sent, sent->lg_xmit->option);
4216 sent->lg_xmit = NULL;
4217 }
4218 LL_DELETE(session->lg_xmit, lg_xmit);
4219 coap_block_delete_lg_xmit(session, lg_xmit);
4220
4221call_app_handler:
4222 if (lg_crcv_is_refed)
4223 coap_lg_crcv_release_lkd(session, lg_crcv);
4224 return 0;
4225
4226skip_app_handler:
4227 if (lg_crcv_is_refed)
4228 coap_lg_crcv_release_lkd(session, lg_crcv);
4229 return 1;
4230}
4231#endif /* COAP_CLIENT_SUPPORT */
4232
4233void
4235 coap_block_data_handler_t block_data_handler) {
4236 context->block_data_cb = block_data_handler;
4237}
4238
4240coap_block_build_body(coap_binary_t *body_data, size_t length,
4241 const uint8_t *data, size_t offset, size_t total) {
4242 coap_binary_t *ret;
4243
4244 coap_lock_lock(return NULL);
4245 ret = coap_block_build_body_lkd(body_data, length, data, offset, total);
4247 return ret;
4248}
4249/*
4250 * Re-assemble payloads into a body
4251 */
4254 const uint8_t *data, size_t offset, size_t total) {
4255 if (data == NULL)
4256 return NULL;
4257 if (body_data == NULL && total) {
4258 body_data = coap_new_binary(total);
4259 }
4260 if (body_data == NULL)
4261 return NULL;
4262
4263 /* Check no overflow (including a 8 byte small headroom) */
4264 if (SIZE_MAX - length < 8 || offset > SIZE_MAX - length - 8) {
4265 coap_delete_binary(body_data);
4266 return NULL;
4267 }
4268
4269 /* Update saved data */
4270 if (offset + length <= total && body_data->length >= total) {
4271 memcpy(&body_data->s[offset], data, length);
4272 } else {
4273 /*
4274 * total may be inaccurate as per
4275 * https://rfc-editor.org/rfc/rfc7959#section-4
4276 * o In a request carrying a Block1 Option, to indicate the current
4277 * estimate the client has of the total size of the resource
4278 * representation, measured in bytes ("size indication").
4279 * o In a response carrying a Block2 Option, to indicate the current
4280 * estimate the server has of the total size of the resource
4281 * representation, measured in bytes ("size indication").
4282 */
4283 coap_binary_t *new = coap_resize_binary(body_data, offset + length);
4284
4285 if (new) {
4286 body_data = new;
4287 memcpy(&body_data->s[offset], data, length);
4288 } else {
4289 coap_delete_binary(body_data);
4290 return NULL;
4291 }
4292 }
4293 return body_data;
4294}
4295
4296#if COAP_CLIENT_SUPPORT
4297/*
4298 * Need to see if this is a large body response to a request. If so,
4299 * need to initiate the request for the next block and not trouble the
4300 * application. Note that Token must be unique per request/response.
4301 *
4302 * This is set up using coap_send()
4303 * Client receives large data from server ((Q-)Block2)
4304 *
4305 * Return: 0 Call application handler
4306 * 1 Do not call application handler - just sent the next request
4307 */
4308int
4309coap_handle_response_get_block(coap_context_t *context,
4310 coap_session_t *session,
4311 coap_pdu_t *sent,
4312 coap_pdu_t *rcvd,
4313 coap_recurse_t recursive) {
4314 coap_lg_crcv_t *lg_crcv;
4315 coap_block_b_t block;
4316#if COAP_Q_BLOCK_SUPPORT
4317 coap_block_b_t qblock;
4318#endif /* COAP_Q_BLOCK_SUPPORT */
4319 int have_block = 0;
4320 uint16_t block_opt = 0;
4321 size_t offset;
4322 int ack_rst_sent = 0;
4323 coap_opt_iterator_t opt_iter;
4324
4326 memset(&block, 0, sizeof(block));
4327#if COAP_Q_BLOCK_SUPPORT
4328 memset(&qblock, 0, sizeof(qblock));
4329#endif /* COAP_Q_BLOCK_SUPPORT */
4330 lg_crcv = coap_find_lg_crcv(session, rcvd);
4331 if (lg_crcv) {
4332 size_t chunk = 0;
4333 uint8_t buf[8];
4334 coap_tick_t adjust;
4335
4336 if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4337 size_t length;
4338 const uint8_t *data;
4340 &opt_iter);
4341 size_t size2 = size_opt ?
4343 coap_opt_length(size_opt)) : 0;
4344
4345 /* length and data are cleared on error */
4346 (void)coap_get_data(rcvd, &length, &data);
4347 rcvd->body_offset = 0;
4348 rcvd->body_total = length;
4349 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
4350 have_block = 1;
4351 block_opt = COAP_OPTION_BLOCK2;
4352 }
4353#if COAP_Q_BLOCK_SUPPORT
4354 if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) {
4355 if (have_block) {
4356 coap_log_warn("Both Block1 and Q-Block1 not supported in a response\n");
4357 }
4358 have_block = 1;
4359 block_opt = COAP_OPTION_Q_BLOCK2;
4360 block = qblock;
4361 /* server indicating that it supports Q_BLOCK */
4362 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
4363 set_block_mode_has_q(session->block_mode);
4364 }
4365 }
4366#endif /* COAP_Q_BLOCK_SUPPORT */
4367 track_echo(session, rcvd);
4368 if (have_block && (block.m || length)) {
4369 coap_opt_t *fmt_opt = coap_check_option(rcvd,
4371 &opt_iter);
4372 uint16_t fmt = fmt_opt ?
4374 coap_opt_length(fmt_opt)) :
4376 coap_opt_t *etag_opt = coap_check_option(rcvd,
4378 &opt_iter);
4379 size_t saved_offset;
4380 int updated_block;
4381
4382 if (length > block.chunk_size) {
4383 coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %" PRIuS "\n",
4384 block.chunk_size, length);
4385 length = block.chunk_size;
4386 }
4387 if (block.m && length != block.chunk_size) {
4388 coap_log_warn("block: Undersized packet - expected %"PRIu32", got %" PRIuS "\n",
4389 block.chunk_size, length);
4390 /* Unclear how to properly handle this */
4391 rcvd->code = COAP_RESPONSE_CODE(402);
4392 goto expire_lg_crcv;
4393 }
4394 /* Possibility that Size2 not sent, or is too small */
4395 chunk = (size_t)1 << (block.szx + 4);
4396 offset = block.num * chunk;
4397 if (size2 < (offset + length)) {
4398 if (block.m)
4399 size2 = offset + length + 1;
4400 else
4401 size2 = offset + length;
4402 }
4403 saved_offset = offset;
4404
4405 if (lg_crcv->initial) {
4406#if COAP_Q_BLOCK_SUPPORT
4407reinit:
4408#endif /* COAP_Q_BLOCK_SUPPORT */
4409 lg_crcv->initial = 0;
4410 if (lg_crcv->body_data) {
4411 coap_free_type(COAP_STRING, lg_crcv->body_data);
4412 lg_crcv->body_data = NULL;
4413 }
4414 if (etag_opt) {
4415 lg_crcv->etag_length = coap_opt_length(etag_opt);
4416 memcpy(lg_crcv->etag, coap_opt_value(etag_opt), lg_crcv->etag_length);
4417 lg_crcv->etag_set = 1;
4418 } else {
4419 lg_crcv->etag_set = 0;
4420 }
4421 lg_crcv->total_len = size2;
4422 lg_crcv->content_format = fmt;
4423 lg_crcv->szx = block.szx;
4424 lg_crcv->block_option = block_opt;
4425 lg_crcv->last_type = rcvd->type;
4426 lg_crcv->rec_blocks.used = 0;
4427 lg_crcv->rec_blocks.total_blocks = 0;
4428#if COAP_Q_BLOCK_SUPPORT
4429 lg_crcv->rec_blocks.processing_payload_set = 0;
4430#endif /* COAP_Q_BLOCK_SUPPORT */
4431 }
4432 if (lg_crcv->total_len < size2)
4433 lg_crcv->total_len = size2;
4434
4435 /* Check whether we can handle this size */
4436 uint32_t max_body;
4437 uint8_t max_block_szx;
4438
4439 max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
4440 if (max_block_szx == 0 || max_block_szx > block.szx) {
4441 max_block_szx = block.szx;
4442 }
4443 max_body = ((1UL << 20) * (1 << (max_block_szx + 4)));
4444 if (max_body > MAX_BLK_LEN)
4445 max_body = MAX_BLK_LEN;
4446 if ((context->max_body_size && size2 > context->max_body_size) ||
4447 (size2 > max_body)) {
4448 uint32_t max_body_size = context->max_body_size;
4449
4450 if (max_body_size == 0 || max_body < max_body_size) {
4451 max_body_size = max_body;
4452 }
4453 coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", size2, max_body_size);
4454 /* Try to hint to the server thare is an issue */
4455 coap_send_rst_lkd(session, rcvd);
4457 return 1;
4458 }
4459
4460 if (etag_opt) {
4461 if (!full_match(coap_opt_value(etag_opt),
4462 coap_opt_length(etag_opt),
4463 lg_crcv->etag, lg_crcv->etag_length)) {
4464 /* body of data has changed - need to restart request */
4465 coap_pdu_t *pdu;
4466 uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token,
4467 ++lg_crcv->retry_counter);
4468 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
4469 coap_opt_filter_t drop_options;
4470
4471#if COAP_Q_BLOCK_SUPPORT
4472 if (block_opt == COAP_OPTION_Q_BLOCK2)
4473 goto reinit;
4474#endif /* COAP_Q_BLOCK_SUPPORT */
4475
4476 coap_log_warn("Data body updated during receipt - new request started\n");
4477 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
4479
4480 lg_crcv->initial = 1;
4481 coap_free_type(COAP_STRING, lg_crcv->body_data);
4482 lg_crcv->body_data = NULL;
4483
4484 coap_session_new_token(session, &len, buf);
4485 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
4488 pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf,
4489 &drop_options, COAP_BOOL_FALSE);
4490 if (!pdu)
4491 goto fail_resp;
4492
4493 coap_update_option(pdu, block_opt,
4494 coap_encode_var_safe(buf, sizeof(buf),
4495 (0 << 4) | (0 << 3) | block.aszx),
4496 buf);
4497
4498 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4499 goto fail_resp;
4500
4501 goto skip_app_handler;
4502 }
4503 } else if (lg_crcv->etag_set) {
4504 /* Cannot handle this change in ETag to not being there */
4505 coap_log_warn("Not all blocks have ETag option\n");
4506 goto fail_resp;
4507 }
4508
4509 if (fmt != lg_crcv->content_format) {
4510 coap_log_warn("Content-Format option mismatch\n");
4511 goto fail_resp;
4512 }
4513#if COAP_Q_BLOCK_SUPPORT
4514 if (block_opt == COAP_OPTION_Q_BLOCK2 && size2 != lg_crcv->total_len) {
4515 coap_log_warn("Size2 option mismatch\n");
4516 goto fail_resp;
4517 }
4518#endif /* COAP_Q_BLOCK_SUPPORT */
4519 if (block.num == 0) {
4520 coap_opt_t *obs_opt = coap_check_option(rcvd,
4522 &opt_iter);
4523 if (obs_opt) {
4524 lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3);
4525 memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length);
4526 lg_crcv->observe_set = 1;
4527 } else {
4528 lg_crcv->observe_set = 0;
4529 }
4530 }
4531 updated_block = 0;
4532 while (offset < saved_offset + length) {
4533 if (!check_if_received_block(&lg_crcv->rec_blocks, block.num)) {
4534#if COAP_Q_BLOCK_SUPPORT
4535 uint32_t this_payload_set = block.num / COAP_MAX_PAYLOADS(session);
4536#endif /* COAP_Q_BLOCK_SUPPORT */
4537
4538 coap_log_debug("found Block option, block size is %u, block nr. %u\n",
4539 1 << (block.szx + 4), block.num);
4540#if COAP_Q_BLOCK_SUPPORT
4541 if (block_opt == COAP_OPTION_Q_BLOCK2 && lg_crcv->rec_blocks.used &&
4542 this_payload_set > lg_crcv->rec_blocks.processing_payload_set &&
4543 this_payload_set != lg_crcv->rec_blocks.latest_payload_set) {
4544 coap_request_missing_q_block2(session, lg_crcv);
4545 }
4546 lg_crcv->rec_blocks.latest_payload_set = this_payload_set;
4547#endif /* COAP_Q_BLOCK_SUPPORT */
4548 /* Update list of blocks received */
4549 if (blocks_add_entry(&lg_crcv->rec_blocks, block.num, block.m)) {
4550 updated_block = 1;
4551 } else {
4552 coap_log_debug("Block nr %u ignored (too many missing blocks)\n", block.num);
4553 }
4554 } else {
4555 coap_log_debug("Duplicate block nr %u\n", block.num);
4556 }
4557 block.num++;
4558 offset = block.num << (block.szx + 4);
4559 if (!block.bert && block_opt != COAP_OPTION_Q_BLOCK2)
4560 break;
4561 }
4562 block.num--;
4563 /* Only process if not duplicate block */
4564 if (updated_block) {
4565 void *body_free;
4566
4567 /* Update last_used to prevent premature timeout during long transfers */
4568 coap_ticks(&lg_crcv->last_used);
4569
4570 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
4571 if (size2 < saved_offset + length) {
4572 size2 = saved_offset + length;
4573 }
4574 if (context && context->block_data_cb) {
4575 coap_response_t resp;
4576
4578 context->block_data_cb(session, rcvd, 0,
4579 &lg_crcv->body_data,
4580 length, data,
4581 saved_offset, size2));
4582 if (resp != COAP_RESPONSE_OK) {
4583 goto fail_resp;
4584 }
4585 } else {
4586 lg_crcv->body_data = coap_block_build_body_lkd(lg_crcv->body_data, length, data,
4587 saved_offset, size2);
4588 if (lg_crcv->body_data == NULL) {
4589 goto fail_resp;
4590 }
4591 }
4592 }
4593 if (block.m || !check_all_blocks_in(&lg_crcv->rec_blocks)) {
4594 /* Not all the payloads of the body have arrived */
4595 size_t len;
4596 coap_pdu_t *pdu;
4597 uint64_t token;
4598 coap_opt_filter_t drop_options;
4599
4600 if (block.m) {
4601#if COAP_Q_BLOCK_SUPPORT
4602 if (block_opt == COAP_OPTION_Q_BLOCK2) {
4603 /* Blocks could arrive in wrong order */
4604 if (check_all_blocks_in(&lg_crcv->rec_blocks)) {
4605 goto give_to_app;
4606 }
4607 if (check_all_blocks_in_for_payload_set(session,
4608 &lg_crcv->rec_blocks)) {
4609 block.num = lg_crcv->rec_blocks.range[0].end;
4610 /* Now requesting next payload */
4611 lg_crcv->rec_blocks.processing_payload_set =
4612 block.num / COAP_MAX_PAYLOADS(session) + 1;
4613 if (check_any_blocks_next_payload_set(session,
4614 &lg_crcv->rec_blocks)) {
4615 /* Need to ask for them individually */
4616 coap_request_missing_q_block2(session, lg_crcv);
4617 goto skip_app_handler;
4618 }
4619 } else {
4620 /* The remote end will be sending the next one unless this
4621 is a MAX_PAYLOADS and all previous have been received */
4622 goto skip_app_handler;
4623 }
4624 if (COAP_PROTO_RELIABLE(session->proto) ||
4625 rcvd->type != COAP_MESSAGE_NON)
4626 goto skip_app_handler;
4627
4628 } else
4629#endif /* COAP_Q_BLOCK_SUPPORT */
4630 block.m = 0;
4631
4632 /* Ask for the next block */
4633 token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter);
4634 len = coap_encode_var_safe8(buf, sizeof(token), token);
4635 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
4637 pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf,
4638 &drop_options, COAP_BOOL_FALSE);
4639 if (!pdu)
4640 goto fail_resp;
4641
4642 if (rcvd->type == COAP_MESSAGE_NON)
4643 pdu->type = COAP_MESSAGE_NON; /* Server is using NON */
4644
4645 /* Only sent with the first block */
4647
4648 coap_update_option(pdu, block_opt,
4649 coap_encode_var_safe(buf, sizeof(buf),
4650 ((block.num + 1) << 4) |
4651 (block.m << 3) | block.aszx),
4652 buf);
4653
4655 (void)coap_get_data(lg_crcv->sent_pdu, &length, &data);
4656 coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, length, data, NULL, NULL, NULL,
4657 0, 0);
4658 }
4659 if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4660 /* Session could now be disconnected, so no lg_crcv */
4661 goto skip_app_handler;
4662 }
4663 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert)
4664 goto skip_app_handler;
4665
4666 /* need to put back original token into rcvd */
4667 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4668 rcvd->body_offset = saved_offset;
4669#if COAP_Q_BLOCK_SUPPORT
4670 rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
4671 lg_crcv->total_len : size2;
4672#else /* ! COAP_Q_BLOCK_SUPPORT */
4673 rcvd->body_total = size2;
4674#endif /* ! COAP_Q_BLOCK_SUPPORT */
4675 coap_log_debug("Client app version of updated PDU (2)\n");
4677
4678 if (sent) {
4679 /* need to put back original token into sent */
4680 if (lg_crcv->app_token)
4681 coap_update_token(sent, lg_crcv->app_token->length,
4682 lg_crcv->app_token->s);
4683 coap_remove_option(sent, lg_crcv->block_option);
4684 }
4685 goto call_app_handler;
4686 }
4687#if COAP_Q_BLOCK_SUPPORT
4688give_to_app:
4689#endif /* COAP_Q_BLOCK_SUPPORT */
4690 if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
4691 /* Pretend that there is no block */
4692 coap_remove_option(rcvd, block_opt);
4693 if (lg_crcv->observe_set) {
4695 lg_crcv->observe_length, lg_crcv->observe);
4696 }
4697 rcvd->body_data = lg_crcv->body_data ? lg_crcv->body_data->s : NULL;
4698#if COAP_Q_BLOCK_SUPPORT
4699 if (context && context->block_data_cb) {
4700 /* Data has already been provided - do not duplicate */
4701 if (rcvd->data) {
4702 rcvd->used_size = rcvd->data - rcvd->token - 1;
4703 rcvd->data = NULL;
4704 }
4705 }
4706 rcvd->body_length = block_opt == COAP_OPTION_Q_BLOCK2 ?
4707 lg_crcv->total_len : saved_offset + length;
4708#else /* ! COAP_Q_BLOCK_SUPPORT */
4709 rcvd->body_length = saved_offset + length;
4710#endif /* ! COAP_Q_BLOCK_SUPPORT */
4711 rcvd->body_offset = 0;
4712 rcvd->body_total = rcvd->body_length;
4713 } else {
4714 rcvd->body_offset = saved_offset;
4715#if COAP_Q_BLOCK_SUPPORT
4716 rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
4717 lg_crcv->total_len : size2;
4718#else /* ! COAP_Q_BLOCK_SUPPORT */
4719 rcvd->body_total = size2;
4720#endif /* ! COAP_Q_BLOCK_SUPPORT */
4721 }
4722 /* need to put back original token into rcvd */
4723 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4724 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4725 coap_log_debug("Client app version of updated PDU (3)\n");
4727 }
4728 if (sent) {
4729 /* need to put back original token into sent */
4730 if (lg_crcv->app_token)
4731 coap_update_token(sent, lg_crcv->app_token->length,
4732 lg_crcv->app_token->s);
4733 coap_remove_option(sent, lg_crcv->block_option);
4734 }
4735 body_free = lg_crcv->body_data;
4736 lg_crcv->body_data = NULL;
4737 coap_call_response_handler(session, sent, rcvd, body_free);
4738
4739 ack_rst_sent = 1;
4740 if (lg_crcv->observe_set == 0) {
4741 /* Expire this entry */
4742 LL_DELETE(session->lg_crcv, lg_crcv);
4743 coap_block_delete_lg_crcv(session, lg_crcv);
4744 goto skip_app_handler;
4745 }
4746 /* Set up for the next data body as observing */
4747 lg_crcv->initial = 1;
4748 }
4749 coap_ticks(&lg_crcv->last_used);
4750 goto skip_app_handler;
4751 } else {
4752 coap_opt_t *obs_opt = coap_check_option(rcvd,
4754 &opt_iter);
4755 if (context->max_body_size && length > context->max_body_size) {
4756 coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", length,
4757 context->max_body_size);
4758 /* Try to hint to the server thare is an issue */
4759 coap_send_rst_lkd(session, rcvd);
4761 return 1;
4762 }
4763 if (obs_opt) {
4764 lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3);
4765 memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length);
4766 lg_crcv->observe_set = 1;
4767 } else {
4768 lg_crcv->observe_set = 0;
4769 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4770 /* need to put back original token into rcvd */
4771 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4773 coap_log_debug("PDU presented to app.\n");
4775 }
4776 /* Expire this entry */
4777 goto expire_lg_crcv;
4778 }
4779 }
4780 coap_ticks(&lg_crcv->last_used);
4781 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4782#if COAP_OSCORE_SUPPORT
4783 if (check_freshness(session, rcvd,
4784 (session->oscore_encryption == 0) ? sent : NULL,
4785 NULL, lg_crcv))
4786#else /* !COAP_OSCORE_SUPPORT */
4787 if (check_freshness(session, rcvd, sent, NULL, lg_crcv))
4788#endif /* !COAP_OSCORE_SUPPORT */
4789 goto skip_app_handler;
4790 goto expire_lg_crcv;
4791 } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
4792 coap_opt_t *abb_opt = sent ? coap_check_option(sent,
4794 &opt_iter) : NULL;
4795 if (abb_opt) {
4796 /* Send the request again with the Uri-Path-Abbrev expanded out */
4797 coap_pdu_t *pdu;
4798 size_t data_len;
4799 const uint8_t *data;
4800 uint64_t token;
4801 uint8_t ltoken[8];
4802 size_t ltoken_len;
4803
4804 session->no_path_abbrev = 1;
4805 token = STATE_TOKEN_FULL(lg_crcv->state_token,
4806 ++lg_crcv->retry_counter);
4807 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
4808 pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, ltoken_len,
4809 ltoken, NULL, COAP_BOOL_TRUE);
4810 if (pdu) {
4811 if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) {
4812 coap_add_data(pdu, data_len, data);
4813 }
4814#if COAP_PROXY_SUPPORT
4815 coap_proxy_req_t *proxy_req = session->context->proxy_list_count ?
4816 coap_proxy_map_outgoing_request(session, rcvd, NULL) : NULL;
4817
4818 if (proxy_req) {
4819 coap_bin_const_t *new = coap_new_bin_const(ltoken, ltoken_len);
4820 if (new) {
4821 coap_delete_bin_const(proxy_req->token_used);
4822 proxy_req->token_used = new;
4823 coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used, "upd");
4824 }
4825 }
4826#endif /* COAP_PROXY_SUPPORT */
4827 coap_log_debug("* Retransmitting PDU with Uri-Path-Abbrev replaced (1)\n");
4828 coap_delete_pdu_lkd(lg_crcv->sent_pdu);
4829 lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu);
4830 coap_send_internal(session, pdu, NULL);
4831 goto skip_app_handler;
4832 }
4833 }
4834 goto expire_lg_crcv;
4835 } else {
4836 /* Not 2.xx, 4.01 or 4.02 - assume it is a failure of some sort */
4837 goto expire_lg_crcv;
4838 }
4839 if (!block.m && !lg_crcv->observe_set) {
4840fail_resp:
4841
4842 /* lg_crcv no longer required - cache it for 1 sec */
4845 } else {
4846 adjust = 0;
4847 }
4848 coap_ticks(&lg_crcv->last_used);
4849 lg_crcv->last_used -= adjust;
4850 }
4851 /* need to put back original token into rcvd */
4852 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4853 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4854 coap_log_debug("Client app version of updated PDU (4)\n");
4856 }
4857 }
4858
4859 /* Check if receiving a block response and if blocks can be set up */
4860 if (recursive == COAP_RECURSE_OK && !lg_crcv) {
4861 if (!sent) {
4862 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)
4863#if COAP_Q_BLOCK_SUPPORT
4864 ||
4865 coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)
4866#endif /* COAP_Q_BLOCK_SUPPORT */
4867 ) {
4868 coap_log_debug("** %s: large body receive internal issue\n",
4869 coap_session_str(session));
4870 goto skip_app_handler;
4871 }
4872 } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4873 const uint8_t *data;
4874 size_t length;
4875
4876 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
4877#if COAP_Q_BLOCK_SUPPORT
4878 if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) {
4879 set_block_mode_drop_q(session->block_mode);
4880 coap_log_debug("Q-Block support disabled\n");
4881 }
4882#endif /* COAP_Q_BLOCK_SUPPORT */
4883 have_block = 1;
4884 if (block.num != 0) {
4885 /* Assume random access and just give the single response to app */
4886 size_t chunk = (size_t)1 << (block.szx + 4);
4887
4888 coap_get_data(rcvd, &length, &data);
4889 rcvd->body_offset = block.num*chunk;
4890 rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
4891 goto call_app_handler;
4892 }
4893 }
4894#if COAP_Q_BLOCK_SUPPORT
4895 else if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)) {
4896 have_block = 1;
4897 /* server indicating that it supports Q_BLOCK2 */
4898 if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
4899 set_block_mode_has_q(session->block_mode);
4900 }
4901 }
4902#endif /* COAP_Q_BLOCK_SUPPORT */
4903 if (have_block) {
4904 lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
4905
4906 if (lg_crcv) {
4907 LL_PREPEND(session->lg_crcv, lg_crcv);
4908 return coap_handle_response_get_block(context, session, sent, rcvd,
4910 }
4911 }
4912 coap_get_data(rcvd, &length, &data);
4913 if (context->max_body_size && length > context->max_body_size) {
4914 coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", length,
4915 context->max_body_size);
4916 /* Try to hint to the server thare is an issue */
4917 coap_send_rst_lkd(session, rcvd);
4919 return 1;
4920 }
4921 track_echo(session, rcvd);
4922 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4923 lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
4924
4925 if (lg_crcv) {
4926 LL_PREPEND(session->lg_crcv, lg_crcv);
4927 return coap_handle_response_get_block(context, session, sent, rcvd,
4929 }
4930 } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
4931 coap_opt_t *abb_opt = coap_check_option(sent,
4933 &opt_iter);
4934 if (abb_opt) {
4935 /*
4936 * Send the request again with the Uri-Path-Abbrev expanded out, but need
4937 lg_crcv in place to handle the token update.
4938 */
4939 lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
4940
4941 if (lg_crcv) {
4942 LL_PREPEND(session->lg_crcv, lg_crcv);
4943 return coap_handle_response_get_block(context, session, sent, rcvd,
4945 }
4946 }
4947 }
4948 }
4949 return 0;
4950
4951expire_lg_crcv:
4952 /* need to put back original token into rcvd */
4953 if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4954 coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4955 coap_log_debug("Client app version of updated PDU (5)\n");
4957 }
4958
4959 if (sent) {
4960 /* need to put back original token into sent */
4961 if (lg_crcv->app_token)
4962 coap_update_token(sent, lg_crcv->app_token->length,
4963 lg_crcv->app_token->s);
4964 coap_remove_option(sent, lg_crcv->block_option);
4965 }
4966 /* Expire this entry */
4967 LL_DELETE(session->lg_crcv, lg_crcv);
4968 coap_block_delete_lg_crcv(session, lg_crcv);
4969
4970call_app_handler:
4971 return 0;
4972
4973skip_app_handler:
4974 if (!ack_rst_sent)
4975 coap_send_ack_lkd(session, rcvd);
4976 return 1;
4977}
4978#endif /* COAP_CLIENT_SUPPORT */
4979
4980#if COAP_SERVER_SUPPORT
4981/* Check if lg_xmit generated and update PDU code if so */
4982void
4984 const coap_pdu_t *request,
4985 coap_pdu_t *response, const coap_resource_t *resource,
4986 const coap_string_t *query) {
4987 coap_lg_xmit_t *lg_xmit;
4988
4989 if (response->code == 0)
4990 return;
4991 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
4992 if (lg_xmit && lg_xmit->sent_pdu && lg_xmit->sent_pdu->code == 0) {
4993 lg_xmit->sent_pdu->code = response->code;
4994 return;
4995 }
4996}
4997#endif /* COAP_SERVER_SUPPORT */
4998
4999#if COAP_CLIENT_SUPPORT
5000void
5002 uint64_t token_match =
5004 pdu->actual_token.length));
5005 coap_lg_xmit_t *lg_xmit;
5006 coap_lg_crcv_t *lg_crcv;
5007
5008 if (session->lg_crcv) {
5009 LL_FOREACH(session->lg_crcv, lg_crcv) {
5010 if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token))
5011 return;
5012 if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) {
5013 coap_update_token(pdu, lg_crcv->app_token->length,
5014 lg_crcv->app_token->s);
5015 coap_log_debug("Client app version of updated PDU (6)\n");
5017 return;
5018 }
5019 }
5020 }
5021 if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) {
5022 LL_FOREACH(session->lg_xmit, lg_xmit) {
5023 if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token))
5024 return;
5025 if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) {
5026 coap_update_token(pdu, lg_xmit->b.b1.app_token->length,
5027 lg_xmit->b.b1.app_token->s);
5028 coap_log_debug("Client app version of updated PDU (7)\n");
5030 return;
5031 }
5032 }
5033 }
5034}
5035#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:5439
#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:1209
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:1224
#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:5279
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:1482
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:2134
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.
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
union coap_lg_xmit_t::@141351233174036235206056225041131316302112162312 b
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
coap_bin_const_t * last_token
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