Coverage Report

Created: 2026-04-02 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/src/coap_pdu.c
Line
Count
Source
1
/* coap_pdu.c -- CoAP PDU handling
2
 *
3
 * Copyright (C) 2010--2026 Olaf Bergmann <bergmann@tzi.org>
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_pdu.c
13
 * @brief CoAP PDU handling
14
 */
15
16
#include "coap3/coap_libcoap_build.h"
17
18
#if defined(HAVE_LIMITS_H)
19
#include <limits.h>
20
#endif
21
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <string.h>
25
#ifndef __ZEPHYR__
26
#ifdef HAVE_ARPA_INET_H
27
#include <arpa/inet.h>
28
#endif
29
#ifdef HAVE_WINSOCK2_H
30
#include <winsock2.h>
31
#endif
32
#endif /* !__ZEPHYR__ */
33
34
#include <ctype.h>
35
36
#ifndef min
37
22.9k
#define min(a,b) ((a) < (b) ? (a) : (b))
38
#endif
39
40
#ifndef max
41
152
#define max(a,b) ((a) > (b) ? (a) : (b))
42
#endif
43
44
void
45
22.9k
coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
46
22.9k
  assert(pdu);
47
22.9k
  assert(pdu->token);
48
22.9k
  assert(pdu->max_hdr_size >= COAP_PDU_MAX_UDP_HEADER_SIZE);
49
22.9k
  if (pdu->alloc_size > size)
50
0
    pdu->alloc_size = size;
51
22.9k
  pdu->type = 0;
52
22.9k
  pdu->code = 0;
53
22.9k
  pdu->ref = 0;
54
22.9k
  pdu->hdr_size = 0;
55
22.9k
  pdu->actual_token.length = 0;
56
22.9k
  pdu->e_token_length = 0;
57
22.9k
  pdu->crit_opt = 0;
58
22.9k
  pdu->mid = 0;
59
22.9k
  pdu->max_opt = 0;
60
22.9k
  pdu->max_size = size;
61
22.9k
  pdu->used_size = 0;
62
22.9k
  pdu->data = NULL;
63
22.9k
  pdu->body_data = NULL;
64
22.9k
  pdu->body_length = 0;
65
22.9k
  pdu->body_offset = 0;
66
22.9k
  pdu->body_total = 0;
67
22.9k
  pdu->lg_xmit = NULL;
68
22.9k
  pdu->session = NULL;
69
22.9k
  pdu->data_free = NULL;
70
22.9k
}
71
72
#ifdef WITH_LWIP
73
coap_pdu_t *
74
coap_pdu_from_pbuf(struct pbuf *pbuf) {
75
  coap_pdu_t *pdu;
76
77
  if (pbuf == NULL)
78
    return NULL;
79
80
  LWIP_ASSERT("Can only deal with contiguous PBUFs (increase PBUF_POOL_BUFSIZE)",
81
              pbuf->tot_len == pbuf->len);
82
  LWIP_ASSERT("coap_io_do_io needs to receive an exclusive copy of the incoming pbuf",
83
              pbuf->ref == 1);
84
85
  pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
86
  if (!pdu) {
87
    pbuf_free(pbuf);
88
    return NULL;
89
  }
90
91
  pdu->max_hdr_size = COAP_PDU_MAX_UDP_HEADER_SIZE;
92
  pdu->pbuf = pbuf;
93
  pdu->token = (uint8_t *)pbuf->payload + pdu->max_hdr_size;
94
  pdu->alloc_size = pbuf->tot_len - pdu->max_hdr_size;
95
  coap_pdu_clear(pdu, pdu->alloc_size);
96
97
  return pdu;
98
}
99
#endif /* LWIP */
100
101
coap_pdu_t *
102
coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid,
103
22.9k
              size_t size) {
104
22.9k
  coap_pdu_t *pdu;
105
106
22.9k
#ifndef RIOT_VERSION
107
22.9k
  assert(type <= 0x3);
108
22.9k
  assert(code <= 0xff);
109
22.9k
  assert(mid >= 0 && mid <= 0xffff);
110
22.9k
#endif /* RIOT_VERSION */
111
112
#if defined(WITH_LWIP) && MEMP_USE_CUSTOM_POOLS
113
#if MEMP_STATS
114
  /* Reserve 1 PDU for a response packet */
115
  if (memp_pools[MEMP_COAP_PDU]->stats->used + 1 >=
116
      memp_pools[MEMP_COAP_PDU]->stats->avail) {
117
    memp_pools[MEMP_COAP_PDU]->stats->err++;
118
    return NULL;
119
  }
120
#endif /* MEMP_STATS */
121
#endif /* LWIP && MEMP_USE_CUSTOM_POOLS */
122
22.9k
  pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
123
22.9k
  if (!pdu)
124
0
    return NULL;
125
126
#if COAP_DEFAULT_MAX_PDU_RX_SIZE <= COAP_MAX_MESSAGE_SIZE_TCP16
127
  /* on TCP, the CoAP header will also have a maximum length of 4 bytes */
128
  pdu->max_hdr_size = COAP_PDU_MAX_UDP_HEADER_SIZE;
129
#else
130
22.9k
  pdu->max_hdr_size = COAP_PDU_MAX_TCP_HEADER_SIZE;
131
22.9k
#endif
132
22.9k
  if (size > ((size_t)COAP_DEFAULT_MAX_PDU_RX_SIZE - pdu->max_hdr_size)) {
133
0
    coap_free_type(COAP_PDU, pdu);
134
0
    return NULL;
135
0
  }
136
137
22.9k
  pdu->alloc_size = min(size, COAP_DEFAULT_MTU);
138
#ifdef WITH_LWIP
139
  pdu->pbuf = pbuf_alloc(PBUF_TRANSPORT, pdu->alloc_size + pdu->max_hdr_size, PBUF_RAM);
140
  if (pdu->pbuf == NULL) {
141
    coap_free_type(COAP_PDU, pdu);
142
    return NULL;
143
  }
144
  pdu->token = (uint8_t *)pdu->pbuf->payload + pdu->max_hdr_size;
145
#else /* WITH_LWIP */
146
22.9k
  uint8_t *buf;
147
22.9k
  buf = coap_malloc_type(COAP_PDU_BUF, pdu->alloc_size + pdu->max_hdr_size);
148
22.9k
  if (buf == NULL) {
149
0
    coap_free_type(COAP_PDU, pdu);
150
0
    return NULL;
151
0
  }
152
22.9k
  pdu->token = buf + pdu->max_hdr_size;
153
22.9k
#endif /* WITH_LWIP */
154
22.9k
  coap_pdu_clear(pdu, size);
155
22.9k
  pdu->mid = mid;
156
22.9k
  pdu->type = type;
157
22.9k
  pdu->code = code;
158
22.9k
  return pdu;
159
22.9k
}
160
161
COAP_API coap_pdu_t *
162
coap_new_pdu(coap_pdu_type_t type, coap_pdu_code_t code,
163
0
             coap_session_t *session) {
164
0
  coap_pdu_t *pdu;
165
166
0
  coap_lock_lock(return NULL);
167
0
  pdu = coap_new_pdu_lkd(type, code, session);
168
0
  coap_lock_unlock();
169
0
  return pdu;
170
0
}
171
172
coap_pdu_t *
173
coap_new_pdu_lkd(coap_pdu_type_t type, coap_pdu_code_t code,
174
0
                 coap_session_t *session) {
175
0
  coap_pdu_t *pdu;
176
177
0
  coap_lock_check_locked();
178
0
  pdu = coap_pdu_init(type, code, coap_new_message_id_lkd(session),
179
0
                      coap_session_max_pdu_size_lkd(session));
180
0
  if (!pdu)
181
0
    coap_log_crit("coap_new_pdu: cannot allocate memory for new PDU (size = %" PRIuS ")\n",
182
0
                  coap_session_max_pdu_size_lkd(session));
183
0
  return pdu;
184
0
}
185
186
COAP_API void
187
15.7k
coap_delete_pdu(coap_pdu_t *pdu) {
188
15.7k
  coap_lock_lock(return);
189
15.7k
  coap_delete_pdu_lkd(pdu);
190
15.7k
  coap_lock_unlock();
191
15.7k
}
192
193
void
194
32.6k
coap_delete_pdu_lkd(coap_pdu_t *pdu) {
195
32.6k
  if (pdu != NULL) {
196
23.0k
    if (pdu->ref) {
197
79
      pdu->ref--;
198
79
      return;
199
79
    }
200
#ifdef WITH_LWIP
201
    pbuf_free(pdu->pbuf);
202
#else
203
22.9k
    if (pdu->token != NULL)
204
22.9k
      coap_free_type(COAP_PDU_BUF, pdu->token - pdu->max_hdr_size);
205
22.9k
#endif
206
22.9k
    coap_delete_binary(pdu->data_free);
207
22.9k
    coap_free_type(COAP_PDU, pdu);
208
22.9k
  }
209
32.6k
}
210
211
COAP_API coap_pdu_t *
212
coap_pdu_duplicate(const coap_pdu_t *old_pdu,
213
                   coap_session_t *session,
214
                   size_t token_length,
215
                   const uint8_t *token,
216
0
                   coap_opt_filter_t *drop_options) {
217
0
  coap_pdu_t *new_pdu;
218
219
0
  coap_lock_lock(return NULL);
220
0
  new_pdu = coap_pdu_duplicate_lkd(old_pdu,
221
0
                                   session,
222
0
                                   token_length,
223
0
                                   token,
224
0
                                   drop_options);
225
0
  coap_lock_unlock();
226
0
  return new_pdu;
227
0
}
228
229
230
/*
231
 * Note: This does not include any data, just the token and options
232
 */
