Coverage Report

Created: 2025-02-03 06:14

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