• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 void
coap_pdu_clear(coap_pdu_t * pdu,size_t size)42 coap_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
68 coap_pdu_t *
coap_pdu_from_pbuf(struct pbuf * pbuf)69 coap_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 
96 coap_pdu_t *
coap_pdu_init(coap_pdu_type_t type,coap_pdu_code_t code,coap_mid_t mid,size_t size)97 coap_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 
152 coap_pdu_t *
coap_new_pdu(coap_pdu_type_t type,coap_pdu_code_t code,coap_session_t * session)153 coap_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 
162 void
coap_delete_pdu(coap_pdu_t * pdu)163 coap_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  */
178 coap_pdu_t *
coap_pdu_duplicate(const coap_pdu_t * old_pdu,coap_session_t * session,size_t token_length,const uint8_t * token,coap_opt_filter_t * drop_options)179 coap_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 
235 fail:
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  */
244 int
coap_pdu_resize(coap_pdu_t * pdu,size_t new_size)245 coap_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 
286 int
coap_pdu_check_resize(coap_pdu_t * pdu,size_t size)287 coap_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 
303 int
coap_add_token(coap_pdu_t * pdu,size_t len,const uint8_t * data)304 coap_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 */
360 int
coap_update_token(coap_pdu_t * pdu,size_t len,const uint8_t * data)361 coap_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 
428 int
coap_remove_option(coap_pdu_t * pdu,coap_option_num_t number)429 coap_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 
522 int
coap_option_check_repeatable(coap_option_num_t number)523 coap_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 
565 size_t
coap_insert_option(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)566 coap_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 
659 size_t
coap_update_option(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)660 coap_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 
705 size_t
coap_add_option(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)706 coap_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 
715 size_t
coap_add_option_internal(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)716 coap_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 
780 int
coap_add_data(coap_pdu_t * pdu,size_t len,const uint8_t * data)781 coap_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 
792 uint8_t *
coap_add_data_after(coap_pdu_t * pdu,size_t len)793 coap_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 
811 int
coap_get_data(const coap_pdu_t * pdu,size_t * len,const uint8_t ** data)812 coap_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 
819 int
coap_get_data_large(const coap_pdu_t * pdu,size_t * len,const uint8_t ** data,size_t * offset,size_t * total)820 coap_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
848 typedef 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. */
855 error_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 
886 const char *
coap_response_phrase(unsigned char code)887 coap_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  */
902 static size_t
next_option_safe(coap_opt_t ** optp,size_t * length,uint16_t * max_opt)903 next_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 
928 size_t
coap_pdu_parse_header_size(coap_proto_t proto,const uint8_t * data)929 coap_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  */
958 size_t
coap_pdu_parse_size(coap_proto_t proto,const uint8_t * data,size_t length)959 coap_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 
1010 int
coap_pdu_parse_header(coap_pdu_t * pdu,coap_proto_t proto)1011 coap_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 
1064 static int
coap_pdu_parse_opt_csm(coap_pdu_t * pdu,uint16_t len)1065 coap_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;
1128 bad:
1129   return 0;
1130 }
1131 
1132 static int
coap_pdu_parse_opt_base(coap_pdu_t * pdu,uint16_t len)1133 coap_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 
1239 static int
write_prefix(char ** obp,size_t * len,const char * prf,size_t prflen)1240 write_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 
1252 static int
write_char(char ** obp,size_t * len,char c,int printable)1253 write_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 
1272 int
coap_pdu_parse_opt(coap_pdu_t * pdu)1273 coap_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 
1396 int
coap_pdu_parse(coap_proto_t proto,const uint8_t * data,size_t length,coap_pdu_t * pdu)1397 coap_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 
1419 size_t
coap_pdu_encode_header(coap_pdu_t * pdu,coap_proto_t proto)1420 coap_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 
1511 coap_pdu_code_t
coap_pdu_get_code(const coap_pdu_t * pdu)1512 coap_pdu_get_code(const coap_pdu_t *pdu) {
1513   return pdu->code;
1514 }
1515 
1516 void
coap_pdu_set_code(coap_pdu_t * pdu,coap_pdu_code_t code)1517 coap_pdu_set_code(coap_pdu_t *pdu, coap_pdu_code_t code) {
1518   assert(code <= 0xff);
1519   pdu->code = code;
1520 }
1521 
1522 coap_pdu_type_t
coap_pdu_get_type(const coap_pdu_t * pdu)1523 coap_pdu_get_type(const coap_pdu_t *pdu) {
1524   return pdu->type;
1525 }
1526 
1527 void
coap_pdu_set_type(coap_pdu_t * pdu,coap_pdu_type_t type)1528 coap_pdu_set_type(coap_pdu_t *pdu, coap_pdu_type_t type) {
1529   assert(type <= 0x3);
1530   pdu->type = type;
1531 }
1532 
1533 coap_bin_const_t
coap_pdu_get_token(const coap_pdu_t * pdu)1534 coap_pdu_get_token(const coap_pdu_t *pdu) {
1535   return pdu->actual_token;
1536 }
1537 
1538 coap_mid_t
coap_pdu_get_mid(const coap_pdu_t * pdu)1539 coap_pdu_get_mid(const coap_pdu_t *pdu) {
1540   return pdu->mid;
1541 }
1542 
1543 void
coap_pdu_set_mid(coap_pdu_t * pdu,coap_mid_t mid)1544 coap_pdu_set_mid(coap_pdu_t *pdu, coap_mid_t mid) {
1545   assert(mid >= 0 && mid <= 0xffff);
1546   pdu->mid = mid;
1547 }
1548