Coverage Report

Created: 2026-09-03 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/src/coap_block.c
Line
Count
Source
1
/* coap_block.c -- block transfer
2
 *
3
 * Copyright (C) 2010--2012,2015-2026 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_libcoap_build.h"
17
18
#include <stdio.h>
19
20
#ifndef min
21
0
#define min(a,b) ((a) < (b) ? (a) : (b))
22
#endif
23
24
/* Can be 1 - 8 bytes long */
25
#ifndef COAP_ETAG_MAX_BYTES
26
0
#define COAP_ETAG_MAX_BYTES 4
27
#endif
28
#if COAP_ETAG_MAX_BYTES < 1 || COAP_ETAG_MAX_BYTES > 8
29
#error COAP_ETAG_MAX_BYTES byte size invalid
30
#endif
31
32
0
#define COAP_LG_XMIT_TXT_SCALAR (8)
33
34
#if COAP_Q_BLOCK_SUPPORT
35
static int blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num);
36
static int blocks_add_entry(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m);
37
#endif /* COAP_Q_BLOCK_SUPPORT */
38
39
#if COAP_Q_BLOCK_SUPPORT
40
int
41
0
coap_q_block_is_supported(void) {
42
0
  return 1;
43
0
}
44
#else /* ! COAP_Q_BLOCK_SUPPORT */
45
int
46
coap_q_block_is_supported(void) {
47
  return 0;
48
}
49
#endif /* ! COAP_Q_BLOCK_SUPPORT */
50
51
unsigned int
52
0
coap_opt_block_num(const coap_opt_t *block_opt) {
53
0
  unsigned int num = 0;
54
0
  uint16_t len;
55
56
0
  len = coap_opt_length(block_opt);
57
58
0
  if (len == 0) {
59
0
    return 0;
60
0
  }
61
62
0
  if (len > 1) {
63
0
    num = coap_decode_var_bytes(coap_opt_value(block_opt),
64
0
                                coap_opt_length(block_opt) - 1);
65
0
  }
66
67
0
  return (num << 4) | ((COAP_OPT_BLOCK_END_BYTE(block_opt) & 0xF0) >> 4);
68
0
}
69
70
int
71
coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu,
72
0
                 coap_option_num_t number, coap_block_b_t *block) {
73
0
  coap_opt_iterator_t opt_iter;
74
0
  coap_opt_t *option;
75
76
0
  assert(block);
77
0
  memset(block, 0, sizeof(coap_block_b_t));
78
79
0
  if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) {
80
0
    uint32_t num;
81
82
0
    if (COAP_OPT_BLOCK_MORE(option))
83
0
      block->m = 1;
84
0
    block->aszx = block->szx = COAP_OPT_BLOCK_SZX(option);
85
0
    if (block->szx == 7) {
86
0
      size_t length;
87
0
      const uint8_t *data;
88
89
0
      if (session == NULL || COAP_PROTO_NOT_RELIABLE(session->proto) ||
90
0
          !(session->csm_bert_rem_support && session->csm_bert_loc_support))
91
        /* No BERT support */
92
0
        return 0;
93
94
0
      block->szx = 6; /* BERT is 1024 block chunks */
95
0
      block->bert = 1;
96
0
      if (coap_get_data(pdu, &length, &data)) {
97
0
        if (block->m && (length % 1024) != 0) {
98
0
          coap_log_debug("block: Oversized packet - reduced to %" PRIuS " from %" PRIuS "\n",
99
0
                         length - (length % 1024), length);
100
0
          length -= length % 1024;
101
0
        }
102
0
        block->chunk_size = (uint32_t)length;
103
0
      } else
104
0
        block->chunk_size = 0;
105
0
    } else {
106
0
      block->chunk_size = (size_t)1 << (block->szx + 4);
107
0
    }
108
0
    block->defined = 1;
109
110
    /* The block number is at most 20 bits, so values above 2^20 - 1
111
     * are illegal. */
112
0
    num = coap_opt_block_num(option);
113
0
    if (num > 0xFFFFF) {
114
0
      return 0;
115
0
    }
116
0
    block->num = num;
117
0
    return 1;
118
0
  }
119
120
0
  return 0;
121
0
}
122
123
int
124
coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number,
125
0
               coap_block_t *block) {
126
0
  coap_block_b_t block_b;
127
128
0
  assert(block);
129
0
  memset(block, 0, sizeof(coap_block_t));
130
131
0
  if (coap_get_block_b(NULL, pdu, number, &block_b)) {
132
0
    block->num = block_b.num;
133
0
    block->m   = block_b.m;
134
0
    block->szx = block_b.szx;
135
0
    return 1;
136
0
  }
137
0
  return 0;
138
0
}
139
140
static int
141
setup_block_b(coap_session_t *session, coap_pdu_t *pdu, coap_block_b_t *block,
142
              unsigned int num,
143
0
              unsigned int blk_size, size_t total) {
144
0
  size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
145
0
  size_t avail = pdu->max_size - token_options;
146
0
  unsigned int start = num << (blk_size + 4);
147
0
  unsigned int can_use_bert = block->defined == 0 || block->bert;
148
149
0
  assert(start <= total);
150
0
  memset(block, 0, sizeof(*block));
151
0
  block->num = num;
152
0
  block->szx = block->aszx = blk_size;
153
0
  if (can_use_bert && blk_size == 6 && avail >= 1024 && session != NULL &&
154
0
      COAP_PROTO_RELIABLE(session->proto) &&
155
0
      session->csm_bert_rem_support && session->csm_bert_loc_support) {
156
0
    block->bert = 1;
157
0
    block->aszx = 7;
158
0
    block->chunk_size = (uint32_t)((avail / 1024) * 1024);
159
0
  } else {
160
0
    block->chunk_size = (size_t)1 << (blk_size + 4);
161
0
    if (avail < block->chunk_size && (total - start) >= avail) {
162
      /* Need to reduce block size */
163
0
      unsigned int szx;
164
0
      int new_blk_size;
165
166
0
      if (avail < 16) {         /* bad luck, this is the smallest block size */
167
0
        coap_log_debug("not enough space, even the smallest block does not fit (1)\n");
168
0
        return 0;
169
0
      }
170
0
      new_blk_size = coap_flsll((long long)avail) - 5;
171
0
      coap_log_debug("decrease block size for %" PRIuS " to %d\n", avail, new_blk_size);
172
0
      szx = block->szx;
173
0
      block->szx = new_blk_size;
174
0
      block->num <<= szx - block->szx;
175
0
      block->chunk_size = (size_t)1 << (new_blk_size + 4);
176
0
    }
177
0
  }
178
0
  block->m = block->chunk_size < total - start;
179
0
  return 1;
180
0
}
181
182
int
183
coap_write_block_opt(coap_block_t *block, coap_option_num_t number,
184
0
                     coap_pdu_t *pdu, size_t data_length) {
185
0
  size_t start;
186
0
  unsigned char buf[4];
187
0
  coap_block_b_t block_b;
188
189
0
  assert(pdu);
190
191
0
  start = block->num << (block->szx + 4);
192
0
  if (block->num != 0 && data_length <= start) {
193
0
    coap_log_debug("illegal block requested\n");
194
0
    return -2;
195
0
  }
196
197
0
  assert(pdu->max_size > 0);
198
199
0
  block_b.defined = 1;
200
0
  block_b.bert = 0;
201
0
  if (!setup_block_b(NULL, pdu, &block_b, block->num,
202
0
                     block->szx, data_length))
203
0
    return -3;
204
205
  /* to re-encode the block option */
206
0
  coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
207
0
                                                       ((block_b.num << 4) |
208
0
                                                        (block_b.m << 3) |
209
0
                                                        block_b.szx)),
210
0
                     buf);
211
212
0
  return 1;
213
0
}
214
215
int
216
coap_write_block_b_opt(coap_session_t *session, coap_block_b_t *block,
217
                       coap_option_num_t number,
218
0
                       coap_pdu_t *pdu, size_t data_length) {
219
0
  size_t start;
220
0
  unsigned char buf[4];
221
222
0
  assert(pdu);
223
224
0
  start = block->num << (block->szx + 4);
225
0
  if (block->num != 0 && data_length <= start) {
226
0
    coap_log_debug("illegal block requested\n");
227
0
    return -2;
228
0
  }
229
230
0
  assert(pdu->max_size > 0);
231
232
0
  if (!setup_block_b(session, pdu, block, block->num,
233
0
                     block->szx, data_length))
234
0
    return -3;
235
236
  /* to re-encode the block option */
237
0
  coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf),
238
0
                                                       ((block->num << 4) |
239
0
                                                        (block->m << 3) |
240
0
                                                        block->aszx)),
241
0
                     buf);
242
243
0
  return 1;
244
0
}
245
246
int
247
coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data,
248
0
               unsigned int block_num, unsigned char block_szx) {
249
0
  unsigned int start;
250
0
  start = block_num << (block_szx + 4);
251
252
0
  if (len <= start)
253
0
    return 0;
254
255
0
  return coap_add_data(pdu,
256
0
                       min(len - start, ((size_t)1 << (block_szx + 4))),
257
0
                       data + start);
258
0
}
259
260
int
261
coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data,
262
0
                      coap_block_b_t *block) {
263
0
  unsigned int start = block->num << (block->szx + 4);
264
0
  size_t max_size;
265
266
0
  if (len <= start)
267
0
    return 0;
268
269
0
  if (block->bert) {
270
0
    size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
271
0
    max_size = ((pdu->max_size - token_options) / 1024) * 1024;
272
0
  } else {
273
0
    max_size = (size_t)1 << (block->szx + 4);
274
0
  }
275
0
  block->chunk_size = (uint32_t)max_size;
276
277
0
  return coap_add_data(pdu,
278
0
                       min(len - start, max_size),
279
0
                       data + start);
280
0
}
281
282
/*
283
 * Note that the COAP_OPTION_ have to be added in the correct order
284
 */
285
void
286
coap_add_data_blocked_response(const coap_pdu_t *request,
287
                               coap_pdu_t *response,
288
                               uint16_t media_type,
289
                               int maxage,
290
                               size_t length,
291
                               const uint8_t *data
292
0
                              ) {
293
0
  unsigned char buf[4];
294
0
  coap_block_t block2;
295
0
  int block2_requested = 0;
296
0
#if COAP_SERVER_SUPPORT
297
0
  uint64_t etag = 0;
298
0
  coap_digest_t digest;
299
0
  coap_digest_ctx_t *dctx = NULL;
300
0
#endif /* COAP_SERVER_SUPPORT */
301
302
0
  memset(&block2, 0, sizeof(block2));
303
  /*
304
   * Need to check that a valid block is getting asked for so that the
305
   * correct options are put into the PDU.
306
   */
307
0
  if (request) {
308
0
    if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) {
309
0
      block2_requested = 1;
310
0
      if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) {
311
0
        coap_log_debug("Illegal block requested (%d > last = %" PRIuS ")\n",
312
0
                       block2.num,
313
0
                       length >> (block2.szx + 4));
314
0
        response->code = COAP_RESPONSE_CODE(400);
315
0
        goto error;
316
0
      }
317
0
    }
318
0
  }
319
0
  response->code = COAP_RESPONSE_CODE(205);
320
321
0
#if COAP_SERVER_SUPPORT
322
  /* add ETag for the resource data */
323
0
  if (length) {
324
0
    dctx =  coap_digest_setup();
325
0
    if (!dctx)
326
0
      goto error;
327
0
    if (request && request->session &&
328
0
        coap_is_mcast(&request->session->addr_info.local)) {
329
0
      coap_digest_update(dctx, coap_unique_id, sizeof(coap_unique_id));
330
0
    }
331
0
    if (!coap_digest_update(dctx, data, length))
332
0
      goto error;
333
0
    if (!coap_digest_final(dctx, &digest))
334
0
      goto error;
335
0
    dctx = NULL;
336
0
    memcpy(&etag, digest.key, sizeof(etag));
337
0
#if COAP_ETAG_MAX_BYTES != 8
338
0
    etag = etag >> 8*(8 - COAP_ETAG_MAX_BYTES);
339
0
#endif
340
0
    if (!etag)
341
0
      etag = 1;
342
0
    coap_update_option(response,
343
0
                       COAP_OPTION_ETAG,
344
0
                       coap_encode_var_safe8(buf, sizeof(buf), etag),
345
0
                       buf);
346
0
  }
347
0
#endif /* COAP_SERVER_SUPPORT */
348
349
0
  coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT,
350
0
                     coap_encode_var_safe(buf, sizeof(buf),
351
0
                                          media_type),
352
0
                     buf);
353
354
0
  if (maxage >= 0) {
355
0
    coap_insert_option(response,
356
0
                       COAP_OPTION_MAXAGE,
357
0
                       coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
358
0
  }
359
360
0
  if (block2_requested) {
361
0
    int res;
362
363
0
    res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
364
365
0
    switch (res) {
366
0
    case -2:                        /* illegal block (caught above) */
367
0
      response->code = COAP_RESPONSE_CODE(400);
368
0
      goto error;
369
0
    case -1:                        /* should really not happen */
370
0
      assert(0);
371
    /* fall through if assert is a no-op */
372
0
    case -3:                        /* cannot handle request */
373
0
      response->code = COAP_RESPONSE_CODE(500);
374
0
      goto error;
375
0
    default:                        /* everything is good */
376
0
      ;
377
0
    }
378
379
0
    coap_add_option_internal(response,
380
0
                             COAP_OPTION_SIZE2,
381
0
                             coap_encode_var_safe8(buf, sizeof(buf), length),
382
0
                             buf);
383
384
0
    coap_add_block(response, length, data,
385
0
                   block2.num, block2.szx);
386
0
    return;
387
0
  }
388
389
  /*
390
   * Block2 not requested
391
   */
392
0
  if (!coap_add_data(response, length, data)) {
393
    /*
394
     * Insufficient space to add in data - use block mode
395
     * set initial block size, will be lowered by
396
     * coap_write_block_opt() automatically
397
     */
398
0
    block2.num = 0;
399
0
    block2.szx = 6;
400
0
    coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length);
401
402
0
    coap_add_option_internal(response,
403
0
                             COAP_OPTION_SIZE2,
404
0
                             coap_encode_var_safe8(buf, sizeof(buf), length),
405
0
                             buf);
406
407
0
    coap_add_block(response, length, data,
408
0
                   block2.num, block2.szx);
409
0
  }
410
0
  return;
411
412
0
error:
413
0
#if COAP_SERVER_SUPPORT
414
0
  coap_digest_free(dctx);
415
0
#endif /* COAP_SERVER_SUPPORT */
416
0
  coap_add_data(response,
417
0
                strlen(coap_response_phrase(response->code)),
418
0
                (const unsigned char *)coap_response_phrase(response->code));
419
0
}
420
421
COAP_API void
422
coap_context_set_block_mode(coap_context_t *context,
423
0
                            uint32_t block_mode) {
424
0
  coap_lock_lock(return);
425
0
  coap_context_set_block_mode_lkd(context, block_mode);
426
0
  coap_lock_unlock();
427
0
}
428
429
void
430
coap_context_set_block_mode_lkd(coap_context_t *context,
431
0
                                uint32_t block_mode) {
432
0
  coap_lock_check_locked();
433
0
  if (!(block_mode & COAP_BLOCK_USE_LIBCOAP))
434
0
    block_mode = 0;
435
0
  context->block_mode &= ~COAP_BLOCK_SET_MASK;
436
0
  context->block_mode |= block_mode & COAP_BLOCK_SET_MASK;
437
#if ! COAP_Q_BLOCK_SUPPORT
438
  if (block_mode & (COAP_BLOCK_TRY_Q_BLOCK|COAP_BLOCK_USE_M_Q_BLOCK|COAP_BLOCK_FORCE_Q_BLOCK))
439
    coap_log_debug("Q-Block support not compiled in - ignored\n");
440
#endif /* ! COAP_Q_BLOCK_SUPPORT */
441
0
}
442
443
COAP_API int
444
coap_context_set_max_block_size(coap_context_t *context,
445
0
                                size_t max_block_size) {
446
0
  int ret;
447
448
0
  coap_lock_lock(return 0);
449
0
  ret = coap_context_set_max_block_size_lkd(context, max_block_size);
450
0
  coap_lock_unlock();
451
0
  return ret;
452
0
}
453
454
int
455
0
coap_context_set_max_block_size_lkd(coap_context_t *context, size_t max_block_size) {
456
0
  switch (max_block_size) {
457
0
  case 0:
458
0
  case 16:
459
0
  case 32:
460
0
  case 64:
461
0
  case 128:
462
0
  case 256:
463
0
  case 512:
464
0
  case 1024:
465
0
    break;
466
0
  default:
467
0
    coap_log_info("coap_context_set_max_block_size: Invalid max block size (%" PRIuS ")\n",
468
0
                  max_block_size);
469
0
    return 0;
470
0
  }
471
0
  coap_lock_check_locked();
472
0
  max_block_size = (coap_fls((uint32_t)max_block_size >> 4) - 1) & 0x07;
473
0
  context->block_mode &= ~COAP_BLOCK_MAX_SIZE_MASK;
474
0
  context->block_mode |= COAP_BLOCK_MAX_SIZE_SET((uint32_t)max_block_size);
475
0
  return 1;
476
0
}
477
478
COAP_STATIC_INLINE int
479
full_match(const uint8_t *a, size_t alen,
480
0
           const uint8_t *b, size_t blen) {
481
0
  return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
482
0
}
483
484
coap_lg_xmit_t *
485
0
coap_find_lg_xmit(coap_session_t *session, coap_pdu_t *pdu) {
486
0
  coap_lg_xmit_t *lg_xmit = NULL;
487
0
  coap_lg_xmit_t *m_lg_xmit = NULL;
488
0
  uint64_t token_match =
489
0
      STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s,
490
0
                                              pdu->actual_token.length));
491
492
0
  LL_FOREACH(session->lg_xmit, lg_xmit) {
493
0
    if (token_match != STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) &&
494
0
        !coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) {
495
      /* try out the next one */
496
0
      continue;
497
0
    }
498
0
#if COAP_CLIENT_SUPPORT
499
0
    if (COAP_PDU_IS_RESPONSE(pdu)) {
500
0
      if (coap_is_mcast(&lg_xmit->b.b1.upstream)) {
501
0
        m_lg_xmit = lg_xmit;
502
0
      }
503
0
      if (!coap_address_equals(&lg_xmit->b.b1.upstream, &session->addr_info.remote)) {
504
        /* try out the next one */
505
0
        continue;
506
0
      }
507
0
    }
508
    /* Have a match */
509
0
    return lg_xmit;
510
0
#endif /* COAP_CLIENT_SUPPORT */
511
0
  }
512
0
  if (m_lg_xmit && (session->sock.flags & COAP_SOCKET_MULTICAST)) {
513
    /* Need to set up unicast version of mcast lg_xmit entry */
514
0
    lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
515
0
    if (!lg_xmit)
516
0
      return NULL;
517
0
    memcpy(lg_xmit, m_lg_xmit, sizeof(coap_lg_xmit_t));
518
0
    lg_xmit->next = NULL;
519
0
    lg_xmit->b.b1.app_token = NULL;
520
0
    lg_xmit->data_info->ref++;
521
0
    lg_xmit->sent_pdu = coap_pdu_reference_lkd(m_lg_xmit->sent_pdu);
522
0
    coap_address_copy(&lg_xmit->b.b1.upstream, &session->addr_info.remote);
523
0
    lg_xmit->b.b1.app_token = coap_new_binary(m_lg_xmit->b.b1.app_token->length);
524
0
    if (!lg_xmit->b.b1.app_token)
525
0
      goto fail;
526
0
    if (m_lg_xmit->b.b1.app_token->length)
527
0
      memcpy(lg_xmit->b.b1.app_token->s, m_lg_xmit->b.b1.app_token->s,
528
0
             m_lg_xmit->b.b1.app_token->length);
529
0
    LL_PREPEND(session->lg_xmit, lg_xmit);
530
0
    coap_log_debug("** %s: lg_xmit %p mcast slave initialized\n",
531
0
                   coap_session_str(session), (void *)lg_xmit);
532
    /* Allow the mcast lg_xmit to time out earlier */
533
0
    coap_ticks(&m_lg_xmit->last_all_sent);
534
0
#if COAP_CLIENT_SUPPORT
535
0
    if (COAP_PDU_IS_RESPONSE(pdu)) {
536
0
      coap_lg_crcv_t *lg_crcv;
537
538
0
      lg_crcv = coap_find_lg_crcv(session, pdu);
539
0
      if (lg_crcv) {
540
0
        lg_xmit->b.b1.state_token = lg_crcv->state_token;
541
0
      }
542
0
    }
543
0
#endif /* COAP_CLIENT_SUPPORT */
544
0
  }
545
0
  return lg_xmit;
546
547
0
fail:
548
0
  coap_block_delete_lg_xmit(session, lg_xmit);
549
0
  return NULL;
550
0
}
551
552
#if COAP_CLIENT_SUPPORT
553
554
COAP_API int
555
coap_cancel_observe(coap_session_t *session, coap_binary_t *token,
556
0
                    coap_pdu_type_t type) {
557
0
  int ret;
558
559
0
  coap_lock_lock(return 0);
560
0
  ret = coap_cancel_observe_lkd(session, token, type);
561
0
  coap_lock_unlock();
562
0
  return ret;
563
0
}
564
565
int
566
coap_cancel_observe_lkd(coap_session_t *session, coap_binary_t *token,
567
0
                        coap_pdu_type_t type) {
568
0
  coap_lg_crcv_t *lg_crcv, *q;
569
570
0
  assert(session);
571
0
  if (!session)
572
0
    return 0;
573
574
0
  coap_lock_check_locked();
575
0
  if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
576
0
    coap_log_debug("** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n",
577
0
                   coap_session_str(session));
578
0
    return 0;
579
0
  }
580
581
0
  LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) {
582
0
    if (lg_crcv->observe_set) {
583
0
      if ((!token && !lg_crcv->app_token->length) || (token &&
584
0
                                                      coap_binary_equal(token, lg_crcv->app_token))) {
585
0
        uint8_t buf[8];
586
0
        coap_mid_t mid;
587
0
        size_t size;
588
0
        const uint8_t *data;
589
0
#if COAP_Q_BLOCK_SUPPORT
590
0
        coap_block_b_t block;
591
0
        int using_q_block1 = coap_get_block_b(session, lg_crcv->sent_pdu,
592
0
                                              COAP_OPTION_Q_BLOCK1, &block);
593
0
#endif /* COAP_Q_BLOCK_SUPPORT */
594
0
        coap_bin_const_t *otoken = lg_crcv->obs_token ?
595
0
                                   lg_crcv->obs_token[0] ?
596
0
                                   lg_crcv->obs_token[0] :
597
0
                                   (coap_bin_const_t *)lg_crcv->app_token :
598
0
                                   (coap_bin_const_t *)lg_crcv->app_token;
599
0
        coap_pdu_t *pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu,
600
0
                                                 session,
601
0
                                                 otoken->length,
602
0
                                                 otoken->s,
603
0
                                                 NULL,
604
0
                                                 COAP_BOOL_FALSE);
605
606
0
        lg_crcv->observe_set = 0;
607
0
        if (pdu == NULL)
608
0
          return 0;
609
        /* Need to make sure that this is the correct requested type */
610
0
        pdu->type = type;
611
612
0
        coap_update_option(pdu, COAP_OPTION_OBSERVE,
613
0
                           coap_encode_var_safe(buf, sizeof(buf),
614
0
                                                COAP_OBSERVE_CANCEL),
615
0
                           buf);
616
0
        if (lg_crcv->o_block_option) {
617
0
          coap_update_option(pdu, lg_crcv->o_block_option,
618
0
                             coap_encode_var_safe(buf, sizeof(buf),
619
0
                                                  lg_crcv->o_blk_size),
620
0
                             buf);
621
0
        }
622
0
        if (lg_crcv->obs_data) {
623
0
          coap_add_data_large_request_lkd(session, pdu,
624
0
                                          lg_crcv->obs_data->length,
625
0
                                          lg_crcv->obs_data->data, NULL, NULL);
626
0
        } else if (coap_get_data(lg_crcv->sent_pdu, &size, &data)) {
627
0
          coap_add_data_large_request_lkd(session, pdu, size, data, NULL, NULL);
628
0
        }
629
630
        /*
631
         * Need to fix lg_xmit stateless token as using tokens from
632
         * observe setup
633
         */
634
0
        if (pdu->lg_xmit)
635
0
          pdu->lg_xmit->b.b1.state_token = lg_crcv->state_token;
636
637
0
        coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream);
638
0
#if COAP_Q_BLOCK_SUPPORT
639
        /* See if large xmit using Q-Block1 (but not testing Q-Block1) */
640
0
        if (using_q_block1) {
641
0
          mid = coap_send_q_block1(session, block, pdu, COAP_SEND_INC_PDU);
642
0
        } else {
643
0
          mid = coap_send_internal(session, pdu, NULL);
644
0
        }
645
#else /* ! COAP_Q_BLOCK_SUPPORT */
646
        mid = coap_send_internal(session, pdu, NULL);
647
#endif /* ! COAP_Q_BLOCK_SUPPORT */
648
0
        if (mid == COAP_INVALID_MID)
649
0
          break;
650
0
      }
651
0
    }
652
0
  }
653
0
  return 1;
654
0
}
655
656
coap_lg_crcv_t *
657
0
coap_find_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) {
658
0
  coap_lg_crcv_t *lg_crcv;
659
0
  coap_lg_crcv_t *m_lg_crcv = NULL;
660
0
  uint64_t token_match =
661
0
      STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s,
662
0
                                              pdu->actual_token.length));
663
664
0
  LL_FOREACH(session->lg_crcv, lg_crcv) {
665
0
    if (token_match != STATE_TOKEN_BASE(lg_crcv->state_token) &&
666
0
        !coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) {
667
      /* try out the next one */
668
0
      continue;
669
0
    }
670
0
    if (coap_is_mcast(&lg_crcv->upstream)) {
671
0
      m_lg_crcv = lg_crcv;
672
0
    }
673
0
    if (!coap_address_equals(&lg_crcv->upstream, &session->addr_info.remote)) {
674
      /* try out the next one */
675
0
      continue;
676
0
    }
677
    /* Have a match */
678
0
    return lg_crcv;
679
0
  }
680
0
  if (m_lg_crcv && (session->sock.flags & COAP_SOCKET_MULTICAST)) {
681
    /* Need to set up unicast version of mcast lg_crcv entry */
682
0
    lg_crcv = coap_block_new_lg_crcv(session, m_lg_crcv->sent_pdu, NULL);
683
0
    if (lg_crcv) {
684
0
      if (m_lg_crcv->obs_data) {
685
0
        m_lg_crcv->obs_data->ref++;
686
0
        lg_crcv->obs_data = m_lg_crcv->obs_data;
687
0
      }
688
0
      LL_PREPEND(session->lg_crcv, lg_crcv);
689
0
    }
690
0
  }
691
0
  return lg_crcv;
692
0
}
693
694
#if COAP_OSCORE_SUPPORT
695
coap_mid_t
696
coap_retransmit_oscore_pdu(coap_session_t *session,
697
                           coap_pdu_t *pdu,
698
0
                           coap_opt_t *echo) {
699
0
  coap_lg_crcv_t *lg_crcv;
700
0
  uint8_t ltoken[8];
701
0
  size_t ltoken_len;
702
0
  uint64_t token;
703
0
  const uint8_t *data;
704
0
  size_t data_len;
705
0
  coap_pdu_t *resend_pdu;
706
0
  coap_block_b_t block;
707
708
0
  lg_crcv = coap_find_lg_crcv(session, pdu);
709
0
  if (lg_crcv) {
710
0
    coap_opt_iterator_t opt_iter;
711
0
    coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE,
712
0
                                        &opt_iter);
713
    /* Re-send request with new token */
714
0
    token = STATE_TOKEN_FULL(lg_crcv->state_token,
715
0
                             ++lg_crcv->retry_counter);
716
0
    ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
717
    /* There could be a Block option in pdu */
718
0
    resend_pdu = coap_pdu_duplicate_lkd(pdu, session, ltoken_len,
719
0
                                        ltoken, NULL, COAP_BOOL_FALSE);
720
0
    if (!resend_pdu)
721
0
      goto error;
722
0
    if (echo) {
723
0
      coap_insert_option(resend_pdu, COAP_OPTION_ECHO, coap_opt_length(echo),
724
0
                         coap_opt_value(echo));
725
0
    }
726
0
    if (opt) {
727
0
      if (!lg_crcv->obs_token) {
728
0
        lg_crcv->obs_token = coap_malloc_type(COAP_STRING, sizeof(lg_crcv->obs_token[0]));
729
0
        if (!lg_crcv->obs_token) {
730
0
          return COAP_INVALID_MID;
731
0
        }
732
0
        lg_crcv->obs_token_cnt = 1;
733
0
      } else {
734
0
        coap_delete_bin_const(lg_crcv->obs_token[0]);
735
0
      }
736
0
      lg_crcv->obs_token[0] = coap_new_bin_const(ltoken, ltoken_len);
737
0
      if (lg_crcv->obs_token[0] == NULL)
738
0
        return COAP_INVALID_MID;
739
0
    }
740
0
    if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) {
741
0
      if (coap_get_block_b(session, resend_pdu, COAP_OPTION_BLOCK1, &block)) {
742
0
        if (data_len > block.chunk_size && block.chunk_size != 0) {
743
0
          data_len = block.chunk_size;
744
0
        }
745
0
      }
746
0
      coap_add_data(resend_pdu, data_len, data);
747
0
    }