233
coap_pdu_t *
234
coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu,
235
                       coap_session_t *session,
236
                       size_t token_length,
237
                       const uint8_t *token,
238
139
                       coap_opt_filter_t *drop_options) {
239
139
#if COAP_CLIENT_SUPPORT
240
139
  uint8_t doing_first = session->doing_first;
241
139
#endif /* COAP_CLIENT_SUPPORT */
242
139
  coap_pdu_t *pdu;
243
244
139
  coap_lock_check_locked();
245
  /*
246
   * Need to make sure that coap_session_max_pdu_size_lkd() immediately
247
   * returns, rather than wait for the first CSM response from remote
248
   * that indicates BERT size (TCP/TLS only) as this may be called early
249
   * the OSCORE logic.
250
   */
251
139
#if COAP_CLIENT_SUPPORT
252
139
  session->doing_first = 0;
253
139
#endif /* COAP_CLIENT_SUPPORT */
254
139
  pdu = coap_pdu_init(old_pdu->type, old_pdu->code,
255
139
                      coap_new_message_id_lkd(session),
256
139
                      max(old_pdu->max_size,
257
139
                          coap_session_max_pdu_size_lkd(session)));
258
139
#if COAP_CLIENT_SUPPORT
259
  /* Restore any pending waits */
260
139
  session->doing_first = doing_first;
261
139
#endif /* COAP_CLIENT_SUPPORT */
262
139
  if (pdu == NULL)
263
0
    return NULL;
264
265
139
  coap_add_token(pdu, token_length, token);
266
139
  pdu->lg_xmit = old_pdu->lg_xmit;
267
268
139
  if (drop_options == NULL) {
269
    /* Drop COAP_PAYLOAD_START as well if data */
270
139
    size_t length = old_pdu->used_size - old_pdu->e_token_length -
271
139
                    (old_pdu->data ?
272
139
                     old_pdu->used_size - (old_pdu->data - old_pdu->token) +1 : 0);
273
139
    if (!coap_pdu_resize(pdu, length + pdu->e_token_length))
274
0
      goto fail;
275
    /* Copy the options but not any data across */
276
139
    memcpy(pdu->token + pdu->e_token_length,
277
139
           old_pdu->token + old_pdu->e_token_length, length);
278
139
    pdu->used_size += length;
279
139
    pdu->max_opt = old_pdu->max_opt;
280
139
  } else {
281
    /* Copy across all the options the slow way */
282
0
    coap_opt_iterator_t opt_iter;
283
0
    coap_opt_t *option;
284
285
0
    coap_option_iterator_init(old_pdu, &opt_iter, COAP_OPT_ALL);
286
0
    while ((option = coap_option_next(&opt_iter))) {
287
0
      if (drop_options && coap_option_filter_get(drop_options, opt_iter.number))
288
0
        continue;
289
0
      if (!coap_add_option_internal(pdu, opt_iter.number,
290
0
                                    coap_opt_length(option),
291
0
                                    coap_opt_value(option)))
292
0
        goto fail;
293
0
    }
294
0
  }
295
139
  return pdu;
296
297
0
fail:
298
0
  coap_delete_pdu_lkd(pdu);
299
0
  return NULL;
300
139
}
301
302
303
/*
304
 * The new size does not include the coap header (max_hdr_size)
305
 */
306
int
307
15.5k
coap_pdu_resize(coap_pdu_t *pdu, size_t new_size) {
308
15.5k
  if (new_size > pdu->alloc_size) {
309
    /* Expanding the PDU usage */
310
2.10k
#if !defined(WITH_LWIP)
311
2.10k
    uint8_t *new_hdr;
312
#else /* WITH_LWIP */
313
    struct pbuf *new_hdr;
314
#endif /* WITH_LWIP */
315
2.10k
    size_t offset;
316
317
2.10k
    if (pdu->max_size && new_size > pdu->max_size) {
318
140
      coap_log_warn("coap_pdu_resize: pdu too big\n");
319
140
      return 0;
320
140
    }
321
1.96k
    if (pdu->data != NULL) {
322
0
      assert(pdu->data > pdu->token);
323
0
      offset = pdu->data - pdu->token;
324
1.96k
    } else {
325
1.96k
      offset = 0;
326
1.96k
    }
327
1.96k
#if !defined(WITH_LWIP)
328
1.96k
    new_hdr = (uint8_t *)coap_realloc_type(COAP_PDU_BUF,
329
1.96k
                                           pdu->token - pdu->max_hdr_size,
330
1.96k
                                           new_size + pdu->max_hdr_size);
331
#else /* WITH_LWIP */
332
    new_hdr = pbuf_alloc(PBUF_TRANSPORT, new_size + pdu->max_hdr_size, PBUF_RAM);
333
#endif /* WITH_LWIP */
334
1.96k
    if (new_hdr == NULL) {
335
0
      coap_log_warn("coap_pdu_resize: realloc failed\n");
336
0
      return 0;
337
0
    }
338
1.96k
#if !defined(WITH_LWIP)
339
1.96k
    pdu->token = new_hdr + pdu->max_hdr_size;
340
#else /* WITH_LWIP */
341
    memcpy(new_hdr->payload, pdu->pbuf->payload, pdu->pbuf->len);
342
    pbuf_free(pdu->pbuf);
343
    pdu->pbuf = new_hdr;
344
    pdu->token = (uint8_t *)pdu->pbuf->payload + pdu->max_hdr_size;
345
#endif /* WITH_LWIP */
346
1.96k
    if (offset > 0)
347
0
      pdu->data = pdu->token + offset;
348
1.96k
    else
349
1.96k
      pdu->data = NULL;
350
1.96k
    if (pdu->actual_token.length < COAP_TOKEN_EXT_1B_BIAS)
351
1.96k
      pdu->actual_token.s = &pdu->token[0];
352
0
    else if (pdu->actual_token.length < COAP_TOKEN_EXT_2B_BIAS)
353
0
      pdu->actual_token.s = &pdu->token[1];
354
0
    else
355
0
      pdu->actual_token.s = &pdu->token[2];
356
1.96k
    pdu->alloc_size = new_size;
357
1.96k
  }
358
15.4k
  return 1;
359
15.5k
}
360
361
int
362
9.81k
coap_pdu_check_resize(coap_pdu_t *pdu, size_t size) {
363
9.81k
  if (size > pdu->alloc_size) {
364
13
    size_t new_size = max(256, pdu->alloc_size * 2);
365
14
    while (size > new_size)
366
1
      new_size *= 2;
367
13
    if (pdu->max_size && new_size > pdu->max_size) {
368
13
      new_size = pdu->max_size;
369
13
      if (new_size < size)
370
13
        return 0;
371
13
    }
372
0
    if (!coap_pdu_resize(pdu, new_size))
373
0
      return 0;
374
0
  }
375
9.80k
  return 1;
376
9.81k
}
377
378
int
379
5.37k
coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) {
380
5.37k
  size_t bias = 0;
381
382
  /* must allow for pdu == NULL as callers may rely on this */
383
5.37k
  if (!pdu)
384
0
    return 0;
385
386
5.37k
  if (pdu->used_size) {
387
0
    coap_log_warn("coap_add_token: The token must defined first. Token ignored\n");
388
0
    return 0;
389
0
  }
390
5.37k
  pdu->actual_token.length = len;
391
5.37k
  if (len < COAP_TOKEN_EXT_1B_BIAS) {
392
5.29k
    bias = 0;
393
5.29k
  } else if (len < COAP_TOKEN_EXT_2B_BIAS) {
394
39
    bias = 1;
395
39
  } else if (len <= COAP_TOKEN_EXT_MAX) {
396
37
    bias = 2;
397
37
  } else {
398
0
    coap_log_warn("coap_add_token: Token size too large. Token ignored\n");
399
0
    return 0;
400
0
  }
401
5.37k
  if (!coap_pdu_check_resize(pdu, len + bias)) {
402
13
    coap_log_warn("coap_add_token: Insufficient space for token. Token ignored\n");
403
13
    return 0;
404
13
  }
405
406
5.36k
  pdu->actual_token.length = len;
407
5.36k
  pdu->actual_token.s = &pdu->token[bias];
408
5.36k
  pdu->e_token_length = (uint32_t)(len + bias);
409
5.36k
  if (len) {
410
5.33k
    switch (bias) {
411
5.27k
    case 0:
412
5.27k
      memcpy(pdu->token, data, len);
413
5.27k
      break;
414
39
    case 1:
415
39
      pdu->token[0] = (uint8_t)(len - COAP_TOKEN_EXT_1B_BIAS);
416
39
      memcpy(&pdu->token[1], data, len);
417
39
      break;
418
24
    case 2:
419
24
      pdu->token[0] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) >> 8);
420
24
      pdu->token[1] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) & 0xff);
421
24
      memcpy(&pdu->token[2], data, len);
422
24
      break;
423
0
    default:
424
0
      break;
425
5.33k
    }
426
5.33k
  }
427
5.36k
  pdu->max_opt = 0;
428
5.36k
  pdu->used_size = len + bias;
429
5.36k
  pdu->data = NULL;
430
431
5.36k
  return 1;
