• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* coap_block.c -- block transfer
2  *
3  * Copyright (C) 2010--2012,2015-2023 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 /**
12  * @file coap_block.c
13  * @brief CoAP Block handling
14  */
15 
16 #include "coap3/coap_internal.h"
17 
18 #ifndef min
19 #define min(a,b) ((a) < (b) ? (a) : (b))
20 #endif
21 
22 #define STATE_TOKEN_BASE(t) ((t) & 0xffffffffffffULL)
23 #define STATE_TOKEN_RETRY(t) ((uint64_t)(t) >> 48)
24 #define STATE_TOKEN_FULL(t,r) (STATE_TOKEN_BASE(t) + ((uint64_t)(r) << 48))
25 
26 #if COAP_Q_BLOCK_SUPPORT
27 int
coap_q_block_is_supported(void)28 coap_q_block_is_supported(void) {
29   return 1;
30 }
31 #else /* ! COAP_Q_BLOCK_SUPPORT */
32 int
coap_q_block_is_supported(void)33 coap_q_block_is_supported(void) {
34   return 0;
35 }
36 #endif /* ! COAP_Q_BLOCK_SUPPORT */
37 
38 unsigned int
coap_opt_block_num(const coap_opt_t * block_opt)39 coap_opt_block_num(const coap_opt_t *block_opt) {
40   unsigned int num = 0;
41   uint16_t len;
42 
43   len = coap_opt_length(block_opt);
44 
45   if (len == 0) {
46     return 0;
47   }
48 
49   if (len > 1) {
50     num = coap_decode_var_bytes(coap_opt_value(block_opt),
51                                 coap_opt_length(block_opt) - 1);
52   }
53 
54   return (num << 4) | ((COAP_OPT_BLOCK_END_BYTE(block_opt) & 0xF0) >> 4);
55 }
56 
57 int
coap_get_block_b(const coap_session_t * session,const coap_pdu_t * pdu,coap_option_num_t number,coap_block_b_t * block)58 coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu,
59                  coap_option_num_t number, coap_block_b_t *block) {
60   coap_opt_iterator_t opt_iter;
61   coap_opt_t *option;
62 
63   assert(block);
64   memset(block, 0, sizeof(coap_block_b_t));
65 
66   if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) {
67     unsigned int num;
68 
69     if (COAP_OPT_BLOCK_MORE(option))
70       block->m = 1;
71     block->aszx = block->szx = COAP_OPT_BLOCK_SZX(option);
72     if (block->szx == 7) {
73       size_t length;
74       const uint8_t *data;
75 
76       if (session == NULL || COAP_PROTO_NOT_RELIABLE(session->proto) ||
77           !(session->csm_bert_rem_support && session->csm_bert_loc_support))
78         /* No BERT support */
79         return 0;
80 
81       block->szx = 6; /* BERT is 1024 block chunks */
82       block->bert = 1;
83       if (coap_get_data(pdu, &length, &data)) {
84         if (block->m && (length % 1024) != 0) {
85           coap_log_debug("block: Oversized packet - reduced to %zu from %zu\n",
86                          length - (length % 1024), length);
87           length -= length % 1024;
88         }
89         block->chunk_size = (uint32_t)length;
90       } else
91         block->chunk_size = 0;
92     } else {
93       block->chunk_size = (size_t)1 << (block->szx + 4);
94     }
95     block->defined = 1;
96 
97     /* The block number is at most 20 bits, so values above 2^20 - 1
98      * are illegal. */
99     num = coap_opt_block_num(option);
100     if (num > 0xFFFFF) {
101       return 0;
102     }
103     block->num = num;
104     return 1;
105   }
106 
107   return 0;
108 }
109 
110 int
coap_get_block(const coap_pdu_t * pdu,coap_option_num_t number,coap_block_t * block)111 coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number,
112                coap_block_t *block) {
113   coap_block_b_t block_b;
114 
115   assert(block);
116   memset(block, 0, sizeof(coap_block_t));
117 
118   if (coap_get_block_b(NULL, pdu, number, &block_b)) {
119     block->num = block_b.num;
120     block->m   = block_b.m;
121     block->szx = block_b.szx;
122     return 1;
123   }
124   return 0;
125 }
126 
127 static int
setup_block_b(coap_session_t * session,coap_pdu_t * pdu,coap_block_b_t * block,unsigned int num,unsigned int blk_size,size_t total)128 setup_block_b(coap_session_t *session, coap_pdu_t *pdu, coap_block_b_t *block,
129               unsigned int num,
130               unsigned int blk_size, size_t total) {
131   size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
132   size_t avail = pdu->max_size - token_options;
133   unsigned int start = num << (blk_size + 4);
134   unsigned int can_use_bert = block->defined == 0 || block->bert;
135 
136   assert(start <= total);
137   memset(block, 0, sizeof(*block));
138   block->num = num;
139   block->szx = block->aszx = blk_size;
140   if (can_use_bert && blk_size == 6 && avail >= 1024 && session != NULL &&
141       COAP_PROTO_RELIABLE(session->proto) &&
142       session->csm_bert_rem_support && session->csm_bert_loc_support) {
143     block->bert = 1;
144     block->aszx = 7;
145     block->chunk_size = (uint32_t)((avail / 1024) * 1024);
146   } else {
147     block->chunk_size = (size_t)1 << (blk_size + 4);
148     if (avail < block->chunk_size && (total - start) >= avail) {
149       /* Need to reduce block size */
150       unsigned int szx;
151       int new_blk_size;
152 
153       if (avail < 16) {         /* bad luck, this is the smallest block size */
154         coap_log_debug("not enough space, even the smallest block does not fit (1)\n");
155         return 0;
156       }
157       new_blk_size = coap_flsll((long long)avail) - 5;
158       coap_log_debug("decrease block size for %zu to %d\n", avail, new_blk_size);
159       szx = block->szx;
160       block->szx = new_blk_size;
161       block->num <<= szx - block->szx;
162       block->chunk_size = (size_t)1 << (new_blk_size + 4);
163     }
164   }
165   block->m = block->chunk_size < total - start;
166   return 1;
167 }
168 
169 int
coap_write_block_opt(coap_block_t * block,coap_option_num_t number,coap_pdu_t * pdu,size_t data_length)170 coap_write_block_opt(coap_block_t *block, coap_option_num_t number,
171                      coap_pdu_t *pdu, size_t data_length) {
172   size_t start;
173   unsigned char buf[4];
174   coap_block_b_t block_b;
175 
176   assert(pdu);
177 
178   start = block->num << (block->szx + 4);
179   if (block->num != 0 && data_length <= start) {
180     coap_log_debug("illegal block requested\n");
181     return -2;
182   }
183 
184   assert(pdu->max_size > 0);
185 
186   block_b.defined = 1;
187   block_b.bert = 0;
188   if (!setup_block_b(NULL, pdu, &block_b, block->num,
189                      block->szx, data_length))
190     return -3;
191 
192   /* to re-encode the block option */
193   coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
194                                                        ((block_b.num << 4) |
195                                                         (block_b.m << 3) |
196                                                         block_b.szx)),
197                      buf);
198 
199   return 1;
200 }
201 
202 int
coap_write_block_b_opt(coap_session_t * session,coap_block_b_t * block,coap_option_num_t number,coap_pdu_t * pdu,size_t data_length)203 coap_write_block_b_opt(coap_session_t *session, coap_block_b_t *block,
204                        coap_option_num_t number,
205                        coap_pdu_t *pdu, size_t data_length) {
206   size_t start;
207   unsigned char buf[4];
208 
209   assert(pdu);
210 
211   start = block->num << (block->szx + 4);
212   if (block->num != 0 && data_length <= start) {
213     coap_log_debug("illegal block requested\n");
214     return -2;
215   }
216 
217   assert(pdu->max_size > 0);
218 
219   if (!setup_block_b(session, pdu, block, block->num,
220                      block->szx, data_length))
221     return -3;
222 
223   /* to re-encode the block option */
224   coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
225                                                        ((block->num << 4) |
226                                                         (block->m << 3) |
227                                                         block->aszx)),
228                      buf);
229 
230   return 1;
231 }
232 
233 int
coap_add_block(coap_pdu_t * pdu,size_t len,const uint8_t * data,unsigned int block_num,unsigned char block_szx)234 coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data,
235                unsigned int block_num, unsigned char block_szx) {
236   unsigned int start;
237   start = block_num << (block_szx + 4);
238 
239   if (len <= start)
240     return 0;
241 
242   return coap_add_data(pdu,
243                        min(len - start, ((size_t)1 << (block_szx + 4))),
244                        data + start);
245 }
246 
247 int
coap_add_block_b_data(coap_pdu_t * pdu,size_t len,const uint8_t * data,coap_block_b_t * block)248 coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data,
249                       coap_block_b_t *block) {
250   unsigned int start = block->num << (block->szx + 4);
251   size_t max_size;
252 
253   if (len <= start)
254     return 0;
255 
256   if (block->bert) {
257     size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
258     max_size = ((pdu->max_size - token_options) / 1024) * 1024;
259   } else {
260     max_size = (size_t)1 << (block->szx + 4);
261   }
262   block->chunk_size = (uint32_t)max_size;
263 
264   return coap_add_data(pdu,
265                        min(len - start, max_size),
266                        data + start);
267 }
268 
269 /*
270  * Note that the COAP_OPTION_ have to be added in the correct order
271  */
272 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)273 coap_add_data_blocked_response(const coap_pdu_t *request,
274                                coap_pdu_t *response,
275                                uint16_t media_type,
276                                int maxage,
277                                size_t length,
278                                const uint8_t *data
279                               ) {
280   coap_key_t etag;
281   unsigned char buf[4];
282   coap_block_t block2;
283   int block2_requested = 0;
284 
285   memset(&block2, 0, sizeof(block2));
286   /*
287    * Need to check that a valid block is getting asked for so that the
288    * correct options are put into the PDU.
289    */
290   if (request) {
291     if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
292       block2_requested = 1;
293       if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) {
294         coap_log_debug("Illegal block requested (%d > last = %zu)\n",
295                        block2.num,
296                        length >> (block2.szx + 4));
297         response->code = COAP_RESPONSE_CODE(400);
298         goto error;
299       }
300     }
301   }
302   response->code = COAP_RESPONSE_CODE(205);
303 
304   /* add etag for the resource */
305   memset(etag, 0, sizeof(etag));
306   coap_hash(data, length, etag);
307   coap_insert_option(response, COAP_OPTION_ETAG, sizeof(etag), etag);
308 
309   coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT,
310                      coap_encode_var_safe(buf, sizeof(buf),
311                                           media_type),
312                      buf);
313 
314   if (maxage >= 0) {
315     coap_insert_option(response,
316                        COAP_OPTION_MAXAGE,
317                        coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
318   }
319 
320   if (block2_requested) {
321     int res;
322 
323     res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
324 
325     switch (res) {
326     case -2:                        /* illegal block (caught above) */
327       response->code = COAP_RESPONSE_CODE(400);
328       goto error;
329     case -1:                        /* should really not happen */
330       assert(0);
331     /* fall through if assert is a no-op */
332     case -3:                        /* cannot handle request */
333       response->code = COAP_RESPONSE_CODE(500);
334       goto error;
335     default:                        /* everything is good */
336       ;
337     }
338 
339     coap_add_option_internal(response,
340                              COAP_OPTION_SIZE2,
341                              coap_encode_var_safe8(buf, sizeof(buf), length),
342                              buf);
343 
344     coap_add_block(response, length, data,
345                    block2.num, block2.szx);
346     return;
347   }
348 
349   /*
350    * Block2 not requested
351    */
352   if (!coap_add_data(response, length, data)) {
353     /*
354      * Insufficient space to add in data - use block mode
355      * set initial block size, will be lowered by
356      * coap_write_block_opt() automatically
357      */
358     block2.num = 0;
359     block2.szx = 6;
360     coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
361 
362     coap_add_option_internal(response,
363                              COAP_OPTION_SIZE2,
364                              coap_encode_var_safe8(buf, sizeof(buf), length),
365                              buf);
366 
367     coap_add_block(response, length, data,
368                    block2.num, block2.szx);
369   }
370   return;
371 
372 error:
373   coap_add_data(response,
374                 strlen(coap_response_phrase(response->code)),
375                 (const unsigned char *)coap_response_phrase(response->code));
376 }
377 
378 void
coap_context_set_block_mode(coap_context_t * context,uint8_t block_mode)379 coap_context_set_block_mode(coap_context_t *context,
380                             uint8_t block_mode) {
381   context->block_mode = (block_mode & (COAP_BLOCK_USE_LIBCOAP |
382                                        COAP_BLOCK_SINGLE_BODY |
383 #if COAP_Q_BLOCK_SUPPORT
384                                        COAP_BLOCK_TRY_Q_BLOCK |
385                                        COAP_BLOCK_USE_M_Q_BLOCK |
386 #endif /* COAP_Q_BLOCK_SUPPORT */
387                                        COAP_BLOCK_NO_PREEMPTIVE_RTAG));
388   if (!(block_mode & COAP_BLOCK_USE_LIBCOAP))
389     context->block_mode = 0;
390 #if ! COAP_Q_BLOCK_SUPPORT
391   if (block_mode & (COAP_BLOCK_TRY_Q_BLOCK|COAP_BLOCK_USE_M_Q_BLOCK))
392     coap_log_debug("Q-Block support not compiled in - ignored\n");
393 #endif /* ! COAP_Q_BLOCK_SUPPORT */
394 }
395 
396 COAP_STATIC_INLINE int
full_match(const uint8_t * a,size_t alen,const uint8_t * b,size_t blen)397 full_match(const uint8_t *a, size_t alen,
398            const uint8_t *b, size_t blen) {
399   return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
400 }
401 
402 #if COAP_CLIENT_SUPPORT
403 
404 int
coap_cancel_observe(coap_session_t * session,coap_binary_t * token,coap_pdu_type_t type)405 coap_cancel_observe(coap_session_t *session, coap_binary_t *token,
406                     coap_pdu_type_t type) {
407   coap_lg_crcv_t *lg_crcv, *q;
408 
409   assert(session);
410   if (!session)
411     return 0;
412 
413   if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
414     coap_log_debug("** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n",
415                    coap_session_str(session));
416     return 0;
417   }
418 
419   LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) {
420     if (lg_crcv->observe_set) {
421       if ((!token && !lg_crcv->app_token->length) || (token &&
422                                                       coap_binary_equal(token, lg_crcv->app_token))) {
423         uint8_t buf[8];
424         coap_mid_t mid;
425         size_t size;
426         const uint8_t *data;
427 #if COAP_Q_BLOCK_SUPPORT
428         coap_block_b_t block;
429         int using_q_block1 = coap_get_block_b(session, &lg_crcv->pdu,
430                                               COAP_OPTION_Q_BLOCK1, &block);
431 #endif /* COAP_Q_BLOCK_SUPPORT */
432         coap_bin_const_t *otoken = lg_crcv->obs_token ?
433                                    lg_crcv->obs_token[0] ?
434                                    lg_crcv->obs_token[0] :
435                                    (coap_bin_const_t *)lg_crcv->app_token :
436                                    (coap_bin_const_t *)lg_crcv->app_token;
437         coap_pdu_t *pdu = coap_pdu_duplicate(&lg_crcv->pdu,
438                                              session,
439                                              otoken->length,
440                                              otoken->s,
441                                              NULL);
442 
443         lg_crcv->observe_set = 0;
444         if (pdu == NULL)
445           return 0;
446 #if COAP_Q_BLOCK_SUPPORT
447         if (pdu->code == COAP_REQUEST_CODE_FETCH && using_q_block1) {
448           /* Have to make sure all gets through in case of packet loss */
449           pdu->type = COAP_MESSAGE_CON;
450         } else {
451           /* Need to make sure that this is the correct requested type */
452           pdu->type = type;
453         }
454 #else /* ! COAP_Q_BLOCK_SUPPORT */
455         /* Need to make sure that this is the correct requested type */
456         pdu->type = type;
457 #endif /* ! COAP_Q_BLOCK_SUPPORT */
458 
459         coap_update_option(pdu, COAP_OPTION_OBSERVE,
460                            coap_encode_var_safe(buf, sizeof(buf),
461                                                 COAP_OBSERVE_CANCEL),
462                            buf);
463         if (coap_get_data(&lg_crcv->pdu, &size, &data))
464           coap_add_data_large_request(session, pdu, size, data, NULL, NULL);
465 
466         /*
467          * Need to fix lg_xmit stateless token as using tokens from
468          * observe setup
469          */
470         if (pdu->lg_xmit)
471           pdu->lg_xmit->b.b1.state_token = lg_crcv->state_token;
472 
473 #if COAP_Q_BLOCK_SUPPORT
474         /* See if large xmit using Q-Block1 (but not testing Q-Block1) */
475         if (using_q_block1) {
476           mid = coap_send_q_block1(session, block, pdu, COAP_SEND_INC_PDU);
477         } else {
478           mid = coap_send_internal(session, pdu);
479         }
480 #else /* ! COAP_Q_BLOCK_SUPPORT */
481         mid = coap_send_internal(session, pdu);
482 #endif /* ! COAP_Q_BLOCK_SUPPORT */
483         if (mid != COAP_INVALID_MID)
484           return 1;
485         break;
486       }
487     }
488   }
489   return 0;
490 }
491 
492 #if COAP_OSCORE_SUPPORT
493 coap_mid_t
coap_retransmit_oscore_pdu(coap_session_t * session,coap_pdu_t * pdu,coap_opt_t * echo)494 coap_retransmit_oscore_pdu(coap_session_t *session,
495                            coap_pdu_t *pdu,
496                            coap_opt_t *echo) {
497   coap_lg_crcv_t *lg_crcv;
498   uint64_t token_match =
499       STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s,
500                                               pdu->actual_token.length));
501   uint8_t ltoken[8];
502   size_t ltoken_len;
503   uint64_t token;
504   const uint8_t *data;
505   size_t data_len;
506   coap_pdu_t *resend_pdu;
507   coap_block_b_t block;
508 
509   LL_FOREACH(session->lg_crcv, lg_crcv) {
510     if (token_match != STATE_TOKEN_BASE(lg_crcv->state_token) &&
511         !coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) {
512       /* try out the next one */
513       continue;
514     }
515 
516     /* lg_crcv found */
517 
518     /* Re-send request with new token */
519     token = STATE_TOKEN_FULL(lg_crcv->state_token,
520                              ++lg_crcv->retry_counter);
521     ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
522     /* There could be a Block option in pdu */
523     resend_pdu = coap_pdu_duplicate(pdu, session, ltoken_len,
524                                     ltoken, NULL);
525     if (!resend_pdu)
526       goto error;
527     if (echo) {
528       coap_insert_option(resend_pdu, COAP_OPTION_ECHO, coap_opt_length(echo),
529                          coap_opt_value(echo));
530     }
531     if (coap_get_data(&lg_crcv->pdu, &data_len, &data)) {
532       if (coap_get_block_b(session, resend_pdu, COAP_OPTION_BLOCK1, &block)) {
533         if (data_len > block.chunk_size && block.chunk_size != 0) {
534           data_len = block.chunk_size;
535         }
536       }
537       coap_add_data(resend_pdu, data_len, data);
538     }
539 
540     return coap_send_internal(session, resend_pdu);
541   }
542 error:
543   return COAP_INVALID_MID;
544 }
545 #endif /* COAP_OSCORE_SUPPORT */
546 #endif /* COAP_CLIENT_SUPPORT */
547 
548 #if COAP_SERVER_SUPPORT
549 /*
550  * Find the response lg_xmit
551  */
552 coap_lg_xmit_t *
coap_find_lg_xmit_response(const coap_session_t * session,const coap_pdu_t * request,const coap_resource_t * resource,const coap_string_t * query)553 coap_find_lg_xmit_response(const coap_session_t *session,
554                            const coap_pdu_t *request,
555                            const coap_resource_t *resource,
556                            const coap_string_t *query) {
557   coap_lg_xmit_t *lg_xmit;
558   coap_opt_iterator_t opt_iter;
559   coap_opt_t *rtag_opt = coap_check_option(request,
560                                            COAP_OPTION_RTAG,
561                                            &opt_iter);
562   size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
563   const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
564 
565   LL_FOREACH(session->lg_xmit, lg_xmit) {
566     static coap_string_t empty = { 0, NULL};
567 
568     if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu) ||
569         resource != lg_xmit->b.b2.resource ||
570         request->code != lg_xmit->b.b2.request_method ||
571         !coap_string_equal(query ? query : &empty,
572                            lg_xmit->b.b2.query ?
573                            lg_xmit->b.b2.query : &empty)) {
574       /* try out the next one */
575       continue;
576     }
577     /* lg_xmit is a response */
578     if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) {
579       if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1))
580         continue;
581       if (lg_xmit->b.b2.rtag_length != rtag_length ||
582           memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0)
583         continue;
584     }
585     return lg_xmit;
586   }
587   return NULL;
588 }
589 #endif /* COAP_SERVER_SUPPORT */
590 
591 static int
coap_add_data_large_internal(coap_session_t * session,const coap_pdu_t * request,coap_pdu_t * pdu,coap_resource_t * resource,const coap_string_t * query,int maxage,uint64_t etag,size_t length,const uint8_t * data,coap_release_large_data_t release_func,void * app_ptr,int single_request,coap_pdu_code_t request_method)592 coap_add_data_large_internal(coap_session_t *session,
593                              const coap_pdu_t *request,
594                              coap_pdu_t *pdu,
595                              coap_resource_t *resource,
596                              const coap_string_t *query,
597                              int maxage,
598                              uint64_t etag,
599                              size_t length,
600                              const uint8_t *data,
601                              coap_release_large_data_t release_func,
602                              void *app_ptr,
603                              int single_request, coap_pdu_code_t request_method) {
604 
605   ssize_t avail;
606   coap_block_b_t block;
607 #if COAP_Q_BLOCK_SUPPORT
608   coap_block_b_t alt_block;
609 #endif /* COAP_Q_BLOCK_SUPPORT */
610   size_t chunk;
611   coap_lg_xmit_t *lg_xmit = NULL;
612   uint8_t buf[8];
613   int have_block_defined = 0;
614   uint8_t blk_size;
615   uint16_t option;
616   size_t token_options;
617   coap_opt_t *opt;
618   coap_opt_iterator_t opt_iter;
619 #if COAP_Q_BLOCK_SUPPORT
620   uint16_t alt_option;
621 #endif /* COAP_Q_BLOCK_SUPPORT */
622 
623   assert(pdu);
624   if (pdu->data) {
625     coap_log_warn("coap_add_data_large: PDU already contains data\n");
626     if (release_func)
627       release_func(session, app_ptr);
628     return 0;
629   }
630 
631   if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
632     coap_log_debug("** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n",
633                    coap_session_str(session));
634     goto add_data;
635   }
636 
637   /* A lot of the reliable code assumes type is CON */
638   if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON)
639     pdu->type = COAP_MESSAGE_CON;
640 
641   /* Block NUM max 20 bits and block size is "2**(SZX + 4)"
642      and using SZX max of 6 gives maximum size = 1,073,740,800
643      CSM Max-Message-Size theoretical maximum = 4,294,967,295
644      So, if using blocks, we are limited to 1,073,740,800.
645    */
646 #define MAX_BLK_LEN (((1 << 20) - 1) * (1 << (6 + 4)))
647 
648   if (length > MAX_BLK_LEN) {
649     coap_log_warn("Size of large buffer restricted to 0x%x bytes\n", MAX_BLK_LEN);
650     length = MAX_BLK_LEN;
651   }
652 
653   /* Determine the block size to use, adding in sensible options if needed */
654   if (COAP_PDU_IS_REQUEST(pdu)) {
655     coap_lg_xmit_t *q;
656 
657 #if COAP_Q_BLOCK_SUPPORT
658     if (session->block_mode & (COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_TRY_Q_BLOCK)) {
659       option = COAP_OPTION_Q_BLOCK1;
660       alt_option = COAP_OPTION_BLOCK1;
661     } else {
662       option = COAP_OPTION_BLOCK1;
663       alt_option = COAP_OPTION_Q_BLOCK1;
664     }
665 #else /* ! COAP_Q_BLOCK_SUPPORT */
666     if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
667       coap_remove_option(pdu, COAP_OPTION_Q_BLOCK1);
668     }
669     option = COAP_OPTION_BLOCK1;
670 #endif /* ! COAP_Q_BLOCK_SUPPORT */
671 
672     /* See if this token is already in use for large bodies (unlikely) */
673     LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
674       if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) {
675         /* Unfortunately need to free this off as potential size change */
676         LL_DELETE(session->lg_xmit, lg_xmit);
677         coap_block_delete_lg_xmit(session, lg_xmit);
678         lg_xmit = NULL;
679         coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
680         break;
681       }
682     }
683   } else {
684     /* Have to assume that it is a response even if code is 0.00 */
685     assert(resource);
686 #if COAP_Q_BLOCK_SUPPORT
687     if (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) {
688       option = COAP_OPTION_Q_BLOCK2;
689       alt_option = COAP_OPTION_BLOCK2;
690     } else {
691       option = COAP_OPTION_BLOCK2;
692       alt_option = COAP_OPTION_Q_BLOCK2;
693     }
694 #else /* ! COAP_Q_BLOCK_SUPPORT */
695     if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
696       coap_remove_option(pdu, COAP_OPTION_Q_BLOCK2);
697     }
698     option = COAP_OPTION_BLOCK2;
699 #endif /* ! COAP_Q_BLOCK_SUPPORT */
700 #if COAP_SERVER_SUPPORT
701     /*
702      * Check if resource+query+rtag is already in use for large bodies
703      * (unlikely)
704      */
705     lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
706     if (lg_xmit) {
707       /* Unfortunately need to free this off as potential size change */
708       LL_DELETE(session->lg_xmit, lg_xmit);
709       coap_block_delete_lg_xmit(session, lg_xmit);
710       lg_xmit = NULL;
711       coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
712     }
713 #endif /* COAP_SERVER_SUPPORT */
714   }
715 #if COAP_OSCORE_SUPPORT
716   if (session->oscore_encryption) {
717     /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */
718     if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu))
719       goto fail;
720   }
721 #endif /* COAP_OSCORE_SUPPORT */
722 
723   token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
724   avail = pdu->max_size - token_options;
725   /* There may be a response with Echo option */
726   avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40);
727 #if COAP_OSCORE_SUPPORT
728   avail -= coap_oscore_overhead(session, pdu);
729 #endif /* COAP_OSCORE_SUPPORT */
730   /* May need token of length 8, so account for this */
731   avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0;
732   blk_size = coap_flsll((long long)avail) - 4 - 1;
733   if (blk_size > 6)
734     blk_size = 6;
735 
736   /* see if BlockX defined - if so update blk_size as given by app */
737   if (coap_get_block_b(session, pdu, option, &block)) {
738     if (block.szx < blk_size)
739       blk_size = block.szx;
740     have_block_defined = 1;
741   }
742 #if COAP_Q_BLOCK_SUPPORT
743   /* see if alternate BlockX defined */
744   if (coap_get_block_b(session, pdu, alt_option, &alt_block)) {
745     if (have_block_defined) {
746       /* Cannot have both options set */
747       coap_log_warn("Both BlockX and Q-BlockX cannot be set at the same time\n");
748       coap_remove_option(pdu, alt_option);
749     } else {
750       block = alt_block;
751       if (block.szx < blk_size)
752         blk_size = block.szx;
753       have_block_defined = 1;
754       option = alt_option;
755     }
756   }
757 #endif /* COAP_Q_BLOCK_SUPPORT */
758 
759   if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) {
760     /* bad luck, this is the smallest block size */
761     coap_log_debug("not enough space, even the smallest block does not fit (2)\n");
762     goto fail;
763   }
764 
765   chunk = (size_t)1 << (blk_size + 4);
766   if (have_block_defined &&
767       (block.num != 0 || single_request)) {
768     /* App is defining a single block to send */
769     size_t rem;
770 
771     if (length >= block.num * chunk) {
772       rem = chunk;
773       if (chunk > length - block.num * chunk)
774         rem = length - block.num * chunk;
775       if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
776         goto fail;
777     }
778     if (release_func)
779       release_func(session, app_ptr);
780   } else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) {
781     /* Only add in lg_xmit if more than one block needs to be handled */
782     size_t rem;
783 
784     lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
785     if (!lg_xmit)
786       goto fail;
787 
788     /* Set up for displaying all the data in the pdu */
789     pdu->body_data = data;
790     pdu->body_length = length;
791     coap_log_debug("PDU presented by app.\n");
792     coap_show_pdu(COAP_LOG_DEBUG, pdu);
793     pdu->body_data = NULL;
794     pdu->body_length = 0;
795 
796     coap_log_debug("** %s: lg_xmit %p initialized\n",
797                    coap_session_str(session), (void *)lg_xmit);
798     /* Update lg_xmit with large data information */
799     memset(lg_xmit, 0, sizeof(coap_lg_xmit_t));
800     lg_xmit->blk_size = blk_size;
801     lg_xmit->option = option;
802     lg_xmit->data = data;
803     lg_xmit->length = length;
804 #if COAP_Q_BLOCK_SUPPORT
805     lg_xmit->non_timeout_random_ticks =
806         coap_get_non_timeout_random_ticks(session);
807 #endif /* COAP_Q_BLOCK_SUPPORT */
808     lg_xmit->release_func = release_func;
809     lg_xmit->app_ptr = app_ptr;
810     pdu->lg_xmit = lg_xmit;
811     coap_ticks(&lg_xmit->last_obs);
812     coap_ticks(&lg_xmit->last_sent);
813     if (COAP_PDU_IS_REQUEST(pdu)) {
814       /* Need to keep original token for updating response PDUs */
815       lg_xmit->b.b1.app_token = coap_new_binary(pdu->actual_token.length);
816       if (!lg_xmit->b.b1.app_token)
817         goto fail;
818       memcpy(lg_xmit->b.b1.app_token->s, pdu->actual_token.s,
819              pdu->actual_token.length);
820       /*
821        * Need to set up new token for use during transmits
822        * RFC9177#section-5
823        */
824       lg_xmit->b.b1.count = 1;
825       lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token,
826                                                    lg_xmit->b.b1.count);
827       /*
828        * Token will be updated in pdu later as original pdu may be needed in
829        * coap_send()
830        */
831       coap_update_option(pdu,
832                          COAP_OPTION_SIZE1,
833                          coap_encode_var_safe(buf, sizeof(buf),
834                                               (unsigned int)length),
835                          buf);
836       if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter))
837         coap_insert_option(pdu,
838                            COAP_OPTION_RTAG,
839                            coap_encode_var_safe(buf, sizeof(buf),
840                                                 ++session->tx_rtag),
841                            buf);
842     } else {
843       /*
844        * resource+query+rtag match is used for Block2 large body transmissions
845        * token match is used for Block1 large body transmissions
846        */
847       lg_xmit->b.b2.resource = resource;
848       if (query) {
849         lg_xmit->b.b2.query = coap_new_string(query->length);
850         if (lg_xmit->b.b2.query) {
851           memcpy(lg_xmit->b.b2.query->s, query->s, query->length);
852         }
853       } else {
854         lg_xmit->b.b2.query = NULL;
855       }
856       opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter);
857       if (opt) {
858         lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt),
859                                                  sizeof(lg_xmit->b.b2.rtag));
860         memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), coap_opt_length(opt));
861         lg_xmit->b.b2.rtag_set = 1;
862       } else {
863         lg_xmit->b.b2.rtag_set = 0;
864       }
865       lg_xmit->b.b2.etag = etag;
866       lg_xmit->b.b2.request_method = request_method;
867       if (maxage >= 0) {
868         coap_tick_t now;
869 
870         coap_ticks(&now);
871         lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage;
872       } else {
873         lg_xmit->b.b2.maxage_expire = 0;
874       }
875       coap_update_option(pdu,
876                          COAP_OPTION_SIZE2,
877                          coap_encode_var_safe(buf, sizeof(buf),
878                                               (unsigned int)length),
879                          buf);
880       if (etag == 0) {
881         if (++session->context->etag == 0)
882           ++session->context->etag;
883         etag = session->context->etag;
884       }
885       coap_update_option(pdu,
886                          COAP_OPTION_ETAG,
887                          coap_encode_var_safe8(buf, sizeof(buf), etag),
888                          buf);
889     }
890 
891     if (!setup_block_b(session, pdu, &block, block.num,
892                        blk_size, lg_xmit->length))
893       goto fail;
894 
895     /* Add in with requested block num, more bit and block size */
896     coap_update_option(pdu,
897                        lg_xmit->option,
898                        coap_encode_var_safe(buf, sizeof(buf),
899                                             (block.num << 4) | (block.m << 3) | block.aszx),
900                        buf);
901 
902     /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
903     memcpy(&lg_xmit->pdu, pdu, sizeof(lg_xmit->pdu));
904     lg_xmit->pdu.token = coap_malloc_type(COAP_PDU_BUF,
905                                           lg_xmit->pdu.used_size + lg_xmit->pdu.max_hdr_size);
906     if (!lg_xmit->pdu.token)
907       goto fail;
908 
909     lg_xmit->pdu.alloc_size = lg_xmit->pdu.used_size;
910     lg_xmit->pdu.token += lg_xmit->pdu.max_hdr_size;
911     memcpy(lg_xmit->pdu.token, pdu->token, lg_xmit->pdu.used_size);
912     if (pdu->data)
913       lg_xmit->pdu.data = lg_xmit->pdu.token + (pdu->data - pdu->token);
914     lg_xmit->pdu.actual_token.s = lg_xmit->pdu.token + pdu->e_token_length -
915                                   pdu->actual_token.length;
916     lg_xmit->pdu.actual_token.length = pdu->actual_token.length;
917 
918     /* Check we still have space after adding in some options */
919     token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
920     avail = pdu->max_size - token_options;
921     /* There may be a response with Echo option */
922     avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40);
923     /* May need token of length 8, so account for this */
924     avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0;
925 #if COAP_OSCORE_SUPPORT
926     avail -= coap_oscore_overhead(session, pdu);
927 #endif /* COAP_OSCORE_SUPPORT */
928     if (avail < (ssize_t)chunk) {
929       /* chunk size change down */
930       if (avail < 16) {
931         coap_log_warn("not enough space, even the smallest block does not fit (3)\n");
932         goto fail;
933       }
934       blk_size = coap_flsll((long long)avail) - 4 - 1;
935       block.num = block.num << (lg_xmit->blk_size - blk_size);
936       lg_xmit->blk_size = blk_size;
937       chunk = (size_t)1 << (lg_xmit->blk_size + 4);
938       block.chunk_size = (uint32_t)chunk;
939       block.bert = 0;
940       coap_update_option(pdu,
941                          lg_xmit->option,
942                          coap_encode_var_safe(buf, sizeof(buf),
943                                               (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
944                          buf);
945     }
946 
947     rem = block.chunk_size;
948     if (rem > lg_xmit->length - block.num * chunk)
949       rem = lg_xmit->length - block.num * chunk;
950     if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
951       goto fail;
952 
953     if (COAP_PDU_IS_REQUEST(pdu))
954       lg_xmit->b.b1.bert_size = rem;
955 
956     lg_xmit->last_block = -1;
957 
958     /* Link the new lg_xmit in */
959     LL_PREPEND(session->lg_xmit,lg_xmit);
960   } else {
961     /* No need to use blocks */
962     if (etag) {
963       coap_update_option(pdu,
964                          COAP_OPTION_ETAG,
965                          coap_encode_var_safe8(buf, sizeof(buf), etag),
966                          buf);
967     }
968     if (have_block_defined) {
969       coap_update_option(pdu,
970                          option,
971                          coap_encode_var_safe(buf, sizeof(buf),
972                                               (0 << 4) | (0 << 3) | blk_size), buf);
973     }
974 add_data:
975     if (!coap_add_data(pdu, length, data))
976       goto fail;
977 
978     if (release_func)
979       release_func(session, app_ptr);
980   }
981   return 1;
982 
983 fail:
984   if (lg_xmit) {
985     coap_block_delete_lg_xmit(session, lg_xmit);
986   } else if (release_func) {
987     release_func(session, app_ptr);
988   }
989   return 0;
990 }
991 
992 #if COAP_CLIENT_SUPPORT
993 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)994 coap_add_data_large_request(coap_session_t *session,
995                             coap_pdu_t *pdu,
996                             size_t length,
997                             const uint8_t *data,
998                             coap_release_large_data_t release_func,
999                             void *app_ptr) {
1000   /*
1001    * Delay if session->doing_first is set.
1002    * E.g. Reliable and CSM not in yet for checking block support
1003    */
1004   if (coap_client_delay_first(session) == 0) {
1005     if (release_func)
1006       release_func(session, app_ptr);
1007     return 0;
1008   }
1009   return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0,
1010                                       length, data, release_func, app_ptr, 0, 0);
1011 }
1012 #endif /* ! COAP_CLIENT_SUPPORT */
1013 
1014 #if COAP_SERVER_SUPPORT
1015 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)1016 coap_add_data_large_response(coap_resource_t *resource,
1017                              coap_session_t *session,
1018                              const coap_pdu_t *request,
1019                              coap_pdu_t *response,
1020                              const coap_string_t *query,
1021                              uint16_t media_type,
1022                              int maxage,
1023                              uint64_t etag,
1024                              size_t length,
1025                              const uint8_t *data,
1026                              coap_release_large_data_t release_func,
1027                              void *app_ptr
1028                             ) {
1029   unsigned char buf[4];
1030   coap_block_b_t block;
1031   int block_requested = 0;
1032   int single_request = 0;
1033 #if COAP_Q_BLOCK_SUPPORT
1034   uint16_t block_opt = (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) ?
1035                        COAP_OPTION_Q_BLOCK2 : COAP_OPTION_BLOCK2;
1036 #else /* ! COAP_Q_BLOCK_SUPPORT */
1037   uint16_t block_opt = COAP_OPTION_BLOCK2;
1038 #endif /* ! COAP_Q_BLOCK_SUPPORT */
1039 
1040   memset(&block, 0, sizeof(block));
1041   /*
1042    * Need to check that a valid block is getting asked for so that the
1043    * correct options are put into the PDU.
1044    */
1045   if (request) {
1046     if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) {
1047       block_requested = 1;
1048       if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
1049         coap_log_debug("Illegal block requested (%d > last = %zu)\n",
1050                        block.num,
1051                        length >> (block.szx + 4));
1052         response->code = COAP_RESPONSE_CODE(400);
1053         goto error;
1054       }
1055     }
1056 #if COAP_Q_BLOCK_SUPPORT
1057     else if (coap_get_block_b(session, request, COAP_OPTION_Q_BLOCK2, &block)) {
1058       block_requested = 1;
1059       if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
1060         coap_log_debug("Illegal block requested (%d > last = %zu)\n",
1061                        block.num,
1062                        length >> (block.szx + 4));
1063         response->code = COAP_RESPONSE_CODE(400);
1064         goto error;
1065       }
1066       if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
1067         set_block_mode_has_q(session->block_mode);
1068         block_opt = COAP_OPTION_Q_BLOCK2;
1069       }
1070       if (block.m == 0)
1071         single_request = 1;
1072     }
1073 #endif /* COAP_Q_BLOCK_SUPPORT */
1074   }
1075 
1076   coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT,
1077                      coap_encode_var_safe(buf, sizeof(buf),
1078                                           media_type),
1079                      buf);
1080 
1081   if (maxage >= 0) {
1082     coap_insert_option(response,
1083                        COAP_OPTION_MAXAGE,
1084                        coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
1085   }
1086 
1087   if (block_requested) {
1088     int res;
1089 
1090     res = coap_write_block_b_opt(session, &block, block_opt, response,
1091                                  length);
1092 
1093     switch (res) {
1094     case -2:                        /* illegal block (caught above) */
1095       response->code = COAP_RESPONSE_CODE(400);
1096       goto error;
1097     case -1:                        /* should really not happen */
1098       assert(0);
1099     /* fall through if assert is a no-op */
1100     case -3:                        /* cannot handle request */
1101       response->code = COAP_RESPONSE_CODE(500);
1102       goto error;
1103     default:                        /* everything is good */
1104       ;
1105     }
1106   }
1107 
1108   /* add data body */
1109   if (request &&
1110       !coap_add_data_large_internal(session, request, response, resource,
1111                                     query, maxage, etag, length, data,
1112                                     release_func, app_ptr, single_request,
1113                                     request->code)) {
1114     response->code = COAP_RESPONSE_CODE(500);
1115     goto error_released;
1116   }
1117 
1118   return 1;
1119 
1120 error:
1121   if (release_func)
1122     release_func(session, app_ptr);
1123 error_released:
1124 #if COAP_ERROR_PHRASE_LENGTH > 0
1125   coap_add_data(response,
1126                 strlen(coap_response_phrase(response->code)),
1127                 (const unsigned char *)coap_response_phrase(response->code));
1128 #endif /* COAP_ERROR_PHRASE_LENGTH > 0 */
1129   return 0;
1130 }
1131 #endif /* ! COAP_SERVER_SUPPORT */
1132 
1133 /*
1134  * return 1 if there is a future expire time, else 0.
1135  * update tim_rem with remaining value if return is 1.
1136  */
1137 int
coap_block_check_lg_xmit_timeouts(coap_session_t * session,coap_tick_t now,coap_tick_t * tim_rem)1138 coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now,
1139                                   coap_tick_t *tim_rem) {
1140   coap_lg_xmit_t *p;
1141   coap_lg_xmit_t *q;
1142 #if COAP_Q_BLOCK_SUPPORT
1143   coap_tick_t idle_timeout = 4 * COAP_NON_TIMEOUT_TICKS(session);
1144 #else /* ! COAP_Q_BLOCK_SUPPORT */
1145   coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND;
1146 #endif /* ! COAP_Q_BLOCK_SUPPORT */
1147   coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1148   int ret = 0;
1149 
1150   *tim_rem = -1;
1151 
1152   LL_FOREACH_SAFE(session->lg_xmit, p, q) {
1153     if (p->last_all_sent) {
1154       if (p->last_all_sent + idle_timeout <= now) {
1155         /* Expire this entry */
1156         LL_DELETE(session->lg_xmit, p);
1157         coap_block_delete_lg_xmit(session, p);
1158       } else {
1159         /* Delay until the lg_xmit needs to expire */
1160         if (*tim_rem > p->last_all_sent + idle_timeout - now) {
1161           *tim_rem = p->last_all_sent + idle_timeout - now;
1162           ret = 1;
1163         }
1164       }
1165     } else if (p->last_sent) {
1166       if (p->last_sent + partial_timeout <= now) {
1167         /* Expire this entry */
1168         LL_DELETE(session->lg_xmit, p);
1169         coap_block_delete_lg_xmit(session, p);
1170         coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
1171       } else {
1172         /* Delay until the lg_xmit needs to expire */
1173         if (*tim_rem > p->last_sent + partial_timeout - now) {
1174           *tim_rem = p->last_sent + partial_timeout - now;
1175           ret = 1;
1176         }
1177       }
1178     }
1179   }
1180   return ret;
1181 }
1182 
1183 #if COAP_CLIENT_SUPPORT
1184 #if COAP_Q_BLOCK_SUPPORT
1185 static coap_pdu_t *
coap_build_missing_pdu(coap_session_t * session,coap_lg_crcv_t * p)1186 coap_build_missing_pdu(coap_session_t *session, coap_lg_crcv_t *p) {
1187   coap_pdu_t *pdu;
1188   coap_opt_filter_t drop_options;
1189   uint64_t token = STATE_TOKEN_FULL(p->state_token, ++p->retry_counter);
1190   uint8_t buf[8];
1191   size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
1192 
1193   memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1194   coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK2);
1195   coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
1196   pdu = coap_pdu_duplicate(&p->pdu, session, len, buf,
1197                            &drop_options);
1198   if (!pdu)
1199     return NULL;
1200   pdu->type = p->last_type;
1201   return pdu;
1202 }
1203 
1204 static void
coap_request_missing_q_block2(coap_session_t * session,coap_lg_crcv_t * lg_crcv)1205 coap_request_missing_q_block2(coap_session_t *session, coap_lg_crcv_t *lg_crcv) {
1206   uint8_t buf[8];
1207   uint32_t i;
1208   int block = -1; /* Last one seen */
1209   size_t sofar;
1210   size_t block_size;
1211   coap_pdu_t *pdu = NULL;
1212   int block_payload_set = -1;
1213 
1214   if (session->block_mode & COAP_BLOCK_USE_M_Q_BLOCK) {
1215     /*
1216      * See if it is safe to use the single 'M' block variant of request
1217      *
1218      * If any blocks seen, then missing blocks are after range[0].end and
1219      * terminate on the last block or before range[1].begin if set.
1220      * If not defined or range[1].begin is in a different payload set then
1221      * safe to use M bit.
1222      */
1223     if (lg_crcv->rec_blocks.used &&
1224         (lg_crcv->rec_blocks.used < 2 ||
1225          ((lg_crcv->rec_blocks.range[0].end + 1) / COAP_MAX_PAYLOADS(session) !=
1226           (lg_crcv->rec_blocks.range[1].begin -1) / COAP_MAX_PAYLOADS(session)))) {
1227       block = lg_crcv->rec_blocks.range[0].end + 1;
1228       block_size = (size_t)1 << (lg_crcv->szx + 4);
1229       sofar = block * block_size;
1230       if (sofar < lg_crcv->total_len) {
1231         /* Ask for missing blocks */
1232         if (pdu == NULL) {
1233           pdu = coap_build_missing_pdu(session, lg_crcv);
1234           if (!pdu)
1235             return;
1236         }
1237         coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
1238                            coap_encode_var_safe(buf, sizeof(buf),
1239                                                 (block << 4) | (1 << 3) | lg_crcv->szx),
1240                            buf);
1241         block_payload_set = block / COAP_MAX_PAYLOADS(session);
1242         goto send_it;
1243       }
1244     }
1245   }
1246   block = -1;
1247   for (i = 0; i < lg_crcv->rec_blocks.used; i++) {
1248     if (block < (int)lg_crcv->rec_blocks.range[i].begin &&
1249         lg_crcv->rec_blocks.range[i].begin != 0) {
1250       /* Ask for missing blocks */
1251       if (pdu == NULL) {
1252         pdu = coap_build_missing_pdu(session, lg_crcv);
1253         if (!pdu)
1254           continue;
1255       }
1256       block++;
1257       if (block_payload_set == -1)
1258         block_payload_set = block / COAP_MAX_PAYLOADS(session);
1259       for (; block < (int)lg_crcv->rec_blocks.range[i].begin &&
1260            block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) {
1261         coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
1262                            coap_encode_var_safe(buf, sizeof(buf),
1263                                                 (block << 4) | (0 << 3) | lg_crcv->szx),
1264                            buf);
1265       }
1266     }
1267     if (block < (int)lg_crcv->rec_blocks.range[i].end) {
1268       block = lg_crcv->rec_blocks.range[i].end;
1269     }
1270   }
1271   block_size = (size_t)1 << (lg_crcv->szx + 4);
1272   sofar = (block + 1) * block_size;
1273   if (sofar < lg_crcv->total_len) {
1274     /* Ask for trailing missing blocks */
1275     if (pdu == NULL) {
1276       pdu = coap_build_missing_pdu(session, lg_crcv);
1277       if (!pdu)
1278         return;
1279     }
1280     sofar = (lg_crcv->total_len + block_size - 1)/block_size;
1281     block++;
1282     if (block_payload_set == -1)
1283       block_payload_set = block / COAP_MAX_PAYLOADS(session);
1284     for (; block < (ssize_t)sofar &&
1285          block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) {
1286       coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
1287                          coap_encode_var_safe(buf, sizeof(buf),
1288                                               (block << 4) | (0 << 3) | lg_crcv->szx),
1289                          buf);
1290     }
1291   }
1292 send_it:
1293   if (pdu)
1294     coap_send_internal(session, pdu);
1295   lg_crcv->rec_blocks.retry++;
1296   if (block_payload_set != -1)
1297     lg_crcv->rec_blocks.processing_payload_set = block_payload_set;
1298   coap_ticks(&lg_crcv->rec_blocks.last_seen);
1299 }
1300 #endif /* COAP_Q_BLOCK_SUPPORT */
1301 
1302 /*
1303  * return 1 if there is a future expire time, else 0.
1304  * update tim_rem with remaining value if return is 1.
1305  */
1306 int
coap_block_check_lg_crcv_timeouts(coap_session_t * session,coap_tick_t now,coap_tick_t * tim_rem)1307 coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now,
1308                                   coap_tick_t *tim_rem) {
1309   coap_lg_crcv_t *p;
1310   coap_lg_crcv_t *q;
1311   coap_tick_t partial_timeout;
1312 #if COAP_Q_BLOCK_SUPPORT
1313   coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session);
1314 #endif /* COAP_Q_BLOCK_SUPPORT */
1315   int ret = 0;
1316 
1317   *tim_rem = -1;
1318 #if COAP_Q_BLOCK_SUPPORT
1319   if (COAP_PROTO_NOT_RELIABLE(session->proto))
1320     partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session);
1321   else
1322 #endif /* COAP_Q_BLOCK_SUPPORT */
1323     partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1324 
1325   LL_FOREACH_SAFE(session->lg_crcv, p, q) {
1326     if (COAP_PROTO_RELIABLE(session->proto) || p->last_type != COAP_MESSAGE_NON)
1327       goto check_expire;
1328 
1329 #if COAP_Q_BLOCK_SUPPORT
1330     if (p->block_option == COAP_OPTION_Q_BLOCK2 && p->rec_blocks.used) {
1331       size_t scaled_timeout = receive_timeout *
1332                               ((size_t)1 << p->rec_blocks.retry);
1333 
1334       if (p->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) {
1335         /* Done NON_MAX_RETRANSMIT retries */
1336         coap_update_token(&p->pdu, p->app_token->length, p->app_token->s);
1337         session->context->nack_handler(session, &p->pdu,
1338                                        COAP_NACK_TOO_MANY_RETRIES,
1339                                        p->pdu.mid);
1340         goto expire;
1341       }
1342       if (p->rec_blocks.last_seen + scaled_timeout <= now) {
1343         coap_request_missing_q_block2(session, p);
1344       } else {
1345         if (*tim_rem > p->rec_blocks.last_seen + scaled_timeout - now) {
1346           *tim_rem = p->rec_blocks.last_seen + scaled_timeout - now;
1347           ret = 1;
1348         }
1349       }
1350     }
1351 #endif /* COAP_Q_BLOCK_SUPPORT */
1352     /* Used for Block2 and Q-Block2 */
1353 check_expire:
1354     if (!p->observe_set && p->last_used &&
1355         p->last_used + partial_timeout <= now) {
1356 #if COAP_Q_BLOCK_SUPPORT
1357 expire:
1358 #endif /* COAP_Q_BLOCK_SUPPORT */
1359       /* Expire this entry */
1360       LL_DELETE(session->lg_crcv, p);
1361       coap_block_delete_lg_crcv(session, p);
1362     } else if (!p->observe_set && p->last_used) {
1363       /* Delay until the lg_crcv needs to expire */
1364       if (*tim_rem > p->last_used + partial_timeout - now) {
1365         *tim_rem = p->last_used + partial_timeout - now;
1366         ret = 1;
1367       }
1368     }
1369   }
1370   return ret;
1371 }
1372 #endif /* COAP_CLIENT_SUPPORT */
1373 
1374 #if COAP_SERVER_SUPPORT
1375 #if COAP_Q_BLOCK_SUPPORT
1376 static coap_pdu_t *
pdu_408_build(coap_session_t * session,coap_lg_srcv_t * p)1377 pdu_408_build(coap_session_t *session, coap_lg_srcv_t *p) {
1378   coap_pdu_t *pdu;
1379   uint8_t buf[4];
1380 
1381   pdu = coap_pdu_init(COAP_MESSAGE_NON,
1382                       COAP_RESPONSE_CODE(408),
1383                       coap_new_message_id(session),
1384                       coap_session_max_pdu_size(session));
1385   if (!pdu)
1386     return NULL;
1387   if (p->last_token)
1388     coap_add_token(pdu, p->last_token->length, p->last_token->s);
1389   coap_add_option_internal(pdu, COAP_OPTION_CONTENT_TYPE,
1390                            coap_encode_var_safe(buf, sizeof(buf),
1391                                                 COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ),
1392                            buf);
1393   pdu->token[pdu->used_size++] = COAP_PAYLOAD_START;
1394   pdu->data = pdu->token + pdu->used_size;
1395   return pdu;
1396 }
1397 
1398 static int
add_408_block(coap_pdu_t * pdu,int block)1399 add_408_block(coap_pdu_t *pdu, int block) {
1400   size_t len;
1401   uint8_t val[8];
1402 
1403   assert(block >= 0 && block < (1 << 20));
1404 
1405   if (block < 0 || block >= (1 << 20)) {
1406     return 0;
1407   } else if (block < 24) {
1408     len = 1;
1409     val[0] = block;
1410   } else if (block < 0x100) {
1411     len = 2;
1412     val[0] = 24;
1413     val[1] = block;
1414   } else if (block < 0x10000) {
1415     len = 3;
1416     val[0] = 25;
1417     val[1] = block >> 8;
1418     val[2] = block & 0xff;
1419   } else { /* Largest block number is 2^^20 - 1 */
1420     len = 4;
1421     val[0] = 26;
1422     val[1] = block >> 16;
1423     val[2] = (block >> 8) & 0xff;
1424     val[3] = block & 0xff;
1425   }
1426   if (coap_pdu_check_resize(pdu, pdu->used_size + len)) {
1427     memcpy(&pdu->token[pdu->used_size], val, len);
1428     pdu->used_size += len;
1429     return 1;
1430   }
1431   return 0;
1432 }
1433 #endif /* COAP_Q_BLOCK_SUPPORT */
1434 #endif /* COAP_SERVER_SUPPORT */
1435 
1436 static int
check_if_received_block(coap_rblock_t * rec_blocks,uint32_t block_num)1437 check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
1438   uint32_t i;
1439 
1440   for (i = 0; i < rec_blocks->used; i++) {
1441     if (block_num < rec_blocks->range[i].begin)
1442       return 0;
1443     if (block_num <= rec_blocks->range[i].end)
1444       return 1;
1445   }
1446   return 0;
1447 }
1448 
1449 static int
check_all_blocks_in(coap_rblock_t * rec_blocks,size_t total_blocks)1450 check_all_blocks_in(coap_rblock_t *rec_blocks, size_t total_blocks) {
1451   uint32_t i;
1452   uint32_t block = 0;
1453 
1454   for (i = 0; i < rec_blocks->used; i++) {
1455     if (block < rec_blocks->range[i].begin)
1456       return 0;
1457     if (block < rec_blocks->range[i].end)
1458       block = rec_blocks->range[i].end;
1459   }
1460   /* total_blocks counts from 1 */
1461   if (block + 1 < total_blocks)
1462     return 0;
1463 
1464   return 1;
1465 }
1466 
1467 #if COAP_CLIENT_SUPPORT
1468 #if COAP_Q_BLOCK_SUPPORT
1469 static int
check_all_blocks_in_for_payload_set(coap_session_t * session,coap_rblock_t * rec_blocks)1470 check_all_blocks_in_for_payload_set(coap_session_t *session,
1471                                     coap_rblock_t *rec_blocks) {
1472   if (rec_blocks->used &&
1473       (rec_blocks->range[0].end + 1) / COAP_MAX_PAYLOADS(session) >
1474       rec_blocks->processing_payload_set)
1475     return 1;
1476   return 0;
1477 }
1478 
1479 static int
check_any_blocks_next_payload_set(coap_session_t * session,coap_rblock_t * rec_blocks)1480 check_any_blocks_next_payload_set(coap_session_t *session,
1481                                   coap_rblock_t *rec_blocks) {
1482   if (rec_blocks->used > 1 &&
1483       rec_blocks->range[1].begin / COAP_MAX_PAYLOADS(session) ==
1484       rec_blocks->processing_payload_set)
1485     return 1;
1486   return 0;
1487 }
1488 #endif /* COAP_Q_BLOCK_SUPPORT */
1489 #endif /* COAP_CLIENT_SUPPORT */
1490 
1491 #if COAP_SERVER_SUPPORT
1492 /*
1493  * return 1 if there is a future expire time, else 0.
1494  * update tim_rem with remaining value if return is 1.
1495  */
1496 int
coap_block_check_lg_srcv_timeouts(coap_session_t * session,coap_tick_t now,coap_tick_t * tim_rem)1497 coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now,
1498                                   coap_tick_t *tim_rem) {
1499   coap_lg_srcv_t *p;
1500   coap_lg_srcv_t *q;
1501   coap_tick_t partial_timeout;
1502 #if COAP_Q_BLOCK_SUPPORT
1503   coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session);
1504 #endif /* COAP_Q_BLOCK_SUPPORT */
1505   int ret = 0;
1506 
1507   *tim_rem = -1;
1508 #if COAP_Q_BLOCK_SUPPORT
1509   if (COAP_PROTO_NOT_RELIABLE(session->proto))
1510     partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session);
1511   else
1512 #endif /* COAP_Q_BLOCK_SUPPORT */
1513     partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1514 
1515   LL_FOREACH_SAFE(session->lg_srcv, p, q) {
1516     if (COAP_PROTO_RELIABLE(session->proto) || p->last_type != COAP_MESSAGE_NON)
1517       goto check_expire;
1518 
1519 #if COAP_Q_BLOCK_SUPPORT
1520     if (p->block_option == COAP_OPTION_Q_BLOCK1 && p->rec_blocks.used) {
1521       size_t scaled_timeout = receive_timeout *
1522                               ((size_t)1 << p->rec_blocks.retry);
1523 
1524       if (p->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) {
1525         /* Done NON_MAX_RETRANSMIT retries */
1526         goto expire;
1527       }
1528       if (p->rec_blocks.last_seen + scaled_timeout <= now) {
1529         uint32_t i;
1530         int block = -1; /* Last one seen */
1531         size_t block_size = (size_t)1 << (p->szx + 4);
1532         size_t final_block = (p->total_len + block_size - 1)/block_size - 1;
1533         size_t cur_payload;
1534         size_t last_payload_block;
1535         coap_pdu_t *pdu = NULL;
1536         size_t no_blocks = 0;
1537 
1538         /* Need to count the number of missing blocks */
1539         for (i = 0; i < p->rec_blocks.used; i++) {
1540           if (block < (int)p->rec_blocks.range[i].begin &&
1541               p->rec_blocks.range[i].begin != 0) {
1542             block++;
1543             no_blocks += p->rec_blocks.range[i].begin - block;
1544           }
1545           if (block < (int)p->rec_blocks.range[i].end) {
1546             block = p->rec_blocks.range[i].end;
1547           }
1548         }
1549         if (no_blocks == 0 && block == (int)final_block)
1550           goto expire;
1551 
1552         /* Include missing up to end of current payload or total amount */
1553         cur_payload = block / COAP_MAX_PAYLOADS(session);
1554         last_payload_block = (cur_payload + 1) * COAP_MAX_PAYLOADS(session) - 1;
1555         if (final_block > last_payload_block) {
1556           final_block = last_payload_block;
1557         }
1558         no_blocks += final_block - block;
1559         if (no_blocks == 0) {
1560           /* Add in the blocks out of the next payload */
1561           final_block = (p->total_len + block_size - 1)/block_size - 1;
1562           last_payload_block += COAP_MAX_PAYLOADS(session);
1563           if (final_block > last_payload_block) {
1564             final_block = last_payload_block;
1565           }
1566           no_blocks += final_block - block;
1567         }
1568         /* Ask for the missing blocks */
1569         block = -1;
1570         for (i = 0; i < p->rec_blocks.used; i++) {
1571           if (block < (int)p->rec_blocks.range[i].begin &&
1572               p->rec_blocks.range[i].begin != 0) {
1573             /* Report on missing blocks */
1574             if (pdu == NULL) {
1575               pdu = pdu_408_build(session, p);
1576               if (!pdu)
1577                 continue;
1578             }
1579             block++;
1580             for (; block < (int)p->rec_blocks.range[i].begin; block++) {
1581               if (!add_408_block(pdu, block)) {
1582                 break;
1583               }
1584             }
1585           }
1586           if (block < (int)p->rec_blocks.range[i].end) {
1587             block = p->rec_blocks.range[i].end;
1588           }
1589         }
1590         block++;
1591         for (; block <= (int)final_block; block++) {
1592           if (pdu == NULL) {
1593             pdu = pdu_408_build(session, p);
1594             if (!pdu)
1595               continue;
1596           }
1597           if (!add_408_block(pdu, block)) {
1598             break;
1599           }
1600         }
1601         if (pdu)
1602           coap_send_internal(session, pdu);
1603         p->rec_blocks.retry++;
1604         coap_ticks(&p->rec_blocks.last_seen);
1605       }
1606       if (*tim_rem > p->rec_blocks.last_seen + scaled_timeout - now) {
1607         *tim_rem = p->rec_blocks.last_seen + scaled_timeout - now;
1608         ret = 1;
1609       }
1610     }
1611 #endif /* COAP_Q_BLOCK_SUPPORT */
1612     /* Used for Block1 and Q-Block1 */
1613 check_expire:
1614     if (p->last_used && p->last_used + partial_timeout <= now) {
1615 #if COAP_Q_BLOCK_SUPPORT
1616 expire:
1617 #endif /* COAP_Q_BLOCK_SUPPORT */
1618       /* Expire this entry */
1619       LL_DELETE(session->lg_srcv, p);
1620       coap_block_delete_lg_srcv(session, p);
1621     } else if (p->last_used) {
1622       /* Delay until the lg_srcv needs to expire */
1623       if (*tim_rem > p->last_used + partial_timeout - now) {
1624         *tim_rem = p->last_used + partial_timeout - now;
1625         ret = 1;
1626       }
1627     }
1628   }
1629   return ret;
1630 }
1631 #endif /* COAP_SERVER_SUPPORT */
1632 
1633 #if COAP_Q_BLOCK_SUPPORT
1634 /*
1635  * pdu is always released before return IF COAP_SEND_INC_PDU
1636  */
1637 coap_mid_t
coap_send_q_blocks(coap_session_t * session,coap_lg_xmit_t * lg_xmit,coap_block_b_t block,coap_pdu_t * pdu,coap_send_pdu_t send_pdu)1638 coap_send_q_blocks(coap_session_t *session,
1639                    coap_lg_xmit_t *lg_xmit,
1640                    coap_block_b_t block,
1641                    coap_pdu_t *pdu,
1642                    coap_send_pdu_t send_pdu) {
1643   coap_pdu_t *block_pdu = NULL;
1644   coap_opt_filter_t drop_options;
1645   coap_mid_t mid = COAP_INVALID_MID;
1646   uint64_t token = coap_decode_var_bytes8(pdu->actual_token.s,
1647                                           pdu->actual_token.length);
1648   const uint8_t *ptoken;
1649   uint8_t ltoken[8];
1650   size_t ltoken_length;
1651   uint32_t delayqueue_cnt = 0;
1652 
1653   if (!lg_xmit) {
1654     if (send_pdu == COAP_SEND_INC_PDU)
1655       return coap_send_internal(session, pdu);
1656     return COAP_INVALID_MID;
1657   }
1658 
1659   if (pdu->type == COAP_MESSAGE_CON) {
1660     coap_queue_t *delayqueue;
1661 
1662     delayqueue_cnt = session->con_active +
1663                      (send_pdu == COAP_SEND_INC_PDU ? 1 : 0);
1664     LL_FOREACH(session->delayqueue, delayqueue) {
1665       delayqueue_cnt++;
1666     }
1667   }
1668   pdu->lg_xmit = lg_xmit;
1669   if (block.m &&
1670       ((pdu->type == COAP_MESSAGE_NON &&
1671         ((block.num + 1) % COAP_MAX_PAYLOADS(session)) + 1 !=
1672         COAP_MAX_PAYLOADS(session)) ||
1673        (pdu->type == COAP_MESSAGE_ACK &&
1674         lg_xmit->option == COAP_OPTION_Q_BLOCK2) ||
1675        (pdu->type == COAP_MESSAGE_CON &&
1676         delayqueue_cnt < COAP_NSTART(session)) ||
1677        COAP_PROTO_RELIABLE(session->proto))) {
1678     /* Allocate next pdu if there is headroom */
1679     if (COAP_PDU_IS_RESPONSE(pdu)) {
1680       ptoken = pdu->actual_token.s;
1681       ltoken_length = pdu->actual_token.length;
1682     } else {
1683       token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count);
1684       ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
1685       ptoken = ltoken;
1686     }
1687 
1688     memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1689     coap_option_filter_set(&drop_options, lg_xmit->option);
1690     block_pdu = coap_pdu_duplicate(pdu, session,
1691                                    ltoken_length,
1692                                    ptoken, &drop_options);
1693     if (block_pdu->type == COAP_MESSAGE_ACK)
1694       block_pdu->type = COAP_MESSAGE_CON;
1695   }
1696 
1697   /* Send initial pdu (which deletes 'pdu') */
1698   if (send_pdu == COAP_SEND_INC_PDU &&
1699       (mid = coap_send_internal(session, pdu)) == COAP_INVALID_MID) {
1700     /* Not expected, underlying issue somewhere */
1701     coap_delete_pdu(block_pdu);
1702     return COAP_INVALID_MID;
1703   }
1704 
1705   while (block_pdu) {
1706     coap_pdu_t *t_pdu = NULL;
1707     uint8_t buf[8];
1708     size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4));
1709 
1710     block.num++;
1711     lg_xmit->offset = block.num * chunk;
1712     block.m = lg_xmit->offset + chunk < lg_xmit->length;
1713     if (block.m && ((block_pdu->type == COAP_MESSAGE_NON &&
1714                      (block.num % COAP_MAX_PAYLOADS(session)) + 1 !=
1715                      COAP_MAX_PAYLOADS(session)) ||
1716                     (block_pdu->type == COAP_MESSAGE_CON &&
1717                      delayqueue_cnt + 1 < COAP_NSTART(session)) ||
1718                     COAP_PROTO_RELIABLE(session->proto))) {
1719       /*
1720        * Send following block if
1721        *   NON and more in MAX_PAYLOADS
1722        *   CON and NSTART allows it (based on number in delayqueue)
1723        *   Reliable transport
1724        */
1725       if (COAP_PDU_IS_RESPONSE(block_pdu)) {
1726         ptoken = block_pdu->actual_token.s;
1727         ltoken_length = block_pdu->actual_token.length;
1728       } else {
1729         token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count);
1730         ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
1731         ptoken = ltoken;
1732       }
1733       t_pdu = coap_pdu_duplicate(block_pdu, session,
1734                                  ltoken_length, ptoken, &drop_options);
1735     }
1736     if (!coap_update_option(block_pdu, lg_xmit->option,
1737                             coap_encode_var_safe(buf,
1738                                                  sizeof(buf),
1739                                                  ((block.num) << 4) |
1740                                                  (block.m << 3) |
1741                                                  block.szx),
1742                             buf)) {
1743       coap_log_warn("Internal update issue option\n");
1744       coap_delete_pdu(block_pdu);
1745       coap_delete_pdu(t_pdu);
1746       break;
1747     }
1748 
1749     if (!coap_add_block(block_pdu,
1750                         lg_xmit->length,
1751                         lg_xmit->data,
1752                         block.num,
1753                         block.szx)) {
1754       coap_log_warn("Internal update issue data\n");
1755       coap_delete_pdu(block_pdu);
1756       coap_delete_pdu(t_pdu);
1757       break;
1758     }
1759     if (COAP_PDU_IS_RESPONSE(block_pdu)) {
1760       lg_xmit->last_block = block.num;
1761     }
1762     mid = coap_send_internal(session, block_pdu);
1763     if (mid == COAP_INVALID_MID) {
1764       /* Not expected, underlying issue somewhere */
1765       coap_delete_pdu(t_pdu);
1766       return COAP_INVALID_MID;
1767     }
1768     block_pdu = t_pdu;
1769   }
1770   if (!block.m) {
1771     lg_xmit->last_payload = 0;
1772     coap_ticks(&lg_xmit->last_all_sent);
1773   } else
1774     coap_ticks(&lg_xmit->last_payload);
1775   return mid;
1776 }
1777 
1778 #if COAP_CLIENT_SUPPORT
1779 coap_tick_t
coap_block_check_q_block1_xmit(coap_session_t * session,coap_tick_t now)1780 coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now) {
1781   coap_lg_xmit_t *lg_xmit;
1782   coap_lg_xmit_t *q;
1783   coap_tick_t timed_out;
1784   coap_tick_t tim_rem = (coap_tick_t)-1;
1785 
1786   LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
1787     coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks;
1788 
1789     if (now < non_timeout)
1790       return non_timeout - now;
1791     timed_out = now - non_timeout;
1792 
1793     if (lg_xmit->last_payload) {
1794       if (lg_xmit->last_payload <= timed_out) {
1795         /* Send off the next MAX_PAYLOAD set */
1796         coap_block_b_t block;
1797         size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
1798 
1799         memset(&block, 0, sizeof(block));
1800         block.num = (uint32_t)(lg_xmit->offset / chunk);
1801         block.m = lg_xmit->offset + chunk < lg_xmit->length;
1802         block.szx = lg_xmit->blk_size;
1803         coap_send_q_blocks(session, lg_xmit, block, &lg_xmit->pdu, COAP_SEND_SKIP_PDU);
1804         if (tim_rem > non_timeout)
1805           tim_rem = non_timeout;
1806       } else {
1807         /* Delay until the next MAX_PAYLOAD needs to be sent off */
1808         if (tim_rem > lg_xmit->last_payload - timed_out)
1809           tim_rem = lg_xmit->last_payload - timed_out;
1810       }
1811     } else if (lg_xmit->last_all_sent) {
1812       non_timeout = COAP_NON_TIMEOUT_TICKS(session);
1813       if (lg_xmit->last_all_sent + 4 * non_timeout <= now) {
1814         /* Expire this entry */
1815         LL_DELETE(session->lg_xmit, lg_xmit);
1816         coap_block_delete_lg_xmit(session, lg_xmit);
1817       } else {
1818         /* Delay until the lg_xmit needs to expire */
1819         if (tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now)
1820           tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now;
1821       }
1822     }
1823   }
1824   return tim_rem;
1825 }
1826 #endif /* COAP_CLIENT_SUPPORT */
1827 
1828 #if COAP_SERVER_SUPPORT
1829 coap_tick_t
coap_block_check_q_block2_xmit(coap_session_t * session,coap_tick_t now)1830 coap_block_check_q_block2_xmit(coap_session_t *session, coap_tick_t now) {
1831   coap_lg_xmit_t *lg_xmit;
1832   coap_lg_xmit_t *q;
1833   coap_tick_t timed_out;
1834   coap_tick_t tim_rem = (coap_tick_t)-1;
1835 
1836   LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
1837     coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks;
1838 
1839     if (now < non_timeout)
1840       return non_timeout - now;
1841     timed_out = now - non_timeout;
1842 
1843     if (lg_xmit->last_payload) {
1844       if (lg_xmit->last_payload <= timed_out) {
1845         /* Send off the next MAX_PAYLOAD set */
1846         coap_block_b_t block;
1847         size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
1848 
1849         memset(&block, 0, sizeof(block));
1850         block.num = (uint32_t)(lg_xmit->offset / chunk);
1851         block.m = lg_xmit->offset + chunk < lg_xmit->length;
1852         block.szx = lg_xmit->blk_size;
1853         if (block.num == (uint32_t)lg_xmit->last_block)
1854           coap_send_q_blocks(session, lg_xmit, block, &lg_xmit->pdu, COAP_SEND_SKIP_PDU);
1855         if (tim_rem > non_timeout)
1856           tim_rem = non_timeout;
1857       } else {
1858         /* Delay until the next MAX_PAYLOAD needs to be sent off */
1859         if (tim_rem > lg_xmit->last_payload - timed_out)
1860           tim_rem = lg_xmit->last_payload - timed_out;
1861       }
1862     } else if (lg_xmit->last_all_sent) {
1863       non_timeout = COAP_NON_TIMEOUT_TICKS(session);
1864       if (lg_xmit->last_all_sent +  4 * non_timeout <= now) {
1865         /* Expire this entry */
1866         LL_DELETE(session->lg_xmit, lg_xmit);
1867         coap_block_delete_lg_xmit(session, lg_xmit);
1868       } else {
1869         /* Delay until the lg_xmit needs to expire */
1870         if (tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now)
1871           tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now;
1872       }
1873     }
1874   }
1875   return tim_rem;
1876 }
1877 #endif /* COAP_SERVER_SUPPORT */
1878 #endif /* COAP_Q_BLOCK_SUPPORT */
1879 
1880 #if COAP_CLIENT_SUPPORT
1881 /*
1882  * If Observe = 0, save the token away and return NULL
1883  * Else If Observe = 1, return the saved token for this block
1884  * Else, return NULL
1885  */
1886 static coap_bin_const_t *
track_fetch_observe(coap_pdu_t * pdu,coap_lg_crcv_t * lg_crcv,uint32_t block_num,coap_bin_const_t * token)1887 track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv,
1888                     uint32_t block_num, coap_bin_const_t *token) {
1889   /* Need to handle Observe for large FETCH */
1890   coap_opt_iterator_t opt_iter;
1891   coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE,
1892                                       &opt_iter);
1893 
1894   if (opt && lg_crcv) {
1895     int observe_action = -1;
1896     coap_bin_const_t **tmp;
1897 
1898     observe_action = coap_decode_var_bytes(coap_opt_value(opt),
1899                                            coap_opt_length(opt));
1900     if (observe_action == COAP_OBSERVE_ESTABLISH) {
1901       /* Save the token in lg_crcv */
1902       tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token,
1903                               (block_num + 1) * sizeof(lg_crcv->obs_token[0]));
1904       if (tmp == NULL)
1905         return NULL;
1906       lg_crcv->obs_token = tmp;
1907       if (block_num + 1 == lg_crcv->obs_token_cnt)
1908         coap_delete_bin_const(lg_crcv->obs_token[block_num]);
1909 
1910       lg_crcv->obs_token_cnt = block_num + 1;
1911       lg_crcv->obs_token[block_num] = coap_new_bin_const(token->s,
1912                                                          token->length);
1913       if (lg_crcv->obs_token[block_num] == NULL)
1914         return NULL;
1915     } else if (observe_action == COAP_OBSERVE_CANCEL) {
1916       /* Use the token in lg_crcv */
1917       if (block_num < lg_crcv->obs_token_cnt) {
1918         if (lg_crcv->obs_token[block_num]) {
1919           return lg_crcv->obs_token[block_num];
1920         }
1921       }
1922     }
1923   }
1924   return NULL;
1925 }
1926 
1927 #if COAP_Q_BLOCK_SUPPORT
1928 coap_mid_t
coap_send_q_block1(coap_session_t * session,coap_block_b_t block,coap_pdu_t * request,coap_send_pdu_t send_request)1929 coap_send_q_block1(coap_session_t *session,
1930                    coap_block_b_t block,
1931                    coap_pdu_t *request,
1932                    coap_send_pdu_t send_request) {
1933   /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK1 */
1934   coap_lg_xmit_t *lg_xmit;
1935   uint64_t token_match =
1936       STATE_TOKEN_BASE(coap_decode_var_bytes8(request->actual_token.s,
1937                                               request->actual_token.length));
1938 
1939   LL_FOREACH(session->lg_xmit, lg_xmit) {
1940     if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 &&
1941         (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) ||
1942          token_match ==
1943          STATE_TOKEN_BASE(coap_decode_var_bytes8(lg_xmit->b.b1.app_token->s,
1944                                                  lg_xmit->b.b1.app_token->length))))
1945       break;
1946     /* try out the next one */
1947   }
1948   return coap_send_q_blocks(session, lg_xmit, block, request, send_request);
1949 }
1950 #endif /* COAP_Q_BLOCK_SUPPORT */
1951 #endif /* COAP_CLIENT_SUPPORT */
1952 
1953 #if COAP_SERVER_SUPPORT
1954 #if COAP_Q_BLOCK_SUPPORT
1955 /*
1956  * response is always released before return IF COAP_SEND_INC_PDU
1957  */
1958 coap_mid_t
coap_send_q_block2(coap_session_t * session,coap_resource_t * resource,const coap_string_t * query,coap_pdu_code_t request_method,coap_block_b_t block,coap_pdu_t * response,coap_send_pdu_t send_response)1959 coap_send_q_block2(coap_session_t *session,
1960                    coap_resource_t *resource,
1961                    const coap_string_t *query,
1962                    coap_pdu_code_t request_method,
1963                    coap_block_b_t block,
1964                    coap_pdu_t *response,
1965                    coap_send_pdu_t send_response) {
1966   /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK2 */
1967   coap_lg_xmit_t *lg_xmit;
1968   coap_string_t empty = { 0, NULL};
1969 
1970   LL_FOREACH(session->lg_xmit, lg_xmit) {
1971     if (lg_xmit->option == COAP_OPTION_Q_BLOCK2 &&
1972         resource == lg_xmit->b.b2.resource &&
1973         request_method == lg_xmit->b.b2.request_method &&
1974         coap_string_equal(query ? query : &empty,
1975                           lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty))
1976       break;
1977   }
1978   return coap_send_q_blocks(session, lg_xmit, block, response, send_response);
1979 }
1980 #endif /* COAP_Q_BLOCK_SUPPORT */
1981 #endif /* COAP_SERVER_SUPPORT */
1982 
1983 #if COAP_CLIENT_SUPPORT
1984 #if COAP_Q_BLOCK_SUPPORT
1985 /*
1986  * Send out a test PDU for Q-Block.
1987  */
1988 coap_mid_t
coap_block_test_q_block(coap_session_t * session,coap_pdu_t * actual)1989 coap_block_test_q_block(coap_session_t *session, coap_pdu_t *actual) {
1990   coap_pdu_t *pdu;
1991   uint8_t token[8];
1992   size_t token_len;
1993   uint8_t buf[4];
1994   coap_mid_t mid;
1995 
1996 #if NDEBUG
1997   (void)actual;
1998 #endif /* NDEBUG */
1999   assert(session->block_mode & COAP_BLOCK_TRY_Q_BLOCK &&
2000          session->type == COAP_SESSION_TYPE_CLIENT &&
2001          COAP_PDU_IS_REQUEST(actual));
2002 
2003   coap_log_debug("Testing for Q-Block support\n");
2004   /* RFC9177 Section 3.1 when checking if available */
2005   pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET,
2006                       coap_new_message_id(session),
2007                       coap_session_max_pdu_size(session));
2008   if (!pdu) {
2009     return COAP_INVALID_MID;
2010   }
2011 
2012   coap_session_new_token(session, &token_len, token);
2013   coap_add_token(pdu, token_len, token);
2014   /* M needs to be unset as 'asking' for only the first block */
2015   coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
2016                      coap_encode_var_safe(buf, sizeof(buf),
2017                                           (0 << 4) | (0 << 3) | 0),
2018                      buf);
2019   set_block_mode_probe_q(session->block_mode);
2020   mid = coap_send_internal(session, pdu);
2021   if (mid == COAP_INVALID_MID)
2022     return COAP_INVALID_MID;
2023   session->remote_test_mid = mid;
2024   return mid;
2025 }
2026 #endif /* COAP_Q_BLOCK_SUPPORT */
2027 
2028 coap_lg_crcv_t *
coap_block_new_lg_crcv(coap_session_t * session,coap_pdu_t * pdu,coap_lg_xmit_t * lg_xmit)2029 coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu,
2030                        coap_lg_xmit_t *lg_xmit) {
2031   coap_lg_crcv_t *lg_crcv;
2032   uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);
2033   size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
2034                          pdu->used_size;
2035   size_t data_len = lg_xmit ? lg_xmit->length :
2036                     pdu->data ?
2037                     pdu->used_size - (pdu->data - pdu->token) : 0;
2038 
2039   lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
2040 
2041   if (lg_crcv == NULL)
2042     return NULL;
2043 
2044   coap_log_debug("** %s: lg_crcv %p initialized - stateless token xxxx%012llx\n",
2045                  coap_session_str(session), (void *)lg_crcv,
2046                  STATE_TOKEN_BASE(state_token));
2047   memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
2048   lg_crcv->initial = 1;
2049   coap_ticks(&lg_crcv->last_used);
2050   /* Set up skeletal PDU to use as a basis for all the subsequent blocks */
2051   memcpy(&lg_crcv->pdu, pdu, sizeof(lg_crcv->pdu));
2052   /* Make sure that there is space for increased token + option change */
2053   lg_crcv->pdu.max_size = token_options + data_len + 9;
2054   lg_crcv->pdu.used_size = token_options + data_len;
2055   lg_crcv->pdu.token = coap_malloc_type(COAP_PDU_BUF,
2056                                         token_options + data_len + lg_crcv->pdu.max_hdr_size);
2057   if (!lg_crcv->pdu.token) {
2058     coap_block_delete_lg_crcv(session, lg_crcv);
2059     return NULL;
2060   }
2061   lg_crcv->pdu.token += lg_crcv->pdu.max_hdr_size;
2062   memcpy(lg_crcv->pdu.token, pdu->token, token_options);
2063   if (lg_crcv->pdu.data) {
2064     lg_crcv->pdu.data = lg_crcv->pdu.token + token_options;
2065     memcpy(lg_crcv->pdu.data, lg_xmit ? lg_xmit->data : pdu->data, data_len);
2066   }
2067 
2068   /* Need to keep original token for updating response PDUs */
2069   lg_crcv->app_token = coap_new_binary(pdu->actual_token.length);
2070   if (!lg_crcv->app_token) {
2071     coap_block_delete_lg_crcv(session, lg_crcv);
2072     return NULL;
2073   }
2074   memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length);
2075 
2076   /* Need to set up a base token for actual communications if retries needed */
2077   lg_crcv->retry_counter = 1;
2078   lg_crcv->state_token = state_token;
2079 
2080   if (pdu->code == COAP_REQUEST_CODE_FETCH) {
2081     coap_bin_const_t *new_token;
2082 
2083     /* Need to save/restore Observe Token for large FETCH */
2084     new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token);
2085     if (new_token)
2086       coap_update_token(pdu, new_token->length, new_token->s);
2087   }
2088 
2089   /* In case it is there - must not be in continuing request PDUs */
2090   coap_remove_option(&lg_crcv->pdu, COAP_OPTION_BLOCK1);
2091 
2092   return lg_crcv;
2093 }
2094 
2095 void
coap_block_delete_lg_crcv(coap_session_t * session,coap_lg_crcv_t * lg_crcv)2096 coap_block_delete_lg_crcv(coap_session_t *session,
2097                           coap_lg_crcv_t *lg_crcv) {
2098   size_t i;
2099 
2100 #if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2101   (void)session;
2102 #endif
2103   if (lg_crcv == NULL)
2104     return;
2105 
2106   if (lg_crcv->pdu.token)
2107     coap_free_type(COAP_PDU_BUF, lg_crcv->pdu.token - lg_crcv->pdu.max_hdr_size);
2108   coap_free_type(COAP_STRING, lg_crcv->body_data);
2109   coap_log_debug("** %s: lg_crcv %p released\n",
2110                  coap_session_str(session), (void *)lg_crcv);
2111   coap_delete_binary(lg_crcv->app_token);
2112   for (i = 0; i < lg_crcv->obs_token_cnt; i++) {
2113     coap_delete_bin_const(lg_crcv->obs_token[i]);
2114   }
2115   coap_free_type(COAP_STRING, lg_crcv->obs_token);
2116   coap_free_type(COAP_LG_CRCV, lg_crcv);
2117 }
2118 #endif /* COAP_CLIENT_SUPPORT */
2119 
2120 #if COAP_SERVER_SUPPORT
2121 void
coap_block_delete_lg_srcv(coap_session_t * session,coap_lg_srcv_t * lg_srcv)2122 coap_block_delete_lg_srcv(coap_session_t *session,
2123                           coap_lg_srcv_t *lg_srcv) {
2124 #if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2125   (void)session;
2126 #endif
2127   if (lg_srcv == NULL)
2128     return;
2129 
2130   coap_delete_str_const(lg_srcv->uri_path);
2131 #if COAP_Q_BLOCK_SUPPORT
2132   coap_delete_bin_const(lg_srcv->last_token);
2133 #endif /* COAP_Q_BLOCK_SUPPORT */
2134   coap_free_type(COAP_STRING, lg_srcv->body_data);
2135   coap_log_debug("** %s: lg_srcv %p released\n",
2136                  coap_session_str(session), (void *)lg_srcv);
2137   coap_free_type(COAP_LG_SRCV, lg_srcv);
2138 }
2139 #endif /* COAP_SERVER_SUPPORT */
2140 
2141 void
coap_block_delete_lg_xmit(coap_session_t * session,coap_lg_xmit_t * lg_xmit)2142 coap_block_delete_lg_xmit(coap_session_t *session,
2143                           coap_lg_xmit_t *lg_xmit) {
2144   if (lg_xmit == NULL)
2145     return;
2146 
2147   if (lg_xmit->release_func) {
2148     lg_xmit->release_func(session, lg_xmit->app_ptr);
2149   }
2150   if (lg_xmit->pdu.token) {
2151     coap_free_type(COAP_PDU_BUF, lg_xmit->pdu.token - lg_xmit->pdu.max_hdr_size);
2152   }
2153   if (COAP_PDU_IS_REQUEST(&lg_xmit->pdu))
2154     coap_delete_binary(lg_xmit->b.b1.app_token);
2155   else
2156     coap_delete_string(lg_xmit->b.b2.query);
2157 
2158   coap_log_debug("** %s: lg_xmit %p released\n",
2159                  coap_session_str(session), (void *)lg_xmit);
2160   coap_free_type(COAP_LG_XMIT, lg_xmit);
2161 }
2162 
2163 #if COAP_SERVER_SUPPORT
2164 typedef struct {
2165   uint32_t num;
2166   int is_continue;
2167 } send_track;
2168 
2169 static int
add_block_send(uint32_t num,int is_continue,send_track * out_blocks,uint32_t * count,uint32_t max_count)2170 add_block_send(uint32_t num, int is_continue, send_track *out_blocks,
2171                uint32_t *count, uint32_t max_count) {
2172   uint32_t i;
2173 
2174   for (i = 0; i < *count && *count < max_count; i++) {
2175     if (num == out_blocks[i].num)
2176       return 0;
2177     else if (num < out_blocks[i].num) {
2178       if (*count - i > 1)
2179         memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
2180       out_blocks[i].num = num;
2181       out_blocks[i].is_continue = is_continue;
2182       (*count)++;
2183       return 1;
2184     }
2185   }
2186   if (*count < max_count) {
2187     out_blocks[i].num = num;
2188     out_blocks[i].is_continue = is_continue;
2189     (*count)++;
2190     return 1;
2191   }
2192   return 0;
2193 }
2194 
2195 /*
2196  * Need to see if this is a request for the next block of a large body
2197  * transfer.  If so, need to initiate the response with the next blocks
2198  * and not trouble the application.
2199  *
2200  * If additional responses needed, then these are expicitly sent out and
2201  * 'response' is updated to be the last response to be sent.  There can be
2202  * multiple Q-Block2 in the request, as well as  the 'Continue' Q-Block2
2203  * request.
2204  *
2205  * This is set up using coap_add_data_large_response()
2206  *
2207  * Server is sending a large data response to GET / observe (Block2)
2208  *
2209  * Return: 0 Call application handler
2210  *         1 Do not call application handler - just send the built response
2211  */
2212 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)2213 coap_handle_request_send_block(coap_session_t *session,
2214                                coap_pdu_t *pdu,
2215                                coap_pdu_t *response,
2216                                coap_resource_t *resource,
2217                                coap_string_t *query) {
2218   coap_lg_xmit_t *p = NULL;
2219   coap_block_b_t block;
2220   coap_block_b_t alt_block;
2221   uint16_t block_opt = 0;
2222   send_track *out_blocks = NULL;
2223   const char *error_phrase;
2224   coap_opt_iterator_t opt_iter;
2225   size_t chunk;
2226   coap_opt_iterator_t opt_b_iter;
2227   coap_opt_t *option;
2228   uint32_t request_cnt, i;
2229   coap_opt_t *etag_opt = NULL;
2230   coap_pdu_t *out_pdu = response;
2231 #if COAP_Q_BLOCK_SUPPORT
2232   size_t max_block;
2233 
2234   /* Is client indicating that it supports Q_BLOCK2 ? */
2235   if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
2236     if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK))
2237       set_block_mode_has_q(session->block_mode);
2238     block_opt = COAP_OPTION_Q_BLOCK2;
2239   }
2240 #endif /* COAP_Q_BLOCK_SUPPORT */
2241   if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &alt_block)) {
2242     if (block_opt) {
2243       coap_log_warn("Block2 and Q-Block2 cannot be in the same request\n");
2244       coap_add_data(response, sizeof("Both Block2 and Q-Block2 invalid")-1,
2245                     (const uint8_t *)"Both Block2 and Q-Block2 invalid");
2246       response->code = COAP_RESPONSE_CODE(400);
2247       goto skip_app_handler;
2248     }
2249     block = alt_block;
2250     block_opt = COAP_OPTION_BLOCK2;
2251   }
2252   if (block_opt == 0)
2253     return 0;
2254   if (block.num == 0) {
2255     /* Get a fresh copy of the data */
2256     return 0;
2257   }
2258   p = coap_find_lg_xmit_response(session, pdu, resource, query);
2259   if (p == NULL)
2260     return 0;
2261 
2262 #if COAP_Q_BLOCK_SUPPORT
2263   out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track) * COAP_MAX_PAYLOADS(session));
2264 #else /* ! COAP_Q_BLOCK_SUPPORT */
2265   out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track));
2266 #endif /* ! COAP_Q_BLOCK_SUPPORT */
2267   if (!out_blocks) {
2268     goto internal_issue;
2269   }
2270 
2271   /* lg_xmit (response) found */
2272 
2273   etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
2274   if (etag_opt) {
2275     uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
2276                                            coap_opt_length(etag_opt));
2277     if (etag != p->b.b2.etag) {
2278       /* Not a match - pass up to a higher level */
2279       return 0;
2280     }
2281     out_pdu->code = COAP_RESPONSE_CODE(203);
2282     coap_ticks(&p->last_sent);
2283     goto skip_app_handler;
2284   } else {
2285     out_pdu->code = p->pdu.code;
2286   }
2287   coap_ticks(&p->last_obs);
2288   p->last_all_sent = 0;
2289 
2290   chunk = (size_t)1 << (p->blk_size + 4);
2291   if (block_opt) {
2292     if (block.bert) {
2293       coap_log_debug("found Block option, block is BERT, block nr. %u, M %d\n",
2294                      block.num, block.m);
2295     } else {
2296       coap_log_debug("found Block option, block size is %u, block nr. %u, M %d\n",
2297                      1 << (block.szx + 4), block.num, block.m);
2298     }
2299     if (block.bert == 0 && block.szx != p->blk_size) {
2300       if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
2301         /*
2302          * Recompute the block number of the previous packet given
2303          * the new block size
2304          */
2305         block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
2306         p->blk_size = block.szx;
2307         chunk = (size_t)1 << (p->blk_size + 4);
2308         p->offset = block.num * chunk;
2309         coap_log_debug("new Block size is %u, block number %u completed\n",
2310                        1 << (block.szx + 4), block.num);
2311       } else {
2312         coap_log_debug("ignoring request to increase Block size, "
2313                        "next block is not aligned on requested block size "
2314                        "boundary. (%zu x %u mod %u = %zu (which is not 0)\n",
2315                        p->offset/chunk + 1, (1 << (p->blk_size + 4)),
2316                        (1 << (block.szx + 4)),
2317                        (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
2318       }
2319     }
2320   }
2321 
2322   /*
2323    * Need to check if there are multiple Q-Block2 requests.  If so, they
2324    * need to be sent out in order of requests with the final request being
2325    * handled as per singular Block 2 request.
2326    */
2327   request_cnt = 0;
2328 #if COAP_Q_BLOCK_SUPPORT
2329   max_block = (p->length + chunk - 1)/chunk;
2330 #endif /* COAP_Q_BLOCK_SUPPORT */
2331   coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
2332   while ((option = coap_option_next(&opt_b_iter))) {
2333     unsigned int num;
2334     if (opt_b_iter.number != p->option)
2335       continue;
2336     num = coap_opt_block_num(option);
2337     if (num > 0xFFFFF) /* 20 bits max for num */
2338       continue;
2339     if (block.aszx != COAP_OPT_BLOCK_SZX(option)) {
2340       coap_add_data(response,
2341                     sizeof("Changing blocksize during request invalid")-1,
2342                     (const uint8_t *)"Changing blocksize during request invalid");
2343       response->code = COAP_RESPONSE_CODE(400);
2344       goto skip_app_handler;
2345     }
2346 #if COAP_Q_BLOCK_SUPPORT
2347     if (COAP_OPT_BLOCK_MORE(option) && p->option == COAP_OPTION_Q_BLOCK2) {
2348       if ((num % COAP_MAX_PAYLOADS(session)) == 0) {
2349         if (num == 0) {
2350           /* This is a repeat request for everything - hmm */
2351           goto call_app_handler;
2352         }
2353         /* 'Continue' request */
2354         for (i = 0; i < COAP_MAX_PAYLOADS(session) &&
2355              num + i < max_block; i++) {
2356           add_block_send(num + i, 1, out_blocks, &request_cnt,
2357                          COAP_MAX_PAYLOADS(session));
2358           p->last_block = num + i;
2359         }
2360       } else {
2361         /* Requesting remaining payloads in this MAX_PAYLOADS */
2362         for (i = 0; i < COAP_MAX_PAYLOADS(session) -
2363              num % COAP_MAX_PAYLOADS(session) &&
2364              num + i < max_block; i++) {
2365           add_block_send(num + i, 0, out_blocks, &request_cnt,
2366                          COAP_MAX_PAYLOADS(session));
2367         }
2368       }
2369     } else
2370       add_block_send(num, 0, out_blocks, &request_cnt,
2371                      COAP_MAX_PAYLOADS(session));
2372 #else /* ! COAP_Q_BLOCK_SUPPORT */
2373     add_block_send(num, 0, out_blocks, &request_cnt, 1);
2374     break;
2375 #endif /* ! COAP_Q_BLOCK_SUPPORT */
2376   }
2377   if (request_cnt == 0) {
2378     /* Block2 or Q-Block2 not found - give them the first block */
2379     block.szx = p->blk_size;
2380     p->offset = 0;
2381     out_blocks[0].num = 0;
2382     out_blocks[0].is_continue = 0;
2383     request_cnt = 1;
2384   }
2385 
2386   for (i = 0; i < request_cnt; i++) {
2387     uint8_t buf[8];
2388 
2389     block.num = out_blocks[i].num;
2390     p->offset = block.num * chunk;
2391 
2392     if (i + 1 < request_cnt) {
2393       /* Need to set up a copy of the pdu to send */
2394       coap_opt_filter_t drop_options;
2395 
2396       memset(&drop_options, 0, sizeof(coap_opt_filter_t));
2397       if (block.num != 0)
2398         coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
2399       if (out_blocks[i].is_continue) {
2400         out_pdu = coap_pdu_duplicate(&p->pdu, session, p->pdu.actual_token.length,
2401                                      p->pdu.actual_token.s, &drop_options);
2402       } else {
2403         out_pdu = coap_pdu_duplicate(&p->pdu, session, pdu->actual_token.length,
2404                                      pdu->actual_token.s, &drop_options);
2405       }
2406       if (!out_pdu) {
2407         goto internal_issue;
2408       }
2409     } else {
2410       if (out_blocks[i].is_continue)
2411         coap_update_token(response, p->pdu.actual_token.length,
2412                           p->pdu.actual_token.s);
2413       /*
2414        * Copy the options across and then fix the block option
2415        *
2416        * Need to drop Observe option if Block2 and block.num != 0
2417        */
2418       coap_option_iterator_init(&p->pdu, &opt_iter, COAP_OPT_ALL);
2419       while ((option = coap_option_next(&opt_iter))) {
2420         if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0)
2421           continue;
2422         if (!coap_insert_option(response, opt_iter.number,
2423                                 coap_opt_length(option),
2424                                 coap_opt_value(option))) {
2425           goto internal_issue;
2426         }
2427       }
2428       out_pdu = response;
2429     }
2430     if (pdu->type == COAP_MESSAGE_NON)
2431       out_pdu->type = COAP_MESSAGE_NON;
2432     if (block.bert) {
2433       size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
2434       block.m = (p->length - p->offset) >
2435                 ((out_pdu->max_size - token_options) /1024) * 1024;
2436     } else {
2437       block.m = (p->offset + chunk) < p->length;
2438     }
2439     if (!coap_update_option(out_pdu, p->option,
2440                             coap_encode_var_safe(buf,
2441                                                  sizeof(buf),
2442                                                  (block.num << 4) |
2443                                                  (block.m << 3) |
2444                                                  block.aszx),
2445                             buf)) {
2446       goto internal_issue;
2447     }
2448     if (!(p->offset + chunk < p->length)) {
2449       /* Last block - keep in cache for 4 * ACK_TIMOUT */
2450       coap_ticks(&p->last_all_sent);
2451     }
2452     if (p->b.b2.maxage_expire) {
2453       coap_tick_t now;
2454       coap_time_t rem;
2455 
2456       if (!(p->offset + chunk < p->length)) {
2457         /* Last block - keep in cache for 4 * ACK_TIMOUT */
2458         coap_ticks(&p->last_all_sent);
2459       }
2460       coap_ticks(&now);
2461       rem = coap_ticks_to_rt(now);
2462       if (p->b.b2.maxage_expire > rem) {
2463         rem = p->b.b2.maxage_expire - rem;
2464       } else {
2465         rem = 0;
2466         /* Entry needs to be expired */
2467         coap_ticks(&p->last_all_sent);
2468       }
2469       if (!coap_update_option(out_pdu, COAP_OPTION_MAXAGE,
2470                               coap_encode_var_safe8(buf,
2471                                                     sizeof(buf),
2472                                                     rem),
2473                               buf)) {
2474         goto internal_issue;
2475       }
2476     }
2477 
2478     if (!etag_opt && !coap_add_block_b_data(out_pdu,
2479                                             p->length,
2480                                             p->data,
2481                                             &block)) {
2482       goto internal_issue;
2483     }
2484     if (i + 1 < request_cnt) {
2485       coap_ticks(&p->last_sent);
2486       coap_send_internal(session, out_pdu);
2487     }
2488   }
2489   coap_ticks(&p->last_payload);
2490   goto skip_app_handler;
2491 #if COAP_Q_BLOCK_SUPPORT
2492 call_app_handler:
2493   coap_free_type(COAP_STRING, out_blocks);
2494   return 0;
2495 #endif /* COAP_Q_BLOCK_SUPPORT */
2496 
2497 internal_issue:
2498   response->code = COAP_RESPONSE_CODE(500);
2499   error_phrase = coap_response_phrase(response->code);
2500   coap_add_data(response, strlen(error_phrase),
2501                 (const uint8_t *)error_phrase);
2502   /* Keep in cache for 4 * ACK_TIMOUT incase of retry */
2503   if (p)
2504     coap_ticks(&p->last_all_sent);
2505 
2506 skip_app_handler:
2507   coap_free_type(COAP_STRING, out_blocks);
2508   return 1;
2509 }
2510 #endif /* COAP_SERVER_SUPPORT */
2511 
2512 static int
update_received_blocks(coap_rblock_t * rec_blocks,uint32_t block_num)2513 update_received_blocks(coap_rblock_t *rec_blocks, uint32_t block_num) {
2514   uint32_t i;
2515 
2516   /* Reset as there is activity */
2517   rec_blocks->retry = 0;
2518 
2519   for (i = 0; i < rec_blocks->used; i++) {
2520     if (block_num >= rec_blocks->range[i].begin &&
2521         block_num <= rec_blocks->range[i].end)
2522       break;
2523 
2524     if (block_num < rec_blocks->range[i].begin) {
2525       if (block_num + 1 == rec_blocks->range[i].begin) {
2526         rec_blocks->range[i].begin = block_num;
2527       } else {
2528         /* Need to insert a new range */
2529         if (rec_blocks->used == COAP_RBLOCK_CNT -1)
2530           /* Too many losses */
2531           return 0;
2532         memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
2533                 (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
2534         rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
2535         rec_blocks->used++;
2536       }
2537       break;
2538     }
2539     if (block_num == rec_blocks->range[i].end + 1) {
2540       rec_blocks->range[i].end = block_num;
2541       if (i + 1 < rec_blocks->used) {
2542         if (rec_blocks->range[i+1].begin == block_num + 1) {
2543           /* Merge the 2 ranges */
2544           rec_blocks->range[i].end = rec_blocks->range[i+1].end;
2545           if (i+2 < rec_blocks->used) {
2546             memmove(&rec_blocks->range[i+1], &rec_blocks->range[i+2],
2547                     (rec_blocks->used - (i+2)) * sizeof(rec_blocks->range[0]));
2548           }
2549           rec_blocks->used--;
2550         }
2551       }
2552       break;
2553     }
2554   }
2555   if (i == rec_blocks->used) {
2556     if (rec_blocks->used == COAP_RBLOCK_CNT -1)
2557       /* Too many losses */
2558       return 0;
2559     rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
2560     rec_blocks->used++;
2561   }
2562   coap_ticks(&rec_blocks->last_seen);
2563   return 1;
2564 }
2565 
2566 #if COAP_SERVER_SUPPORT
2567 /*
2568  * Need to check if this is a large PUT / POST using multiple blocks
2569  *
2570  * Server receiving PUT/POST etc. of a large amount of data (Block1)
2571  *
2572  * Return: 0 Call application handler
2573  *         1 Do not call application handler - just send the built response
2574  */
2575 int
coap_handle_request_put_block(coap_context_t * context,coap_session_t * session,coap_pdu_t * pdu,coap_pdu_t * response,coap_resource_t * resource,coap_string_t * uri_path,coap_opt_t * observe,int * added_block,coap_lg_srcv_t ** pfree_lg_srcv)2576 coap_handle_request_put_block(coap_context_t *context,
2577                               coap_session_t *session,
2578                               coap_pdu_t *pdu,
2579                               coap_pdu_t *response,
2580                               coap_resource_t *resource,
2581                               coap_string_t *uri_path,
2582                               coap_opt_t *observe,
2583                               int *added_block,
2584                               coap_lg_srcv_t **pfree_lg_srcv) {
2585   size_t length = 0;
2586   const uint8_t *data = NULL;
2587   size_t offset = 0;
2588   size_t total = 0;
2589   coap_block_b_t block;
2590   coap_opt_iterator_t opt_iter;
2591   uint16_t block_option = 0;
2592 
2593   *added_block = 0;
2594   *pfree_lg_srcv = NULL;
2595   coap_get_data_large(pdu, &length, &data, &offset, &total);
2596   pdu->body_offset = 0;
2597   pdu->body_total = length;
2598 
2599   if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
2600     block_option = COAP_OPTION_BLOCK1;
2601 #if COAP_Q_BLOCK_SUPPORT
2602     if (coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter)) {
2603       /* Cannot handle Q-Block1 as well */
2604       coap_add_data(response, sizeof("Block1 + Q-Block1 together")-1,
2605                     (const uint8_t *)"Block1 + Q-Block1 together");
2606       response->code = COAP_RESPONSE_CODE(402);
2607       goto skip_app_handler;
2608     }
2609 #endif /* COAP_Q_BLOCK_SUPPORT */
2610   }
2611 #if COAP_Q_BLOCK_SUPPORT
2612   else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
2613     block_option = COAP_OPTION_Q_BLOCK1;
2614     set_block_mode_has_q(session->block_mode);
2615   }
2616 #endif /* COAP_Q_BLOCK_SUPPORT */
2617   if (block_option) {
2618     coap_lg_srcv_t *p;
2619     coap_opt_t *size_opt = coap_check_option(pdu,
2620                                              COAP_OPTION_SIZE1,
2621                                              &opt_iter);
2622     coap_opt_t *fmt_opt = coap_check_option(pdu,
2623                                             COAP_OPTION_CONTENT_FORMAT,
2624                                             &opt_iter);
2625     uint16_t fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
2626                                                    coap_opt_length(fmt_opt)) :
2627                    COAP_MEDIATYPE_TEXT_PLAIN;
2628     coap_opt_t *rtag_opt = coap_check_option(pdu,
2629                                              COAP_OPTION_RTAG,
2630                                              &opt_iter);
2631     size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
2632     const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
2633 
2634     if (length > block.chunk_size) {
2635       coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n",
2636                      block.chunk_size, length);
2637       length = block.chunk_size;
2638     }
2639     total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
2640                                              coap_opt_length(size_opt)) : 0;
2641     offset = block.num << (block.szx + 4);
2642 
2643     LL_FOREACH(session->lg_srcv, p) {
2644       if (rtag_opt || p->rtag_set == 1) {
2645         if (!(rtag_opt && p->rtag_set == 1))
2646           continue;
2647         if (p->rtag_length != rtag_length ||
2648             memcmp(p->rtag, rtag, rtag_length) != 0)
2649           continue;
2650       }
2651       if (resource == p->resource) {
2652         break;
2653       }
2654       if ((p->resource == context->unknown_resource ||
2655            resource == context->proxy_uri_resource) &&
2656           coap_string_equal(uri_path, p->uri_path))
2657         break;
2658     }
2659     if (!p && block.num != 0) {
2660       /* random access - no need to track */
2661       pdu->body_data = data;
2662       pdu->body_length = length;
2663       pdu->body_offset = offset;
2664       pdu->body_total = length + offset + (block.m ? 1 : 0);
2665     }
2666     /* Do not do this if this is a single block */
2667     else if (!p && !(offset == 0 && block.m == 0)) {
2668       p = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t));
2669       if (p == NULL) {
2670         coap_add_data(response, sizeof("Memory issue")-1,
2671                       (const uint8_t *)"Memory issue");
2672         response->code = COAP_RESPONSE_CODE(500);
2673         goto skip_app_handler;
2674       }
2675       coap_log_debug("** %s: lg_srcv %p initialized\n",
2676                      coap_session_str(session), (void *)p);
2677       memset(p, 0, sizeof(coap_lg_srcv_t));
2678       coap_ticks(&p->last_used);
2679       p->resource = resource;
2680       if (resource == context->unknown_resource ||
2681           resource == context->proxy_uri_resource)
2682         p->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
2683       p->content_format = fmt;
2684       p->total_len = total;
2685       p->amount_so_far = length;
2686       p->szx = block.szx;
2687       p->block_option = block_option;
2688       if (observe) {
2689         p->observe_length = min(coap_opt_length(observe), 3);
2690         memcpy(p->observe, coap_opt_value(observe), p->observe_length);
2691         p->observe_set = 1;
2692       }
2693       if (rtag_opt) {
2694         p->rtag_length = coap_opt_length(rtag_opt);
2695         memcpy(p->rtag, coap_opt_value(rtag_opt), p->rtag_length);
2696         p->rtag_set = 1;
2697       }
2698       p->body_data = NULL;
2699       LL_PREPEND(session->lg_srcv, p);
2700     }
2701     if (p) {
2702       if (fmt != p->content_format) {
2703         coap_add_data(response, sizeof("Content-Format mismatch")-1,
2704                       (const uint8_t *)"Content-Format mismatch");
2705         response->code = COAP_RESPONSE_CODE(408);
2706         goto free_lg_srcv;
2707       }
2708 #if COAP_Q_BLOCK_SUPPORT
2709       if (block_option == COAP_OPTION_Q_BLOCK1) {
2710         if (total != p->total_len) {
2711           coap_add_data(response, sizeof("Size1 mismatch")-1,
2712                         (const uint8_t *)"Size1 mismatch");
2713           response->code = COAP_RESPONSE_CODE(408);
2714           goto free_lg_srcv;
2715         }
2716       }
2717 #endif /* COAP_Q_BLOCK_SUPPORT */
2718       p->last_mid = pdu->mid;
2719       p->last_type = pdu->type;
2720 #if COAP_Q_BLOCK_SUPPORT
2721       coap_delete_bin_const(p->last_token);
2722       p->last_token = coap_new_bin_const(pdu->actual_token.s,
2723                                          pdu->actual_token.length);
2724 #endif /* COAP_Q_BLOCK_SUPPORT */
2725       if (session->block_mode &
2726 #if COAP_Q_BLOCK_SUPPORT
2727           (COAP_BLOCK_SINGLE_BODY|COAP_BLOCK_HAS_Q_BLOCK) ||
2728 #else /* ! COAP_Q_BLOCK_SUPPORT */
2729           (COAP_BLOCK_SINGLE_BODY) ||
2730 #endif /* ! COAP_Q_BLOCK_SUPPORT */
2731           block.bert) {
2732         size_t chunk = (size_t)1 << (block.szx + 4);
2733         int update_data = 0;
2734         unsigned int saved_num = block.num;
2735         size_t saved_offset = offset;
2736 
2737         while (offset < saved_offset + length) {
2738           if (!check_if_received_block(&p->rec_blocks, block.num)) {
2739             /* Update list of blocks received */
2740             if (!update_received_blocks(&p->rec_blocks, block.num)) {
2741               coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session);
2742               coap_add_data(response, sizeof("Too many missing blocks")-1,
2743                             (const uint8_t *)"Too many missing blocks");
2744               response->code = COAP_RESPONSE_CODE(408);
2745               goto free_lg_srcv;
2746             }
2747             update_data = 1;
2748           }
2749           block.num++;
2750           offset = block.num << (block.szx + 4);
2751         }
2752         block.num--;
2753         if (update_data) {
2754 #if COAP_Q_BLOCK_SUPPORT
2755           p->rec_blocks.processing_payload_set =
2756               block.num / COAP_MAX_PAYLOADS(session);
2757 #endif /* COAP_Q_BLOCK_SUPPORT */
2758           /* Update saved data */
2759           if (p->total_len < saved_offset + length) {
2760             p->total_len = saved_offset + length;
2761           }
2762           p->body_data = coap_block_build_body(p->body_data, length, data,
2763                                                saved_offset, p->total_len);
2764           if (!p->body_data)
2765             goto call_app_handler;
2766 
2767         }
2768         if (block.m ||
2769             !check_all_blocks_in(&p->rec_blocks,
2770                                  (uint32_t)(p->total_len + chunk -1)/chunk)) {
2771           /* Not all the payloads of the body have arrived */
2772           if (block.m) {
2773             uint8_t buf[4];
2774 
2775 #if COAP_Q_BLOCK_SUPPORT
2776             if (block_option == COAP_OPTION_Q_BLOCK1) {
2777               if (check_all_blocks_in(&p->rec_blocks,
2778                                       (uint32_t)(p->total_len + chunk -1)/chunk)) {
2779                 goto give_app_data;
2780               }
2781               if (p->rec_blocks.used == 1 &&
2782                   (p->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1
2783                   == COAP_MAX_PAYLOADS(session)) {
2784                 /* Blocks could arrive in wrong order */
2785                 block.num = p->rec_blocks.range[0].end;
2786               } else {
2787                 /* The remote end will be sending the next one unless this
2788                    is a MAX_PAYLOADS and all previous have been received */
2789                 goto skip_app_handler;
2790               }
2791               if (COAP_PROTO_RELIABLE(session->proto) ||
2792                   pdu->type != COAP_MESSAGE_NON)
2793                 goto skip_app_handler;
2794             }
2795 #endif /* COAP_Q_BLOCK_SUPPORT */
2796             /* Ask for the next block */
2797             coap_insert_option(response, block_option,
2798                                coap_encode_var_safe(buf, sizeof(buf),
2799                                                     (saved_num << 4) |
2800                                                     (block.m << 3) |
2801                                                     block.aszx),
2802                                buf);
2803             response->code = COAP_RESPONSE_CODE(231);
2804             goto skip_app_handler;
2805           }
2806           goto skip_app_handler;
2807         }
2808 
2809         /*
2810          * Remove the Block1 option as passing all of the data to
2811          * application layer. Add back in observe option if appropriate.
2812          * Adjust all other information.
2813          */
2814 #if COAP_Q_BLOCK_SUPPORT
2815 give_app_data:
2816 #endif /* COAP_Q_BLOCK_SUPPORT */
2817         if (p->observe_set) {
2818           coap_update_option(pdu, COAP_OPTION_OBSERVE,
2819                              p->observe_length, p->observe);
2820         }
2821         coap_remove_option(pdu, block_option);
2822         pdu->body_data = p->body_data->s;
2823         pdu->body_length = p->total_len;
2824         pdu->body_offset = 0;
2825         pdu->body_total = p->total_len;
2826         coap_log_debug("Server app version of updated PDU\n");
2827         coap_show_pdu(COAP_LOG_DEBUG, pdu);
2828         *pfree_lg_srcv = p;
2829         goto call_app_handler;
2830       } else {
2831         /* No need to update body_data and body_length as a single PDU */
2832         pdu->body_offset = offset;
2833         /* Exact match if last block */
2834         if (block.m) {
2835           uint8_t buf[4];
2836 
2837           if (total > offset + length + block.m)
2838             pdu->body_total = total;
2839           else
2840             pdu->body_total = offset + length + block.m;
2841 
2842           coap_insert_option(response, block_option,
2843                              coap_encode_var_safe(buf, sizeof(buf),
2844                                                   (block.num << 4) |
2845                                                   (block.m << 3) |
2846                                                   block.aszx),
2847                              buf);
2848           *added_block = 1;
2849           goto call_app_handler;
2850         } else {
2851           pdu->body_total = offset + length + block.m;
2852         }
2853       }
2854 
2855       if (block.m == 0) {
2856         /* Last chunk - free off all */
2857         coap_ticks(&p->last_used);
2858       }
2859       goto call_app_handler;
2860 
2861 free_lg_srcv:
2862       LL_DELETE(session->lg_srcv, p);
2863       coap_block_delete_lg_srcv(session, p);
2864       goto skip_app_handler;
2865     }
2866   }
2867 call_app_handler:
2868   return 0;
2869 
2870 skip_app_handler:
2871   return 1;
2872 }
2873 #endif /* COAP_SERVER_SUPPORT */
2874 
2875 #if COAP_CLIENT_SUPPORT
2876 #if COAP_Q_BLOCK_SUPPORT
2877 static uint32_t
derive_cbor_value(const uint8_t ** bp,size_t rem_len)2878 derive_cbor_value(const uint8_t **bp, size_t rem_len) {
2879   uint32_t value = **bp & 0x1f;
2880   (*bp)++;
2881   if (value < 24) {
2882     return value;
2883   } else if (value == 24) {
2884     if (rem_len < 2)
2885       return (uint32_t)-1;
2886     value = **bp;
2887     (*bp)++;
2888     return value;
2889   } else if (value == 25) {
2890     if (rem_len < 3)
2891       return (uint32_t)-1;
2892     value = **bp << 8;
2893     (*bp)++;
2894     value |= **bp;
2895     (*bp)++;
2896     return value;
2897   }
2898   if (rem_len < 4)
2899     return (uint32_t)-1;
2900   value = **bp << 24;
2901   (*bp)++;
2902   value = **bp << 16;
2903   (*bp)++;
2904   value = **bp << 8;
2905   (*bp)++;
2906   value |= **bp;
2907   (*bp)++;
2908   return value;
2909 }
2910 #endif /* COAP_Q_BLOCK_SUPPORT */
2911 
2912 static int
check_freshness(coap_session_t * session,coap_pdu_t * rcvd,coap_pdu_t * sent,coap_lg_xmit_t * lg_xmit,coap_lg_crcv_t * lg_crcv)2913 check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
2914                 coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) {
2915   /* Check for Echo option for freshness */
2916   coap_opt_iterator_t opt_iter;
2917   coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
2918 
2919   if (opt) {
2920     if (sent || lg_xmit || lg_crcv) {
2921       /* Need to retransmit original request with Echo option added */
2922       coap_pdu_t *echo_pdu;
2923       coap_mid_t mid;
2924       const uint8_t *data;
2925       size_t data_len;
2926       int have_data = 0;
2927       uint8_t ltoken[8];
2928       size_t ltoken_len;
2929       uint64_t token;
2930 
2931       if (sent) {
2932         if (coap_get_data(sent, &data_len, &data))
2933           have_data = 1;
2934       } else if (lg_xmit) {
2935         sent = &lg_xmit->pdu;
2936         if (lg_xmit->length) {
2937           size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
2938           size_t offset = (lg_xmit->last_block + 1) * blk_size;
2939           have_data = 1;
2940           data = &lg_xmit->data[offset];
2941           data_len = (lg_xmit->length - offset) > blk_size ? blk_size :
2942                      lg_xmit->length - offset;
2943         }
2944       } else { /* lg_crcv */
2945         sent = &lg_crcv->pdu;
2946         if (coap_get_data(sent, &data_len, &data))
2947           have_data = 1;
2948       }
2949       if (lg_xmit) {
2950         token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
2951                                  ++lg_xmit->b.b1.count);
2952       } else {
2953         token = STATE_TOKEN_FULL(lg_crcv->state_token,
2954                                  ++lg_crcv->retry_counter);
2955       }
2956       ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
2957       echo_pdu = coap_pdu_duplicate(sent, session, ltoken_len, ltoken, NULL);
2958       if (!echo_pdu)
2959         return 0;
2960       if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
2961                               coap_opt_length(opt), coap_opt_value(opt)))
2962         goto not_sent;
2963       if (have_data) {
2964         coap_add_data(echo_pdu, data_len, data);
2965       }
2966       /* Need to track Observe token change if Observe */
2967       track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token);
2968 #if COAP_OSCORE_SUPPORT
2969       if (session->oscore_encryption &&
2970           (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) &&
2971           coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt) == 0)) {
2972         /* Need to update the base PDU's Token for closing down Observe */
2973         if (lg_xmit) {
2974           lg_xmit->b.b1.state_token = token;
2975         } else {
2976           lg_crcv->state_token = token;
2977         }
2978       }
2979 #endif /* COAP_OSCORE_SUPPORT */
2980       mid = coap_send_internal(session, echo_pdu);
2981       if (mid == COAP_INVALID_MID)
2982         goto not_sent;
2983       return 1;
2984     } else {
2985       /* Need to save Echo option value to add to next reansmission */
2986 not_sent:
2987       coap_delete_bin_const(session->echo);
2988       session->echo = coap_new_bin_const(coap_opt_value(opt),
2989                                          coap_opt_length(opt));
2990     }
2991   }
2992   return 0;
2993 }
2994 
2995 static void
track_echo(coap_session_t * session,coap_pdu_t * rcvd)2996 track_echo(coap_session_t *session, coap_pdu_t *rcvd) {
2997   coap_opt_iterator_t opt_iter;
2998   coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
2999 
3000   if (opt) {
3001     coap_delete_bin_const(session->echo);
3002     session->echo = coap_new_bin_const(coap_opt_value(opt),
3003                                        coap_opt_length(opt));
3004   }
3005 }
3006 
3007 /*
3008  * Need to see if this is a response to a large body request transfer. If so,
3009  * need to initiate the request containing the next block and not trouble the
3010  * application.  Note that Token must unique per request/response.
3011  *
3012  * Client receives large data acknowledgement from server (Block1)
3013  *
3014  * This is set up using coap_add_data_large_request()
3015  *
3016  * Client is using GET etc.
3017  *
3018  * Return: 0 Call application handler
3019  *         1 Do not call application handler - just send the built response
3020  */
3021 int
coap_handle_response_send_block(coap_session_t * session,coap_pdu_t * sent,coap_pdu_t * rcvd)3022 coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent,
3023                                 coap_pdu_t *rcvd) {
3024   coap_lg_xmit_t *p;
3025   coap_lg_xmit_t *q;
3026   uint64_t token_match =
3027       STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->actual_token.s,
3028                                               rcvd->actual_token.length));
3029   coap_lg_crcv_t *lg_crcv = NULL;
3030 
3031   LL_FOREACH_SAFE(session->lg_xmit, p, q) {
3032     if (!COAP_PDU_IS_REQUEST(&p->pdu) ||
3033         (token_match != STATE_TOKEN_BASE(p->b.b1.state_token) &&
3034          token_match !=
3035          STATE_TOKEN_BASE(coap_decode_var_bytes8(p->b.b1.app_token->s,
3036                                                  p->b.b1.app_token->length)))) {
3037       /* try out the next one */
3038       continue;
3039     }
3040     /* lg_xmit found */
3041     size_t chunk = (size_t)1 << (p->blk_size + 4);
3042     coap_block_b_t block;
3043 
3044     if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
3045         coap_get_block_b(session, rcvd, p->option, &block)) {
3046 
3047       if (block.bert) {
3048         coap_log_debug("found Block option, block is BERT, block nr. %u (%zu)\n",
3049                        block.num, p->b.b1.bert_size);
3050       } else {
3051         coap_log_debug("found Block option, block size is %u, block nr. %u\n",
3052                        1 << (block.szx + 4), block.num);
3053       }
3054       if (block.szx != p->blk_size) {
3055         if ((p->offset + chunk) % ((size_t)1 << (block.szx + 4)) == 0) {
3056           /*
3057            * Recompute the block number of the previous packet given the
3058            * new block size
3059            */
3060           block.num = (uint32_t)(((p->offset + chunk) >> (block.szx + 4)) - 1);
3061           p->blk_size = block.szx;
3062           chunk = (size_t)1 << (p->blk_size + 4);
3063           p->offset = block.num * chunk;
3064           coap_log_debug("new Block size is %u, block number %u completed\n",
3065                          1 << (block.szx + 4), block.num);
3066           block.bert = 0;
3067           block.aszx = block.szx;
3068         } else {
3069           coap_log_debug("ignoring request to increase Block size, "
3070                          "next block is not aligned on requested block size boundary. "
3071                          "(%zu x %u mod %u = %zu != 0)\n",
3072                          p->offset/chunk + 1, (1 << (p->blk_size + 4)),
3073                          (1 << (block.szx + 4)),
3074                          (p->offset + chunk) % ((size_t)1 << (block.szx + 4)));
3075         }
3076       }
3077       track_echo(session, rcvd);
3078       if (p->last_block == (int)block.num &&
3079           p->option != COAP_OPTION_Q_BLOCK1) {
3080         /*
3081          * Duplicate Block1 ACK
3082          *
3083          * RFCs not clear here, but on a lossy connection, there could
3084          * be multiple Block1 ACKs, causing the client to retransmit the
3085          * same block multiple times, or the server retransmitting the
3086          * same ACK.
3087          *
3088          * Once a block has been ACKd, there is no need to retransmit it.
3089          */
3090         return 1;
3091       }
3092       if (block.bert)
3093         block.num += (unsigned int)(p->b.b1.bert_size / 1024 - 1);
3094       p->last_block = block.num;
3095       p->offset = (block.num + 1) * chunk;
3096       if (p->offset < p->length) {
3097         /* Build the next PDU request based off the skeletal PDU */
3098         uint8_t buf[8];
3099         coap_pdu_t *pdu;
3100         uint64_t token = STATE_TOKEN_FULL(p->b.b1.state_token, ++p->b.b1.count);
3101         size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
3102 
3103         if (p->pdu.code == COAP_REQUEST_CODE_FETCH) {
3104           /* Need to handle Observe for large FETCH */
3105           LL_FOREACH(session->lg_crcv, lg_crcv) {
3106             if (coap_binary_equal(p->b.b1.app_token, lg_crcv->app_token)) {
3107               coap_bin_const_t *new_token;
3108               coap_bin_const_t ctoken = { len, buf };
3109 
3110               /* Need to save/restore Observe Token for large FETCH */
3111               new_token = track_fetch_observe(&p->pdu, lg_crcv, block.num + 1,
3112                                               &ctoken);
3113               if (new_token) {
3114                 assert(len <= sizeof(buf));
3115                 len = new_token->length;
3116                 memcpy(buf, new_token->s, len);
3117               }
3118               break;
3119             }
3120           }
3121         }
3122         pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
3123         if (!pdu)
3124           goto fail_body;
3125 
3126         block.num++;
3127         if (block.bert) {
3128           size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
3129                                  pdu->used_size;
3130           block.m = (p->length - p->offset) >
3131                     ((pdu->max_size - token_options) /1024) * 1024;
3132         } else {
3133           block.m = (p->offset + chunk) < p->length;
3134         }
3135         coap_update_option(pdu, p->option,
3136                            coap_encode_var_safe(buf, sizeof(buf),
3137                                                 (block.num << 4) |
3138                                                 (block.m << 3) |
3139                                                 block.aszx),
3140                            buf);
3141 
3142         if (!coap_add_block_b_data(pdu,
3143                                    p->length,
3144                                    p->data,
3145                                    &block))
3146           goto fail_body;
3147         p->b.b1.bert_size = block.chunk_size;
3148         coap_ticks(&p->last_sent);
3149 #if COAP_Q_BLOCK_SUPPORT
3150         if (p->option == COAP_OPTION_Q_BLOCK1 &&
3151             pdu->type == COAP_MESSAGE_NON) {
3152           if (coap_send_q_block1(session, block, pdu,
3153                                  COAP_SEND_INC_PDU) == COAP_INVALID_MID)
3154             goto fail_body;
3155           return 1;
3156         } else if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
3157           goto fail_body;
3158 #else /* ! COAP_Q_BLOCK_SUPPORT */
3159         if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
3160           goto fail_body;
3161 #endif /* ! COAP_Q_BLOCK_SUPPORT */
3162         return 1;
3163       }
3164     } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
3165       if (check_freshness(session, rcvd, sent, p, NULL))
3166         return 1;
3167 #if COAP_Q_BLOCK_SUPPORT
3168     } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
3169       /* Q-Block1 or Q-Block2 not present in p - duplicate error ? */
3170       if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) ||
3171           coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK1, &block))
3172         return 1;
3173     } else if (rcvd->code == COAP_RESPONSE_CODE(408) &&
3174                p->option == COAP_OPTION_Q_BLOCK1) {
3175       size_t length;
3176       const uint8_t *data;
3177       coap_opt_iterator_t opt_iter;
3178       coap_opt_t *fmt_opt = coap_check_option(rcvd,
3179                                               COAP_OPTION_CONTENT_FORMAT,
3180                                               &opt_iter);
3181       uint16_t fmt = fmt_opt ?
3182                      coap_decode_var_bytes(coap_opt_value(fmt_opt),
3183                                            coap_opt_length(fmt_opt)) :
3184                      COAP_MEDIATYPE_TEXT_PLAIN;
3185 
3186       if (fmt != COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ)
3187         goto fail_body;
3188 
3189       if (COAP_PROTO_RELIABLE(session->proto) ||
3190           rcvd->type != COAP_MESSAGE_NON) {
3191         coap_log_debug("Unexpected 4.08 - protocol violation - ignore\n");
3192         return 1;
3193       }
3194 
3195       if (coap_get_data(rcvd, &length, &data)) {
3196         /* Need to decode CBOR to work out what blocks to re-send */
3197         const uint8_t *bp = data;
3198         uint32_t i;
3199         uint8_t buf[8];
3200         coap_pdu_t *pdu;
3201         uint64_t token = coap_decode_var_bytes8(rcvd->actual_token.s,
3202                                                 rcvd->actual_token.length);
3203         uint8_t ltoken[8];
3204         size_t ltoken_length;
3205 
3206         for (i = 0; (bp < data + length) &&
3207              i < COAP_MAX_PAYLOADS(session); i++) {
3208           if ((*bp & 0xc0) != 0x00) /* uint(value) */
3209             goto fail_cbor;
3210           block.num = derive_cbor_value(&bp, data + length - bp);
3211           coap_log_debug("Q-Block1: Missing block %d\n", block.num);
3212           if (block.num > (1 << 20) -1)
3213             goto fail_cbor;
3214           block.m = (block.num + 1) * chunk < p->length;
3215           block.szx = p->blk_size;
3216 
3217           /* Build the next PDU request based off the skeletal PDU */
3218           token = STATE_TOKEN_FULL(p->b.b1.state_token,++p->b.b1.count);
3219           ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
3220           pdu = coap_pdu_duplicate(&p->pdu, session, ltoken_length,
3221                                    ltoken, NULL);
3222           if (!pdu)
3223             goto fail_body;
3224 
3225           coap_update_option(pdu, p->option,
3226                              coap_encode_var_safe(buf, sizeof(buf),
3227                                                   (block.num << 4) |
3228                                                   (block.m << 3) |
3229                                                   block.szx),
3230                              buf);
3231 
3232           if (!coap_add_block(pdu,
3233                               p->length,
3234                               p->data,
3235                               block.num,
3236                               block.szx))
3237             goto fail_body;
3238           if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
3239             goto fail_body;
3240         }
3241         return 1;
3242       }
3243 fail_cbor:
3244       coap_log_info("Invalid application/missing-blocks+cbor-seq\n");
3245 #endif /* COAP_Q_BLOCK_SUPPORT */
3246     }
3247     goto lg_xmit_finished;
3248   } /* end of LL_FOREACH_SAFE */
3249   return 0;
3250 
3251 fail_body:
3252   coap_handle_event(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
3253   /* There has been an internal error of some sort */
3254   rcvd->code = COAP_RESPONSE_CODE(500);
3255 lg_xmit_finished:
3256   if (session->lg_crcv) {
3257     LL_FOREACH(session->lg_crcv, lg_crcv) {
3258       if (STATE_TOKEN_BASE(p->b.b1.state_token) ==
3259           STATE_TOKEN_BASE(lg_crcv->state_token)) {
3260         /* In case of observe */
3261         lg_crcv->state_token = p->b.b1.state_token;
3262         break;
3263       }
3264     }
3265   }
3266   if (!lg_crcv) {
3267     /* need to put back original token into rcvd */
3268     if (p->b.b1.app_token)
3269       coap_update_token(rcvd, p->b.b1.app_token->length,
3270                         p->b.b1.app_token->s);
3271     coap_log_debug("Client app version of updated PDU\n");
3272     coap_show_pdu(COAP_LOG_DEBUG, rcvd);
3273   }
3274 
3275   LL_DELETE(session->lg_xmit, p);
3276   coap_block_delete_lg_xmit(session, p);
3277   return 0;
3278 }
3279 #endif /* COAP_CLIENT_SUPPORT */
3280 
3281 /*
3282  * Re-assemble payloads into a body
3283  */
3284 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)3285 coap_block_build_body(coap_binary_t *body_data, size_t length,
3286                       const uint8_t *data, size_t offset, size_t total) {
3287   if (data == NULL)
3288     return NULL;
3289   if (body_data == NULL && total) {
3290     body_data = coap_new_binary(total);
3291   }
3292   if (body_data == NULL)
3293     return NULL;
3294 
3295   /* Update saved data */
3296   if (offset + length <= total && body_data->length >= total) {
3297     memcpy(&body_data->s[offset], data, length);
3298   } else {
3299     /*
3300      * total may be inaccurate as per
3301      * https://rfc-editor.org/rfc/rfc7959#section-4
3302      * o In a request carrying a Block1 Option, to indicate the current
3303      *   estimate the client has of the total size of the resource
3304      *   representation, measured in bytes ("size indication").
3305      * o In a response carrying a Block2 Option, to indicate the current
3306      *   estimate the server has of the total size of the resource
3307      *   representation, measured in bytes ("size indication").
3308      */
3309     coap_binary_t *new = coap_resize_binary(body_data, offset + length);
3310 
3311     if (new) {
3312       body_data = new;
3313       memcpy(&body_data->s[offset], data, length);
3314     } else {
3315       coap_delete_binary(body_data);
3316       return NULL;
3317     }
3318   }
3319   return body_data;
3320 }
3321 
3322 #if COAP_CLIENT_SUPPORT
3323 /*
3324  * Need to see if this is a large body response to a request. If so,
3325  * need to initiate the request for the next block and not trouble the
3326  * application.  Note that Token must be unique per request/response.
3327  *
3328  * This is set up using coap_send()
3329  * Client receives large data from server ((Q-)Block2)
3330  *
3331  * Return: 0 Call application handler
3332  *         1 Do not call application handler - just sent the next request
3333  */
3334 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)3335 coap_handle_response_get_block(coap_context_t *context,
3336                                coap_session_t *session,
3337                                coap_pdu_t *sent,
3338                                coap_pdu_t *rcvd,
3339                                coap_recurse_t recursive) {
3340   coap_lg_crcv_t *p;
3341   coap_block_b_t block;
3342 #if COAP_Q_BLOCK_SUPPORT
3343   coap_block_b_t qblock;
3344 #endif /* COAP_Q_BLOCK_SUPPORT */
3345   int have_block = 0;
3346   uint16_t block_opt = 0;
3347   size_t offset;
3348   int ack_rst_sent = 0;
3349   uint64_t token_match =
3350       STATE_TOKEN_BASE(coap_decode_var_bytes8(rcvd->actual_token.s,
3351                                               rcvd->actual_token.length));
3352 
3353   memset(&block, 0, sizeof(block));
3354 #if COAP_Q_BLOCK_SUPPORT
3355   memset(&qblock, 0, sizeof(qblock));
3356 #endif /* COAP_Q_BLOCK_SUPPORT */
3357   LL_FOREACH(session->lg_crcv, p) {
3358     size_t chunk = 0;
3359     uint8_t buf[8];
3360     coap_opt_iterator_t opt_iter;
3361 
3362     if (token_match != STATE_TOKEN_BASE(p->state_token) &&
3363         !coap_binary_equal(&rcvd->actual_token, p->app_token)) {
3364       /* try out the next one */
3365       continue;
3366     }
3367 
3368     /* lg_crcv found */
3369 
3370     if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
3371       size_t length;
3372       const uint8_t *data;
3373       coap_opt_t *size_opt = coap_check_option(rcvd, COAP_OPTION_SIZE2,
3374                                                &opt_iter);
3375       size_t size2 = size_opt ?
3376                      coap_decode_var_bytes(coap_opt_value(size_opt),
3377                                            coap_opt_length(size_opt)) : 0;
3378 
3379       /* length and data are cleared on error */
3380       (void)coap_get_data(rcvd, &length, &data);
3381       rcvd->body_offset = 0;
3382       rcvd->body_total = length;
3383       if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
3384         have_block = 1;
3385         block_opt = COAP_OPTION_BLOCK2;
3386       }
3387 #if COAP_Q_BLOCK_SUPPORT
3388       if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) {
3389         if (have_block) {
3390           coap_log_warn("Both Block1 and Q-Block1 not supported in a response\n");
3391         }
3392         have_block = 1;
3393         block_opt = COAP_OPTION_Q_BLOCK2;
3394         block = qblock;
3395         /* server indicating that it supports Q_BLOCK */
3396         if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
3397           set_block_mode_has_q(session->block_mode);
3398         }
3399       }
3400 #endif /* COAP_Q_BLOCK_SUPPORT */
3401       track_echo(session, rcvd);
3402       if (have_block && (block.m || length)) {
3403         coap_opt_t *fmt_opt = coap_check_option(rcvd,
3404                                                 COAP_OPTION_CONTENT_FORMAT,
3405                                                 &opt_iter);
3406         uint16_t fmt = fmt_opt ?
3407                        coap_decode_var_bytes(coap_opt_value(fmt_opt),
3408                                              coap_opt_length(fmt_opt)) :
3409                        COAP_MEDIATYPE_TEXT_PLAIN;
3410         coap_opt_t *etag_opt = coap_check_option(rcvd,
3411                                                  COAP_OPTION_ETAG,
3412                                                  &opt_iter);
3413         size_t saved_offset;
3414         int updated_block;
3415 
3416         if (length > block.chunk_size) {
3417           coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %zu\n",
3418                          block.chunk_size, length);
3419           length = block.chunk_size;
3420         }
3421         /* Possibility that Size2 not sent, or is too small */
3422         chunk = (size_t)1 << (block.szx + 4);
3423         offset = block.num * chunk;
3424         if (size2 < (offset + length)) {
3425           if (block.m)
3426             size2 = offset + length + 1;
3427           else
3428             size2 = offset + length;
3429         }
3430         saved_offset = offset;
3431 
3432         if (p->initial) {
3433 #if COAP_Q_BLOCK_SUPPORT
3434 reinit:
3435 #endif /* COAP_Q_BLOCK_SUPPORT */
3436           p->initial = 0;
3437           if (p->body_data) {
3438             coap_free_type(COAP_STRING, p->body_data);
3439             p->body_data = NULL;
3440           }
3441           if (etag_opt) {
3442             p->etag_length = coap_opt_length(etag_opt);
3443             memcpy(p->etag, coap_opt_value(etag_opt), p->etag_length);
3444             p->etag_set = 1;
3445           } else {
3446             p->etag_set = 0;
3447           }
3448           p->total_len = size2;
3449           p->content_format = fmt;
3450           p->szx = block.szx;
3451           p->block_option = block_opt;
3452           p->last_type = rcvd->type;
3453           p->rec_blocks.used = 0;
3454 #if COAP_Q_BLOCK_SUPPORT
3455           p->rec_blocks.processing_payload_set = 0;
3456 #endif /* COAP_Q_BLOCK_SUPPORT */
3457         }
3458         if (p->total_len < size2)
3459           p->total_len = size2;
3460 
3461         if (etag_opt) {
3462           if (!full_match(coap_opt_value(etag_opt),
3463                           coap_opt_length(etag_opt),
3464                           p->etag, p->etag_length)) {
3465             /* body of data has changed - need to restart request */
3466             coap_pdu_t *pdu;
3467             uint64_t token = STATE_TOKEN_FULL(p->state_token,
3468                                               ++p->retry_counter);
3469             size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
3470             coap_opt_filter_t drop_options;
3471 
3472 #if COAP_Q_BLOCK_SUPPORT
3473             if (block_opt == COAP_OPTION_Q_BLOCK2)
3474               goto reinit;
3475 #endif /* COAP_Q_BLOCK_SUPPORT */
3476 
3477             coap_log_warn("Data body updated during receipt - new request started\n");
3478             if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
3479               coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session);
3480 
3481             p->initial = 1;
3482             coap_free_type(COAP_STRING, p->body_data);
3483             p->body_data = NULL;
3484 
3485             coap_session_new_token(session, &len, buf);
3486             memset(&drop_options, 0, sizeof(coap_opt_filter_t));
3487             coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
3488             pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, &drop_options);
3489             if (!pdu)
3490               goto fail_resp;
3491 
3492             coap_update_option(pdu, block_opt,
3493                                coap_encode_var_safe(buf, sizeof(buf),
3494                                                     (0 << 4) | (0 << 3) | block.aszx),
3495                                buf);
3496 
3497             if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
3498               goto fail_resp;
3499 
3500             goto skip_app_handler;
3501           }
3502         } else if (p->etag_set) {
3503           /* Cannot handle this change in ETag to not being there */
3504           coap_log_warn("Not all blocks have ETag option\n");
3505           goto fail_resp;
3506         }
3507 
3508         if (fmt != p->content_format) {
3509           coap_log_warn("Content-Format option mismatch\n");
3510           goto fail_resp;
3511         }
3512 #if COAP_Q_BLOCK_SUPPORT
3513         if (block_opt == COAP_OPTION_Q_BLOCK2 && size2 != p->total_len) {
3514           coap_log_warn("Size2 option mismatch\n");
3515           goto fail_resp;
3516         }
3517 #endif /* COAP_Q_BLOCK_SUPPORT */
3518         if (block.num == 0) {
3519           coap_opt_t *obs_opt = coap_check_option(rcvd,
3520                                                   COAP_OPTION_OBSERVE,
3521                                                   &opt_iter);
3522           if (obs_opt) {
3523             p->observe_length = min(coap_opt_length(obs_opt), 3);
3524             memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
3525             p->observe_set = 1;
3526           } else {
3527             p->observe_set = 0;
3528           }
3529         }
3530         updated_block = 0;
3531         while (offset < saved_offset + length) {
3532           if (!check_if_received_block(&p->rec_blocks, block.num)) {
3533 #if COAP_Q_BLOCK_SUPPORT
3534             uint32_t this_payload_set = block.num / COAP_MAX_PAYLOADS(session);
3535 #endif /* COAP_Q_BLOCK_SUPPORT */
3536 
3537             coap_log_debug("found Block option, block size is %u, block nr. %u\n",
3538                            1 << (block.szx + 4), block.num);
3539 #if COAP_Q_BLOCK_SUPPORT
3540             if (block_opt == COAP_OPTION_Q_BLOCK2 && p->rec_blocks.used &&
3541                 this_payload_set > p->rec_blocks.processing_payload_set &&
3542                 this_payload_set != p->rec_blocks.latest_payload_set) {
3543               coap_request_missing_q_block2(session, p);
3544             }
3545             p->rec_blocks.latest_payload_set = this_payload_set;
3546 #endif /* COAP_Q_BLOCK_SUPPORT */
3547             /* Update list of blocks received */
3548             if (!update_received_blocks(&p->rec_blocks, block.num)) {
3549               coap_handle_event(context, COAP_EVENT_PARTIAL_BLOCK, session);
3550               goto fail_resp;
3551             }
3552             updated_block = 1;
3553           }
3554           block.num++;
3555           offset = block.num << (block.szx + 4);
3556           if (!block.bert && block_opt != COAP_OPTION_Q_BLOCK2)
3557             break;
3558         }
3559         block.num--;
3560         /* Only process if not duplicate block */
3561         if (updated_block) {
3562           if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
3563             if (size2 < saved_offset + length) {
3564               size2 = saved_offset + length;
3565             }
3566             p->body_data = coap_block_build_body(p->body_data, length, data,
3567                                                  saved_offset, size2);
3568             if (p->body_data == NULL) {
3569               goto fail_resp;
3570             }
3571           }
3572           if (block.m || !check_all_blocks_in(&p->rec_blocks,
3573                                               (size2 + chunk -1) / chunk)) {
3574             /* Not all the payloads of the body have arrived */
3575             size_t len;
3576             coap_pdu_t *pdu;
3577             uint64_t token;
3578 
3579             if (block.m) {
3580 #if COAP_Q_BLOCK_SUPPORT
3581               if (block_opt == COAP_OPTION_Q_BLOCK2) {
3582                 /* Blocks could arrive in wrong order */
3583                 if (check_all_blocks_in(&p->rec_blocks,
3584                                         (size2 + chunk -1) / chunk)) {
3585                   goto give_to_app;
3586                 }
3587                 if (check_all_blocks_in_for_payload_set(session,
3588                                                         &p->rec_blocks)) {
3589                   block.num = p->rec_blocks.range[0].end;
3590                   /* Now requesting next payload */
3591                   p->rec_blocks.processing_payload_set =
3592                       block.num / COAP_MAX_PAYLOADS(session) + 1;
3593                   if (check_any_blocks_next_payload_set(session,
3594                                                         &p->rec_blocks)) {
3595                     /* Need to ask for them individually */
3596                     coap_request_missing_q_block2(session, p);
3597                     goto skip_app_handler;
3598                   }
3599                 } else {
3600                   /* The remote end will be sending the next one unless this
3601                      is a MAX_PAYLOADS and all previous have been received */
3602                   goto skip_app_handler;
3603                 }
3604                 if (COAP_PROTO_RELIABLE(session->proto) ||
3605                     rcvd->type != COAP_MESSAGE_NON)
3606                   goto skip_app_handler;
3607 
3608               } else
3609 #endif /* COAP_Q_BLOCK_SUPPORT */
3610                 block.m = 0;
3611 
3612               /* Ask for the next block */
3613               token = STATE_TOKEN_FULL(p->state_token, ++p->retry_counter);
3614               len = coap_encode_var_safe8(buf, sizeof(token), token);
3615               pdu = coap_pdu_duplicate(&p->pdu, session, len, buf, NULL);
3616               if (!pdu)
3617                 goto fail_resp;
3618 
3619               if (rcvd->type == COAP_MESSAGE_NON)
3620                 pdu->type = COAP_MESSAGE_NON; /* Server is using NON */
3621 
3622               /* Only sent with the first block */
3623               coap_remove_option(pdu, COAP_OPTION_OBSERVE);
3624 
3625               coap_update_option(pdu, block_opt,
3626                                  coap_encode_var_safe(buf, sizeof(buf),
3627                                                       ((block.num + 1) << 4) |
3628                                                       (block.m << 3) | block.aszx),
3629                                  buf);
3630 
3631               if (coap_send_internal(session, pdu) == COAP_INVALID_MID)
3632                 goto fail_resp;
3633             }
3634             if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) ||  block.bert)
3635               goto skip_app_handler;
3636 
3637             /* need to put back original token into rcvd */
3638             coap_update_token(rcvd, p->app_token->length, p->app_token->s);
3639             rcvd->body_offset = saved_offset;
3640 #if COAP_Q_BLOCK_SUPPORT
3641             rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
3642                                p->total_len : size2;
3643 #else /* ! COAP_Q_BLOCK_SUPPORT */
3644             rcvd->body_total = size2;
3645 #endif /* ! COAP_Q_BLOCK_SUPPORT */
3646             coap_log_debug("Client app version of updated PDU\n");
3647             coap_show_pdu(COAP_LOG_DEBUG, rcvd);
3648             goto call_app_handler;
3649           }
3650 #if COAP_Q_BLOCK_SUPPORT
3651 give_to_app:
3652 #endif /* COAP_Q_BLOCK_SUPPORT */
3653           if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
3654             /* Pretend that there is no block */
3655             coap_remove_option(rcvd, block_opt);
3656             if (p->observe_set) {
3657               coap_update_option(rcvd, COAP_OPTION_OBSERVE,
3658                                  p->observe_length, p->observe);
3659             }
3660             rcvd->body_data = p->body_data->s;
3661 #if COAP_Q_BLOCK_SUPPORT
3662             rcvd->body_length = block_opt == COAP_OPTION_Q_BLOCK2 ?
3663                                 p->total_len : saved_offset + length;
3664 #else /* ! COAP_Q_BLOCK_SUPPORT */
3665             rcvd->body_length = saved_offset + length;
3666 #endif /* ! COAP_Q_BLOCK_SUPPORT */
3667             rcvd->body_offset = 0;
3668             rcvd->body_total = rcvd->body_length;
3669           } else {
3670             rcvd->body_offset = saved_offset;
3671 #if COAP_Q_BLOCK_SUPPORT
3672             rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
3673                                p->total_len : size2;
3674 #else /* ! COAP_Q_BLOCK_SUPPORT */
3675             rcvd->body_total = size2;
3676 #endif /* ! COAP_Q_BLOCK_SUPPORT */
3677           }
3678           if (context->response_handler) {
3679             /* need to put back original token into rcvd */
3680             if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) {
3681               coap_update_token(rcvd, p->app_token->length, p->app_token->s);
3682               coap_log_debug("Client app version of updated PDU\n");
3683               coap_show_pdu(COAP_LOG_DEBUG, rcvd);
3684             }
3685             if (context->response_handler(session, sent, rcvd,
3686                                           rcvd->mid) == COAP_RESPONSE_FAIL)
3687               coap_send_rst(session, rcvd);
3688             else
3689               coap_send_ack(session, rcvd);
3690           } else {
3691             coap_send_ack(session, rcvd);
3692           }
3693           ack_rst_sent = 1;
3694           if (p->observe_set == 0) {
3695             /* Expire this entry */
3696             LL_DELETE(session->lg_crcv, p);
3697             coap_block_delete_lg_crcv(session, p);
3698             goto skip_app_handler;
3699           }
3700           /* Set up for the next data body as observing */
3701           p->initial = 1;
3702           if (p->body_data) {
3703             coap_free_type(COAP_STRING, p->body_data);
3704             p->body_data = NULL;
3705           }
3706         }
3707         coap_ticks(&p->last_used);
3708         goto skip_app_handler;
3709       } else {
3710         coap_opt_t *obs_opt = coap_check_option(rcvd,
3711                                                 COAP_OPTION_OBSERVE,
3712                                                 &opt_iter);
3713         if (obs_opt) {
3714           p->observe_length = min(coap_opt_length(obs_opt), 3);
3715           memcpy(p->observe, coap_opt_value(obs_opt), p->observe_length);
3716           p->observe_set = 1;
3717         } else {
3718           p->observe_set = 0;
3719           if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) {
3720             /* need to put back original token into rcvd */
3721             coap_update_token(rcvd, p->app_token->length, p->app_token->s);
3722             coap_log_debug("PDU presented to app.\n");
3723             coap_show_pdu(COAP_LOG_DEBUG, rcvd);
3724           }
3725           /* Expire this entry */
3726           goto expire_lg_crcv;
3727         }
3728       }
3729       coap_ticks(&p->last_used);
3730     } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
3731 #if COAP_OSCORE_SUPPORT
3732       if (check_freshness(session, rcvd,
3733                           (session->oscore_encryption == 0) ? sent : NULL,
3734                           NULL, p))
3735 #else /* !COAP_OSCORE_SUPPORT */
3736       if (check_freshness(session, rcvd, sent, NULL, p))
3737 #endif /* !COAP_OSCORE_SUPPORT */
3738         goto skip_app_handler;
3739       goto expire_lg_crcv;
3740     } else {
3741       /* Not 2.xx or 4.01 - assume it is a failure of some sort */
3742       goto expire_lg_crcv;
3743     }
3744     if (!block.m && !p->observe_set) {
3745 fail_resp:
3746       /* lg_crcv no longer required - cache it for 1 sec */
3747       coap_ticks(&p->last_used);
3748       p->last_used = p->last_used - COAP_MAX_TRANSMIT_WAIT_TICKS(session) +
3749                      COAP_TICKS_PER_SECOND;
3750     }
3751     /* need to put back original token into rcvd */
3752     if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) {
3753       coap_update_token(rcvd, p->app_token->length, p->app_token->s);
3754       coap_log_debug("Client app version of updated PDU (3)\n");
3755       coap_show_pdu(COAP_LOG_DEBUG, rcvd);
3756     }
3757     break;
3758   } /* LL_FOREACH() */
3759 
3760   /* Check if receiving a block response and if blocks can be set up */
3761   if (recursive == COAP_RECURSE_OK && !p) {
3762     if (!sent) {
3763       if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)
3764 #if COAP_Q_BLOCK_SUPPORT
3765           ||
3766           coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)
3767 #endif /* COAP_Q_BLOCK_SUPPORT */
3768          ) {
3769         coap_log_debug("** %s: large body receive internal issue\n",
3770                        coap_session_str(session));
3771         goto skip_app_handler;
3772       }
3773     } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
3774       if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
3775 #if COAP_Q_BLOCK_SUPPORT
3776         if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) {
3777           set_block_mode_drop_q(session->block_mode);
3778           coap_log_debug("Q-Block support disabled\n");
3779         }
3780 #endif /* COAP_Q_BLOCK_SUPPORT */
3781         have_block = 1;
3782         block_opt = COAP_OPTION_BLOCK2;
3783         if (block.num != 0) {
3784           /* Assume random access and just give the single response to app */
3785           size_t length;
3786           const uint8_t *data;
3787           size_t chunk = (size_t)1 << (block.szx + 4);
3788 
3789           coap_get_data(rcvd, &length, &data);
3790           rcvd->body_offset = block.num*chunk;
3791           rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
3792           goto call_app_handler;
3793         }
3794       }
3795 #if COAP_Q_BLOCK_SUPPORT
3796       else if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)) {
3797         have_block = 1;
3798         block_opt = COAP_OPTION_Q_BLOCK2;
3799         /* server indicating that it supports Q_BLOCK2 */
3800         if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
3801           set_block_mode_has_q(session->block_mode);
3802         }
3803       }
3804 #endif /* COAP_Q_BLOCK_SUPPORT */
3805       if (have_block) {
3806         coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
3807 
3808         if (lg_crcv) {
3809           LL_PREPEND(session->lg_crcv, lg_crcv);
3810           return coap_handle_response_get_block(context, session, sent, rcvd,
3811                                                 COAP_RECURSE_NO);
3812         }
3813       }
3814       track_echo(session, rcvd);
3815     } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
3816       coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
3817 
3818       if (lg_crcv) {
3819         LL_PREPEND(session->lg_crcv, lg_crcv);
3820         return coap_handle_response_get_block(context, session, sent, rcvd,
3821                                               COAP_RECURSE_NO);
3822       }
3823     }
3824   }
3825   return 0;
3826 
3827 expire_lg_crcv:
3828   /* need to put back original token into rcvd */
3829   if (!coap_binary_equal(&rcvd->actual_token, p->app_token)) {
3830     coap_update_token(rcvd, p->app_token->length, p->app_token->s);
3831     coap_log_debug("Client app version of updated PDU\n");
3832     coap_show_pdu(COAP_LOG_DEBUG, rcvd);
3833   }
3834   /* Expire this entry */
3835   LL_DELETE(session->lg_crcv, p);
3836   coap_block_delete_lg_crcv(session, p);
3837 
3838 call_app_handler:
3839   return 0;
3840 
3841 skip_app_handler:
3842   if (!ack_rst_sent)
3843     coap_send_ack(session, rcvd);
3844   return 1;
3845 }
3846 #endif /* COAP_CLIENT_SUPPORT */
3847 
3848 #if COAP_SERVER_SUPPORT
3849 /* Check if lg_xmit generated and update PDU code if so */
3850 void
coap_check_code_lg_xmit(const coap_session_t * session,const coap_pdu_t * request,coap_pdu_t * response,const coap_resource_t * resource,const coap_string_t * query)3851 coap_check_code_lg_xmit(const coap_session_t *session,
3852                         const coap_pdu_t *request,
3853                         coap_pdu_t *response, const coap_resource_t *resource,
3854                         const coap_string_t *query) {
3855   coap_lg_xmit_t *lg_xmit;
3856 
3857   if (response->code == 0)
3858     return;
3859   lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
3860   if (lg_xmit && lg_xmit->pdu.code == 0) {
3861     lg_xmit->pdu.code = response->code;
3862     return;
3863   }
3864 }
3865 #endif /* COAP_SERVER_SUPPORT */
3866 
3867 #if COAP_CLIENT_SUPPORT
3868 void
coap_check_update_token(coap_session_t * session,coap_pdu_t * pdu)3869 coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu) {
3870   uint64_t token_match =
3871       STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s,
3872                                               pdu->actual_token.length));
3873   coap_lg_xmit_t *lg_xmit;
3874   coap_lg_crcv_t *lg_crcv;
3875 
3876   if (session->lg_crcv) {
3877     LL_FOREACH(session->lg_crcv, lg_crcv) {
3878       if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token))
3879         return;
3880       if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) {
3881         coap_update_token(pdu, lg_crcv->app_token->length,
3882                           lg_crcv->app_token->s);
3883         coap_log_debug("Client app version of updated PDU\n");
3884         coap_show_pdu(COAP_LOG_DEBUG, pdu);
3885         return;
3886       }
3887     }
3888   }
3889   if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) {
3890     LL_FOREACH(session->lg_xmit, lg_xmit) {
3891       if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token))
3892         return;
3893       if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) {
3894         coap_update_token(pdu, lg_xmit->b.b1.app_token->length,
3895                           lg_xmit->b.b1.app_token->s);
3896         coap_log_debug("Client app version of updated PDU\n");
3897         coap_show_pdu(COAP_LOG_DEBUG, pdu);
3898         return;
3899       }
3900     }
3901   }
3902 }
3903 #endif /* ! COAP_CLIENT_SUPPORT */
3904