748
749
0
    return coap_send_internal(session, resend_pdu, NULL);
750
0
  }
751
0
error:
752
0
  return COAP_INVALID_MID;
753
0
}
754
#endif /* COAP_OSCORE_SUPPORT */
755
#endif /* COAP_CLIENT_SUPPORT */
756
757
#if COAP_SERVER_SUPPORT
758
/*
759
 * Find the response lg_xmit
760
 */
761
coap_lg_xmit_t *
762
coap_find_lg_xmit_response(const coap_session_t *session,
763
                           const coap_pdu_t *request,
764
                           const coap_resource_t *resource,
765
0
                           const coap_string_t *query) {
766
0
  coap_lg_xmit_t *lg_xmit;
767
0
  coap_opt_iterator_t opt_iter;
768
0
  coap_opt_t *rtag_opt = coap_check_option(request,
769
0
                                           COAP_OPTION_RTAG,
770
0
                                           &opt_iter);
771
0
  size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
772
0
  const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
773
774
0
  LL_FOREACH(session->lg_xmit, lg_xmit) {
775
0
    static coap_string_t empty = { 0, NULL};
776
777
0
    if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) ||
778
0
        resource != lg_xmit->b.b2.resource ||
779
0
        request->code != lg_xmit->b.b2.request_method ||
780
0
        !coap_string_equal(query ? query : &empty,
781
0
                           lg_xmit->b.b2.query ?
782
0
                           lg_xmit->b.b2.query : &empty)) {
783
      /* try out the next one */
784
0
      continue;
785
0
    }
786
    /* lg_xmit is a response */
787
0
    if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) {
788
0
      if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1))
789
0
        continue;
790
0
      if (lg_xmit->b.b2.rtag_length != rtag_length ||
791
0
          memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0)
792
0
        continue;
793
0
    }
794
0
    return lg_xmit;
795
0
  }
796
0
  return NULL;
797
0
}
798
#endif /* COAP_SERVER_SUPPORT */
799
800
static int
801
coap_add_data_large_internal(coap_session_t *session,
802
                             const coap_pdu_t *request,
803
                             coap_pdu_t *pdu,
804
                             coap_resource_t *resource,
805
                             const coap_string_t *query,
806
                             int maxage,
807
                             uint64_t etag,
808
                             size_t length,
809
                             const uint8_t *data,
810
                             coap_release_large_data_t release_func,
811
                             coap_get_large_data_t get_func,
812
                             void *app_ptr,
813
0
                             int single_request, coap_pdu_code_t request_method) {
814
815
0
  ssize_t avail;
816
0
  coap_block_b_t block;
817
0
#if COAP_Q_BLOCK_SUPPORT
818
0
  coap_block_b_t alt_block;
819
0
#endif /* COAP_Q_BLOCK_SUPPORT */
820
0
  size_t chunk;
821
0
  coap_lg_xmit_t *lg_xmit = NULL;
822
0
  uint8_t buf[8];
823
0
  int have_block_defined = 0;
824
0
  uint8_t blk_size;
825
0
  uint8_t max_blk_size;
826
0
  uint16_t option;
827
0
  size_t token_options;
828
0
  coap_opt_t *opt;
829
0
  coap_opt_iterator_t opt_iter;
830
0
#if COAP_Q_BLOCK_SUPPORT
831
0
  uint16_t alt_option;
832
0
#endif /* COAP_Q_BLOCK_SUPPORT */
833
834
#if !COAP_SERVER_SUPPORT
835
  (void)etag;
836
#endif /* COAP_SERVER_SUPPORT */
837
838
0
  assert(pdu);
839
0
  if (pdu->data) {
840
0
    coap_log_warn("coap_add_data_large: PDU already contains data\n");
841
0
    if (release_func) {
842
0
      coap_lock_callback(release_func(session, app_ptr));
843
0
    }
844
0
    return 0;
845
0
  }
846
847
0
  if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) {
848
0
    coap_log_debug("** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n",
849
0
                   coap_session_str(session));
850
0
    goto add_data;
851
0
  }
852
853
  /* A lot of the reliable code assumes type is CON */
854
0
  if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON)
855
0
    pdu->type = COAP_MESSAGE_CON;
856
857
  /* Block NUM max 20 bits (starting from 0) and block size is "2**(SZX + 4)"
858
     and using SZX max of 6 gives maximum size = 1,073,740,800
859
     CSM Max-Message-Size theoretical maximum = 4,294,967,295
860
     So, if using blocks, we are limited to 1,073,740,800.
861
   */
862
0
#define MAX_BLK_LEN ((1UL << 20) * (1 << (6 + 4)))
863
#if UINT_MAX < MAX_BLK_LEN
864
#undef MAX_BLK_LEN
865
#define MAX_BLK_LEN UINT_MAX
866
#endif
867
868
0
  if (length > MAX_BLK_LEN) {
869
0
    coap_log_warn("Size of large buffer restricted to 0x%lx bytes\n", MAX_BLK_LEN);
870
0
    length = MAX_BLK_LEN;
871
0
  }
872
873
0
#if COAP_SERVER_SUPPORT
874
  /* Possible response code not yet set, so check if not request */
875
0
  if (!COAP_PDU_IS_REQUEST(pdu) && length) {
876
0
    coap_opt_t *etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
877
878
0
    if (etag_opt) {
879
      /* Have to use ETag as supplied in the response PDU */
880
0
      etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
881
0
                                    coap_opt_length(etag_opt));
882
0
    } else {
883
0
      if (!etag) {
884
        /* calculate ETag for the response */
885
0
        coap_digest_t digest;
886
0
        coap_digest_ctx_t *dctx =  coap_digest_setup();
887
888
0
        if (dctx) {
889
0
          if (coap_is_mcast(&session->addr_info.local)) {
890
0
            (void)coap_digest_update(dctx, coap_unique_id, sizeof(coap_unique_id));
891
0
          }
892
0
          if (coap_digest_update(dctx, data, length)) {
893
0
            if (coap_digest_final(dctx, &digest)) {
894
0
              memcpy(&etag, digest.key, sizeof(etag));
895
0
#if COAP_ETAG_MAX_BYTES != 8
896
0
              etag = etag >> 8*(8 - COAP_ETAG_MAX_BYTES);
897
0
#endif
898
0
              dctx = NULL;
899
0
            }
900
0
          }
901
0
          coap_digest_free(dctx);
902
0
        }
903
0
        if (!etag)
904
0
          etag = 1;
905
0
      }
906
0
      coap_update_option(pdu,
907
0
                         COAP_OPTION_ETAG,
908
0
                         coap_encode_var_safe8(buf, sizeof(buf), etag),
909
0
                         buf);
910
0
    }
911
0
    if (request) {
912
0
      etag_opt = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter);
913
0
      if (etag_opt) {
914
        /* There may be multiple ETag - need to check each one */
915
0
        coap_option_iterator_init(request, &opt_iter, COAP_OPT_ALL);
916
0
        while ((etag_opt = coap_option_next(&opt_iter))) {
917
0
          if (opt_iter.number == COAP_OPTION_ETAG) {
918
0
            uint64_t etag_r = coap_decode_var_bytes8(coap_opt_value(etag_opt),
919
0
                                                     coap_opt_length(etag_opt));
920
921
0
            if (etag == etag_r) {
922
0
              pdu->code = COAP_RESPONSE_CODE(203);
923
0
              return 1;
924
0
            }
925
0
          }
926
0
        }
927
0
      }
928
0
    }
929
0
  }
930
0
#endif /* COAP_SERVER_SUPPORT */
931
932
  /* Determine the block size to use, adding in sensible options if needed */
933
0
  if (COAP_PDU_IS_REQUEST(pdu)) {
934
0
    coap_lg_xmit_t *q;
935
936
0
#if COAP_Q_BLOCK_SUPPORT
937
0
    if (session->block_mode & (COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_TRY_Q_BLOCK)) {
938
0
      option = COAP_OPTION_Q_BLOCK1;
939
0
      alt_option = COAP_OPTION_BLOCK1;
940
0
    } else {
941
0
      option = COAP_OPTION_BLOCK1;
942
0
      alt_option = COAP_OPTION_Q_BLOCK1;
943
0
    }
944
#else /* ! COAP_Q_BLOCK_SUPPORT */
945
    if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
946
      coap_remove_option(pdu, COAP_OPTION_Q_BLOCK1);
947
    }
948
    option = COAP_OPTION_BLOCK1;
949
#endif /* ! COAP_Q_BLOCK_SUPPORT */
950
951
    /* See if this token is already in use for large bodies (unlikely) */
952
0
    LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
953
0
      if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) {
954
        /* Unfortunately need to free this off as potential size change */
955
0
        int is_mcast = 0;
956
0
        coap_tick_t last_all_sent = lg_xmit->last_all_sent;
957
0
#if COAP_CLIENT_SUPPORT
958
0
        is_mcast = coap_is_mcast(&lg_xmit->b.b1.upstream);
959
0
#endif /* COAP_CLIENT_SUPPORT */
960
0
        LL_DELETE(session->lg_xmit, lg_xmit);
961
0
        coap_block_delete_lg_xmit(session, lg_xmit);
962
0
        lg_xmit = NULL;
963
0
        if (!is_mcast && !last_all_sent)
964
0
          coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
965
0
        break;
966
0
      }
967
0
    }
968
0
  } else {
969
    /* Have to assume that it is a response even if code is 0.00 */
970
0
    assert(resource);
971
0
#if COAP_Q_BLOCK_SUPPORT
972
0
    if (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) {
973
0
      option = COAP_OPTION_Q_BLOCK2;
974
0
      alt_option = COAP_OPTION_BLOCK2;
975
0
    } else {
976
0
      option = COAP_OPTION_BLOCK2;
977
0
      alt_option = COAP_OPTION_Q_BLOCK2;
978
0
    }
979
#else /* ! COAP_Q_BLOCK_SUPPORT */
980
    if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
981
      coap_remove_option(pdu, COAP_OPTION_Q_BLOCK2);
982
    }
983
    option = COAP_OPTION_BLOCK2;
984
#endif /* ! COAP_Q_BLOCK_SUPPORT */
985
0
#if COAP_SERVER_SUPPORT
986
    /*
987
     * Check if resource+query+rtag is already in use for large bodies
988
     * (unlikely)
989
     */
990
0
    lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
991
0
    if (lg_xmit) {
992
0
      coap_tick_t last_all_sent = lg_xmit->last_all_sent;
993
      /* Unfortunately need to free this off as potential size change */
994
0
      LL_DELETE(session->lg_xmit, lg_xmit);
995
0
      coap_block_delete_lg_xmit(session, lg_xmit);
996
0
      lg_xmit = NULL;
997
0
      if (!last_all_sent)
998
0
        coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
999
0
    }
1000
0
#endif /* COAP_SERVER_SUPPORT */
1001
0
  }
1002
0
#if COAP_OSCORE_SUPPORT
1003
0
  if (session->oscore_encryption) {
1004
    /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */
1005
0
    if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu))
1006
0
      goto fail;
1007
0
  }
1008
0
#endif /* COAP_OSCORE_SUPPORT */
1009
1010
0
  token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
1011
0
  avail = pdu->max_size - token_options;
1012
  /* There may be a response with Echo option */
1013
0
  avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40);
1014
0
#if COAP_OSCORE_SUPPORT
1015
0
  avail -= coap_oscore_overhead(session, pdu);
1016
0
#endif /* COAP_OSCORE_SUPPORT */
1017
  /* May need token of length 8, so account for this */
1018
0
  avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0;
1019
1020
0
  if (avail < 16) {
1021
0
    blk_size = 0;
1022
0
  } else {
1023
0
    blk_size = coap_flsll((long long)avail) - 4 - 1;
1024
0
  }
1025
0
  if (blk_size > 6)
1026
0
    blk_size = 6;
1027
1028
0
  max_blk_size = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
1029
0
  if (max_blk_size && blk_size > max_blk_size)
1030
0
    blk_size = max_blk_size;
1031
1032
  /* see if BlockX defined - if so update blk_size as given by app */
1033
0
  if (coap_get_block_b(session, pdu, option, &block)) {
1034
0
    if (block.szx < blk_size)
1035
0
      blk_size = block.szx;
1036
0
    have_block_defined = 1;
1037
0
  }
1038
0
#if COAP_Q_BLOCK_SUPPORT
1039
  /* see if alternate BlockX defined */
1040
0
  if (coap_get_block_b(session, pdu, alt_option, &alt_block)) {
1041
0
    if (have_block_defined) {
1042
      /* Cannot have both options set */
1043
0
      coap_log_warn("Both BlockX and Q-BlockX cannot be set at the same time\n");
1044
0
      coap_remove_option(pdu, alt_option);
1045
0
    } else {
1046
0
      block = alt_block;
1047
0
      if (block.szx < blk_size)
1048
0
        blk_size = block.szx;
1049
0
      have_block_defined = 1;
1050
0
      option = alt_option;
1051
0
    }
1052
0
  }
1053
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1054
1055
0
  if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) {
1056
    /* bad luck, this is the smallest block size */
1057
0
    coap_log_debug("not enough space, even the smallest block does not fit (2)\n");
1058
0
    goto fail;
1059
0
  }
1060
1061
0
  chunk = (size_t)1 << (blk_size + 4);
1062
0
  if ((have_block_defined && block.num != 0) || single_request ||
1063
0
      ((session->block_mode & COAP_BLOCK_STLESS_BLOCK2) && session->type != COAP_SESSION_TYPE_CLIENT)) {
1064
    /* App is defining a single block to send or we are stateless */
1065
0
    size_t rem;
1066
1067
0
    if (length >= block.num * chunk) {
1068
0
#if COAP_SERVER_SUPPORT
1069
0
      if (session->block_mode & COAP_BLOCK_STLESS_BLOCK2 && session->type != COAP_SESSION_TYPE_CLIENT) {
1070
        /* We are running server stateless */
1071
0
        coap_update_option(pdu,
1072
0
                           COAP_OPTION_SIZE2,
1073
0
                           coap_encode_var_safe(buf, sizeof(buf),
1074
0
                                                (unsigned int)length),
1075
0
                           buf);
1076
0
        if (request) {
1077
0
          if (!coap_get_block_b(session, request, option, &block))
1078
0
            block.num = 0;
1079
0
        }
1080
0
        if (!setup_block_b(session, pdu, &block, block.num,
1081
0
                           blk_size, length))
1082
0
          goto fail;
1083
1084
        /* Add in with requested block num, more bit and block size */
1085
0
        coap_update_option(pdu,
1086
0
                           option,
1087
0
                           coap_encode_var_safe(buf, sizeof(buf),
1088
0
                                                (block.num << 4) | (block.m << 3) | block.aszx),
1089
0
                           buf);
1090
0
      }
1091
0
#endif /* COAP_SERVER_SUPPORT */
1092
0
      rem = chunk;
1093
0
      if (chunk > length - block.num * chunk)
1094
0
        rem = length - block.num * chunk;
1095
0
      if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
1096
0
        goto fail;
1097
0
    }
1098
0
    if (release_func) {
1099
0
      coap_lock_callback(release_func(session, app_ptr));
1100
0
    }
1101
0
  } else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) {
1102
    /* Only add in lg_xmit if more than one block needs to be handled */
1103
0
    size_t rem;
1104
1105
0
    lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t));
1106
0
    if (!lg_xmit)
1107
0
      goto fail;
1108
1109
    /* Set up for displaying all the data in the pdu */
1110
0
    if (!get_func) {
1111
0
      pdu->body_data = data;
1112
0
      pdu->body_length = length;
1113
0
      coap_log_debug("PDU presented by app.\n");
1114
0
      coap_show_pdu(COAP_LOG_DEBUG, pdu);
1115
0
      pdu->body_data = NULL;
1116
0
      pdu->body_length = 0;
1117
0
    } else {
1118
0
      coap_log_debug("PDU presented by app without data.\n");
1119
0
      coap_show_pdu(COAP_LOG_DEBUG, pdu);
1120
0
    }
1121
1122
0
    coap_log_debug("** %s: lg_xmit %p initialized\n",
1123
0
                   coap_session_str(session), (void *)lg_xmit);
1124
    /* Update lg_xmit with large data information */
1125
0
    memset(lg_xmit, 0, sizeof(coap_lg_xmit_t));
1126
0
    lg_xmit->blk_size = blk_size;
1127
0
    lg_xmit->option = option;
1128
0
    lg_xmit->data_info = coap_malloc_type(COAP_STRING, sizeof(coap_lg_xmit_data_t));
1129
0
    if (!lg_xmit->data_info)
1130
0
      goto fail;
1131
0
    lg_xmit->data_info->ref = 0;
1132
0
    lg_xmit->data_info->data = data;
1133
0
    lg_xmit->data_info->length = length;
1134
0
#if COAP_Q_BLOCK_SUPPORT
1135
0
    lg_xmit->non_timeout_random_ticks =
1136
0
        coap_get_non_timeout_random_ticks(session);
1137
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1138
0
    lg_xmit->data_info->get_func = get_func;
1139
0
    lg_xmit->data_info->release_func = release_func;
1140
0
    lg_xmit->data_info->app_ptr = app_ptr;
1141
0
    pdu->lg_xmit = lg_xmit;
1142
0
    coap_ticks(&lg_xmit->last_obs);
1143
0
    coap_ticks(&lg_xmit->last_sent);
1144
0
    if (COAP_PDU_IS_REQUEST(pdu)) {
1145
      /* Need to keep original token for updating response PDUs */
1146
0
      lg_xmit->b.b1.app_token = coap_new_binary(pdu->actual_token.length);
1147
0
      if (!lg_xmit->b.b1.app_token)
1148
0
        goto fail;
1149
0
      memcpy(lg_xmit->b.b1.app_token->s, pdu->actual_token.s,
1150
0
             pdu->actual_token.length);
1151
      /*
1152
       * Need to set up new token for use during transmits
1153
       * RFC9177#section-5
1154
       */
1155
0
      lg_xmit->b.b1.count = 1;
1156
0
      lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token,
1157
0
                                                   lg_xmit->b.b1.count);
1158
0
      coap_address_copy(&lg_xmit->b.b1.upstream, &session->addr_info.remote);
1159
      /*
1160
       * Token will be updated in pdu later as original pdu may be needed in
1161
       * coap_send()
1162
       */
1163
0
      coap_update_option(pdu,
1164
0
                         COAP_OPTION_SIZE1,
1165
0
                         coap_encode_var_safe(buf, sizeof(buf),
1166
0
                                              (unsigned int)length),
1167
0
                         buf);
1168
0
      if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter))
1169
0
        coap_insert_option(pdu,
1170
0
                           COAP_OPTION_RTAG,
1171
0
                           coap_encode_var_safe(buf, sizeof(buf),
1172
0
                                                ++session->tx_rtag),
1173
0
                           buf);
1174
0
    } else {
1175
      /*
1176
       * resource+query+rtag match is used for Block2 large body transmissions
1177
       * token match is used for Block1 large body transmissions
1178
       */
1179
0
      lg_xmit->b.b2.resource = resource;
1180
0
      if (query) {
1181
0
        lg_xmit->b.b2.query = coap_new_string(query->length);
1182
0
        if (lg_xmit->b.b2.query) {
1183
0
          memcpy(lg_xmit->b.b2.query->s, query->s, query->length);
1184
0
        }
1185
0
      } else {
1186
0
        lg_xmit->b.b2.query = NULL;
1187
0
      }
1188
0
      opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter);
1189
0
      if (opt) {
1190
0
        lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt),
1191
0
                                                 sizeof(lg_xmit->b.b2.rtag));
1192
0
        memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), lg_xmit->b.b2.rtag_length);
1193
0
        lg_xmit->b.b2.rtag_set = 1;
1194
0
      } else {
1195
0
        lg_xmit->b.b2.rtag_set = 0;
1196
0
      }
1197
0
      lg_xmit->b.b2.etag = etag;
1198
0
      lg_xmit->b.b2.request_method = request_method;
1199
0
      if (maxage >= 0) {
1200
0
        coap_tick_t now;
1201
1202
0
        coap_ticks(&now);
1203
0
        lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage;
1204
0
      } else {
1205
0
        lg_xmit->b.b2.maxage_expire = 0;
1206
0
      }
1207
0
      coap_update_option(pdu,
1208
0
                         COAP_OPTION_SIZE2,
1209
0
                         coap_encode_var_safe(buf, sizeof(buf),
1210
0
                                              (unsigned int)length),
1211
0
                         buf);
1212
0
    }
1213
1214
0
    if (!setup_block_b(session, pdu, &block, block.num,
1215
0
                       blk_size, lg_xmit->data_info->length))
1216
0
      goto fail;
1217
1218
    /* Add in with requested block num, more bit and block size */
1219
0
    coap_update_option(pdu,
1220
0
                       lg_xmit->option,
1221
0
                       coap_encode_var_safe(buf, sizeof(buf),
1222
0
                                            (block.num << 4) | (block.m << 3) | block.aszx),
1223
0
                       buf);
1224
1225
    /* Reference PDU to use as a basis for all the subsequent blocks */
1226
0
    lg_xmit->sent_pdu = coap_pdu_reference_lkd(pdu);
1227
1228
    /* Check we still have space after adding in some options */
1229
0
    token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
1230
0
    avail = pdu->max_size - token_options;
1231
    /* There may be a response with Echo option */
1232
0
    avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40);
1233
    /* May need token of length 8, so account for this */
1234
0
    avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0;
1235
0
#if COAP_OSCORE_SUPPORT
1236
0
    avail -= coap_oscore_overhead(session, pdu);
1237
0
#endif /* COAP_OSCORE_SUPPORT */
1238
0
    if (avail < (ssize_t)chunk) {
1239
      /* chunk size change down */
1240
0
      if (avail < 16) {
1241
0
        coap_log_warn("not enough space, even the smallest block does not fit (3)\n");
1242
0
        goto fail;
1243
0
      }
1244
0
      blk_size = coap_flsll((long long)avail) - 4 - 1;
1245
0
      block.num = block.num << (lg_xmit->blk_size - blk_size);
1246
0
      lg_xmit->blk_size = blk_size;
1247
0
      chunk = (size_t)1 << (lg_xmit->blk_size + 4);
1248
0
      block.chunk_size = (uint32_t)chunk;
1249
0
      block.bert = 0;
1250
0
      coap_update_option(pdu,
1251
0
                         lg_xmit->option,
1252
0
                         coap_encode_var_safe(buf, sizeof(buf),
1253
0
                                              (block.num << 4) | (block.m << 3) | lg_xmit->blk_size),
1254
0
                         buf);
1255
0
    }
1256
0
#if COAP_Q_BLOCK_SUPPORT
1257
    /* Set up send_blocks */
1258
0
    lg_xmit->send_blocks.used = 1;
1259
0
    lg_xmit->send_blocks.range[0].end = (uint32_t)((lg_xmit->data_info->length - 1) / chunk);
1260
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1261
1262
0
    rem = block.chunk_size;
1263
0
    if (rem > lg_xmit->data_info->length - block.num * chunk)
1264
0
      rem = lg_xmit->data_info->length - block.num * chunk;
1265
0
    if (get_func) {
1266
#if COAP_CONSTRAINED_STACK
1267
      /* Protected by global_lock if needed */
1268
      static uint8_t l_data[1024];
1269
#else /* ! COAP_CONSTRAINED_STACK */
1270
0
      uint8_t l_data[1024];
1271
0
#endif /* ! COAP_CONSTRAINED_STACK */
1272
0
      size_t l_length;
1273
1274
0
      assert(rem <= 1024);
1275
0
      if (get_func(session, rem, block.num * chunk, l_data, &l_length, lg_xmit->data_info->app_ptr)) {
1276
0
        if (!coap_add_data(pdu, l_length, l_data)) {
1277
0
          goto fail;
1278
0
        }
1279
0
      }
1280
0
    } else {
1281
0
      if (!coap_add_data(pdu, rem, &data[block.num * chunk]))
1282
0
        goto fail;
1283
0
    }
1284
1285
0
    if (COAP_PDU_IS_REQUEST(pdu))
1286
0
      lg_xmit->b.b1.bert_size = rem;
1287
1288
0
    lg_xmit->last_block = -1;
1289
1290
    /* Link the new lg_xmit in */
1291
0
    LL_PREPEND(session->lg_xmit,lg_xmit);
1292
0
  } else {
1293
    /* No need to use blocks */
1294
0
    if (have_block_defined) {
1295
0
      coap_update_option(pdu,
1296
0
                         option,
1297
0
                         coap_encode_var_safe(buf, sizeof(buf),
1298
0
                                              (0 << 4) | (0 << 3) | blk_size), buf);
1299
0
      if (COAP_PDU_IS_REQUEST(pdu)) {
1300
0
        coap_update_option(pdu,
1301
0
                           COAP_OPTION_SIZE1,
1302
0
                           coap_encode_var_safe(buf, sizeof(buf),
1303
0
                                                (unsigned int)length),
1304
0
                           buf);
1305
0
      } else {
1306
0
        coap_update_option(pdu,
1307
0
                           COAP_OPTION_SIZE2,
1308
0
                           coap_encode_var_safe(buf, sizeof(buf),
1309
0
                                                (unsigned int)length),
1310
0
                           buf);
1311
0
      }
1312
0
    }
1313
0
add_data:
1314
0
    if (get_func) {
1315
0
      uint8_t *l_data = coap_malloc_type(COAP_STRING, length);
1316
0
      size_t l_length;
1317
1318
0
      if (get_func(session, length, 0, l_data, &l_length, app_ptr)) {
1319
0
        if (!coap_add_data(pdu, l_length, l_data)) {
1320
0
          coap_free_type(COAP_STRING, l_data);
1321
0
          goto fail;
1322
0
        }
1323
0
        coap_free_type(COAP_STRING, l_data);
1324
0
      }
1325
0
    } else {
1326
0
      if (!coap_add_data(pdu, length, data))
1327
0
        goto fail;
1328
0
    }
1329
1330
0
    if (release_func) {
1331
0
      coap_lock_callback(release_func(session, app_ptr));
1332
0
    }
1333
0
  }
1334
0
  return 1;
1335
1336
0
fail:
1337
0
  if (lg_xmit) {
1338
0
    coap_block_delete_lg_xmit(session, lg_xmit);
1339
0
  } else if (release_func) {
1340
0
    coap_lock_callback(release_func(session, app_ptr));
1341
0
  }
1342
0
  return 0;
1343
0
}
1344
1345
#if COAP_CLIENT_SUPPORT
1346
COAP_API int
1347
coap_add_data_large_request(coap_session_t *session,
1348
                            coap_pdu_t *pdu,
1349
                            size_t length,
1350
                            const uint8_t *data,
1351
                            coap_release_large_data_t release_func,
1352
                            void *app_ptr
1353
0
                           ) {
1354
0
  int ret;
1355
1356
0
  coap_lock_lock(return 0);
1357
0
  ret = coap_add_data_large_request_lkd(session, pdu, length, data,
1358
0
                                        release_func, app_ptr);
1359
0
  coap_lock_unlock();
1360
0
  return ret;
1361
0
}
1362
1363
int
1364
coap_add_data_large_request_lkd(coap_session_t *session,
1365
                                coap_pdu_t *pdu,
1366
                                size_t length,
1367
                                const uint8_t *data,
1368
                                coap_release_large_data_t release_func,
1369
0
                                void *app_ptr) {
1370
  /*
1371
   * Delay if session->doing_first is set.
1372
   * E.g. Reliable and CSM not in yet for checking block support
1373
   */
1374
0
  if (coap_client_delay_first(session) == 0) {
1375
0
    if (release_func) {
1376
0
      coap_lock_callback(release_func(session, app_ptr));
1377
0
    }
1378
0
    return 0;
1379
0
  }
1380
0
  return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0,
1381
0
                                      length, data, release_func, NULL, app_ptr, 0, 0);
1382
0
}
1383
1384
COAP_API int
1385
coap_add_data_large_request_app(coap_session_t *session,
1386
                                coap_pdu_t *pdu,
1387
                                size_t length,
1388
                                coap_release_large_data_t release_func,
1389
                                coap_get_large_data_t get_func,
1390
0
                                void *app_ptr) {
1391
0
  int ret;
1392
1393
0
  coap_lock_lock(return 0);
1394
0
  ret = coap_add_data_large_request_app_lkd(session, pdu, length,
1395
0
                                            release_func, get_func, app_ptr);
1396
0
  coap_lock_unlock();
1397
0
  return ret;
1398
0
}
1399
1400
int
1401
coap_add_data_large_request_app_lkd(coap_session_t *session,
1402
                                    coap_pdu_t *pdu,
1403
                                    size_t length,
1404
                                    coap_release_large_data_t release_func,
1405
                                    coap_get_large_data_t get_func,
1406
0
                                    void *app_ptr) {
1407
  /*
1408
   * Delay if session->doing_first is set.
1409
   * E.g. Reliable and CSM not in yet for checking block support
1410
   */
1411
0
  if (coap_client_delay_first(session) == 0) {
1412
0
    return 0;
1413
0
  }
1414
0
  return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0,
1415
0
                                      length, NULL, release_func, get_func,
1416
0
                                      app_ptr, 0, 0);
