libcoap 4.3.0
block.c
Go to the documentation of this file.
1/* block.c -- block transfer
2 *
3 * Copyright (C) 2010--2012,2015-2019 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
11#include "coap3/coap_internal.h"
12
13#ifndef min
14#define min(a,b) ((a) < (b) ? (a) : (b))
15#endif
16
17unsigned int
18coap_opt_block_num(const coap_opt_t *block_opt) {
19 unsigned int num = 0;
20 uint16_t len;
21
22 len = coap_opt_length(block_opt);
23
24 if (len == 0) {
25 return 0;
26 }
27
28 if (len > 1) {
30 coap_opt_length(block_opt) - 1);
31 }
32
33 return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
34}
35
36int
38 coap_block_t *block) {
39 coap_opt_iterator_t opt_iter;
40 coap_opt_t *option;
41
42 assert(block);
43 memset(block, 0, sizeof(coap_block_t));
44
45 if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) {
46 unsigned int num;
47
48 block->szx = COAP_OPT_BLOCK_SZX(option);
49 if (COAP_OPT_BLOCK_MORE(option))
50 block->m = 1;
51
52 /* The block number is at most 20 bits, so values above 2^20 - 1
53 * are illegal. */
54 num = coap_opt_block_num(option);
55 if (num > 0xFFFFF) {
56 return 0;
57 }
58 block->num = num;
59 return 1;
60 }
61
62 return 0;
63}
64
65int
67 coap_pdu_t *pdu, size_t data_length) {
68 size_t start, want, avail;
69 unsigned char buf[4];
70
71 assert(pdu);
72
73 start = block->num << (block->szx + 4);
74 if (block->num != 0 && data_length <= start) {
75 coap_log(LOG_DEBUG, "illegal block requested\n");
76 return -2;
77 }
78
79 assert(pdu->max_size > 0);
80 avail = pdu->max_size - pdu->used_size - pdu->hdr_size;
81 want = (size_t)1 << (block->szx + 4);
82
83 /* check if entire block fits in message */
84 if (want <= avail) {
85 block->m = want < data_length - start;
86 } else {
87 /* Sender has requested a block that is larger than the remaining
88 * space in pdu. This is ok if the remaining data fits into the pdu
89 * anyway. The block size needs to be adjusted only if there is more
90 * data left that cannot be delivered in this message. */
91
92 if (data_length - start <= avail) {
93
94 /* it's the final block and everything fits in the message */
95 block->m = 0;
96 } else {
97 unsigned int szx;
98 int newBlockSize;
99
100 /* we need to decrease the block size */
101 if (avail < 16) { /* bad luck, this is the smallest block size */
103 "not enough space, even the smallest block does not fit\n");
104 return -3;
105 }
106 newBlockSize = coap_flsll((long long)avail) - 5;
108 "decrease block size for %zu to %d\n", avail, newBlockSize);
109 szx = block->szx;
110 block->szx = newBlockSize;
111 block->m = 1;
112 block->num <<= szx - block->szx;
113 }
114 }
115
116 /* to re-encode the block option */
117 coap_insert_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
118 ((block->num << 4) |
119 (block->m << 3) |
120 block->szx)),
121 buf);
122
123 return 1;
124}
125
126int
127coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data,
128 unsigned int block_num, unsigned char block_szx) {
129 unsigned int start;
130 start = block_num << (block_szx + 4);
131
132 if (len <= start)
133 return 0;
134
135 return coap_add_data(pdu,
136 min(len - start, ((size_t)1 << (block_szx + 4))),
137 data + start);
138}
139
140/*
141 * Note that the COAP_OPTION_ have to be added in the correct order
142 */
143void
145 coap_pdu_t *response,
146 uint16_t media_type,
147 int maxage,
148 size_t length,
149 const uint8_t* data
150) {
151 coap_key_t etag;
152 unsigned char buf[4];
153 coap_block_t block2 = { 0, 0, 0 };
154 int block2_requested = 0;
155
156 /*
157 * Need to check that a valid block is getting asked for so that the
158 * correct options are put into the PDU.
159 */
160 if (request) {
161 if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
162 block2_requested = 1;
163 if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) {
164 coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
165 block2.num,
166 length >> (block2.szx + 4));
167 response->code = COAP_RESPONSE_CODE(400);
168 goto error;
169 }
170 }
171 }
172 response->code = COAP_RESPONSE_CODE(205);
173
174 /* add etag for the resource */
175 memset(etag, 0, sizeof(etag));
176 coap_hash(data, length, etag);
177 coap_add_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
178
180 coap_encode_var_safe(buf, sizeof(buf),
181 media_type),
182 buf);
183
184 if (maxage >= 0) {
185 coap_insert_option(response,
187 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
188 }
189
190 if (block2_requested) {
191 int res;
192
193 res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
194
195 switch (res) {
196 case -2: /* illegal block (caught above) */
197 response->code = COAP_RESPONSE_CODE(400);
198 goto error;
199 case -1: /* should really not happen */
200 assert(0);
201 /* fall through if assert is a no-op */
202 case -3: /* cannot handle request */
203 response->code = COAP_RESPONSE_CODE(500);
204 goto error;
205 default: /* everything is good */
206 ;
207 }
208
209 coap_add_option(response,
211 coap_encode_var_safe8(buf, sizeof(buf), length),
212 buf);
213
214 coap_add_block(response, length, data,
215 block2.num, block2.szx);
216 return;
217 }
218
219 /*
220 * BLOCK2 not requested
221 */
222 if (!coap_add_data(response, length, data)) {
223 /*
224 * Insufficient space to add in data - use block mode
225 * set initial block size, will be lowered by
226 * coap_write_block_opt() automatically
227 */
228 block2.num = 0;
229 block2.szx = 6;
230 coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
231
232 coap_add_option(response,
234 coap_encode_var_safe8(buf, sizeof(buf), length),
235 buf);
236
237 coap_add_block(response, length, data,
238 block2.num, block2.szx);
239 }
240 return;
241
242error:
243 coap_add_data(response,
244 strlen(coap_response_phrase(response->code)),
245 (const unsigned char *)coap_response_phrase(response->code));
246}
247
248void
250 uint8_t block_mode) {
251 context->block_mode = block_mode &= (COAP_BLOCK_USE_LIBCOAP |
253 if (!(block_mode & COAP_BLOCK_USE_LIBCOAP))
254 context->block_mode = 0;
255}
256
257/*
258 * The block token match only matches on the bottom 32 bits
259 * [The upper 32 bits are incremented as different payloads are sent]
260 *
261 */
263block_token_match(const uint8_t *a, size_t alen,
264 const uint8_t *b, size_t blen) {
265 size_t bias;
266 if (blen < 4)
267 return alen == blen && memcmp(a, b, blen) == 0;
268 bias = blen - 4;
269 return alen == blen && memcmp(a+bias, b+bias, 4) == 0;
270}
271
273full_match(const uint8_t *a, size_t alen,
274 const uint8_t *b, size_t blen) {
275 return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
276}
277
278int
280 coap_pdu_type_t type) {
281 coap_lg_crcv_t *cq;
282
283 assert(session);
284 if (!session)
285 return 0;
286
287 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
289 "** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n",
290 coap_session_str(session));
291 return 0;
292 }
293
294 LL_FOREACH(session->lg_crcv, cq) {
295 if (cq->observe_set) {
296 if ((!token && !cq->app_token->length) || (token &&
297 full_match(token->s, token->length, cq->app_token->s,
298 cq->app_token->length))) {
299 uint8_t buf[4];
300 coap_mid_t mid;
301 size_t size;
302 const uint8_t *data;
303 coap_pdu_t * pdu = coap_pdu_duplicate(&cq->pdu,
304 session,
306 cq->base_token,
307 NULL);
308
309 cq->observe_set = 0;
310 if (pdu == NULL)
311 return 0;
312 /* Need to make sure that this is the correct type */
313 pdu->type = type;
314
315 if (coap_get_data(&cq->pdu, &size, &data)) {
316 coap_add_data(pdu, size, data);
317 }
319 coap_encode_var_safe(buf, sizeof(buf),
321 buf);
322 mid = coap_send_internal(session, pdu);
323 if (mid != COAP_INVALID_MID)
324 return 1;
325 break;
326 }
327 }
328 }
329 return 0;
330}
331
332int
334 coap_pdu_t *pdu,
335 coap_resource_t *resource,
336 const coap_string_t *query,
337 int maxage,
338 uint64_t etag,
339 size_t length,
340 const uint8_t *data,
341 coap_release_large_data_t release_func,
342 void *app_ptr) {
343
344 ssize_t avail;
345 coap_block_t block;
346 size_t chunk;
347 coap_lg_xmit_t *lg_xmit = NULL;
348 uint8_t buf[8];
349 int have_block_defined = 0;
350 uint8_t blk_size;
351 uint16_t option;
352
353 assert(pdu);
354
355 if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
357 "** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n",
358 coap_session_str(session));
359 goto add_data;
360 }
361
362/* Block NUM max 20 bits and block size is "2**(SZX + 4)" and SZX max of 6 */
363#define MAX_BLK_LEN (((1 << 20) - 1) * (1 << (6 + 4)))
364
365 if (length > MAX_BLK_LEN) {
367 "Size of large buffer restricted to 0x%x bytes\n", MAX_BLK_LEN);
368 length = MAX_BLK_LEN;
369 }
370 /* Determine the block size to use, adding in sensible options if needed */
371 if (COAP_PDU_IS_REQUEST(pdu)) {
373
374 option = COAP_OPTION_BLOCK1;
375
376 /* See if this token is already in use for large bodies (unlikely) */
377 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
378 if (full_match(pdu->token, pdu->token_length,
379 lg_xmit->b.b1.app_token->s,
380 lg_xmit->b.b1.app_token->length)) {
381 /* Unfortunately need to free this off as potential size change */
382 LL_DELETE(session->lg_xmit, lg_xmit);
383 coap_block_delete_lg_xmit(session, lg_xmit);
384 lg_xmit = NULL;
385 break;
386 }
387 }
388 }
389 else {
390 /* Have to assume that it is a response even if code is 0.00 */
392 coap_string_t empty = { 0, NULL};
393
394 assert(resource);
395 option = COAP_OPTION_BLOCK2;
396
397 /* Check if resource+query is already in use for large bodies (unlikely) */
398 LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
399 if (resource == lg_xmit->b.b2.resource &&
400 coap_string_equal(query ? query : &empty,
401 lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) {
402 /* Unfortunately need to free this off as potential size change */
403 LL_DELETE(session->lg_xmit, lg_xmit);
404 coap_block_delete_lg_xmit(session, lg_xmit);
405 lg_xmit = NULL;
406 break;
407 }
408 }
409 }
410
411 avail = pdu->max_size - pdu->used_size - pdu->hdr_size;
412 /* May need token of length 8, so account for this */
413 avail -= (pdu->token_length <= 8) ? pdu->token_length <= 8 : 0;
414 blk_size = coap_flsll((long long)avail) - 4 - 1;
415
416 /* see if BLOCKx defined - if so update blk_size as given by app */
417 if (coap_get_block(pdu, option, &block)) {
418 if (block.szx < blk_size)
419 blk_size = block.szx;
420 have_block_defined = 1;
421 }
422
423 if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) {
424 /* bad luck, this is the smallest block size */
426 "not enough space, even the smallest block does not fit\n");
427 goto fail;
428 }
429
430 chunk = (size_t)1 << (blk_size + 4);
431 if (have_block_defined && block.num != 0) {
432 /* App is defining a single block to send */
433 size_t rem;
434
435 pdu->body_data = data;
436 pdu->body_length = length;
437 coap_log(LOG_DEBUG, "PDU presented by app\n");
439 pdu->body_data = NULL;
440 pdu->body_length = 0;
441 if (length >= block.num * chunk) {
442 rem = chunk;
443 if (chunk > length - block.num * chunk)
444 rem = length - block.num * chunk;
445 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
446 goto fail;
447 }
448 if (release_func)
449 release_func(session, app_ptr);
450 }
451 else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) {
452 /* Only add in lg_xmit if more than one block needs to be handled */
453 uint64_t token;
454 size_t rem;
455
456 lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
457 if (!lg_xmit)
458 goto fail;
459
460 coap_log(LOG_DEBUG, "** %s: lg_xmit %p initialized\n",
461 coap_session_str(session), (void*)lg_xmit);
462 /* Set up for displaying all the data in the pdu */
463 pdu->body_data = data;
464 pdu->body_length = length;
465 coap_log(LOG_DEBUG, "PDU presented by app\n");
467 pdu->body_data = NULL;
468 pdu->body_length = 0;
469 /* Update lg_xmit with large data information */
470 lg_xmit->blk_size = blk_size;
471 lg_xmit->option = option;
472 lg_xmit->data = data;
473 lg_xmit->length = length;
474 lg_xmit->offset = 0;
475 lg_xmit->release_func = release_func;
476 lg_xmit->last_payload = 0;
477 lg_xmit->last_used = 0;
478 lg_xmit->app_ptr = app_ptr;
479 if (COAP_PDU_IS_REQUEST(pdu)) {
480 /* Need to keep original token for updating response PDUs */
481 lg_xmit->b.b1.app_token = coap_new_binary(pdu->token_length);
482 if (!lg_xmit->b.b1.app_token)
483 goto fail;
484 memcpy(lg_xmit->b.b1.app_token->s, pdu->token, pdu->token_length);
485 /*
486 * Need to set up new token for use during transmits
487 */
488 lg_xmit->b.b1.count = 1;
489 token = ((++session->tx_token) & 0xffffffff) +
490 ((uint64_t)lg_xmit->b.b1.count << 32);
491 memset(lg_xmit->b.b1.token, 0, sizeof(lg_xmit->b.b1.token));
492 lg_xmit->b.b1.token_length = coap_encode_var_safe8(lg_xmit->b.b1.token,
493 sizeof(token), token);
494 /*
495 * Token will be updated in pdu later as original pdu may be needed in
496 * coap_send()
497 */
500 coap_encode_var_safe(buf, sizeof(buf),
501 (unsigned int)length),
502 buf);
503 }
504 else {
505 /*
506 * resource+query match is used for BLOCK2 large body transmissions
507 * token match is used for BLOCK1 large body transmissions
508 */
509 lg_xmit->b.b2.resource = resource;
510 if (query) {
511 lg_xmit->b.b2.query = coap_new_string(query->length);
512 if (lg_xmit->b.b2.query) {
513 memcpy(lg_xmit->b.b2.query->s, query->s, query->length);
514 }
515 }
516 else {
517 lg_xmit->b.b2.query = NULL;
518 }
519 lg_xmit->b.b2.etag = etag;
520 if (maxage >= 0) {
521 coap_tick_t now;
522
523 coap_ticks(&now);
524 lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage;
525 }
526 else {
527 lg_xmit->b.b2.maxage_expire = 0;
528 }
531 coap_encode_var_safe(buf, sizeof(buf),
532 (unsigned int)length),
533 buf);
534 if (etag == 0) {
535 if (++session->context->etag == 0)
536 ++session->context->etag;
537 etag = session->context->etag;
538 }
541 coap_encode_var_safe8(buf, sizeof(buf), etag),
542 buf);
543 }
544
545 /* Add in with requested block num, more bit and block size */
546 block.m = ((block.num + 1) * chunk) < lg_xmit->length;
548 lg_xmit->option,
549 coap_encode_var_safe(buf, sizeof(buf),
550 (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
551 buf);
552
553 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
554 memcpy(&lg_xmit->pdu, pdu, sizeof(lg_xmit->pdu));
555 lg_xmit->pdu.token = coap_malloc_type(COAP_PDU_BUF,
556 8 + lg_xmit->pdu.used_size + lg_xmit->pdu.hdr_size);
557 if (!lg_xmit->pdu.token)
558 goto fail;
559
560 lg_xmit->pdu.alloc_size = 8 + lg_xmit->pdu.used_size +
561 lg_xmit->pdu.hdr_size;
562 lg_xmit->pdu.token += lg_xmit->pdu.hdr_size;
563 memcpy(lg_xmit->pdu.token, pdu->token, lg_xmit->pdu.used_size);
564 if (pdu->data)
565 lg_xmit->pdu.data = lg_xmit->pdu.token + (pdu->data - pdu->token);
566
567 /* Check we still have space after adding in some options */
568 avail = pdu->max_size - pdu->used_size - pdu->hdr_size;
569 /* May need token of length 8, so account for this */
570 avail -= (pdu->token_length <= 8) ? pdu->token_length <= 8 : 0;
571 if (avail < (ssize_t)chunk) {
572 /* chunk size change down */
573 if (avail < 16) {
575 "not enough space, even the smallest block does not fit\n");
576 goto fail;
577 }
578 blk_size = coap_flsll((long long)avail) - 4 - 1;
579 block.num = block.num << (lg_xmit->blk_size - blk_size);
580 lg_xmit->blk_size = blk_size;
581 chunk = (size_t)1 << (lg_xmit->blk_size + 4);
583 lg_xmit->option,
584 coap_encode_var_safe(buf, sizeof(buf),
585 (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
586 buf);
587 }
588
589 rem = chunk;
590 if (chunk > lg_xmit->length - block.num * chunk)
591 rem = lg_xmit->length - block.num * chunk;
592 if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
593 goto fail;
594
595 lg_xmit->last_block = -1;
596
597 /* Link the new lg_xmit in */
598 LL_PREPEND(session->lg_xmit,lg_xmit);
599 }
600 else {
601 /* No need to use blocks */
602 if (have_block_defined) {
604 option,
605 coap_encode_var_safe(buf, sizeof(buf),
606 (0 << 4) | (0 << 3) | blk_size), buf);
607 }
608add_data:
609 if (!coap_add_data(pdu, length, data))
610 goto fail;
611
612 if (release_func)
613 release_func(session, app_ptr);
614 }
615 return 1;
616
617fail:
618 if (lg_xmit) {
619 coap_block_delete_lg_xmit(session, lg_xmit);
620 }
621 if (release_func)
622 release_func(session, app_ptr);
623 return 0;
624}
625
626int
628 coap_pdu_t *pdu,
629 size_t length,
630 const uint8_t *data,
631 coap_release_large_data_t release_func,
632 void *app_ptr) {
633 return coap_add_data_large_internal(session, pdu, NULL, NULL, -1,
634 0, length, data, release_func, app_ptr);
635}
636
637int
639 coap_session_t *session,
640 const coap_pdu_t *request,
641 coap_pdu_t *response,
642 const coap_string_t *query,
643 uint16_t media_type,
644 int maxage,
645 uint64_t etag,
646 size_t length,
647 const uint8_t *data,
648 coap_release_large_data_t release_func,
649 void *app_ptr
650) {
651 unsigned char buf[4];
652 coap_block_t block = { 0, 0, 0 };
653 int block_requested = 0;
654 uint16_t block_opt = COAP_OPTION_BLOCK2;
655
656 /*
657 * Need to check that a valid block is getting asked for so that the
658 * correct options are put into the PDU.
659 */
660 if (request) {
661 if (coap_get_block(request, COAP_OPTION_BLOCK2, &block)) {
662 block_requested = 1;
663 if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
664 coap_log(LOG_DEBUG, "Illegal block requested (%d > last = %zu)\n",
665 block.num,
666 length >> (block.szx + 4));
667 response->code = COAP_RESPONSE_CODE(400);
668 goto error;
669 }
670 }
671 }
672
674 coap_encode_var_safe(buf, sizeof(buf),
675 media_type),
676 buf);
677
678 if (maxage >= 0) {
679 coap_insert_option(response,
681 coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
682 }
683
684 if (block_requested) {
685 int res;
686
687 res = coap_write_block_opt(&block, block_opt, response,
688 length);
689
690 switch (res) {
691 case -2: /* illegal block (caught above) */
692 response->code = COAP_RESPONSE_CODE(400);
693 goto error;
694 case -1: /* should really not happen */
695 assert(0);
696 /* fall through if assert is a no-op */
697 case -3: /* cannot handle request */
698 response->code = COAP_RESPONSE_CODE(500);
699 goto error;
700 default: /* everything is good */
701 ;
702 }
703
704 if (!coap_add_data_large_internal(session, response, resource, query,
705 maxage, etag, length, data,
706 release_func, app_ptr)) {
707 response->code = COAP_RESPONSE_CODE(500);
708 goto error;
709 }
710
711 return 1;
712 }
713
714 /*
715 * BLOCK2 not requested
716 */
717 if (!coap_add_data_large_internal(session, response, resource, query, maxage,
718 etag, length, data, release_func,
719 app_ptr)) {
720 response->code = COAP_RESPONSE_CODE(400);
721 goto error;
722 }
723
724 return 1;
725
726error:
727 coap_add_data(response,
728 strlen(coap_response_phrase(response->code)),
729 (const unsigned char *)coap_response_phrase(response->code));
730 return 0;
731}
732
737 coap_tick_t partial_timeout = COAP_EXCHANGE_LIFETIME(session);
738 coap_tick_t tim_rem = -1;
739
740 LL_FOREACH_SAFE(session->lg_crcv, p, q) {
741 if (!p->observe_set && p->last_used &&
742 p->last_used + partial_timeout <= now) {
743 /* Expire this entry */
744 LL_DELETE(session->lg_crcv, p);
745 coap_block_delete_lg_crcv(session, p);
746 }
747 else if (!p->observe_set && p->last_used) {
748 /* Delay until the lg_crcv needs to expire */
749 if (tim_rem > p->last_used + partial_timeout - now)
750 tim_rem = p->last_used + partial_timeout - now;
751 }
752 }
753 return tim_rem;
754}
755
756static int
757check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
758 uint32_t i;
759
760 for (i = 0; i < rec_blocks->used; i++) {
761 if (block_num < rec_blocks->range[i].begin)
762 return 0;
763 if (block_num <= rec_blocks->range[i].end)
764 return 1;
765 }
766 return 0;
767}
768
769static int
770check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks) {
771 uint32_t i;
772 uint32_t block = 0;
773
774 for (i = 0; i < rec_blocks->used; i++) {
775 if (block < rec_blocks->range[i].begin)
776 return 0;
777 if (block < rec_blocks->range[i].end)
778 block = rec_blocks->range[i].end;
779 }
780 /* total_blocks counts from 1 */
781 if (block + 1 < total_blocks)
782 return 0;
783
784 return 1;
785}
786
791 coap_tick_t partial_timeout = COAP_EXCHANGE_LIFETIME(session);
792 coap_tick_t tim_rem = -1;
793
794 LL_FOREACH_SAFE(session->lg_srcv, p, q) {
795 if (p->last_used && p->last_used + partial_timeout <= now) {
796 /* Expire this entry */
797 LL_DELETE(session->lg_srcv, p);
798 coap_block_delete_lg_srcv(session, p);
799 }
800 else if (p->last_used) {
801 /* Delay until the lg_srcv needs to expire */
802 if (tim_rem > p->last_used + partial_timeout - now)
803 tim_rem = p->last_used + partial_timeout - now;
804 }
805 }
806 return tim_rem;
807}
808
811 coap_lg_crcv_t *lg_crcv;
812
813 lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
814
815 if (lg_crcv == NULL)
816 return NULL;
817
818 coap_log(LOG_DEBUG, "** %s: lg_crcv %p initialized\n",
819 coap_session_str(session), (void*)lg_crcv);
820 memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
821 lg_crcv->initial = 1;
822 /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
823 memcpy(&lg_crcv->pdu, pdu, sizeof(lg_crcv->pdu));
825 lg_crcv->pdu.alloc_size + lg_crcv->pdu.hdr_size);
826 if (!lg_crcv->pdu.token) {
827 coap_block_delete_lg_crcv(session, lg_crcv);
828 return NULL;
829 }
830 lg_crcv->pdu.token += lg_crcv->pdu.hdr_size;
831 memcpy(lg_crcv->pdu.token, pdu->token, lg_crcv->pdu.used_size);
832 if (lg_crcv->pdu.data)
833 lg_crcv->pdu.data = lg_crcv->pdu.token + (pdu->data - pdu->token);
834 /* Check that there is space for increased token + option change */
835 if (lg_crcv->pdu.max_size < lg_crcv->pdu.used_size + 9)
836 lg_crcv->pdu.max_size = lg_crcv->pdu.used_size + 9;
837
838 assert(pdu->token_length <= 8);
839 lg_crcv->token_length = min(pdu->token_length, 8);
840 memset(lg_crcv->token, 0, sizeof(lg_crcv->token));
841 memcpy(lg_crcv->token, pdu->token, lg_crcv->token_length);
842
843 /* Need to keep original token for handling observe responses */
844 memset(lg_crcv->base_token, 0, sizeof(lg_crcv->base_token));
845 memcpy(lg_crcv->base_token, pdu->token, lg_crcv->token_length);
846 lg_crcv->base_token_length = lg_crcv->token_length;
847
848 /* Need to keep original token for updating response PDUs */
849 lg_crcv->app_token = coap_new_binary(lg_crcv->token_length);
850 if (!lg_crcv->app_token) {
851 coap_block_delete_lg_crcv(session, lg_crcv);
852 return NULL;
853 }
854 memcpy(lg_crcv->app_token->s, pdu->token, lg_crcv->token_length);
855 /* In case it is there - must not be in continuing request PDUs */
857
858 return lg_crcv;
859}
860
861void
863 coap_lg_crcv_t *lg_crcv) {
864 if (lg_crcv == NULL)
865 return;
866
867 if (lg_crcv->pdu.token)
868 coap_free_type(COAP_PDU_BUF, lg_crcv->pdu.token - lg_crcv->pdu.hdr_size);
870 coap_log(LOG_DEBUG, "** %s: lg_crcv %p released\n",
871 coap_session_str(session), (void*)lg_crcv);
874}
875
876void
878 coap_lg_srcv_t *lg_srcv) {
879 if (lg_srcv == NULL)
880 return;
881
884 coap_log(LOG_DEBUG, "** %s: lg_srcv %p released\n",
885 coap_session_str(session), (void*)lg_srcv);
887}
888
889void
891 coap_lg_xmit_t *lg_xmit) {
892 if (lg_xmit == NULL)
893 return;
894
895 if (lg_xmit->release_func) {
896 lg_xmit->release_func(session, lg_xmit->app_ptr);
897 }
898 if (lg_xmit->pdu.token) {
899 coap_free_type(COAP_PDU_BUF, lg_xmit->pdu.token - lg_xmit->pdu.hdr_size);
900 }
901 if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu))
903 else
904 coap_delete_string(lg_xmit->b.b2.query);
905
906 coap_log(LOG_DEBUG, "** %s: lg_xmit %p released\n",
907 coap_session_str(session), (void*)lg_xmit);
909}
910
911static int
912add_block_send(uint32_t num, uint32_t *out_blocks,
913 uint32_t *count, uint32_t max_count) {
914 uint32_t i;
915
916 for (i = 0; i < *count && *count < max_count; i++) {
917 if (num == out_blocks[i])
918 return 0;
919 else if (num < out_blocks[i]) {
920 if (*count - i > 1)
921 memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
922 out_blocks[i] = num;
923 (*count)++;
924 return 1;
925 }
926 }
927 if (*count < max_count) {
928 out_blocks[i] = num;
929 (*count)++;
930 return 1;
931 }
932 return 0;
933}
934
935/*
936 * Need to see if this is a request for the next block of a large body
937 * transfer. If so, need to initiate the response with the next blocks
938 * and not trouble the application.
939 *
940 * If additional responses needed, then these are expicitly sent out and
941 * 'response' is updated to be the last response to be sent.
942 *
943 * This is set up using coap_add_data_response_large()
944 *
945 * Server is sending a large data response to GET / observe (BLOCK2)
946 *
947 * Return: 0 Call application handler
948 * 1 Do not call application handler - just send the built response
949 */
950int
952 coap_pdu_t *pdu,
953 coap_pdu_t *response,
954 coap_resource_t *resource,
955 coap_string_t *query) {
957 coap_block_t block;
958 uint16_t block_opt = 0;
959 uint32_t out_blocks[1];
960 const char *error_phrase;
961
962 if (coap_get_block(pdu, COAP_OPTION_BLOCK2, &block)) {
963 block_opt = COAP_OPTION_BLOCK2;
964 }
965 LL_FOREACH(session->lg_xmit, p) {
966 size_t chunk;
967 coap_opt_iterator_t opt_iter;
968 coap_opt_iterator_t opt_b_iter;
969 coap_opt_t *option;
970 uint32_t request_cnt, i;
971 coap_opt_t *etag_opt = NULL;
972 coap_pdu_t *out_pdu = response;
973 static coap_string_t empty = { 0, NULL};
974
975 if (COAP_PDU_IS_REQUEST(&p->pdu) || resource != p->b.b2.resource ||
976 !coap_string_equal(query ? query : &empty,
977 p->b.b2.query ? p->b.b2.query : &empty)) {
978 /* try out the next one */
979 continue;
980 }
981 etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
982 if (etag_opt) {
983 uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
984 coap_opt_length(etag_opt));
985 if (etag != p->b.b2.etag) {
986 /* try out the next one */
987 continue;
988 }
989 out_pdu->code = COAP_RESPONSE_CODE(203);
990 return 1;
991 }
992 else {
993 out_pdu->code = p->pdu.code;
994 }
995
996 /* lg_xmit (response) found */
997
998 chunk = (size_t)1 << (p->blk_size + 4);
999 if (block_opt) {
1001 "found Block option, block size is %zu, block nr. %u, M %d\n",
1002 (size_t)1 << (block.szx + 4), block.num, block.m);
1003 if (block.szx != p->blk_size) {
1004 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
1005 /*
1006 * Recompute the block number of the previous packet given
1007 * the new block size
1008 */
1009 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
1010 p->blk_size = block.szx;
1011 chunk = (size_t)1 << (p->blk_size + 4);
1012 p->offset = block.num * chunk;
1014 "new Block size is %u, block number %u completed\n",
1015 1 << (block.szx + 4), block.num);
1016 } else {
1018 "ignoring request to increase Block size, "
1019 "next block is not aligned on requested block size "
1020 "boundary. (%zu x %u mod %u = %zu (which is not 0)\n",
1021 p->offset/chunk + 1, (1 << (p->blk_size + 4)),
1022 (1 << (block.szx + 4)),
1023 (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
1024 }
1025 }
1026 }
1027
1028 request_cnt = 0;
1029 coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
1030 while ((option = coap_option_next(&opt_b_iter))) {
1031 unsigned int num;
1032 if (opt_b_iter.number != p->option)
1033 continue;
1034 num = coap_opt_block_num(option);
1035 if (num > 0xFFFFF) /* 20 bits max for num */
1036 continue;
1037 if (block.szx != COAP_OPT_BLOCK_SZX(option)) {
1038 coap_add_data(response,
1039 sizeof("Changing blocksize during request invalid")-1,
1040 (const uint8_t *)"Changing blocksize during request invalid");
1041 response->code = COAP_RESPONSE_CODE(400);
1042 return 1;
1043 }
1044 add_block_send(num, out_blocks, &request_cnt, 1);
1045 break;
1046 }
1047 if (request_cnt == 0) {
1048 /* Block2 not found - give them the first block */
1049 block.szx = p->blk_size;
1050 p->offset = 0;
1051 out_blocks[0] = 0;
1052 request_cnt = 1;
1053 }
1054
1055 for (i = 0; i < request_cnt; i++) {
1056 uint8_t buf[8];
1057
1058 block.num = out_blocks[i];
1059 p->offset = block.num * chunk;
1060
1061 if (i + 1 < request_cnt) {
1062 /* Need to set up a copy of the pdu to send */
1063 coap_opt_filter_t drop_options;
1064
1065 memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1066 if (block.num != 0)
1068 out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->token_length,
1069 pdu->token, &drop_options);
1070 if (!out_pdu) {
1071 response->code = COAP_RESPONSE_CODE(500);
1072 goto fail;
1073 }
1074 }
1075 else {
1076 /*
1077 * Copy the options across and then fix the block option
1078 *
1079 * Need to drop Observe option if BLOCK2 and block.num != 0
1080 */
1082 while ((option = coap_option_next(&opt_iter))) {
1083 if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0)
1084 continue;
1085 if (!coap_add_option(response, opt_iter.number,
1086 coap_opt_length(option),
1087 coap_opt_value(option))) {
1088 goto internal_issue;
1089 }
1090 }
1091 out_pdu = response;
1092 }
1093 if (pdu->type == COAP_MESSAGE_NON)
1094 out_pdu->type = COAP_MESSAGE_NON;
1095 if (!coap_update_option(out_pdu, p->option,
1097 sizeof(buf),
1098 (block.num << 4) |
1099 ((p->offset + chunk < p->length) << 3) |
1100 block.szx),
1101 buf)) {
1102 goto internal_issue;
1103 }
1104 if (p->b.b2.maxage_expire) {
1105 coap_tick_t now;
1106 coap_time_t rem;
1107
1108 coap_ticks(&now);
1109 rem = coap_ticks_to_rt(now);
1110 if (p->b.b2.maxage_expire > rem) {
1111 rem = p->b.b2.maxage_expire - rem;
1112 }
1113 else {
1114 rem = 0;
1115 /* Entry needs to be expired */
1116 coap_ticks(&p->last_used);
1117 }
1120 sizeof(buf),
1121 rem),
1122 buf)) {
1123 goto internal_issue;
1124 }
1125 }
1126
1127 if (!etag_opt && !coap_add_block(out_pdu,
1128 p->length,
1129 p->data,
1130 block.num,
1131 block.szx)) {
1132 goto internal_issue;
1133 }
1134 if (i + 1 < request_cnt) {
1135 coap_send_internal(session, out_pdu);
1136 }
1137 }
1138 goto skip_app_handler;
1139
1140fail:
1141 /* Keep in cache for 4 * ACK_TIMOUT */
1142 coap_ticks(&p->last_used);
1143 goto skip_app_handler;
1144 } /* end of LL_FOREACH() */
1145 return 0;
1146
1147skip_app_handler:
1148 return 1;
1149
1150internal_issue:
1151 response->code = COAP_RESPONSE_CODE(500);
1152 error_phrase = coap_response_phrase(response->code);
1153 coap_add_data(response, strlen(error_phrase),
1154 (const uint8_t *)error_phrase);
1155 goto fail;
1156}
1157
1158static int
1159update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num) {
1160 uint32_t i;
1161
1162 /* Reset as there is activity */
1163 rec_blocks->retry = 0;
1164
1165 for (i = 0; i < rec_blocks->used; i++) {
1166 if (block_num >= rec_blocks->range[i].begin &&
1167 block_num <= rec_blocks->range[i].end)
1168 break;
1169
1170 if (block_num < rec_blocks->range[i].begin) {
1171 if (block_num + 1 == rec_blocks->range[i].begin) {
1172 rec_blocks->range[i].begin = block_num;
1173 }
1174 else {
1175 /* Need to insert a new range */
1176 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
1177 /* Too many losses */
1178 return 0;
1179 memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
1180 (rec_blocks->used - i) * sizeof (rec_blocks->range[0]));
1181 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
1182 rec_blocks->used++;
1183 }
1184 break;
1185 }
1186 if (block_num == rec_blocks->range[i].end + 1) {
1187 rec_blocks->range[i].end = block_num;
1188 if (i + 1 < rec_blocks->used) {
1189 if (rec_blocks->range[i+1].begin == block_num + 1) {
1190 /* Merge the 2 ranges */
1191 rec_blocks->range[i].end = rec_blocks->range[i+1].end;
1192 if (i+2 < rec_blocks->used) {
1193 memmove (&rec_blocks->range[i+1], &rec_blocks->range[i+2],
1194 (rec_blocks->used - (i+2)) * sizeof (rec_blocks->range[0]));
1195 }
1196 rec_blocks->used--;
1197 }
1198 }
1199 break;
1200 }
1201 }
1202 if (i == rec_blocks->used) {
1203 if (rec_blocks->used == COAP_RBLOCK_CNT -1)
1204 /* Too many losses */
1205 return 0;
1206 rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
1207 rec_blocks->used++;
1208 }
1209 coap_ticks(&rec_blocks->last_seen);
1210 return 1;
1211}
1212
1213/*
1214 * Need to check if this is a large PUT / POST using multiple blocks
1215 *
1216 * Server receiving PUT/POST etc. of a large amount of data (BLOCK1)
1217 *
1218 * Return: 0 Call application handler
1219 * 1 Do not call application handler - just send the built response
1220 */
1221int
1223 coap_session_t *session,
1224 coap_pdu_t *pdu,
1225 coap_pdu_t *response,
1226 coap_resource_t *resource,
1227 coap_string_t *uri_path,
1228 coap_opt_t *observe,
1229 coap_string_t *query,
1231 int *added_block) {
1232 size_t length = 0;
1233 const uint8_t *data = NULL;
1234 size_t offset = 0;
1235 size_t total = 0;
1236 coap_block_t block;
1237 coap_opt_iterator_t opt_iter;
1238 uint16_t block_option = 0;
1239
1240 coap_get_data_large(pdu, &length, &data, &offset, &total);
1241 pdu->body_offset = 0;
1242 pdu->body_total = length;
1243
1244 if (coap_get_block(pdu, COAP_OPTION_BLOCK1, &block)) {
1245 block_option = COAP_OPTION_BLOCK1;
1246 }
1247 if (block_option) {
1248 coap_lg_srcv_t *p;
1249 coap_opt_t *size_opt = coap_check_option(pdu,
1251 &opt_iter);
1252 coap_opt_t *fmt_opt = coap_check_option(pdu,
1254 &opt_iter);
1255 uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
1256 coap_opt_length(fmt_opt)) :
1258
1259 total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
1260 coap_opt_length(size_opt)) : 0;
1261 offset = block.num << (block.szx + 4);
1262
1263 LL_FOREACH(session->lg_srcv, p) {
1264 if (resource == p->resource) {
1265 break;
1266 }
1267 if ((p->resource == context->unknown_resource ||
1268 resource == context->proxy_uri_resource) &&
1269 coap_string_equal(uri_path, p->uri_path))
1270 break;
1271 }
1272 if (!p && block.num != 0) {
1273 /* random access - no need to track */
1274 pdu->body_data = data;
1275 pdu->body_length = length;
1276 pdu->body_offset = offset;
1277 pdu->body_total = length + offset + (block.m ? 1 : 0);
1278 }
1279 /* Do not do this if this is a single block */
1280 else if (!p && !(offset == 0 && block.m == 0)) {
1282 if (p == NULL) {
1283 coap_add_data(response, sizeof("Memory issue")-1,
1284 (const uint8_t *)"Memory issue");
1285 response->code = COAP_RESPONSE_CODE(500);
1286 goto skip_app_handler;
1287 }
1288 coap_log(LOG_DEBUG, "** %s: lg_srcv %p initialized\n",
1289 coap_session_str(session), (void*)p);
1290 memset(p, 0, sizeof(coap_lg_srcv_t));
1291 p->resource = resource;
1292 if (resource == context->unknown_resource ||
1293 resource == context->proxy_uri_resource)
1294 p->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
1295 p->content_format = fmt;
1296 p->total_len = total;
1297 p->amount_so_far = length;
1298 p->szx = block.szx;
1299 p->block_option = block_option;
1300 if (observe) {
1301 p->observe_length = min(coap_opt_length(observe), 3);
1302 memcpy(p->observe, coap_opt_value(observe), p->observe_length);
1303 p->observe_set = 1;
1304 }
1305 p->body_data = NULL;
1306 LL_PREPEND(session->lg_srcv, p);
1307 }
1308 if (p) {
1309 if (fmt != p->content_format) {
1310 coap_add_data(response, sizeof("Content-Format mismatch")-1,
1311 (const uint8_t *)"Content-Format mismatch");
1312 response->code = COAP_RESPONSE_CODE(408);
1313 goto free_lg_recv;
1314 }
1315 p->last_mid = pdu->mid;
1316 p->last_type = pdu->type;
1317 memcpy(p->last_token, pdu->token, pdu->token_length);
1319 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY)) {
1320 size_t chunk = (size_t)1 << (block.szx + 4);
1321 if (!check_if_received_block(&p->rec_blocks, block.num)) {
1322 /* Update list of blocks received */
1323 if (!update_received_blocks(&p->rec_blocks, block.num)) {
1325 coap_add_data(response, sizeof("Too many missing blocks")-1,
1326 (const uint8_t *)"Too many missing blocks");
1327 response->code = COAP_RESPONSE_CODE(408);
1328 goto free_lg_recv;
1329 }
1330 /* Update saved data */
1331 p->body_data = coap_block_build_body(p->body_data, length, data,
1332 offset, p->total_len);
1333 if (!p->body_data)
1334 goto call_app_handler;
1335
1336 }
1338 (uint32_t)(p->total_len + chunk -1)/chunk)) {
1339 /* Not all the payloads of the body have arrived */
1340 if (block.m) {
1341 uint8_t buf[4];
1342
1343 /* Ask for the next block */
1344 coap_insert_option(response, block_option,
1345 coap_encode_var_safe(buf, sizeof(buf),
1346 (block.num << 4) |
1347 (block.m << 3) |
1348 block.szx),
1349 buf);
1350 response->code = COAP_RESPONSE_CODE(231);
1351 goto skip_app_handler;
1352 }
1353 goto skip_app_handler;
1354 }
1355
1356 /*
1357 * Remove the BLOCK1 option as passing all of the data to
1358 * application layer. Add back in observe option if appropriate.
1359 * Adjust all other information.
1360 */
1361 if (p->observe_set) {
1363 p->observe_length, p->observe);
1364 }
1365 coap_remove_option(pdu, block_option);
1366 pdu->body_data = p->body_data->s;
1367 pdu->body_length = p->total_len;
1368 pdu->body_offset = 0;
1369 pdu->body_total = p->total_len;
1370 coap_log(LOG_DEBUG, "Server app version of updated PDU\n");
1372 /* Need to do this here as we need to free off p */
1373 h(resource, session, pdu, query, response);
1374 /* Check if lg_xmit generated and update PDU code if so */
1375 coap_check_code_lg_xmit(session, response, resource, query);
1376 /* Last chunk - free off shortly */
1377 coap_ticks(&p->last_used);
1378 goto skip_app_handler;
1379 }
1380 else {
1381 /* No need to update body_data and body_length as a single PDU */
1382 pdu->body_offset = offset;
1383 /* Exact match if last block */
1384 if (block.m) {
1385 uint8_t buf[4];
1386
1387 if (total > offset + length + block.m)
1388 pdu->body_total = total;
1389 else
1390 pdu->body_total = offset + length + block.m;
1391
1392 coap_insert_option(response, block_option,
1393 coap_encode_var_safe(buf, sizeof(buf),
1394 (block.num << 4) |
1395 (block.m << 3) |
1396 block.szx),
1397 buf);
1398 h(resource, session, pdu, query, response);
1399 /* Check if lg_xmit generated and update PDU code if so */
1400 coap_check_code_lg_xmit(session, response, resource, query);
1401 if (COAP_RESPONSE_CLASS(response->code) == 2) {
1402 /* Just in case, as there are more to go */
1403 response->code = COAP_RESPONSE_CODE(231);
1404 }
1405 *added_block = 1;
1406 goto skip_app_handler;
1407 }
1408 else {
1409 pdu->body_total = offset + length + block.m;
1410 }
1411 }
1412
1413 if (block.m == 0) {
1414 /* Last chunk - free off all */
1415 coap_ticks(&p->last_used);
1416 }
1417 goto call_app_handler;
1418
1419free_lg_recv:
1420 LL_DELETE(session->lg_srcv, p);
1421 coap_block_delete_lg_srcv(session, p);
1422 goto skip_app_handler;
1423 }
1424 }
1425call_app_handler:
1426 return 0;
1427
1428skip_app_handler:
1429 return 1;
1430}
1431
1432/*
1433 * Need to see if this is a response to a large body request transfer. If so,
1434 * need to initiate the request containing the next block and not trouble the
1435 * application. Note that Token must unique per request/response.
1436 *
1437 * Client receives large data acknowledgement from server (BLOCK1)
1438 *
1439 * This is set up using coap_add_data_request_large()
1440 *
1441 * Client is sending a large data request using GET etc.
1442 *
1443 * Return: 0 Call application handler
1444 * 1 Do not call application handler - just send the built response
1445 */
1446int
1448 coap_lg_xmit_t *p;
1449 coap_lg_xmit_t *q;
1450
1451 LL_FOREACH_SAFE(session->lg_xmit, p, q) {
1452 if (COAP_PDU_IS_REQUEST(&p->pdu) &&
1453 !block_token_match(rcvd->token, rcvd->token_length,
1454 p->b.b1.token, p->b.b1.token_length)) {
1455 }
1456 /* lg_xmit found */
1457 size_t chunk = (size_t)1 << (p->blk_size + 4);
1458 coap_block_t block;
1459
1460 if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
1461 coap_get_block(rcvd, p->option, &block)) {
1463 "found Block option, block size is %u, block nr. %u\n",
1464 1 << (block.szx + 4), block.num);
1465 if (block.szx != p->blk_size) {
1466 if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
1467 /*
1468 * Recompute the block number of the previous packet given the
1469 * new block size
1470 */
1471 block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
1472 p->blk_size = block.szx;
1473 chunk = (size_t)1 << (p->blk_size + 4);
1474 p->offset = block.num * chunk;
1476 "new Block size is %u, block number %u completed\n",
1477 1 << (block.szx + 4), block.num);
1478 } else {
1479 coap_log(LOG_DEBUG, "ignoring request to increase Block size, "
1480 "next block is not aligned on requested block size boundary. "
1481 "(%zu x %u mod %u = %zu != 0)\n",
1482 p->offset/chunk + 1, (1 << (p->blk_size + 4)),
1483 (1 << (block.szx + 4)),
1484 (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
1485 }
1486 }
1487 if (p->last_block == (int)block.num) {
1488 /*
1489 * Duplicate BLOCK ACK
1490 *
1491 * RFCs not clear here, but on a lossy connection, there could
1492 * be multiple BLOCK ACKs, causing the client to retransmit the
1493 * same block multiple times, or the server retransmitting the
1494 * same ACK.
1495 *
1496 * Once a block has been ACKd, there is no need to retransmit it.
1497 */
1498 return 1;
1499 }
1500 p->last_block = block.num;
1501 p->offset = (block.num + 1) * chunk;
1502 if (p->offset < p->length) {
1503 /* Build the next PDU request based off the skeletal PDU */
1504 uint8_t buf[8];
1505 coap_pdu_t *pdu;
1506 uint64_t token = coap_decode_var_bytes8(p->pdu.token,
1507 p->pdu.token_length);
1508 uint8_t ltoken[8];
1509 size_t ltoken_length;
1510
1511 token = (token & 0xffffffff) + ((uint64_t)(++p->b.b1.count) << 32);
1512 ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
1513 pdu = coap_pdu_duplicate(&p->pdu, session, ltoken_length, ltoken, NULL);
1514 if (!pdu)
1515 goto fail_body;
1516
1517 block.num++;
1518 coap_update_option(pdu, p->option,
1519 coap_encode_var_safe(buf, sizeof(buf),
1520 (block.num << 4) |
1521 ((p->offset + chunk < p->length) << 3) |
1522 block.szx),
1523 buf);
1524
1525 if (!coap_add_block(pdu,
1526 p->length,
1527 p->data,
1528 block.num,
1529 block.szx))
1530 goto fail_body;
1531 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
1532 goto fail_body;
1533 return 1;
1534 }
1535 }
1536fail_body:
1537 /* need to put back original token into rcvd */
1538 if (p->b.b1.app_token)
1540 p->b.b1.app_token->s);
1541 coap_log(LOG_DEBUG, "PDU given to app\n");
1542 coap_show_pdu(LOG_DEBUG, rcvd);
1543
1544 LL_DELETE(session->lg_xmit, p);
1545 coap_block_delete_lg_xmit(session, p);
1546 /*
1547 * There may be a block response after doing the large request
1548 * https://tools.ietf.org/html/rfc7959#section-3.3
1549 */
1550 break;
1551 } /* end of LL_FOREACH_SAFE */
1552 return 0;
1553}
1554
1555/*
1556 * Re-assemble payloads into a body
1557 */
1559coap_block_build_body(coap_binary_t *body_data, size_t length,
1560 const uint8_t *data, size_t offset, size_t total)
1561{
1562 if (data == NULL)
1563 return NULL;
1564 if (body_data == NULL && total) {
1565 body_data = coap_new_binary(total);
1566 }
1567 if (body_data == NULL)
1568 return NULL;
1569
1570 /* Update saved data */
1571 if (offset + length <= total && body_data->length >= total) {
1572 memcpy(&body_data->s[offset], data, length);
1573 }
1574 else {
1575 /*
1576 * total may be inaccurate as per
1577 * https://tools.ietf.org/html/rfc7959#section-4
1578 * o In a request carrying a Block1 Option, to indicate the current
1579 * estimate the client has of the total size of the resource
1580 * representation, measured in bytes ("size indication").
1581 * o In a response carrying a Block2 Option, to indicate the current
1582 * estimate the server has of the total size of the resource
1583 * representation, measured in bytes ("size indication").
1584 */
1585 coap_binary_t *new = coap_resize_binary(body_data, offset + length);
1586
1587 if (new) {
1588 body_data = new;
1589 memcpy(&body_data->s[offset], data, length);
1590 }
1591 else {
1592 coap_delete_binary(body_data);
1593 return NULL;
1594 }
1595 }
1596 return body_data;
1597}
1598
1599/*
1600 * Need to see if this is a large body response to a request. If so,
1601 * need to initiate the request for the next block and not trouble the
1602 * application. Note that Token must unique per request/response.
1603 *
1604 * This is set up using coap_send()
1605 * Client receives large data from server (BLOCK2)
1606 *
1607 * Return: 0 Call application handler
1608 * 1 Do not call application handler - just sent the next request
1609 */
1610int
1612 coap_session_t *session,
1613 coap_pdu_t *sent,
1614 coap_pdu_t *rcvd,
1615 coap_recurse_t recursive) {
1616 coap_lg_crcv_t *p;
1617 int app_has_response = 0;
1618 coap_block_t block = {0, 0, 0};
1619 int have_block = 0;
1620 uint16_t block_opt = 0;
1621 size_t offset;
1622
1623 LL_FOREACH(session->lg_crcv, p) {
1624 size_t chunk = 0;
1625 uint8_t buf[8];
1626 coap_opt_iterator_t opt_iter;
1627
1628 if (!full_match(rcvd->token, rcvd->token_length,
1629 p->token, p->token_length)) {
1630 /* try out the next one */
1631 continue;
1632 }
1633
1634 /* lg_crcv found */
1635
1636 if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
1637 size_t length;
1638 const uint8_t *data;
1640 &opt_iter);
1641 size_t size2 = size_opt ?
1643 coap_opt_length(size_opt)) : 0;
1644
1645 coap_get_data(rcvd, &length, &data);
1646 rcvd->body_offset = 0;
1647 rcvd->body_total = length;
1648 if (coap_get_block(rcvd, COAP_OPTION_BLOCK2, &block)) {
1649 have_block = 1;
1650 block_opt = COAP_OPTION_BLOCK2;
1651 }
1652 if (have_block) {
1653 coap_opt_t *fmt_opt = coap_check_option(rcvd,
1655 &opt_iter);
1656 uint16_t fmt = fmt_opt ?
1658 coap_opt_length(fmt_opt)) :
1660 coap_opt_t *etag_opt = coap_check_option(rcvd,
1662 &opt_iter);
1663 /* Possibility that Size2 not sent, or is too small */
1664 chunk = (size_t)1 << (block.szx + 4);
1665 offset = block.num * chunk;
1666 if (size2 < (offset + length)) {
1667 if (block.m)
1668 size2 = offset + length + 1;
1669 else
1670 size2 = offset + length;
1671 }
1672
1673 if (p->initial) {
1674 p->initial = 0;
1675 if (etag_opt) {
1676 p->etag_length = coap_opt_length(etag_opt);
1677 memcpy(p->etag, coap_opt_value(etag_opt), p->etag_length);
1678 p->etag_set = 1;
1679 }
1680 else {
1681 p->etag_set = 0;
1682 }
1683 p->total_len = size2;
1684 p->content_format = fmt;
1685 p->szx = block.szx;
1686 p->block_option = block_opt;
1687 p->last_type = rcvd->type;
1688 p->rec_blocks.used = 0;
1689 }
1690 if (p->total_len < size2)
1691 p->total_len = size2;
1692
1693 if (etag_opt) {
1694 if (!full_match(coap_opt_value(etag_opt),
1695 coap_opt_length(etag_opt),
1696 p->etag, p->etag_length)) {
1697 /* body of data has changed - need to restart request */
1698 size_t len;
1699 coap_pdu_t *pdu;
1700
1702 "Data body updated during receipt - new request started\n");
1703 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
1705
1706 p->initial = 1;
1708 p->body_data = NULL;
1709
1710 coap_session_new_token(session, &len, buf);
1711 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
1712 if (!pdu)
1713 goto fail_resp;
1714
1715 memcpy(p->token, pdu->token, pdu->token_length);
1716 p->token_length = pdu->token_length;
1717
1718 coap_update_option(pdu, block_opt,
1719 coap_encode_var_safe(buf, sizeof(buf),
1720 (0 << 4) | (0 << 3) | block.szx),
1721 buf);
1722
1723 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
1724 goto fail_resp;
1725
1726 goto skip_app_handler;
1727 }
1728 }
1729 else if (p->etag_set) {
1730 /* Cannot handle this change in ETag to not being there */
1731 coap_log(LOG_WARNING, "Not all blocks have ETag option\n");
1732 session->block_mode &= ~(COAP_BLOCK_SINGLE_BODY);
1733 goto block_mode;
1734 }
1735
1736 if (fmt != p->content_format) {
1737 coap_log(LOG_WARNING, "Content-Format option mismatch\n");
1738 session->block_mode &= ~(COAP_BLOCK_SINGLE_BODY);
1739 goto block_mode;
1740 }
1742 "found Block option, block size is %u, block nr. %u\n",
1743 1 << (block.szx + 4), block.num);
1744 if (block.num == 0) {
1745 coap_opt_t *obs_opt = coap_check_option(rcvd,
1747 &opt_iter);
1748 if (obs_opt) {
1749 p->observe_length = min(coap_opt_length(obs_opt), 3);
1750 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
1751 p->observe_set = 1;
1752 }
1753 else {
1754 p->observe_set = 0;
1755 }
1756 }
1757 if (!check_if_received_block(&p->rec_blocks, block.num)) {
1758 /* Update list of blocks received */
1759 if (!update_received_blocks(&p->rec_blocks, block.num)) {
1761 goto fail_resp;
1762 }
1763
1764 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY)) {
1765 p->body_data = coap_block_build_body(p->body_data, length, data,
1766 offset, size2);
1767 if (p->body_data == NULL) {
1768 /* Need to do it block by block */
1769 session->block_mode &= ~(COAP_BLOCK_SINGLE_BODY);
1770 goto block_mode;
1771 }
1772 }
1774 (size2 + chunk -1) / chunk)) {
1775 /* Not all the payloads of the body have arrived */
1776 size_t len;
1777 coap_pdu_t *pdu;
1778
1779 if (block.m) {
1780 block.m = 0;
1781
1782 /* Ask for the next block */
1783 coap_session_new_token(session, &len, buf);
1784 pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
1785 if (!pdu)
1786 goto fail_resp;
1787
1788 memcpy(p->token, pdu->token, pdu->token_length);
1789 p->token_length = pdu->token_length;
1790
1791 /* Only sent with the first block */
1793
1794 coap_update_option(pdu, block_opt,
1795 coap_encode_var_safe(buf, sizeof(buf),
1796 ((block.num + 1) << 4) |
1797 (block.m << 3) | block.szx),
1798 buf);
1799
1800 if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
1801 goto fail_resp;
1802 }
1803 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY))
1804 goto skip_app_handler;
1805
1806 /* need to put back original token into rcvd */
1808 rcvd->body_offset = block.num*chunk;
1809 rcvd->body_total = size2;
1810 goto call_app_handler;
1811 }
1812 /* need to put back original token into rcvd */
1814 if (session->block_mode & (COAP_BLOCK_SINGLE_BODY)) {
1815 /* Pretend that there is no block */
1816 coap_remove_option(rcvd, block_opt);
1817 if (p->observe_set) {
1819 p->observe_length, p->observe);
1820 }
1821 rcvd->body_data = p->body_data->s;
1822 rcvd->body_length = block.num*chunk + length;
1823 rcvd->body_offset = 0;
1824 rcvd->body_total = rcvd->body_length;
1825 }
1826 else {
1827 rcvd->body_offset = block.num*chunk;
1828 rcvd->body_total = size2;
1829 }
1830 if (context->response_handler) {
1831 if (session->block_mode &
1833 coap_log(LOG_DEBUG, "Client app version of updated PDU\n");
1834 coap_show_pdu(LOG_DEBUG, rcvd);
1835 }
1836 context->response_handler(session, sent, rcvd, rcvd->mid);
1837 }
1838 app_has_response = 1;
1839 /* Set up for the next data body if observing */
1840 p->initial = 1;
1841 memcpy(p->token, p->base_token, p->base_token_length);
1843 if (p->body_data) {
1845 p->body_data = NULL;
1846 }
1847 else {
1848 goto skip_app_handler;
1849 }
1850 }
1851 else {
1852block_mode:
1853 /* need to put back original token into rcvd */
1855 rcvd->body_offset = block.num*chunk;
1856 /* slightly oversize if there is more data */
1857 if (block.m) {
1858 if(size2 > block.num*chunk + length + block.m)
1859 rcvd->body_total = size2;
1860 else
1861 rcvd->body_total = block.num*chunk + length + block.m;
1862 }
1863 else {
1864 rcvd->body_total = block.num*chunk + length;
1865 /* Set up for the next data body if observing */
1866 p->initial = 1;
1867 memcpy(p->token, p->base_token, p->base_token_length);
1869 }
1870 if (context->response_handler) {
1871 coap_log(LOG_DEBUG, "Client app version of updated PDU\n");
1872 coap_show_pdu(LOG_DEBUG, rcvd);
1873 context->response_handler(session, sent, rcvd, rcvd->mid);
1874 }
1875 app_has_response = 1;
1876 }
1877 }
1878 else {
1879 coap_opt_t *obs_opt = coap_check_option(rcvd,
1881 &opt_iter);
1882 if (obs_opt) {
1883 p->observe_length = min(coap_opt_length(obs_opt), 3);
1884 memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
1885 p->observe_set = 1;
1886 }
1887 else {
1888 p->observe_set = 0;
1889 }
1890 }
1891 }
1892 if (!block.m && !p->observe_set) {
1893fail_resp:
1894 /* lg_crcv no longer required - cache it */
1895 coap_ticks(&p->last_used);
1896 }
1897 /* need to put back original token into rcvd */
1899 break;
1900 } /* LL_FOREACH() */
1901
1902 /* Check if receiving a block response and if blocks can be set up */
1903 if (recursive == COAP_RECURSE_OK && !p) {
1904 if (!sent) {
1905 if (coap_get_block(rcvd, COAP_OPTION_BLOCK2, &block)) {
1906 coap_log(LOG_DEBUG, "** %s: large body receive internal issue\n",
1907 coap_session_str(session));
1908 }
1909 }
1910 else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
1911 if (coap_get_block(rcvd, COAP_OPTION_BLOCK2, &block)) {
1912 have_block = 1;
1913 block_opt = COAP_OPTION_BLOCK2;
1914 if (block.num != 0) {
1915 /* Assume random access and just give the single response to app */
1916 size_t length;
1917 const uint8_t *data;
1918 size_t chunk = (size_t)1 << (block.szx + 4);
1919
1920 coap_get_data(rcvd, &length, &data);
1921 rcvd->body_offset = block.num*chunk;
1922 rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
1923 return 0;
1924 }
1925 }
1926 if (have_block) {
1927 coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent);
1928
1929 if (lg_crcv) {
1930 LL_PREPEND(session->lg_crcv, lg_crcv);
1931 return coap_handle_response_get_block(context, session, sent, rcvd,
1933 }
1934 }
1935 }
1936 }
1937 return app_has_response;
1938
1939call_app_handler:
1940 return 0;
1941
1942skip_app_handler:
1943 return 1;
1944}
1945
1946/* Check if lg_xmit generated and update PDU code if so */
1947void
1949 coap_resource_t *resource, coap_string_t *query) {
1950 coap_lg_xmit_t *lg_xmit;
1951 coap_string_t empty = { 0, NULL};
1952
1953 if (response->code == 0)
1954 return;
1955 LL_FOREACH(session->lg_xmit, lg_xmit) {
1956 if (!COAP_PDU_IS_REQUEST(&lg_xmit->pdu) &&
1957 lg_xmit->b.b2.resource == resource &&
1958 coap_string_equal(query ? query : &empty,
1959 lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) {
1960 /* lg_xmit found */
1961 if (lg_xmit->pdu.code == 0) {
1962 lg_xmit->pdu.code = response->code;
1963 return;
1964 }
1965 }
1966 }
1967}
static int add_block_send(uint32_t num, uint32_t *out_blocks, uint32_t *count, uint32_t max_count)
Definition: block.c:912
COAP_STATIC_INLINE int full_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition: block.c:273
#define MAX_BLK_LEN
static int check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks)
Definition: block.c:770
static int update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num)
Definition: block.c:1159
#define min(a, b)
Definition: block.c:14
static int check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num)
Definition: block.c:757
COAP_STATIC_INLINE int block_token_match(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
Definition: block.c:263
unsigned char coap_key_t[4]
Definition: coap_hashkey.h:24
#define coap_hash(String, Length, Result)
Definition: coap_hashkey.h:38
Pulls together all the internal only header files.
int coap_flsll(long long i)
Definition: encode.c:21
coap_tick_t coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now)
Definition: block.c:788
void coap_block_delete_lg_srcv(coap_session_t *session, coap_lg_srcv_t *lg_srcv)
Definition: block.c:877
#define COAP_RBLOCK_CNT
void coap_block_delete_lg_crcv(coap_session_t *session, coap_lg_crcv_t *lg_crcv)
Definition: block.c:862
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)
Definition: block.c:1611
coap_lg_crcv_t * coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu)
Definition: block.c:810
coap_recurse_t
void coap_block_delete_lg_xmit(coap_session_t *session, coap_lg_xmit_t *lg_xmit)
Definition: block.c:890
void coap_check_code_lg_xmit(coap_session_t *session, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *query)
The function checks that the code in a newly formed lg_xmit created by coap_add_data_large_response()...
Definition: block.c:1948
int coap_add_data_large_internal(coap_session_t *session, coap_pdu_t *pdu, coap_resource_t *resource, const coap_string_t *query, int maxage, uint64_t etag, size_t length, const uint8_t *data, coap_release_large_data_t release_func, void *app_ptr)
The function that does all the work for the coap_add_data_large*() functions.
Definition: block.c:333
coap_tick_t coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now)
Definition: block.c:734
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)
Definition: block.c:951
int coap_handle_request_put_block(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *response, coap_resource_t *resource, coap_string_t *uri_path, coap_opt_t *observe, coap_string_t *query, coap_method_handler_t h, int *added_block)
Definition: block.c:1222
int coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd)
Definition: block.c:1447
@ 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:249
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.
Definition: block.c:627
#define COAP_OPT_BLOCK_SZX(opt)
Returns the value of the SZX-field of a Block option opt.
Definition: block.h:57
#define COAP_BLOCK_SINGLE_BODY
Definition: block.h:42
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:127
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:197
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:144
#define COAP_OPT_BLOCK_MORE(opt)
Returns the value of the More-bit of a Block option opt.
Definition: block.h:53
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:18
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:37
#define COAP_OPT_BLOCK_LAST(opt)
Returns the value of the least significant byte of a Block option opt.
Definition: block.h:49
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:66
int coap_cancel_observe(coap_session_t *session, coap_binary_t *token, coap_pdu_type_t type)
Cancel an observe that is being tracked by the client large receive logic.
Definition: block.c:279
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.
Definition: block.c:638
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:1559
#define COAP_BLOCK_USE_LIBCOAP
Definition: block.h:41
#define COAP_EXCHANGE_LIFETIME(s)
The EXCHANGE_LIFETIME definition for the session (s).
Definition: coap_session.h:495
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:127
uint64_t coap_tick_t
This data type represents internal timer ticks with COAP_TICKS_PER_SECOND resolution.
Definition: coap_time.h:122
coap_time_t coap_ticks_to_rt(coap_tick_t t)
Helper function that converts coap ticks to wallclock time.
void(* coap_method_handler_t)(coap_resource_t *, coap_session_t *, const coap_pdu_t *, const coap_string_t *, coap_pdu_t *)
Definition of message handler function.
Definition: resource.h:42
coap_mid_t coap_send_internal(coap_session_t *session, coap_pdu_t *pdu)
Sends a CoAP message to given peer.
Definition: net.c:1154
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:3163
unsigned int coap_encode_var_safe(uint8_t *buf, size_t length, unsigned int value)
Encodes multiple-length byte sequences.
Definition: encode.c:40
unsigned int coap_decode_var_bytes(const uint8_t *buf, size_t length)
Decodes multiple-length byte sequences.
Definition: encode.c:31
uint64_t coap_decode_var_bytes8(const uint8_t *buf, size_t length)
Decodes multiple-length byte sequences.
Definition: encode.c:60
unsigned int coap_encode_var_safe8(uint8_t *buf, size_t length, uint64_t value)
Encodes multiple-length byte sequences.
Definition: encode.c:70
#define COAP_EVENT_PARTIAL_BLOCK
BLOCK2 receive errors.
Definition: coap_event.h:54
void coap_show_pdu(coap_log_t level, const coap_pdu_t *pdu)
Display the contents of the specified pdu.
Definition: coap_debug.c:507
const char * coap_session_str(const coap_session_t *session)
Get session description.
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:152
@ LOG_WARNING
Warning.
Definition: coap_debug.h:56
@ LOG_DEBUG
Debug.
Definition: coap_debug.h:59
#define COAP_OBSERVE_CANCEL
The value COAP_OBSERVE_CANCEL in a GET/FETCH request option COAP_OPTION_OBSERVE indicates that the ob...
Definition: subscribe.h:39
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
Definition: option.c:148
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: 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: option.c:112
#define COAP_OPT_ALL
Pre-defined filter that includes all options.
Definition: option.h:107
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: 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: option.c:248
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t number)
Sets the corresponding entry for number in filter.
Definition: 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:414
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:314
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:282
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:503
#define COAP_PDU_IS_REQUEST(pdu)
#define COAP_OPTION_BLOCK2
Definition: pdu.h:124
const char * coap_response_phrase(unsigned char code)
Returns a human-readable response phrase for the specified CoAP response code.
Definition: pdu.c:729
#define COAP_OPTION_CONTENT_FORMAT
Definition: pdu.h:116
#define COAP_OPTION_SIZE2
Definition: pdu.h:126
#define COAP_OPTION_BLOCK1
Definition: pdu.h:125
int coap_mid_t
coap_mid_t is used to store the CoAP Message ID of a CoAP PDU.
Definition: pdu.h:231
#define COAP_RESPONSE_CODE(N)
Definition: pdu.h:140
#define COAP_RESPONSE_CLASS(C)
Definition: pdu.h:143
#define COAP_OPTION_SIZE1
Definition: pdu.h:129
coap_pdu_type_t
CoAP PDU message type definitions.
Definition: pdu.h:56
#define COAP_MEDIATYPE_TEXT_PLAIN
Definition: pdu.h:189
#define COAP_OPTION_CONTENT_TYPE
Definition: pdu.h:117
size_t coap_add_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, const uint8_t *data)
Adds option of given number to pdu that is passed as first parameter.
Definition: pdu.c:543
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:654
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:155
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:662
#define COAP_INVALID_MID
Indicates an invalid message id.
Definition: pdu.h:234
#define COAP_OPTION_MAXAGE
Definition: pdu.h:119
#define COAP_OPTION_ETAG
Definition: pdu.h:109
#define COAP_OPTION_OBSERVE
Definition: pdu.h:111
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:624
@ COAP_MESSAGE_NON
Definition: pdu.h:58
void coap_session_new_token(coap_session_t *session, size_t *length, uint8_t *token)
Creates a new token for use.
void coap_delete_str_const(coap_str_const_t *string)
Deletes the given const string and releases any memory allocated.
Definition: str.c:53
coap_binary_t * coap_new_binary(size_t size)
Returns a new binary object with at least size bytes storage allocated.
Definition: str.c:67
coap_binary_t * coap_resize_binary(coap_binary_t *binary, size_t new_size)
Resizes the given coap_binary_t object.
Definition: str.c:71
void coap_delete_binary(coap_binary_t *binary)
Deletes the given coap_binary_t object and releases any memory allocated.
Definition: str.c:91
#define coap_string_equal(string1, string2)
Compares the two strings for equality.
Definition: str.h:183
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:15
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:44
void coap_delete_string(coap_string_t *string)
Deletes the given string and releases any memory allocated.
Definition: str.c:40
#define COAP_STATIC_INLINE
Definition: libcoap.h:40
@ COAP_LG_XMIT
Definition: mem.h:50
@ COAP_LG_CRCV
Definition: mem.h:51
@ COAP_LG_SRCV
Definition: mem.h:52
@ COAP_STRING
Definition: mem.h:32
@ COAP_PDU_BUF
Definition: mem.h:40
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().
uint16_t coap_option_num_t
Definition: 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: option.h:26
CoAP binary data definition.
Definition: str.h:50
size_t length
length of binary data
Definition: str.h:51
uint8_t * s
binary data
Definition: str.h:52
Structure of Block options.
Definition: block.h:35
unsigned int num
block number
Definition: block.h:36
unsigned int szx
block size
Definition: block.h:38
unsigned int m
1 if more blocks follow, 0 otherwise
Definition: block.h:37
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
size_t token_length
length of token
uint32_t count
the number of packets sent for payload
coap_binary_t * app_token
original PDU token
uint8_t token[8]
last used token
coap_string_t * query
Associated query for the resource.
uint64_t etag
ETag value.
coap_resource_t * resource
associated resource
coap_time_t maxage_expire
When this entry expires.
Structure to hold large body (many blocks) client receive information.
uint16_t block_option
Block option in use.
uint8_t etag[8]
ETag for block checking.
uint8_t etag_length
ETag length.
uint8_t last_type
Last request type (CON/NON)
uint8_t observe_length
Length of observe data.
uint8_t observe[3]
Observe data (if 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
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
coap_binary_t * app_token
app requesting PDU token
uint8_t token[8]
last used token
uint8_t base_token[8]
established base PDU token
size_t token_length
length of token
size_t base_token_length
length of token
coap_binary_t * body_data
Used for re-assembling entire body.
uint8_t observe_set
Set if this is an observe receive PDU.
size_t total_len
Length as indicated by SIZE2 option.
Structure to hold large body (many blocks) server receive information.
coap_mid_t last_mid
Last received mid for this set of packets.
uint8_t last_token[8]
< list of received blocks
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 last_type
Last request type (CON/NON)
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_release_large_data_t release_func
large data de-alloc function
uint8_t blk_size
large block transmission size
union coap_lg_xmit_t::@1 b
const uint8_t * data
large data ptr
int last_block
last acknowledged block number
size_t offset
large data next offset to transmit
coap_pdu_t pdu
skeletal PDU
size_t length
large data length
coap_tick_t last_used
Last time all data sent or 0.
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
Iterator to run through PDU options.
Definition: option.h:170
coap_option_num_t number
decoded option number
Definition: option.h:172
structure for CoAP PDUs token, if any, follows the fixed size header, then options until payload mark...
uint8_t * token
first byte of token, if any, or options
size_t body_length
Holds body data length.
size_t max_size
maximum size for token, options and payload, or zero for variable size pdu
const uint8_t * body_data
Holds ptr to re-assembled data or NULL.
size_t body_offset
Holds body data offset.
coap_pdu_code_t code
request method (value 1–31) or response code (value 64-255)
uint8_t token_length
length of Token
uint8_t hdr_size
actual size used for protocol-specific header
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 alloc_size
allocated storage for token, options and payload
size_t body_total
Holds body data total size.
coap_pdu_type_t type
message type
Structure to keep track of received blocks.
coap_tick_t last_seen
struct coap_lg_range range[COAP_RBLOCK_CNT]
Abstraction of resource that can be attached to coap_context_t.
coap_str_const_t * uri_path
Request URI Path for this resource.
Abstraction of virtual session that can be attached to coap_context_t (client) or coap_endpoint_t (se...
coap_lg_xmit_t * lg_xmit
list of large transmissions
uint64_t tx_token
Next token number to use.
uint8_t block_mode
Zero or more COAP_BLOCK_ or'd options.
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 string data definition.
Definition: str.h:32
uint8_t * s
string data
Definition: str.h:34
size_t length
length of string
Definition: str.h:33