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