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