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