1417
0
}
1418
#endif /* ! COAP_CLIENT_SUPPORT */
1419
1420
#if COAP_SERVER_SUPPORT
1421
COAP_API int
1422
coap_add_data_large_response(coap_resource_t *resource,
1423
                             coap_session_t *session,
1424
                             const coap_pdu_t *request,
1425
                             coap_pdu_t *response,
1426
                             const coap_string_t *query,
1427
                             uint16_t media_type,
1428
                             int maxage,
1429
                             uint64_t etag,
1430
                             size_t length,
1431
                             const uint8_t *data,
1432
                             coap_release_large_data_t release_func,
1433
0
                             void *app_ptr) {
1434
0
  int ret;
1435
1436
0
  coap_lock_lock(return 0);
1437
0
  ret = coap_add_data_large_response_lkd(resource, session, request,
1438
0
                                         response, query, media_type, maxage, etag,
1439
0
                                         length, data, release_func, app_ptr);
1440
0
  coap_lock_unlock();
1441
0
  return ret;
1442
0
}
1443
1444
int
1445
coap_add_data_large_response_lkd(coap_resource_t *resource,
1446
                                 coap_session_t *session,
1447
                                 const coap_pdu_t *request,
1448
                                 coap_pdu_t *response,
1449
                                 const coap_string_t *query,
1450
                                 uint16_t media_type,
1451
                                 int maxage,
1452
                                 uint64_t etag,
1453
                                 size_t length,
1454
                                 const uint8_t *data,
1455
                                 coap_release_large_data_t release_func,
1456
                                 void *app_ptr
1457
0
                                ) {
1458
0
  unsigned char buf[4];
1459
0
  coap_block_b_t block;
1460
0
  int block_requested = 0;
1461
0
  int single_request = 0;
1462
0
#if COAP_Q_BLOCK_SUPPORT
1463
0
  uint32_t block_opt = (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) ?
1464
0
                       COAP_OPTION_Q_BLOCK2 : COAP_OPTION_BLOCK2;
1465
#else /* ! COAP_Q_BLOCK_SUPPORT */
1466
  uint16_t block_opt = COAP_OPTION_BLOCK2;
1467
#endif /* ! COAP_Q_BLOCK_SUPPORT */
1468
1469
0
  memset(&block, 0, sizeof(block));
1470
  /*
1471
   * Need to check that a valid block is getting asked for so that the
1472
   * correct options are put into the PDU.
1473
   */
1474
0
  if (request) {
1475
0
    if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) {
1476
0
      block_requested = 1;
1477
0
      if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
1478
0
        coap_log_debug("Illegal block requested (%d > last = %" PRIuS ")\n",
1479
0
                       block.num,
1480
0
                       length >> (block.szx + 4));
1481
0
        response->code = COAP_RESPONSE_CODE(400);
1482
0
        goto error;
1483
0
      }
1484
0
    }
1485
0
#if COAP_Q_BLOCK_SUPPORT
1486
0
    else if (coap_get_block_b(session, request, COAP_OPTION_Q_BLOCK2, &block)) {
1487
0
      block_requested = 1;
1488
0
      if (block.num != 0 && length <= (block.num << (block.szx + 4))) {
1489
0
        coap_log_debug("Illegal block requested (%d > last = %" PRIuS ")\n",
1490
0
                       block.num,
1491
0
                       length >> (block.szx + 4));
1492
0
        response->code = COAP_RESPONSE_CODE(400);
1493
0
        goto error;
1494
0
      }
1495
0
      if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
1496
0
        set_block_mode_has_q(session->block_mode);
1497
0
        block_opt = COAP_OPTION_Q_BLOCK2;
1498
0
      }
1499
0
      if (block.m == 0)
1500
0
        single_request = 1;
1501
0
    }
1502
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1503
0
  }
1504
1505
0
  coap_update_option(response, COAP_OPTION_CONTENT_FORMAT,
1506
0
                     coap_encode_var_safe(buf, sizeof(buf),
1507
0
                                          media_type),
1508
0
                     buf);
1509
1510
0
  if (maxage >= 0) {
1511
0
    coap_update_option(response,
1512
0
                       COAP_OPTION_MAXAGE,
1513
0
                       coap_encode_var_safe(buf, sizeof(buf), maxage), buf);
1514
0
  }
1515
1516
0
  if (block_requested) {
1517
0
    int res;
1518
1519
0
    res = coap_write_block_b_opt(session, &block, block_opt, response,
1520
0
                                 length);
1521
1522
0
    switch (res) {
1523
0
    case -2:                        /* illegal block (caught above) */
1524
0
      response->code = COAP_RESPONSE_CODE(400);
1525
0
      goto error;
1526
0
    case -1:                        /* should really not happen */
1527
0
      assert(0);
1528
    /* fall through if assert is a no-op */
1529
0
    case -3:                        /* cannot handle request */
1530
0
      response->code = COAP_RESPONSE_CODE(500);
1531
0
      goto error;
1532
0
    default:                        /* everything is good */
1533
0
      ;
1534
0
    }
1535
0
  }
1536
1537
  /* add data body */
1538
0
  if (request &&
1539
0
      !coap_add_data_large_internal(session, request, response, resource,
1540
0
                                    query, maxage, etag, length, data,
1541
0
                                    release_func, NULL, app_ptr, single_request,
1542
0
                                    request->code)) {
1543
0
    response->code = COAP_RESPONSE_CODE(500);
1544
0
    goto error_released;
1545
0
  }
1546
1547
0
  return 1;
1548
1549
0
error:
1550
0
  if (release_func) {
1551
0
    coap_lock_callback(release_func(session, app_ptr));
1552
0
  }
1553
0
error_released:
1554
0
#if COAP_ERROR_PHRASE_LENGTH > 0
1555
0
  coap_add_data(response,
1556
0
                strlen(coap_response_phrase(response->code)),
1557
0
                (const unsigned char *)coap_response_phrase(response->code));
1558
0
#endif /* COAP_ERROR_PHRASE_LENGTH > 0 */
1559
0
  return 0;
1560
0
}
1561
#endif /* ! COAP_SERVER_SUPPORT */
1562
1563
/*
1564
 * return 1 if there is a future expire time, else 0.
1565
 * update tim_rem with remaining value if return is 1.
1566
 */
1567
int
1568
coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now,
1569
0
                                  coap_tick_t *tim_rem) {
1570
0
  coap_lg_xmit_t *lg_xmit;
1571
0
  coap_lg_xmit_t *q;
1572
0
#if COAP_Q_BLOCK_SUPPORT
1573
0
  coap_tick_t idle_timeout = COAP_LG_XMIT_TXT_SCALAR * COAP_NON_TIMEOUT_TICKS(session);
1574
#else /* ! COAP_Q_BLOCK_SUPPORT */
1575
  coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND;
1576
#endif /* ! COAP_Q_BLOCK_SUPPORT */
1577
0
  coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1578
0
  int ret = 0;
1579
1580
0
  *tim_rem = COAP_MAX_DELAY_TICKS;
1581
1582
0
  LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
1583
0
    if (lg_xmit->last_all_sent) {
1584
0
      if (lg_xmit->last_all_sent + idle_timeout <= now) {
1585
        /* Expire this entry */
1586
0
        LL_DELETE(session->lg_xmit, lg_xmit);
1587
0
        coap_block_delete_lg_xmit(session, lg_xmit);
1588
0
      } else {
1589
        /* Delay until the lg_xmit needs to expire */
1590
0
        if (*tim_rem > lg_xmit->last_all_sent + idle_timeout - now) {
1591
0
          *tim_rem = lg_xmit->last_all_sent + idle_timeout - now;
1592
0
          ret = 1;
1593
0
        }
1594
0
      }
1595
0
    } else if (lg_xmit->last_sent) {
1596
0
      if (lg_xmit->last_sent + partial_timeout <= now) {
1597
        /* Expire this entry */
1598
0
        coap_tick_t last_all_sent = lg_xmit->last_all_sent;
1599
0
        int is_mcast = 0;
1600
0
#if COAP_CLIENT_SUPPORT
1601
0
        is_mcast = COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) &&
1602
0
                   coap_is_mcast(&lg_xmit->b.b1.upstream);
1603
0
#endif /* COAP_CLIENT_SUPPORT */
1604
0
        LL_DELETE(session->lg_xmit, lg_xmit);
1605
1606
0
        coap_block_delete_lg_xmit(session, lg_xmit);
1607
0
        if (!is_mcast && !last_all_sent)
1608
0
          coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
1609
0
      } else {
1610
        /* Delay until the lg_xmit needs to expire */
1611
0
        if (*tim_rem > lg_xmit->last_sent + partial_timeout - now) {
1612
0
          *tim_rem = lg_xmit->last_sent + partial_timeout - now;
1613
0
          ret = 1;
1614
0
        }
1615
0
      }
1616
0
    }
1617
0
  }
1618
0
  return ret;
1619
0
}
1620
1621
#if COAP_CLIENT_SUPPORT
1622
#if COAP_Q_BLOCK_SUPPORT
1623
static coap_pdu_t *
1624
0
coap_build_missing_pdu(coap_session_t *session, coap_lg_crcv_t *lg_crcv) {
1625
0
  coap_pdu_t *pdu;
1626
0
  coap_opt_filter_t drop_options;
1627
0
  uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter);
1628
0
  uint8_t buf[8];
1629
0
  size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
1630
1631
0
  memset(&drop_options, 0, sizeof(coap_opt_filter_t));
1632
0
  coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK1);
1633
0
  coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK2);
1634
0
  coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
1635
0
  pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf,
1636
0
                               &drop_options, COAP_BOOL_FALSE);
1637
0
  if (!pdu)
1638
0
    return NULL;
1639
0
  pdu->type = lg_crcv->last_type;
1640
0
  return pdu;
1641
0
}
1642
1643
static void
1644
0
coap_request_missing_q_block2(coap_session_t *session, coap_lg_crcv_t *lg_crcv) {
1645
0
  uint8_t buf[8];
1646
0
  uint32_t i;
1647
0
  int block = -1; /* Last one seen */
1648
0
  size_t sofar;
1649
0
  size_t block_size;
1650
0
  coap_pdu_t *pdu = NULL;
1651
0
  int block_payload_set = -1;
1652
1653
0
  if (session->block_mode & COAP_BLOCK_USE_M_Q_BLOCK) {
1654
    /*
1655
     * See if it is safe to use the single 'M' block variant of request
1656
     *
1657
     * If any blocks seen, then missing blocks are after range[0].end and
1658
     * terminate on the last block or before range[1].begin if set.
1659
     * If not defined or range[1].begin is in a different payload set then
1660
     * safe to use M bit.
1661
     */
1662
0
    if (lg_crcv->rec_blocks.used &&
1663
0
        (lg_crcv->rec_blocks.used < 2 ||
1664
0
         ((lg_crcv->rec_blocks.range[0].end + 1) / COAP_MAX_PAYLOADS(session) !=
1665
0
          (lg_crcv->rec_blocks.range[1].begin -1) / COAP_MAX_PAYLOADS(session)))) {
1666
0
      block = lg_crcv->rec_blocks.range[0].end + 1;
1667
0
      block_size = (size_t)1 << (lg_crcv->szx + 4);
1668
0
      sofar = block * block_size;
1669
0
      if (sofar < lg_crcv->total_len) {
1670
        /* Ask for missing blocks */
1671
0
        if (pdu == NULL) {
1672
0
          pdu = coap_build_missing_pdu(session, lg_crcv);
1673
0
          if (!pdu)
1674
0
            return;
1675
0
        }
1676
0
        coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
1677
0
                           coap_encode_var_safe(buf, sizeof(buf),
1678
0
                                                (block << 4) | (1 << 3) | lg_crcv->szx),
1679
0
                           buf);
1680
0
        block_payload_set = block / COAP_MAX_PAYLOADS(session);
1681
0
        goto send_it;
1682
0
      }
1683
0
    }
1684
0
  }
1685
0
  block = -1;
1686
0
  for (i = 0; i < lg_crcv->rec_blocks.used; i++) {
1687
0
    if (block < (int)lg_crcv->rec_blocks.range[i].begin &&
1688
0
        lg_crcv->rec_blocks.range[i].begin != 0) {
1689
      /* Ask for missing blocks */
1690
0
      if (pdu == NULL) {
1691
0
        pdu = coap_build_missing_pdu(session, lg_crcv);
1692
0
        if (!pdu)
1693
0
          continue;
1694
0
      }
1695
0
      block++;
1696
0
      if (block_payload_set == -1)
1697
0
        block_payload_set = block / COAP_MAX_PAYLOADS(session);
1698
0
      for (; block < (int)lg_crcv->rec_blocks.range[i].begin &&
1699
0
           block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) {
1700
0
        coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
1701
0
                           coap_encode_var_safe(buf, sizeof(buf),
1702
0
                                                (block << 4) | (0 << 3) | lg_crcv->szx),
1703
0
                           buf);
1704
0
      }
1705
0
    }
1706
0
    if (block < (int)lg_crcv->rec_blocks.range[i].end) {
1707
0
      block = lg_crcv->rec_blocks.range[i].end;
1708
0
    }
1709
0
  }
1710
0
  block_size = (size_t)1 << (lg_crcv->szx + 4);
1711
0
  sofar = (block + 1) * block_size;
1712
0
  if (sofar < lg_crcv->total_len) {
1713
    /* Ask for trailing missing blocks */
1714
0
    if (pdu == NULL) {
1715
0
      pdu = coap_build_missing_pdu(session, lg_crcv);
1716
0
      if (!pdu)
1717
0
        return;
1718
0
    }
1719
0
    sofar = (lg_crcv->total_len + block_size - 1)/block_size;
1720
0
    block++;
1721
0
    if (block_payload_set == -1)
1722
0
      block_payload_set = block / COAP_MAX_PAYLOADS(session);
1723
0
    for (; block < (ssize_t)sofar &&
1724
0
         block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) {
1725
0
      coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
1726
0
                         coap_encode_var_safe(buf, sizeof(buf),
1727
0
                                              (block << 4) | (0 << 3) | lg_crcv->szx),
1728
0
                         buf);
1729
0
    }
1730
0
  }
1731
0
send_it:
1732
0
  if (pdu)
1733
0
    coap_send_internal(session, pdu, NULL);
1734
0
  lg_crcv->rec_blocks.retry++;
1735
0
  if (block_payload_set != -1)
1736
0
    lg_crcv->rec_blocks.processing_payload_set = block_payload_set;
1737
0
  coap_ticks(&lg_crcv->rec_blocks.last_seen);
1738
0
}
1739
#endif /* COAP_Q_BLOCK_SUPPORT */
1740
1741
/*
1742
 * return 1 if there is a future expire time, else 0.
1743
 * update tim_rem with remaining value if return is 1.
1744
 */
1745
int
1746
coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now,
1747
0
                                  coap_tick_t *tim_rem) {
1748
0
  coap_lg_crcv_t *lg_crcv;
1749
0
  coap_lg_crcv_t *q;
1750
0
  coap_tick_t partial_timeout;
1751
0
#if COAP_Q_BLOCK_SUPPORT
1752
0
  coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session);
1753
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1754
0
  int ret = 0;
1755
1756
0
  *tim_rem = COAP_MAX_DELAY_TICKS;
1757
0
#if COAP_Q_BLOCK_SUPPORT
1758
0
  if (COAP_PROTO_NOT_RELIABLE(session->proto) &&
1759
0
      session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)
1760
0
    partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session);
1761
0
  else
1762
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1763
0
    partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
1764
1765
0
  LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) {
1766
0
    if (COAP_PROTO_RELIABLE(session->proto) || lg_crcv->last_type != COAP_MESSAGE_NON)
1767
0
      goto check_expire;
1768
1769
0
#if COAP_Q_BLOCK_SUPPORT
1770
0
    if (lg_crcv->block_option == COAP_OPTION_Q_BLOCK2 && lg_crcv->rec_blocks.used) {
1771
0
      size_t scaled_timeout = receive_timeout *
1772
0
                              ((size_t)1 << lg_crcv->rec_blocks.retry);
1773
1774
0
      if (lg_crcv->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) {
1775
        /* Done NON_MAX_RETRANSMIT retries */
1776
0
        coap_handle_nack(session, lg_crcv->sent_pdu,
1777
0
                         COAP_NACK_TOO_MANY_RETRIES, lg_crcv->sent_pdu->mid);
1778
0
        goto expire;
1779
0
      }
1780
0
      if (lg_crcv->rec_blocks.last_seen + scaled_timeout <= now) {
1781
0
        coap_log_debug("** %s: lg_crcv %p timeout\n",
1782
0
                       coap_session_str(session), (void *)lg_crcv);
1783
0
        coap_request_missing_q_block2(session, lg_crcv);
1784
0
      } else {
1785
0
        if (*tim_rem > lg_crcv->rec_blocks.last_seen + scaled_timeout - now) {
1786
0
          *tim_rem = lg_crcv->rec_blocks.last_seen + scaled_timeout - now;
1787
0
          ret = 1;
1788
0
        }
1789
0
      }
1790
0
    }
1791
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1792
    /* Used for Block2 and Q-Block2 */
1793
0
check_expire:
1794
0
    if (!lg_crcv->observe_set && lg_crcv->last_used &&
1795
0
        lg_crcv->last_used + partial_timeout <= now) {
1796
0
#if COAP_Q_BLOCK_SUPPORT
1797
0
expire:
1798
0
#endif /* COAP_Q_BLOCK_SUPPORT */
1799
      /* Expire this entry */
1800
0
      LL_DELETE(session->lg_crcv, lg_crcv);
1801
0
      coap_block_delete_lg_crcv(session, lg_crcv);
1802
0
    } else if (!lg_crcv->observe_set && lg_crcv->last_used) {
1803
      /* Delay until the lg_crcv needs to expire */
1804
0
      if (*tim_rem > lg_crcv->last_used + partial_timeout - now) {
1805
0
        *tim_rem = lg_crcv->last_used + partial_timeout - now;
1806
0
        ret = 1;
1807
0
      }
1808
0
    }
1809
0
  }
1810
0
  return ret;
1811
0
}
1812
#endif /* COAP_CLIENT_SUPPORT */
1813
1814
#if COAP_SERVER_SUPPORT
1815
#if COAP_Q_BLOCK_SUPPORT
1816
static coap_pdu_t *
1817
0
pdu_408_build(coap_session_t *session, coap_lg_srcv_t *lg_srcv) {
1818
0
  coap_pdu_t *pdu;
1819
0
  uint8_t buf[4];
1820
1821
0
  pdu = coap_pdu_init(COAP_MESSAGE_NON,
1822
0
                      COAP_RESPONSE_CODE(408),
1823
0
                      coap_new_message_id_lkd(session),
1824
0
                      coap_session_max_pdu_size_lkd(session));
1825
0
  if (!pdu)
1826
0
    return NULL;
1827
0
  if (lg_srcv->last_token)
1828
0
    coap_add_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
1829
0
  coap_add_option_internal(pdu, COAP_OPTION_CONTENT_TYPE,
1830
0
                           coap_encode_var_safe(buf, sizeof(buf),
1831
0
                                                COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ),
1832
0
                           buf);
1833
0
  pdu->token[pdu->used_size++] = COAP_PAYLOAD_START;
1834
0
  pdu->data = pdu->token + pdu->used_size;
1835
0
  return pdu;
1836
0
}
1837
1838
static int
1839
0
add_408_block(coap_pdu_t *pdu, uint32_t block) {
1840
0
  size_t len;
1841
0
  uint8_t val[8];
1842
1843
0
  assert(block < (1U << 20));
1844
0
  coap_log_debug("Q-Block1: Requesting missing block %" PRIu32 "\n", block);
1845
1846
0
  if (block >= (1U << 20)) {
1847
0
    return 0;
1848
0
  } else if (block < 24) {
1849
0
    len = 1;
1850
0
    val[0] = (uint8_t)block;
1851
0
  } else if (block < 0x100) {
1852
0
    len = 2;
1853
0
    val[0] = 24;
1854
0
    val[1] = (uint8_t)block;
1855
0
  } else if (block < 0x10000) {
1856
0
    len = 3;
1857
0
    val[0] = 25;
1858
0
    val[1] = (uint8_t)((block >> 8) & 0xff);
1859
0
    val[2] = (uint8_t)(block & 0xff);
1860
0
  } else { /* Largest block number is 2^^20 - 1 */
1861
0
    len = 5;
1862
0
    val[0] = 26;
1863
0
    val[1] = (uint8_t)((block >> 24) & 0xff); /* Will be 0 */
1864
0
    val[2] = (uint8_t)((block >> 16) & 0xff);
1865
0
    val[3] = (uint8_t)((block >> 8) & 0xff);
1866
0
    val[4] = (uint8_t)(block & 0xff);
1867
0
  }
1868
0
  if (coap_pdu_check_resize(pdu, pdu->used_size + len)) {
1869
0
    memcpy(&pdu->token[pdu->used_size], val, len);
1870
0
    pdu->used_size += len;
1871
0
    return 1;
1872
0
  }
1873
0
  return 0;
1874
0
}
1875
#endif /* COAP_Q_BLOCK_SUPPORT */
1876
#endif /* COAP_SERVER_SUPPORT */
1877
1878
static int
1879
0
check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
1880
0
  uint32_t i;
1881
1882
0
  for (i = 0; i < rec_blocks->used; i++) {
1883
0
    if (block_num < rec_blocks->range[i].begin)
1884
0
      return 0;
1885
0
    if (block_num <= rec_blocks->range[i].end)
1886
0
      return 1;
1887
0
  }
1888
0
  return 0;
1889
0
}
1890
1891
#if COAP_SERVER_SUPPORT
1892
static int
1893
0
check_if_next_block(coap_rblock_t *rec_blocks, uint32_t block_num) {
1894
0
  if (rec_blocks->used == 0) {
1895
0
    return block_num == 0 ? 1 : 0;
1896
0
  }
1897
0
  if (rec_blocks->range[rec_blocks->used-1].end + 1 == block_num)
1898
0
    return 1;
1899
1900
0
  return 0;
1901
0
}
1902
#endif /* COAP_SERVER_SUPPORT */
1903
1904
static int
1905
0
check_all_blocks_in(coap_rblock_t *rec_blocks) {
1906
0
  uint32_t i;
1907
0
  uint32_t block = 0;
1908
1909
0
  if (rec_blocks->total_blocks == 0) {
1910
    /* Not seen block with More bit unset yet */
1911
0
    return 0;
1912
0
  }
1913
1914
0
  for (i = 0; i < rec_blocks->used; i++) {
1915
0
    if (block < rec_blocks->range[i].begin)
1916
0
      return 0;
1917
0
    if (block < rec_blocks->range[i].end)
1918
0
      block = rec_blocks->range[i].end;
1919
0
  }
1920
0
  return 1;
1921
0
}
1922
1923
#if COAP_CLIENT_SUPPORT
1924
#if COAP_Q_BLOCK_SUPPORT
1925
static int
1926
check_all_blocks_in_for_payload_set(coap_session_t *session,
1927
0
                                    coap_rblock_t *rec_blocks) {
1928
0
  if (rec_blocks->used &&
1929
0
      (rec_blocks->range[0].end + 1) / COAP_MAX_PAYLOADS(session) >
1930
0
      rec_blocks->processing_payload_set)
1931
0
    return 1;
1932
0
  return 0;
1933
0
}
1934
1935
static int
1936
check_any_blocks_next_payload_set(coap_session_t *session,
1937
0
                                  coap_rblock_t *rec_blocks) {
1938
0
  if (rec_blocks->used > 1 &&
1939
0
      rec_blocks->range[1].begin / COAP_MAX_PAYLOADS(session) ==
1940
0
      rec_blocks->processing_payload_set)
1941
0
    return 1;
1942
0
  return 0;
1943
0
}
1944
#endif /* COAP_Q_BLOCK_SUPPORT */
1945
#endif /* COAP_CLIENT_SUPPORT */
1946
1947
#if COAP_SERVER_SUPPORT
1948
#if COAP_Q_BLOCK_SUPPORT
1949
/*
1950
 * To reduce the number of retransmitted missing requests, if the request comes
1951
 * from a inc_to_block in the same PAYLOAD_SET as the last time, the request is not
1952
 * retransmitted, unless this is from a timeout request when inc_to_block is the
1953
 * the last expected block based on SIZE1, not the last received block.
1954
 *
1955
 * If lg_srcv->rec_blocks.used == 0, then no blocks have been received (not expected).
1956
 * If lg_srcv->rec_blocks.used == 1 and lg_srcv->rec_blocks.range[0].begin == 0,
1957
 * then no blocks are missing, but the next block may be missing (picked up when
1958
 * there is a timeout request).
1959
 * If lg_srcv->rec_blocks.used > 1, then there are missing blocks.
1960
 *
1961
 * The final PAYLOAD_SET is a special case as it can have 1 to MAX_PAYLOADS -1 blocks.
1962
 *
1963
 * The first PAYLOAD_SET that has missing blocks is reported in the 4.08 code, and
1964
 * is triggered when inc_to_block is in the next PAYLOAD_SET (unless in final).
1965
 *
1966
 * It is not safe to report missing blocks in the same PAYLOAD_SET as inc_to_block as
1967
 * the individual blocks may arrive in a random order (generates a lot of duplicate
1968
 * blocks).
1969
 *
1970
 * However, if some missing blocks, but the last 2 blocks of a PAYLOAD_SET have been
1971
 * received, then immediately trigger the 4.08 missing blocks request (not suggested
1972
 * in RFC9177) as this considerably reduces the risk that the penultimate block is in
1973
 * flight, but received in the wrong order and hence requesting a duplicate
1974
 * re-transmission.
1975
 */
1976
static int
1977
coap_request_missing_q_block1(coap_session_t *session, coap_lg_srcv_t *lg_srcv,
1978
0
                              uint32_t inc_to_block) {
1979
0
  uint32_t i;
1980
0
  int32_t block = -1; /* Last one seen */
1981
0
  uint32_t block_size = (uint32_t)1 << (lg_srcv->szx + 4);
1982
0
  uint32_t first_missing;
1983
0
  uint32_t missing_payload;
1984
0
  uint32_t last_payload_block;
1985
0
  coap_pdu_t *pdu = NULL;
1986
  /* Block number starts at 0 */
1987
0
  uint32_t end_block = ((uint32_t)lg_srcv->total_len + block_size - 1) / block_size - 1;
1988
0
  int count = 0;
1989
1990
0
  if (COAP_MAX_PAYLOADS(session) == 0 ||
1991
0
      (int32_t)(inc_to_block / COAP_MAX_PAYLOADS(session)) == lg_srcv->r_m_payload_set)
1992
0
    return 0;
1993
1994
0
  if (inc_to_block != end_block) {
1995
0
    lg_srcv->r_m_payload_set = inc_to_block / COAP_MAX_PAYLOADS(session);
1996
0
  }
1997
1998
0
  if (lg_srcv->rec_blocks.range[0].begin != 0) {
1999
0
    first_missing = 0;
2000
0
  } else {
2001
0
    first_missing = lg_srcv->rec_blocks.range[0].end + 1;
2002
0
  }
2003
0
  if (first_missing > end_block)
2004
0
    return 0;
2005
0
  missing_payload = first_missing / COAP_MAX_PAYLOADS(session);
2006
0
  last_payload_block = (missing_payload + 1) * COAP_MAX_PAYLOADS(session) - 1;
2007
0
  if (inc_to_block != end_block && inc_to_block > last_payload_block) {
2008
0
    inc_to_block = last_payload_block;
2009
0
  }
2010
  /* Ask for the missing blocks */
2011
0
  block = -1;
2012
0
  for (i = 0; i < lg_srcv->rec_blocks.used; i++) {
2013
0
    if (block < (int32_t)lg_srcv->rec_blocks.range[i].begin &&
2014
0
        lg_srcv->rec_blocks.range[i].begin != 0) {
2015
      /* Report on missing blocks */
2016
0
      if (pdu == NULL) {
2017
0
        pdu = pdu_408_build(session, lg_srcv);
2018
0
        if (!pdu)
2019
0
          continue;
2020
0
      }
2021
0
      block++;
2022
0
      for (; block < (int32_t)lg_srcv->rec_blocks.range[i].begin; block++) {
2023
0
        if (!add_408_block(pdu, block)) {
2024
0
          break;
2025
0
        }
2026
0
        count++;
2027
0
      }
2028
0
    }
2029
0
    if (block < (int32_t)lg_srcv->rec_blocks.range[i].end) {
2030
0
      block = lg_srcv->rec_blocks.range[i].end;
2031
0
    }
2032
0
  }
2033
0
  if (inc_to_block == end_block && block < (int32_t)inc_to_block) {
2034
    /* Need to fill to the end */
2035
0
    if (pdu == NULL) {
2036
0
      pdu = pdu_408_build(session, lg_srcv);
2037
0
      if (!pdu)
2038
0
        goto retry;
2039
0
    }
2040
0
    block++;
2041
0
    for (; block <= (int32_t)inc_to_block && count < COAP_MAX_PAYLOADS(session); block++) {
2042
0
      if (!add_408_block(pdu, block)) {
2043
0
        break;
2044
0
      }
2045
0
      count++;
2046
0
    }
2047
0
  }
2048
0
  if (pdu)
2049
0
    coap_send_internal(session, pdu, NULL);
2050
0
retry:
2051
0
  lg_srcv->rec_blocks.retry++;
2052
0
  coap_ticks(&lg_srcv->rec_blocks.last_seen);
2053
0
  return 1;
2054
0
}
2055
#endif /* COAP_Q_BLOCK_SUPPORT */
2056
2057
/*
2058
 * return 1 if there is a future expire time, else 0.
2059
 * update tim_rem with remaining value if return is 1.
2060
 */