432
5.36k
}
433
434
/* It is assumed that coap_encode_var_safe8() has been called to reduce data */
435
int
436
0
coap_update_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) {
437
0
  size_t bias = 0;
438
0
  size_t old_len;
439
440
  /* must allow for pdu == NULL as callers may rely on this */
441
0
  if (!pdu)
442
0
    return 0;
443
444
0
  if (pdu->used_size == 0) {
445
0
    return coap_add_token(pdu, len, data);
446
0
  }
447
448
0
  old_len = pdu->e_token_length;
449
450
0
  if (len < COAP_TOKEN_EXT_1B_BIAS) {
451
0
    bias = 0;
452
0
  } else if (len < COAP_TOKEN_EXT_2B_BIAS) {
453
0
    bias = 1;
454
0
  } else if (len <= COAP_TOKEN_EXT_MAX) {
455
0
    bias = 2;
456
0
  } else {
457
0
    coap_log_warn("coap_add_token: Token size too large. Token ignored\n");
458
0
    return 0;
459
0
  }
460
0
  if ((len + bias) == pdu->e_token_length) {
461
    /* Easy case - just data has changed */
462
0
  } else if ((len + bias) > pdu->e_token_length) {
463
0
    if (!coap_pdu_check_resize(pdu,
464
0
                               pdu->used_size + (len + bias) - pdu->e_token_length)) {
465
0
      coap_log_warn("Failed to update token\n");
466
0
      return 0;
467
0
    }
468
0
    memmove(&pdu->token[(len + bias) - pdu->e_token_length],
469
0
            pdu->token, pdu->used_size);
470
0
    pdu->used_size += len + bias - pdu->e_token_length;
471
0
    if (pdu->data) {
472
0
      pdu->data += (len + bias) - pdu->e_token_length;
473
0
    }
474
0
  } else {
475
0
    pdu->used_size -= pdu->e_token_length - (len + bias);
476
0
    memmove(pdu->token, &pdu->token[pdu->e_token_length - (len + bias)], pdu->used_size);
477
0
    if (pdu->data) {
478
0
      pdu->data -= pdu->e_token_length - (len + bias);
479
0
    }
480
0
  }
481
482
0
  pdu->actual_token.length = len;
483
0
  pdu->actual_token.s = &pdu->token[bias];
484
0
  pdu->e_token_length = (uint8_t)(len + bias);
485
0
  if (len) {
486
0
    switch (bias) {
487
0
    case 0:
488
0
      if (memcmp(pdu->token, data, len) != 0)
489
0
        memcpy(pdu->token, data, len);
490
0
      break;
491
0
    case 1:
492
0
      pdu->token[0] = (uint8_t)(len - COAP_TOKEN_EXT_1B_BIAS);
493
0
      memcpy(&pdu->token[1], data, len);
494
0
      break;
495
0
    case 2:
496
0
      pdu->token[0] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) >> 8);
497
0
      pdu->token[1] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) & 0xff);
498
0
      memcpy(&pdu->token[2], data, len);
499
0
      break;
500
0
    default:
501
0
      break;
502
0
    }
503
0
  }
504
0
  if (old_len != pdu->e_token_length && pdu->hdr_size && pdu->session)
505
    /* Need to fix up the header */
506
0
    if (!coap_pdu_encode_header(pdu, pdu->session->proto))
507
0
      return 0;
508
0
  return 1;
509
0
}
510
511
int
512
384
coap_remove_option(coap_pdu_t *pdu, coap_option_num_t number) {
513
384
  coap_opt_iterator_t opt_iter;
514
384
  coap_opt_t *option;
515
384
  coap_opt_t *next_option = NULL;
516
384
  size_t opt_delta;
517
384
  coap_option_t decode_this;
518
384
  coap_option_t decode_next;
519
520
  /* Need to locate where in current options to remove this one */
521
384
  coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
522
384
  while ((option = coap_option_next(&opt_iter))) {
523
0
    if (opt_iter.number == number) {
524
      /* Found option to delete */
525
0
      break;
526
0
    }
527
0
  }
528
384
  if (!option)
529
384
    return 0;
530
531
0
  if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token),
532
0
                      &decode_this))
533
0
    return 0;
534
535
0
  next_option = coap_option_next(&opt_iter);
536
0
  if (next_option) {
537
0
    if (!coap_opt_parse(next_option,
538
0
                        pdu->used_size - (next_option - pdu->token),
539
0
                        &decode_next))
540
0
      return 0;
541
0
    opt_delta = decode_this.delta + decode_next.delta;
542
0
    if (opt_delta < 13) {
543
      /* can simply update the delta of next option */
544
0
      next_option[0] = (next_option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
545
0
    } else if (opt_delta < 269 && decode_next.delta < 13) {
546
      /* next option delta size increase */
547
0
      next_option -= 1;
548
0
      next_option[0] = (next_option[1] & 0x0f) + (13 << 4);
549
0
      next_option[1] = (coap_opt_t)(opt_delta - 13);
550
0
    } else if (opt_delta < 269) {
551
      /* can simply update the delta of next option */
552
0
      next_option[1] = (coap_opt_t)(opt_delta - 13);
553
0
    } else if (decode_next.delta < 13) { /* opt_delta >= 269 */
554
      /* next option delta size increase */
555
0
      if (next_option - option < 2) {
556
        /* Need to shuffle everything up by 1 before decrement */
557
0
        if (!coap_pdu_check_resize(pdu, pdu->used_size + 1))
558
0
          return 0;
559
        /* Possible a re-size took place with a realloc() */
560
        /* Need to rediscover this and next options */
561
0
        coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
562
0
        while ((option = coap_option_next(&opt_iter))) {
563
0
          if (opt_iter.number == number) {
564
            /* Found option to delete */
565
0
            break;
566
0
          }
567
0
        }
568
0
        next_option = coap_option_next(&opt_iter);
569
0
        assert(option != NULL);
570
0
        assert(next_option != NULL);
571
0
        memmove(&next_option[1], next_option,
572
0
                pdu->used_size - (next_option - pdu->token));
573
0
        pdu->used_size++;
574
0
        if (pdu->data)
575
0
          pdu->data++;
576
0
        next_option++;
577
0
      }
578
0
      next_option -= 2;
579
0
      next_option[0] = (next_option[2] & 0x0f) + (14 << 4);
580
0
      next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
581
0
      next_option[2] = (opt_delta - 269) & 0xff;
582
0
    } else if (decode_next.delta < 269) { /* opt_delta >= 269 */
583
      /* next option delta size increase */
584
0
      next_option -= 1;
585
0
      next_option[0] = (next_option[1] & 0x0f) + (14 << 4);
586
0
      next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
587
0
      next_option[2] = (opt_delta - 269) & 0xff;
588
0
    } else { /* decode_next.delta >= 269 && opt_delta >= 269 */
589
0
      next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
590
0
      next_option[2] = (opt_delta - 269) & 0xff;
591
0
    }
592
0
  } else {
593
0
    next_option = option + coap_opt_encode_size(decode_this.delta,
594
0
                                                coap_opt_length(option));
595
0
    pdu->max_opt -= decode_this.delta;
596
0
  }
597
0
  if (pdu->used_size - (next_option - pdu->token))
598
0
    memmove(option, next_option, pdu->used_size - (next_option - pdu->token));
599
0
  pdu->used_size -= next_option - option;
600
0
  if (pdu->data)
601
0
    pdu->data -= next_option - option;
602
0
  return 1;
603
0
}
604
605
int
606
11.5k
coap_option_check_repeatable(coap_option_num_t number) {
607
  /* Validate that the option is repeatable */
608
11.5k
  switch (number) {
609
  /* Ignore list of genuine repeatable */
610
460
  case COAP_OPTION_IF_MATCH:
611
666
  case COAP_OPTION_ETAG:
612
956
  case COAP_OPTION_LOCATION_PATH:
613
3.12k
  case COAP_OPTION_URI_PATH:
614
3.37k
  case COAP_OPTION_URI_QUERY:
615
3.59k
  case COAP_OPTION_LOCATION_QUERY:
616
3.79k
  case COAP_OPTION_Q_BLOCK2:
617
3.99k
  case COAP_OPTION_RTAG:
618
3.99k
    break;
619
  /* Protest at the known non-repeatable options and ignore them */
620
227
  case COAP_OPTION_URI_HOST:
621
481
  case COAP_OPTION_IF_NONE_MATCH:
622
690
  case COAP_OPTION_OBSERVE:
623
1.05k
  case COAP_OPTION_URI_PORT:
624
1.79k
  case COAP_OPTION_OSCORE:
625
2.03k
  case COAP_OPTION_CONTENT_FORMAT:
626
2.23k
  case COAP_OPTION_MAXAGE:
627
2.49k
  case COAP_OPTION_HOP_LIMIT:
628
2.72k
  case COAP_OPTION_ACCEPT:
629
3.05k
  case COAP_OPTION_Q_BLOCK1:
630
3.35k
  case COAP_OPTION_EDHOC:
631
3.59k
  case COAP_OPTION_BLOCK2:
632
3.79k
  case COAP_OPTION_BLOCK1:
633
3.99k
  case COAP_OPTION_SIZE2:
634
4.20k
  case COAP_OPTION_PROXY_URI:
635
4.43k
  case COAP_OPTION_PROXY_SCHEME:
636
4.62k
  case COAP_OPTION_SIZE1:
637
4.83k
  case COAP_OPTION_ECHO:
638
5.02k
  case COAP_OPTION_NORESPONSE:
639
5.02k
    coap_log_info("Option number %d is not defined as repeatable - dropped\n",
640
5.02k
                  number);
641
5.02k
    return 0;
642
2.54k
  default:
643
2.54k
    coap_log_info("Option number %d is not defined as repeatable\n",
644
2.54k
                  number);
645
    /* Accepting it after warning as there may be user defineable options */
646
2.54k
    break;
647
11.5k
  }
648
6.54k
  return 1;
649
11.5k
}
650
651
size_t
652
coap_insert_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
653
2.22k
                   const uint8_t *data) {
654
2.22k
  coap_opt_iterator_t opt_iter;
655
2.22k
  coap_opt_t *option;
656
2.22k
  uint16_t prev_number = 0;
657
2.22k
  size_t shift;
658
2.22k
  size_t opt_delta;
659
2.22k
  coap_option_t decode;
660
2.22k
  size_t shrink = 0;
661
662
2.22k
  if (number >= pdu->max_opt)
663
2.18k
    return coap_add_option_internal(pdu, number, len, data);
664
665
  /* Need to locate where in current options to insert this one */
666
31
  coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
667
42
  while ((option = coap_option_next(&opt_iter))) {
668
42
    if (opt_iter.number > number) {
669
      /* Found where to insert */
670
31
      break;
671
31
    }
672
11
    prev_number = opt_iter.number;
673
11
  }
674
31
  if (option == NULL) {
675
    /* Code is broken somewhere */
676
0
    coap_log_warn("coap_insert_option: Broken max_opt\n");
677
0
    return 0;
678
0
  }
679
680
  /* size of option inc header to insert */
681
31
  shift = coap_opt_encode_size(number - prev_number, len);
682
683
  /* size of next option (header may shrink in size as delta changes */
684
31
  if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token), &decode))
