libcoap 4.3.1
block.c
Go to the documentation of this file.
1/* block.c -- block transfer
2 *
3 * Copyright (C) 2010--2012,2015-2022 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
16#include "coap3/coap_internal.h"
17
18#ifndef min
19#define min(a,b) ((a) < (b) ? (a) : (b))
20#endif
21
22#define STATE_TOKEN_BASE(t) ((t) & 0xffffffffffffULL)
23#define STATE_TOKEN_RETRY(t) ((uint64_t)(t) >> 48)
24#define STATE_TOKEN_FULL(t,r) (STATE_TOKEN_BASE(t) + ((uint64_t)(r) << 48))
25
26unsigned int
27coap_opt_block_num(const coap_opt_t *block_opt) {
28 unsigned int num = 0;
29 uint16_t len;
30
31 len = coap_opt_length(block_opt);
32
33 if (len == 0) {
34 return 0;
35 }
36
37 if (len > 1) {
39 coap_opt_length(block_opt) - 1);
40 }
41
42 return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
43}
44
45int
46coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu,
47 coap_option_num_t number, coap_block_b_t *block) {
48 coap_opt_iterator_t opt_iter;
49 coap_opt_t *option;
50
51 assert(block);
52 memset(block, 0, sizeof(coap_block_b_t));
53
54 if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) {
55 unsigned int num;
56
57 block->aszx = block->szx = COAP_OPT_BLOCK_SZX(option);
58 if (block->szx == 7) {
59 size_t length;
60 const uint8_t *data;
61
62 if (session == NULL || COAP_PROTO_NOT_RELIABLE(session->proto) ||
63 !(session->csm_bert_rem_support && session->csm_bert_loc_support))
64 /* No BERT support */
65 return 0;
66
67 block->szx = 6; /* BERT is 1024 block chunks */
68 block->bert = 1;
69 if (coap_get_data(pdu, &length, &data))
70 block->chunk_size = (uint32_t)length;
71 else
72 block->chunk_size = 0;
73 } else {
74 block->chunk_size = (size_t)1 << (block->szx + 4);
75 }
76 block->defined = 1;
77 if (COAP_OPT_BLOCK_MORE(option))
78 block->m = 1;
79
80 /* The block number is at most 20 bits, so values above 2^20 - 1
81 * are illegal. */
82 num = coap_opt_block_num(option);
83 if (num > 0xFFFFF) {
84 return 0;
85 }
86 block->num = num;
87 return 1;
88 }
89
90 return 0;
91}
92
93int
95 coap_block_t *block) {
96 coap_block_b_t block_b;
97
98 assert(block);
99 memset(block, 0, sizeof(coap_block_t));
100
101 if (coap_get_block_b(NULL, pdu, number, &block_b)) {
102 block->num = block_b.num;
103 block->m = block_b.m;
104 block->szx = block_b.szx;
105 return 1;
106 }
107 return 0;
108}
109
110static int
112 unsigned int num,
113 unsigned int blk_size, size_t total) {
114 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
115 size_t avail = pdu->max_size - token_options;
116 unsigned int start = num << (blk_size + 4);
117 unsigned int can_use_bert = block->defined == 0 || block->bert;
118
119 assert(start <= total);
120 memset(block, 0, sizeof(*block));
121 block->num = num;
122 block->szx = block->aszx = blk_size;
123 if (can_use_bert && blk_size == 6 && avail >= 1024 && session != NULL &&
124 COAP_PROTO_RELIABLE(session->proto) &&
125 session->csm_bert_rem_support && session->csm_bert_loc_support) {
126 block->bert = 1;
127 block->aszx = 7;
128 block->chunk_size = (uint32_t)((avail / 1024) * 1024);
129 } else {
130 block->chunk_size = (size_t)1 << (blk_size + 4);
131 if (avail < block->chunk_size && (total - start) >= avail) {
132 /* Need to reduce block size */
133 unsigned int szx;
134 int new_blk_size;
135
136 if (avail < 16) { /* bad luck, this is the smallest block size */
138 "not enough space, even the smallest block does not fit\n");
139 return 0;
140 }
141 new_blk_size = coap_flsll((long long)avail) - 5;
143 "decrease block size for %zu to %d\n", avail, new_blk_size);
144 szx = block->szx;
145 block->szx = new_blk_size;
146 block->num <<= szx - block->szx;
147 block->chunk_size = (size_t)1 << (new_blk_size + 4);
148 }
149 }
150 block->m = block->chunk_size < total - start;
151 return 1;
152}
153
154int
156 coap_pdu_t *pdu, size_t data_length) {
157 size_t start;
158 unsigned char buf[4];
159 coap_block_b_t block_b;
160
161 assert(pdu);
162
163 start = block->num << (block->szx + 4);
164 if (block->num != 0 && data_length <= start) {
165 coap_log(LOG_DEBUG, "illegal block requested\n");
166 return -2;
167 }
168
169 assert(pdu->max_size > 0);
170
171 block_b.defined = 1;
172 block_b.bert = 0;
173 if (!setup_block_b(NULL, pdu, &block_b, block->num,
174 block->szx, data_length))
175 return -3;
176
177 /* to re-encode the block option */
178 coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
179 ((block_b.num << 4) |
180 (block_b.m << 3) |
181 block_b.szx)),
182 buf);
183
184 return 1;
185}
186
187int
189 coap_option_num_t number,
190 coap_pdu_t *pdu, size_t data_length) {
191 size_t start;
192 unsigned char buf[4];
193
194 assert(pdu);
195
196 start = block->num << (block->szx + 4);
197 if (block->num != 0 && data_length <= start) {
198 coap_log(LOG_DEBUG, "illegal block requested\n");
199 return -2;
200 }
201
202 assert(pdu->max_size > 0);
203
204 if (!setup_block_b(session, pdu, block, block->num,
205 block->szx, data_length))
206 return -3;
207
208 /* to re-encode the block option */
209 coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
210 ((block->num << 4) |
211 (block->m << 3) |
212 block->aszx)),
213 buf);
214
215 return 1;
216}
217
218int
219coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data,
220 unsigned int block_num, unsigned char block_szx) {
221 unsigned int start;
222 start = block_num << (block_szx + 4);
223
224 if (len <= start)
225 return 0;
226
227 return coap_add_data(pdu,
228 min(len - start, ((size_t)1 << (block_szx + 4))),
229 data + start);
230}
231
232int
233coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data,
234 coap_block_b_t *block) {
235 unsigned int start = block->num << (block->szx + 4);
236 size_t max_size;
237
238 if (len <= start)
239 return 0;
240
241 if (block->bert) {
242 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
243 max_size = ((pdu->max_size - token_options) / 1024) * 1024;
244 } else {
245 max_size = (size_t)1 << (block->szx + 4);
246 }
247 block->chunk_size = (uint32_t)max_size;
248
249 return coap_add_data(pdu,
250 min(len - start, max_size),
251 data + start);
252}
253
254/*
255 * Note that the COAP_OPTION_ have to be added in the correct order
256 */
257void
259 coap_pdu_t *response,
260 uint16_t media_type,
261 int maxage,
262 size_t length,
263 const uint8_t* data
264) {
265 coap_key_t etag;
266 unsigned char buf[4];
267 coap_block_t block2;
268 int block2_requested = 0;
269
270 memset(&block2, 0, sizeof(block2));
271 /*
272 * Need to check that a valid block is getting asked for so that the
273 * correct options are put into the PDU.
274 */
275 if (request) {
276 if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
277 block2_requested = 1;
278 if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) {
279 coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
280 block2.num,
281 length >> (block2.szx + 4));
282 response->code = COAP_RESPONSE_CODE(400);
283 goto error;
284 }
285 }
286 }
287 response->code = COAP_RESPONSE_CODE(205);
288
289 /* add etag for the resource */
290 memset(etag, 0, sizeof(etag));
291 coap_hash(data, length, etag);
292 coap_add_option_internal(response, COAP_OPTION_ETAG, sizeof(etag), etag);
293
295 coap_encode_var_safe(buf, sizeof(buf),
296 media_type),
297 buf);
298
299 if (maxage >= 0) {
300 coap_insert_option(response,
302 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
303 }
304
305 if (block2_requested) {
306 int res;
307
308 res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
309
310 switch (res) {
311 case -2: /* illegal block (caught above) */
312 response->code = COAP_RESPONSE_CODE(400);
313 goto error;
314 case -1: /* should really not happen */
315 assert(0);
316 /* fall through if assert is a no-op */
317 case -3: /* cannot handle request */
318 response->code = COAP_RESPONSE_CODE(500);
319 goto error;
320 default: /* everything is good */
321 ;
322 }
323
326 coap_encode_var_safe8(buf, sizeof(buf), length),
327 buf);
328
329 coap_add_block(response, length, data,
330 block2.num, block2.szx);
331 return;
332 }
333
334 /*
335 * Block2 not requested
336 */
337 if (!coap_add_data(response, length, data)) {
338 /*
339 * Insufficient space to add in data - use block mode
340 * set initial block size, will be lowered by
341 * coap_write_block_opt() automatically
342 */
343 block2.num = 0;
344 block2.szx = 6;
345 coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
346
349 coap_encode_var_safe8(buf, sizeof(buf), length),
350 buf);
351
352 coap_add_block(response, length, data,
353 block2.num, block2.szx);
354 }
355 return;
356
357error:
358 coap_add_data(response,
359 strlen(coap_response_phrase(response->code)),
360 (const unsigned char *)coap_response_phrase(response->code));
361}
362
363void
365 uint8_t block_mode) {
366 context->block_mode = block_mode &= (COAP_BLOCK_USE_LIBCOAP |
368 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP))
369 context->block_mode = 0;
370}
371
373full_match(const uint8_t *a, size_t alen,
374 const uint8_t *b, size_t blen) {
375 return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
376}
377
378#if COAP_CLIENT_SUPPORT
379int
381 coap_pdu_type_t type) {
382 coap_lg_crcv_t *cq;
383
384 assert(session);
385 if (!session)
386 return 0;
387
388 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
390 "** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n",
391 coap_session_str(session));
392 return 0;
393 }
394
395 LL_FOREACH(session->lg_crcv, cq) {
396 if (cq->observe_set) {
397 if ((!token && !cq->app_token->length) || (token &&
398 full_match(token->s, token->length, cq->app_token->s,
399 cq->app_token->length))) {
400 uint8_t buf[8];
401 coap_mid_t mid;
402 size_t size;
403 const uint8_t *data;
404 coap_binary_t *otoken = cq->obs_token ? cq->obs_token : cq->app_token;
405 coap_pdu_t * pdu = coap_pdu_duplicate(&cq->pdu,
406 session,
407 otoken->length,
408 otoken->s,
409 NULL);
410
411 cq->observe_set = 0;
412 if (pdu == NULL)
413 return 0;
414 /* Need to make sure that this is the correct type */
415 pdu->type = type;
416
417 if (coap_get_data(&cq->pdu, &size, &data)) {
418 coap_add_data(pdu, size, data);
419 }
421 coap_encode_var_safe(buf, sizeof(buf),
423 buf);
424 mid = coap_send_internal(session, pdu);
425 if (mid != COAP_INVALID_MID)
426 return 1;
427 break;
428 }
429 }
430 }
431 return 0;
432}
433#endif /* COAP_CLIENT_SUPPORT */
434
435static int
437 coap_pdu_t *pdu,
438 coap_resource_t *resource,
439 const coap_string_t *query,
440 int maxage,
441 uint64_t etag,
442 size_t length,
443 const uint8_t *data,
444 coap_release_large_data_t release_func,
445 void *app_ptr,
446 coap_pdu_code_t request_method) {
447
448 ssize_t avail;
449 coap_block_b_t block;
450 size_t chunk;
451 coap_lg_xmit_t *lg_xmit = NULL;
452 uint8_t buf[8];
453 int have_block_defined = 0;
454 uint8_t blk_size;
455 uint16_t option;
456 size_t token_options;
457
458 assert(pdu);
459 if (pdu->data) {
460 coap_log(LOG_WARNING, "coap_add_data_large: PDU already contains data\n");
461 if (release_func)
462 release_func(session, app_ptr);
463 return 0;
464 }
465
466 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
468 "** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n",
469 coap_session_str(session));
470 goto add_data;
471 }
472
473 /* Block NUM max 20 bits and block size is "2**(SZX + 4)"
474 and using SZX max of 6 gives maximum size = 1,073,740,800
475 CSM Max-Message-Size theoretical maximum = 4,294,967,295
476 So, if using blocks, we are limited to 1,073,740,800.
477 */
478#define MAX_BLK_LEN (((1 << 20) - 1) * (1 << (6 + 4)))
479
480 if (length > MAX_BLK_LEN) {
482 "Size of large buffer restricted to 0x%x bytes\n", MAX_BLK_LEN);
483 length = MAX_BLK_LEN;
484 }
485 /* Determine the block size to use, adding in sensible options if needed */
486 if (COAP_PDU_IS_REQUEST(pdu)) {
488
489 option = COAP_OPTION_BLOCK1;
490
491 /* See if this token is already in use for large bodies (unlikely) */
492 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
493 if (full_match(pdu->token, pdu->token_length,
494 lg_xmit->b.b1.app_token->s,
495 lg_xmit->b.b1.app_token->length)) {
496 /* Unfortunately need to free this off as potential size change */
497 LL_DELETE(session->lg_xmit, lg_xmit);
498 coap_block_delete_lg_xmit(session, lg_xmit);
499 lg_xmit = NULL;
501 break;
502 }
503 }
504 }
505 else {
506 /* Have to assume that it is a response even if code is 0.00 */
508 coap_string_t empty = { 0, NULL};
509
510 assert(resource);
511 option = COAP_OPTION_BLOCK2;
512 /* Check if resource+query is already in use for large bodies (unlikely) */
513 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
514 if (resource == lg_xmit->b.b2.resource &&
515 request_method == lg_xmit->b.b2.request_method &&
516 coap_string_equal(query ? query : &empty,
517 lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) {
518 /* Unfortunately need to free this off as potential size change */
519 LL_DELETE(session->lg_xmit, lg_xmit);
520 coap_block_delete_lg_xmit(session, lg_xmit);
521 lg_xmit = NULL;
523 break;
524 }
525 }
526 }
527
528 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
529 avail = pdu->max_size - token_options;
530 /* There may be a response with Echo option */
532 /* May need token of length 8, so account for this */
533 avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0;
534 blk_size = coap_flsll((long long)avail) - 4 - 1;
535 if (blk_size > 6)
536 blk_size = 6;
537
538 /* see if BlockX defined - if so update blk_size as given by app */
539 if (coap_get_block_b(session, pdu, option, &block)) {
540 if (block.szx < blk_size)
541 blk_size = block.szx;
542 have_block_defined = 1;
543 }
544
545 if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) {
546 /* bad luck, this is the smallest block size */
548 "not enough space, even the smallest block does not fit\n");
549 goto fail;
550 }
551
552 chunk = (size_t)1 << (blk_size + 4);
553 if (have_block_defined && block.num != 0) {
554 /* App is defining a single block to send */
555 size_t rem;
556
557 if (length >= block.num * chunk) {
558 rem = chunk;
559 if (chunk > length - block.num * chunk)
560 rem = length - block.num * chunk;
561 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
562 goto fail;
563 }
564 if (release_func)
565 release_func(session, app_ptr);
566 }
567 else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) {
568 /* Only add in lg_xmit if more than one block needs to be handled */
569 size_t rem;
570
571 lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
572 if (!lg_xmit)
573 goto fail;
574
575 /* Set up for displaying all the data in the pdu */
576 pdu->body_data = data;
577 pdu->body_length = length;
578 coap_log(LOG_DEBUG, "PDU presented by app.\n");
580 pdu->body_data = NULL;
581 pdu->body_length = 0;
582
583 coap_log(LOG_DEBUG, "** %s: lg_xmit %p initialized\n",
584 coap_session_str(session), (void*)lg_xmit);
585 /* Update lg_xmit with large data information */
586 memset(lg_xmit, 0, sizeof(coap_lg_xmit_t));
587 lg_xmit->blk_size = blk_size;
588 lg_xmit->option = option;
589 lg_xmit->last_block = 0;
590 lg_xmit->data = data;
591 lg_xmit->length = length;
592 lg_xmit->release_func = release_func;
593 lg_xmit->app_ptr = app_ptr;
594 coap_ticks(&lg_xmit->last_obs);
595 coap_ticks(&lg_xmit->last_sent);
596 if (COAP_PDU_IS_REQUEST(pdu)) {
597 /* Need to keep original token for updating response PDUs */
598 lg_xmit->b.b1.app_token = coap_new_binary(pdu->token_length);
599 if (!lg_xmit->b.b1.app_token)
600 goto fail;
601 memcpy(lg_xmit->b.b1.app_token->s, pdu->token, pdu->token_length);
602 /*
603 * Need to set up new token for use during transmits
604 */
605 lg_xmit->b.b1.count = 1;
606 lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token,
607 lg_xmit->b.b1.count);
608 /*
609 * Token will be updated in pdu later as original pdu may be needed in
610 * coap_send()
611 */
614 coap_encode_var_safe(buf, sizeof(buf),
615 (unsigned int)length),
616 buf);
619 coap_encode_var_safe8(buf, sizeof(buf),
620 ++session->tx_rtag),
621 buf);
622 } else {
623 /*
624 * resource+query match is used for Block2 large body transmissions
625 * token match is used for Block1 large body transmissions
626 */
627 lg_xmit->b.b2.resource = resource;
628 if (query) {
629 lg_xmit->b.b2.query = coap_new_string(query->length);
630 if (lg_xmit->b.b2.query) {
631 memcpy(lg_xmit->b.b2.query->s, query->s, query->length);
632 }
633 }
634 else {
635 lg_xmit->b.b2.query = NULL;
636 }
637 lg_xmit->b.b2.etag = etag;
638 lg_xmit->b.b2.request_method = request_method;
639 if (maxage >= 0) {
640 coap_tick_t now;
641
642 coap_ticks(&now);
643 lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage;
644 }
645 else {
646 lg_xmit->b.b2.maxage_expire = 0;
647 }
650 coap_encode_var_safe(buf, sizeof(buf),
651 (unsigned int)length),
652 buf);
653 if (etag == 0) {
654 if (++session->context->etag == 0)
655 ++session->context->etag;
656 etag = session->context->etag;
657 }
660 coap_encode_var_safe8(buf, sizeof(buf), etag),
661 buf);
662 }
663
664 if (!setup_block_b(session, pdu, &block, block.num,
665 blk_size, lg_xmit->length))
666 goto fail;
667
668 /* Add in with requested block num, more bit and block size */
670 lg_xmit->option,
671 coap_encode_var_safe(buf, sizeof(buf),
672 (block.num << 4) | (block.m << 3) | block.aszx),
673 buf);
674
675 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
676 memcpy(&lg_xmit->pdu, pdu, sizeof(lg_xmit->pdu));
677 lg_xmit->pdu.token = coap_malloc_type(COAP_PDU_BUF,
678 lg_xmit->pdu.used_size + lg_xmit->pdu.max_hdr_size);
679 if (!lg_xmit->pdu.token)
680 goto fail;
681
682 lg_xmit->pdu.alloc_size = lg_xmit->pdu.used_size;
683 lg_xmit->pdu.token += lg_xmit->pdu.max_hdr_size;
684 memcpy(lg_xmit->pdu.token, pdu->token, lg_xmit->pdu.used_size);
685 if (pdu->data)
686 lg_xmit->pdu.data = lg_xmit->pdu.token + (pdu->data - pdu->token);
687
688 /* Check we still have space after adding in some options */
689 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
690 avail = pdu->max_size - token_options;
691 /* There may be a response with Echo option */
693 /* May need token of length 8, so account for this */
694 avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0;
695 if (avail < (ssize_t)chunk) {
696 /* chunk size change down */
697 if (avail < 16) {
699 "not enough space, even the smallest block does not fit\n");
700 goto fail;
701 }
702 blk_size = coap_flsll((long long)avail) - 4 - 1;
703 block.num = block.num << (lg_xmit->blk_size - blk_size);
704 lg_xmit->blk_size = blk_size;
705 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
706 block.chunk_size = (uint32_t)chunk;
707 block.bert = 0;
709 lg_xmit->option,
710 coap_encode_var_safe(buf, sizeof(buf),
711 (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
712 buf);
713 }
714
715 rem = block.chunk_size;
716 if (rem > lg_xmit->length - block.num * chunk)
717 rem = lg_xmit->length - block.num * chunk;
718 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
719 goto fail;
720
721 if (COAP_PDU_IS_REQUEST(pdu))
722 lg_xmit->b.b1.bert_size = rem;
723
724 lg_xmit->last_block = -1;
725
726 /* Link the new lg_xmit in */
727 LL_PREPEND(session->lg_xmit,lg_xmit);
728 }
729 else {
730 /* No need to use blocks */
731 if (have_block_defined) {
733 option,
734 coap_encode_var_safe(buf, sizeof(buf),
735 (0 << 4) | (0 << 3) | blk_size), buf);
736 }
737add_data:
738 if (!coap_add_data(pdu, length, data))
739 goto fail;
740
741 if (release_func)
742 release_func(session, app_ptr);
743 }
744 return 1;
745
746fail:
747 if (lg_xmit) {
748 coap_block_delete_lg_xmit(session, lg_xmit);
749 } else if (release_func) {
750 release_func(session, app_ptr);
751 }
752 return 0;
753}
754
755#if COAP_CLIENT_SUPPORT
756int
758 coap_pdu_t *pdu,
759 size_t length,
760 const uint8_t *data,
761 coap_release_large_data_t release_func,
762 void *app_ptr) {
763 /*
764 * Delay if session->doing_first is set.
765 * E.g. Reliable and CSM not in yet for checking block support
766 */
767 if (coap_client_delay_first(session) == 0) {
768 if (release_func)
769 release_func(session, app_ptr);
770 return 0;
771 }
772 return coap_add_data_large_internal(session, pdu, NULL, NULL, -1, 0, length,
773 data, release_func, app_ptr, 0);
774}
775#endif /* ! COAP_CLIENT_SUPPORT */
776
777#if COAP_SERVER_SUPPORT
778int
780 coap_session_t *session,
781 const coap_pdu_t *request,
782 coap_pdu_t *response,
783 const coap_string_t *query,
784 uint16_t media_type,
785 int maxage,
786 uint64_t etag,
787 size_t length,
788 const uint8_t *data,
789 coap_release_large_data_t release_func,
790 void *app_ptr
791) {
792 unsigned char buf[4];
793 coap_block_b_t block;
794 int block_requested = 0;
795 uint16_t block_opt = COAP_OPTION_BLOCK2;
796
797 memset(&block, 0, sizeof(block));
798 /*
799 * Need to check that a valid block is getting asked for so that the
800 * correct options are put into the PDU.
801 */
802 if (request) {
803 if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) {
804 block_requested = 1;
805 if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
806 coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
807 block.num,
808 length >> (block.szx + 4));
809 response->code = COAP_RESPONSE_CODE(400);
810 goto error;
811 }
812 }
813 }
814
815 if (media_type != 0)
817 coap_encode_var_safe(buf, sizeof(buf),
818 media_type),
819 buf);
820
821 if (maxage >= 0) {
822 coap_insert_option(response,
824 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
825 }
826
827 if (block_requested) {
828 int res;
829
830 res = coap_write_block_b_opt(session, &block, block_opt, response,
831 length);
832
833 switch (res) {
834 case -2: /* illegal block (caught above) */
835 response->code = COAP_RESPONSE_CODE(400);
836 goto error;
837 case -1: /* should really not happen */
838 assert(0);
839 /* fall through if assert is a no-op */
840 case -3: /* cannot handle request */
841 response->code = COAP_RESPONSE_CODE(500);
842 goto error;
843 default: /* everything is good */
844 ;
845 }
846 }
847
848 /* add data body */
849 if (request &&
850 !coap_add_data_large_internal(session, response, resource, query,
851 maxage, etag, length, data,
852 release_func, app_ptr,
853 request->code)) {
854 response->code = COAP_RESPONSE_CODE(500);
855 goto error_released;
856 }
857
858 return 1;
859
860error:
861 if (release_func)
862 release_func(session, app_ptr);
863error_released:
864#if COAP_ERROR_PHRASE_LENGTH > 0
865 coap_add_data(response,
866 strlen(coap_response_phrase(response->code)),
867 (const unsigned char *)coap_response_phrase(response->code));
868#endif /* COAP_ERROR_PHRASE_LENGTH > 0 */
869 return 0;
870}
871#endif /* ! COAP_SERVER_SUPPORT */
872
873/*
874 * return 1 if there is a future expire time, else 0.
875 * update tim_rem with remaining value if return is 1.
876 */
877int
879 coap_tick_t *tim_rem) {
882 coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND;
883 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
884 int ret = 0;
885
886 *tim_rem = -1;
887
888 LL_FOREACH_SAFE(session->lg_xmit, p, q) {
889 if (p->last_all_sent) {
890 if (p->last_all_sent + idle_timeout <= now) {
891 /* Expire this entry */
892 LL_DELETE(session->lg_xmit, p);
893 coap_block_delete_lg_xmit(session, p);
894 }
895 else {
896 /* Delay until the lg_xmit needs to expire */
897 if (*tim_rem > p->last_all_sent + idle_timeout - now) {
898 *tim_rem = p->last_all_sent + idle_timeout - now;
899 ret = 1;
900 }
901 }
902 }
903 else if (p->last_sent) {
904 if (p->last_sent + partial_timeout <= now) {
905 /* Expire this entry */
906 LL_DELETE(session->lg_xmit, p);
907 coap_block_delete_lg_xmit(session, p);
909 }
910 else {
911 /* Delay until the lg_xmit needs to expire */
912 if (*tim_rem > p->last_sent + partial_timeout - now) {
913 *tim_rem = p->last_sent + partial_timeout - now;
914 ret = 1;
915 }
916 }
917 }
918 }
919 return ret;
920}
921
922#if COAP_CLIENT_SUPPORT
923/*
924 * return 1 if there is a future expire time, else 0.
925 * update tim_rem with remaining value if return is 1.
926 */
927int
929 coap_tick_t *tim_rem) {
932 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
933 int ret = 0;
934
935 *tim_rem = -1;
936
937 LL_FOREACH_SAFE(session->lg_crcv, p, q) {
938 if (!p->observe_set && p->last_used &&
939 p->last_used + partial_timeout <= now) {
940 /* Expire this entry */
941 LL_DELETE(session->lg_crcv, p);
942 coap_block_delete_lg_crcv(session, p);
943 }
944 else if (!p->observe_set && p->last_used) {
945 /* Delay until the lg_crcv needs to expire */
946 if (*tim_rem > p->last_used + partial_timeout - now) {
947 *tim_rem = p->last_used + partial_timeout - now;
948 ret = 1;
949 }
950 }
951 }
952 return ret;
953}
954#endif /* COAP_CLIENT_SUPPORT */
955
956static int
957check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
958 uint32_t i;
959
960 for (i = 0; i < rec_blocks->used; i++) {
961 if (block_num < rec_blocks->range[i].begin)
962 return 0;
963 if (block_num <= rec_blocks->range[i].end)
964 return 1;
965 }
966 return 0;
967}
968
969static int
970check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks) {
971 uint32_t i;
972 uint32_t block = 0;
973
974 for (i = 0; i < rec_blocks->used; i++) {
975 if (block < rec_blocks->range[i].begin)
976 return 0;
977 if (block < rec_blocks->range[i].end)
978 block = rec_blocks->range[i].end;
979 }
980 /* total_blocks counts from 1 */
981 if (block + 1 < total_blocks)
982 return 0;
983
984 return 1;
985}
986
987#if COAP_SERVER_SUPPORT
988/*
989 * return 1 if there is a future expire time, else 0.
990 * update tim_rem with remaining value if return is 1.
991 */
992int
994 coap_tick_t *tim_rem) {
997 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
998 int ret = 0;
999
1000 *tim_rem = -1;
1001
1002 LL_FOREACH_SAFE(session->lg_srcv, p, q) {
1003 if (p->last_used && p->last_used + partial_timeout <= now) {
1004 /* Expire this entry */
1005 LL_DELETE(session->lg_srcv, p);
1006 coap_block_delete_lg_srcv(session, p);
1007 }
1008 else if (p->last_used) {
1009 /* Delay until the lg_srcv needs to expire */
1010 if (*tim_rem > p->last_used + partial_timeout - now) {
1011 *tim_rem = p->last_used + partial_timeout - now;
1012 ret = 1;
1013 }
1014 }
1015 }
1016 return ret;
1017}
1018#endif /* COAP_SERVER_SUPPORT */
1019
1020#if COAP_CLIENT_SUPPORT
1023 coap_lg_crcv_t *lg_crcv;
1024 uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);
1025
1026 lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
1027
1028 if (lg_crcv == NULL)
1029 return NULL;
1030
1032 "** %s: lg_crcv %p initialized - stateless token xxxx%012llx\n",
1033 coap_session_str(session), (void*)lg_crcv,
1034 STATE_TOKEN_BASE(state_token));
1035 memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
1036 lg_crcv->initial = 1;
1037 coap_ticks(&lg_crcv->last_used);
1038 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
1039 memcpy(&lg_crcv->pdu, pdu, sizeof(lg_crcv->pdu));
1040 lg_crcv->pdu.token = coap_malloc_type(COAP_PDU_BUF,
1041 lg_crcv->pdu.alloc_size + lg_crcv->pdu.max_hdr_size);
1042 if (!lg_crcv->pdu.token) {
1043 coap_block_delete_lg_crcv(session, lg_crcv);
1044 return NULL;
1045 }
1046 lg_crcv->pdu.token += lg_crcv->pdu.max_hdr_size;
1047 memcpy(lg_crcv->pdu.token, pdu->token, lg_crcv->pdu.used_size);
1048 if (lg_crcv->pdu.data)
1049 lg_crcv->pdu.data = lg_crcv->pdu.token + (pdu->data - pdu->token);
1050 /* Check that there is space for increased token + option change */
1051 if (lg_crcv->pdu.max_size < lg_crcv->pdu.used_size + 9)
1052 lg_crcv->pdu.max_size = lg_crcv->pdu.used_size + 9;
1053
1054 /* Need to keep original token for updating response PDUs */
1055 lg_crcv->app_token = coap_new_binary(pdu->token_length);
1056 if (!lg_crcv->app_token) {
1057 coap_block_delete_lg_crcv(session, lg_crcv);
1058 return NULL;
1059 }
1060 memcpy(lg_crcv->app_token->s, pdu->token, pdu->token_length);
1061
1062 /* Need to set up a base token for actual communications if retries needed */
1063 lg_crcv->retry_counter = 1;
1064 lg_crcv->state_token = state_token;
1065
1066 /* In case it is there - must not be in continuing request PDUs */
1067 coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1);
1068
1069 return lg_crcv;
1070}
1071
1072void
1074 coap_lg_crcv_t *lg_crcv) {
1075 if (lg_crcv == NULL)
1076 return;
1077
1078 if (lg_crcv->pdu.token)
1079 coap_free_type(COAP_PDU_BUF, lg_crcv->pdu.token - lg_crcv->pdu.max_hdr_size);
1081 coap_log(LOG_DEBUG, "** %s: lg_crcv %p released\n",
1082 coap_session_str(session), (void*)lg_crcv);
1083 coap_delete_binary(lg_crcv->app_token);
1084 coap_delete_binary(lg_crcv->obs_token);
1085 coap_free_type(COAP_LG_CRCV, lg_crcv);
1086}
1087#endif /* COAP_CLIENT_SUPPORT */
1088
1089#if COAP_SERVER_SUPPORT
1090void
1092 coap_lg_srcv_t *lg_srcv) {
1093 if (lg_srcv == NULL)
1094 return;
1095
1098 coap_log(LOG_DEBUG, "** %s: lg_srcv %p released\n",
1099 coap_session_str(session), (void*)lg_srcv);
1100 coap_free_type(COAP_LG_SRCV, lg_srcv);
1101}
1102#endif /* COAP_SERVER_SUPPORT */
1103
1104void
1106 coap_lg_xmit_t *lg_xmit) {
1107 if (lg_xmit == NULL)
1108 return;
1109
1110 if (lg_xmit->release_func) {
1111 lg_xmit->release_func(session, lg_xmit->app_ptr);
1112 }
1113 if (lg_xmit->pdu.token) {
1114 coap_free_type(COAP_PDU_BUF, lg_xmit->pdu.token - lg_xmit->pdu.max_hdr_size);
1115 }
1116 if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu))
1117 coap_delete_binary(lg_xmit->b.b1.app_token);
1118 else
1119 coap_delete_string(lg_xmit->b.b2.query);
1120
1121 coap_log(LOG_DEBUG, "** %s: lg_xmit %p released\n",
1122 coap_session_str(session), (void*)lg_xmit);
1123 coap_free_type(COAP_LG_XMIT, lg_xmit);
1124}
1125
1126#if COAP_SERVER_SUPPORT
1127static int
1128add_block_send(uint32_t num, uint32_t *out_blocks,
1129 uint32_t *count, uint32_t max_count) {
1130 uint32_t i;
1131
1132 for (i = 0; i < *count && *count < max_count; i++) {
1133 if (num == out_blocks[i])
1134 return 0;
1135 else if (num < out_blocks[i]) {
1136 if (*count - i > 1)
1137 memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
1138 out_blocks[i] = num;
1139 (*count)++;
1140 return 1;
1141 }
1142 }
1143 if (*count < max_count) {
1144 out_blocks[i] = num;
1145 (*count)++;
1146 return 1;
1147 }
1148 return 0;
1149}
1150
1151/*
1152 * Need to see if this is a request for the next block of a large body
1153 * transfer. If so, need to initiate the response with the next blocks
1154 * and not trouble the application.
1155 *
1156 * If additional responses needed, then these are expicitly sent out and
1157 * 'response' is updated to be the last response to be sent.
1158 *
1159 * This is set up using coap_add_data_large_response()
1160 *
1161 * Server is sending a large data response to GET / observe (Block2)
1162 *
1163 * Return: 0 Call application handler
1164 * 1 Do not call application handler - just send the built response
1165 */
1166int
1168 coap_pdu_t *pdu,
1169 coap_pdu_t *response,
1170 coap_resource_t *resource,
1171 coap_string_t *query) {
1172 coap_lg_xmit_t *p = NULL;
1173 coap_block_b_t block;
1174 uint16_t block_opt = 0;
1175 uint32_t out_blocks[1];
1176 const char *error_phrase;
1177
1178 if (!coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &block))
1179 return 0;
1180 if (block.num == 0) {
1181 /* Get a fresh copy of the data */
1182 return 0;
1183 }
1184 block_opt = COAP_OPTION_BLOCK2;
1185 LL_FOREACH(session->lg_xmit, p) {
1186 size_t chunk;
1187 coap_opt_iterator_t opt_iter;
1188 coap_opt_iterator_t opt_b_iter;
1189 coap_opt_t *option;
1190 uint32_t request_cnt, i;
1191 coap_opt_t *etag_opt = NULL;
1192 coap_pdu_t *out_pdu = response;
1193 static coap_string_t empty = { 0, NULL};
1194
1195 if (COAP_PDU_IS_REQUEST(&p->pdu) || resource != p->b.b2.resource ||
1196 pdu->code != p->b.b2.request_method ||
1197 !coap_string_equal(query ? query : &empty,
1198 p->b.b2.query ? p->b.b2.query : &empty)) {
1199 /* try out the next one */
1200 continue;
1201 }
1202 p->last_all_sent = 0;
1203 etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
1204 if (etag_opt) {
1205 uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
1206 coap_opt_length(etag_opt));
1207 if (etag != p->b.b2.etag) {
1208 /* try out the next one */
1209 continue;
1210 }
1211 out_pdu->code = COAP_RESPONSE_CODE(203);
1212 coap_ticks(&p->last_sent);
1213 goto skip_app_handler;
1214 }
1215 else {
1216 out_pdu->code = p->pdu.code;
1217 }
1218
1219 /* lg_xmit (response) found */
1220 coap_ticks(&p->last_obs);
1221
1222 chunk = (size_t)1 << (p->blk_size + 4);
1223 if (block_opt) {
1224 if (block.bert) {
1226 "found Block option, block is BERT, block nr. %u\n",
1227 block.num);
1228 } else {
1230 "found Block option, block size is %u, block nr. %u\n",
1231 1 << (block.szx + 4), block.num);
1232 }
1233 if (block.bert == 0 && block.szx != p->blk_size) {
1234 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
1235 /*
1236 * Recompute the block number of the previous packet given
1237 * the new block size
1238 */
1239 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
1240 p->blk_size = block.szx;
1241 chunk = (size_t)1 << (p->blk_size + 4);
1242 p->offset = block.num * chunk;
1244 "new Block size is %u, block number %u completed\n",
1245 1 << (block.szx + 4), block.num);
1246 } else {
1248 "ignoring request to increase Block size, "
1249 "next block is not aligned on requested block size "
1250 "boundary. (%zu x %u mod %u = %zu (which is not 0)\n",
1251 p->offset/chunk + 1, (1 << (p->blk_size + 4)),
1252 (1 << (block.szx + 4)),
1253 (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
1254 }
1255 }
1256 }
1257
1258 request_cnt = 0;
1259 coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
1260 while ((option = coap_option_next(&opt_b_iter))) {
1261 unsigned int num;
1262 if (opt_b_iter.number != p->option)
1263 continue;
1264 num = coap_opt_block_num(option);
1265 if (num > 0xFFFFF) /* 20 bits max for num */
1266 continue;
1267 if (block.aszx != COAP_OPT_BLOCK_SZX(option)) {
1268 coap_add_data(response,
1269 sizeof("Changing blocksize during request invalid")-1,
1270 (const uint8_t *)"Changing blocksize during request invalid");
1271 response->code = COAP_RESPONSE_CODE(400);
1272 return 1;
1273 }
1274 add_block_send(num, out_blocks, &request_cnt, 1);
1275 break;
1276 }
1277 if (request_cnt == 0) {
1278 /* Block2 not found - give them the first block */
1279 block.szx = p->blk_size;
1280 p->offset = 0;
1281 out_blocks[0] = 0;
1282 request_cnt = 1;
1283 }
1284
1285 for (i = 0; i < request_cnt; i++) {
1286 uint8_t buf[8];
1287
1288 block.num = out_blocks[i];
1289 p->offset = block.num * chunk;
1290
1291 if (i + 1 < request_cnt) {
1292 /* Need to set up a copy of the pdu to send */
1293 coap_opt_filter_t drop_options;
1294
1295 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1297 out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->token_length,
1298 pdu->token, &drop_options);
1299 if (!out_pdu) {
1300 goto internal_issue;
1301 }
1302 }
1303 else {
1304 /*
1305 * Copy the options across and then fix the block option
1306 *
1307 * Need to drop Observe option if Block2 and block.num != 0
1308 */
1310 while ((option = coap_option_next(&opt_iter))) {
1311 if (opt_iter.number == COAP_OPTION_OBSERVE)
1312 continue;
1313 if (opt_iter.number == p->option)
1314 continue;
1315 if (!coap_insert_option(response, opt_iter.number,
1316 coap_opt_length(option),
1317 coap_opt_value(option))) {
1318 goto internal_issue;
1319 }
1320 }
1321 out_pdu = response;
1322 }
1323 if (pdu->type == COAP_MESSAGE_NON)
1324 out_pdu->type = COAP_MESSAGE_NON;
1325 if (block.bert) {
1326 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
1327 block.m = (p->length - p->offset) >
1328 ((out_pdu->max_size - token_options) /1024) * 1024;
1329 } else {
1330 block.m = (p->offset + chunk) < p->length;
1331 }
1332 if (!coap_update_option(out_pdu, p->option,
1334 sizeof(buf),
1335 (block.num << 4) |
1336 (block.m << 3) |
1337 block.aszx),
1338 buf)) {
1339 goto internal_issue;
1340 }
1341 if (!(p->offset + chunk < p->length)) {
1342 /* Last block - keep in cache for 4 * ACK_TIMOUT */
1344 }
1345 if (p->b.b2.maxage_expire) {
1346 coap_tick_t now;
1347 coap_time_t rem;
1348
1349 coap_ticks(&now);
1350 rem = coap_ticks_to_rt(now);
1351 if (p->b.b2.maxage_expire > rem) {
1352 rem = p->b.b2.maxage_expire - rem;
1353 }
1354 else {
1355 rem = 0;
1356 /* Entry needs to be expired */
1358 }
1361 sizeof(buf),
1362 rem),
1363 buf)) {
1364 goto internal_issue;
1365 }
1366 }
1367
1368 if (!etag_opt && !coap_add_block_b_data(out_pdu,
1369 p->length,
1370 p->data,
1371 &block)) {
1372 goto internal_issue;
1373 }
1374 if (i + 1 < request_cnt) {
1375 coap_ticks(&p->last_sent);
1376 coap_send_internal(session, out_pdu);
1377 }
1378 }
1380 goto skip_app_handler;
1381
1382 } /* end of LL_FOREACH() */
1383 return 0;
1384
1385internal_issue:
1386 response->code = COAP_RESPONSE_CODE(500);
1387 error_phrase = coap_response_phrase(response->code);
1388 coap_add_data(response, strlen(error_phrase),
1389 (const uint8_t *)error_phrase);
1390 /* Keep in cache for 4 * ACK_TIMOUT incase of retry */
1391 if (p)
1393
1394skip_app_handler:
1395 return 1;
1396}
1397#endif /* COAP_SERVER_SUPPORT */
1398
1399static int
1400update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num) {
1401 uint32_t i;
1402
1403 /* Reset as there is activity */
1404 rec_blocks->retry = 0;
1405
1406 for (i = 0; i < rec_blocks->used; i++) {
1407 if (block_num >= rec_blocks->range[i].begin &&
1408 block_num <= rec_blocks->range[i].end)
1409 break;
1410
1411 if (block_num < rec_blocks->range[i].begin) {
1412 if (block_num + 1 == rec_blocks->range[i].begin) {
1413 rec_blocks->range[i].begin = block_num;
1414 }
1415 else {
1416 /* Need to insert a new range */
1417 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
1418 /* Too many losses */
1419 return 0;
1420 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
1421 (rec_blocks->used - i) * sizeof (rec_blocks->range[0]));
1422 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
1423 rec_blocks->used++;
1424 }
1425 break;
1426 }
1427 if (block_num == rec_blocks->range[i].end + 1) {
1428 rec_blocks->range[i].end = block_num;
1429 if (i + 1 < rec_blocks->used) {
1430 if (rec_blocks->range[i+1].begin == block_num + 1) {
1431 /* Merge the 2 ranges */
1432 rec_blocks->range[i].end = rec_blocks->range[i+1].end;
1433 if (i+2 < rec_blocks->used) {
1434 memmove (&rec_blocks->range[i+1], &rec_blocks->range[i+2],
1435 (rec_blocks->used - (i+2)) * sizeof (rec_blocks->range[0]));
1436 }
1437 rec_blocks->used--;
1438 }
1439 }
1440 break;
1441 }
1442 }
1443 if (i == rec_blocks->used) {
1444 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
1445 /* Too many losses */
1446 return 0;
1447 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
1448 rec_blocks->used++;
1449 }
1450 coap_ticks(&rec_blocks->last_seen);
1451 return 1;
1452}
1453
1454#if COAP_SERVER_SUPPORT
1455/*
1456 * Need to check if this is a large PUT / POST using multiple blocks
1457 *
1458 * Server receiving PUT/POST etc. of a large amount of data (Block1)
1459 *
1460 * Return: 0 Call application handler
1461 * 1 Do not call application handler - just send the built response
1462 */
1463int
1465 coap_session_t *session,
1466 coap_pdu_t *pdu,
1467 coap_pdu_t *response,
1468 coap_resource_t *resource,
1469 coap_string_t *uri_path,
1470 coap_opt_t *observe,
1471 coap_string_t *query,
1473 int *added_block) {
1474 size_t length = 0;
1475 const uint8_t *data = NULL;
1476 size_t offset = 0;
1477 size_t total = 0;
1478 coap_block_b_t block;
1479 coap_opt_iterator_t opt_iter;
1480 uint16_t block_option = 0;
1481
1482 coap_get_data_large(pdu, &length, &data, &offset, &total);
1483 pdu->body_offset = 0;
1484 pdu->body_total = length;
1485
1486 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
1487 block_option = COAP_OPTION_BLOCK1;
1488 }
1489 if (block_option) {
1490 coap_lg_srcv_t *p;
1491 coap_opt_t *size_opt = coap_check_option(pdu,
1493 &opt_iter);
1494 coap_opt_t *fmt_opt = coap_check_option(pdu,
1496 &opt_iter);
1497 uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
1498 coap_opt_length(fmt_opt)) :
1500 coap_opt_t *rtag_opt = coap_check_option(pdu,
1502 &opt_iter);
1503 size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
1504 const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
1505
1506 total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
1507 coap_opt_length(size_opt)) : 0;
1508 offset = block.num << (block.szx + 4);
1509
1510 LL_FOREACH(session->lg_srcv, p) {
1511 if (rtag_opt || p->rtag_set == 1) {
1512 if (!(rtag_opt && p->rtag_set == 1))
1513 continue;
1514 if (p->rtag_length != rtag_length ||
1515 memcmp(p->rtag, rtag, rtag_length) != 0)
1516 continue;
1517 }
1518 if (resource == p->resource) {
1519 break;
1520 }
1521 if ((p->resource == context->unknown_resource ||
1522 resource == context->proxy_uri_resource) &&
1523 coap_string_equal(uri_path, p->uri_path))
1524 break;
1525 }
1526 if (!p && block.num != 0) {
1527 /* random access - no need to track */
1528 pdu->body_data = data;
1529 pdu->body_length = length;
1530 pdu->body_offset = offset;
1531 pdu->body_total = length + offset + (block.m ? 1 : 0);
1532 }
1533 /* Do not do this if this is a single block */
1534 else if (!p && !(offset == 0 && block.m == 0)) {
1536 if (p == NULL) {
1537 coap_add_data(response, sizeof("Memory issue")-1,
1538 (const uint8_t *)"Memory issue");
1539 response->code = COAP_RESPONSE_CODE(500);
1540 goto skip_app_handler;
1541 }
1542 coap_log(LOG_DEBUG, "** %s: lg_srcv %p initialized\n",
1543 coap_session_str(session), (void*)p);
1544 memset(p, 0, sizeof(coap_lg_srcv_t));
1545 coap_ticks(&p->last_used);
1546 p->resource = resource;
1547 if (resource == context->unknown_resource ||
1548 resource == context->proxy_uri_resource)
1549 p->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
1550 p->content_format = fmt;
1551 p->total_len = total;
1552 p->amount_so_far = length;
1553 p->szx = block.szx;
1554 p->block_option = block_option;
1555 if (observe) {
1556 p->observe_length = min(coap_opt_length(observe), 3);
1557 memcpy(p->observe, coap_opt_value(observe), p->observe_length);
1558 p->observe_set = 1;
1559 }
1560 if (rtag_opt) {
1561 p->rtag_length = (uint8_t)rtag_length;
1562 memcpy(p->rtag, rtag, rtag_length);
1563 p->rtag_set = 1;
1564 }
1565 p->body_data = NULL;
1566 LL_PREPEND(session->lg_srcv, p);
1567 }
1568 if (p) {
1569 if (fmt != p->content_format) {
1570 coap_add_data(response, sizeof("Content-Format mismatch")-1,
1571 (const uint8_t *)"Content-Format mismatch");
1572 response->code = COAP_RESPONSE_CODE(408);
1573 goto free_lg_srcv;
1574 }
1575 p->last_mid = pdu->mid;
1576 p->last_type = pdu->type;
1577 memcpy(p->last_token, pdu->token, pdu->token_length);
1579 if ((session->block_mode & COAP_BLOCK_SINGLE_BODY) || block.bert) {
1580 size_t chunk = (size_t)1 << (block.szx + 4);
1581 int update_data = 0;
1582 unsigned int saved_num = block.num;
1583 size_t saved_offset = offset;
1584
1585 while (offset < saved_offset + length) {
1586 if (!check_if_received_block(&p->rec_blocks, block.num)) {
1587 /* Update list of blocks received */
1588 if (!update_received_blocks(&p->rec_blocks, block.num)) {
1590 coap_add_data(response, sizeof("Too many missing blocks")-1,
1591 (const uint8_t *)"Too many missing blocks");
1592 response->code = COAP_RESPONSE_CODE(408);
1593 goto free_lg_srcv;
1594 }
1595 update_data = 1;
1596 }
1597 block.num++;
1598 offset = block.num << (block.szx + 4);
1599 }
1600 block.num--;
1601 if (update_data) {
1602 /* Update saved data */
1603 p->body_data = coap_block_build_body(p->body_data, length, data,
1604 saved_offset, p->total_len);
1605 if (!p->body_data)
1606 goto call_app_handler;
1607
1608 }
1609 if (block.m ||
1611 (uint32_t)(p->total_len + chunk -1)/chunk)) {
1612 /* Not all the payloads of the body have arrived */
1613 if (block.m) {
1614 uint8_t buf[4];
1615
1616 /* Ask for the next block */
1617 coap_insert_option(response, block_option,
1618 coap_encode_var_safe(buf, sizeof(buf),
1619 (saved_num << 4) |
1620 (block.m << 3) |
1621 block.aszx),
1622 buf);
1623 response->code = COAP_RESPONSE_CODE(231);
1624 goto skip_app_handler;
1625 }
1626 goto skip_app_handler;
1627 }
1628
1629 /*
1630 * Remove the Block1 option as passing all of the data to
1631 * application layer. Add back in observe option if appropriate.
1632 * Adjust all other information.
1633 */
1634 if (p->observe_set) {
1636 p->observe_length, p->observe);
1637 }
1638 coap_remove_option(pdu, block_option);
1639 pdu->body_data = p->body_data->s;
1640 pdu->body_length = p->total_len;
1641 pdu->body_offset = 0;
1642 pdu->body_total = p->total_len;
1643 coap_log(LOG_DEBUG, "Server app version of updated PDU\n");
1645 coap_log(LOG_DEBUG, "call custom handler for resource '%*.*s'\n",
1646 (int)resource->uri_path->length,
1647 (int)resource->uri_path->length, resource->uri_path->s);
1648 /* Need to do this here as we need to free off p */
1649 h(resource, session, pdu, query, response);
1650 /* Check if lg_xmit generated and update PDU code if so */
1651 coap_check_code_lg_xmit(session, response, resource, query, pdu->code);
1652 /* Check to see if the server is doing a 4.01 + Echo response */
1653 if (response->code == COAP_RESPONSE_CODE(401) &&
1654 coap_check_option(response, COAP_OPTION_ECHO, &opt_iter)) {
1655 /* Need to keep lg_srcv around for client's response */
1656 goto skip_app_handler;
1657 } else {
1658 /* Last chunk - lg_srcv no longer needed */
1659 goto free_lg_srcv;
1660 }
1661 }
1662 else {
1663 /* No need to update body_data and body_length as a single PDU */
1664 pdu->body_offset = offset;
1665 /* Exact match if last block */
1666 if (block.m) {
1667 uint8_t buf[4];
1668
1669 if (total > offset + length + block.m)
1670 pdu->body_total = total;
1671 else
1672 pdu->body_total = offset + length + block.m;
1673
1674 coap_insert_option(response, block_option,
1675 coap_encode_var_safe(buf, sizeof(buf),
1676 (block.num << 4) |
1677 (block.m << 3) |
1678 block.aszx),
1679 buf);
1680 coap_log(LOG_DEBUG, "call custom handler for resource '%*.*s'\n",
1681 (int)resource->uri_path->length,
1682 (int)resource->uri_path->length, resource->uri_path->s);
1683 h(resource, session, pdu, query, response);
1684 /* Check if lg_xmit generated and update PDU code if so */
1685 coap_check_code_lg_xmit(session, response, resource, query,
1686 pdu->code);
1687 if (COAP_RESPONSE_CLASS(response->code) == 2) {
1688 /* Just in case, as there are more to go */
1689 response->code = COAP_RESPONSE_CODE(231);
1690 }
1691 *added_block = 1;
1692 goto skip_app_handler;
1693 }
1694 else {
1695 pdu->body_total = offset + length + block.m;
1696 }
1697 }
1698
1699 if (block.m == 0) {
1700 /* Last chunk - free off all */
1701 coap_ticks(&p->last_used);
1702 }
1703 goto call_app_handler;
1704
1705free_lg_srcv:
1706 LL_DELETE(session->lg_srcv, p);
1707 coap_block_delete_lg_srcv(session, p);
1708 goto skip_app_handler;
1709 }
1710 }
1711call_app_handler:
1712 return 0;
1713
1714skip_app_handler:
1715 return 1;
1716}
1717#endif /* COAP_SERVER_SUPPORT */
1718
1719#if COAP_CLIENT_SUPPORT
1720static int
1721check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
1722 coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv)
1723{
1724 /* Check for Echo option for freshness */
1725 coap_opt_iterator_t opt_iter;
1726 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
1727
1728 if (opt) {
1729 if (sent || lg_xmit || lg_crcv) {
1730 /* Need to retransmit original request with Echo option added */
1731 coap_pdu_t *echo_pdu;
1732 coap_mid_t mid;
1733 const uint8_t *data;
1734 size_t data_len;
1735 int have_data = 0;
1736 uint8_t ltoken[8];
1737 size_t ltoken_len;
1738 uint64_t token;
1739
1740 if (sent) {
1741 if (coap_get_data(sent, &data_len, &data))
1742 have_data = 1;
1743 } else if (lg_xmit) {
1744 sent = &lg_xmit->pdu;
1745 if (lg_xmit->length) {
1746 size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
1747 size_t offset = (lg_xmit->last_block + 1) * blk_size;
1748 have_data = 1;
1749 data = &lg_xmit->data[offset];
1750 data_len = (lg_xmit->length - offset) > blk_size ? blk_size :
1751 lg_xmit->length - offset;
1752 }
1753 } else /* lg_crcv */ {
1754 sent = &lg_crcv->pdu;
1755 if (coap_get_data(sent, &data_len, &data))
1756 have_data = 1;
1757 }
1758 if (lg_xmit) {
1759 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
1760 ++lg_xmit->b.b1.count);
1761 } else {
1762 token = STATE_TOKEN_FULL(lg_crcv->state_token,
1763 ++lg_crcv->retry_counter);
1764 }
1765 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
1766 echo_pdu = coap_pdu_duplicate(sent, session, ltoken_len, ltoken, NULL);
1767 if (!echo_pdu)
1768 return 0;
1769 if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
1770 coap_opt_length(opt), coap_opt_value(opt)))
1771 goto not_sent;
1772 if (have_data) {
1773 coap_add_data(echo_pdu, data_len, data);
1774 }
1775
1776 mid = coap_send_internal(session, echo_pdu);
1777 if (mid == COAP_INVALID_MID)
1778 goto not_sent;
1779 return 1;
1780 } else {
1781 /* Need to save Echo option value to add to next reansmission */
1782not_sent:
1783 coap_delete_bin_const(session->echo);
1784 session->echo = coap_new_bin_const(coap_opt_value(opt),
1785 coap_opt_length(opt));
1786 }
1787 }
1788 return 0;
1789}
1790
1791static void
1792track_echo(coap_session_t *session, coap_pdu_t *rcvd)
1793{
1794 coap_opt_iterator_t opt_iter;
1795 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
1796
1797 if (opt) {
1798 coap_delete_bin_const(session->echo);
1799 session->echo = coap_new_bin_const(coap_opt_value(opt),
1800 coap_opt_length(opt));
1801 }
1802}
1803
1804/*
1805 * Need to see if this is a response to a large body request transfer. If so,
1806 * need to initiate the request containing the next block and not trouble the
1807 * application. Note that Token must unique per request/response.
1808 *
1809 * Client receives large data acknowledgement from server (Block1)
1810 *
1811 * This is set up using coap_add_data_large_request()
1812 *
1813 * Client is using GET etc.
1814 *
1815 * Return: 0 Call application handler
1816 * 1 Do not call application handler - just send the built response
1817 */
1818int
1820 coap_pdu_t *rcvd) {
1821 coap_lg_xmit_t *p;
1822 coap_lg_xmit_t *q;
1824 rcvd->token_length));
1825 coap_lg_crcv_t *lg_crcv = NULL;
1826
1827 LL_FOREACH_SAFE(session->lg_xmit, p, q) {
1828 if (!COAP_PDU_IS_REQUEST(&p->pdu) ||
1830 token_match !=
1832 p->b.b1.app_token->length)))) {
1833 /* try out the next one */
1834 continue;
1835 }
1836 /* lg_xmit found */
1837 size_t chunk = (size_t)1 << (p->blk_size + 4);
1838 coap_block_b_t block;
1839
1840 if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
1841 coap_get_block_b(session, rcvd, p->option, &block)) {
1842
1843 if (block.bert) {
1845 "found Block option, block is BERT, block nr. %u (%zu)\n",
1846 block.num, p->b.b1.bert_size);
1847 } else {
1849 "found Block option, block size is %u, block nr. %u\n",
1850 1 << (block.szx + 4), block.num);
1851 }
1852 if (block.szx != p->blk_size) {
1853 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
1854 /*
1855 * Recompute the block number of the previous packet given the
1856 * new block size
1857 */
1858 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
1859 p->blk_size = block.szx;
1860 chunk = (size_t)1 << (p->blk_size + 4);
1861 p->offset = block.num * chunk;
1863 "new Block size is %u, block number %u completed\n",
1864 1 << (block.szx + 4), block.num);
1865 block.bert = 0;
1866 block.aszx = block.szx;
1867 } else {
1868 coap_log(LOG_DEBUG, "ignoring request to increase Block size, "
1869 "next block is not aligned on requested block size boundary. "
1870 "(%zu x %u mod %u = %zu != 0)\n",
1871 p->offset/chunk + 1, (1 << (p->blk_size + 4)),
1872 (1 << (block.szx + 4)),
1873 (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
1874 }
1875 }
1876 track_echo(session, rcvd);
1877 if (p->last_block == (int)block.num) {
1878 /*
1879 * Duplicate Block1 ACK
1880 *
1881 * RFCs not clear here, but on a lossy connection, there could
1882 * be multiple Block1 ACKs, causing the client to retransmit the
1883 * same block multiple times, or the server retransmitting the
1884 * same ACK.
1885 *
1886 * Once a block has been ACKd, there is no need to retransmit it.
1887 */
1888 return 1;
1889 }
1890 if (block.bert)
1891 block.num += (unsigned int)(p->b.b1.bert_size / 1024 - 1);
1892 p->last_block = block.num;
1893 p->offset = (block.num + 1) * chunk;
1894 if (p->offset < p->length) {
1895 /* Build the next PDU request based off the skeletal PDU */
1896 uint8_t buf[8];
1897 coap_pdu_t *pdu;
1898 uint64_t token = STATE_TOKEN_FULL(p->b.b1.state_token, ++p->b.b1.count);
1899 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
1900
1901 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
1902 if (!pdu)
1903 goto fail_body;
1904
1905 block.num++;
1906 if (block.bert) {
1907 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
1908 block.m = (p->length - p->offset) >
1909 ((pdu->max_size - token_options) /1024) * 1024;
1910 } else {
1911 block.m = (p->offset + chunk) < p->length;
1912 }
1913 coap_update_option(pdu, p->option,
1914 coap_encode_var_safe(buf, sizeof(buf),
1915 (block.num << 4) |
1916 (block.m << 3) |
1917 block.aszx),
1918 buf);
1919
1920 if (!coap_add_block_b_data(pdu,
1921 p->length,
1922 p->data,
1923 &block))
1924 goto fail_body;
1925 p->b.b1.bert_size = block.chunk_size;
1926 coap_ticks(&p->last_sent);
1927 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
1928 goto fail_body;
1929 return 1;
1930 }
1931 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
1932 if (check_freshness(session, rcvd, sent, p, NULL))
1933 return 1;
1934 }
1935 goto lg_xmit_finished;
1936 } /* end of LL_FOREACH_SAFE */
1937 return 0;
1938
1939fail_body:
1941 /* There has been an internal error of some sort */
1942 rcvd->code = COAP_RESPONSE_CODE(500);
1943lg_xmit_finished:
1944 if (session->lg_crcv) {
1945 LL_FOREACH(session->lg_crcv, lg_crcv) {
1946 if (STATE_TOKEN_BASE(p->b.b1.state_token) ==
1947 STATE_TOKEN_BASE(lg_crcv->state_token)) {
1948 /* In case of observe */
1949 lg_crcv->state_token = p->b.b1.state_token;
1950 break;
1951 }
1952 }
1953 }
1954 if (!lg_crcv) {
1955 /* need to put back original token into rcvd */
1956 if (p->b.b1.app_token)
1958 p->b.b1.app_token->s);
1959 coap_log(LOG_DEBUG, "PDU given to app\n");
1960 coap_show_pdu(LOG_DEBUG, rcvd);
1961 }
1962
1963 LL_DELETE(session->lg_xmit, p);
1964 coap_block_delete_lg_xmit(session, p);
1965 return 0;
1966}
1967#endif /* COAP_CLIENT_SUPPORT */
1968
1969/*
1970 * Re-assemble payloads into a body
1971 */
1973coap_block_build_body(coap_binary_t *body_data, size_t length,
1974 const uint8_t *data, size_t offset, size_t total)
1975{
1976 if (data == NULL)
1977 return NULL;
1978 if (body_data == NULL && total) {
1979 body_data = coap_new_binary(total);
1980 }
1981 if (body_data == NULL)
1982 return NULL;
1983
1984 /* Update saved data */
1985 if (offset + length <= total && body_data->length >= total) {
1986 memcpy(&body_data->s[offset], data, length);
1987 }
1988 else {
1989 /*
1990 * total may be inaccurate as per
1991 * https://tools.ietf.org/html/rfc7959#section-4
1992 * o In a request carrying a Block1 Option, to indicate the current
1993 * estimate the client has of the total size of the resource
1994 * representation, measured in bytes ("size indication").
1995 * o In a response carrying a Block2 Option, to indicate the current
1996 * estimate the server has of the total size of the resource
1997 * representation, measured in bytes ("size indication").
1998 */
1999 coap_binary_t *new = coap_resize_binary(body_data, offset + length);
2000
2001 if (new) {
2002 body_data = new;
2003 memcpy(&body_data->s[offset], data, length);
2004 }
2005 else {
2006 coap_delete_binary(body_data);
2007 return NULL;
2008 }
2009 }
2010 return body_data;
2011}
2012
2013#if COAP_CLIENT_SUPPORT
2014/*
2015 * Need to see if this is a large body response to a request. If so,
2016 * need to initiate the request for the next block and not trouble the
2017 * application. Note that Token must be unique per request/response.
2018 *
2019 * This is set up using coap_send()
2020 * Client receives large data from server (Block2)
2021 *
2022 * Return: 0 Call application handler
2023 * 1 Do not call application handler - just sent the next request
2024 */
2025int
2027 coap_session_t *session,
2028 coap_pdu_t *sent,
2029 coap_pdu_t *rcvd,
2030 coap_recurse_t recursive) {
2031 coap_lg_crcv_t *p;
2032 int app_has_response = 0;
2033 coap_block_b_t block;
2034 int have_block = 0;
2035 uint16_t block_opt = 0;
2036 size_t offset;
2038 rcvd->token_length));
2039
2040 memset(&block, 0, sizeof(block));
2041 LL_FOREACH(session->lg_crcv, p) {
2042 size_t chunk = 0;
2043 uint8_t buf[8];
2044 coap_opt_iterator_t opt_iter;
2045
2047 !full_match(rcvd->token, rcvd->token_length,
2048 p->app_token->s, p->app_token->length)) {
2049 /* try out the next one */
2050 continue;
2051 }
2052
2053 /* lg_crcv found */
2054
2055 if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
2056 size_t length;
2057 const uint8_t *data;
2059 &opt_iter);
2060 size_t size2 = size_opt ?
2062 coap_opt_length(size_opt)) : 0;
2063
2064 /* length and data are cleared on error */
2065 (void)coap_get_data(rcvd, &length, &data);
2066 rcvd->body_offset = 0;
2067 rcvd->body_total = length;
2068 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
2069 have_block = 1;
2070 block_opt = COAP_OPTION_BLOCK2;
2071 }
2072 track_echo(session, rcvd);
2073 if (have_block && (block.m || length)) {
2074 coap_opt_t *fmt_opt = coap_check_option(rcvd,
2076 &opt_iter);
2077 uint16_t fmt = fmt_opt ?
2079 coap_opt_length(fmt_opt)) :
2081 coap_opt_t *etag_opt = coap_check_option(rcvd,
2083 &opt_iter);
2084 size_t saved_offset;
2085 int updated_block;
2086
2087 /* Possibility that Size2 not sent, or is too small */
2088 chunk = (size_t)1 << (block.szx + 4);
2089 offset = block.num * chunk;
2090 if (size2 < (offset + length)) {
2091 if (block.m)
2092 size2 = offset + length + 1;
2093 else
2094 size2 = offset + length;
2095 }
2096 saved_offset = offset;
2097
2098 if (p->initial) {
2099 p->initial = 0;
2100 if (etag_opt) {
2101 p->etag_length = coap_opt_length(etag_opt);
2102 memcpy(p->etag, coap_opt_value(etag_opt), p->etag_length);
2103 p->etag_set = 1;
2104 }
2105 else {
2106 p->etag_set = 0;
2107 }
2108 p->total_len = size2;
2109 p->content_format = fmt;
2110 p->szx = block.szx;
2111 p->block_option = block_opt;
2112 p->last_type = rcvd->type;
2113 p->rec_blocks.used = 0;
2114 }
2115 if (p->total_len < size2)
2116 p->total_len = size2;
2117
2118 if (etag_opt) {
2119 if (!full_match(coap_opt_value(etag_opt),
2120 coap_opt_length(etag_opt),
2121 p->etag, p->etag_length)) {
2122 /* body of data has changed - need to restart request */
2123 coap_pdu_t *pdu;
2124 uint64_t token = STATE_TOKEN_FULL(p->state_token,
2125 ++p->retry_counter);
2126 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
2127 coap_opt_filter_t drop_options;
2128
2130 "Data body updated during receipt - new request started\n");
2131 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
2133
2134 p->initial = 1;
2136 p->body_data = NULL;
2137
2138 coap_session_new_token(session, &len, buf);
2139 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
2141 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, &drop_options);
2142 if (!pdu)
2143 goto fail_resp;
2144
2145 coap_update_option(pdu, block_opt,
2146 coap_encode_var_safe(buf, sizeof(buf),
2147 (0 << 4) | (0 << 3) | block.aszx),
2148 buf);
2149
2150 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
2151 goto fail_resp;
2152
2153 goto skip_app_handler;
2154 }
2155 }
2156 else if (p->etag_set) {
2157 /* Cannot handle this change in ETag to not being there */
2158 coap_log(LOG_WARNING, "Not all blocks have ETag option\n");
2159 session->block_mode &= ~(COAP_BLOCK_SINGLE_BODY);
2160 goto block_mode;
2161 }
2162
2163 if (fmt != p->content_format) {
2164 coap_log(LOG_WARNING, "Content-Format option mismatch\n");
2165 session->block_mode &= ~(COAP_BLOCK_SINGLE_BODY);
2166 goto block_mode;
2167 }
2168 if (block.num == 0) {
2169 coap_opt_t *obs_opt = coap_check_option(rcvd,
2171 &opt_iter);
2172 if (obs_opt) {
2173 p->observe_length = min(coap_opt_length(obs_opt), 3);
2174 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
2175 p->observe_set = 1;
2176 /* Need to keep observe response token for later cancellation */
2179 if (!p->obs_token) {
2180 goto fail_resp;
2181 }
2182 memcpy(p->obs_token->s, rcvd->token, rcvd->token_length);
2183 }
2184 else {
2185 p->observe_set = 0;
2186 }
2187 }
2188 updated_block = 0;
2189 while (offset < saved_offset + length) {
2190 if (!check_if_received_block(&p->rec_blocks, block.num)) {
2191 /* Update list of blocks received */
2192 if (!update_received_blocks(&p->rec_blocks, block.num)) {
2194 goto fail_resp;
2195 }
2196 updated_block = 1;
2197 }
2198 block.num++;
2199 offset = block.num << (block.szx + 4);
2200 if (!block.bert)
2201 break;
2202 }
2203 block.num--;
2204 if (updated_block) {
2205 if ((session->block_mode & COAP_BLOCK_SINGLE_BODY) || block.bert) {
2206 p->body_data = coap_block_build_body(p->body_data, length, data,
2207 saved_offset, size2);
2208 if (p->body_data == NULL) {
2209 /* Need to do it block by block */
2210 session->block_mode &= ~(COAP_BLOCK_SINGLE_BODY);
2211 goto block_mode;
2212 }
2213 }
2214 if (block.m || !check_all_blocks_in(&p->rec_blocks,
2215 (size2 + chunk -1) / chunk)) {
2216 /* Not all the payloads of the body have arrived */
2217 size_t len;
2218 coap_pdu_t *pdu;
2219 uint64_t token;
2220
2221 if (block.m) {
2222 block.m = 0;
2223
2224 /* Ask for the next block */
2225 token = STATE_TOKEN_FULL(p->state_token, ++p->retry_counter);
2226 len = coap_encode_var_safe8(buf, sizeof(token), token);
2227 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
2228 if (!pdu)
2229 goto fail_resp;
2230
2231 if (rcvd->type == COAP_MESSAGE_NON)
2232 pdu->type = COAP_MESSAGE_NON; /* Server is using NON */
2233
2234 /* Only sent with the first block */
2236
2237 coap_update_option(pdu, block_opt,
2238 coap_encode_var_safe(buf, sizeof(buf),
2239 ((block.num + 1) << 4) |
2240 (block.m << 3) | block.aszx),
2241 buf);
2242
2243 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
2244 goto fail_resp;
2245 }
2246 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY) || block.bert)
2247 goto skip_app_handler;
2248
2249 /* need to put back original token into rcvd */
2251 rcvd->body_offset = saved_offset;
2252 rcvd->body_total = size2;
2253 goto call_app_handler;
2254 }
2255 /* need to put back original token into rcvd */
2257 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY) || block.bert) {
2258 /* Pretend that there is no block */
2259 coap_remove_option(rcvd, block_opt);
2260 if (p->observe_set) {
2262 p->observe_length, p->observe);
2263 }
2264 rcvd->body_data = p->body_data->s;
2265 rcvd->body_length = saved_offset + length;
2266 rcvd->body_offset = 0;
2267 rcvd->body_total = rcvd->body_length;
2268 }
2269 else {
2270 rcvd->body_offset = saved_offset;
2271 rcvd->body_total = size2;
2272 }
2273 if (context->response_handler) {
2274 if (block.m != 0 || block.num != 0) {
2275 coap_log(LOG_DEBUG, "Client app version of updated PDU\n");
2276 coap_show_pdu(LOG_DEBUG, rcvd);
2277 }
2278 context->response_handler(session, sent, rcvd, rcvd->mid);
2279 }
2280 app_has_response = 1;
2281 /* Set up for the next data body if observing */
2282 p->initial = 1;
2283 if (p->body_data) {
2285 p->body_data = NULL;
2286 }
2287 else {
2288 goto skip_app_handler;
2289 }
2290 }
2291 else {
2292block_mode:
2293 /* need to put back original token into rcvd */
2295 rcvd->body_offset = saved_offset;
2296 /* slightly oversize if there is more data */
2297 if (block.m) {
2298 if(size2 > saved_offset + length + block.m)
2299 rcvd->body_total = size2;
2300 else
2301 rcvd->body_total = saved_offset + length + block.m;
2302 }
2303 else {
2304 rcvd->body_total = saved_offset + length;
2305 /* Set up for the next data body if observing */
2306 p->initial = 1;
2307 }
2308 if (context->response_handler) {
2309 coap_log(LOG_DEBUG, "Client app version of updated PDU\n");
2310 coap_show_pdu(LOG_DEBUG, rcvd);
2311 context->response_handler(session, sent, rcvd, rcvd->mid);
2312 }
2313 app_has_response = 1;
2314 }
2315 }
2316 else {
2317 coap_opt_t *obs_opt = coap_check_option(rcvd,
2319 &opt_iter);
2320 if (obs_opt) {
2321 p->observe_length = min(coap_opt_length(obs_opt), 3);
2322 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
2323 p->observe_set = 1;
2324 /* Need to keep observe response token for later cancellation */
2327 if (!p->obs_token) {
2328 goto fail_resp;
2329 }
2330 memcpy(p->obs_token->s, rcvd->token, rcvd->token_length);
2331 }
2332 else {
2333 p->observe_set = 0;
2334 /* Expire this entry */
2335 goto expire_lg_crcv;
2336 }
2337 }
2338 coap_ticks(&p->last_used);
2339 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
2340 if (check_freshness(session, rcvd, sent, NULL, p))
2341 goto skip_app_handler;
2342 goto expire_lg_crcv;
2343 } else {
2344 /* Not 2.xx or 4.01 - assume it is a failure of some sort */
2345 goto expire_lg_crcv;
2346 }
2347 if (!block.m && !p->observe_set) {
2348fail_resp:
2349 /* lg_crcv no longer required - cache it for 1 sec */
2350 coap_ticks(&p->last_used);
2353 }
2354 /* need to put back original token into rcvd */
2356 break;
2357 } /* LL_FOREACH() */
2358
2359 /* Check if receiving a block response and if blocks can be set up */
2360 if (recursive == COAP_RECURSE_OK && !p) {
2361 if (!sent) {
2362 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
2363 coap_log(LOG_DEBUG, "** %s: large body receive internal issue\n",
2364 coap_session_str(session));
2365 goto skip_app_handler;
2366 }
2367 }
2368 else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
2369 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
2370 have_block = 1;
2371 block_opt = COAP_OPTION_BLOCK2;
2372 if (block.num != 0) {
2373 /* Assume random access and just give the single response to app */
2374 size_t length;
2375 const uint8_t *data;
2376 size_t chunk = (size_t)1 << (block.szx + 4);
2377
2378 coap_get_data(rcvd, &length, &data);
2379 rcvd->body_offset = block.num*chunk;
2380 rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
2381 goto call_app_handler;
2382 }
2383 }
2384 if (have_block) {
2385 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent);
2386
2387 if (lg_crcv) {
2388 uint8_t buf[8];
2389 size_t length = coap_encode_var_safe8(buf,
2390 sizeof(lg_crcv->state_token),
2391 lg_crcv->state_token);
2392 if (coap_update_token(rcvd, length, buf)) {
2393 LL_PREPEND(session->lg_crcv, lg_crcv);
2394 return coap_handle_response_get_block(context, session, sent, rcvd,
2396 }
2397 coap_block_delete_lg_crcv(session, lg_crcv);
2398 }
2399 }
2400 track_echo(session, rcvd);
2401 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
2402 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent);
2403
2404 if (lg_crcv) {
2405 LL_PREPEND(session->lg_crcv, lg_crcv);
2406 return coap_handle_response_get_block(context, session, sent, rcvd,
2408 }
2409 }
2410 }
2411 return app_has_response;
2412
2413expire_lg_crcv:
2414 /* need to put back original token into rcvd */
2416 /* Expire this entry */
2417 LL_DELETE(session->lg_crcv, p);
2418 coap_block_delete_lg_crcv(session, p);
2419
2420call_app_handler:
2421 return 0;
2422
2423skip_app_handler:
2424 return 1;
2425}
2426#endif /* COAP_CLIENT_SUPPORT */
2427
2428/* Check if lg_xmit generated and update PDU code if so */
2429void
2431 coap_resource_t *resource, coap_string_t *query,
2432 coap_pdu_code_t request_method) {
2433 coap_lg_xmit_t *lg_xmit;
2434 coap_string_t empty = { 0, NULL};
2435
2436 if (response->code == 0)
2437 return;
2438 LL_FOREACH(session->lg_xmit, lg_xmit) {
2439 if (!COAP_PDU_IS_REQUEST(&lg_xmit->pdu) &&
2440 lg_xmit->b.b2.resource == resource &&
2441 lg_xmit->b.b2.request_method == request_method &&
2442 coap_string_equal(query ? query : &empty,
2443 lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) {
2444 /* lg_xmit found */
2445 if (lg_xmit->pdu.code == 0) {
2446 lg_xmit->pdu.code = response->code;
2447 return;
2448 }
2449 }
2450 }
2451}
COAP_STATIC_INLINE int full_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition: block.c:373
#define MAX_BLK_LEN
static int coap_add_data_large_internal(coap_session_t *session, 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, void *app_ptr, coap_pdu_code_t request_method)
Definition: block.c:436
#define STATE_TOKEN_FULL(t, r)
Definition: block.c:24
static int check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks)
Definition: block.c:970
#define STATE_TOKEN_BASE(t)
Definition: block.c:22
static int update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num)
Definition: block.c:1400
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: block.c:111
#define min(a, b)
Definition: block.c:19
static int check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num)
Definition: block.c:957
unsigned char coap_key_t[4]
Definition: coap_hashkey.h:24
#define coap_hash(String, Length, Result)
Definition: coap_hashkey.h:38
Pulls together all the internal only header files.
uint16_t coap_option_num_t
Definition: coap_option.h:20
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:26
int coap_flsll(long long i)
Definition: encode.c:26
void coap_block_delete_lg_srcv(coap_session_t *session, coap_lg_srcv_t *lg_srcv)
int coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
int coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
#define COAP_RBLOCK_CNT
void coap_block_delete_lg_crcv(coap_session_t *session, coap_lg_crcv_t *lg_crcv)
int coap_handle_response_get_block(coap_context_t *context, coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd, coap_recurse_t recursive)
coap_lg_crcv_t * coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu)
int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd)
coap_recurse_t
void coap_block_delete_lg_xmit(coap_session_t *session, coap_lg_xmit_t *lg_xmit)
Definition: block.c:1105
void coap_check_code_lg_xmit(coap_session_t *session, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *query, coap_pdu_code_t request_method)
The function checks that the code in a newly formed lg_xmit created by coap_add_data_large_response()...
Definition: block.c:2430
int coap_handle_request_send_block(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *query)
int coap_handle_request_put_block(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *uri_path, coap_opt_t *observe, coap_string_t *query, coap_method_handler_t h, int *added_block)
int coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
Definition: block.c:878
@ COAP_RECURSE_OK
@ COAP_RECURSE_NO
void coap_context_set_block_mode(coap_context_t *context, uint8_t block_mode)
Set the context level CoAP block handling bits for handling RFC7959.
Definition: block.c:364
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_OPT_BLOCK_SZX(opt)
Returns the value of the SZX-field of a Block option opt.
Definition: block.h:77
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: block.c:233
#define COAP_BLOCK_SINGLE_BODY
Definition: block.h:62
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: block.c:188
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: block.c:219
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: block.h:276
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: block.c:258
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: block.c:46
#define COAP_OPT_BLOCK_MORE(opt)
Returns the value of the More-bit of a Block option opt.
Definition: block.h:73
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: block.c:27
int coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number, coap_block_t *block)
Initializes block from pdu.
Definition: block.c:94
#define COAP_OPT_BLOCK_LAST(opt)
Returns the value of the least significant byte of a Block option opt.
Definition: block.h:69
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: block.c:155
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.
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.
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.
Definition: block.c:1973
#define COAP_BLOCK_USE_LIBCOAP
Definition: block.h:61
void coap_ticks(coap_tick_t *t)
Sets t to the internal time with COAP_TICKS_PER_SECOND resolution.
time_t coap_time_t
CoAP time in seconds since epoch.
Definition: coap_time.h:132
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition: coap_time.h:127
coap_time_t coap_ticks_to_rt(coap_tick_t t)
Helper function that converts coap ticks to wallclock time.
#define COAP_TICKS_PER_SECOND
Use ms resolution on POSIX systems.
Definition: coap_time.h:142
void(* coap_method_handler_t)(coap_resource_t *, coap_session_t *, const coap_pdu_t *, const coap_string_t *, coap_pdu_t *)
Definition of message handler function.
Definition: resource.h:43
int coap_client_delay_first(coap_session_t *session)
Delay the sending of the first client request until some other negotiation has completed.
Definition: net.c:1045
coap_mid_t coap_send_internal(coap_session_t *session, coap_pdu_t *pdu)
Sends a CoAP message to given peer.
Definition: net.c:1242
int coap_handle_event(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: net.c:3352
unsigned int coap_encode_var_safe(uint8_t *buf, size_t length, unsigned int val)
Encodes multiple-length byte sequences.
Definition: encode.c:45
unsigned int coap_decode_var_bytes(const uint8_t *buf, size_t len)
Decodes multiple-length byte sequences.
Definition: encode.c:36
uint64_t coap_decode_var_bytes8(const uint8_t *buf, size_t len)
Decodes multiple-length byte sequences.
Definition: encode.c:65
unsigned int coap_encode_var_safe8(uint8_t *buf, size_t length, uint64_t val)
Encodes multiple-length byte sequences.
Definition: encode.c:75
@ COAP_EVENT_PARTIAL_BLOCK
Triggered when not all of a large body has been received.
Definition: coap_event.h:71
@ COAP_EVENT_XMIT_BLOCK_FAIL
Triggered when not all of a large body has been transmitted.
Definition: coap_event.h:73
void coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu)
Display the contents of the specified pdu.
Definition: coap_debug.c:523
#define LOG_DEBUG
Definition: coap_debug.h:81
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define LOG_WARNING
Definition: coap_debug.h:72
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:165
#define COAP_OBSERVE_CANCEL
The value COAP_OBSERVE_CANCEL in a GET/FETCH request option COAP_OPTION_OBSERVE indicates that the ob...
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
Definition: coap_option.c:152
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: coap_option.c:215
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.
Definition: coap_option.c:116
size_t coap_opt_encode_size(uint16_t delta, size_t length)
Compute storage bytes needed for an option with given delta and length.
Definition: coap_option.c:354
#define COAP_OPT_ALL
Pre-defined filter that includes all options.
Definition: coap_option.h:108
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.
Definition: coap_option.c:202
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition: coap_option.c:252
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option)
Sets the corresponding entry for number in filter.
Definition: coap_option.c:497
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: pdu.c:477
int coap_remove_option(coap_pdu_t *pdu, coap_option_num_t number)
Removes (first) option of given number from the pdu.
Definition: pdu.c:333
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: pdu.c:299
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: pdu.c:565
#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: pdu.c:615
#define COAP_OPTION_BLOCK2
Definition: pdu.h:128
const char * coap_response_phrase(unsigned char code)
Returns a human-readable response phrase for the specified CoAP response code.
Definition: pdu.c:788
#define COAP_OPTION_CONTENT_FORMAT
Definition: pdu.h:120
#define COAP_OPTION_SIZE2
Definition: pdu.h:130
#define COAP_OPTION_BLOCK1
Definition: pdu.h:129
int coap_mid_t
coap_mid_t is used to store the CoAP Message ID of a CoAP PDU.
Definition: pdu.h:243
#define COAP_RESPONSE_CODE(N)
Definition: pdu.h:146
#define COAP_RESPONSE_CLASS(C)
Definition: pdu.h:149
coap_pdu_code_t
Set of codes available for a PDU.
Definition: pdu.h:303
#define COAP_OPTION_SIZE1
Definition: pdu.h:133
coap_pdu_type_t
CoAP PDU message type definitions.
Definition: pdu.h:60
#define COAP_MEDIATYPE_TEXT_PLAIN
Definition: pdu.h:195
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: pdu.c:713
#define COAP_OPTION_RTAG
Definition: pdu.h:136
coap_pdu_t * coap_pdu_duplicate(const coap_pdu_t *old_pdu, coap_session_t *session, size_t token_length, const uint8_t *token, coap_opt_filter_t *drop_options)
Duplicate an existing PDU.
Definition: pdu.c:167
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: pdu.c:721
#define COAP_INVALID_MID
Indicates an invalid message id.
Definition: pdu.h:246
#define COAP_OPTION_MAXAGE
Definition: pdu.h:123
#define COAP_OPTION_ETAG
Definition: pdu.h:113
#define COAP_OPTION_OBSERVE
Definition: pdu.h:115
#define COAP_OPTION_ECHO
Definition: pdu.h:134
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: pdu.c:682
@ COAP_MESSAGE_NON
Definition: pdu.h:62
#define COAP_MAX_TRANSMIT_WAIT_TICKS(s)
#define COAP_PROTO_NOT_RELIABLE(p)
Definition: coap_session.h:36
#define COAP_PROTO_RELIABLE(p)
Definition: coap_session.h:37
void coap_session_new_token(coap_session_t *session, size_t *len, uint8_t *data)
Creates a new token for use.
void coap_delete_bin_const(coap_bin_const_t *s)
Deletes the given const binary data and releases any memory allocated.
Definition: str.c:109
void coap_delete_str_const(coap_str_const_t *s)
Deletes the given const string and releases any memory allocated.
Definition: str.c:58
coap_binary_t * coap_new_binary(size_t size)
Returns a new binary object with at least size bytes storage allocated.
Definition: str.c:72
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: str.c:100
coap_binary_t * coap_resize_binary(coap_binary_t *s, size_t size)
Resizes the given coap_binary_t object.
Definition: str.c:76
void coap_delete_binary(coap_binary_t *s)
Deletes the given coap_binary_t object and releases any memory allocated.
Definition: str.c:96
#define coap_string_equal(string1, string2)
Compares the two strings for equality.
Definition: str.h:189
coap_string_t * coap_new_string(size_t size)
Returns a new string object with at least size+1 bytes storage allocated.
Definition: str.c:20
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: str.c:49
void coap_delete_string(coap_string_t *s)
Deletes the given string and releases any memory allocated.
Definition: str.c:45
#define COAP_STATIC_INLINE
Definition: libcoap.h:45
@ COAP_LG_XMIT
Definition: mem.h:55
@ COAP_LG_CRCV
Definition: mem.h:56
@ COAP_LG_SRCV
Definition: mem.h:57
@ COAP_STRING
Definition: mem.h:37
@ COAP_PDU_BUF
Definition: mem.h:45
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().
COAP_STATIC_INLINE int token_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition: net.c:1039
CoAP binary data definition.
Definition: str.h:56
size_t length
length of binary data
Definition: str.h:57
uint8_t * s
binary data
Definition: str.h:58
Structure of Block options with BERT support.
Definition: block.h:51
unsigned int num
block number
Definition: block.h:52
uint32_t chunk_size
‍1024 if BERT
Definition: block.h:58
unsigned int bert
Operating as BERT.
Definition: block.h:57
unsigned int aszx
block size (0-7 including BERT
Definition: block.h:55
unsigned int defined
Set if block found.
Definition: block.h:56
unsigned int m
1 if more blocks follow, 0 otherwise
Definition: block.h:53
unsigned int szx
block size (0-6)
Definition: block.h:54
Structure of Block options.
Definition: block.h:42
unsigned int num
block number
Definition: block.h:43
unsigned int szx
block size
Definition: block.h:45
unsigned int m
1 if more blocks follow, 0 otherwise
Definition: block.h:44
The CoAP stack's global state is stored in a coap_context_t object.
uint64_t etag
Next ETag to use.
coap_response_handler_t response_handler
coap_resource_t * proxy_uri_resource
can be used for handling proxy URI resources
uint8_t block_mode
Zero or more COAP_BLOCK_ or'd options.
coap_resource_t * unknown_resource
can be used for handling unknown resources
uint64_t state_token
state token
size_t bert_size
size of last BERT block
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.
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.
Structure to hold large body (many blocks) client receive information.
uint16_t block_option
Block option in use.
uint8_t etag[8]
ETag for block checking.
uint8_t etag_length
ETag length.
uint8_t last_type
Last request type (CON/NON)
uint8_t observe_length
Length of observe data.
uint8_t observe[3]
Observe data (if observe_set) (only 24 bits)
uint8_t etag_set
Set if ETag is in receive PDU.
coap_rblock_t rec_blocks
coap_binary_t * obs_token
Initial Observe response PDU token.
uint8_t initial
If set, has not been used yet.
uint8_t szx
size of individual blocks
uint16_t content_format
Content format for the set of blocks.
coap_pdu_t pdu
skeletal PDU
coap_tick_t last_used
< list of received blocks
uint64_t state_token
state token
coap_binary_t * app_token
app requesting PDU token
uint16_t retry_counter
Retry counter (part of state token)
coap_binary_t * body_data
Used for re-assembling entire body.
uint8_t observe_set
Set if this is an observe receive PDU.
size_t total_len
Length as indicated by SIZE2 option.
Structure to hold large body (many blocks) server receive information.
uint8_t rtag[8]
RTag for block checking.
coap_mid_t last_mid
Last received mid for this set of packets.
uint8_t last_token[8]
< list of received blocks
uint8_t rtag_set
Set if RTag is in receive PDU.
uint16_t block_option
Block option in use.
size_t total_len
Length as indicated by SIZE1 option.
uint8_t observe_length
Length of observe data.
coap_rblock_t rec_blocks
set to uri_path if unknown resource
coap_binary_t * body_data
Used for re-assembling entire body.
coap_resource_t * resource
associated resource
size_t last_token_length
length of token
uint8_t observe_set
Set if this is an observe receive PDU.
uint8_t rtag_length
RTag length.
uint8_t last_type
Last request type (CON/NON)
uint8_t szx
size of individual blocks
size_t amount_so_far
Amount of data seen so far.
coap_tick_t last_used
Last time data sent or 0.
uint8_t observe[3]
Observe data (if set) (only 24 bits)
uint16_t content_format
Content format for the set of blocks.
coap_str_const_t * uri_path
Structure to hold large body (many blocks) transmission information.
coap_tick_t last_all_sent
Last time all data sent or 0.
coap_release_large_data_t release_func
large data de-alloc function
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
const uint8_t * data
large data ptr
int last_block
last acknowledged block number
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 pdu
skeletal PDU
size_t length
large data length
coap_l_block1_t b1
coap_l_block2_t b2
uint16_t option
large block transmisson CoAP option
void * app_ptr
applicaton provided ptr for de-alloc function
coap_tick_t last_obs
Last time used (Observe tracking) or 0.
Iterator to run through PDU options.
Definition: coap_option.h:171
coap_option_num_t number
decoded option number
Definition: coap_option.h:173
structure for CoAP PDUs
uint8_t max_hdr_size
space reserved for protocol-specific header
uint8_t * token
first byte of token, if any, or options
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)
uint8_t token_length
length of Token
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
size_t body_total
Holds body data total size.
coap_pdu_type_t type
message type
Structure to keep track of received blocks.
coap_tick_t last_seen
struct coap_lg_range range[COAP_RBLOCK_CNT]
Abstraction of resource that can be attached to coap_context_t.
coap_str_const_t * uri_path
Request URI Path for this resource.
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
uint8_t csm_bert_rem_support
CSM TCP BERT blocks supported (remote)
uint64_t tx_token
Next token number to use.
uint8_t block_mode
Zero or more COAP_BLOCK_ or'd options.
uint8_t csm_bert_loc_support
CSM TCP BERT blocks supported (local)
coap_proto_t proto
protocol used
uint32_t tx_rtag
Next Request-Tag number to use.
coap_lg_srcv_t * lg_srcv
Server list of expected large receives.
coap_lg_crcv_t * lg_crcv
Client list of expected large receives.
coap_context_t * context
session's context
coap_bin_const_t * echo
last token used to make a request
const uint8_t * s
read-only string data
Definition: str.h:48
size_t length
length of string
Definition: str.h:47
CoAP string data definition.
Definition: str.h:38
uint8_t * s
string data
Definition: str.h:40
size_t length
length of string
Definition: str.h:39