2061
int
2062
coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now,
2063
0
                                  coap_tick_t *tim_rem) {
2064
0
  coap_lg_srcv_t *lg_srcv;
2065
0
  coap_lg_srcv_t *q;
2066
0
  coap_tick_t partial_timeout;
2067
0
#if COAP_Q_BLOCK_SUPPORT
2068
0
  coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session);
2069
0
#endif /* COAP_Q_BLOCK_SUPPORT */
2070
0
  int ret = 0;
2071
2072
0
  *tim_rem = COAP_MAX_DELAY_TICKS;
2073
0
#if COAP_Q_BLOCK_SUPPORT
2074
0
  if (COAP_PROTO_NOT_RELIABLE(session->proto) &&
2075
0
      session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)
2076
0
    partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session);
2077
0
  else
2078
0
#endif /* COAP_Q_BLOCK_SUPPORT */
2079
0
    partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session);
2080
2081
0
  LL_FOREACH_SAFE(session->lg_srcv, lg_srcv, q) {
2082
0
    if (lg_srcv->dont_timeout) {
2083
      /* Not safe to timeout at present */
2084
0
      continue;
2085
0
    }
2086
0
    if (COAP_PROTO_RELIABLE(session->proto) || lg_srcv->block_option != COAP_OPTION_Q_BLOCK1)
2087
0
      goto check_expire;
2088
2089
0
#if COAP_Q_BLOCK_SUPPORT
2090
0
    if (lg_srcv->block_option == COAP_OPTION_Q_BLOCK1 && lg_srcv->rec_blocks.used) {
2091
0
      size_t scaled_timeout = receive_timeout *
2092
0
                              ((size_t)1 << lg_srcv->rec_blocks.retry);
2093
2094
0
      if (lg_srcv->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) {
2095
        /* Done NON_MAX_RETRANSMIT retries */
2096
0
        goto expire;
2097
0
      }
2098
0
      if (lg_srcv->rec_blocks.last_seen + scaled_timeout <= now) {
2099
0
        uint32_t block_size = (size_t)1 << (lg_srcv->szx + 4);
2100
0
        coap_log_debug("** %s: lg_srcv %p timeout\n",
2101
0
                       coap_session_str(session), (void *)lg_srcv);
2102
0
        if (!coap_request_missing_q_block1(session, lg_srcv,
2103
0
                                           ((uint32_t)lg_srcv->total_len + block_size - 1) / block_size - 1))
2104
0
          goto expire;
2105
0
      }
2106
0
      if (*tim_rem > lg_srcv->rec_blocks.last_seen + scaled_timeout - now) {
2107
0
        *tim_rem = lg_srcv->rec_blocks.last_seen + scaled_timeout - now;
2108
0
        ret = 1;
2109
0
      }
2110
0
    }
2111
0
#endif /* COAP_Q_BLOCK_SUPPORT */
2112
    /* Used for Block1 and Q-Block1 */
2113
0
check_expire:
2114
0
    if (lg_srcv->no_more_seen)
2115
0
      partial_timeout = 10 * COAP_TICKS_PER_SECOND;
2116
0
    if (lg_srcv->last_used && lg_srcv->last_used + partial_timeout <= now) {
2117
0
#if COAP_Q_BLOCK_SUPPORT
2118
0
expire:
2119
0
#endif /* COAP_Q_BLOCK_SUPPORT */
2120
      /* Expire this entry */
2121
0
      if (lg_srcv->no_more_seen && lg_srcv->block_option == COAP_OPTION_BLOCK1) {
2122
        /*
2123
         * Need to send a separate 4.08 to indicate missing blocks
2124
         * Using NON is permissible as per
2125
         * https://datatracker.ietf.org/doc/html/rfc7252#section-5.2.3
2126
         */
2127
0
        coap_pdu_t *pdu;
2128
2129
0
        pdu = coap_pdu_init(COAP_MESSAGE_NON,
2130
0
                            COAP_RESPONSE_CODE(408),
2131
0
                            coap_new_message_id_lkd(session),
2132
0
                            coap_session_max_pdu_size_lkd(session));
2133
0
        if (pdu) {
2134
0
          if (lg_srcv->last_token)
2135
0
            coap_add_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
2136
0
          coap_add_data(pdu, sizeof("Missing interim block")-1,
2137
0
                        (const uint8_t *)"Missing interim block");
2138
0
          coap_send_internal(session, pdu, NULL);
2139
0
        }
2140
0
      }
2141
0
      LL_DELETE(session->lg_srcv, lg_srcv);
2142
0
      coap_block_delete_lg_srcv(session, lg_srcv);
2143
0
    } else if (lg_srcv->last_used) {
2144
      /* Delay until the lg_srcv needs to expire */
2145
0
      if (*tim_rem > lg_srcv->last_used + partial_timeout - now) {
2146
0
        *tim_rem = lg_srcv->last_used + partial_timeout - now;
2147
0
        ret = 1;
2148
0
      }
2149
0
    }
2150
0
  }
2151
0
  return ret;
2152
0
}
2153
#endif /* COAP_SERVER_SUPPORT */
2154
2155
#if COAP_Q_BLOCK_SUPPORT
2156
static coap_pdu_t *
2157
coap_next_block_to_send(coap_session_t *session,
2158
                        coap_lg_xmit_t *lg_xmit,
2159
                        coap_block_b_t *block,
2160
                        coap_pdu_t *pdu,
2161
                        uint32_t delayqueue_cnt,
2162
0
                        int is_first) {
2163
0
  const uint8_t *ptoken;
2164
0
  uint8_t ltoken[8];
2165
0
  size_t ltoken_length;
2166
0
  coap_pdu_t *block_pdu = NULL;
2167
0
  uint64_t token;
2168
0
  coap_opt_filter_t drop_options;
2169
0
  size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4));
2170
0
  size_t offset;
2171
0
  uint8_t buf[8];
2172
0
  int need_block = 0;
2173
2174
0
  if (COAP_MAX_PAYLOADS(session) == 0)
2175
0
    return NULL;
2176
  /* Get the next block to transmit (which could be a retry */
2177
0
  block->num = lg_xmit->send_blocks.range[0].begin;
2178
2179
0
  offset = block->num * chunk;
2180
0
  block->m = offset + chunk < lg_xmit->data_info->length;
2181
2182
0
  if (!(block->m || lg_xmit->send_blocks.used) && COAP_MAX_PAYLOADS(session)) {
2183
0
    return NULL;
2184
0
  }
2185
0
  if (is_first &&
2186
0
      ((pdu->type == COAP_MESSAGE_ACK && lg_xmit->option == COAP_OPTION_Q_BLOCK2) ||
2187
0
       (pdu->type == COAP_MESSAGE_CON && delayqueue_cnt < COAP_NSTART(session)))) {
2188
0
    need_block = 1;
2189
0
  }
2190
0
  if (COAP_PROTO_RELIABLE(session->proto)) {
2191
0
    need_block = 1;
2192
0
  }
2193
2194
0
  if (need_block ||
2195
0
      (pdu->type == COAP_MESSAGE_NON &&
2196
0
       (lg_xmit->send_blocks.used > 1 ||
2197
0
        (block->num % COAP_MAX_PAYLOADS(session) != 0) ||
2198
0
        is_first))) {
2199
0
    if (!is_first && block->num % COAP_MAX_PAYLOADS(session) == 0 &&
2200
0
        lg_xmit->send_blocks.used == 1 && !COAP_PROTO_RELIABLE(session->proto)) {
2201
0
      coap_log_debug("Q-Block: Stopping at block %d\n", block->num);
2202
0
    } else {
2203
      /*
2204
       * Allocate next block pdu if there is headroom and if one of
2205
       *   NON and more in MAX_PAYLOADS
2206
       *   CON and NSTART allows it (based on number in delayqueue)
2207
       *   Reliable transport
2208
       */
2209
0
      coap_log_debug("Q-Block: %s block %d\n", is_first ? "First" : "Next", block->num);
2210
0
      if (COAP_PDU_IS_RESPONSE(pdu)) {
2211
0
        ptoken = pdu->actual_token.s;
2212
0
        ltoken_length = pdu->actual_token.length;
2213
0
      } else {
2214
0
        token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count);
2215
0
        ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token);
2216
0
        ptoken = ltoken;
2217
0
      }
2218
2219
0
      memset(&drop_options, 0, sizeof(coap_opt_filter_t));
2220
0
      coap_option_filter_set(&drop_options, lg_xmit->option);
2221
0
      block_pdu = coap_pdu_duplicate_lkd(pdu, session,
2222
0
                                         ltoken_length,
2223
0
                                         ptoken, &drop_options, COAP_BOOL_FALSE);
2224
0
      if (block_pdu->type == COAP_MESSAGE_ACK)
2225
0
        block_pdu->type = COAP_MESSAGE_CON;
2226
2227
0
      if (!coap_update_option(block_pdu, lg_xmit->option,
2228
0
                              coap_encode_var_safe(buf,
2229
0
                                                   sizeof(buf),
2230
0
                                                   ((block->num) << 4) |
2231
0
                                                   (block->m << 3) |
2232
0
                                                   block->szx),
2233
0
                              buf)) {
2234
0
        coap_log_warn("Internal update issue option\n");
2235
0
        coap_delete_pdu_lkd(block_pdu);
2236
0
        return NULL;
2237
0
      }
2238
0
    }
2239
0
  }
2240
0
  return block_pdu;
2241
0
}
2242
2243
/*
2244
 * pdu is always released before return IF COAP_SEND_INC_PDU
2245
 */
2246
coap_mid_t
2247
coap_send_q_blocks(coap_session_t *session,
2248
                   coap_lg_xmit_t *lg_xmit,
2249
                   coap_block_b_t block,
2250
                   coap_pdu_t *pdu,
2251
0
                   coap_send_pdu_t send_pdu) {
2252
0
  coap_pdu_t *block_pdu = NULL; /* The next block to send after any initial PDU */
2253
0
  coap_mid_t mid = COAP_INVALID_MID;
2254
0
  uint32_t delayqueue_cnt = 0;
2255
0
  coap_block_b_t l_block;
2256
0
  int is_non;
2257
2258
0
  if (!pdu)
2259
0
    return COAP_INVALID_MID;
2260
2261
0
  is_non = pdu->type == COAP_MESSAGE_NON ? 1 : 0;
2262
2263
0
  if (!lg_xmit || session->is_rate_limiting) {
2264
0
    if (send_pdu == COAP_SEND_INC_PDU) {
2265
0
      if (lg_xmit &&
2266
0
          (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &l_block) ||
2267
0
           coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &l_block))) {
2268
0
        mid = coap_send_internal(session, pdu, NULL);
2269
0
        if (mid != COAP_INVALID_MID) {
2270
0
          blocks_delete_entry(&lg_xmit->send_blocks, l_block.num);
2271
0
        }
2272
0
      } else {
2273
0
        return coap_send_internal(session, pdu, NULL);
2274
0
      }
2275
0
    }
2276
0
    return COAP_INVALID_MID;
2277
0
  }
2278
2279
0
  if (pdu->type == COAP_MESSAGE_CON) {
2280
0
    coap_queue_t *delayqueue;
2281
2282
0
    delayqueue_cnt = session->con_active +
2283
0
                     (send_pdu == COAP_SEND_INC_PDU ? 1 : 0);
2284
0
    LL_FOREACH(session->delayqueue, delayqueue) {
2285
0
      delayqueue_cnt++;
2286
0
    }
2287
0
  }
2288
0
  pdu->lg_xmit = lg_xmit;
2289
2290
  /* Send initial pdu (which deletes 'pdu') */
2291
0
  if (send_pdu == COAP_SEND_INC_PDU) {
2292
0
    if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &l_block) ||
2293
0
        coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &l_block)) {
2294
0
      mid = coap_send_internal(session, pdu, NULL);
2295
0
      if (mid != COAP_INVALID_MID) {
2296
0
        blocks_delete_entry(&lg_xmit->send_blocks, l_block.num);
2297
0
      }
2298
0
    } else {
2299
0
      mid = coap_send_internal(session, pdu, NULL);
2300
0
    }
2301
0
    if (mid == COAP_INVALID_MID) {
2302
      /* Not expected, underlying issue somewhere */
2303
0
      coap_delete_pdu_lkd(block_pdu);
2304
0
      return COAP_INVALID_MID;
2305
0
    }
2306
0
    coap_ticks(&lg_xmit->last_sent);
2307
0
  }
2308
  /* Unsafe to use pdu in later code */
2309
2310
0
  coap_lg_xmit_reference_lkd(lg_xmit);
2311
0
  l_block = block;
2312
0
  block_pdu = coap_next_block_to_send(session, lg_xmit, &l_block, pdu, delayqueue_cnt, 1);
2313
0
  if (block_pdu) {
2314
0
    block = l_block;
2315
0
  }
2316
2317
0
  while (block_pdu && lg_xmit->send_blocks.used) {
2318
0
    coap_pdu_t *t_pdu = NULL;
2319
2320
0
    if (lg_xmit->data_info->get_func) {
2321
#if COAP_CONSTRAINED_STACK
2322
      /* Protected by global_lock if needed */
2323
      static uint8_t l_data[1024];
2324
#else /* ! COAP_CONSTRAINED_STACK */
2325
0
      uint8_t l_data[1024];
2326
0
#endif /* ! COAP_CONSTRAINED_STACK */
2327
0
      size_t l_length;
2328
0
      size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4));
2329
2330
0
      (void)chunk;
2331
0
      assert(chunk <= 1024);
2332
0
      if (lg_xmit->data_info->get_func(session, chunk,
2333
0
                                       block.num * chunk, l_data, &l_length,
2334
0
                                       lg_xmit->data_info->app_ptr)) {
2335
0
        if (!coap_add_data(block_pdu, l_length, l_data)) {
2336
0
          coap_log_warn("Internal update issue data (1)\n");
2337
0
          coap_delete_pdu_lkd(block_pdu);
2338
0
          block_pdu = NULL;
2339
0
          break;
2340
0
        }
2341
0
      }
2342
0
    } else {
2343
0
      if (!coap_add_block(block_pdu,
2344
0
                          lg_xmit->data_info->length,
2345
0
                          lg_xmit->data_info->data,
2346
0
                          block.num,
2347
0
                          block.szx)) {
2348
0
        coap_log_warn("Internal update issue data (2)\n");
2349
0
        coap_delete_pdu_lkd(block_pdu);
2350
0
        block_pdu = NULL;
2351
0
        break;
2352
0
      }
2353
0
    }
2354
0
    if (COAP_PDU_IS_RESPONSE(block_pdu)) {
2355
0
      lg_xmit->last_block = block.num;
2356
0
    }
2357
    /* This block will now be transmitted */
2358
0
    blocks_delete_entry(&lg_xmit->send_blocks, block.num);
2359
2360
0
    if (block_pdu->type == COAP_MESSAGE_NON || COAP_PROTO_RELIABLE(session->proto)) {
2361
      /* Get the next block to transmit (which could be a retry) */
2362
0
      l_block = block;
2363
0
      t_pdu = coap_next_block_to_send(session, lg_xmit, &l_block, block_pdu, delayqueue_cnt, 0);
2364
0
      if (t_pdu) {
2365
0
        block = l_block;
2366
0
      }
2367
0
    }
2368
2369
0
    mid = coap_send_internal(session, block_pdu, NULL);
2370
0
    if (mid == COAP_INVALID_MID) {
2371
      /* Need to resent this one ... */
2372
0
      blocks_add_entry(&lg_xmit->send_blocks, block.num, block.m);
2373
      /* Not expected, underlying issue somewhere */
2374
0
      coap_lg_xmit_release_lkd(session, lg_xmit);
2375
0
      coap_delete_pdu_lkd(t_pdu);
2376
0
      return COAP_INVALID_MID;
2377
0
    }
2378
0
    coap_ticks(&lg_xmit->last_sent);
2379
0
    block_pdu = t_pdu;
2380
0
  }
2381
0
  if (is_non) {
2382
0
    coap_log_debug("Q-Block: Current MAX_PAYLOAD sent\n");
2383
0
  }
2384
0
  coap_delete_pdu_lkd(block_pdu);
2385
0
  if (!block.m) {
2386
0
    lg_xmit->last_payload = 0;
2387
0
    coap_ticks(&lg_xmit->last_all_sent);
2388
0
  } else
2389
0
    coap_ticks(&lg_xmit->last_payload);
2390
0
  coap_lg_xmit_release_lkd(session, lg_xmit);
2391
0
  return mid;
2392
0
}
2393
2394
#if COAP_CLIENT_SUPPORT
2395
/*
2396
 * Return 1 if there is a future expire time, else 0.
2397
 * Update tim_rem with remaining value if return is 1.
2398
 */
2399
int
2400
0
coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem) {
2401
0
  coap_lg_xmit_t *lg_xmit;
2402
0
  coap_lg_xmit_t *q;
2403
0
  coap_tick_t timed_out;
2404
0
  int ret = 0;
2405
2406
0
  *tim_rem = COAP_MAX_DELAY_TICKS;
2407
0
  LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
2408
0
    coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks;
2409
2410
0
    if (lg_xmit->sent_pdu->type != COAP_MESSAGE_NON)
2411
0
      continue;
2412
0
    if (now <= non_timeout) {
2413
      /* Too early in the startup cycle to have an accurate response */
2414
0
      *tim_rem = non_timeout - now;
2415
0
      return 1;
2416
0
    }
2417
0
    timed_out = now - non_timeout;
2418
2419
0
    if (lg_xmit->last_payload && lg_xmit->send_blocks.used) {
2420
0
      if (lg_xmit->last_payload <= timed_out) {
2421
        /* Send off the next MAX_PAYLOAD set */
2422
0
        coap_block_b_t block;
2423
0
        size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2424
0
        size_t offset;
2425
2426
0
        coap_log_debug("Q-Block: Timeout - start next MAX_PAYLOADS_SET\n");
2427
0
        memset(&block, 0, sizeof(block));
2428
0
        block.num = lg_xmit->send_blocks.range[0].begin;
2429
0
        offset = block.num * chunk;
2430
0
        block.m = offset + chunk < lg_xmit->data_info->length;
2431
0
        block.szx = lg_xmit->blk_size;
2432
0
        coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
2433
0
        if (*tim_rem > non_timeout) {
2434
0
          *tim_rem = non_timeout;
2435
0
          ret = 1;
2436
0
        }
2437
0
      } else {
2438
        /* Delay until the next MAX_PAYLOAD needs to be sent off */
2439
0
        if (*tim_rem > lg_xmit->last_payload - timed_out) {
2440
0
          *tim_rem = lg_xmit->last_payload - timed_out;
2441
0
          ret = 1;
2442
0
        }
2443
0
      }
2444
0
    } else if (lg_xmit->last_all_sent) {
2445
0
      non_timeout = COAP_NON_TIMEOUT_TICKS(session);
2446
0
      if (lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout <= now) {
2447
        /* Expire this entry */
2448
0
        LL_DELETE(session->lg_xmit, lg_xmit);
2449
0
        coap_block_delete_lg_xmit(session, lg_xmit);
2450
0
      } else {
2451
        /* Delay until the lg_xmit needs to expire */
2452
0
        if (*tim_rem > lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout - now) {
2453
0
          *tim_rem = lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout - now;
2454
0
          ret = 1;
2455
0
        }
2456
0
      }
2457
0
    }
2458
0
  }
2459
0
  return ret;
2460
0
}
2461
#endif /* COAP_CLIENT_SUPPORT */
2462
2463
#if COAP_SERVER_SUPPORT
2464
/*
2465
 * Return 1 if there is a future expire time, else 0.
2466
 * Update tim_rem with remaining value if return is 1.
2467
 */
2468
int
2469
0
coap_block_check_q_block2_xmit(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem) {
2470
0
  coap_lg_xmit_t *lg_xmit;
2471
0
  coap_lg_xmit_t *q;
2472
0
  coap_tick_t timed_out;
2473
0
  int ret = 0;
2474
2475
0
  *tim_rem = (coap_tick_t)-1;
2476
0
  LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) {
2477
0
    coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks;
2478
2479
0
    if (now <= non_timeout) {
2480
      /* Too early in the startup cycle to have an accurate response */
2481
0
      *tim_rem = non_timeout - now;
2482
0
      return 1;
2483
0
    }
2484
0
    timed_out = now - non_timeout;
2485
2486
0
    if (lg_xmit->last_payload && lg_xmit->send_blocks.used) {
2487
0
      if (lg_xmit->last_payload <= timed_out) {
2488
        /* Send off the next MAX_PAYLOAD set */
2489
0
        coap_block_b_t block;
2490
0
        size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
2491
0
        size_t offset;
2492
2493
0
        memset(&block, 0, sizeof(block));
2494
0
        block.num = lg_xmit->send_blocks.range[0].begin;
2495
0
        offset = block.num * chunk;
2496
0
        block.m = offset + chunk < lg_xmit->data_info->length;
2497
0
        block.szx = lg_xmit->blk_size;
2498
0
        if (block.num == (uint32_t)lg_xmit->last_block)
2499
0
          coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
2500
0
        if (*tim_rem > non_timeout) {
2501
0
          *tim_rem = non_timeout;
2502
0
          ret = 1;
2503
0
        }
2504
0
      } else {
2505
        /* Delay until the next MAX_PAYLOAD needs to be sent off */
2506
0
        if (*tim_rem > lg_xmit->last_payload - timed_out) {
2507
0
          *tim_rem = lg_xmit->last_payload - timed_out;
2508
0
          ret = 1;
2509
0
        }
2510
0
      }
2511
0
    } else if (lg_xmit->last_all_sent) {
2512
0
      non_timeout = COAP_NON_TIMEOUT_TICKS(session);
2513
0
      if (lg_xmit->last_all_sent +  4 * non_timeout <= now) {
2514
        /* Expire this entry */
2515
0
        LL_DELETE(session->lg_xmit, lg_xmit);
2516
0
        coap_block_delete_lg_xmit(session, lg_xmit);
2517
0
      } else {
2518
        /* Delay until the lg_xmit needs to expire */
2519
0
        if (*tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) {
2520
0
          *tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now;
2521
0
          ret = 1;
2522
0
        }
2523
0
      }
2524
0
    }
2525
0
  }
2526
0
  return ret;
2527
0
}
2528
#endif /* COAP_SERVER_SUPPORT */
2529
#endif /* COAP_Q_BLOCK_SUPPORT */
2530
2531
#if COAP_CLIENT_SUPPORT
2532
/*
2533
 * If Observe = 0, save the token away and return NULL
2534
 * Else If Observe = 1, return the saved token for this block
2535
 * Else, return NULL
2536
 */
2537
static coap_bin_const_t *
2538
track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv,
2539
0
                    uint32_t block_num, coap_bin_const_t *token) {
2540
  /* Need to handle Observe for large FETCH */
2541
0
  coap_opt_iterator_t opt_iter;
2542
0
  coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE,
2543
0
                                      &opt_iter);
2544
2545
0
  if (opt && lg_crcv) {
2546
0
    int observe_action = -1;
2547
0
    coap_bin_const_t **tmp;
2548
2549
0
    observe_action = coap_decode_var_bytes(coap_opt_value(opt),
2550
0
                                           coap_opt_length(opt));
2551
0
    if (observe_action == COAP_OBSERVE_ESTABLISH) {
2552
      /* Save the token in lg_crcv */
2553
0
      if (lg_crcv->obs_token_cnt <= block_num) {
2554
0
        size_t i;
2555
2556
0
        tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token,
2557
0
                                (block_num + 1) * sizeof(lg_crcv->obs_token[0]));
2558
0
        if (tmp == NULL)
2559
0
          return NULL;
2560
0
        lg_crcv->obs_token = tmp;
2561
0
        for (i = lg_crcv->obs_token_cnt; i < block_num + 1; i++) {
2562
0
          lg_crcv->obs_token[i] = NULL;
2563
0
        }
2564
0
      }
2565
0
      coap_delete_bin_const(lg_crcv->obs_token[block_num]);
2566
2567
0
      if (lg_crcv->obs_token_cnt <= block_num)
2568
0
        lg_crcv->obs_token_cnt = block_num + 1;
2569
0
      lg_crcv->obs_token[block_num] = coap_new_bin_const(token->s,
2570
0
                                                         token->length);
2571
0
      if (lg_crcv->obs_token[block_num] == NULL)
2572
0
        return NULL;
2573
0
    } else if (observe_action == COAP_OBSERVE_CANCEL) {
2574
      /* Use the token in lg_crcv */
2575
0
      if (block_num < lg_crcv->obs_token_cnt) {
2576
0
        return lg_crcv->obs_token[block_num];
2577
0
      }
2578
0
    }
2579
0
  }
2580
0
  return NULL;
2581
0
}
2582
2583
#if COAP_Q_BLOCK_SUPPORT
2584
coap_mid_t
2585
coap_send_q_block1(coap_session_t *session,
2586
                   coap_block_b_t block,
2587
                   coap_pdu_t *request,
2588
0
                   coap_send_pdu_t send_request) {
2589
  /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK1 */
2590
0
  coap_lg_xmit_t *lg_xmit;
2591
0
  uint64_t token_match =
2592
0
      STATE_TOKEN_BASE(coap_decode_var_bytes8(request->actual_token.s,
2593
0
                                              request->actual_token.length));
2594
2595
0
  LL_FOREACH(session->lg_xmit, lg_xmit) {
2596
0
    if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 &&
2597
0
        (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) ||
2598
0
         token_match ==
2599
0
         STATE_TOKEN_BASE(coap_decode_var_bytes8(lg_xmit->b.b1.app_token->s,
2600
0
                                                 lg_xmit->b.b1.app_token->length))))
2601
0
      break;
2602
    /* try out the next one */
2603
0
  }
2604
0
  return coap_send_q_blocks(session, lg_xmit, block, request, send_request);
2605
0
}
2606
#endif /* COAP_Q_BLOCK_SUPPORT */
2607
#endif /* COAP_CLIENT_SUPPORT */
2608
2609
#if COAP_SERVER_SUPPORT
2610
#if COAP_Q_BLOCK_SUPPORT
2611
/*
2612
 * response is always released before return IF COAP_SEND_INC_PDU
2613
 */
2614
coap_mid_t
2615
coap_send_q_block2(coap_session_t *session,
2616
                   coap_resource_t *resource,
2617
                   const coap_string_t *query,
2618
                   coap_pdu_code_t request_method,
2619
                   coap_block_b_t block,
2620
                   coap_pdu_t *response,
2621
0
                   coap_send_pdu_t send_response) {
2622
  /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK2 */
2623
0
  coap_lg_xmit_t *lg_xmit;
2624
0
  coap_string_t empty = { 0, NULL};
2625
2626
0
  LL_FOREACH(session->lg_xmit, lg_xmit) {
2627
0
    if (lg_xmit->option == COAP_OPTION_Q_BLOCK2 &&
2628
0
        resource == lg_xmit->b.b2.resource &&
2629
0
        request_method == lg_xmit->b.b2.request_method &&
2630
0
        coap_string_equal(query ? query : &empty,
2631
0
                          lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty))
2632
0
      break;
2633
0
  }
2634
0
  return coap_send_q_blocks(session, lg_xmit, block, response, send_response);
2635
0
}
2636
#endif /* COAP_Q_BLOCK_SUPPORT */
2637
#endif /* COAP_SERVER_SUPPORT */
2638
2639
static void
2640
coap_block_release_lg_xmit_data(coap_session_t *session,
2641
0
                                coap_lg_xmit_data_t *data_info) {
2642
0
  if (!data_info)
2643
0
    return;
2644
0
  if (data_info->ref > 0) {
2645
0
    data_info->ref--;
2646
0
    return;
2647
0
  }
2648
0
  if (data_info->release_func) {
2649
0
    coap_lock_callback(data_info->release_func(session,
2650
0
                                               data_info->app_ptr));
2651
0
    data_info->release_func = NULL;
2652
0
  }
2653
0
  coap_free_type(COAP_STRING, data_info);
2654
0
}
2655
2656
#if COAP_CLIENT_SUPPORT
2657
#if COAP_Q_BLOCK_SUPPORT
2658
/*
2659
 * Send out a test PDU for Q-Block.
2660
 */
2661
coap_mid_t
2662
0
coap_block_test_q_block(coap_session_t *session, coap_pdu_t *actual) {
2663
0
  coap_pdu_t *pdu;
2664
0
  uint8_t token[8];
2665
0
  size_t token_len;
2666
0
  uint8_t buf[4];
2667
0
  coap_mid_t mid;
2668
0
  coap_bin_const_t *k_token;
2669
2670
#if NDEBUG
2671
  (void)actual;
2672
#endif /* NDEBUG */
2673
0
  assert(session->block_mode & COAP_BLOCK_TRY_Q_BLOCK &&
2674
0
         session->type == COAP_SESSION_TYPE_CLIENT &&
2675
0
         COAP_PDU_IS_REQUEST(actual));
2676
2677
0
  coap_log_debug("Testing for Q-Block support\n");
2678
  /* RFC9177 Section 4.1 when checking if available */
2679
0
  pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET,
2680
0
                      coap_new_message_id_lkd(session),
2681
0
                      coap_session_max_pdu_size_lkd(session));
2682
0
  if (!pdu) {
2683
0
    return COAP_INVALID_MID;
2684
0
  }
2685
2686
0
  coap_session_new_token(session, &token_len, token);
2687
0
  coap_add_token(pdu, token_len, token);
2688
  /* Use a resource that the server MUST support (.well-known/core) */
2689
0
  coap_add_option(pdu, COAP_OPTION_URI_PATH,
2690
0
                  11, (const uint8_t *)".well-known");