685
0
    return 0;
686
31
  opt_delta = opt_iter.number - number;
687
31
  if (opt_delta == 0) {
688
0
    if (!coap_option_check_repeatable(number))
689
0
      return 0;
690
0
  }
691
692
31
  if (!coap_pdu_check_resize(pdu,
693
31
                             pdu->used_size + shift - shrink))
694
0
    return 0;
695
696
  /* Possible a re-size took place with a realloc() */
697
  /* Need to locate where in current options to insert this one */
698
31
  coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
699
42
  while ((option = coap_option_next(&opt_iter))) {
700
42
    if (opt_iter.number > number) {
701
      /* Found where to insert */
702
31
      break;
703
31
    }
704
42
  }
705
31
  assert(option != NULL);
706
707
31
  if (decode.delta < 13) {
708
    /* can simply patch in the new delta of next option */
709
21
    option[0] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
710
21
  } else if (decode.delta < 269 && opt_delta < 13) {
711
    /* option header is going to shrink by one byte */
712
1
    option[1] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
713
1
    shrink = 1;
714
9
  } else if (decode.delta < 269 && opt_delta < 269) {
715
    /* can simply patch in the new delta of next option */
716
9
    option[1] = (coap_opt_t)(opt_delta - 13);
717
9
  } else if (opt_delta < 13) {
718
    /* option header is going to shrink by two bytes */
719
0
    option[2] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
720
0
    shrink = 2;
721
0
  } else if (opt_delta < 269) {
722
    /* option header is going to shrink by one bytes */
723
0
    option[1] = (option[0] & 0x0f) + 0xd0;
724
0
    option[2] = (coap_opt_t)(opt_delta - 13);
725
0
    shrink = 1;
726
0
  } else {
727
    /* can simply patch in the new delta of next option */
728
0
    option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
729
0
    option[2] = (opt_delta - 269) & 0xff;
730
0
  }
731
732
31
  memmove(&option[shift], &option[shrink],
733
31
          pdu->used_size - (option - pdu->token) - shrink);
734
31
  if (!coap_opt_encode(option, pdu->alloc_size - pdu->used_size,
735
31
                       number - prev_number, data, len))
736
0
    return 0;
737
738
31
  if (shift >= shrink) {
739
31
    pdu->used_size += shift - shrink;
740
31
    if (pdu->data)
741
0
      pdu->data += shift - shrink;
742
31
  } else {
743
0
    pdu->used_size -= shrink - shift;
744
0
    if (pdu->data)
745
0
      pdu->data -= shrink - shift;
746
0
  }
747
31
  return shift;
748
31
}
749
750
size_t
751
coap_update_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
752
213
                   const uint8_t *data) {
753
213
  coap_opt_iterator_t opt_iter;
754
213
  coap_opt_t *option;
755
213
  coap_option_t decode;
756
213
  size_t new_length = 0;
757
213
  size_t old_length = 0;
758
759
213
  option = coap_check_option(pdu, number, &opt_iter);
760
213
  if (!option)
761
155
    return coap_insert_option(pdu, number, len, data);
762
763
58
  old_length = coap_opt_parse(option, (size_t)-1, &decode);
764
58
  if (old_length == 0)
765
0
    return 0;
766
58
  new_length = coap_opt_encode_size(decode.delta, len);
767
768
58
  if (new_length > old_length) {
769
0
    if (!coap_pdu_check_resize(pdu,
770
0
                               pdu->used_size + new_length - old_length))
771
0
      return 0;
772
    /* Possible a re-size took place with a realloc() */
773
0
    option = coap_check_option(pdu, number, &opt_iter);
774
0
  }
775
776
58
  if (new_length != old_length)
777
35
    memmove(&option[new_length], &option[old_length],
778
35
            pdu->used_size - (option - pdu->token) - old_length);
779
780
58
  if (!coap_opt_encode(option, new_length,
781
58
                       decode.delta, data, len))
782
0
    return 0;
783
784
58
  if (new_length >= old_length) {
785
23
    pdu->used_size += new_length - old_length;
786
23
    if (pdu->data)
787
13
      pdu->data += new_length - old_length;
788
35
  } else {
789
35
    pdu->used_size -= old_length - new_length;
790
35
    if (pdu->data)
791
11
      pdu->data -= old_length - new_length;
792
35
  }
793
58
  return 1;
794
58
}
795
796
size_t
797
coap_add_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
798
2.14k
                const uint8_t *data) {
799
2.14k
  if (pdu->data) {
800
0
    coap_log_warn("coap_add_optlist_pdu: PDU already contains data\n");
801
0
    return 0;
802
0
  }
803
2.14k
  return coap_add_option_internal(pdu, number, len, data);
804
2.14k
}
805
806
size_t
807
coap_add_option_internal(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
808
4.41k
                         const uint8_t *data) {
809
4.41k
  size_t optsize;
810
4.41k
  coap_opt_t *opt;
811
812
4.41k
  assert(pdu);
813
814
4.41k
  if (number == pdu->max_opt) {
815
0
    if (!coap_option_check_repeatable(number))
816
0
      return 0;
817
0
  }
818
819
4.41k
  if (COAP_PDU_IS_REQUEST(pdu) &&
820
2.29k
      (number == COAP_OPTION_PROXY_URI ||
821
2.29k
       number == COAP_OPTION_PROXY_SCHEME)) {
822
    /*
823
     * Need to check whether there is a hop-limit option.  If not, it needs
824
     * to be inserted by default (RFC 8768).
825
     */
826
0
    coap_opt_iterator_t opt_iter;
827
828
0
    if (coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter) == NULL) {
829
0
      size_t hop_limit = COAP_OPTION_HOP_LIMIT;
830
831
0
      coap_insert_option(pdu, COAP_OPTION_HOP_LIMIT, 1, (uint8_t *)&hop_limit);
832
0
    }
833
0
  }
834
835
4.41k
  if (number < pdu->max_opt) {
836
0
    coap_log_debug("coap_add_option: options are not in correct order\n");
837
0
    return coap_insert_option(pdu, number, len, data);
838
0
  }
839
840
4.41k
  optsize = coap_opt_encode_size(number - pdu->max_opt, len);
841
4.41k
  if (!coap_pdu_check_resize(pdu,
842
4.41k
                             pdu->used_size + optsize))
843
0
    return 0;
844
845
4.41k
  if (pdu->data) {
846
    /* include option delimiter */
847
18
    memmove(&pdu->data[optsize-1], &pdu->data[-1],
848
18
            pdu->used_size - (pdu->data - pdu->token) + 1);
849
18
    opt = pdu->data -1;
850
18
    pdu->data += optsize;
851
4.39k
  } else {
852
4.39k
    opt = pdu->token + pdu->used_size;
853
4.39k
  }
854
855
  /* encode option and check length */
856
4.41k
  optsize = coap_opt_encode(opt, pdu->alloc_size - pdu->used_size,
857
4.41k
                            number - pdu->max_opt, data, len);
858
859
4.41k
  if (!optsize) {
860
0
    coap_log_warn("coap_add_option: cannot add option\n");
861
    /* error */
862
0
    return 0;
863
4.41k
  } else {
864
4.41k
    pdu->max_opt = number;
865
4.41k
    pdu->used_size += optsize;
866
4.41k
  }
867
868
4.41k
  return optsize;
