Coverage Report

Created: 2026-08-03 07:05

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