2691
0
  coap_add_option(pdu, COAP_OPTION_URI_PATH,
2692
0
                  4, (const uint8_t *)"core");
2693
  /*
2694
   * M needs to be unset as 'asking' for only the first block using
2695
   * Q-Block2 as a test for server support.
2696
   * See RFC9177 Section 4.4 Using the Q-Block2 Option.
2697
   *
2698
   * As the client is asking for 16 byte chunks, it is unlikely that
2699
   * the .well-known/core response will be 16 bytes or less, so
2700
   * if the server supports Q-Block, it will be forced to respond with
2701
   * a Q-Block2, so the client can detect the server Q-Block support.
2702
   */
2703
0
  coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2,
2704
0
                     coap_encode_var_safe(buf, sizeof(buf),
2705
0
                                          (0 << 4) | (0 << 3) | 0),
2706
0
                     buf);
2707
0
  k_token = coap_new_bin_const(pdu->actual_token.s, pdu->actual_token.length);
2708
0
  set_block_mode_probe_q(session->block_mode);
2709
0
  mid = coap_send_internal(session, pdu, NULL);
2710
0
  if (mid == COAP_INVALID_MID) {
2711
0
    coap_delete_bin_const(k_token);
2712
0
    return COAP_INVALID_MID;
2713
0
  }
2714
0
  session->remote_test_mid = mid;
2715
0
  coap_delete_bin_const(session->last_token);
2716
0
  session->last_token = k_token;
2717
0
  return mid;
2718
0
}
2719
#endif /* COAP_Q_BLOCK_SUPPORT */
2720
2721
coap_lg_crcv_t *
2722
coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu,
2723
0
                       coap_lg_xmit_t *lg_xmit) {
2724
0
  coap_block_b_t block;
2725
0
  coap_lg_crcv_t *lg_crcv;
2726
0
  uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1);
2727
2728
0
  lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t));
2729
2730
0
  if (lg_crcv == NULL)
2731
0
    return NULL;
2732
2733
0
  coap_log_debug("** %s: lg_crcv %p initialized - stateless token xxxxx%011llx\n",
2734
0
                 coap_session_str(session), (void *)lg_crcv,
2735
0
                 STATE_TOKEN_BASE(state_token));
2736
0
  memset(lg_crcv, 0, sizeof(coap_lg_crcv_t));
2737
0
  lg_crcv->initial = 1;
2738
0
  coap_ticks(&lg_crcv->last_used);
2739
  /* Keep a copy of the sent pdu */
2740
0
  lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu);
2741
0
  if (lg_xmit) {
2742
0
    coap_opt_iterator_t opt_iter;
2743
0
    coap_opt_t *opt;
2744
2745
0
    opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter);
2746
2747
0
    if (opt) {
2748
0
      int observe_action;
2749
2750
0
      observe_action = coap_decode_var_bytes(coap_opt_value(opt),
2751
0
                                             coap_opt_length(opt));
2752
0
      if (observe_action == COAP_OBSERVE_ESTABLISH) {
2753
        /* Need to keep information for Observe Cancel */
2754
0
        size_t data_len;
2755
0
        const uint8_t *data;
2756
2757
0
        if (coap_get_data(pdu, &data_len, &data)) {
2758
0
          if (data_len < lg_xmit->data_info->length) {
2759
0
            lg_xmit->data_info->ref++;
2760
0
            lg_crcv->obs_data = lg_xmit->data_info;
2761
0
          }
2762
0
        }
2763
0
      }
2764
0
    }
2765
0
  }
2766
2767
  /* Need to keep original token for updating response PDUs */
2768
0
  lg_crcv->app_token = coap_new_binary(pdu->actual_token.length);
2769
0
  if (!lg_crcv->app_token) {
2770
0
    coap_block_delete_lg_crcv(session, lg_crcv);
2771
0
    return NULL;
2772
0
  }
2773
0
  memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length);
2774
2775
  /* Need to set up a base token for actual communications if retries needed */
2776
0
  lg_crcv->retry_counter = 1;
2777
0
  lg_crcv->state_token = state_token;
2778
0
  coap_address_copy(&lg_crcv->upstream, &session->addr_info.remote);
2779
2780
0
  if (pdu->code == COAP_REQUEST_CODE_FETCH) {
2781
0
    coap_bin_const_t *new_token;
2782
2783
    /* Need to save/restore Observe Token for large FETCH */
2784
0
    new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token);
2785
0
    if (new_token)
2786
0
      coap_update_token(pdu, new_token->length, new_token->s);
2787
0
  }
2788
2789
0
  if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
2790
    /* In case it is there - must not be in continuing request PDUs */
2791
0
    lg_crcv->o_block_option = COAP_OPTION_BLOCK1;
2792
0
    lg_crcv->o_blk_size = block.aszx;
2793
0
  }
2794
2795
0
  return lg_crcv;
2796
0
}
2797
2798
void
2799
coap_block_delete_lg_crcv(coap_session_t *session,
2800
0
                          coap_lg_crcv_t *lg_crcv) {
2801
0
  size_t i;
2802
2803
#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2804
  (void)session;
2805
#endif
2806
0
  if (lg_crcv == NULL)
2807
0
    return;
2808
2809
0
  if (lg_crcv->ref > 0) {
2810
0
    lg_crcv->ref--;
2811
0
    return;
2812
0
  }
2813
2814
0
  coap_free_type(COAP_STRING, lg_crcv->body_data);
2815
0
  if (lg_crcv->obs_data) {
2816
0
    coap_block_release_lg_xmit_data(session, lg_crcv->obs_data);
2817
0
    lg_crcv->obs_data = NULL;
2818
0
  }
2819
0
  coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream);
2820
0
  coap_log_debug("** %s: lg_crcv %p released\n",
2821
0
                 coap_session_str(session), (void *)lg_crcv);
2822
0
  coap_delete_binary(lg_crcv->app_token);
2823
0
  for (i = 0; i < lg_crcv->obs_token_cnt; i++) {
2824
0
    coap_delete_bin_const(lg_crcv->obs_token[i]);
2825
0
  }
2826
0
  coap_free_type(COAP_STRING, lg_crcv->obs_token);
2827
0
  coap_delete_pdu_lkd(lg_crcv->sent_pdu);
2828
0
  coap_free_type(COAP_LG_CRCV, lg_crcv);
2829
0
}
2830
#endif /* COAP_CLIENT_SUPPORT */
2831
2832
#if COAP_SERVER_SUPPORT
2833
void
2834
coap_block_delete_lg_srcv(coap_session_t *session,
2835
0
                          coap_lg_srcv_t *lg_srcv) {
2836
#if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG)
2837
  (void)session;
2838
#endif
2839
0
  if (lg_srcv == NULL)
2840
0
    return;
2841
2842
0
  if (lg_srcv->ref > 0) {
2843
0
    lg_srcv->ref--;
2844
0
    return;
2845
0
  }
2846
2847
0
  coap_delete_str_const(lg_srcv->uri_path);
2848
0
  coap_delete_bin_const(lg_srcv->last_token);
2849
0
  coap_free_type(COAP_STRING, lg_srcv->body_data);
2850
0
  coap_log_debug("** %s: lg_srcv %p released\n",
2851
0
                 coap_session_str(session), (void *)lg_srcv);
2852
0
  coap_free_type(COAP_LG_SRCV, lg_srcv);
2853
0
}
2854
#endif /* COAP_SERVER_SUPPORT */
2855
2856
void
2857
coap_block_delete_lg_xmit(coap_session_t *session,
2858
0
                          coap_lg_xmit_t *lg_xmit) {
2859
0
  if (lg_xmit == NULL)
2860
0
    return;
2861
2862
0
  if (lg_xmit->ref > 0) {
2863
0
    lg_xmit->ref--;
2864
0
    return;
2865
0
  }
2866
2867
0
  coap_block_release_lg_xmit_data(session, lg_xmit->data_info);
2868
0
  if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu))
2869
0
    coap_delete_binary(lg_xmit->b.b1.app_token);
2870
0
  else
2871
0
    coap_delete_string(lg_xmit->b.b2.query);
2872
0
  coap_delete_pdu_lkd(lg_xmit->sent_pdu);
2873
2874
0
  coap_log_debug("** %s: lg_xmit %p released\n",
2875
0
                 coap_session_str(session), (void *)lg_xmit);
2876
0
  coap_free_type(COAP_LG_XMIT, lg_xmit);
2877
0
}
2878
2879
#if COAP_SERVER_SUPPORT
2880
typedef struct {
2881
  uint32_t num;
2882
  int is_continue;
2883
} send_track;
2884
2885
static int
2886
add_block_send(uint32_t num, int is_continue, send_track *out_blocks,
2887
0
               uint32_t *count, uint32_t max_count) {
2888
0
  uint32_t i;
2889
2890
0
  for (i = 0; i < *count && *count < max_count; i++) {
2891
0
    if (num == out_blocks[i].num)
2892
0
      return 0;
2893
0
    else if (num < out_blocks[i].num) {
2894
0
      if (*count - i > 1)
2895
0
        memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1);
2896
0
      out_blocks[i].num = num;
2897
0
      out_blocks[i].is_continue = is_continue;
2898
0
      (*count)++;
2899
0
      return 1;
2900
0
    }
2901
0
  }
2902
0
  if (*count < max_count) {
2903
0
    out_blocks[i].num = num;
2904
0
    out_blocks[i].is_continue = is_continue;
2905
0
    (*count)++;
2906
0
    return 1;
2907
0
  }
2908
0
  return 0;
2909
0
}
2910
2911
/*
2912
 * Need to see if this is a request for the next block of a large body
2913
 * transfer.  If so, need to initiate the response with the next blocks
2914
 * and not trouble the application.
2915
 *
2916
 * If additional responses needed, then these are explicitly sent out and
2917
 * 'response' is updated to be the last response to be sent.  There can be
2918
 * multiple Q-Block2 in the request, as well as  the 'Continue' Q-Block2
2919
 * request.
2920
 *
2921
 * This is set up using coap_add_data_large_response_lkd()
2922
 *
2923
 * Server is sending a large data response to GET / observe (Block2)
2924
 *
2925
 * Return: 0 Call application handler
2926
 *         1 Do not call application handler - just send the built response
2927
 */
2928
int
2929
coap_handle_request_send_block(coap_session_t *session,
2930
                               coap_pdu_t *pdu,
2931
                               coap_pdu_t *response,
2932
                               coap_resource_t *resource,
2933
0
                               coap_string_t *query) {
2934
0
  coap_lg_xmit_t *lg_xmit = NULL;
2935
0
  coap_block_b_t block;
2936
0
  coap_block_b_t alt_block;
2937
0
  uint16_t block_opt = 0;
2938
0
  send_track *out_blocks = NULL;
2939
0
  const char *error_phrase;
2940
0
  coap_opt_iterator_t opt_iter;
2941
0
  size_t chunk;
2942
0
  coap_opt_iterator_t opt_b_iter;
2943
0
  coap_opt_t *option;
2944
0
  uint32_t request_cnt, i;
2945
0
  coap_opt_t *etag_opt = NULL;
2946
0
  coap_pdu_t *out_pdu = response;
2947
0
#if COAP_Q_BLOCK_SUPPORT
2948
0
  size_t max_block;
2949
2950
  /* Is client indicating that it supports Q_BLOCK2 ? */
2951
0
  if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) {
2952
0
    if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK))
2953
0
      set_block_mode_has_q(session->block_mode);
2954
0
    block_opt = COAP_OPTION_Q_BLOCK2;
2955
0
  }
2956
0
#endif /* COAP_Q_BLOCK_SUPPORT */
2957
0
  if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &alt_block)) {
2958
0
    if (block_opt) {
2959
0
      coap_log_warn("Block2 and Q-Block2 cannot be in the same request\n");
2960
0
      coap_add_data(response, sizeof("Both Block2 and Q-Block2 invalid")-1,
2961
0
                    (const uint8_t *)"Both Block2 and Q-Block2 invalid");
2962
0
      response->code = COAP_RESPONSE_CODE(400);
2963
0
      goto skip_app_handler;
2964
0
    }
2965
0
    block = alt_block;
2966
0
    block_opt = COAP_OPTION_BLOCK2;
2967
0
  }
2968
0
  if (block_opt == 0)
2969
0
    return 0;
2970
0
  if (block.num == 0) {
2971
0
#if COAP_Q_BLOCK_SUPPORT
2972
0
    if (block_opt == COAP_OPTION_Q_BLOCK2) {
2973
0
      if (block.m) {
2974
0
        return 0;
2975
0
      }
2976
0
    } else
2977
0
#endif /* COAP_Q_BLOCK_SUPPORT */
2978
      /* Get a fresh copy of the data */
2979
0
      return 0;
2980
0
  }
2981
0
  lg_xmit = coap_find_lg_xmit_response(session, pdu, resource, query);
2982
0
  if (lg_xmit == NULL)
2983
0
    return 0;
2984
2985
0
#if COAP_Q_BLOCK_SUPPORT
2986
0
  out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track) * COAP_MAX_PAYLOADS(session));
2987
#else /* ! COAP_Q_BLOCK_SUPPORT */
2988
  out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track));
2989
#endif /* ! COAP_Q_BLOCK_SUPPORT */
2990
0
  if (!out_blocks) {
2991
0
    goto internal_issue;
2992
0
  }
2993
2994
  /* lg_xmit (response) found */
2995
2996
0
  etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter);
2997
0
  if (etag_opt) {
2998
    /* There may be multiple ETag - need to check each one */
2999
0
    coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
3000
0
    while ((etag_opt = coap_option_next(&opt_iter))) {
3001
0
      if (opt_iter.number == COAP_OPTION_ETAG) {
3002
0
        uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt),
3003
0
                                               coap_opt_length(etag_opt));
3004
0
        if (etag == lg_xmit->b.b2.etag) {
3005
0
          break;
3006
0
        }
3007
0
      }
3008
0
    }
3009
0
    if (!etag_opt) {
3010
      /* Not a match - pass up to a higher level */
3011
0
      return 0;
3012
0
    }
3013
0
  }
3014
0
  out_pdu->code = lg_xmit->sent_pdu->code;
3015
0
  coap_ticks(&lg_xmit->last_obs);
3016
3017
0
  chunk = (size_t)1 << (lg_xmit->blk_size + 4);
3018
0
  if (block_opt) {
3019
0
    if (block.bert) {
3020
0
      coap_log_debug("found Block option, block is BERT, block nr. %u, M %d\n",
3021
0
                     block.num, block.m);
3022
0
    } else {
3023
0
      coap_log_debug("found Block option, block size is %u, block nr. %u, M %d\n",
3024
0
                     1 << (block.szx + 4), block.num, block.m);
3025
0
    }
3026
0
    if (block.bert == 0 && block.szx != lg_xmit->blk_size) {
3027
0
      if (block.num == 0) {
3028
0
        if (block.szx < lg_xmit->blk_size) {
3029
          /*
3030
           * Recompute the block number of the previous packet given
3031
           * the new block size
3032
           */
3033
0
          block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1);
3034
0
          chunk = (size_t)1 << (lg_xmit->blk_size + 4);
3035
0
#if COAP_Q_BLOCK_SUPPORT
3036
0
          lg_xmit->send_blocks.range[0].begin = block.num;
3037
0
          lg_xmit->send_blocks.range[0].end = (uint32_t)((lg_xmit->data_info->length - 1) / chunk);
3038
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3039
0
          lg_xmit->blk_size = block.szx;
3040
0
          coap_log_debug("new Block size is %u, block number %u completed\n",
3041
0
                         (1 << (block.szx + 4)), block.num);
3042
0
        } else {
3043
0
          coap_log_debug("ignoring request to increase Block size from %u to %u\n",
3044
0
                         (1 << (lg_xmit->blk_size + 4)), (1 << (lg_xmit->blk_size + 4)));
3045
0
        }
3046
0
      } else {
3047
0
        coap_log_debug("ignoring request to change Block size from %u to %u\n",
3048
0
                       (1 << (lg_xmit->blk_size + 4)), (1 << (block.szx + 4)));
3049
0
        block.szx = block.aszx = lg_xmit->blk_size;
3050
0
      }
3051
0
    }
3052
0
  }
3053
3054
  /*
3055
   * Need to check if there are multiple Q-Block2 requests.  If so, they
3056
   * need to be sent out in order of requests with the final request being
3057
   * handled as per singular Block 2 request.
3058
   */
3059
0
  request_cnt = 0;
3060
0
#if COAP_Q_BLOCK_SUPPORT
3061
0
  max_block = (lg_xmit->data_info->length + chunk - 1)/chunk;
3062
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3063
0
  coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL);
3064
0
  while ((option = coap_option_next(&opt_b_iter))) {
3065
0
    uint32_t num;
3066
0
    if (opt_b_iter.number != lg_xmit->option)
3067
0
      continue;
3068
0
    num = coap_opt_block_num(option);
3069
0
    if (num > 0xFFFFF) /* 20 bits max for num */
3070
0
      continue;
3071
0
    if (block.aszx != COAP_OPT_BLOCK_SZX(option)) {
3072
0
      coap_add_data(response,
3073
0
                    sizeof("Changing blocksize during request invalid")-1,
3074
0
                    (const uint8_t *)"Changing blocksize during request invalid");
3075
0
      response->code = COAP_RESPONSE_CODE(400);
3076
0
      goto skip_app_handler;
3077
0
    }
3078
0
#if COAP_Q_BLOCK_SUPPORT
3079
0
    if (COAP_OPT_BLOCK_MORE(option) && lg_xmit->option == COAP_OPTION_Q_BLOCK2) {
3080
0
      if ((num % COAP_MAX_PAYLOADS(session)) == 0) {
3081
0
        if (num == 0) {
3082
          /* This is a repeat request for everything - hmm */
3083
0
          goto call_app_handler;
3084
0
        }
3085
        /* 'Continue' request */
3086
0
        for (i = 0; i < COAP_MAX_PAYLOADS(session) &&
3087
0
             num + i < max_block; i++) {
3088
0
          add_block_send(num + i, 1, out_blocks, &request_cnt,
3089
0
                         COAP_MAX_PAYLOADS(session));
3090
0
          lg_xmit->last_block = num + i;
3091
0
        }
3092
0
      } else {
3093
        /* Requesting remaining payloads in this MAX_PAYLOADS */
3094
0
        for (i = 0; i < COAP_MAX_PAYLOADS(session) -
3095
0
             num % COAP_MAX_PAYLOADS(session) &&
3096
0
             num + i < max_block; i++) {
3097
0
          add_block_send(num + i, 0, out_blocks, &request_cnt,
3098
0
                         COAP_MAX_PAYLOADS(session));
3099
0
        }
3100
0
      }
3101
0
    } else
3102
0
      add_block_send(num, 0, out_blocks, &request_cnt,
3103
0
                     COAP_MAX_PAYLOADS(session));
3104
#else /* ! COAP_Q_BLOCK_SUPPORT */
3105
    add_block_send(num, 0, out_blocks, &request_cnt, 1);
3106
    break;
3107
#endif /* ! COAP_Q_BLOCK_SUPPORT */
3108
0
  }
3109
0
  if (request_cnt == 0) {
3110
    /* Block2 or Q-Block2 not found - give them the first block */
3111
0
    block.szx = lg_xmit->blk_size;
3112
0
    out_blocks[0].num = 0;
3113
0
    out_blocks[0].is_continue = 0;
3114
0
    request_cnt = 1;
3115
0
  }
3116
3117
0
  for (i = 0; i < request_cnt; i++) {
3118
0
    uint8_t buf[8];
3119
0
    size_t offset;
3120
3121
0
    block.num = out_blocks[i].num;
3122
3123
0
    if (i + 1 < request_cnt) {
3124
      /* Need to set up a copy of the pdu to send */
3125
0
      coap_opt_filter_t drop_options;
3126
3127
0
      memset(&drop_options, 0, sizeof(coap_opt_filter_t));
3128
0
      if (block.num != 0)
3129
0
        coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
3130
0
      if (out_blocks[i].is_continue) {
3131
0
        out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
3132
0
                                         lg_xmit->sent_pdu->actual_token.length,
3133
0
                                         lg_xmit->sent_pdu->actual_token.s,
3134
0
                                         &drop_options, COAP_BOOL_FALSE);
3135
0
      } else {
3136
0
        out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
3137
0
                                         pdu->actual_token.length,
3138
0
                                         pdu->actual_token.s,
3139
0
                                         &drop_options, COAP_BOOL_FALSE);
3140
0
      }
3141
0
      if (!out_pdu) {
3142
0
        goto internal_issue;
3143
0
      }
3144
0
    } else {
3145
0
      if (out_blocks[i].is_continue)
3146
0
        coap_update_token(response, lg_xmit->sent_pdu->actual_token.length,
3147
0
                          lg_xmit->sent_pdu->actual_token.s);
3148
      /*
3149
       * Copy the options across and then fix the block option
3150
       *
3151
       * Need to drop Observe option if Block2 and block.num != 0
3152
       */
3153
0
      coap_option_iterator_init(lg_xmit->sent_pdu, &opt_iter, COAP_OPT_ALL);
3154
0
      while ((option = coap_option_next(&opt_iter))) {
3155
0
        if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0)
3156
0
          continue;
3157
0
        if (!coap_insert_option(response, opt_iter.number,
3158
0
                                coap_opt_length(option),
3159
0
                                coap_opt_value(option))) {
3160
0
          goto internal_issue;
3161
0
        }
3162
0
      }
3163
0
      out_pdu = response;
3164
0
    }
3165
0
    if (pdu->type == COAP_MESSAGE_NON)
3166
0
      out_pdu->type = COAP_MESSAGE_NON;
3167
0
    offset = block.num * chunk;
3168
0
    if (block.bert) {
3169
0
      size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size;
3170
0
      block.m = (lg_xmit->data_info->length - offset) >
3171
0
                ((out_pdu->max_size - token_options) /1024) * 1024;
3172
0
    } else {
3173
0
      block.m = (offset + chunk) < lg_xmit->data_info->length;
3174
0
    }
3175
0
    if (!coap_update_option(out_pdu, lg_xmit->option,
3176
0
                            coap_encode_var_safe(buf,
3177
0
                                                 sizeof(buf),
3178
0
                                                 (block.num << 4) |
3179
0
                                                 (block.m << 3) |
3180
0
                                                 block.aszx),
3181
0
                            buf)) {
3182
0
      goto internal_issue;
3183
0
    }
3184
0
    if (!(offset + chunk < lg_xmit->data_info->length)) {
3185
      /* Last block - keep in cache for 4 * ACK_TIMOUT */
3186
0
      coap_ticks(&lg_xmit->last_all_sent);
3187
0
    }
3188
0
    if (lg_xmit->b.b2.maxage_expire) {
3189
0
      coap_tick_t now;
3190
0
      coap_time_t rem;
3191
3192
0
      if (!(offset + chunk < lg_xmit->data_info->length)) {
3193
        /* Last block - keep in cache for 4 * ACK_TIMOUT */
3194
0
        coap_ticks(&lg_xmit->last_all_sent);
3195
0
      }
3196
0
      coap_ticks(&now);
3197
0
      rem = coap_ticks_to_rt(now);
3198
0
      if (lg_xmit->b.b2.maxage_expire > rem) {
3199
0
        rem = lg_xmit->b.b2.maxage_expire - rem;
3200
0
      } else {
3201
0
        rem = 0;
3202
        /* Entry needs to be expired */
3203
0
        coap_ticks(&lg_xmit->last_all_sent);
3204
0
      }
3205
0
      if (!coap_update_option(out_pdu, COAP_OPTION_MAXAGE,
3206
0
                              coap_encode_var_safe8(buf,
3207
0
                                                    sizeof(buf),
3208
0
                                                    rem),
3209
0
                              buf)) {
3210
0
        goto internal_issue;
3211
0
      }
3212
0
    }
3213
3214
0
    if (!coap_add_block_b_data(out_pdu,
3215
0
                               lg_xmit->data_info->length,
3216
0
                               lg_xmit->data_info->data,
3217
0
                               &block)) {
3218
0
      goto internal_issue;
3219
0
    }
3220
0
    if (i + 1 < request_cnt) {
3221
0
      coap_ticks(&lg_xmit->last_sent);
3222
0
      coap_send_internal(session, out_pdu, NULL);
3223
0
    }
3224
0
  }
3225
0
  coap_ticks(&lg_xmit->last_payload);
3226
0
  coap_ticks(&lg_xmit->last_sent);
3227
0
  if (lg_xmit->last_all_sent) {
3228
0
    coap_ticks(&lg_xmit->last_all_sent);
3229
0
  }
3230
0
  goto skip_app_handler;
3231
0
#if COAP_Q_BLOCK_SUPPORT
3232
0
call_app_handler:
3233
0
  coap_free_type(COAP_STRING, out_blocks);
3234
0
  return 0;
3235
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3236
3237
0
internal_issue:
3238
0
  response->code = COAP_RESPONSE_CODE(500);
3239
0
  error_phrase = coap_response_phrase(response->code);
3240
0
  coap_add_data(response, strlen(error_phrase),
3241
0
                (const uint8_t *)error_phrase);
3242
  /* Keep in cache for 4 * ACK_TIMOUT in case of retry */
3243
0
  if (lg_xmit)
3244
0
    coap_ticks(&lg_xmit->last_all_sent);
3245
3246
0
skip_app_handler:
3247
0
  coap_free_type(COAP_STRING, out_blocks);
3248
0
  return 1;
3249
0
}
3250
#endif /* COAP_SERVER_SUPPORT */
3251
3252
#if COAP_Q_BLOCK_SUPPORT
3253
static int
3254
0
blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num) {
3255
0
  uint32_t i;
3256
3257
0
  if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) {
3258
    /* received block number greater than Block No defined when More bit unset */
3259
0
    return 0;
3260
0
  }
3261
3262
0
  for (i = 0; i < rec_blocks->used; i++) {
3263
0
    if (block_num >= rec_blocks->range[i].begin &&
3264
0
        block_num <= rec_blocks->range[i].end) {
3265
      /* In this block */
3266
0
      if (block_num == rec_blocks->range[i].begin) {
3267
0
        if (block_num == rec_blocks->range[i].end) {
3268
          /* Need to delete this range */
3269
0
          if (i + 1 < rec_blocks->used) {
3270
0
            memmove(&rec_blocks->range[i], &rec_blocks->range[i+1],
3271
0
                    (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3272
0
          }
3273
0
          rec_blocks->used--;
3274
0
          break;
3275
0
        }
3276
0
        rec_blocks->range[i].begin++;
3277
0
      } else if (block_num == rec_blocks->range[i].end) {
3278
0
        rec_blocks->range[i].end--;
3279
0
        if (rec_blocks->range[i].begin == rec_blocks->range[i].end) {
3280
          /* Need to delete this range */
3281
0
          rec_blocks->used--;
3282
0
          if (i == rec_blocks->used)
3283
0
            break;
3284
0
          memmove(&rec_blocks->range[i], &rec_blocks->range[i+1],
3285
0
                  sizeof(rec_blocks->range[i]) * (rec_blocks->used - i));
3286
0
        }
3287
0
      } else {
3288
        /* Need to split the range */
3289
0
        if (rec_blocks->used == COAP_RBLOCK_CNT)
3290
          /* Too many losses */
3291
0
          return 0;
3292
0
        memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
3293
0
                (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3294
0
        rec_blocks->range[i].end = block_num - 1;
3295
0
        rec_blocks->range[i+1].begin = block_num + 1;
3296
0
        rec_blocks->used++;
3297
0
      }
3298
0
      break;
3299
0
    }
3300
0
  }
3301
0
  coap_ticks(&rec_blocks->last_seen);
3302
0
  return 1;
3303
0
}
3304
#endif /* COAP_Q_BLOCK_SUPPORT */
3305
3306
static int
3307
0
blocks_add_entry(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m) {
3308
0
  uint32_t i;
3309
3310
0
  if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) {
3311
    /* received block number greater than Block No defined when More bit unset */
3312
0
    return 0;
3313
0
  }
3314
3315
  /* Reset as there is activity */
3316
0
  rec_blocks->retry = 0;
3317
3318
0
  for (i = 0; i < rec_blocks->used; i++) {
3319
0
    if (block_num >= rec_blocks->range[i].begin &&
3320
0
        block_num <= rec_blocks->range[i].end)
3321
0
      break;
3322
3323
0
    if (block_num < rec_blocks->range[i].begin) {
3324
0
      if (block_num + 1 == rec_blocks->range[i].begin) {
3325
0
        rec_blocks->range[i].begin = block_num;
3326
0
      } else {
3327
        /* Need to insert a new range */
3328
0
        if (rec_blocks->used == COAP_RBLOCK_CNT)
3329
          /* Too many losses */
3330
0
          return 0;
3331
0
        memmove(&rec_blocks->range[i+1], &rec_blocks->range[i],
3332
0
                (rec_blocks->used - i) * sizeof(rec_blocks->range[0]));
3333
0
        rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
3334
0
        rec_blocks->used++;
3335
0
      }
3336
0
      break;
3337
0
    }
3338
0
    if (block_num == rec_blocks->range[i].end + 1) {
3339
0
      rec_blocks->range[i].end = block_num;
3340
0
      if (i + 1 < rec_blocks->used) {
3341
0
        if (rec_blocks->range[i+1].begin == block_num + 1) {
3342
          /* Merge the 2 ranges */
3343
0
          rec_blocks->range[i].end = rec_blocks->range[i+1].end;
3344
0
          if (i+2 < rec_blocks->used) {
3345
0
            memmove(&rec_blocks->range[i+1], &rec_blocks->range[i+2],
3346
0
                    (rec_blocks->used - (i+2)) * sizeof(rec_blocks->range[0]));
3347
0
          }
3348
0
          rec_blocks->used--;
3349
0
        }
3350
0
      }
3351
0
      break;
3352
0
    }
3353
0
  }
3354
0
  if (i == rec_blocks->used) {
3355
0
    if (rec_blocks->used == COAP_RBLOCK_CNT) {
3356
      /* Too many losses */
3357
0
      return 0;
3358
0
    }
3359
0
    rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num;
3360
0
    rec_blocks->used++;
3361
0
  }
3362
0
  if (!block_m)
3363
0
    rec_blocks->total_blocks = block_num + 1;
3364
3365
0
  coap_ticks(&rec_blocks->last_seen);
3366
0
  return 1;
3367
0
}
3368
3369
#if COAP_SERVER_SUPPORT
3370
/*
3371
 * Need to check if this is a large PUT / POST etc. using multiple blocks
3372
 *
3373
 * Server receiving PUT/POST etc. of a large amount of data (Block1)
3374
 *
3375
 * Return: 0 Call application handler
3376
 *         1 Do not call application handler - just send the built response
3377
 */
3378
int
3379
coap_handle_request_put_block(coap_context_t *context,
3380
                              coap_session_t *session,
3381
                              coap_pdu_t *pdu,
3382
                              coap_pdu_t *response,
3383
                              coap_resource_t *resource,
3384
                              coap_string_t *uri_path,
3385
                              coap_opt_t *observe,
3386
                              int *added_block,
3387
0
                              coap_lg_srcv_t **pfree_lg_srcv) {
3388
0
  size_t length = 0;
3389
0
  const uint8_t *data = NULL;
3390
0
  size_t offset = 0;
3391
0
  size_t total = 0;
3392
0
  coap_block_b_t block;
3393
0
  coap_opt_iterator_t opt_iter;
3394
0
  uint16_t block_option = 0;
3395
0
  coap_lg_srcv_t *lg_srcv;
3396
0
  coap_opt_t *size_opt;
3397
0
  coap_opt_t *fmt_opt;
3398
0
  uint16_t fmt;
3399
0
  coap_opt_t *rtag_opt;
3400
0
  size_t rtag_length;
3401
0
  const uint8_t *rtag;
3402
0
  uint32_t max_block_szx;
3403
0
  int update_data;
3404
0
  unsigned int saved_num;
3405
0
  size_t saved_offset;
3406
0
  int lg_srcv_is_refed = 0;
3407
0
#if COAP_Q_BLOCK_SUPPORT
3408
0
  int request_missing = 0;
3409
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3410
3411
0
  *added_block = 0;
3412
0
  *pfree_lg_srcv = NULL;
3413
0
  coap_get_data_large(pdu, &length, &data, &offset, &total);
3414
0
  pdu->body_offset = 0;
3415
0
  pdu->body_total = length;
3416
3417
0
  if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) {
3418
0
    block_option = COAP_OPTION_BLOCK1;
3419
0
#if COAP_Q_BLOCK_SUPPORT
3420
0
    if (coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter)) {
3421
      /* Cannot handle Q-Block1 as well */
3422
0
      coap_add_data(response, sizeof("Block1 + Q-Block1 together")-1,
3423
0
                    (const uint8_t *)"Block1 + Q-Block1 together");
3424
0
      response->code = COAP_RESPONSE_CODE(402);
3425
0
      goto skip_app_handler;
3426
0
    }
3427
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3428
0
  }