869
4.41k
}
870
871
int
872
1.64k
coap_add_data(coap_pdu_t *pdu, size_t len, const uint8_t *data) {
873
1.64k
  if (len == 0) {
874
0
    return 1;
875
1.64k
  } else {
876
1.64k
    uint8_t *payload = coap_add_data_after(pdu, len);
877
1.64k
    if (payload != NULL)
878
1.60k
      memcpy(payload, data, len);
879
1.64k
    return payload != NULL;
880
1.64k
  }
881
1.64k
}
882
883
uint8_t *
884
1.64k
coap_add_data_after(coap_pdu_t *pdu, size_t len) {
885
1.64k
  assert(pdu);
886
1.64k
  if (pdu->data) {
887
0
    coap_log_warn("coap_add_data: PDU already contains data\n");
888
0
    return 0;
889
0
  }
890
891
1.64k
  if (len == 0)
892
0
    return NULL;
893
894
1.64k
  if (!coap_pdu_resize(pdu, pdu->used_size + len + 1))
895
42
    return 0;
896
1.60k
  pdu->token[pdu->used_size++] = COAP_PAYLOAD_START;
897
1.60k
  pdu->data = pdu->token + pdu->used_size;
898
1.60k
  pdu->used_size += len;
899
1.60k
  return pdu->data;
900
1.64k
}
901
902
int
903
10.2k
coap_get_data(const coap_pdu_t *pdu, size_t *len, const uint8_t **data) {
904
10.2k
  size_t offset;
905
10.2k
  size_t total;
906
907
10.2k
  return coap_get_data_large(pdu, len, data, &offset, &total);
908
10.2k
}
909
910
int
911
coap_get_data_large(const coap_pdu_t *pdu, size_t *len, const uint8_t **data,
912
10.2k
                    size_t *offset, size_t *total) {
913
10.2k
  assert(pdu);
914
10.2k
  assert(len);
915
10.2k
  assert(data);
916
917
10.2k
  *offset = pdu->body_offset;
918
10.2k
  *total = pdu->body_total;
919
10.2k
  if (pdu->body_data) {
920
0
    *data = pdu->body_data;
921
0
    *len = pdu->body_length;
922
0
    return 1;
923
0
  }
924
10.2k
  *data = pdu->data;
925
10.2k
  if (pdu->data == NULL) {
926
7.86k
    *len = 0;
927
7.86k
    *total = 0;
928
7.86k
    return 0;
929
7.86k
  }
930
931
2.38k
  *len = pdu->used_size - (pdu->data - pdu->token);
932
2.38k
  if (*total == 0)
933
2.38k
    *total = *len;
934
935
2.38k
  return 1;
936
10.2k
}
937
938
#ifndef SHORT_ERROR_RESPONSE
939
typedef struct {
940
  unsigned char code;
941
  const char *phrase;
942
} error_desc_t;
943
944
/* if you change anything here, make sure, that the longest string does not
945
 * exceed COAP_ERROR_PHRASE_LENGTH. */
