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_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_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_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_insert_option(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 |
369 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP))
370 context->block_mode = 0;
371}
372
374full_match(const uint8_t *a, size_t alen,
375 const uint8_t *b, size_t blen) {
376 return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
377}
378
379#if COAP_CLIENT_SUPPORT
380
381int
383 coap_pdu_type_t type) {
384 coap_lg_crcv_t *lg_crcv, *q;
385
386 assert(session);
387 if (!session)
388 return 0;
389
390 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
392 "** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n",
393 coap_session_str(session));
394 return 0;
395 }
396
397 LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) {
398 if (lg_crcv->observe_set) {
399 if ((!token && !lg_crcv->app_token->length) || (token &&
400 full_match(token->s, token->length, lg_crcv->app_token->s,
401 lg_crcv->app_token->length))) {
402 uint8_t buf[8];
403 coap_mid_t mid;
404 size_t size;
405 const uint8_t *data;
406 coap_binary_t *otoken = lg_crcv->obs_token ? lg_crcv->obs_token[0] ?
407 lg_crcv->obs_token[0] :
408 lg_crcv->app_token :
409 lg_crcv->app_token;
410 coap_pdu_t * pdu = coap_pdu_duplicate(&lg_crcv->pdu,
411 session,
412 otoken->length,
413 otoken->s,
414 NULL);
415
416 lg_crcv->observe_set = 0;
417 if (pdu == NULL)
418 return 0;
419 /* Need to make sure that this is the correct type */
420 pdu->type = type;
421
423 coap_encode_var_safe(buf, sizeof(buf),
425 buf);
426 if (coap_get_data(&lg_crcv->pdu, &size, &data))
427 coap_add_data_large_request(session, pdu, size, data, NULL, NULL);
428
429 /*
430 * Need to fix lg_xmit stateless token as using tokens from
431 observe setup
432 */
433 if (pdu->lg_xmit)
434 pdu->lg_xmit->b.b1.state_token = lg_crcv->state_token;
435
436 mid = coap_send_internal(session, pdu);
437 if (mid != COAP_INVALID_MID)
438 return 1;
439 break;
440 }
441 }
442 }
443 return 0;
444}
445#endif /* COAP_CLIENT_SUPPORT */
446
447#if COAP_SERVER_SUPPORT
448/*
449 * Find the response lg_xmit
450 */
453 const coap_pdu_t *request,
454 const coap_resource_t *resource,
455 const coap_string_t *query)
456{
457 coap_lg_xmit_t *lg_xmit;
458 coap_opt_iterator_t opt_iter;
459 coap_opt_t *rtag_opt = coap_check_option(request,
461 &opt_iter);
462 size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
463 const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
464
465 LL_FOREACH(session->lg_xmit, lg_xmit) {
466 static coap_string_t empty = { 0, NULL};
467
468 if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu) ||
469 resource != lg_xmit->b.b2.resource ||
470 request->code != lg_xmit->b.b2.request_method ||
471 !coap_string_equal(query ? query : &empty,
472 lg_xmit->b.b2.query ?
473 lg_xmit->b.b2.query : &empty)) {
474 /* try out the next one */
475 continue;
476 }
477 /* lg_xmit is a response */
478 if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) {
479 if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1))
480 continue;
481 if (lg_xmit->b.b2.rtag_length != rtag_length ||
482 memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0)
483 continue;
484 }
485 return lg_xmit;
486 }
487 return NULL;
488}
489#endif /* COAP_SERVER_SUPPORT */
490
491static int
493 const coap_pdu_t *request,
494 coap_pdu_t *pdu,
495 coap_resource_t *resource,
496 const coap_string_t *query,
497 int maxage,
498 uint64_t etag,
499 size_t length,
500 const uint8_t *data,
501 coap_release_large_data_t release_func,
502 void *app_ptr,
503 coap_pdu_code_t request_method) {
504
505 ssize_t avail;
506 coap_block_b_t block;
507 size_t chunk;
508 coap_lg_xmit_t *lg_xmit = NULL;
509 uint8_t buf[8];
510 int have_block_defined = 0;
511 uint8_t blk_size;
512 uint16_t option;
513 size_t token_options;
514 coap_opt_t *opt;
515 coap_opt_iterator_t opt_iter;
516
517 assert(pdu);
518 if (pdu->data) {
519 coap_log_warn("coap_add_data_large: PDU already contains data\n");
520 if (release_func)
521 release_func(session, app_ptr);
522 return 0;
523 }
524
525 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
527 "** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n",
528 coap_session_str(session));
529 goto add_data;
530 }
531
532 /* A lot of the reliable code assumes type is CON */
533 if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON)
534 pdu->type = COAP_MESSAGE_CON;
535
536 /* Block NUM max 20 bits and block size is "2**(SZX + 4)"
537 and using SZX max of 6 gives maximum size = 1,073,740,800
538 CSM Max-Message-Size theoretical maximum = 4,294,967,295
539 So, if using blocks, we are limited to 1,073,740,800.
540 */
541#define MAX_BLK_LEN (((1 << 20) - 1) * (1 << (6 + 4)))
542
543 if (length > MAX_BLK_LEN) {
545 "Size of large buffer restricted to 0x%x bytes\n", MAX_BLK_LEN);
546 length = MAX_BLK_LEN;
547 }
548 /* Determine the block size to use, adding in sensible options if needed */
549 if (COAP_PDU_IS_REQUEST(pdu)) {
551
552 option = COAP_OPTION_BLOCK1;
553
554 /* See if this token is already in use for large bodies (unlikely) */
555 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
556 if (full_match(pdu->token, pdu->token_length,
557 lg_xmit->b.b1.app_token->s,
558 lg_xmit->b.b1.app_token->length)) {
559 /* Unfortunately need to free this off as potential size change */
560 LL_DELETE(session->lg_xmit, lg_xmit);
561 coap_block_delete_lg_xmit(session, lg_xmit);
562 lg_xmit = NULL;
564 break;
565 }
566 }
567 }
568 else {
569 /* Have to assume that it is a response even if code is 0.00 */
570 assert(resource);
571 option = COAP_OPTION_BLOCK2;
572#if COAP_SERVER_SUPPORT
573 /*
574 * Check if resource+query+rtag is already in use for large bodies
575 * (unlikely)
576 */
577 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
578 if (lg_xmit) {
579 /* Unfortunately need to free this off as potential size change */
580 LL_DELETE(session->lg_xmit, lg_xmit);
581 coap_block_delete_lg_xmit(session, lg_xmit);
582 lg_xmit = NULL;
584 }
585#endif /* COAP_SERVER_SUPPORT */
586 }
587
588 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
589 avail = pdu->max_size - token_options;
590 /* There may be a response with Echo option */
592 /* May need token of length 8, so account for this */
593 avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0;
594 blk_size = coap_flsll((long long)avail) - 4 - 1;
595 if (blk_size > 6)
596 blk_size = 6;
597
598 /* see if BlockX defined - if so update blk_size as given by app */
599 if (coap_get_block_b(session, pdu, option, &block)) {
600 if (block.szx < blk_size)
601 blk_size = block.szx;
602 have_block_defined = 1;
603 }
604
605 if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) {
606 /* bad luck, this is the smallest block size */
608 "not enough space, even the smallest block does not fit\n");
609 goto fail;
610 }
611
612 chunk = (size_t)1 << (blk_size + 4);
613 if (have_block_defined && block.num != 0) {
614 /* App is defining a single block to send */
615 size_t rem;
616
617 if (length >= block.num * chunk) {
618 rem = chunk;
619 if (chunk > length - block.num * chunk)
620 rem = length - block.num * chunk;
621 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
622 goto fail;
623 }
624 if (release_func)
625 release_func(session, app_ptr);
626 }
627 else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) {
628 /* Only add in lg_xmit if more than one block needs to be handled */
629 size_t rem;
630
631 lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
632 if (!lg_xmit)
633 goto fail;
634
635 /* Set up for displaying all the data in the pdu */
636 pdu->body_data = data;
637 pdu->body_length = length;
638 coap_log_debug("PDU presented by app.\n");
640 pdu->body_data = NULL;
641 pdu->body_length = 0;
642
643 coap_log_debug("** %s: lg_xmit %p initialized\n",
644 coap_session_str(session), (void*)lg_xmit);
645 /* Update lg_xmit with large data information */
646 memset(lg_xmit, 0, sizeof(coap_lg_xmit_t));
647 lg_xmit->blk_size = blk_size;
648 lg_xmit->option = option;
649 lg_xmit->last_block = 0;
650 lg_xmit->data = data;
651 lg_xmit->length = length;
652 lg_xmit->release_func = release_func;
653 lg_xmit->app_ptr = app_ptr;
654 pdu->lg_xmit = lg_xmit;
655 coap_ticks(&lg_xmit->last_obs);
656 coap_ticks(&lg_xmit->last_sent);
657 if (COAP_PDU_IS_REQUEST(pdu)) {
658 /* Need to keep original token for updating response PDUs */
659 lg_xmit->b.b1.app_token = coap_new_binary(pdu->token_length);
660 if (!lg_xmit->b.b1.app_token)
661 goto fail;
662 memcpy(lg_xmit->b.b1.app_token->s, pdu->token, pdu->token_length);
663 /*
664 * Need to set up new token for use during transmits
665 */
666 lg_xmit->b.b1.count = 1;
667 lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token,
668 lg_xmit->b.b1.count);
669 /*
670 * Token will be updated in pdu later as original pdu may be needed in
671 * coap_send()
672 */
675 coap_encode_var_safe(buf, sizeof(buf),
676 (unsigned int)length),
677 buf);
678 if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter))
681 coap_encode_var_safe(buf, sizeof(buf),
682 ++session->tx_rtag),
683 buf);
684 } else {
685 /*
686 * resource+query+rtag match is used for Block2 large body transmissions
687 * token match is used for Block1 large body transmissions
688 */
689 lg_xmit->b.b2.resource = resource;
690 if (query) {
691 lg_xmit->b.b2.query = coap_new_string(query->length);
692 if (lg_xmit->b.b2.query) {
693 memcpy(lg_xmit->b.b2.query->s, query->s, query->length);
694 }
695 } else {
696 lg_xmit->b.b2.query = NULL;
697 }
698 opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter);
699 if (opt) {
700 lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt),
701 sizeof(lg_xmit->b.b2.rtag));
702 memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), coap_opt_length(opt));
703 lg_xmit->b.b2.rtag_set = 1;
704 }
705 else {
706 lg_xmit->b.b2.rtag_set = 0;
707 }
708 lg_xmit->b.b2.etag = etag;
709 lg_xmit->b.b2.request_method = request_method;
710 if (maxage >= 0) {
711 coap_tick_t now;
712
713 coap_ticks(&now);
714 lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage;
715 }
716 else {
717 lg_xmit->b.b2.maxage_expire = 0;
718 }
721 coap_encode_var_safe(buf, sizeof(buf),
722 (unsigned int)length),
723 buf);
724 if (etag == 0) {
725 if (++session->context->etag == 0)
726 ++session->context->etag;
727 etag = session->context->etag;
728 }
731 coap_encode_var_safe8(buf, sizeof(buf), etag),
732 buf);
733 }
734
735 if (!setup_block_b(session, pdu, &block, block.num,
736 blk_size, lg_xmit->length))
737 goto fail;
738
739 /* Add in with requested block num, more bit and block size */
741 lg_xmit->option,
742 coap_encode_var_safe(buf, sizeof(buf),
743 (block.num << 4) | (block.m << 3) | block.aszx),
744 buf);
745
746 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
747 memcpy(&lg_xmit->pdu, pdu, sizeof(lg_xmit->pdu));
748 lg_xmit->pdu.token = coap_malloc_type(COAP_PDU_BUF,
749 lg_xmit->pdu.used_size + lg_xmit->pdu.max_hdr_size);
750 if (!lg_xmit->pdu.token)
751 goto fail;
752
753 lg_xmit->pdu.alloc_size = lg_xmit->pdu.used_size;
754 lg_xmit->pdu.token += lg_xmit->pdu.max_hdr_size;
755 memcpy(lg_xmit->pdu.token, pdu->token, lg_xmit->pdu.used_size);
756 if (pdu->data)
757 lg_xmit->pdu.data = lg_xmit->pdu.token + (pdu->data - pdu->token);
758
759 /* Check we still have space after adding in some options */
760 token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
761 avail = pdu->max_size - token_options;
762 /* There may be a response with Echo option */
764 /* May need token of length 8, so account for this */
765 avail -= (pdu->token_length < 8) ? 8 - pdu->token_length : 0;
766 if (avail < (ssize_t)chunk) {
767 /* chunk size change down */
768 if (avail < 16) {
770 "not enough space, even the smallest block does not fit\n");
771 goto fail;
772 }
773 blk_size = coap_flsll((long long)avail) - 4 - 1;
774 block.num = block.num << (lg_xmit->blk_size - blk_size);
775 lg_xmit->blk_size = blk_size;
776 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
777 block.chunk_size = (uint32_t)chunk;
778 block.bert = 0;
780 lg_xmit->option,
781 coap_encode_var_safe(buf, sizeof(buf),
782 (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
783 buf);
784 }
785
786 rem = block.chunk_size;
787 if (rem > lg_xmit->length - block.num * chunk)
788 rem = lg_xmit->length - block.num * chunk;
789 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
790 goto fail;
791
792 if (COAP_PDU_IS_REQUEST(pdu))
793 lg_xmit->b.b1.bert_size = rem;
794
795 lg_xmit->last_block = -1;
796
797 /* Link the new lg_xmit in */
798 LL_PREPEND(session->lg_xmit,lg_xmit);
799 }
800 else {
801 /* No need to use blocks */
802 if (have_block_defined) {
804 option,
805 coap_encode_var_safe(buf, sizeof(buf),
806 (0 << 4) | (0 << 3) | blk_size), buf);
807 }
808add_data:
809 if (!coap_add_data(pdu, length, data))
810 goto fail;
811
812 if (release_func)
813 release_func(session, app_ptr);
814 }
815 return 1;
816
817fail:
818 if (lg_xmit) {
819 coap_block_delete_lg_xmit(session, lg_xmit);
820 } else if (release_func) {
821 release_func(session, app_ptr);
822 }
823 return 0;
824}
825
826#if COAP_CLIENT_SUPPORT
827int
829 coap_pdu_t *pdu,
830 size_t length,
831 const uint8_t *data,
832 coap_release_large_data_t release_func,
833 void *app_ptr) {
834 /*
835 * Delay if session->doing_first is set.
836 * E.g. Reliable and CSM not in yet for checking block support
837 */
838 if (coap_client_delay_first(session) == 0) {
839 if (release_func)
840 release_func(session, app_ptr);
841 return 0;
842 }
843 return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0,
844 length, data, release_func, app_ptr, 0);
845}
846#endif /* ! COAP_CLIENT_SUPPORT */
847
848#if COAP_SERVER_SUPPORT
849int
851 coap_session_t *session,
852 const coap_pdu_t *request,
853 coap_pdu_t *response,
854 const coap_string_t *query,
855 uint16_t media_type,
856 int maxage,
857 uint64_t etag,
858 size_t length,
859 const uint8_t *data,
860 coap_release_large_data_t release_func,
861 void *app_ptr
862) {
863 unsigned char buf[4];
864 coap_block_b_t block;
865 int block_requested = 0;
866 uint16_t block_opt = COAP_OPTION_BLOCK2;
867
868 memset(&block, 0, sizeof(block));
869 /*
870 * Need to check that a valid block is getting asked for so that the
871 * correct options are put into the PDU.
872 */
873 if (request) {
874 if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) {
875 block_requested = 1;
876 if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
877 coap_log_debug("Illegal block requested (%d > last = %zu)\n",
878 block.num,
879 length >> (block.szx + 4));
880 response->code = COAP_RESPONSE_CODE(400);
881 goto error;
882 }
883 }
884 }
885
886 if (media_type != 0)
888 coap_encode_var_safe(buf, sizeof(buf),
889 media_type),
890 buf);
891
892 if (maxage >= 0) {
893 coap_insert_option(response,
895 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
896 }
897
898 if (block_requested) {
899 int res;
900
901 res = coap_write_block_b_opt(session, &block, block_opt, response,
902 length);
903
904 switch (res) {
905 case -2: /* illegal block (caught above) */
906 response->code = COAP_RESPONSE_CODE(400);
907 goto error;
908 case -1: /* should really not happen */
909 assert(0);
910 /* fall through if assert is a no-op */
911 case -3: /* cannot handle request */
912 response->code = COAP_RESPONSE_CODE(500);
913 goto error;
914 default: /* everything is good */
915 ;
916 }
917 }
918
919 /* add data body */
920 if (request &&
921 !coap_add_data_large_internal(session, request, response, resource,
922 query, maxage, etag, length, data,
923 release_func, app_ptr, request->code)) {
924 response->code = COAP_RESPONSE_CODE(500);
925 goto error_released;
926 }
927
928 return 1;
929
930error:
931 if (release_func)
932 release_func(session, app_ptr);
933error_released:
934#if COAP_ERROR_PHRASE_LENGTH > 0
935 coap_add_data(response,
936 strlen(coap_response_phrase(response->code)),
937 (const unsigned char *)coap_response_phrase(response->code));
938#endif /* COAP_ERROR_PHRASE_LENGTH > 0 */
939 return 0;
940}
941#endif /* ! COAP_SERVER_SUPPORT */
942
943/*
944 * return 1 if there is a future expire time, else 0.
945 * update tim_rem with remaining value if return is 1.
946 */
947int
949 coap_tick_t *tim_rem) {
952 coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND;
953 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
954 int ret = 0;
955
956 *tim_rem = -1;
957
958 LL_FOREACH_SAFE(session->lg_xmit, p, q) {
959 if (p->last_all_sent) {
960 if (p->last_all_sent + idle_timeout <= now) {
961 /* Expire this entry */
962 LL_DELETE(session->lg_xmit, p);
963 coap_block_delete_lg_xmit(session, p);
964 }
965 else {
966 /* Delay until the lg_xmit needs to expire */
967 if (*tim_rem > p->last_all_sent + idle_timeout - now) {
968 *tim_rem = p->last_all_sent + idle_timeout - now;
969 ret = 1;
970 }
971 }
972 }
973 else if (p->last_sent) {
974 if (p->last_sent + partial_timeout <= now) {
975 /* Expire this entry */
976 LL_DELETE(session->lg_xmit, p);
977 coap_block_delete_lg_xmit(session, p);
979 }
980 else {
981 /* Delay until the lg_xmit needs to expire */
982 if (*tim_rem > p->last_sent + partial_timeout - now) {
983 *tim_rem = p->last_sent + partial_timeout - now;
984 ret = 1;
985 }
986 }
987 }
988 }
989 return ret;
990}
991
992#if COAP_CLIENT_SUPPORT
993/*
994 * return 1 if there is a future expire time, else 0.
995 * update tim_rem with remaining value if return is 1.
996 */
997int
999 coap_tick_t *tim_rem) {
1000 coap_lg_crcv_t *p;
1001 coap_lg_crcv_t *q;
1002 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1003 int ret = 0;
1004
1005 *tim_rem = -1;
1006
1007 LL_FOREACH_SAFE(session->lg_crcv, p, q) {
1008 if (!p->observe_set && p->last_used &&
1009 p->last_used + partial_timeout <= now) {
1010 /* Expire this entry */
1011 LL_DELETE(session->lg_crcv, p);
1012 coap_block_delete_lg_crcv(session, p);
1013 }
1014 else if (!p->observe_set && p->last_used) {
1015 /* Delay until the lg_crcv needs to expire */
1016 if (*tim_rem > p->last_used + partial_timeout - now) {
1017 *tim_rem = p->last_used + partial_timeout - now;
1018 ret = 1;
1019 }
1020 }
1021 }
1022 return ret;
1023}
1024#endif /* COAP_CLIENT_SUPPORT */
1025
1026static int
1027check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
1028 uint32_t i;
1029
1030 for (i = 0; i < rec_blocks->used; i++) {
1031 if (block_num < rec_blocks->range[i].begin)
1032 return 0;
1033 if (block_num <= rec_blocks->range[i].end)
1034 return 1;
1035 }
1036 return 0;
1037}
1038
1039static int
1040check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks) {
1041 uint32_t i;
1042 uint32_t block = 0;
1043
1044 for (i = 0; i < rec_blocks->used; i++) {
1045 if (block < rec_blocks->range[i].begin)
1046 return 0;
1047 if (block < rec_blocks->range[i].end)
1048 block = rec_blocks->range[i].end;
1049 }
1050 /* total_blocks counts from 1 */
1051 if (block + 1 < total_blocks)
1052 return 0;
1053
1054 return 1;
1055}
1056
1057#if COAP_SERVER_SUPPORT
1058/*
1059 * return 1 if there is a future expire time, else 0.
1060 * update tim_rem with remaining value if return is 1.
1061 */
1062int
1064 coap_tick_t *tim_rem) {
1065 coap_lg_srcv_t *p;
1066 coap_lg_srcv_t *q;
1067 coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1068 int ret = 0;
1069
1070 *tim_rem = -1;
1071
1072 LL_FOREACH_SAFE(session->lg_srcv, p, q) {
1073 if (p->last_used && p->last_used + partial_timeout <= now) {
1074 /* Expire this entry */
1075 LL_DELETE(session->lg_srcv, p);
1076 coap_block_delete_lg_srcv(session, p);
1077 }
1078 else if (p->last_used) {
1079 /* Delay until the lg_srcv needs to expire */
1080 if (*tim_rem > p->last_used + partial_timeout - now) {
1081 *tim_rem = p->last_used + partial_timeout - now;
1082 ret = 1;
1083 }
1084 }
1085 }
1086 return ret;
1087}
1088#endif /* COAP_SERVER_SUPPORT */
1089
1090#if COAP_CLIENT_SUPPORT
1091static void
1092track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv,
1093 uint32_t block_num, uint8_t *buf, size_t len) {
1094 /* Need to handle Observe for large FETCH */
1095 coap_opt_iterator_t opt_iter;
1097 &opt_iter);
1098
1099 if (opt) {
1100 int observe_action = -1;
1101 coap_binary_t **tmp;
1102
1103 observe_action = coap_decode_var_bytes(coap_opt_value(opt),
1104 coap_opt_length(opt));
1105 if (observe_action == COAP_OBSERVE_ESTABLISH) {
1106 /* Save the token in lg_crcv */
1107 tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token,
1108 (block_num + 1) * sizeof(lg_crcv->obs_token[0]));
1109 if (tmp == NULL)
1110 return;
1111 lg_crcv->obs_token = tmp;
1112 if (block_num + 1 == lg_crcv->obs_token_cnt)
1113 coap_delete_binary(lg_crcv->obs_token[block_num]);
1114
1115 lg_crcv->obs_token_cnt = block_num + 1;
1116 lg_crcv->obs_token[block_num] = coap_new_binary(len);
1117 if (lg_crcv->obs_token[block_num] == NULL)
1118 return;
1119 memcpy(lg_crcv->obs_token[block_num]->s, buf, len);
1120 } else if (observe_action == COAP_OBSERVE_CANCEL) {
1121 /* Use the token in lg_crcv */
1122 if (block_num < lg_crcv->obs_token_cnt) {
1123 if (lg_crcv->obs_token[block_num]) {
1124 memcpy(buf, lg_crcv->obs_token[block_num]->s, len);
1125 }
1126 }
1127 }
1128 }
1129}
1130
1133 coap_lg_xmit_t *lg_xmit) {
1134 coap_lg_crcv_t *lg_crcv;
1135 uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);
1136 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
1137 pdu->used_size;
1138 size_t data_len = lg_xmit ? lg_xmit->length :
1139 pdu->data ?
1140 pdu->used_size - (pdu->data - pdu->token) : 0;
1141
1142 lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
1143
1144 if (lg_crcv == NULL)
1145 return NULL;
1146
1148 "** %s: lg_crcv %p initialized - stateless token xxxx%012llx\n",
1149 coap_session_str(session), (void*)lg_crcv,
1150 STATE_TOKEN_BASE(state_token));
1151 memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
1152 lg_crcv->initial = 1;
1153 coap_ticks(&lg_crcv->last_used);
1154 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
1155 memcpy(&lg_crcv->pdu, pdu, sizeof(lg_crcv->pdu));
1156 /* Make sure that there is space for increased token + option change */
1157 lg_crcv->pdu.max_size = token_options + data_len + 9;
1158 lg_crcv->pdu.used_size = token_options + data_len;
1159 lg_crcv->pdu.token = coap_malloc_type(COAP_PDU_BUF,
1160 token_options + data_len + lg_crcv->pdu.max_hdr_size);
1161 if (!lg_crcv->pdu.token) {
1162 coap_block_delete_lg_crcv(session, lg_crcv);
1163 return NULL;
1164 }
1165 lg_crcv->pdu.token += lg_crcv->pdu.max_hdr_size;
1166 memcpy(lg_crcv->pdu.token, pdu->token, token_options);
1167 if (lg_crcv->pdu.data) {
1168 lg_crcv->pdu.data = lg_crcv->pdu.token + token_options;
1169 memcpy(lg_crcv->pdu.data, lg_xmit ? lg_xmit->data : pdu->data, data_len);
1170 }
1171
1172 /* Need to keep original token for updating response PDUs */
1173 lg_crcv->app_token = coap_new_binary(pdu->token_length);
1174 if (!lg_crcv->app_token) {
1175 coap_block_delete_lg_crcv(session, lg_crcv);
1176 return NULL;
1177 }
1178 memcpy(lg_crcv->app_token->s, pdu->token, pdu->token_length);
1179
1180 /* Need to set up a base token for actual communications if retries needed */
1181 lg_crcv->retry_counter = 1;
1182 lg_crcv->state_token = state_token;
1183
1184 if (pdu->code == COAP_REQUEST_CODE_FETCH) {
1185 /* Need to handle Observe for large FETCH */
1186 track_fetch_observe(pdu, lg_crcv, 0, pdu->token, pdu->token_length);
1187 }
1188
1189 /* In case it is there - must not be in continuing request PDUs */
1190 coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1);
1191
1192 return lg_crcv;
1193}
1194
1195void
1197 coap_lg_crcv_t *lg_crcv) {
1198 size_t i;
1199
1200 if (lg_crcv == NULL)
1201 return;
1202
1203 if (lg_crcv->pdu.token)
1204 coap_free_type(COAP_PDU_BUF, lg_crcv->pdu.token - lg_crcv->pdu.max_hdr_size);
1206 coap_log_debug("** %s: lg_crcv %p released\n",
1207 coap_session_str(session), (void*)lg_crcv);
1208 coap_delete_binary(lg_crcv->app_token);
1209 for (i = 0; i < lg_crcv->obs_token_cnt; i++) {
1210 coap_delete_binary(lg_crcv->obs_token[i]);
1211 }
1213 coap_free_type(COAP_LG_CRCV, lg_crcv);
1214}
1215#endif /* COAP_CLIENT_SUPPORT */
1216
1217#if COAP_SERVER_SUPPORT
1218void
1220 coap_lg_srcv_t *lg_srcv) {
1221 if (lg_srcv == NULL)
1222 return;
1223
1226 coap_log_debug("** %s: lg_srcv %p released\n",
1227 coap_session_str(session), (void*)lg_srcv);
1228 coap_free_type(COAP_LG_SRCV, lg_srcv);
1229}
1230#endif /* COAP_SERVER_SUPPORT */
1231
1232void
1234 coap_lg_xmit_t *lg_xmit) {
1235 if (lg_xmit == NULL)
1236 return;
1237
1238 if (lg_xmit->release_func) {
1239 lg_xmit->release_func(session, lg_xmit->app_ptr);
1240 }
1241 if (lg_xmit->pdu.token) {
1242 coap_free_type(COAP_PDU_BUF, lg_xmit->pdu.token - lg_xmit->pdu.max_hdr_size);
1243 }
1244 if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu))
1245 coap_delete_binary(lg_xmit->b.b1.app_token);
1246 else
1247 coap_delete_string(lg_xmit->b.b2.query);
1248
1249 coap_log_debug("** %s: lg_xmit %p released\n",
1250 coap_session_str(session), (void*)lg_xmit);
1251 coap_free_type(COAP_LG_XMIT, lg_xmit);
1252}
1253
1254#if COAP_SERVER_SUPPORT
1255static int
1256add_block_send(uint32_t num, uint32_t *out_blocks,
1257 uint32_t *count, uint32_t max_count) {
1258 uint32_t i;
1259
1260 for (i = 0; i < *count && *count < max_count; i++) {
1261 if (num == out_blocks[i])
1262 return 0;
1263 else if (num < out_blocks[i]) {
1264 if (*count - i > 1)
1265 memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
1266 out_blocks[i] = num;
1267 (*count)++;
1268 return 1;
1269 }
1270 }
1271 if (*count < max_count) {
1272 out_blocks[i] = num;
1273 (*count)++;
1274 return 1;
1275 }
1276 return 0;
1277}
1278
1279/*
1280 * Need to see if this is a request for the next block of a large body
1281 * transfer. If so, need to initiate the response with the next blocks
1282 * and not trouble the application.
1283 *
1284 * If additional responses needed, then these are expicitly sent out and
1285 * 'response' is updated to be the last response to be sent.
1286 *
1287 * This is set up using coap_add_data_large_response()
1288 *
1289 * Server is sending a large data response to GET / observe (Block2)
1290 *
1291 * Return: 0 Call application handler
1292 * 1 Do not call application handler - just send the built response
1293 */
1294int
1296 coap_pdu_t *pdu,
1297 coap_pdu_t *response,
1298 coap_resource_t *resource,
1299 coap_string_t *query) {
1300 coap_lg_xmit_t *p = NULL;
1301 coap_block_b_t block;
1302 uint16_t block_opt = 0;
1303 uint32_t out_blocks[1];
1304 const char *error_phrase;
1305 coap_opt_iterator_t opt_iter;
1306 size_t chunk;
1307 coap_opt_iterator_t opt_b_iter;
1308 coap_opt_t *option;
1309 uint32_t request_cnt, i;
1310 coap_opt_t *etag_opt = NULL;
1311 coap_pdu_t *out_pdu = response;
1312
1313 if (!coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &block))
1314 return 0;
1315 if (block.num == 0) {
1316 /* Get a fresh copy of the data */
1317 return 0;
1318 }
1319 block_opt = COAP_OPTION_BLOCK2;
1320 p = coap_find_lg_xmit_response(session, pdu, resource, query);
1321 if (p == NULL)
1322 return 0;
1323
1324 /* lg_xmit (response) found */
1325
1326 etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
1327 if (etag_opt) {
1328 uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
1329 coap_opt_length(etag_opt));
1330 if (etag != p->b.b2.etag) {
1331 /* Not a match - pass up to a higher level */
1332 return 0;
1333 }
1334 out_pdu->code = COAP_RESPONSE_CODE(203);
1335 coap_ticks(&p->last_sent);
1336 goto skip_app_handler;
1337 }
1338 else {
1339 out_pdu->code = p->pdu.code;
1340 }
1341 coap_ticks(&p->last_obs);
1342 p->last_all_sent = 0;
1343
1344 chunk = (size_t)1 << (p->blk_size + 4);
1345 if (block_opt) {
1346 if (block.bert) {
1348 "found Block option, block is BERT, block nr. %u, M %d\n",
1349 block.num, block.m);
1350 } else {
1352 "found Block option, block size is %u, block nr. %u, M %d\n",
1353 1 << (block.szx + 4), block.num, block.m);
1354 }
1355 if (block.bert == 0 && block.szx != p->blk_size) {
1356 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
1357 /*
1358 * Recompute the block number of the previous packet given
1359 * the new block size
1360 */
1361 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
1362 p->blk_size = block.szx;
1363 chunk = (size_t)1 << (p->blk_size + 4);
1364 p->offset = block.num * chunk;
1366 "new Block size is %u, block number %u completed\n",
1367 1 << (block.szx + 4), block.num);
1368 } else {
1370 "ignoring request to increase Block size, "
1371 "next block is not aligned on requested block size "
1372 "boundary. (%zu x %u mod %u = %zu (which is not 0)\n",
1373 p->offset/chunk + 1, (1 << (p->blk_size + 4)),
1374 (1 << (block.szx + 4)),
1375 (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
1376 }
1377 }
1378 }
1379
1380 request_cnt = 0;
1381 coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
1382 while ((option = coap_option_next(&opt_b_iter))) {
1383 unsigned int num;
1384 if (opt_b_iter.number != p->option)
1385 continue;
1386 num = coap_opt_block_num(option);
1387 if (num > 0xFFFFF) /* 20 bits max for num */
1388 continue;
1389 if (block.aszx != COAP_OPT_BLOCK_SZX(option)) {
1390 coap_add_data(response,
1391 sizeof("Changing blocksize during request invalid")-1,
1392 (const uint8_t *)"Changing blocksize during request invalid");
1393 response->code = COAP_RESPONSE_CODE(400);
1394 return 1;
1395 }
1396 add_block_send(num, out_blocks, &request_cnt, 1);
1397 break;
1398 }
1399 if (request_cnt == 0) {
1400 /* Block2 not found - give them the first block */
1401 block.szx = p->blk_size;
1402 p->offset = 0;
1403 out_blocks[0] = 0;
1404 request_cnt = 1;
1405 }
1406
1407 for (i = 0; i < request_cnt; i++) {
1408 uint8_t buf[8];
1409
1410 block.num = out_blocks[i];
1411 p->offset = block.num * chunk;
1412
1413 if (i + 1 < request_cnt) {
1414 /* Need to set up a copy of the pdu to send */
1415 coap_opt_filter_t drop_options;
1416
1417 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1418 if (block.num != 0)
1420 out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->token_length,
1421 pdu->token, &drop_options);
1422 if (!out_pdu) {
1423 goto internal_issue;
1424 }
1425 }
1426 else {
1427 /*
1428 * Copy the options across and then fix the block option
1429 *
1430 * Need to drop Observe option if Block2 and block.num != 0
1431 */
1433 while ((option = coap_option_next(&opt_iter))) {
1434 if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0)
1435 continue;
1436 if (!coap_insert_option(response, opt_iter.number,
1437 coap_opt_length(option),
1438 coap_opt_value(option))) {
1439 goto internal_issue;
1440 }
1441 }
1442 out_pdu = response;
1443 }
1444 if (pdu->type == COAP_MESSAGE_NON)
1445 out_pdu->type = COAP_MESSAGE_NON;
1446 if (block.bert) {
1447 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
1448 block.m = (p->length - p->offset) >
1449 ((out_pdu->max_size - token_options) /1024) * 1024;
1450 } else {
1451 block.m = (p->offset + chunk) < p->length;
1452 }
1453 if (!coap_update_option(out_pdu, p->option,
1455 sizeof(buf),
1456 (block.num << 4) |
1457 (block.m << 3) |
1458 block.aszx),
1459 buf)) {
1460 goto internal_issue;
1461 }
1462 if (!(p->offset + chunk < p->length)) {
1463 /* Last block - keep in cache for 4 * ACK_TIMOUT */
1465 }
1466 if (p->b.b2.maxage_expire) {
1467 coap_tick_t now;
1468 coap_time_t rem;
1469
1470 coap_ticks(&now);
1471 rem = coap_ticks_to_rt(now);
1472 if (p->b.b2.maxage_expire > rem) {
1473 rem = p->b.b2.maxage_expire - rem;
1474 }
1475 if (!(p->offset + chunk < p->length)) {
1476 /* Last block - keep in cache for 4 * ACK_TIMOUT */
1478 }
1479 if (p->b.b2.maxage_expire) {
1480 coap_ticks(&now);
1481 rem = coap_ticks_to_rt(now);
1482 if (p->b.b2.maxage_expire > rem) {
1483 rem = p->b.b2.maxage_expire - rem;
1484 }
1485 else {
1486 rem = 0;
1487 /* Entry needs to be expired */
1489 }
1492 sizeof(buf),
1493 rem),
1494 buf)) {
1495 goto internal_issue;
1496 }
1497 }
1498 }
1499
1500 if (!etag_opt && !coap_add_block_b_data(out_pdu,
1501 p->length,
1502 p->data,
1503 &block)) {
1504 goto internal_issue;
1505 }
1506 if (i + 1 < request_cnt) {
1507 coap_ticks(&p->last_sent);
1508 coap_send_internal(session, out_pdu);
1509 }
1510 }
1512 goto skip_app_handler;
1513
1514internal_issue:
1515 response->code = COAP_RESPONSE_CODE(500);
1516 error_phrase = coap_response_phrase(response->code);
1517 coap_add_data(response, strlen(error_phrase),
1518 (const uint8_t *)error_phrase);
1519 /* Keep in cache for 4 * ACK_TIMOUT incase of retry */
1520 if (p)
1522
1523skip_app_handler:
1524 return 1;
1525}
1526#endif /* COAP_SERVER_SUPPORT */
1527
1528static int
1529update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num) {
1530 uint32_t i;
1531
1532 /* Reset as there is activity */
1533 rec_blocks->retry = 0;
1534
1535 for (i = 0; i < rec_blocks->used; i++) {
1536 if (block_num >= rec_blocks->range[i].begin &&
1537 block_num <= rec_blocks->range[i].end)
1538 break;
1539
1540 if (block_num < rec_blocks->range[i].begin) {
1541 if (block_num + 1 == rec_blocks->range[i].begin) {
1542 rec_blocks->range[i].begin = block_num;
1543 }
1544 else {
1545 /* Need to insert a new range */
1546 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
1547 /* Too many losses */
1548 return 0;
1549 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
1550 (rec_blocks->used - i) * sizeof (rec_blocks->range[0]));
1551 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
1552 rec_blocks->used++;
1553 }
1554 break;
1555 }
1556 if (block_num == rec_blocks->range[i].end + 1) {
1557 rec_blocks->range[i].end = block_num;
1558 if (i + 1 < rec_blocks->used) {
1559 if (rec_blocks->range[i+1].begin == block_num + 1) {
1560 /* Merge the 2 ranges */
1561 rec_blocks->range[i].end = rec_blocks->range[i+1].end;
1562 if (i+2 < rec_blocks->used) {
1563 memmove (&rec_blocks->range[i+1], &rec_blocks->range[i+2],
1564 (rec_blocks->used - (i+2)) * sizeof (rec_blocks->range[0]));
1565 }
1566 rec_blocks->used--;
1567 }
1568 }
1569 break;
1570 }
1571 }
1572 if (i == rec_blocks->used) {
1573 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
1574 /* Too many losses */
1575 return 0;
1576 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
1577 rec_blocks->used++;
1578 }
1579 coap_ticks(&rec_blocks->last_seen);
1580 return 1;
1581}
1582
1583#if COAP_SERVER_SUPPORT
1584/*
1585 * Need to check if this is a large PUT / POST using multiple blocks
1586 *
1587 * Server receiving PUT/POST etc. of a large amount of data (Block1)
1588 *
1589 * Return: 0 Call application handler
1590 * 1 Do not call application handler - just send the built response
1591 */
1592int
1594 coap_session_t *session,
1595 coap_pdu_t *pdu,
1596 coap_pdu_t *response,
1597 coap_resource_t *resource,
1598 coap_string_t *uri_path,
1599 coap_opt_t *observe,
1600 int *added_block,
1601 coap_lg_srcv_t **pfree_lg_srcv) {
1602 size_t length = 0;
1603 const uint8_t *data = NULL;
1604 size_t offset = 0;
1605 size_t total = 0;
1606 coap_block_b_t block;
1607 coap_opt_iterator_t opt_iter;
1608 uint16_t block_option = 0;
1609
1610 *added_block = 0;
1611 *pfree_lg_srcv = NULL;
1612 coap_get_data_large(pdu, &length, &data, &offset, &total);
1613 pdu->body_offset = 0;
1614 pdu->body_total = length;
1615
1616 if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
1617 block_option = COAP_OPTION_BLOCK1;
1618 }
1619 if (block_option) {
1620 coap_lg_srcv_t *p;
1621 coap_opt_t *size_opt = coap_check_option(pdu,
1623 &opt_iter);
1624 coap_opt_t *fmt_opt = coap_check_option(pdu,
1626 &opt_iter);
1627 uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
1628 coap_opt_length(fmt_opt)) :
1630 coap_opt_t *rtag_opt = coap_check_option(pdu,
1632 &opt_iter);
1633 size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
1634 const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
1635
1636 total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
1637 coap_opt_length(size_opt)) : 0;
1638 offset = block.num << (block.szx + 4);
1639
1640 LL_FOREACH(session->lg_srcv, p) {
1641 if (rtag_opt || p->rtag_set == 1) {
1642 if (!(rtag_opt && p->rtag_set == 1))
1643 continue;
1644 if (p->rtag_length != rtag_length ||
1645 memcmp(p->rtag, rtag, rtag_length) != 0)
1646 continue;
1647 }
1648 if (resource == p->resource) {
1649 break;
1650 }
1651 if ((p->resource == context->unknown_resource ||
1652 resource == context->proxy_uri_resource) &&
1653 coap_string_equal(uri_path, p->uri_path))
1654 break;
1655 }
1656 if (!p && block.num != 0) {
1657 /* random access - no need to track */
1658 pdu->body_data = data;
1659 pdu->body_length = length;
1660 pdu->body_offset = offset;
1661 pdu->body_total = length + offset + (block.m ? 1 : 0);
1662 }
1663 /* Do not do this if this is a single block */
1664 else if (!p && !(offset == 0 && block.m == 0)) {
1666 if (p == NULL) {
1667 coap_add_data(response, sizeof("Memory issue")-1,
1668 (const uint8_t *)"Memory issue");
1669 response->code = COAP_RESPONSE_CODE(500);
1670 goto skip_app_handler;
1671 }
1672 coap_log_debug("** %s: lg_srcv %p initialized\n",
1673 coap_session_str(session), (void*)p);
1674 memset(p, 0, sizeof(coap_lg_srcv_t));
1675 coap_ticks(&p->last_used);
1676 p->resource = resource;
1677 if (resource == context->unknown_resource ||
1678 resource == context->proxy_uri_resource)
1679 p->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
1680 p->content_format = fmt;
1681 p->total_len = total;
1682 p->amount_so_far = length;
1683 p->szx = block.szx;
1684 p->block_option = block_option;
1685 if (observe) {
1686 p->observe_length = min(coap_opt_length(observe), 3);
1687 memcpy(p->observe, coap_opt_value(observe), p->observe_length);
1688 p->observe_set = 1;
1689 }
1690 if (rtag_opt) {
1691 p->rtag_length = (uint8_t)rtag_length;
1692 memcpy(p->rtag, rtag, rtag_length);
1693 p->rtag_set = 1;
1694 }
1695 p->body_data = NULL;
1696 LL_PREPEND(session->lg_srcv, p);
1697 }
1698 if (p) {
1699 if (fmt != p->content_format) {
1700 coap_add_data(response, sizeof("Content-Format mismatch")-1,
1701 (const uint8_t *)"Content-Format mismatch");
1702 response->code = COAP_RESPONSE_CODE(408);
1703 goto free_lg_srcv;
1704 }
1705 p->last_mid = pdu->mid;
1706 p->last_type = pdu->type;
1707 memcpy(p->last_token, pdu->token, pdu->token_length);
1709 if ((session->block_mode & COAP_BLOCK_SINGLE_BODY) || block.bert) {
1710 size_t chunk = (size_t)1 << (block.szx + 4);
1711 int update_data = 0;
1712 unsigned int saved_num = block.num;
1713 size_t saved_offset = offset;
1714
1715 while (offset < saved_offset + length) {
1716 if (!check_if_received_block(&p->rec_blocks, block.num)) {
1717 /* Update list of blocks received */
1718 if (!update_received_blocks(&p->rec_blocks, block.num)) {
1720 coap_add_data(response, sizeof("Too many missing blocks")-1,
1721 (const uint8_t *)"Too many missing blocks");
1722 response->code = COAP_RESPONSE_CODE(408);
1723 goto free_lg_srcv;
1724 }
1725 update_data = 1;
1726 }
1727 block.num++;
1728 offset = block.num << (block.szx + 4);
1729 }
1730 block.num--;
1731 if (update_data) {
1732 /* Update saved data */
1733 p->body_data = coap_block_build_body(p->body_data, length, data,
1734 saved_offset, p->total_len);
1735 if (!p->body_data)
1736 goto call_app_handler;
1737
1738 }
1739 if (block.m ||
1741 (uint32_t)(p->total_len + chunk -1)/chunk)) {
1742 /* Not all the payloads of the body have arrived */
1743 if (block.m) {
1744 uint8_t buf[4];
1745
1746 /* Ask for the next block */
1747 coap_insert_option(response, block_option,
1748 coap_encode_var_safe(buf, sizeof(buf),
1749 (saved_num << 4) |
1750 (block.m << 3) |
1751 block.aszx),
1752 buf);
1753 response->code = COAP_RESPONSE_CODE(231);
1754 goto skip_app_handler;
1755 }
1756 goto skip_app_handler;
1757 }
1758
1759 /*
1760 * Remove the Block1 option as passing all of the data to
1761 * application layer. Add back in observe option if appropriate.
1762 * Adjust all other information.
1763 */
1764 if (p->observe_set) {
1766 p->observe_length, p->observe);
1767 }
1768 coap_remove_option(pdu, block_option);
1769 pdu->body_data = p->body_data->s;
1770 pdu->body_length = p->total_len;
1771 pdu->body_offset = 0;
1772 pdu->body_total = p->total_len;
1773 coap_log_debug("Server app version of updated PDU\n");
1775 *pfree_lg_srcv = p;
1776 goto call_app_handler;
1777 }
1778 else {
1779 /* No need to update body_data and body_length as a single PDU */
1780 pdu->body_offset = offset;
1781 /* Exact match if last block */
1782 if (block.m) {
1783 uint8_t buf[4];
1784
1785 if (total > offset + length + block.m)
1786 pdu->body_total = total;
1787 else
1788 pdu->body_total = offset + length + block.m;
1789
1790 coap_insert_option(response, block_option,
1791 coap_encode_var_safe(buf, sizeof(buf),
1792 (block.num << 4) |
1793 (block.m << 3) |
1794 block.aszx),
1795 buf);
1796 *added_block = 1;
1797 goto call_app_handler;
1798 }
1799 else {
1800 pdu->body_total = offset + length + block.m;
1801 }
1802 }
1803
1804 if (block.m == 0) {
1805 /* Last chunk - free off all */
1806 coap_ticks(&p->last_used);
1807 }
1808 goto call_app_handler;
1809
1810free_lg_srcv:
1811 LL_DELETE(session->lg_srcv, p);
1812 coap_block_delete_lg_srcv(session, p);
1813 goto skip_app_handler;
1814 }
1815 }
1816call_app_handler:
1817 return 0;
1818
1819skip_app_handler:
1820 return 1;
1821}
1822#endif /* COAP_SERVER_SUPPORT */
1823
1824#if COAP_CLIENT_SUPPORT
1825static int
1826check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
1827 coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv)
1828{
1829 /* Check for Echo option for freshness */
1830 coap_opt_iterator_t opt_iter;
1831 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
1832
1833 if (opt) {
1834 if (sent || lg_xmit || lg_crcv) {
1835 /* Need to retransmit original request with Echo option added */
1836 coap_pdu_t *echo_pdu;
1837 coap_mid_t mid;
1838 const uint8_t *data;
1839 size_t data_len;
1840 int have_data = 0;
1841 uint8_t ltoken[8];
1842 size_t ltoken_len;
1843 uint64_t token;
1844
1845 if (sent) {
1846 if (coap_get_data(sent, &data_len, &data))
1847 have_data = 1;
1848 } else if (lg_xmit) {
1849 sent = &lg_xmit->pdu;
1850 if (lg_xmit->length) {
1851 size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
1852 size_t offset = (lg_xmit->last_block + 1) * blk_size;
1853 have_data = 1;
1854 data = &lg_xmit->data[offset];
1855 data_len = (lg_xmit->length - offset) > blk_size ? blk_size :
1856 lg_xmit->length - offset;
1857 }
1858 } else /* lg_crcv */ {
1859 sent = &lg_crcv->pdu;
1860 if (coap_get_data(sent, &data_len, &data))
1861 have_data = 1;
1862 }
1863 if (lg_xmit) {
1864 token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
1865 ++lg_xmit->b.b1.count);
1866 } else {
1867 token = STATE_TOKEN_FULL(lg_crcv->state_token,
1868 ++lg_crcv->retry_counter);
1869 }
1870 ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
1871 echo_pdu = coap_pdu_duplicate(sent, session, ltoken_len, ltoken, NULL);
1872 if (!echo_pdu)
1873 return 0;
1874 if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
1875 coap_opt_length(opt), coap_opt_value(opt)))
1876 goto not_sent;
1877 if (have_data) {
1878 coap_add_data(echo_pdu, data_len, data);
1879 }
1880
1881 mid = coap_send_internal(session, echo_pdu);
1882 if (mid == COAP_INVALID_MID)
1883 goto not_sent;
1884 return 1;
1885 } else {
1886 /* Need to save Echo option value to add to next reansmission */
1887not_sent:
1888 coap_delete_bin_const(session->echo);
1889 session->echo = coap_new_bin_const(coap_opt_value(opt),
1890 coap_opt_length(opt));
1891 }
1892 }
1893 return 0;
1894}
1895
1896static void
1897track_echo(coap_session_t *session, coap_pdu_t *rcvd)
1898{
1899 coap_opt_iterator_t opt_iter;
1900 coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
1901
1902 if (opt) {
1903 coap_delete_bin_const(session->echo);
1904 session->echo = coap_new_bin_const(coap_opt_value(opt),
1905 coap_opt_length(opt));
1906 }
1907}
1908
1909/*
1910 * Need to see if this is a response to a large body request transfer. If so,
1911 * need to initiate the request containing the next block and not trouble the
1912 * application. Note that Token must unique per request/response.
1913 *
1914 * Client receives large data acknowledgement from server (Block1)
1915 *
1916 * This is set up using coap_add_data_large_request()
1917 *
1918 * Client is using GET etc.
1919 *
1920 * Return: 0 Call application handler
1921 * 1 Do not call application handler - just send the built response
1922 */
1923int
1925 coap_pdu_t *rcvd) {
1926 coap_lg_xmit_t *p;
1927 coap_lg_xmit_t *q;
1929 rcvd->token_length));
1930 coap_lg_crcv_t *lg_crcv = NULL;
1931
1932 LL_FOREACH_SAFE(session->lg_xmit, p, q) {
1933 if (!COAP_PDU_IS_REQUEST(&p->pdu) ||
1935 token_match !=
1937 p->b.b1.app_token->length)))) {
1938 /* try out the next one */
1939 continue;
1940 }
1941 /* lg_xmit found */
1942 size_t chunk = (size_t)1 << (p->blk_size + 4);
1943 coap_block_b_t block;
1944
1945 if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
1946 coap_get_block_b(session, rcvd, p->option, &block)) {
1947
1948 if (block.bert) {
1950 "found Block option, block is BERT, block nr. %u (%zu)\n",
1951 block.num, p->b.b1.bert_size);
1952 } else {
1954 "found Block option, block size is %u, block nr. %u\n",
1955 1 << (block.szx + 4), block.num);
1956 }
1957 if (block.szx != p->blk_size) {
1958 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
1959 /*
1960 * Recompute the block number of the previous packet given the
1961 * new block size
1962 */
1963 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
1964 p->blk_size = block.szx;
1965 chunk = (size_t)1 << (p->blk_size + 4);
1966 p->offset = block.num * chunk;
1968 "new Block size is %u, block number %u completed\n",
1969 1 << (block.szx + 4), block.num);
1970 block.bert = 0;
1971 block.aszx = block.szx;
1972 } else {
1973 coap_log_debug("ignoring request to increase Block size, "
1974 "next block is not aligned on requested block size boundary. "
1975 "(%zu x %u mod %u = %zu != 0)\n",
1976 p->offset/chunk + 1, (1 << (p->blk_size + 4)),
1977 (1 << (block.szx + 4)),
1978 (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
1979 }
1980 }
1981 track_echo(session, rcvd);
1982 if (p->last_block == (int)block.num) {
1983 /*
1984 * Duplicate Block1 ACK
1985 *
1986 * RFCs not clear here, but on a lossy connection, there could
1987 * be multiple Block1 ACKs, causing the client to retransmit the
1988 * same block multiple times, or the server retransmitting the
1989 * same ACK.
1990 *
1991 * Once a block has been ACKd, there is no need to retransmit it.
1992 */
1993 return 1;
1994 }
1995 if (block.bert)
1996 block.num += (unsigned int)(p->b.b1.bert_size / 1024 - 1);
1997 p->last_block = block.num;
1998 p->offset = (block.num + 1) * chunk;
1999 if (p->offset < p->length) {
2000 /* Build the next PDU request based off the skeletal PDU */
2001 uint8_t buf[8];
2002 coap_pdu_t *pdu;
2003 uint64_t token = STATE_TOKEN_FULL(p->b.b1.state_token, ++p->b.b1.count);
2004 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
2005
2006 if (p->pdu.code == COAP_REQUEST_CODE_FETCH) {
2007 /* Need to handle Observe for large FETCH */
2008 LL_FOREACH(session->lg_crcv, lg_crcv) {
2009 if (coap_binary_equal(p->b.b1.app_token, lg_crcv->app_token)) {
2010 /* In case of observe */
2011 track_fetch_observe(&p->pdu, lg_crcv, block.num + 1,
2012 buf, len);
2013 break;
2014 }
2015 }
2016 }
2017 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
2018 if (!pdu)
2019 goto fail_body;
2020
2021 block.num++;
2022 if (block.bert) {
2023 size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
2024 pdu->used_size;
2025 block.m = (p->length - p->offset) >
2026 ((pdu->max_size - token_options) /1024) * 1024;
2027 } else {
2028 block.m = (p->offset + chunk) < p->length;
2029 }
2030 coap_update_option(pdu, p->option,
2031 coap_encode_var_safe(buf, sizeof(buf),
2032 (block.num << 4) |
2033 (block.m << 3) |
2034 block.aszx),
2035 buf);
2036
2037 if (!coap_add_block_b_data(pdu,
2038 p->length,
2039 p->data,
2040 &block))
2041 goto fail_body;
2042 p->b.b1.bert_size = block.chunk_size;
2043 coap_ticks(&p->last_sent);
2044 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
2045 goto fail_body;
2046 return 1;
2047 }
2048 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
2049 if (check_freshness(session, rcvd, sent, p, NULL))
2050 return 1;
2051 }
2052 goto lg_xmit_finished;
2053 } /* end of LL_FOREACH_SAFE */
2054 return 0;
2055
2056fail_body:
2058 /* There has been an internal error of some sort */
2059 rcvd->code = COAP_RESPONSE_CODE(500);
2060lg_xmit_finished:
2061 if (session->lg_crcv) {
2062 LL_FOREACH(session->lg_crcv, lg_crcv) {
2063 if (STATE_TOKEN_BASE(p->b.b1.state_token) ==
2064 STATE_TOKEN_BASE(lg_crcv->state_token)) {
2065 /* In case of observe */
2066 lg_crcv->state_token = p->b.b1.state_token;
2067 break;
2068 }
2069 }
2070 }
2071 if (!lg_crcv) {
2072 /* need to put back original token into rcvd */
2073 if (p->b.b1.app_token)
2075 p->b.b1.app_token->s);
2076 coap_log_debug("Client app version of updated PDU\n");
2077 coap_show_pdu(LOG_DEBUG, rcvd);
2078 }
2079
2080 LL_DELETE(session->lg_xmit, p);
2081 coap_block_delete_lg_xmit(session, p);
2082 return 0;
2083}
2084#endif /* COAP_CLIENT_SUPPORT */
2085
2086/*
2087 * Re-assemble payloads into a body
2088 */
2090coap_block_build_body(coap_binary_t *body_data, size_t length,
2091 const uint8_t *data, size_t offset, size_t total) {
2092 if (data == NULL)
2093 return NULL;
2094 if (body_data == NULL && total) {
2095 body_data = coap_new_binary(total);
2096 }
2097 if (body_data == NULL)
2098 return NULL;
2099
2100 /* Update saved data */
2101 if (offset + length <= total && body_data->length >= total) {
2102 memcpy(&body_data->s[offset], data, length);
2103 }
2104 else {
2105 /*
2106 * total may be inaccurate as per
2107 * https://tools.ietf.org/html/rfc7959#section-4
2108 * o In a request carrying a Block1 Option, to indicate the current
2109 * estimate the client has of the total size of the resource
2110 * representation, measured in bytes ("size indication").
2111 * o In a response carrying a Block2 Option, to indicate the current
2112 * estimate the server has of the total size of the resource
2113 * representation, measured in bytes ("size indication").
2114 */
2115 coap_binary_t *new = coap_resize_binary(body_data, offset + length);
2116
2117 if (new) {
2118 body_data = new;
2119 memcpy(&body_data->s[offset], data, length);
2120 }
2121 else {
2122 coap_delete_binary(body_data);
2123 return NULL;
2124 }
2125 }
2126 return body_data;
2127}
2128
2129#if COAP_CLIENT_SUPPORT
2130/*
2131 * Need to see if this is a large body response to a request. If so,
2132 * need to initiate the request for the next block and not trouble the
2133 * application. Note that Token must be unique per request/response.
2134 *
2135 * This is set up using coap_send()
2136 * Client receives large data from server (Block2)
2137 *
2138 * Return: 0 Call application handler
2139 * 1 Do not call application handler - just sent the next request
2140 */
2141int
2143 coap_session_t *session,
2144 coap_pdu_t *sent,
2145 coap_pdu_t *rcvd,
2146 coap_recurse_t recursive) {
2147 coap_lg_crcv_t *p;
2148 coap_block_b_t block;
2149 int have_block = 0;
2150 uint16_t block_opt = 0;
2151 size_t offset;
2152 int ack_rst_sent = 0;
2154 rcvd->token_length));
2155
2156 memset(&block, 0, sizeof(block));
2157 LL_FOREACH(session->lg_crcv, p) {
2158 size_t chunk = 0;
2159 uint8_t buf[8];
2160 coap_opt_iterator_t opt_iter;
2161
2163 !full_match(rcvd->token, rcvd->token_length,
2164 p->app_token->s, p->app_token->length)) {
2165 /* try out the next one */
2166 continue;
2167 }
2168
2169 /* lg_crcv found */
2170
2171 if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
2172 size_t length;
2173 const uint8_t *data;
2175 &opt_iter);
2176 size_t size2 = size_opt ?
2178 coap_opt_length(size_opt)) : 0;
2179
2180 /* length and data are cleared on error */
2181 (void)coap_get_data(rcvd, &length, &data);
2182 rcvd->body_offset = 0;
2183 rcvd->body_total = length;
2184 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
2185 have_block = 1;
2186 block_opt = COAP_OPTION_BLOCK2;
2187 }
2188 track_echo(session, rcvd);
2189 if (have_block && (block.m || length)) {
2190 coap_opt_t *fmt_opt = coap_check_option(rcvd,
2192 &opt_iter);
2193 uint16_t fmt = fmt_opt ?
2195 coap_opt_length(fmt_opt)) :
2197 coap_opt_t *etag_opt = coap_check_option(rcvd,
2199 &opt_iter);
2200 size_t saved_offset;
2201 int updated_block;
2202
2203 /* Possibility that Size2 not sent, or is too small */
2204 chunk = (size_t)1 << (block.szx + 4);
2205 offset = block.num * chunk;
2206 if (size2 < (offset + length)) {
2207 if (block.m)
2208 size2 = offset + length + 1;
2209 else
2210 size2 = offset + length;
2211 }
2212 saved_offset = offset;
2213
2214 if (p->initial) {
2215 p->initial = 0;
2216 if (p->body_data) {
2218 p->body_data = NULL;
2219 }
2220 if (etag_opt) {
2221 p->etag_length = coap_opt_length(etag_opt);
2222 memcpy(p->etag, coap_opt_value(etag_opt), p->etag_length);
2223 p->etag_set = 1;
2224 }
2225 else {
2226 p->etag_set = 0;
2227 }
2228 p->total_len = size2;
2229 p->content_format = fmt;
2230 p->szx = block.szx;
2231 p->block_option = block_opt;
2232 p->last_type = rcvd->type;
2233 p->rec_blocks.used = 0;
2234 }
2235 if (p->total_len < size2)
2236 p->total_len = size2;
2237
2238 if (etag_opt) {
2239 if (!full_match(coap_opt_value(etag_opt),
2240 coap_opt_length(etag_opt),
2241 p->etag, p->etag_length)) {
2242 /* body of data has changed - need to restart request */
2243 coap_pdu_t *pdu;
2244 uint64_t token = STATE_TOKEN_FULL(p->state_token,
2245 ++p->retry_counter);
2246 size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
2247 coap_opt_filter_t drop_options;
2248
2250 "Data body updated during receipt - new request started\n");
2251 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
2253
2254 p->initial = 1;
2256 p->body_data = NULL;
2257
2258 coap_session_new_token(session, &len, buf);
2259 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
2261 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, &drop_options);
2262 if (!pdu)
2263 goto fail_resp;
2264
2265 coap_update_option(pdu, block_opt,
2266 coap_encode_var_safe(buf, sizeof(buf),
2267 (0 << 4) | (0 << 3) | block.aszx),
2268 buf);
2269
2270 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
2271 goto fail_resp;
2272
2273 goto skip_app_handler;
2274 }
2275 }
2276 else if (p->etag_set) {
2277 /* Cannot handle this change in ETag to not being there */
2278 coap_log_warn("Not all blocks have ETag option\n");
2279 goto fail_resp;
2280 }
2281
2282 if (fmt != p->content_format) {
2283 coap_log_warn("Content-Format option mismatch\n");
2284 goto fail_resp;
2285 }
2286 if (block.num == 0) {
2287 coap_opt_t *obs_opt = coap_check_option(rcvd,
2289 &opt_iter);
2290 if (obs_opt) {
2291 p->observe_length = min(coap_opt_length(obs_opt), 3);
2292 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
2293 p->observe_set = 1;
2294 }
2295 else {
2296 p->observe_set = 0;
2297 }
2298 }
2299 updated_block = 0;
2300 while (offset < saved_offset + length) {
2301 if (!check_if_received_block(&p->rec_blocks, block.num)) {
2302 /* Update list of blocks received */
2303 if (!update_received_blocks(&p->rec_blocks, block.num)) {
2305 goto fail_resp;
2306 }
2307 updated_block = 1;
2308 }
2309 block.num++;
2310 offset = block.num << (block.szx + 4);
2311 if (!block.bert)
2312 break;
2313 }
2314 block.num--;
2315 /* Only process if not duplicate block */
2316 if (updated_block) {
2317 if ((session->block_mode & COAP_BLOCK_SINGLE_BODY) || block.bert) {
2318 p->body_data = coap_block_build_body(p->body_data, length, data,
2319 saved_offset, size2);
2320 if (p->body_data == NULL) {
2321 goto fail_resp;
2322 }
2323 }
2324 if (block.m || !check_all_blocks_in(&p->rec_blocks,
2325 (size2 + chunk -1) / chunk)) {
2326 /* Not all the payloads of the body have arrived */
2327 size_t len;
2328 coap_pdu_t *pdu;
2329 uint64_t token;
2330
2331 if (block.m) {
2332 block.m = 0;
2333
2334 /* Ask for the next block */
2335 token = STATE_TOKEN_FULL(p->state_token, ++p->retry_counter);
2336 len = coap_encode_var_safe8(buf, sizeof(token), token);
2337 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
2338 if (!pdu)
2339 goto fail_resp;
2340
2341 if (rcvd->type == COAP_MESSAGE_NON)
2342 pdu->type = COAP_MESSAGE_NON; /* Server is using NON */
2343
2344 /* Only sent with the first block */
2346
2347 coap_update_option(pdu, block_opt,
2348 coap_encode_var_safe(buf, sizeof(buf),
2349 ((block.num + 1) << 4) |
2350 (block.m << 3) | block.aszx),
2351 buf);
2352
2353 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
2354 goto fail_resp;
2355 }
2356 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY) || block.bert)
2357 goto skip_app_handler;
2358
2359 /* need to put back original token into rcvd */
2361 rcvd->body_offset = saved_offset;
2362 rcvd->body_total = size2;
2363 goto call_app_handler;
2364 }
2365 /* need to put back original token into rcvd */
2367 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY) || block.bert) {
2368 /* Pretend that there is no block */
2369 coap_remove_option(rcvd, block_opt);
2370 if (p->observe_set) {
2372 p->observe_length, p->observe);
2373 }
2374 rcvd->body_data = p->body_data->s;
2375 rcvd->body_length = saved_offset + length;
2376 rcvd->body_offset = 0;
2377 rcvd->body_total = rcvd->body_length;
2378 }
2379 else {
2380 rcvd->body_offset = saved_offset;
2381 rcvd->body_total = size2;
2382 }
2383 if (context->response_handler) {
2384 if (block.m != 0 || block.num != 0) {
2385 coap_log_debug("Client app version of updated PDU\n");
2386 coap_show_pdu(LOG_DEBUG, rcvd);
2387 }
2388 if (context->response_handler(session, sent, rcvd,
2389 rcvd->mid) == COAP_RESPONSE_FAIL)
2390 coap_send_rst(session, rcvd);
2391 else
2392 coap_send_ack(session, rcvd);
2393 }
2394 else {
2395 coap_send_ack(session, rcvd);
2396 }
2397 ack_rst_sent = 1;
2398 if (p->observe_set == 0) {
2399 /* Expire this entry */
2400 LL_DELETE(session->lg_crcv, p);
2401 coap_block_delete_lg_crcv(session, p);
2402 goto skip_app_handler;
2403 }
2404 /* Set up for the next data body as observing */
2405 p->initial = 1;
2406 if (p->body_data) {
2408 p->body_data = NULL;
2409 }
2410 }
2411 coap_ticks(&p->last_used);
2412 goto skip_app_handler;
2413 } else {
2414 coap_opt_t *obs_opt = coap_check_option(rcvd,
2416 &opt_iter);
2417 if (obs_opt) {
2418 p->observe_length = min(coap_opt_length(obs_opt), 3);
2419 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
2420 p->observe_set = 1;
2421 } else {
2422 p->observe_set = 0;
2423 /* Expire this entry */
2424 goto expire_lg_crcv;
2425 }
2426 }
2427 coap_ticks(&p->last_used);
2428 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
2429 if (check_freshness(session, rcvd, sent, NULL, p))
2430 goto skip_app_handler;
2431 goto expire_lg_crcv;
2432 } else {
2433 /* Not 2.xx or 4.01 - assume it is a failure of some sort */
2434 goto expire_lg_crcv;
2435 }
2436 if (!block.m && !p->observe_set) {
2437fail_resp:
2438 /* lg_crcv no longer required - cache it for 1 sec */
2439 coap_ticks(&p->last_used);
2442 }
2443 /* need to put back original token into rcvd */
2444 if (!full_match(rcvd->token, rcvd->token_length,
2445 p->app_token->s, p->app_token->length)) {
2447 coap_log_debug("Client app version of updated PDU\n");
2448 coap_show_pdu(LOG_DEBUG, rcvd);
2449 }
2450 break;
2451 } /* LL_FOREACH() */
2452
2453 /* Check if receiving a block response and if blocks can be set up */
2454 if (recursive == COAP_RECURSE_OK && !p) {
2455 if (!sent) {
2456 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
2457 coap_log_debug("** %s: large body receive internal issue\n",
2458 coap_session_str(session));
2459 goto skip_app_handler;
2460 }
2461 }
2462 else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
2463 if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
2464 have_block = 1;
2465 block_opt = COAP_OPTION_BLOCK2;
2466 if (block.num != 0) {
2467 /* Assume random access and just give the single response to app */
2468 size_t length;
2469 const uint8_t *data;
2470 size_t chunk = (size_t)1 << (block.szx + 4);
2471
2472 coap_get_data(rcvd, &length, &data);
2473 rcvd->body_offset = block.num*chunk;
2474 rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
2475 goto call_app_handler;
2476 }
2477 }
2478 if (have_block) {
2479 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
2480
2481 if (lg_crcv) {
2482 LL_PREPEND(session->lg_crcv, lg_crcv);
2483 return coap_handle_response_get_block(context, session, sent, rcvd,
2485 }
2486 }
2487 track_echo(session, rcvd);
2488 } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
2489 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
2490
2491 if (lg_crcv) {
2492 LL_PREPEND(session->lg_crcv, lg_crcv);
2493 return coap_handle_response_get_block(context, session, sent, rcvd,
2495 }
2496 }
2497 }
2498 return 0;
2499
2500expire_lg_crcv:
2501 /* need to put back original token into rcvd */
2502 if (!full_match(rcvd->token, rcvd->token_length,
2503 p->app_token->s, p->app_token->length)) {
2505 coap_log_debug("Client app version of updated PDU\n");
2506 coap_show_pdu(LOG_DEBUG, rcvd);
2507 }
2508 /* Expire this entry */
2509 LL_DELETE(session->lg_crcv, p);
2510 coap_block_delete_lg_crcv(session, p);
2511
2512call_app_handler:
2513 return 0;
2514
2515skip_app_handler:
2516 if (!ack_rst_sent)
2517 coap_send_ack(session, rcvd);
2518 return 1;
2519}
2520#endif /* COAP_CLIENT_SUPPORT */
2521
2522#if COAP_SERVER_SUPPORT
2523/* Check if lg_xmit generated and update PDU code if so */
2524void
2526 const coap_pdu_t *request,
2527 coap_pdu_t *response, const coap_resource_t *resource,
2528 const coap_string_t *query) {
2529 coap_lg_xmit_t *lg_xmit;
2530
2531 if (response->code == 0)
2532 return;
2533 lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
2534 if (lg_xmit && lg_xmit->pdu.code == 0) {
2535 lg_xmit->pdu.code = response->code;
2536 return;
2537 }
2538}
2539#endif /* COAP_SERVER_SUPPORT */
2540
2541#if COAP_CLIENT_SUPPORT
2542void
2545 pdu->token_length));
2546 coap_lg_xmit_t *lg_xmit;
2547 coap_lg_crcv_t *lg_crcv;
2548
2549 if (session->lg_crcv) {
2550 LL_FOREACH(session->lg_crcv, lg_crcv) {
2551 if (full_match(pdu->token, pdu->token_length, lg_crcv->app_token->s,
2552 lg_crcv->app_token->length))
2553 return;
2554 if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) {
2555 coap_update_token(pdu, lg_crcv->app_token->length,
2556 lg_crcv->app_token->s);
2557 coap_log_debug("Client app version of updated PDU\n");
2559 return;
2560 }
2561 }
2562 }
2563 if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) {
2564 LL_FOREACH(session->lg_xmit, lg_xmit) {
2565 if (full_match(pdu->token, pdu->token_length, lg_xmit->b.b1.app_token->s,
2566 lg_xmit->b.b1.app_token->length))
2567 return;
2568 if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) {
2569 coap_update_token(pdu, lg_xmit->b.b1.app_token->length,
2570 lg_xmit->b.b1.app_token->s);
2571 coap_log_debug("Client app version of updated PDU\n");
2573 return;
2574 }
2575 }
2576 }
2577}
2578#endif /* ! COAP_CLIENT_SUPPORT */
COAP_STATIC_INLINE int full_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition: block.c:374
#define MAX_BLK_LEN
#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:1040
#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:1529
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 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, void *app_ptr, coap_pdu_code_t request_method)
Definition: block.c:492
static int check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num)
Definition: block.c:1027
unsigned char coap_key_t[4]
#define coap_hash(String, Length, Result)
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)
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()...
int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, coap_pdu_t *rcvd)
void coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu)
The function checks if the token needs to be updated before PDU is presented to the application (only...
coap_recurse_t
void coap_block_delete_lg_xmit(coap_session_t *session, coap_lg_xmit_t *lg_xmit)
Definition: block.c:1233
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, int *added_block, coap_lg_srcv_t **free_lg_srcv)
coap_lg_xmit_t * coap_find_lg_xmit_response(const coap_session_t *session, const coap_pdu_t *request, const coap_resource_t *resource, const coap_string_t *query)
coap_lg_crcv_t * coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu, coap_lg_xmit_t *lg_xmit)
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_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem)
Definition: block.c:948
@ 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:78
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:277
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:74
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
#define COAP_BLOCK_NO_PREEMPTIVE_RTAG
Definition: block.h:63
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:70
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:2090
#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
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:1037
coap_mid_t coap_send_internal(coap_session_t *session, coap_pdu_t *pdu)
Sends a CoAP message to given peer.
Definition: net.c:1232
coap_mid_t coap_send_ack(coap_session_t *session, const coap_pdu_t *request)
Sends an ACK message with code 0 for the specified request to dst.
Definition: net.c:774
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:3390
COAP_STATIC_INLINE coap_mid_t coap_send_rst(coap_session_t *session, const coap_pdu_t *request)
Sends an RST message with code 0 for the specified request to dst.
Definition: net.h:480
@ COAP_RESPONSE_FAIL
Response not liked - send CoAP RST packet.
Definition: net.h:47
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
#define coap_log_debug(...)
Definition: coap_debug.h:48
void coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu)
Display the contents of the specified pdu.
Definition: coap_debug.c:588
#define LOG_DEBUG
Definition: coap_debug.h:105
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define coap_log_warn(...)
Definition: coap_debug.h:45
#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_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:211
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:350
#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:198
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:248
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:493
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:487
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:343
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:309
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:575
#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:625
#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:798
#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:723
#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:177
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:731
#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:692
@ COAP_REQUEST_CODE_FETCH
Definition: pdu.h:310
@ COAP_MESSAGE_NON
Definition: pdu.h:62
@ COAP_MESSAGE_CON
Definition: pdu.h:61
#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_binary_equal(binary1, binary2)
Compares the two binary data for equality.
Definition: coap_str.h:203
#define coap_string_equal(string1, string2)
Compares the two strings for equality.
Definition: coap_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:53
@ COAP_LG_CRCV
Definition: mem.h:54
@ COAP_LG_SRCV
Definition: mem.h:55
@ COAP_STRING
Definition: mem.h:37
@ COAP_PDU_BUF
Definition: mem.h:45
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().
COAP_STATIC_INLINE int token_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition: net.c:1031
CoAP binary data definition.
Definition: coap_str.h:56
size_t length
length of binary data
Definition: coap_str.h:57
uint8_t * s
binary data
Definition: coap_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.
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.
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
uint8_t initial
If set, has not been used yet.
uint8_t szx
size of individual blocks
coap_binary_t ** obs_token
Tokens used in setting up Observe (to handle large FETCH)
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.
size_t obs_token_cnt
number of tokens used to set up Observe
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:169
coap_option_num_t number
decoded option number
Definition: coap_option.h:171
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
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)
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.
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
CoAP string data definition.
Definition: coap_str.h:38
uint8_t * s
string data
Definition: coap_str.h:40
size_t length
length of string
Definition: coap_str.h:39