3429
0
#if COAP_Q_BLOCK_SUPPORT
3430
0
  else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) {
3431
0
    block_option = COAP_OPTION_Q_BLOCK1;
3432
0
    set_block_mode_has_q(session->block_mode);
3433
0
  }
3434
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3435
0
  if (!block_option ||
3436
0
      (block_option == COAP_OPTION_BLOCK1 && block.num == 0 && block.m == 0)) {
3437
    /* Not blocked, or a single block */
3438
0
    if (context->max_body_size && total > context->max_body_size) {
3439
0
      uint8_t buf[4];
3440
3441
0
      coap_update_option(response,
3442
0
                         COAP_OPTION_SIZE1,
3443
0
                         coap_encode_var_safe((uint8_t *)buf, sizeof(buf),
3444
0
                                              context->max_body_size),
3445
0
                         (uint8_t *)buf);
3446
0
      response->code = COAP_RESPONSE_CODE(413);
3447
0
      coap_log_warn("Unable to handle data size %" PRIuS " (max %" PRIu32 ")\n", total,
3448
0
                    context->max_body_size);
3449
0
      goto skip_app_handler;
3450
0
    }
3451
0
    goto call_app_handler;
3452
0
  }
3453
3454
0
  size_opt = coap_check_option(pdu,
3455
0
                               COAP_OPTION_SIZE1,
3456
0
                               &opt_iter);
3457
0
  fmt_opt = coap_check_option(pdu,
3458
0
                              COAP_OPTION_CONTENT_FORMAT,
3459
0
                              &opt_iter);
3460
0
  fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt),
3461
0
                                        coap_opt_length(fmt_opt)) :
3462
0
        COAP_MEDIATYPE_TEXT_PLAIN;
3463
0
  rtag_opt = coap_check_option(pdu,
3464
0
                               COAP_OPTION_RTAG,
3465
0
                               &opt_iter);
3466
0
#if COAP_Q_BLOCK_SUPPORT
3467
0
  if (block_option == COAP_OPTION_Q_BLOCK1 && (!size_opt || !rtag_opt)) {
3468
    /* RFC9177 section-4.3 */
3469
0
    coap_log_info("Q-Block1: Size1 and RTag options required\n");
3470
0
    response->code = COAP_RESPONSE_CODE(400);
3471
0
    goto skip_app_handler;
3472
0
  }
3473
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3474
0
  rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0;
3475
0
  rtag_length = min(rtag_length, 8);
3476
0
  rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL;
3477
3478
0
  if (length > block.chunk_size) {
3479
0
    coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %" PRIuS "\n",
3480
0
                   block.chunk_size, length);
3481
0
    length = block.chunk_size;
3482
0
  } else if (!block.bert && block.m && length != block.chunk_size) {
3483
0
    coap_log_info("block: Undersized packet chunk %"PRIu32" got %" PRIuS "\n",
3484
0
                  block.chunk_size, length);
3485
0
    response->code = COAP_RESPONSE_CODE(400);
3486
0
    goto skip_app_handler;
3487
0
  }
3488
0
  total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt),
3489
0
                                           coap_opt_length(size_opt)) : 0;
3490
0
  if (total) {
3491
0
    uint32_t max_body;
3492
3493
0
    max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3494
0
    if (max_block_szx == 0 || max_block_szx > block.szx) {
3495
0
      max_block_szx =  block.szx;
3496
0
    }
3497
0
    max_body = ((1UL << 20) * (1 << (max_block_szx + 4)));
3498
0
    if (max_body > MAX_BLK_LEN)
3499
0
      max_body = MAX_BLK_LEN;
3500
0
    if ((context->max_body_size && total > context->max_body_size) ||
3501
0
        (total > max_body)) {
3502
      /* Suggested body size larger than allowed */
3503
0
      char buf[32];
3504
0
      uint32_t max_body_size = context->max_body_size;
3505
3506
0
      if (max_body_size == 0 || max_body < max_body_size) {
3507
0
        max_body_size = max_body;
3508
0
      }
3509
0
      coap_update_option(response,
3510
0
                         COAP_OPTION_SIZE1,
3511
0
                         coap_encode_var_safe((uint8_t *)buf, sizeof(buf),
3512
0
                                              max_body_size),
3513
0
                         (uint8_t *)buf);
3514
0
      snprintf(buf, sizeof(buf), "Max body size %" PRIu32, max_body_size);
3515
0
      coap_add_data(response, strlen(buf), (uint8_t *)buf);
3516
0
      response->code = COAP_RESPONSE_CODE(413);
3517
0
      coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", total, max_body_size);
3518
0
      goto skip_app_handler;
3519
0
    }
3520
0
  }
3521
0
  offset = block.num << (block.szx + 4);
3522
3523
0
  if (!(session->block_mode &
3524
0
#if COAP_Q_BLOCK_SUPPORT
3525
0
        (COAP_BLOCK_SINGLE_BODY|COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_NOT_RANDOM_BLOCK1))
3526
#else /* COAP_Q_BLOCK_SUPPORT */
3527
        (COAP_BLOCK_SINGLE_BODY|COAP_BLOCK_NOT_RANDOM_BLOCK1))
3528
#endif /* COAP_Q_BLOCK_SUPPORT */
3529
0
      && !block.bert) {
3530
0
    uint8_t buf[4];
3531
3532
    /* Ask for the next block */
3533
0
    coap_insert_option(response, block_option,
3534
0
                       coap_encode_var_safe(buf, sizeof(buf),
3535
0
                                            (block.num << 4) |
3536
0
                                            (block.m << 3) |
3537
0
                                            block.aszx),
3538
0
                       buf);
3539
    /* Not re-assembling or checking for receipt order */
3540
0
    pdu->body_data = data;
3541
0
    pdu->body_length = length;
3542
0
    pdu->body_offset = offset;
3543
0
    if (total < (length + offset + (block.m ? 1 : 0)))
3544
0
      total = length + offset + (block.m ? 1 : 0);
3545
0
    pdu->body_total = total;
3546
0
    *added_block = block.m;
3547
    /* The application is responsible for returning the correct 2.01/2.04/2.31 etc. */
3548
0
    goto call_app_handler;
3549
0
  }
3550
3551
  /*
3552
   * locate the lg_srcv
3553
   */
3554
0
  LL_FOREACH(session->lg_srcv, lg_srcv) {
3555
0
    if (rtag_opt || lg_srcv->rtag_set == 1) {
3556
0
      if (!(rtag_opt && lg_srcv->rtag_set == 1))
3557
0
        continue;
3558
0
      if (lg_srcv->rtag_length != rtag_length ||
3559
0
          memcmp(lg_srcv->rtag, rtag, rtag_length) != 0)
3560
0
        continue;
3561
0
    }
3562
0
    if (resource == lg_srcv->resource) {
3563
0
      break;
3564
0
    }
3565
0
    if ((lg_srcv->resource == context->unknown_resource ||
3566
0
         resource == context->proxy_uri_resource) &&
3567
0
        coap_string_equal(uri_path, lg_srcv->uri_path))
3568
0
      break;
3569
0
  }
3570
3571
0
  if (!lg_srcv && block.num != 0 && session->block_mode & COAP_BLOCK_NOT_RANDOM_BLOCK1) {
3572
0
    coap_add_data(response, sizeof("Missing block 0")-1,
3573
0
                  (const uint8_t *)"Missing block 0");
3574
0
    response->code = COAP_RESPONSE_CODE(408);
3575
0
    goto skip_app_handler;
3576
0
  }
3577
3578
0
  if (!lg_srcv) {
3579
    /* Allocate lg_srcv to use for tracking */
3580
0
    lg_srcv = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t));
3581
0
    if (lg_srcv == NULL) {
3582
0
      coap_add_data(response, sizeof("Memory issue")-1,
3583
0
                    (const uint8_t *)"Memory issue");
3584
0
      response->code = COAP_RESPONSE_CODE(500);
3585
0
      goto skip_app_handler;
3586
0
    }
3587
0
    coap_log_debug("** %s: lg_srcv %p initialized\n",
3588
0
                   coap_session_str(session), (void *)lg_srcv);
3589
0
    memset(lg_srcv, 0, sizeof(coap_lg_srcv_t));
3590
0
    lg_srcv->resource = resource;
3591
0
    if (resource == context->unknown_resource ||
3592
0
        resource == context->proxy_uri_resource)
3593
0
      lg_srcv->uri_path = coap_new_str_const(uri_path->s, uri_path->length);
3594
0
    lg_srcv->content_format = fmt;
3595
0
    lg_srcv->total_len = total;
3596
0
    max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3597
0
    if (!block.bert && block.num == 0 && max_block_szx != 0 &&
3598
0
        max_block_szx < block.szx) {
3599
0
      lg_srcv->szx = max_block_szx;
3600
0
    } else {
3601
0
      lg_srcv->szx = block.szx;
3602
0
    }
3603
0
    lg_srcv->block_option = block_option;
3604
0
    if (observe) {
3605
0
      lg_srcv->observe_length = min(coap_opt_length(observe), 3);
3606
0
      memcpy(lg_srcv->observe, coap_opt_value(observe), lg_srcv->observe_length);
3607
0
      lg_srcv->observe_set = 1;
3608
0
    }
3609
0
    if (rtag_opt) {
3610
0
      lg_srcv->rtag_length = (uint8_t)rtag_length;
3611
0
      memcpy(lg_srcv->rtag, coap_opt_value(rtag_opt), lg_srcv->rtag_length);
3612
0
      lg_srcv->rtag_set = 1;
3613
0
    }
3614
0
    lg_srcv->body_data = NULL;
3615
0
#if COAP_Q_BLOCK_SUPPORT
3616
0
    lg_srcv->r_m_payload_set = -1;
3617
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3618
0
    LL_PREPEND(session->lg_srcv, lg_srcv);
3619
0
  }
3620
0
  coap_ticks(&lg_srcv->last_used);
3621
0
  coap_lg_srcv_reference_lkd(lg_srcv);
3622
0
  lg_srcv_is_refed = 1;
3623
3624
0
  if (block_option == COAP_OPTION_BLOCK1 &&
3625
0
      session->block_mode & COAP_BLOCK_NOT_RANDOM_BLOCK1 &&
3626
0
      !check_if_next_block(&lg_srcv->rec_blocks, block.num)) {
3627
0
    coap_add_data(response, sizeof("Missing interim block")-1,
3628
0
                  (const uint8_t *)"Missing interim block");
3629
0
    response->code = COAP_RESPONSE_CODE(408);
3630
0
    goto skip_app_handler;
3631
0
  }
3632
3633
0
  if (fmt != lg_srcv->content_format) {
3634
0
    coap_add_data(response, sizeof("Content-Format mismatch")-1,
3635
0
                  (const uint8_t *)"Content-Format mismatch");
3636
0
    response->code = COAP_RESPONSE_CODE(408);
3637
0
    goto free_lg_srcv;
3638
0
  }
3639
3640
0
#if COAP_Q_BLOCK_SUPPORT
3641
0
  if (block_option == COAP_OPTION_Q_BLOCK1) {
3642
0
    if (total != lg_srcv->total_len) {
3643
0
      coap_add_data(response, sizeof("Size1 mismatch")-1,
3644
0
                    (const uint8_t *)"Size1 mismatch");
3645
0
      response->code = COAP_RESPONSE_CODE(408);
3646
0
      goto free_lg_srcv;
3647
0
    }
3648
0
    coap_delete_bin_const(lg_srcv->last_token);
3649
0
    lg_srcv->last_token = coap_new_bin_const(pdu->actual_token.s,
3650
0
                                             pdu->actual_token.length);
3651
0
  }
3652
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3653
3654
0
  lg_srcv->last_type = pdu->type;
3655
3656
0
  update_data = 0;
3657
0
  saved_num = block.num;
3658
0
  saved_offset = offset;
3659
3660
0
  while (offset < saved_offset + length) {
3661
0
    uint32_t block_m = block.m;
3662
3663
    /*
3664
     * A BERT payload contains multiple logical 1024-byte blocks, but its
3665
     * Block1 M bit describes the complete BERT payload.  Mark intermediate
3666
     * logical blocks as having more data so total_blocks is only set by the
3667
     * final logical block.
3668
     */
3669
0
    if (block.bert && offset + 1024 < saved_offset + length)
3670
0
      block_m = 1;
3671
0
    if (!check_if_received_block(&lg_srcv->rec_blocks, block.num)) {
3672
      /* Update list of blocks received */
3673
0
      if (blocks_add_entry(&lg_srcv->rec_blocks, block.num, block_m)) {
3674
0
        update_data = 1;
3675
0
      } else {
3676
0
        coap_ticks(&lg_srcv->rec_blocks.last_seen);
3677
0
        coap_log_debug("Block nr %u ignored (too many missing blocks)\n", block.num);
3678
0
#if COAP_Q_BLOCK_SUPPORT
3679
0
        if (block_option == COAP_OPTION_Q_BLOCK1)
3680
0
          request_missing = 1;
3681
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3682
0
      }
3683
0
    } else {
3684
0
      coap_ticks(&lg_srcv->rec_blocks.last_seen);
3685
0
      coap_log_debug("Duplicate block nr %u\n", block.num);
3686
0
    }
3687
0
    block.num++;
3688
0
    offset = block.num << (block.szx + 4);
3689
0
  }
3690
0
  if (length)
3691
0
    block.num--;
3692
3693
0
#if COAP_Q_BLOCK_SUPPORT
3694
0
  if (request_missing) {
3695
0
    coap_request_missing_q_block1(session, lg_srcv, block.num);
3696
0
    goto skip_app_handler;
3697
0
  }
3698
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3699
3700
0
  if (update_data) {
3701
    /* Update saved data */
3702
0
#if COAP_Q_BLOCK_SUPPORT
3703
0
    lg_srcv->rec_blocks.processing_payload_set =
3704
0
        block.num / COAP_MAX_PAYLOADS(session);
3705
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3706
0
    if (lg_srcv->total_len < saved_offset + length) {
3707
0
      lg_srcv->total_len = saved_offset + length;
3708
0
    }
3709
3710
0
#define USE_BLOCK_DATA_HANDLER (context && context->block_data_cb && \
3711
0
                                !resource->is_proxy_uri && \
3712
0
                                !resource->is_reverse_proxy && \
3713
0
                                ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) && \
3714
0
                                (resource->flags & COAP_RESOURCE_USE_BLOCK_DATA_HANDLER))
3715
3716
0
    if (USE_BLOCK_DATA_HANDLER) {
3717
0
      coap_response_t resp;
3718
3719
0
      coap_lock_callback_ret(resp,
3720
0
                             context->block_data_cb(session, pdu, resource,
3721
0
                                                    &lg_srcv->body_data,
3722
0
                                                    length, data, saved_offset,
3723
0
                                                    lg_srcv->total_len));
3724
0
      if (resp != COAP_RESPONSE_OK) {
3725
0
        response->code = COAP_RESPONSE_CODE(500);
3726
0
        goto skip_app_handler;
3727
0
      }
3728
0
    } else {
3729
0
      lg_srcv->body_data = coap_block_build_body_lkd(lg_srcv->body_data, length, data,
3730
0
                                                     saved_offset, lg_srcv->total_len);
3731
0
      if (!lg_srcv->body_data) {
3732
0
        coap_add_data(response, sizeof("Memory issue")-1,
3733
0
                      (const uint8_t *)"Memory issue");
3734
0
        response->code = COAP_RESPONSE_CODE(500);
3735
0
        goto skip_app_handler;
3736
0
      }
3737
0
    }
3738
0
  } else {
3739
0
#if COAP_Q_BLOCK_SUPPORT
3740
0
    if (block_option == COAP_OPTION_Q_BLOCK1) {
3741
0
      goto q_block_1_check;
3742
0
    }
3743
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3744
    /*
3745
     * Every block carried by this request had already been received, so there
3746
     * is no new data and the application must not see the request twice
3747
     * (RFC7252 4.5).  The request still has to be answered: falling straight
3748
     * through to skip_app_handler leaves response->code at 0, which goes out
3749
     * as an Empty ACK.
3750
     * Re-issue the 2.31 Continue this block was given the first time.
3751
     */
3752
0
    if (block.m) {
3753
0
      uint8_t buf[4];
3754
3755
0
      coap_insert_option(response, block_option,
3756
0
                         coap_encode_var_safe(buf, sizeof(buf),
3757
0
                                              (saved_num << 4) |
3758
0
                                              (1 << 3) |
3759
0
                                              lg_srcv->szx),
3760
0
                         buf);
3761
0
      response->code = COAP_RESPONSE_CODE(231);
3762
0
    }
3763
0
    goto skip_app_handler;
3764
0
  }
3765
0
#if COAP_Q_BLOCK_SUPPORT
3766
0
q_block_1_check:
3767
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3768
3769
0
  if (block.m ||
3770
0
      !check_all_blocks_in(&lg_srcv->rec_blocks)) {
3771
    /* Not all the payloads of the body have arrived */
3772
0
    if (block.m) {
3773
0
      uint8_t buf[4];
3774
3775
0
#if COAP_Q_BLOCK_SUPPORT
3776
0
      if (block_option == COAP_OPTION_Q_BLOCK1) {
3777
0
        if (check_all_blocks_in(&lg_srcv->rec_blocks)) {
3778
0
          goto give_app_data;
3779
0
        }
3780
0
        if (lg_srcv->rec_blocks.used == 1 &&
3781
0
            (lg_srcv->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1
3782
0
            == COAP_MAX_PAYLOADS(session)) {
3783
          /* Seen all packets of this PAYLOAD_SET */
3784
0
          if (block.num != lg_srcv->rec_blocks.range[0].end) {
3785
            /* Blocks could arrive in wrong order but still need to send 2.31 */
3786
0
            saved_num = block.num = lg_srcv->rec_blocks.range[0].end;
3787
0
          }
3788
0
        } else if (lg_srcv->rec_blocks.used > 1 &&
3789
0
                   (block.num % COAP_MAX_PAYLOADS(session)) + 1 == COAP_MAX_PAYLOADS(session) &&
3790
0
                   lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used-1].begin <
3791
0
                   lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used-1].end &&
3792
0
                   (lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used-1].end % COAP_MAX_PAYLOADS(session)) + 1
3793
0
                   == COAP_MAX_PAYLOADS(session)) {
3794
          /*
3795
           * At least the last 2 of a payload set have been received, but some
3796
           * missing packets, but have seen the emd of a payload set.
3797
           */
3798
0
          coap_log_debug("Fast recovery for block %u\n", block.num);
3799
0
          coap_request_missing_q_block1(session, lg_srcv, block.num);
3800
0
          goto skip_app_handler;
3801
0
        } else if (lg_srcv->rec_blocks.used > 1 &&
3802
0
                   (block.num / COAP_MAX_PAYLOADS(session)) >
3803
0
                   (lg_srcv->rec_blocks.range[0].end / COAP_MAX_PAYLOADS(session))) {
3804
          /*
3805
           * Current MAX_PAYLOAD chunk is different to last MAX_PAYLOAD chunk
3806
           * with some missing packets in last MAX_PAYLOAD chunk.
3807
           * client has timed out and starting transmission of next PAYLOAD_SET.
3808
           */
3809
0
          coap_request_missing_q_block1(session, lg_srcv, block.num);
3810
0
          goto skip_app_handler;
3811
0
        } else {
3812
          /* The remote end will be sending the next one unless this
3813
             is a MAX_PAYLOADS and all previous have been received
3814
             as caught above */
3815
0
          goto skip_app_handler;
3816
0
        }
3817
0
        if (COAP_PROTO_RELIABLE(session->proto) ||
3818
0
            pdu->type != COAP_MESSAGE_NON)
3819
0
          goto skip_app_handler;
3820
0
      }
3821
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3822
3823
      /* Check to see if block size is getting forced down */
3824
0
      max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
3825
0
      if (!block.bert && saved_num == 0 && max_block_szx != 0 &&
3826
0
          max_block_szx < block.aszx) {
3827
0
        block.aszx = max_block_szx;
3828
0
      }
3829
3830
      /*
3831
       * If the last block has been seen, packets are coming in in
3832
       * random order.  If all blocks are now in, then need to send
3833
       * complete payload to application and acknowledge this current
3834
       * block.
3835
       */
3836
0
      if ((total == 0 && block.m) || !check_all_blocks_in(&lg_srcv->rec_blocks)) {
3837
        /* Ask for the next block */
3838
0
        coap_insert_option(response, block_option,
3839
0
                           coap_encode_var_safe(buf, sizeof(buf),
3840
0
                                                (saved_num << 4) |
3841
0
                                                (block.m << 3) |
3842
0
                                                block.aszx),
3843
0
                           buf);
3844
0
        response->code = COAP_RESPONSE_CODE(231);
3845
0
      } else {
3846
        /* Need to separately respond to this request */
3847
0
        coap_pdu_t *tmp_pdu = coap_pdu_duplicate_lkd(response,
3848
0
                                                     session,
3849
0
                                                     response->actual_token.length,
3850
0
                                                     response->actual_token.s,
3851
0
                                                     NULL, COAP_BOOL_FALSE);
3852
0
        if (tmp_pdu) {
3853
0
          tmp_pdu->code = COAP_RESPONSE_CODE(231);
3854
0
          if (coap_send_internal(session, tmp_pdu, NULL) == COAP_INVALID_MID) {
3855
            /* lg_srcv may have got deleted */
3856
0
            coap_lg_srcv_t *sg;
3857
3858
0
            LL_FOREACH(session->lg_srcv, sg) {
3859
0
              if (lg_srcv == sg) {
3860
                /* Still there */
3861
0
                break;
3862
0
              }
3863
0
            }
3864
0
            if (!sg)
3865
0
              goto skip_app_handler;
3866
0
          }
3867
0
        }
3868
0
        if (lg_srcv->last_token) {
3869
0
          coap_update_token(response, lg_srcv->last_token->length, lg_srcv->last_token->s);
3870
0
          coap_update_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s);
3871
0
        }
3872
        /* Pass the assembled pdu and body to the application */
3873
0
        goto give_app_data;
3874
0
      }
3875
0
    } else {
3876
      /* block.m Block More option not set.  Some outstanding blocks */
3877
0
#if COAP_Q_BLOCK_SUPPORT
3878
0
      if (block_option != COAP_OPTION_Q_BLOCK1) {
3879
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3880
        /* Last chunk - but not all in */
3881
0
        coap_ticks(&lg_srcv->last_used);
3882
0
        lg_srcv->no_more_seen = 1;
3883
0
        coap_delete_bin_const(lg_srcv->last_token);
3884
0
        lg_srcv->last_token = coap_new_bin_const(pdu->actual_token.s,
3885
0
                                                 pdu->actual_token.length);
3886
3887
        /*
3888
         * Need to just ACK (no response code) to handle client's NSTART.
3889
         * When final missing block comes in, we will pass all the data
3890
         * for processing so a 2.01, 2.04 etc. code can be generated
3891
         * and responded to as a separate response "RFC7252 5.2.2. Separate"
3892
         * If missing block(s) do not come in, then will generate a 4.08
3893
         * when lg_srcv times out.
3894
         * Fall through to skip_app_handler.
3895
         */
3896
0
#if COAP_Q_BLOCK_SUPPORT
3897
0
      } else {
3898
0
        coap_request_missing_q_block1(session, lg_srcv, block.num);
3899
0
      }
3900
0
#endif /* COAP_Q_BLOCK_SUPPORT */
3901
0
    }
3902
0
    goto skip_app_handler;
3903
0
  }
3904
3905
  /*
3906
   * Entire payload received.
3907
   * Remove the Block1 option as passing all of the data to
3908
   * application layer. Add back in observe option if appropriate.
3909
   * Adjust all other information.
3910
   */
3911
0
give_app_data:
3912
0
  if (lg_srcv->observe_set) {
3913
0
    coap_update_option(pdu, COAP_OPTION_OBSERVE,
3914
0
                       lg_srcv->observe_length, lg_srcv->observe);
3915
0
  }
3916
0
  coap_remove_option(pdu, block_option);
3917
0
  if (lg_srcv->body_data) {
3918
0
    pdu->body_data = lg_srcv->body_data->s;
3919
0
    pdu->body_length = lg_srcv->total_len;
3920
0
  } else {
3921
0
    pdu->body_data = NULL;
3922
0
    pdu->body_length = 0;
3923
0
  }
3924
0
  pdu->body_offset = 0;
3925
0
  pdu->body_total = lg_srcv->total_len;
3926
0
  if (USE_BLOCK_DATA_HANDLER) {
3927
    /* Data has already been provided - do not duplicate */
3928
0
    if (pdu->data) {
3929
0
      pdu->used_size = pdu->data - pdu->token - 1;
3930
0
      pdu->data = NULL;
3931
0
    }
3932
0
  }
3933
0
  coap_log_debug("Server app version of updated PDU\n");
3934
0
  coap_show_pdu(COAP_LOG_DEBUG, pdu);
3935
0
  lg_srcv->dont_timeout = 1;
3936
0
  *pfree_lg_srcv = lg_srcv;
3937
3938
0
call_app_handler:
3939
0
  if (lg_srcv_is_refed)
3940
0
    coap_lg_srcv_release_lkd(session, lg_srcv);
3941
0
  return 0;
3942
3943
0
free_lg_srcv:
3944
0
  LL_DELETE(session->lg_srcv, lg_srcv);
3945
0
  coap_block_delete_lg_srcv(session, lg_srcv);
3946
3947
0
skip_app_handler:
3948
0
  if (lg_srcv_is_refed)
3949
0
    coap_lg_srcv_release_lkd(session, lg_srcv);
3950
0
  return 1;