946
error_desc_t coap_error[] = {
947
  { COAP_RESPONSE_CODE(201), "Created" },
948
  { COAP_RESPONSE_CODE(202), "Deleted" },
949
  { COAP_RESPONSE_CODE(203), "Valid" },
950
  { COAP_RESPONSE_CODE(204), "Changed" },
951
  { COAP_RESPONSE_CODE(205), "Content" },
952
  { COAP_RESPONSE_CODE(231), "Continue" },
953
  { COAP_RESPONSE_CODE(400), "Bad Request" },
954
  { COAP_RESPONSE_CODE(401), "Unauthorized" },
955
  { COAP_RESPONSE_CODE(402), "Bad Option" },
956
  { COAP_RESPONSE_CODE(403), "Forbidden" },
957
  { COAP_RESPONSE_CODE(404), "Not Found" },
958
  { COAP_RESPONSE_CODE(405), "Method Not Allowed" },
959
  { COAP_RESPONSE_CODE(406), "Not Acceptable" },
960
  { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
961
  { COAP_RESPONSE_CODE(409), "Conflict" },
962
  { COAP_RESPONSE_CODE(412), "Precondition Failed" },
963
  { COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
964
  { COAP_RESPONSE_CODE(415), "Unsupported Content-Format" },
965
  { COAP_RESPONSE_CODE(422), "Unprocessable" },
966
  { COAP_RESPONSE_CODE(429), "Too Many Requests" },
967
  { COAP_RESPONSE_CODE(500), "Internal Server Error" },
968
  { COAP_RESPONSE_CODE(501), "Not Implemented" },
969
  { COAP_RESPONSE_CODE(502), "Bad Gateway" },
970
  { COAP_RESPONSE_CODE(503), "Service Unavailable" },
971
  { COAP_RESPONSE_CODE(504), "Gateway Timeout" },
972
  { COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
973
  { COAP_RESPONSE_CODE(508), "Hop Limit Reached" },
974
  { 0, NULL }                        /* end marker */
975
};
976
977
const char *
978
1.49k
coap_response_phrase(unsigned char code) {
979
1.49k
  int i;
980
16.5k
  for (i = 0; coap_error[i].code; ++i) {
981
16.5k
    if (coap_error[i].code == code)
982
1.49k
      return coap_error[i].phrase;
983
16.5k
  }
984
0
  return NULL;
985
1.49k
}
986
#endif
987
988
/**
989
 * Advances *optp to next option if still in PDU. This function
990
 * returns the number of bytes opt has been advanced or @c 0
991
 * on error.
992
 */
993
static size_t
994
41.1M
next_option_safe(coap_opt_t **optp, size_t *length, uint16_t *max_opt) {
995
41.1M
  coap_option_t option;
996
41.1M
  size_t optsize;
997
998
41.1M
  assert(optp);
999
41.1M
  assert(*optp);
1000
41.1M
  assert(length);
1001
1002
41.1M
  optsize = coap_opt_parse(*optp, *length, &option);
1003
41.1M
  assert(optsize <= *length);
1004
1005
  /* signal an error if this option would exceed the
1006
   * allowed number space */
1007
41.1M
  if ((uint32_t)(*max_opt) + option.delta > COAP_MAX_OPT) {
1008
135
    return 0;
1009
135
  }
1010
41.1M
  *max_opt += option.delta;
1011
41.1M
  *optp += optsize;
1012
41.1M
  *length -= optsize;
1013
1014
41.1M
  return optsize;
1015
41.1M
}
1016
1017
size_t
1018
coap_pdu_parse_header_size(coap_proto_t proto,
1019
13.8k
                           const uint8_t *data) {
1020
13.8k
  assert(data);
1021
13.8k
  size_t header_size = 0;
1022
1023
13.8k
  if (proto == COAP_PROTO_TCP || proto==COAP_PROTO_TLS) {
1024
3.84k
    uint8_t len = *data >> 4;
1025
3.84k
    if (len < 13)
1026
3.74k
      header_size = 2;
1027
96
    else if (len==13)
1028
37
      header_size = 3;
1029
59
    else if (len==14)
1030
24
      header_size = 4;
1031
35
    else
1032
35
      header_size = 6;
1033
9.95k
  } else if (proto == COAP_PROTO_WS || proto==COAP_PROTO_WSS) {
1034
3.74k
    header_size = 2;
1035
6.21k
  } else if (proto == COAP_PROTO_UDP || proto==COAP_PROTO_DTLS) {
1036
6.21k
    header_size = 4;
1037
6.21k
  }
1038
1039
13.8k
  return header_size;
1040
13.8k
}
1041
1042
#if !COAP_DISABLE_TCP
1043
/*
1044
 * strm
1045
 * return +ve  PDU size including token
1046
 *          0  PDU does not parse
1047
 */
1048
size_t
1049
coap_pdu_parse_size(coap_proto_t proto,
1050
                    const uint8_t *data,
1051
0
                    size_t length) {
1052
0
  assert(data);
1053
0
  assert(proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS ||
1054
0
         proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS);
1055
0
  assert(coap_pdu_parse_header_size(proto, data) <= length);
1056
1057
0
  size_t size = 0;
1058
0
  const uint8_t *token_start = NULL;
1059
1060
0
  if ((proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) && length >= 1) {
1061
0
    uint8_t len = *data >> 4;
1062
0
    uint8_t tkl = *data & 0x0f;
1063
1064
0
    if (len < 13) {
1065
0
      size = len;
1066
0
      token_start = &data[2];
1067
0
    } else if (length >= 2) {
1068
0
      if (len==13) {
1069
0
        size = (size_t)data[1] + COAP_MESSAGE_SIZE_OFFSET_TCP8;
1070
0
        token_start = &data[3];
1071
0
      } else if (length >= 3) {
1072
0
        if (len==14) {
1073
0
          size = ((size_t)data[1] << 8) + data[2] + COAP_MESSAGE_SIZE_OFFSET_TCP16;
1074
0
          token_start = &data[4];
1075
0
        } else if (length >= 5) {
1076
0
          size = ((size_t)data[1] << 24) + ((size_t)data[2] << 16)
1077
0
                 + ((size_t)data[3] << 8) + data[4] + COAP_MESSAGE_SIZE_OFFSET_TCP32;
1078
0
          token_start = &data[6];
1079
0
        }
1080
0
      }
1081
0
    }
1082
0
    if (token_start) {
1083
      /* account for the token length */
1084
0
      if (tkl < COAP_TOKEN_EXT_1B_TKL) {
1085
0
        size += tkl;
1086
0
      } else if (tkl == COAP_TOKEN_EXT_1B_TKL) {
1087
0
        size += token_start[0] + COAP_TOKEN_EXT_1B_BIAS + 1;
1088
0
      } else if (tkl == COAP_TOKEN_EXT_2B_TKL) {
1089
0
        size += ((uint16_t)token_start[0] << 8) + token_start[1] +
1090
0
                COAP_TOKEN_EXT_2B_BIAS + 2;
1091
0
      } else {
1092
        /* Invalid at this point - caught later as undersized */
1093
0
      }
1094
0
    }
1095
0
  }
1096
1097
0
  return size;
1098
0
}
1099
#endif /* ! COAP_DISABLE_TCP */
1100
1101
int
1102
13.6k
coap_pdu_parse_header(coap_pdu_t *pdu, coap_proto_t proto) {
1103
13.6k
  uint8_t *hdr = pdu->token - pdu->hdr_size;
1104
13.6k
  uint8_t e_token_length;
1105
1106
13.6k
  if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) {
1107
6.11k
    assert(pdu->hdr_size == 4);
1108
6.11k
    if ((hdr[0] >> 6) != COAP_DEFAULT_VERSION) {
1109
12
      coap_log_debug("coap_pdu_parse: UDP version not supported\n");
1110
12
      return 0;
1111
12
    }
1112
6.10k
    pdu->type = (hdr[0] >> 4) & 0x03;
1113
6.10k
    pdu->code = hdr[1];
1114
6.10k
    pdu->mid = (uint16_t)hdr[2] << 8 | hdr[3];
1115
7.57k
  } else if (proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) {
1116
3.83k
    assert(pdu->hdr_size >= 2 && pdu->hdr_size <= 6);
1117
3.83k
    pdu->type = COAP_MESSAGE_CON;
1118
3.83k
    pdu->code = hdr[pdu->hdr_size-1];
1119
3.83k
    pdu->mid = 0;
1120
3.83k
  } else if (proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS) {
1121
3.74k
    assert(pdu->hdr_size == 2);
1122
3.74k
    pdu->type = COAP_MESSAGE_CON;
1123
3.74k
    pdu->code = hdr[pdu->hdr_size-1];
1124
3.74k
    pdu->mid = 0;
1125
3.74k
  } else {
1126
0
    coap_log_debug("coap_pdu_parse: unsupported protocol\n");
1127
0
    return 0;
1128
0
  }
1129
1130
13.6k
  e_token_length = hdr[0] & 0x0f;
1131
13.6k
  if (e_token_length < COAP_TOKEN_EXT_1B_TKL) {
1132
13.2k
    pdu->e_token_length = e_token_length;
1133
13.2k
    pdu->actual_token.length = pdu->e_token_length;
1134
13.2k
    pdu->actual_token.s = &pdu->token[0];
1135
13.2k
  } else if (e_token_length == COAP_TOKEN_EXT_1B_TKL) {
1136
163
    pdu->e_token_length = pdu->token[0] + COAP_TOKEN_EXT_1B_BIAS + 1;
1137
163
    pdu->actual_token.length = pdu->e_token_length - 1;
1138
163
    pdu->actual_token.s = &pdu->token[1];
1139
268
  } else if (e_token_length == COAP_TOKEN_EXT_2B_TKL) {
1140
249
    pdu->e_token_length = ((uint16_t)pdu->token[0] << 8) + pdu->token[1] +
1141
249
                          COAP_TOKEN_EXT_2B_BIAS + 2;
1142
249
    pdu->actual_token.length = pdu->e_token_length - 2;
1143
249
    pdu->actual_token.s = &pdu->token[2];
1144
249
  }
1145
13.6k
  if (pdu->e_token_length > pdu->alloc_size || e_token_length == 15) {
1146
    /* Invalid PDU provided - not wise to assert here though */
1147
82
    coap_log_debug("coap_pdu_parse: PDU header token size broken\n");
1148
82
    pdu->e_token_length = 0;
1149
82
    pdu->actual_token.length = 0;
1150
82
    return 0;
1151
82
  }
1152
13.5k
  return 1;
1153
13.6k
}
1154
1155
static int
1156
23.5M
coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) {
1157
23.5M
  switch ((coap_pdu_signaling_proto_t)pdu->code) {
1158
88.2k
  case COAP_SIGNALING_CSM:
1159
88.2k
    switch (pdu->max_opt) {
1160
25.6k
    case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE:
1161
25.6k
      if (len > 4)
1162
4.41k
        goto bad;
1163
21.2k
      break;
1164
21.2k
    case COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER:
1165
6.10k
      if (len > 0)
1166
4.28k
        goto bad;
1167
1.82k
      break;
1168
20.8k
    case COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH:
1169
20.8k
      if (len > 3)
1170
4.32k
        goto bad;
1171
16.5k
      break;
1172
35.6k
    default:
1173
35.6k
      if (pdu->max_opt & 0x01)
1174
23.3k
        goto bad; /* Critical */
1175
88.2k
    }
1176
51.8k
    break;
1177
51.8k
  case COAP_SIGNALING_PING:
1178
122k
  case COAP_SIGNALING_PONG:
1179
122k
    switch (pdu->max_opt) {
1180
6.32k
    case COAP_SIGNALING_OPTION_CUSTODY:
1181
6.32k
      if (len > 0)
1182
4.02k
        goto bad;
1183
2.30k
      break;
1184
115k
    default:
1185
115k
      if (pdu->max_opt & 0x01)
1186
66.1k
        goto bad; /* Critical */
1187
122k
    }
1188
52.0k
    break;
1189
85.8k
  case COAP_SIGNALING_RELEASE:
1190
85.8k
    switch (pdu->max_opt) {
1191
18.6k
    case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS:
1192
18.6k
      if (len < 1 || len > 255)
1193
16.5k
        goto bad;
1194
2.10k
      break;
1195
8.97k
    case COAP_SIGNALING_OPTION_HOLD_OFF:
1196
8.97k
      if (len > 3)
1197
4.22k
        goto bad;
1198
4.75k
      break;
1199
58.1k
    default:
1200
58.1k
      if (pdu->max_opt & 0x01)
1201
40.6k
        goto bad; /* Critical */
1202
85.8k
    }
1203
24.3k
    break;
1204
35.2k
  case COAP_SIGNALING_ABORT:
1205
35.2k
    switch (pdu->max_opt) {
1206
10.0k
    case COAP_SIGNALING_OPTION_BAD_CSM_OPTION:
1207
10.0k
      if (len > 2)
1208
4.95k
        goto bad;
1209
5.10k
      break;
1210
25.2k
    default:
1211
25.2k
      if (pdu->max_opt & 0x01)
1212
17.3k
        goto bad; /* Critical */
1213
35.2k
    }
1214
12.9k
    break;
1215
23.1M
  default:
1216
23.1M
    ;
1217
23.5M
  }
1218
23.3M
  return 1;
1219
190k
bad:
1220
190k
  return 0;
1221
23.5M
}
1222
1223
static int
1224
17.6M
coap_pdu_parse_opt_base(coap_pdu_t *pdu, uint16_t len) {
1225
17.6M
  int res = 1;
1226
1227
17.6M
  switch (pdu->max_opt) {
1228
8.54k
  case COAP_OPTION_IF_MATCH:
1229
8.54k
    if (len > 8)
1230
3.80k
      res = 0;
1231
8.54k
    break;
1232
25.8k
  case COAP_OPTION_URI_HOST:
1233
25.8k
    if (len < 1 || len > 255)
1234
23.5k
      res = 0;
1235
25.8k
    break;
1236
40.0k
  case COAP_OPTION_ETAG:
1237
40.0k
    if (len < 1 || len > 8)
1238
37.9k
      res = 0;
1239
40.0k
    break;
1240
7.15k
  case COAP_OPTION_IF_NONE_MATCH:
1241
7.15k
    if (len != 0)
1242
4.52k
      res = 0;
1243
7.15k
    break;
1244
17.8k
  case COAP_OPTION_OBSERVE:
1245
17.8k
    if (len > 3)
1246
5.19k
      res = 0;
1247
17.8k
    break;
1248
12.3k
  case COAP_OPTION_URI_PORT:
1249
12.3k
    if (len > 2)
1250
4.41k
      res = 0;
1251
12.3k
    break;
1252
11.5k
  case COAP_OPTION_LOCATION_PATH:
1253
11.5k
    if (len > 255)
1254
2.98k
      res = 0;
1255
11.5k
    break;
1256
60.0k
  case COAP_OPTION_OSCORE:
1257
60.0k
    if (len > 255)
1258
3.03k
      res = 0;
1259
60.0k
    break;
1260
6.52M
  case COAP_OPTION_URI_PATH:
1261
6.52M
    if (len > 255)
1262
3.12k
      res = 0;
1263
6.52M
    break;
1264
12.4k
  case COAP_OPTION_CONTENT_FORMAT:
1265
12.4k
    if (len > 2)
1266
5.16k
      res = 0;
1267
12.4k
    break;
1268
9.34k
  case COAP_OPTION_MAXAGE:
1269
9.34k
    if (len > 4)
1270
4.47k
      res = 0;
1271
9.34k
    break;
1272
198k
  case COAP_OPTION_URI_QUERY:
1273
198k
    if (len < 1 || len > 255)
1274
195k
      res = 0;
1275
198k
    break;
1276
37.7k
  case COAP_OPTION_HOP_LIMIT:
1277
37.7k
    if (len != 1)
1278
36.4k
      res = 0;
1279
37.7k
    break;
1280
14.8k
  case COAP_OPTION_ACCEPT:
1281
14.8k
    if (len > 2)
1282
4.28k
      res = 0;
1283
14.8k
    break;
1284
536k
  case COAP_OPTION_Q_BLOCK1:
1285
536k
    if (len > 3)
1286
4.61k
      res = 0;
1287
536k
    break;
1288
44.4k
  case COAP_OPTION_LOCATION_QUERY:
1289
44.4k
    if (len > 255)
1290
3.23k
      res = 0;
1291
44.4k
    break;
1292
9.98k
  case COAP_OPTION_EDHOC:
1293
9.98k
    if (len != 0)
1294
5.30k
      res = 0;
1295
9.98k
    break;
1296
8.61k
  case COAP_OPTION_BLOCK2:
1297
8.61k
    if (len > 3)
1298
4.17k
      res = 0;
1299
8.61k
    break;
1300
11.3k
  case COAP_OPTION_BLOCK1:
1301
11.3k
    if (len > 3)
1302
4.30k
      res = 0;
1303
11.3k
    break;
1304
24.9k
  case COAP_OPTION_SIZE2:
1305
24.9k
    if (len > 4)
1306
4.38k
      res = 0;
1307
24.9k
    break;
1308
37.5k
  case COAP_OPTION_Q_BLOCK2:
1309
37.5k
    if (len > 3)
1310
4.17k
      res = 0;
1311
37.5k
    break;
1312
19.1k
  case COAP_OPTION_PROXY_URI:
1313
19.1k
    if (len < 1 || len > 1034)
1314
13.5k
      res = 0;
1315
19.1k
    break;
1316
21.5k
  case COAP_OPTION_PROXY_SCHEME:
1317
21.5k
    if (len < 1 || len > 255)
1318
19.5k
      res = 0;
1319
21.5k
    break;
1320
6.35k
  case COAP_OPTION_SIZE1:
1321
6.35k
    if (len > 4)
1322
3.83k
      res = 0;
1323
6.35k
    break;
1324
5.95k
  case COAP_OPTION_ECHO:
1325
5.95k
    if (len > 40)
1326
3.38k
      res = 0;
1327
5.95k
    break;
1328
6.44k
  case COAP_OPTION_NORESPONSE:
1329
6.44k
    if (len > 1)
1330
4.12k
      res = 0;
1331
6.44k
    break;
1332
6.32k
  case COAP_OPTION_RTAG:
1333
6.32k
    if (len > 8)
1334
3.53k
      res = 0;
1335
6.32k
    break;
1336
9.89M
  default:
1337
9.89M
    ;
1338
17.6M
  }
1339
17.6M
  return res;
1340
17.6M
}
1341
1342
static int
1343
357k
write_prefix(char **obp, size_t *len, const char *prf, size_t prflen) {
1344
  /* Make sure space for null terminating byte */
1345
357k
  if (*len < prflen +1) {
1346
106
    return 0;
1347
106
  }
1348
1349
357k
  memcpy(*obp, prf, prflen);
1350
357k
  *obp += prflen;
1351
357k
  *len -= prflen;
1352
357k
  return 1;
1353
357k
}
1354
1355
static int
1356
3.00M
write_char(char **obp, size_t *len, int c, int printable) {
1357
  /* Make sure space for null terminating byte */
1358
3.00M
  if (*len < 2 +1) {
1359
2.32k
    return 0;
1360
2.32k
  }
1361
1362
3.00M
  if (!printable) {
1363
1.50M
    const uint8_t hex[] = "0123456789abcdef";
1364
1.50M
    (*obp)[0] = hex[(c & 0xf0) >> 4];
1365
1.50M
    (*obp)[1] = hex[c & 0x0f];
1366
1.50M
  } else {
1367
1.50M
    (*obp)[0] = isprint(c) ? c : '.';
1368
1.50M
    (*obp)[1] = ' ';
1369
1.50M
  }
1370
3.00M
  *obp += 2;
1371
3.00M
  *len -= 2;
1372
3.00M
  return 1;
1373
3.00M
}
1374
1375
int
1376
13.5k
coap_pdu_parse_opt(coap_pdu_t *pdu, coap_opt_filter_t *error_opts) {
1377
13.5k
  int good = 1;
1378
1379
13.5k
  coap_option_filter_clear(error_opts);
1380
1381
  /* sanity checks */
1382
13.5k
  if (pdu->code == 0) {
1383
107
    if (pdu->used_size != 0 || pdu->e_token_length) {
1384
100
      coap_log_debug("coap_pdu_parse: empty message is not empty\n");
1385
100
      return 0;
1386
100
    }
1387
107
  }
1388
1389
13.4k
  if (pdu->e_token_length > pdu->used_size) {
1390
67
    coap_log_debug("coap_pdu_parse: invalid Token\n");
1391
67
    return 0;
1392
67
  }
1393
1394
13.4k
  pdu->max_opt = 0;
1395
13.4k
  if (pdu->code == 0) {
1396
    /* empty packet */
1397
7
    pdu->used_size = 0;
1398
7
    pdu->data = NULL;
1399
13.4k
  } else {
1400
    /* skip header + token */
1401
13.4k
    coap_opt_t *opt = pdu->token + pdu->e_token_length;
1402
13.4k
    size_t length = pdu->used_size - pdu->e_token_length;
1403
1404
31.9M
    while (length > 0 && *opt != COAP_PAYLOAD_START) {
1405
31.9M
#if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_WARN)
1406
31.9M
      coap_opt_t *opt_last = opt;
1407
31.9M
#endif
1408
31.9M
      size_t optsize = next_option_safe(&opt, &length, &pdu->max_opt);
1409
31.9M
      const uint32_t len =
1410
31.9M
          optsize ? coap_opt_length((const uint8_t *)opt - optsize) : 0;
1411
31.9M
      if (optsize == 0) {
1412
1.86k
        coap_log_debug("coap_pdu_parse: %d.%02d: offset %u malformed option\n",
1413
1.86k
                       pdu->code >> 5, pdu->code & 0x1F,
1414
1.86k
                       (int)(opt_last - pdu->token - pdu->e_token_length));
1415
1.86k
        coap_option_filter_set(error_opts, pdu->max_opt);
1416
1.86k
        good = 0;
1417
1.86k
        break;
1418
1.86k
      }
1419
31.9M
      if (COAP_PDU_IS_SIGNALING(pdu) ?
1420
21.0M
          !coap_pdu_parse_opt_csm(pdu, len) :
1421
31.9M
          !coap_pdu_parse_opt_base(pdu, len)) {
1422
201k
        coap_log_warn("coap_pdu_parse: %d.%02d: offset %u option %u has bad length %" PRIu32 "\n",
1423
201k
                      pdu->code >> 5, pdu->code & 0x1F,
1424
201k
                      (int)(opt_last - pdu->token - pdu->e_token_length), pdu->max_opt,
1425
201k
                      len);
1426
201k
        coap_option_filter_set(error_opts, pdu->max_opt);
1427
201k
        good = 0;
1428
201k
      }
1429
31.9M
    }
1430
1431
13.4k
    if (!good) {
1432
      /*
1433
       * Dump the options in the PDU for analysis, space separated except
1434
       * error options which are prefixed by *
1435
       * Two rows - hex and ascii (if printable)
1436
       */
1437
4.77k
      static char outbuf[COAP_DEBUG_BUF_SIZE];
1438
4.77k
      char *obp;
1439
4.77k
      size_t tlen;
1440
4.77k
      size_t outbuflen;
1441
4.77k
      int i;
1442
4.77k
      int ok;
1443
1444
14.3k
      for (i = 0; i < 2; i++) {
1445
9.54k
        opt = pdu->token + pdu->e_token_length;
1446
9.54k
        length = pdu->used_size - pdu->e_token_length;
1447
9.54k
        pdu->max_opt = 0;
1448
1449
9.54k
        outbuflen = sizeof(outbuf);
1450
9.54k
        obp = outbuf;
1451
9.54k
        ok = write_prefix(&obp, &outbuflen, "O: ", 3);
1452
        /*
1453
         * Not safe to check for 'ok' here as a lot of variables may get
1454
         * partially changed due to lack of outbuflen */
1455
9.19M
        while (length > 0 && *opt != COAP_PAYLOAD_START) {
1456
9.18M
          coap_opt_t *opt_last = opt;
1457
9.18M
          size_t optsize = next_option_safe(&opt, &length, &pdu->max_opt);
1458
9.18M
          const uint32_t len =
1459
9.18M
              optsize ? coap_opt_length((const uint8_t *)opt - optsize) : 0;
1460
9.18M
          if (!optsize || (COAP_PDU_IS_SIGNALING(pdu) ?
1461
2.52M
                           !coap_pdu_parse_opt_csm(pdu, len) :
1462
9.17M
                           !coap_pdu_parse_opt_base(pdu, len))) {
1463
405k
            ok = ok && write_prefix(&obp, &outbuflen, "*", 1);
1464
405k
            if (!optsize) {
1465
              /* Skip to end of options to output all data */
1466
3.73k
              opt = pdu->token + pdu->used_size;
1467
3.73k
              length = 0;
1468
3.73k
            }
1469
8.77M
          } else {
1470
8.77M
            ok = ok && write_prefix(&obp, &outbuflen, " ", 1);
1471
8.77M
          }
1472
9.18M
          tlen = opt - opt_last;
1473
208M
          while (tlen) {
1474
199M
            tlen--;
1475
199M
            ok = ok && write_char(&obp, &outbuflen, *opt_last, i);
1476
199M
            opt_last++;
1477
199M
          }
1478
9.18M
        }
1479
9.54k
        if (length && *opt == COAP_PAYLOAD_START) {
1480
388
          write_char(&obp, &outbuflen, *opt, i);
1481
388
        }
1482
        /* write_*() always leaves a spare byte to null terminate */
1483
9.54k
        *obp = '\000';
1484
9.54k
        coap_log_debug("%s\n", outbuf);
1485
9.54k
      }
1486
4.77k
    }
1487
1488
13.4k
    if (length > 0) {
1489
1.75k
      assert(*opt == COAP_PAYLOAD_START);
1490
1.75k
      opt++;
1491
1.75k
      length--;
1492
1493
1.75k
      if (length == 0) {
1494
91
        coap_log_debug("coap_pdu_parse: message ending in payload start marker\n");
1495
91
        return 0;
1496
91
      }
1497
1.75k
    }
1498
13.3k
    if (length > 0)
1499
1.66k
      pdu->data = (uint8_t *)opt;
1500
11.6k
    else
1501
11.6k
      pdu->data = NULL;
1502
13.3k
  }
1503
1504
13.3k
  return good;
1505
13.4k
}
1506
1507
int
1508
coap_pdu_parse(coap_proto_t proto,
1509
               const uint8_t *data,
1510
               size_t length,
1511
11.3k
               coap_pdu_t *pdu) {
1512
11.3k
  coap_opt_filter_t error_opts;
1513
1514
11.3k
  return coap_pdu_parse2(proto, data, length, pdu, &error_opts);
1515
11.3k
}
1516
1517
int
1518
coap_pdu_parse2(coap_proto_t proto,
1519
                const uint8_t *data,
1520
                size_t length,
1521
                coap_pdu_t *pdu,
1522
13.8k
                coap_opt_filter_t *error_opts) {
1523
13.8k
  size_t hdr_size;
1524
1525
13.8k
  if (length == 0)
1526
0
    return 0;
1527
13.8k
  hdr_size = coap_pdu_parse_header_size(proto, data);
1528
13.8k
  if (!hdr_size || hdr_size > length)
1529
18
    return 0;
1530
13.7k
  if (hdr_size > pdu->max_hdr_size)
1531
0
    return 0;
1532
13.7k
  if (!coap_pdu_resize(pdu, length - hdr_size))
1533
98
    return 0;
1534
13.6k
  if (pdu->token - hdr_size != data)
1535
13.6k
    memcpy(pdu->token - hdr_size, data, length);
1536
13.6k
  pdu->hdr_size = (uint8_t)hdr_size;
1537
13.6k
  pdu->used_size = length - hdr_size;
1538
13.6k
  return coap_pdu_parse_header(pdu, proto) && coap_pdu_parse_opt(pdu, error_opts);
1539
13.7k
}
1540
1541
size_t
1542
10.1k
coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) {
1543
10.1k
  uint8_t e_token_length;
1544
1545
10.1k
  if (pdu->actual_token.length < COAP_TOKEN_EXT_1B_BIAS) {
1546
9.85k
    e_token_length = (uint8_t)pdu->actual_token.length;
1547
9.85k
  } else if (pdu->actual_token.length < COAP_TOKEN_EXT_2B_BIAS) {
1548
123
    e_token_length = COAP_TOKEN_EXT_1B_TKL;
1549
162
  } else if (pdu->actual_token.length <= COAP_TOKEN_EXT_MAX) {
1550
162
    e_token_length = COAP_TOKEN_EXT_2B_TKL;
1551
162
  } else {
1552
0
    coap_log_warn("coap_add_token: Token size too large. PDU ignored\n");
1553
0
    return 0;
1554
0
  }
1555
10.1k
  if (COAP_PROTO_NOT_RELIABLE(proto)) {
1556
4.59k
    assert(pdu->max_hdr_size >= 4);
1557
4.59k
    if (pdu->max_hdr_size < 4) {
1558
0
      coap_log_warn("coap_pdu_encode_header: not enough space for UDP-style header\n");
1559
0
      return 0;
1560
0
    }
1561
4.59k
    pdu->token[-4] = COAP_DEFAULT_VERSION << 6
1562
4.59k
                     | pdu->type << 4
1563
4.59k
                     | e_token_length;
1564
4.59k
    pdu->token[-3] = pdu->code;
1565
4.59k
    pdu->token[-2] = (uint8_t)(pdu->mid >> 8);
1566
4.59k
    pdu->token[-1] = (uint8_t)(pdu->mid);
1567
4.59k
    pdu->hdr_size = 4;
1568
4.59k
#if !COAP_DISABLE_TCP
1569
5.55k
  } else if (COAP_PROTO_RELIABLE(proto)) {
1570
5.55k
    size_t len;
1571
5.55k
    assert(pdu->used_size >= pdu->e_token_length);
1572
5.55k
    if (pdu->used_size < pdu->e_token_length) {
1573
0
      coap_log_warn("coap_pdu_encode_header: corrupted PDU\n");
1574
0
      return 0;
1575
0
    }
1576
1577
    /* A lot of the reliable code assumes type is CON */
1578
5.55k
    if (pdu->type != COAP_MESSAGE_CON)
1579
597
      pdu->type = COAP_MESSAGE_CON;
1580
1581
5.55k
    if (proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS)
1582
2.44k
      len = 0;
1583
3.11k
    else
1584
3.11k
      len = pdu->used_size - pdu->e_token_length;
1585
5.55k
    if (len <= COAP_MAX_MESSAGE_SIZE_TCP0) {
1586
4.09k
      assert(pdu->max_hdr_size >= 2);
1587
4.09k
      if (pdu->max_hdr_size < 2) {
1588
0
        coap_log_warn("coap_pdu_encode_header: not enough space for TCP0 header\n");
1589
0
        return 0;
1590
0
      }
1591
4.09k
      pdu->token[-2] = (uint8_t)len << 4
1592
4.09k
                       | e_token_length;
1593
4.09k
      pdu->token[-1] = pdu->code;
1594
4.09k
      pdu->hdr_size = 2;
1595
4.09k
    } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP8) {
1596
993
      assert(pdu->max_hdr_size >= 3);
1597
993
      if (pdu->max_hdr_size < 3) {
1598
0
        coap_log_warn("coap_pdu_encode_header: not enough space for TCP8 header\n");
1599
0
        return 0;
1600
0
      }
1601
993
      pdu->token[-3] = 13 << 4 | e_token_length;
1602
993
      pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP8);
1603
993
      pdu->token[-1] = pdu->code;
1604
993
      pdu->hdr_size = 3;
1605
993
    } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP16) {
1606
381
      assert(pdu->max_hdr_size >= 4);
1607
381
      if (pdu->max_hdr_size < 4) {
1608
0
        coap_log_warn("coap_pdu_encode_header: not enough space for TCP16 header\n");
1609
0
        return 0;
1610
0
      }
1611
381
      pdu->token[-4] = 14 << 4 | e_token_length;
1612
381
      pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP16) >> 8);
