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
17 unsigned int
coap_opt_block_num(const coap_opt_t * block_opt)18 coap_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) {
29 num = coap_decode_var_bytes(coap_opt_value(block_opt),
30 coap_opt_length(block_opt) - 1);
31 }
32
33 return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
34 }
35
36 int
coap_get_block(const coap_pdu_t * pdu,coap_option_num_t number,coap_block_t * block)37 coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number,
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
65 int
coap_write_block_opt(coap_block_t * block,coap_option_num_t number,coap_pdu_t * pdu,size_t data_length)66 coap_write_block_opt(coap_block_t *block, coap_option_num_t number,
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 */
102 coap_log(LOG_DEBUG,
103 "not enough space, even the smallest block does not fit\n");
104 return -3;
105 }
106 newBlockSize = coap_flsll((long long)avail) - 5;
107 coap_log(LOG_DEBUG,
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
126 int
coap_add_block(coap_pdu_t * pdu,size_t len,const uint8_t * data,unsigned int block_num,unsigned char block_szx)127 coap_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 */
143 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)144 coap_add_data_blocked_response(const coap_pdu_t *request,
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
179 coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT,
180 coap_encode_var_safe(buf, sizeof(buf),
181 media_type),
182 buf);
183
184 if (maxage >= 0) {
185 coap_insert_option(response,
186 COAP_OPTION_MAXAGE,
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,
210 COAP_OPTION_SIZE2,
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,
233 COAP_OPTION_SIZE2,
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
242 error:
243 coap_add_data(response,
244 strlen(coap_response_phrase(response->code)),
245 (const unsigned char *)coap_response_phrase(response->code));
246 }
247
248 void
coap_context_set_block_mode(coap_context_t * context,uint8_t block_mode)249 coap_context_set_block_mode(coap_context_t *context,
250 uint8_t block_mode) {
251 context->block_mode = block_mode &= (COAP_BLOCK_USE_LIBCOAP |
252 COAP_BLOCK_SINGLE_BODY);
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 */
262 COAP_STATIC_INLINE int
block_token_match(const uint8_t * a,size_t alen,const uint8_t * b,size_t blen)263 block_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
272 COAP_STATIC_INLINE int
full_match(const uint8_t * a,size_t alen,const uint8_t * b,size_t blen)273 full_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
278 int
coap_cancel_observe(coap_session_t * session,coap_binary_t * token,coap_pdu_type_t type)279 coap_cancel_observe(coap_session_t *session, coap_binary_t *token,
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)) {
288 coap_log(LOG_DEBUG,
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,
305 cq->base_token_length,
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 }
318 coap_update_option(pdu, COAP_OPTION_OBSERVE,
319 coap_encode_var_safe(buf, sizeof(buf),
320 COAP_OBSERVE_CANCEL),
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
332 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)333 coap_add_data_large_internal(coap_session_t *session,
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)) {
356 coap_log(LOG_DEBUG,
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) {
366 coap_log(LOG_WARNING,
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)) {
372 coap_lg_xmit_t *q;
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 */
391 coap_lg_xmit_t *q;
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 */
425 coap_log(LOG_DEBUG,
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");
438 coap_show_pdu(LOG_DEBUG, pdu);
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");
466 coap_show_pdu(LOG_DEBUG, pdu);
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 */
498 coap_update_option(pdu,
499 COAP_OPTION_SIZE1,
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 }
529 coap_update_option(pdu,
530 COAP_OPTION_SIZE2,
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 }
539 coap_update_option(pdu,
540 COAP_OPTION_ETAG,
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;
547 coap_update_option(pdu,
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) {
574 coap_log(LOG_DEBUG,
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);
582 coap_update_option(pdu,
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) {
603 coap_update_option(pdu,
604 option,
605 coap_encode_var_safe(buf, sizeof(buf),
606 (0 << 4) | (0 << 3) | blk_size), buf);
607 }
608 add_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
617 fail:
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
626 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)627 coap_add_data_large_request(coap_session_t *session,
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
637 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)638 coap_add_data_large_response(coap_resource_t *resource,
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
673 coap_insert_option(response, COAP_OPTION_CONTENT_TYPE,
674 coap_encode_var_safe(buf, sizeof(buf),
675 media_type),
676 buf);
677
678 if (maxage >= 0) {
679 coap_insert_option(response,
680 COAP_OPTION_MAXAGE,
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
726 error:
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
733 coap_tick_t
coap_block_check_lg_crcv_timeouts(coap_session_t * session,coap_tick_t now)734 coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now) {
735 coap_lg_crcv_t *p;
736 coap_lg_crcv_t *q;
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
756 static int
check_if_received_block(coap_rblock_t * rec_blocks,uint32_t block_num)757 check_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
769 static int
check_all_blocks_in(coap_rblock_t * rec_blocks,size_t total_blocks)770 check_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
787 coap_tick_t
coap_block_check_lg_srcv_timeouts(coap_session_t * session,coap_tick_t now)788 coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now) {
789 coap_lg_srcv_t *p;
790 coap_lg_srcv_t *q;
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
809 coap_lg_crcv_t *
coap_block_new_lg_crcv(coap_session_t * session,coap_pdu_t * pdu)810 coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) {
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));
824 lg_crcv->pdu.token = coap_malloc_type(COAP_PDU_BUF,
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 */
856 coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1);
857
858 return lg_crcv;
859 }
860
861 void
coap_block_delete_lg_crcv(coap_session_t * session,coap_lg_crcv_t * lg_crcv)862 coap_block_delete_lg_crcv(coap_session_t *session,
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);
869 coap_free_type(COAP_STRING, lg_crcv->body_data);
870 coap_log(LOG_DEBUG, "** %s: lg_crcv %p released\n",
871 coap_session_str(session), (void*)lg_crcv);
872 coap_delete_binary(lg_crcv->app_token);
873 coap_free_type(COAP_LG_CRCV, lg_crcv);
874 }
875
876 void
coap_block_delete_lg_srcv(coap_session_t * session,coap_lg_srcv_t * lg_srcv)877 coap_block_delete_lg_srcv(coap_session_t *session,
878 coap_lg_srcv_t *lg_srcv) {
879 if (lg_srcv == NULL)
880 return;
881
882 coap_delete_str_const(lg_srcv->uri_path);
883 coap_free_type(COAP_STRING, lg_srcv->body_data);
884 coap_log(LOG_DEBUG, "** %s: lg_srcv %p released\n",
885 coap_session_str(session), (void*)lg_srcv);
886 coap_free_type(COAP_LG_SRCV, lg_srcv);
887 }
888
889 void
coap_block_delete_lg_xmit(coap_session_t * session,coap_lg_xmit_t * lg_xmit)890 coap_block_delete_lg_xmit(coap_session_t *session,
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))
902 coap_delete_binary(lg_xmit->b.b1.app_token);
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);
908 coap_free_type(COAP_LG_XMIT, lg_xmit);
909 }
910
911 static int
add_block_send(uint32_t num,uint32_t * out_blocks,uint32_t * count,uint32_t max_count)912 add_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 */
950 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)951 coap_handle_request_send_block(coap_session_t *session,
952 coap_pdu_t *pdu,
953 coap_pdu_t *response,
954 coap_resource_t *resource,
955 coap_string_t *query) {
956 coap_lg_xmit_t *p;
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) {
1000 coap_log(LOG_DEBUG,
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;
1013 coap_log(LOG_DEBUG,
1014 "new Block size is %u, block number %u completed\n",
1015 1 << (block.szx + 4), block.num);
1016 } else {
1017 coap_log(LOG_DEBUG,
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)
1067 coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
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 */
1081 coap_option_iterator_init(&p->pdu, &opt_iter, COAP_OPT_ALL);
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,
1096 coap_encode_var_safe(buf,
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 }
1118 if (!coap_update_option(out_pdu, COAP_OPTION_MAXAGE,
1119 coap_encode_var_safe8(buf,
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
1140 fail:
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
1147 skip_app_handler:
1148 return 1;
1149
1150 internal_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
1158 static int
update_received_blocks(coap_rblock_t * rec_blocks,uint32_t block_num)1159 update_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 */
1221 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)1222 coap_handle_request_put_block(coap_context_t *context,
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,
1230 coap_method_handler_t h,
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,
1250 COAP_OPTION_SIZE1,
1251 &opt_iter);
1252 coap_opt_t *fmt_opt = coap_check_option(pdu,
1253 COAP_OPTION_CONTENT_FORMAT,
1254 &opt_iter);
1255 uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
1256 coap_opt_length(fmt_opt)) :
1257 COAP_MEDIATYPE_TEXT_PLAIN;
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)) {
1281 p = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t));
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);
1318 p->last_token_length = 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)) {
1324 coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session);
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 }
1337 if (!check_all_blocks_in(&p->rec_blocks,
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) {
1362 coap_update_option(pdu, COAP_OPTION_OBSERVE,
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");
1371 coap_show_pdu(LOG_DEBUG, pdu);
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
1419 free_lg_recv:
1420 LL_DELETE(session->lg_srcv, p);
1421 coap_block_delete_lg_srcv(session, p);
1422 goto skip_app_handler;
1423 }
1424 }
1425 call_app_handler:
1426 return 0;
1427
1428 skip_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 */
1446 int
coap_handle_response_send_block(coap_session_t * session,coap_pdu_t * rcvd)1447 coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *rcvd) {
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)) {
1462 coap_log(LOG_DEBUG,
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;
1475 coap_log(LOG_DEBUG,
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 }
1536 fail_body:
1537 /* need to put back original token into rcvd */
1538 if (p->b.b1.app_token)
1539 coap_update_token(rcvd, p->b.b1.app_token->length,
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 */
1558 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)1559 coap_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 */
1610 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)1611 coap_handle_response_get_block(coap_context_t *context,
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;
1639 coap_opt_t *size_opt = coap_check_option(rcvd, COAP_OPTION_SIZE2,
1640 &opt_iter);
1641 size_t size2 = size_opt ?
1642 coap_decode_var_bytes(coap_opt_value(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,
1654 COAP_OPTION_CONTENT_FORMAT,
1655 &opt_iter);
1656 uint16_t fmt = fmt_opt ?
1657 coap_decode_var_bytes(coap_opt_value(fmt_opt),
1658 coap_opt_length(fmt_opt)) :
1659 COAP_MEDIATYPE_TEXT_PLAIN;
1660 coap_opt_t *etag_opt = coap_check_option(rcvd,
1661 COAP_OPTION_ETAG,
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
1701 coap_log(LOG_WARNING,
1702 "Data body updated during receipt - new request started\n");
1703 if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
1704 coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session);
1705
1706 p->initial = 1;
1707 coap_free_type(COAP_STRING, p->body_data);
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 }
1741 coap_log(LOG_DEBUG,
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,
1746 COAP_OPTION_OBSERVE,
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)) {
1760 coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session);
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 }
1773 if (!check_all_blocks_in(&p->rec_blocks,
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 */
1792 coap_remove_option(pdu, COAP_OPTION_OBSERVE);
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 */
1807 coap_update_token(rcvd, p->app_token->length, p->app_token->s);
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 */
1813 coap_update_token(rcvd, p->app_token->length, p->app_token->s);
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) {
1818 coap_update_option(rcvd, COAP_OPTION_OBSERVE,
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 &
1832 (COAP_BLOCK_SINGLE_BODY)) {
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);
1842 p->token_length = p->base_token_length;
1843 if (p->body_data) {
1844 coap_free_type(COAP_STRING, p->body_data);
1845 p->body_data = NULL;
1846 }
1847 else {
1848 goto skip_app_handler;
1849 }
1850 }
1851 else {
1852 block_mode:
1853 /* need to put back original token into rcvd */
1854 coap_update_token(rcvd, p->app_token->length, p->app_token->s);
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);
1868 p->token_length = 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,
1880 COAP_OPTION_OBSERVE,
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) {
1893 fail_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 */
1898 coap_update_token(rcvd, p->app_token->length, p->app_token->s);
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,
1932 COAP_RECURSE_NO);
1933 }
1934 }
1935 }
1936 }
1937 return app_has_response;
1938
1939 call_app_handler:
1940 return 0;
1941
1942 skip_app_handler:
1943 return 1;
1944 }
1945
1946 /* Check if lg_xmit generated and update PDU code if so */
1947 void
coap_check_code_lg_xmit(coap_session_t * session,coap_pdu_t * response,coap_resource_t * resource,coap_string_t * query)1948 coap_check_code_lg_xmit(coap_session_t *session, coap_pdu_t *response,
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 }
1968