3951
0
}
3952
#endif /* COAP_SERVER_SUPPORT */
3953
3954
#if COAP_CLIENT_SUPPORT
3955
#if COAP_Q_BLOCK_SUPPORT
3956
static uint32_t
3957
0
derive_cbor_value(const uint8_t **bp, size_t rem_len) {
3958
0
  uint32_t value = **bp & 0x1f;
3959
0
  (*bp)++;
3960
0
  if (value < 24) {
3961
0
    return value;
3962
0
  } else if (value == 24) {
3963
0
    if (rem_len < 2)
3964
0
      return (uint32_t)-1;
3965
0
    value = **bp;
3966
0
    (*bp)++;
3967
0
    return value;
3968
0
  } else if (value == 25) {
3969
0
    if (rem_len < 3)
3970
0
      return (uint32_t)-1;
3971
0
    value = **bp << 8;
3972
0
    (*bp)++;
3973
0
    value |= **bp;
3974
0
    (*bp)++;
3975
0
    return value;
3976
0
  }
3977
0
  if (rem_len < 5)
3978
0
    return (uint32_t)-1;
3979
0
  value = (uint32_t)(**bp) << 24;
3980
0
  (*bp)++;
3981
0
  value |= **bp << 16;
3982
0
  (*bp)++;
3983
0
  value |= **bp << 8;
3984
0
  (*bp)++;
3985
0
  value |= **bp;
3986
0
  (*bp)++;
3987
0
  return value;
3988
0
}
3989
#endif /* COAP_Q_BLOCK_SUPPORT */
3990
3991
static int
3992
check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent,
3993
0
                coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) {
3994
  /* Check for Echo option for freshness */
3995
0
  coap_opt_iterator_t opt_iter;
3996
0
  coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
3997
3998
0
  if (opt) {
3999
0
    if (sent || lg_xmit || lg_crcv) {
4000
      /* Need to retransmit original request with Echo option added */
4001
0
      coap_pdu_t *echo_pdu;
4002
0
      coap_mid_t mid;
4003
0
      const uint8_t *data;
4004
0
      size_t data_len;
4005
0
      int have_data = 0;
4006
0
      uint8_t ltoken[8];
4007
0
      size_t ltoken_len;
4008
0
      uint64_t token;
4009
4010
0
      if (sent) {
4011
0
        if (coap_get_data(sent, &data_len, &data))
4012
0
          have_data = 1;
4013
0
      } else if (lg_xmit) {
4014
0
        sent = lg_xmit->sent_pdu;
4015
0
        if (lg_xmit->data_info->length) {
4016
0
          size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4);
4017
0
          size_t offset = (lg_xmit->last_block + 1) * blk_size;
4018
0
          have_data = 1;
4019
0
          data = &lg_xmit->data_info->data[offset];
4020
0
          data_len = (lg_xmit->data_info->length - offset) > blk_size ? blk_size :
4021
0
                     lg_xmit->data_info->length - offset;
4022
0
        }
4023
0
      } else { /* lg_crcv */
4024
0
        sent = lg_crcv->sent_pdu;
4025
0
        if (coap_get_data(sent, &data_len, &data))
4026
0
          have_data = 1;
4027
0
      }
4028
0
      if (lg_xmit) {
4029
0
        token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,
4030
0
                                 ++lg_xmit->b.b1.count);
4031
0
      } else {
4032
0
        token = STATE_TOKEN_FULL(lg_crcv->state_token,
4033
0
                                 ++lg_crcv->retry_counter);
4034
0
      }
4035
0
      ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
4036
0
      echo_pdu = coap_pdu_duplicate_lkd(sent, session, ltoken_len, ltoken,
4037
0
                                        NULL, COAP_BOOL_FALSE);
4038
0
      if (!echo_pdu)
4039
0
        return 0;
4040
0
      if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO,
4041
0
                              coap_opt_length(opt), coap_opt_value(opt)))
4042
0
        goto not_sent;
4043
0
      if (have_data) {
4044
0
        coap_add_data(echo_pdu, data_len, data);
4045
0
      }
4046
      /* Need to track Observe token change if Observe */
4047
0
      track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token);
4048
0
#if COAP_OSCORE_SUPPORT
4049
0
      if (session->oscore_encryption &&
4050
0
          (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) &&
4051
0
          coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt) == 0)) {
4052
        /* Need to update the base PDU's Token for closing down Observe */
4053
0
        if (lg_xmit) {
4054
0
          lg_xmit->b.b1.state_token = token;
4055
0
        } else {
4056
0
          lg_crcv->state_token = token;
4057
0
        }
4058
0
      }
4059
0
#endif /* COAP_OSCORE_SUPPORT */
4060
0
      mid = coap_send_internal(session, echo_pdu, NULL);
4061
0
      if (mid == COAP_INVALID_MID)
4062
0
        goto not_sent;
4063
0
      return 1;
4064
0
    } else {
4065
      /* Need to save Echo option value to add to next reansmission */
4066
0
not_sent:
4067
0
      coap_delete_bin_const(session->echo);
4068
0
      session->echo = coap_new_bin_const(coap_opt_value(opt),
4069
0
                                         coap_opt_length(opt));
4070
0
    }
4071
0
  }
4072
0
  return 0;
4073
0
}
4074
4075
static void
4076
0
track_echo(coap_session_t *session, coap_pdu_t *rcvd) {
4077
0
  coap_opt_iterator_t opt_iter;
4078
0
  coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter);
4079
4080
0
  if (opt) {
4081
0
    coap_delete_bin_const(session->echo);
4082
0
    session->echo = coap_new_bin_const(coap_opt_value(opt),
4083
0
                                       coap_opt_length(opt));
4084
0
  }
4085
0
}
4086
4087
/*
4088
 * Need to see if this is a response to a large body request transfer. If so,
4089
 * need to initiate the request containing the next block and not trouble the
4090
 * application.  Note that Token must unique per request/response.
4091
 *
4092
 * Client receives large data acknowledgement from server (Block1)
4093
 *
4094
 * This is set up using coap_add_data_large_request_lkd()
4095
 *
4096
 * Client is using GET etc.
4097
 *
4098
 * Return: 0 Call application handler
4099
 *         1 Do not call application handler - just send the built response
4100
 */
4101
int
4102
coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent,
4103
0
                                coap_pdu_t *rcvd) {
4104
0
  coap_lg_xmit_t *lg_xmit;
4105
0
  coap_lg_crcv_t *lg_crcv = NULL;
4106
0
  int lg_crcv_is_refed = 0;
4107
4108
0
  lg_xmit = coap_find_lg_xmit(session, rcvd);
4109
0
  if (lg_xmit) {
4110
    /* lg_xmit found */
4111
0
    size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4);
4112
0
    coap_block_b_t block;
4113
4114
0
    lg_crcv = coap_find_lg_crcv(session, rcvd);
4115
0
    if (lg_crcv) {
4116
0
      coap_ticks(&lg_crcv->last_used);
4117
0
      coap_lg_crcv_reference_lkd(lg_crcv);
4118
0
      lg_crcv_is_refed = 1;
4119
0
    }
4120
4121
0
    if (COAP_RESPONSE_CLASS(rcvd->code) == 2 &&
4122
0
        coap_get_block_b(session, rcvd, lg_xmit->option, &block)) {
4123
4124
0
      if (block.bert) {
4125
0
        coap_log_debug("found Block option, block is BERT, block nr. %u (%" PRIuS ")\n",
4126
0
                       block.num, lg_xmit->b.b1.bert_size);
4127
0
      } else {
4128
0
        coap_log_debug("found Block option, block size is %u, block nr. %u\n",
4129
0
                       1 << (block.szx + 4), block.num);
4130
0
      }
4131
0
      if (block.szx != lg_xmit->blk_size) {
4132
0
        if (block.szx > lg_xmit->blk_size) {
4133
0
          coap_log_info("ignoring request to increase Block size, "
4134
0
                        "(%u > %u)\n",
4135
0
                        1 << (block.szx + 4), 1 << (lg_xmit->blk_size + 4));
4136
0
        } else {
4137
          /*
4138
           * Recompute the block number of the previous packet given the
4139
           * new block size
4140
           */
4141
0
          block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1);
4142
0
          chunk = (size_t)1 << (lg_xmit->blk_size + 4);
4143
0
#if COAP_Q_BLOCK_SUPPORT
4144
0
          lg_xmit->send_blocks.range[0].begin = block.num;
4145
0
          lg_xmit->send_blocks.range[0].end = (uint32_t)((lg_xmit->data_info->length -1) / chunk);
4146
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4147
0
          lg_xmit->blk_size = block.szx;
4148
0
          coap_log_debug("new Block size is %u, block number %u completed\n",
4149
0
                         1 << (block.szx + 4), block.num);
4150
0
          block.bert = 0;
4151
0
          block.aszx = block.szx;
4152
0
        }
4153
0
      }
4154
0
      track_echo(session, rcvd);
4155
0
#if COAP_Q_BLOCK_SUPPORT
4156
0
      if (rcvd->code == COAP_RESPONSE_CODE(231) &&
4157
0
          lg_xmit->option == COAP_OPTION_Q_BLOCK1) {
4158
0
        coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU);
4159
0
        goto skip_app_handler;
4160
0
      }
4161
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4162
0
      if (lg_xmit->last_block == (int)block.num &&
4163
0
          lg_xmit->option != COAP_OPTION_Q_BLOCK1) {
4164
        /*
4165
         * Duplicate Block1 ACK
4166
         *
4167
         * RFCs not clear here, but on a lossy connection, there could
4168
         * be multiple Block1 ACKs, causing the client to retransmit the
4169
         * same block multiple times, or the server retransmitting the
4170
         * same ACK.
4171
         *
4172
         * Once a block has been ACKd, there is no need to retransmit it.
4173
         */
4174
0
        goto skip_app_handler;
4175
0
      }
4176
0
      if (block.bert)
4177
0
        block.num += (unsigned int)(lg_xmit->b.b1.bert_size / 1024 - 1);
4178
0
      lg_xmit->last_block = block.num;
4179
0
      if ((block.num + 1) * chunk < lg_xmit->data_info->length) {
4180
        /* Build the next PDU request based off the skeletal PDU */
4181
0
        uint8_t buf[8];
4182
0
        coap_pdu_t *pdu;
4183
0
        uint64_t token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, ++lg_xmit->b.b1.count);
4184
0
        size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
4185
0
        size_t offset;
4186
4187
0
        if (lg_xmit->sent_pdu->code == COAP_REQUEST_CODE_FETCH) {
4188
          /* Need to handle Observe for large FETCH */
4189
0
          if (lg_crcv) {
4190
0
            if (coap_binary_equal(lg_xmit->b.b1.app_token, lg_crcv->app_token)) {
4191
0
              coap_bin_const_t *new_token;
4192
0
              coap_bin_const_t ctoken = { len, buf };
4193
4194
              /* Need to save/restore Observe Token for large FETCH */
4195
0
              new_token = track_fetch_observe(lg_xmit->sent_pdu, lg_crcv, block.num + 1,
4196
0
                                              &ctoken);
4197
0
              if (new_token) {
4198
0
                assert(len <= sizeof(buf));
4199
0
                len = new_token->length;
4200
0
                memcpy(buf, new_token->s, len);
4201
0
              }
4202
0
            }
4203
0
          }
4204
0
        }
4205
0
        pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session,
4206
0
                                     len, buf, NULL, COAP_BOOL_FALSE);
4207
0
        if (!pdu)
4208
0
          goto fail_body;
4209
4210
        /*
4211
         * If initial transmit was multicast, that would have been NON.
4212
         * Make subsequent traffic CON for reliability.
4213
         */
4214
0
        if (session->sock.flags & COAP_SOCKET_MULTICAST) {
4215
0
          pdu->type = COAP_MESSAGE_CON;
4216
0
        }
4217
4218
0
        block.num++;
4219
0
        offset = block.num * chunk;
4220
0
        if (block.bert) {
4221
0
          size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) :
4222
0
                                 pdu->used_size;
4223
0
          block.m = (lg_xmit->data_info->length - offset) >
4224
0
                    ((pdu->max_size - token_options) /1024) * 1024;
4225
0
        } else {
4226
0
          block.m = (offset + chunk) < lg_xmit->data_info->length;
4227
0
        }
4228
0
        coap_update_option(pdu, lg_xmit->option,
4229
0
                           coap_encode_var_safe(buf, sizeof(buf),
4230
0
                                                (block.num << 4) |
4231
0
                                                (block.m << 3) |
4232
0
                                                block.aszx),
4233
0
                           buf);
4234
4235
0
        if (lg_xmit->data_info->get_func) {
4236
#if COAP_CONSTRAINED_STACK
4237
          /* Protected by global_lock if needed */
4238
          static uint8_t l_data[1024];
4239
#else /* ! COAP_CONSTRAINED_STACK */
4240
0
          uint8_t l_data[1024];
4241
0
#endif /* ! COAP_CONSTRAINED_STACK */
4242
0
          size_t l_length;
4243
4244
0
          assert(chunk <= 1024);
4245
0
          if (lg_xmit->data_info->get_func(session, chunk,
4246
0
                                           block.num * chunk, l_data, &l_length,
4247
0
                                           lg_xmit->data_info->app_ptr)) {
4248
0
            if (!coap_add_data(pdu, l_length, l_data)) {
4249
0
              goto fail_body;
4250
0
            }
4251
0
          }
4252
0
        } else {
4253
0
          if (!coap_add_block_b_data(pdu,
4254
0
                                     lg_xmit->data_info->length,
4255
0
                                     lg_xmit->data_info->data,
4256
0
                                     &block))
4257
0
            goto fail_body;
4258
0
        }
4259
0
        lg_xmit->b.b1.bert_size = block.chunk_size;
4260
0
        coap_ticks(&lg_xmit->last_sent);
4261
0
#if COAP_Q_BLOCK_SUPPORT
4262
0
        if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 &&
4263
0
            pdu->type == COAP_MESSAGE_NON) {
4264
0
          if (coap_send_q_block1(session, block, pdu,
4265
0
                                 COAP_SEND_INC_PDU) == COAP_INVALID_MID)
4266
0
            goto fail_body;
4267
0
          goto skip_app_handler;
4268
0
        } else if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4269
0
          goto fail_body;
4270
#else /* ! COAP_Q_BLOCK_SUPPORT */
4271
        if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4272
          goto fail_body;
4273
#endif /* ! COAP_Q_BLOCK_SUPPORT */
4274
0
        goto skip_app_handler;
4275
0
      }
4276
0
    } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4277
      /*
4278
       * Not a block response asking for the next block.
4279
       * Could be an Observe response overlapping with block FETCH doing
4280
       * Observe cancellation.
4281
       */
4282
0
      coap_opt_iterator_t opt_iter;
4283
0
      coap_opt_t *obs_opt;
4284
0
      int observe_action = -1;
4285
4286
0
      if (lg_xmit->sent_pdu->code != COAP_REQUEST_CODE_FETCH) {
4287
0
        goto lg_xmit_finished;
4288
0
      }
4289
0
      obs_opt = coap_check_option(lg_xmit->sent_pdu,
4290
0
                                  COAP_OPTION_OBSERVE,
4291
0
                                  &opt_iter);
4292
0
      if (obs_opt) {
4293
0
        observe_action = coap_decode_var_bytes(coap_opt_value(obs_opt),
4294
0
                                               coap_opt_length(obs_opt));
4295
0
      }
4296
0
      if (observe_action != COAP_OBSERVE_CANCEL) {
4297
0
        goto lg_xmit_finished;
4298
0
      }
4299
0
      obs_opt = coap_check_option(rcvd,
4300
0
                                  COAP_OPTION_OBSERVE,
4301
0
                                  &opt_iter);
4302
0
      if (obs_opt) {
4303
0
        goto call_app_handler;
4304
0
      }
4305
0
      goto lg_xmit_finished;
4306
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4307
0
      if (check_freshness(session, rcvd, sent, lg_xmit, NULL))
4308
0
        goto skip_app_handler;
4309
0
#if COAP_Q_BLOCK_SUPPORT
4310
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
4311
      /* Q-Block1 or Q-Block2 not present in p - duplicate error ? */
4312
0
      if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) ||
4313
0
          coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK1, &block))
4314
0
        goto skip_app_handler;
4315
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(408) &&
4316
0
               lg_xmit->option == COAP_OPTION_Q_BLOCK1) {
4317
0
      size_t length;
4318
0
      const uint8_t *data;
4319
0
      coap_opt_iterator_t opt_iter;
4320
0
      coap_opt_t *fmt_opt = coap_check_option(rcvd,
4321
0
                                              COAP_OPTION_CONTENT_FORMAT,
4322
0
                                              &opt_iter);
4323
0
      uint16_t fmt = fmt_opt ?
4324
0
                     coap_decode_var_bytes(coap_opt_value(fmt_opt),
4325
0
                                           coap_opt_length(fmt_opt)) :
4326
0
                     COAP_MEDIATYPE_TEXT_PLAIN;
4327
4328
0
      if (fmt != COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ)
4329
0
        goto fail_body;
4330
4331
0
      if (COAP_PROTO_RELIABLE(session->proto) ||
4332
0
          rcvd->type != COAP_MESSAGE_NON) {
4333
0
        coap_log_debug("Unexpected 4.08 - protocol violation - ignore\n");
4334
0
        goto skip_app_handler;
4335
0
      }
4336
4337
0
      if (coap_get_data(rcvd, &length, &data)) {
4338
        /* Need to decode CBOR to work out what blocks to re-send */
4339
0
        const uint8_t *bp = data;
4340
0
        uint32_t i;
4341
4342
0
        for (i = 0; (bp < data + length) &&
4343
0
             i < COAP_MAX_PAYLOADS(session); i++) {
4344
0
          if ((*bp & 0xc0) != 0x00) /* uint(value) */
4345
0
            goto fail_cbor;
4346
0
          block.num = derive_cbor_value(&bp, data + length - bp);
4347
0
          coap_log_debug("Q-Block1: Missing block %d\n", block.num);
4348
0
          if (block.num > (1 << 20) -1)
4349
0
            goto fail_cbor;
4350
0
          block.m = (block.num + 1) * chunk < lg_xmit->data_info->length;
4351
0
          block.szx = lg_xmit->blk_size;
4352
4353
0
          blocks_add_entry(&lg_xmit->send_blocks, block.num, block.m);
4354
0
        }
4355
0
        if (lg_xmit->send_blocks.used) {
4356
          /* Flush out the first one */
4357
0
          block.num = lg_xmit->send_blocks.range[0].begin;
4358
0
          block.m = (block.num + 1) * chunk < lg_xmit->data_info->length;
4359
0
          coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_MISSING);
4360
0
        }
4361
0
        goto skip_app_handler;
4362
0
      }
4363
0
fail_cbor:
4364
0
      coap_log_info("Invalid application/missing-blocks+cbor-seq\n");
4365
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4366
0
    }
4367
0
    goto lg_xmit_finished;
4368
0
  }
4369
0
  goto call_app_handler;
4370
4371
0
fail_body:
4372
0
  coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session);
4373
  /* There has been an internal error of some sort */
4374
0
  rcvd->code = COAP_RESPONSE_CODE(500);
4375
0
lg_xmit_finished:
4376
0
  if (lg_crcv) {
4377
0
    if (STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) ==
4378
0
        STATE_TOKEN_BASE(lg_crcv->state_token)) {
4379
      /* In case of observe */
4380
0
      lg_crcv->state_token = lg_xmit->b.b1.state_token;
4381
0
      lg_crcv->retry_counter = lg_xmit->b.b1.count;
4382
0
    }
4383
0
  }
4384
0
  if (!lg_crcv) {
4385
    /* need to put back original token into rcvd */
4386
0
    if (lg_xmit->b.b1.app_token)
4387
0
      coap_update_token(rcvd, lg_xmit->b.b1.app_token->length,
4388
0
                        lg_xmit->b.b1.app_token->s);
4389
0
    coap_log_debug("Client app version of updated PDU (1)\n");
4390
0
    coap_show_pdu(COAP_LOG_DEBUG, rcvd);
4391
0
  } else {
4392
0
    lg_crcv->sent_pdu->lg_xmit = 0;
4393
0
  }
4394
4395
0
  if (sent) {
4396
    /* need to put back original token into sent */
4397
0
    if (lg_xmit->b.b1.app_token)
4398
0
      coap_update_token(sent, lg_xmit->b.b1.app_token->length,
4399
0
                        lg_xmit->b.b1.app_token->s);
4400
0
    if (sent->lg_xmit)
4401
0
      coap_remove_option(sent, sent->lg_xmit->option);
4402
0
    sent->lg_xmit = NULL;
4403
0
  }
4404
0
  LL_DELETE(session->lg_xmit, lg_xmit);
4405
0
  coap_block_delete_lg_xmit(session, lg_xmit);
4406
4407
0
call_app_handler:
4408
0
  if (lg_crcv_is_refed)
4409
0
    coap_lg_crcv_release_lkd(session, lg_crcv);
4410
0
  return 0;
4411
4412
0
skip_app_handler:
4413
0
  if (lg_crcv_is_refed)
4414
0
    coap_lg_crcv_release_lkd(session, lg_crcv);
4415
0
  return 1;
4416
0
}
4417
#endif /* COAP_CLIENT_SUPPORT */
4418
4419
void
4420
coap_register_block_data_handler(coap_context_t *context,
4421
0
                                 coap_block_data_handler_t block_data_handler) {
4422
0
  context->block_data_cb = block_data_handler;
4423
0
}
4424
4425
COAP_API coap_binary_t *
4426
coap_block_build_body(coap_binary_t *body_data, size_t length,
4427
0
                      const uint8_t *data, size_t offset, size_t total) {
4428
0
  coap_binary_t *ret;
4429
4430
0
  coap_lock_lock(return NULL);
4431
0
  ret = coap_block_build_body_lkd(body_data, length, data, offset, total);
4432
0
  coap_lock_unlock();
4433
0
  return ret;
4434
0
}
4435
/*
4436
 * Re-assemble payloads into a body
4437
 */
4438
coap_binary_t *
4439
coap_block_build_body_lkd(coap_binary_t *body_data, size_t length,
4440
0
                          const uint8_t *data, size_t offset, size_t total) {
4441
0
  if (data == NULL)
4442
0
    return NULL;
4443
0
  if (body_data == NULL && total) {
4444
0
    body_data = coap_new_binary(total);
4445
0
  }
4446
0
  if (body_data == NULL)
4447
0
    return NULL;
4448
4449
  /* Check no overflow (including a 8 byte small headroom) */
4450
0
  if (SIZE_MAX - length < 8 || offset > SIZE_MAX - length - 8) {
4451
0
    coap_delete_binary(body_data);
4452
0
    return NULL;
4453
0
  }
4454
4455
  /* Update saved data */
4456
0
  if (offset + length <= total && body_data->length >= total) {
4457
0
    memcpy(&body_data->s[offset], data, length);
4458
0
  } else {
4459
    /*
4460
     * total may be inaccurate as per
4461
     * https://rfc-editor.org/rfc/rfc7959#section-4
4462
     * o In a request carrying a Block1 Option, to indicate the current
4463
     *   estimate the client has of the total size of the resource
4464
     *   representation, measured in bytes ("size indication").
4465
     * o In a response carrying a Block2 Option, to indicate the current
4466
     *   estimate the server has of the total size of the resource
4467
     *   representation, measured in bytes ("size indication").
4468
     */
4469
0
    coap_binary_t *new = coap_resize_binary(body_data, offset + length);
4470
4471
0
    if (new) {
4472
0
      body_data = new;
4473
0
      memcpy(&body_data->s[offset], data, length);
4474
0
    } else {
4475
0
      coap_delete_binary(body_data);
4476
0
      return NULL;
4477
0
    }
4478
0
  }
4479
0
  return body_data;
4480
0
}
4481
4482
#if COAP_CLIENT_SUPPORT
4483
/*
4484
 * Need to see if this is a large body response to a request. If so,
4485
 * need to initiate the request for the next block and not trouble the
4486
 * application.  Note that Token must be unique per request/response.
4487
 *
4488
 * This is set up using coap_send()
4489
 * Client receives large data from server ((Q-)Block2)
4490
 *
4491
 * Return: 0 Call application handler
4492
 *         1 Do not call application handler - just sent the next request
4493
 */
4494
int
4495
coap_handle_response_get_block(coap_context_t *context,
4496
                               coap_session_t *session,
4497
                               coap_pdu_t *sent,
4498
                               coap_pdu_t *rcvd,
4499
0
                               coap_recurse_t recursive) {
4500
0
  coap_lg_crcv_t *lg_crcv;
4501
0
  coap_block_b_t block;
4502
0
#if COAP_Q_BLOCK_SUPPORT
4503
0
  coap_block_b_t qblock;
4504
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4505
0
  int have_block = 0;
4506
0
  uint16_t block_opt = 0;
4507
0
  size_t offset;
4508
0
  int ack_rst_sent = 0;
4509
0
  coap_opt_iterator_t opt_iter;
4510
4511
0
  coap_lock_check_locked();
4512
0
  memset(&block, 0, sizeof(block));
4513
0
#if COAP_Q_BLOCK_SUPPORT
4514
0
  memset(&qblock, 0, sizeof(qblock));
4515
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4516
0
  lg_crcv = coap_find_lg_crcv(session, rcvd);
4517
0
  if (lg_crcv) {
4518
0
    size_t chunk = 0;
4519
0
    uint8_t buf[8];
4520
0
    coap_tick_t adjust;
4521
4522
0
    if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
4523
0
      size_t length;
4524
0
      const uint8_t *data;
4525
0
      coap_opt_t *size_opt = coap_check_option(rcvd, COAP_OPTION_SIZE2,
4526
0
                                               &opt_iter);
4527
0
      size_t size2 = size_opt ?
4528
0
                     coap_decode_var_bytes(coap_opt_value(size_opt),
4529
0
                                           coap_opt_length(size_opt)) : 0;
4530
4531
      /* length and data are cleared on error */
4532
0
      (void)coap_get_data(rcvd, &length, &data);
4533
0
      rcvd->body_offset = 0;
4534
0
      rcvd->body_total = length;
4535
0
      if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
4536
0
        have_block = 1;
4537
0
        block_opt = COAP_OPTION_BLOCK2;
4538
0
      }
4539
0
#if COAP_Q_BLOCK_SUPPORT
4540
0
      if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) {
4541
0
        if (have_block) {
4542
0
          coap_log_warn("Both Block1 and Q-Block1 not supported in a response\n");
4543
0
        }
4544
0
        have_block = 1;
4545
0
        block_opt = COAP_OPTION_Q_BLOCK2;
4546
0
        block = qblock;
4547
        /* server indicating that it supports Q_BLOCK */
4548
0
        if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
4549
0
          set_block_mode_has_q(session->block_mode);
4550
0
        }
4551
0
      }
4552
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4553
0
      track_echo(session, rcvd);
4554
0
      if (have_block && (block.m || length)) {
4555
0
        coap_opt_t *fmt_opt = coap_check_option(rcvd,
4556
0
                                                COAP_OPTION_CONTENT_FORMAT,
4557
0
                                                &opt_iter);
4558
0
        uint16_t fmt = fmt_opt ?
4559
0
                       coap_decode_var_bytes(coap_opt_value(fmt_opt),
4560
0
                                             coap_opt_length(fmt_opt)) :
4561
0
                       COAP_MEDIATYPE_TEXT_PLAIN;
4562
0
        coap_opt_t *etag_opt = coap_check_option(rcvd,
4563
0
                                                 COAP_OPTION_ETAG,
4564
0
                                                 &opt_iter);
4565
0
        size_t saved_offset;
4566
0
        int updated_block;
4567
4568
0
#if COAP_Q_BLOCK_SUPPORT
4569
0
        if (block_opt == COAP_OPTION_Q_BLOCK2 && (!size_opt || !etag_opt)) {
4570
          /* RFC9177 section-4.4 */
4571
0
          coap_log_info("Q-Block2: Size2 and ETag options required\n");
4572
          /* Try to hint to the server there is an issue */
4573
0
          coap_send_rst_lkd(session, rcvd);
4574
0
          coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session);
4575
0
          return 1;
4576
0
        }
4577
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4578
0
        if (length > block.chunk_size) {
4579
0
          coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %" PRIuS "\n",
4580
0
                         block.chunk_size, length);
4581
0
          length = block.chunk_size;
4582
0
        }
4583
0
        if (block.m && length != block.chunk_size) {
4584
0
          coap_log_warn("block: Undersized packet - expected %"PRIu32", got %" PRIuS "\n",
4585
0
                        block.chunk_size, length);
4586
          /* Unclear how to properly handle this */
4587
0
          rcvd->code = COAP_RESPONSE_CODE(402);
4588
0
          goto expire_lg_crcv;
4589
0
        }
4590
        /* Possibility that Size2 not sent, or is too small */
4591
0
        chunk = (size_t)1 << (block.szx + 4);
4592
0
        offset = block.num * chunk;
4593
0
        if (size2 < (offset + length)) {
4594
0
          if (block.m)
4595
0
            size2 = offset + length + 1;
4596
0
          else
4597
0
            size2 = offset + length;
4598
0
        }
4599
0
        saved_offset = offset;
4600
4601
0
        if (lg_crcv->initial) {
4602
0
#if COAP_Q_BLOCK_SUPPORT
4603
0
reinit:
4604
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4605
0
          lg_crcv->initial = 0;
4606
0
          if (lg_crcv->body_data) {
4607
0
            coap_free_type(COAP_STRING, lg_crcv->body_data);
4608
0
            lg_crcv->body_data = NULL;
4609
0
          }
4610
0
          if (etag_opt) {
4611
0
            lg_crcv->etag_length = (uint8_t)min(coap_opt_length(etag_opt), sizeof(lg_crcv->etag));
4612
0
            memcpy(lg_crcv->etag, coap_opt_value(etag_opt), lg_crcv->etag_length);
4613
0
            lg_crcv->etag_set = 1;
4614
0
          } else {
4615
0
            lg_crcv->etag_set = 0;
4616
0
          }
4617
0
          lg_crcv->total_len = size2;
4618
0
          lg_crcv->content_format = fmt;
4619
0
          lg_crcv->szx = block.szx;
4620
0
          lg_crcv->block_option = block_opt;
4621
0
          lg_crcv->last_type = rcvd->type;
4622
0
          lg_crcv->rec_blocks.used = 0;
4623
0
          lg_crcv->rec_blocks.total_blocks = 0;
4624
0
#if COAP_Q_BLOCK_SUPPORT
4625
0
          lg_crcv->rec_blocks.processing_payload_set = 0;
4626
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4627
0
        }
