xref: /third_party/libcoap/src/coap_block.c (revision c87c5fba)
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
27int
28coap_q_block_is_supported(void) {
29  return 1;
30}
31#else /* ! COAP_Q_BLOCK_SUPPORT */
32int
33coap_q_block_is_supported(void) {
34  return 0;
35}
36#endif /* ! COAP_Q_BLOCK_SUPPORT */
37
38unsigned int
39coap_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
57int
58coap_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
110int
111coap_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
127static int
128setup_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
169int
170coap_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
202int
203coap_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
233int
234coap_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
247int
248coap_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 */
272void
273coap_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
372error:
373  coap_add_data(response,
374                strlen(coap_response_phrase(response->code)),
375                (const unsigned char *)coap_response_phrase(response->code));
376}
377
378void
379coap_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
396COAP_STATIC_INLINE int
397full_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
404int
405coap_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
493coap_mid_t
494coap_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  }
542error:
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 */
552coap_lg_xmit_t *
553coap_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
591static int
592coap_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    }
974add_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
983fail:
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
993int
994coap_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
1015int
1016coap_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
1120error:
1121  if (release_func)
1122    release_func(session, app_ptr);
1123error_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 */
1137int
1138coap_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
1185static coap_pdu_t *
1186coap_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
1204static void
1205coap_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  }
1292send_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 */
1306int
1307coap_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 */
1353check_expire:
1354    if (!p->observe_set && p->last_used &&
1355        p->last_used + partial_timeout <= now) {
1356#if COAP_Q_BLOCK_SUPPORT
1357expire:
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
1376static coap_pdu_t *
1377pdu_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
1398static int
1399add_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
1436static int
1437check_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
1449static int
1450check_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
1469static int
1470check_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
1479static int
1480check_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 */
1496int
1497coap_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 */
1613check_expire:
1614    if (p->last_used && p->last_used + partial_timeout <= now) {
1615#if COAP_Q_BLOCK_SUPPORT
1616expire:
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 */
1637coap_mid_t
1638coap_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
1779coap_tick_t
1780coap_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
1829coap_tick_t
1830coap_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 */
1886static coap_bin_const_t *
1887track_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
1928coap_mid_t
1929coap_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 */
1958coap_mid_t
1959coap_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 */
1988coap_mid_t
1989coap_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
2028coap_lg_crcv_t *
2029coap_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
2095void
2096coap_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
2121void
2122coap_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
2141void
2142coap_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
2164typedef struct {
2165  uint32_t num;
2166  int is_continue;
2167} send_track;
2168
2169static int
2170add_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 */
2212int
2213coap_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
2492call_app_handler:
2493  coap_free_type(COAP_STRING, out_blocks);
2494  return 0;
2495#endif /* COAP_Q_BLOCK_SUPPORT */
2496
2497internal_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
2506skip_app_handler:
2507  coap_free_type(COAP_STRING, out_blocks);
2508  return 1;
2509}
2510#endif /* COAP_SERVER_SUPPORT */
2511
2512static int
2513update_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 */
2575int
2576coap_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
2815give_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
2861free_lg_srcv:
2862      LL_DELETE(session->lg_srcv, p);
2863      coap_block_delete_lg_srcv(session, p);
2864      goto skip_app_handler;
2865    }
2866  }
2867call_app_handler:
2868  return 0;
2869
2870skip_app_handler:
2871  return 1;
2872}
2873#endif /* COAP_SERVER_SUPPORT */
2874
2875#if COAP_CLIENT_SUPPORT
2876#if COAP_Q_BLOCK_SUPPORT
2877static uint32_t
2878derive_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
2912static int
2913check_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 */
2986not_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
2995static void
2996track_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 */
3021int
3022coap_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      }
3243fail_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
3251fail_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);
3255lg_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 */
3284coap_binary_t *
3285coap_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 */
3334int
3335coap_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
3434reinit:
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
3651give_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) {
3745fail_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
3827expire_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
3838call_app_handler:
3839  return 0;
3840
3841skip_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 */
3850void
3851coap_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
3868void
3869coap_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