1613
381
      pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP16);
1614
381
      pdu->token[-1] = pdu->code;
1615
381
      pdu->hdr_size = 4;
1616
381
    } else {
1617
80
      assert(pdu->max_hdr_size >= 6);
1618
80
      if (pdu->max_hdr_size < 6) {
1619
0
        coap_log_warn("coap_pdu_encode_header: not enough space for TCP32 header\n");
1620
0
        return 0;
1621
0
      }
1622
80
      pdu->token[-6] = 15 << 4 | e_token_length;
1623
80
      pdu->token[-5] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 24);
1624
80
      pdu->token[-4] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 16);
1625
80
      pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 8);
1626
80
      pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP32);
1627
80
      pdu->token[-1] = pdu->code;
1628
80
      pdu->hdr_size = 6;
1629
80
    }
1630
5.55k
#endif /* ! COAP_DISABLE_TCP */
1631
5.55k
  } else {
1632
0
    coap_log_warn("coap_pdu_encode_header: unsupported protocol\n");
1633
0
  }
1634
10.1k
  return pdu->hdr_size;
1635
10.1k
}
1636
1637
coap_pdu_code_t
1638
0
coap_pdu_get_code(const coap_pdu_t *pdu) {
1639
0
  return pdu->code;
1640
0
}
1641
1642
void
1643
0
coap_pdu_set_code(coap_pdu_t *pdu, coap_pdu_code_t code) {
1644
0
#ifndef RIOT_VERSION
1645
0
  assert(code <= 0xff);
1646
0
#endif /* RIOT_VERSION */
1647
0
  pdu->code = code;
1648
0
}
1649
1650
coap_pdu_type_t
1651
0
coap_pdu_get_type(const coap_pdu_t *pdu) {
1652
0
  return pdu->type;
1653
0
}
1654
1655
void
1656
0
coap_pdu_set_type(coap_pdu_t *pdu, coap_pdu_type_t type) {
1657
0
  assert(type <= 0x3);
1658
0
  pdu->type = type;
1659
0
}
1660
1661
coap_bin_const_t
1662
16
coap_pdu_get_token(const coap_pdu_t *pdu) {
1663
16
  return pdu->actual_token;
1664
16
}
1665
1666
coap_mid_t
1667
0
coap_pdu_get_mid(const coap_pdu_t *pdu) {
1668
0
  return pdu->mid;
1669
0
}
1670
1671
void
1672
0
coap_pdu_set_mid(coap_pdu_t *pdu, coap_mid_t mid) {
1673
0
#if (UINT_MAX > 65535)
1674
0
  assert(mid >= 0 && mid <= 0xffff);
1675
0
#endif /* UINT_MAX > 65535 */
1676
0
  pdu->mid = mid;
1677
0
}
1678
1679
coap_pdu_t *
1680
79
coap_pdu_reference_lkd(coap_pdu_t *pdu) {
1681
79
  if (pdu != NULL) {
1682
79
    ++pdu->ref;
1683
79
  }
1684
79
  return pdu;
1685
79
}
1686
1687
coap_pdu_t *
1688
0
coap_const_pdu_reference_lkd(const coap_pdu_t *pdu) {
1689
0
  coap_pdu_t *pdu_rw = NULL;
1690
1691
0
  if (pdu != NULL) {
1692
1693
    /* Need to do this to not get a compiler warning about const parameters */
1694
0
    memcpy(&pdu_rw, &pdu, sizeof(pdu_rw));
1695
0
    ++pdu_rw->ref;
1696
0
  }
1697
0
  return pdu_rw;
1698
0
}