4628
0
        if (lg_crcv->total_len < size2)
4629
0
          lg_crcv->total_len = size2;
4630
4631
        /* Check whether we can handle this size */
4632
0
        uint32_t max_body;
4633
0
        uint8_t max_block_szx;
4634
4635
0
        max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode);
4636
0
        if (max_block_szx == 0 || max_block_szx > block.szx) {
4637
0
          max_block_szx =  block.szx;
4638
0
        }
4639
0
        max_body = ((1UL << 20) * (1 << (max_block_szx + 4)));
4640
0
        if (max_body > MAX_BLK_LEN)
4641
0
          max_body = MAX_BLK_LEN;
4642
0
        if ((context->max_body_size && size2 > context->max_body_size) ||
4643
0
            (size2 > max_body)) {
4644
0
          uint32_t max_body_size = context->max_body_size;
4645
4646
0
          if (max_body_size == 0 || max_body < max_body_size) {
4647
0
            max_body_size = max_body;
4648
0
          }
4649
0
          coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", size2, max_body_size);
4650
          /* Try to hint to the server there is an issue */
4651
0
          coap_send_rst_lkd(session, rcvd);
4652
0
          coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session);
4653
0
          return 1;
4654
0
        }
4655
4656
0
        if (etag_opt) {
4657
0
          if (!full_match(coap_opt_value(etag_opt),
4658
0
                          coap_opt_length(etag_opt),
4659
0
                          lg_crcv->etag, lg_crcv->etag_length)) {
4660
            /* body of data has changed - need to restart request */
4661
0
            coap_pdu_t *pdu;
4662
0
            uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token,
4663
0
                                              ++lg_crcv->retry_counter);
4664
0
            size_t len = coap_encode_var_safe8(buf, sizeof(token), token);
4665
0
            coap_opt_filter_t drop_options;
4666
4667
0
#if COAP_Q_BLOCK_SUPPORT
4668
0
            if (block_opt == COAP_OPTION_Q_BLOCK2)
4669
0
              goto reinit;
4670
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4671
4672
0
            coap_log_warn("Data body updated during receipt - new request started\n");
4673
0
            if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY))
4674
0
              coap_handle_event_lkd(context, COAP_EVENT_PARTIAL_BLOCK, session);
4675
4676
0
            lg_crcv->initial = 1;
4677
0
            coap_free_type(COAP_STRING, lg_crcv->body_data);
4678
0
            lg_crcv->body_data = NULL;
4679
4680
0
            coap_session_new_token(session, &len, buf);
4681
0
            memset(&drop_options, 0, sizeof(coap_opt_filter_t));
4682
0
            coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE);
4683
0
            coap_option_filter_set(&drop_options, COAP_OPTION_BLOCK1);
4684
0
            pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf,
4685
0
                                         &drop_options, COAP_BOOL_FALSE);
4686
0
            if (!pdu)
4687
0
              goto fail_resp;
4688
4689
0
            coap_update_option(pdu, block_opt,
4690
0
                               coap_encode_var_safe(buf, sizeof(buf),
4691
0
                                                    (0 << 4) | (0 << 3) | block.aszx),
4692
0
                               buf);
4693
4694
0
            if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4695
0
              goto fail_resp;
4696
4697
0
            goto skip_app_handler;
4698
0
          }
4699
0
        } else if (lg_crcv->etag_set) {
4700
          /* Cannot handle this change in ETag to not being there */
4701
0
          coap_log_warn("Not all blocks have ETag option\n");
4702
0
          goto fail_resp;
4703
0
        }
4704
4705
0
        if (fmt != lg_crcv->content_format) {
4706
0
          coap_log_warn("Content-Format option mismatch\n");
4707
0
          goto fail_resp;
4708
0
        }
4709
0
#if COAP_Q_BLOCK_SUPPORT
4710
0
        if (block_opt == COAP_OPTION_Q_BLOCK2 && size2 != lg_crcv->total_len) {
4711
0
          coap_log_warn("Size2 option mismatch (%"PRIuS " != %" PRIuS ")\n",
4712
0
                        size2, lg_crcv->total_len);
4713
0
          goto fail_resp;
4714
0
        }
4715
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4716
0
        if (block.num == 0) {
4717
0
          coap_opt_t *obs_opt = coap_check_option(rcvd,
4718
0
                                                  COAP_OPTION_OBSERVE,
4719
0
                                                  &opt_iter);
4720
0
          if (obs_opt) {
4721
0
            lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3);
4722
0
            memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length);
4723
0
            lg_crcv->observe_set = 1;
4724
0
          } else {
4725
0
            lg_crcv->observe_set = 0;
4726
0
          }
4727
0
        }
4728
0
        updated_block = 0;
4729
0
        while (offset < saved_offset + length) {
4730
0
          if (!check_if_received_block(&lg_crcv->rec_blocks, block.num)) {
4731
0
#if COAP_Q_BLOCK_SUPPORT
4732
0
            uint32_t this_payload_set = block.num / COAP_MAX_PAYLOADS(session);
4733
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4734
4735
0
            coap_log_debug("found Block option, block size is %u, block nr. %u\n",
4736
0
                           1 << (block.szx + 4), block.num);
4737
            /* Update list of blocks received */
4738
0
            if (blocks_add_entry(&lg_crcv->rec_blocks, block.num, block.m)) {
4739
0
              updated_block = 1;
4740
0
            } else {
4741
0
              coap_log_debug("Block nr %u ignored (too many missing blocks)\n", block.num);
4742
0
            }
4743
0
#if COAP_Q_BLOCK_SUPPORT
4744
0
            if (block_opt == COAP_OPTION_Q_BLOCK2) {
4745
0
              if (lg_crcv->rec_blocks.used > 1 &&
4746
0
                  (block.num == lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end ||
4747
0
                   block.num == lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end - 1) &&
4748
0
                  lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].begin <
4749
0
                  lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end &&
4750
0
                  (lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end % COAP_MAX_PAYLOADS(session)) + 1
4751
0
                  == COAP_MAX_PAYLOADS(session)) {
4752
                /*
4753
                 * At least the last 2 of a PAYLOAD_SET have been received, but some
4754
                 * missing packets, but have seen the emd of a PAYLOAD_SET.
4755
                 */
4756
0
                coap_log_debug("Fast recovery for block %u\n", block.num);
4757
0
                coap_request_missing_q_block2(session, lg_crcv);
4758
0
              } else if (lg_crcv->rec_blocks.used &&
4759
0
                         this_payload_set > lg_crcv->rec_blocks.processing_payload_set &&
4760
0
                         this_payload_set != lg_crcv->rec_blocks.latest_payload_set) {
4761
0
                coap_request_missing_q_block2(session, lg_crcv);
4762
0
              }
4763
0
            }
4764
0
            lg_crcv->rec_blocks.latest_payload_set = this_payload_set;
4765
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4766
0
          } else {
4767
0
            coap_log_debug("Duplicate block nr %u\n", block.num);
4768
0
          }
4769
0
          block.num++;
4770
0
          offset = block.num << (block.szx + 4);
4771
0
          if (!block.bert && block_opt != COAP_OPTION_Q_BLOCK2)
4772
0
            break;
4773
0
        }
4774
0
        block.num--;
4775
        /* Only process if not duplicate block */
4776
0
        if (updated_block) {
4777
0
          void *body_free;
4778
4779
          /* Update last_used to prevent premature timeout during long transfers */
4780
0
          coap_ticks(&lg_crcv->last_used);
4781
4782
0
          if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
4783
0
            if (size2 < saved_offset + length) {
4784
0
              size2 = saved_offset + length;
4785
0
            }
4786
0
            if (context && context->block_data_cb) {
4787
0
              coap_response_t resp;
4788
4789
0
              coap_lock_callback_ret(resp,
4790
0
                                     context->block_data_cb(session, rcvd, 0,
4791
0
                                                            &lg_crcv->body_data,
4792
0
                                                            length, data,
4793
0
                                                            saved_offset, size2));
4794
0
              if (resp != COAP_RESPONSE_OK) {
4795
0
                goto fail_resp;
4796
0
              }
4797
0
            } else {
4798
0
              lg_crcv->body_data = coap_block_build_body_lkd(lg_crcv->body_data, length, data,
4799
0
                                                             saved_offset, size2);
4800
0
              if (lg_crcv->body_data == NULL) {
4801
0
                goto fail_resp;
4802
0
              }
4803
0
            }
4804
0
          }
4805
0
          if (block.m || !check_all_blocks_in(&lg_crcv->rec_blocks)) {
4806
            /* Not all the payloads of the body have arrived */
4807
0
            size_t len;
4808
0
            coap_pdu_t *pdu;
4809
0
            uint64_t token;
4810
0
            coap_opt_filter_t drop_options;
4811
4812
0
            if (block.m) {
4813
0
#if COAP_Q_BLOCK_SUPPORT
4814
0
              if (block_opt == COAP_OPTION_Q_BLOCK2) {
4815
                /* Blocks could arrive in wrong order */
4816
0
                if (check_all_blocks_in(&lg_crcv->rec_blocks)) {
4817
0
                  goto give_to_app;
4818
0
                }
4819
0
                if (check_all_blocks_in_for_payload_set(session,
4820
0
                                                        &lg_crcv->rec_blocks)) {
4821
0
                  block.num = lg_crcv->rec_blocks.range[0].end;
4822
                  /* Now requesting next payload */
4823
0
                  lg_crcv->rec_blocks.processing_payload_set =
4824
0
                      block.num / COAP_MAX_PAYLOADS(session) + 1;
4825
0
                  if (check_any_blocks_next_payload_set(session,
4826
0
                                                        &lg_crcv->rec_blocks)) {
4827
                    /* Need to ask for them individually */
4828
0
                    coap_request_missing_q_block2(session, lg_crcv);
4829
0
                    goto skip_app_handler;
4830
0
                  }
4831
0
                } else {
4832
                  /* The remote end will be sending the next one unless this
4833
                     is a MAX_PAYLOADS and all previous have been received */
4834
0
                  goto skip_app_handler;
4835
0
                }
4836
0
                if (COAP_PROTO_RELIABLE(session->proto) ||
4837
0
                    rcvd->type != COAP_MESSAGE_NON)
4838
0
                  goto skip_app_handler;
4839
4840
0
              } else
4841
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4842
0
                block.m = 0;
4843
4844
              /* Ask for the next block */
4845
0
              token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter);
4846
0
              len = coap_encode_var_safe8(buf, sizeof(token), token);
4847
0
              memset(&drop_options, 0, sizeof(coap_opt_filter_t));
4848
0
              coap_option_filter_set(&drop_options, COAP_OPTION_BLOCK1);
4849
0
              pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf,
4850
0
                                           &drop_options, COAP_BOOL_FALSE);
4851
0
              if (!pdu)
4852
0
                goto fail_resp;
4853
4854
0
              if (rcvd->type == COAP_MESSAGE_NON)
4855
0
                pdu->type = COAP_MESSAGE_NON; /* Server is using NON */
4856
4857
              /* Only sent with the first block */
4858
0
              coap_remove_option(pdu, COAP_OPTION_OBSERVE);
4859
4860
0
              coap_update_option(pdu, block_opt,
4861
0
                                 coap_encode_var_safe(buf, sizeof(buf),
4862
0
                                                      ((block.num + 1) << 4) |
4863
0
                                                      (block.m << 3) | block.aszx),
4864
0
                                 buf);
4865
4866
0
              if (session->block_mode & COAP_BLOCK_STLESS_FETCH && pdu->code == COAP_REQUEST_CODE_FETCH) {
4867
0
                (void)coap_get_data(lg_crcv->sent_pdu, &length, &data);
4868
0
                coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, length, data, NULL, NULL, NULL,
4869
0
                                             0, 0);
4870
0
              }
4871
0
              if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID)
4872
                /* Session could now be disconnected, so no lg_crcv */
4873
0
                goto skip_app_handler;
4874
0
            }
4875
0
            if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert)
4876
0
              goto skip_app_handler;
4877
4878
            /* need to put back original token into rcvd */
4879
0
            coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4880
0
            rcvd->body_offset = saved_offset;
4881
0
#if COAP_Q_BLOCK_SUPPORT
4882
0
            rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
4883
0
                               lg_crcv->total_len : size2;
4884
#else /* ! COAP_Q_BLOCK_SUPPORT */
4885
            rcvd->body_total = size2;
4886
#endif /* ! COAP_Q_BLOCK_SUPPORT */
4887
0
            coap_log_debug("Client app version of updated PDU (2)\n");
4888
0
            coap_show_pdu(COAP_LOG_DEBUG, rcvd);
4889
4890
0
            if (sent) {
4891
              /* need to put back original token into sent */
4892
0
              if (lg_crcv->app_token)
4893
0
                coap_update_token(sent, lg_crcv->app_token->length,
4894
0
                                  lg_crcv->app_token->s);
4895
0
              coap_remove_option(sent, lg_crcv->block_option);
4896
0
            }
4897
0
            goto call_app_handler;
4898
0
          }
4899
0
#if COAP_Q_BLOCK_SUPPORT
4900
0
give_to_app:
4901
0
#endif /* COAP_Q_BLOCK_SUPPORT */
4902
0
          if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) {
4903
            /* Pretend that there is no block */
4904
0
            coap_remove_option(rcvd, block_opt);
4905
0
            if (lg_crcv->observe_set) {
4906
0
              coap_update_option(rcvd, COAP_OPTION_OBSERVE,
4907
0
                                 lg_crcv->observe_length, lg_crcv->observe);
4908
0
            }
4909
0
            rcvd->body_data = lg_crcv->body_data ? lg_crcv->body_data->s : NULL;
4910
0
#if COAP_Q_BLOCK_SUPPORT
4911
0
            if (context && context->block_data_cb) {
4912
              /* Data has already been provided - do not duplicate */
4913
0
              if (rcvd->data) {
4914
0
                rcvd->used_size = rcvd->data - rcvd->token - 1;
4915
0
                rcvd->data = NULL;
4916
0
              }
4917
0
            }
4918
0
            rcvd->body_length = block_opt == COAP_OPTION_Q_BLOCK2 ?
4919
0
                                lg_crcv->total_len : saved_offset + length;
4920
#else /* ! COAP_Q_BLOCK_SUPPORT */
4921
            rcvd->body_length = saved_offset + length;
4922
#endif /* ! COAP_Q_BLOCK_SUPPORT */
4923
0
            rcvd->body_offset = 0;
4924
0
            rcvd->body_total = rcvd->body_length;
4925
0
          } else {
4926
0
            rcvd->body_offset = saved_offset;
4927
0
#if COAP_Q_BLOCK_SUPPORT
4928
0
            rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ?
4929
0
                               lg_crcv->total_len : size2;
4930
#else /* ! COAP_Q_BLOCK_SUPPORT */
4931
            rcvd->body_total = size2;
4932
#endif /* ! COAP_Q_BLOCK_SUPPORT */
4933
0
          }
4934
          /* need to put back original token into rcvd */
4935
0
          if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4936
0
            coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4937
0
            coap_log_debug("Client app version of updated PDU (3)\n");
4938
0
            coap_show_pdu(COAP_LOG_DEBUG, rcvd);
4939
0
          }
4940
0
          if (sent) {
4941
            /* need to put back original token into sent */
4942
0
            if (lg_crcv->app_token)
4943
0
              coap_update_token(sent, lg_crcv->app_token->length,
4944
0
                                lg_crcv->app_token->s);
4945
0
            coap_remove_option(sent, lg_crcv->block_option);
4946
0
          }
4947
0
          body_free = lg_crcv->body_data;
4948
0
          lg_crcv->body_data = NULL;
4949
0
          coap_call_response_handler(session, sent, rcvd, body_free);
4950
4951
0
          ack_rst_sent = 1;
4952
0
          if (lg_crcv->observe_set == 0) {
4953
            /* Expire this entry */
4954
0
            LL_DELETE(session->lg_crcv, lg_crcv);
4955
0
            coap_block_delete_lg_crcv(session, lg_crcv);
4956
0
            goto skip_app_handler;
4957
0
          }
4958
          /* Set up for the next data body as observing */
4959
0
          lg_crcv->initial = 1;
4960
0
        }
4961
0
        coap_ticks(&lg_crcv->last_used);
4962
0
        goto skip_app_handler;
4963
0
      } else {
4964
0
        coap_opt_t *obs_opt = coap_check_option(rcvd,
4965
0
                                                COAP_OPTION_OBSERVE,
4966
0
                                                &opt_iter);
4967
0
        if (context->max_body_size && length > context->max_body_size) {
4968
0
          coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", length,
4969
0
                        context->max_body_size);
4970
          /* Try to hint to the server there is an issue */
4971
0
          coap_send_rst_lkd(session, rcvd);
4972
0
          coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session);
4973
0
          return 1;
4974
0
        }
4975
0
        if (obs_opt) {
4976
0
          lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3);
4977
0
          memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length);
4978
0
          lg_crcv->observe_set = 1;
4979
0
        } else {
4980
0
          lg_crcv->observe_set = 0;
4981
0
          if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
4982
            /* need to put back original token into rcvd */
4983
0
            coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
4984
0
            coap_remove_option(rcvd, COAP_OPTION_BLOCK1);
4985
0
            coap_log_debug("PDU presented to app.\n");
4986
0
            coap_show_pdu(COAP_LOG_DEBUG, rcvd);
4987
0
          }
4988
          /* Expire this entry */
4989
0
          goto expire_lg_crcv;
4990
0
        }
4991
0
      }
4992
0
      coap_ticks(&lg_crcv->last_used);
4993
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
4994
0
#if COAP_OSCORE_SUPPORT
4995
0
      if (check_freshness(session, rcvd,
4996
0
                          (session->oscore_encryption == 0) ? sent : NULL,
4997
0
                          NULL, lg_crcv))
4998
#else /* !COAP_OSCORE_SUPPORT */
4999
      if (check_freshness(session, rcvd, sent, NULL, lg_crcv))
5000
#endif /* !COAP_OSCORE_SUPPORT */
5001
0
        goto skip_app_handler;
5002
0
      goto expire_lg_crcv;
5003
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
5004
0
      coap_opt_t *abb_opt = sent ? coap_check_option(sent,
5005
0
                                                     COAP_OPTION_URI_PATH_ABB,
5006
0
                                                     &opt_iter) : NULL;
5007
0
      if (abb_opt) {
5008
        /* Send the request again with the Uri-Path-Abbrev expanded out */
5009
0
        coap_pdu_t *pdu;
5010
0
        size_t data_len;
5011
0
        const uint8_t *data;
5012
0
        uint64_t token;
5013
0
        uint8_t ltoken[8];
5014
0
        size_t ltoken_len;
5015
5016
0
        session->no_path_abbrev = 1;
5017
0
        token = STATE_TOKEN_FULL(lg_crcv->state_token,
5018
0
                                 ++lg_crcv->retry_counter);
5019
0
        ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token);
5020
0
        pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, ltoken_len,
5021
0
                                     ltoken, NULL, COAP_BOOL_TRUE);
5022
0
        if (pdu) {
5023
0
          if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) {
5024
0
            coap_add_data(pdu, data_len, data);
5025
0
          }
5026
0
#if COAP_PROXY_SUPPORT
5027
0
          coap_proxy_req_t *proxy_req = session->context->proxy_list_count ?
5028
0
                                        coap_proxy_map_outgoing_request(session, rcvd, NULL) : NULL;
5029
5030
0
          if (proxy_req) {
5031
0
            coap_bin_const_t *new = coap_new_bin_const(ltoken, ltoken_len);
5032
0
            if (new) {
5033
0
              coap_delete_bin_const(proxy_req->token_used);
5034
0
              proxy_req->token_used = new;
5035
0
              coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used,  "upd");
5036
0
            }
5037
0
          }
5038
0
#endif /* COAP_PROXY_SUPPORT */
5039
0
          coap_log_debug("*  Retransmitting PDU with Uri-Path-Abbrev replaced (1)\n");
5040
0
          coap_delete_pdu_lkd(lg_crcv->sent_pdu);
5041
0
          lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu);
5042
0
          coap_send_internal(session, pdu, NULL);
5043
0
          goto skip_app_handler;
5044
0
        }
5045
0
      }
5046
0
      goto expire_lg_crcv;
5047
0
    } else {
5048
      /* Not 2.xx, 4.01 or 4.02 - assume it is a failure of some sort */
5049
0
      goto expire_lg_crcv;
5050
0
    }
5051
0
    if (!block.m && !lg_crcv->observe_set) {
5052
0
fail_resp:
5053
5054
      /* lg_crcv no longer required - cache it for 1 sec */
5055
0
      if (COAP_MAX_TRANSMIT_WAIT_TICKS(session) >= COAP_TICKS_PER_SECOND) {
5056
0
        adjust = COAP_MAX_TRANSMIT_WAIT_TICKS(session) - COAP_TICKS_PER_SECOND;
5057
0
      } else {
5058
0
        adjust = 0;
5059
0
      }
5060
0
      coap_ticks(&lg_crcv->last_used);
5061
0
      lg_crcv->last_used -= adjust;
5062
0
    }
5063
    /* need to put back original token into rcvd */
5064
0
    if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
5065
0
      coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
5066
0
      coap_log_debug("Client app version of updated PDU (4)\n");
5067
0
      coap_show_pdu(COAP_LOG_DEBUG, rcvd);
5068
0
    }
5069
0
  }
5070
5071
  /* Check if receiving a block response and if blocks can be set up */
5072
0
  if (recursive == COAP_RECURSE_OK && !lg_crcv) {
5073
0
    if (!sent) {
5074
0
      if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)
5075
0
#if COAP_Q_BLOCK_SUPPORT
5076
0
          ||
5077
0
          coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)
5078
0
#endif /* COAP_Q_BLOCK_SUPPORT */
5079
0
         ) {
5080
0
        coap_log_debug("** %s: large body receive internal issue\n",
5081
0
                       coap_session_str(session));
5082
0
        goto skip_app_handler;
5083
0
      }
5084
0
    } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) {
5085
0
      const uint8_t *data;
5086
0
      size_t length;
5087
5088
0
      if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) {
5089
0
#if COAP_Q_BLOCK_SUPPORT
5090
0
        if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) {
5091
0
          set_block_mode_drop_q(session->block_mode);
5092
0
          coap_log_debug("Q-Block support disabled\n");
5093
0
        }
5094
0
#endif /* COAP_Q_BLOCK_SUPPORT */
5095
0
        have_block = 1;
5096
0
        if (block.num != 0) {
5097
          /* Assume random access and just give the single response to app */
5098
0
          size_t chunk = (size_t)1 << (block.szx + 4);
5099
5100
0
          coap_get_data(rcvd, &length, &data);
5101
0
          rcvd->body_offset = block.num*chunk;
5102
0
          rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0);
5103
0
          goto call_app_handler;
5104
0
        }
5105
0
      }
5106
0
#if COAP_Q_BLOCK_SUPPORT
5107
0
      else if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)) {
5108
0
        have_block = 1;
5109
        /* server indicating that it supports Q_BLOCK2 */
5110
0
        if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) {
5111
0
          set_block_mode_has_q(session->block_mode);
5112
0
        }
5113
0
      }
5114
0
#endif /* COAP_Q_BLOCK_SUPPORT */
5115
0
      if (have_block) {
5116
0
        lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
5117
5118
0
        if (lg_crcv) {
5119
0
          LL_PREPEND(session->lg_crcv, lg_crcv);
5120
0
          return coap_handle_response_get_block(context, session, sent, rcvd,
5121
0
                                                COAP_RECURSE_NO);
5122
0
        }
5123
0
      }
5124
0
      coap_get_data(rcvd, &length, &data);
5125
0
      if (context->max_body_size && length > context->max_body_size) {
5126
0
        coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", length,
5127
0
                      context->max_body_size);
5128
        /* Try to hint to the server there is an issue */
5129
0
        coap_send_rst_lkd(session, rcvd);
5130
0
        coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session);
5131
0
        return 1;
5132
0
      }
5133
0
      track_echo(session, rcvd);
5134
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(401)) {
5135
0
      lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
5136
5137
0
      if (lg_crcv) {
5138
0
        LL_PREPEND(session->lg_crcv, lg_crcv);
5139
0
        return coap_handle_response_get_block(context, session, sent, rcvd,
5140
0
                                              COAP_RECURSE_NO);
5141
0
      }
5142
0
    } else if (rcvd->code == COAP_RESPONSE_CODE(402)) {
5143
0
      coap_opt_t *abb_opt = coap_check_option(sent,
5144
0
                                              COAP_OPTION_URI_PATH_ABB,
5145
0
                                              &opt_iter);
5146
0
      if (abb_opt) {
5147
        /*
5148
         * Send the request again with the Uri-Path-Abbrev expanded out, but need
5149
           lg_crcv in place to handle the token update.
5150
        */
5151
0
        lg_crcv = coap_block_new_lg_crcv(session, sent, NULL);
5152
5153
0
        if (lg_crcv) {
5154
0
          LL_PREPEND(session->lg_crcv, lg_crcv);
5155
0
          return coap_handle_response_get_block(context, session, sent, rcvd,
5156
0
                                                COAP_RECURSE_NO);
5157
0
        }
5158
0
      }
5159
0
    }
5160
0
  }
5161
0
  return 0;
5162
5163
0
expire_lg_crcv:
5164
  /* need to put back original token into rcvd */
5165
0
  if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) {
5166
0
    coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s);
5167
0
    coap_log_debug("Client app version of updated PDU (5)\n");
5168
0
    coap_show_pdu(COAP_LOG_DEBUG, rcvd);
5169
0
  }
5170
5171
0
  if (sent) {
5172
    /* need to put back original token into sent */
5173
0
    if (lg_crcv->app_token)
5174
0
      coap_update_token(sent, lg_crcv->app_token->length,
5175
0
                        lg_crcv->app_token->s);
5176
0
    coap_remove_option(sent, lg_crcv->block_option);
5177
0
  }
5178
  /* Expire this entry */
5179
0
  LL_DELETE(session->lg_crcv, lg_crcv);
5180
0
  coap_block_delete_lg_crcv(session, lg_crcv);
5181
5182
0
call_app_handler:
5183
0
  return 0;
5184
5185
0
skip_app_handler:
5186
0
  if (!ack_rst_sent)
5187
0
    coap_send_ack_lkd(session, rcvd);
5188
0
  return 1;
5189
0
}
5190
#endif /* COAP_CLIENT_SUPPORT */
5191
5192
#if COAP_SERVER_SUPPORT
5193
/* Check if lg_xmit generated and update PDU code if so */
5194
void
5195
coap_check_code_lg_xmit(const coap_session_t *session,
5196
                        const coap_pdu_t *request,
5197
                        coap_pdu_t *response, const coap_resource_t *resource,
5198
0
                        const coap_string_t *query) {
5199
0
  coap_lg_xmit_t *lg_xmit;
5200
5201
0
  if (response->code == 0)
5202
0
    return;
5203
0
  lg_xmit = coap_find_lg_xmit_response(session, request, resource, query);
5204
0
  if (lg_xmit && lg_xmit->sent_pdu && lg_xmit->sent_pdu->code == 0) {
5205
0
    lg_xmit->sent_pdu->code = response->code;
5206
0
    return;
5207
0
  }
5208
0
}
5209
#endif /* COAP_SERVER_SUPPORT */
5210
5211
#if COAP_CLIENT_SUPPORT
5212
void
5213
0
coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu) {
5214
0
  uint64_t token_match =
5215
0
      STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s,
5216
0
                                              pdu->actual_token.length));
5217
0
  coap_lg_xmit_t *lg_xmit;
5218
0
  coap_lg_crcv_t *lg_crcv;
5219
5220
0
  if (session->lg_crcv) {
5221
0
    LL_FOREACH(session->lg_crcv, lg_crcv) {
5222
0
      if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token))
5223
0
        return;
5224
0
      if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) {
5225
0
        coap_update_token(pdu, lg_crcv->app_token->length,
5226
0
                          lg_crcv->app_token->s);
5227
0
        coap_log_debug("Client app version of updated PDU (6)\n");
5228
0
        coap_show_pdu(COAP_LOG_DEBUG, pdu);
5229
0
        return;
5230
0
      }
5231
0
    }
5232
0
  }
5233
0
  if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) {
5234
0
    LL_FOREACH(session->lg_xmit, lg_xmit) {
5235
0
      if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token))
5236
0
        return;
5237
0
      if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) {
5238
0
        coap_update_token(pdu, lg_xmit->b.b1.app_token->length,
5239
0
                          lg_xmit->b.b1.app_token->s);
5240
0
        coap_log_debug("Client app version of updated PDU (7)\n");
5241
0
        coap_show_pdu(COAP_LOG_DEBUG, pdu);
5242
0
        return;
5243
0
      }
5244
0
    }
5245
0
  }
5246
0
}
5247
#endif /* ! COAP_CLIENT_SUPPORT */