1 /* pdu.c -- CoAP message structure
2  *
3  * Copyright (C) 2010--2016 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 #include "coap3/coap_internal.h"
12 
13 #if defined(HAVE_LIMITS_H)
14 #include <limits.h>
15 #endif
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #ifdef HAVE_ARPA_INET_H
21 #include <arpa/inet.h>
22 #endif
23 #ifdef HAVE_WINSOCK2_H
24 #include <winsock2.h>
25 #endif
26 #include <ctype.h>
27 
28 #ifndef min
29 #define min(a,b) ((a) < (b) ? (a) : (b))
30 #endif
31 
32 #ifndef max
33 #define max(a,b) ((a) > (b) ? (a) : (b))
34 #endif
35 
36 void
coap_pdu_clear(coap_pdu_t * pdu,size_t size)37 coap_pdu_clear(coap_pdu_t *pdu, size_t size) {
38   assert(pdu);
39   assert(pdu->token);
40   assert(pdu->max_hdr_size >= COAP_PDU_MAX_UDP_HEADER_SIZE);
41   if (pdu->alloc_size > size)
42     pdu->alloc_size = size;
43   pdu->type = 0;
44   pdu->code = 0;
45   pdu->hdr_size = 0;
46   pdu->token_length = 0;
47   pdu->mid = 0;
48   pdu->max_opt = 0;
49   pdu->max_size = size;
50   pdu->used_size = 0;
51   pdu->data = NULL;
52   pdu->body_data = NULL;
53   pdu->body_length = 0;
54   pdu->body_offset = 0;
55   pdu->body_total = 0;
56   pdu->lg_xmit = NULL;
57 }
58 
59 #ifdef WITH_LWIP
60 coap_pdu_t *
coap_pdu_from_pbuf(struct pbuf * pbuf)61 coap_pdu_from_pbuf( struct pbuf *pbuf )
62 {
63   coap_pdu_t *pdu;
64 
65   if (pbuf == NULL) return NULL;
66 
67   LWIP_ASSERT("Can only deal with contiguous PBUFs", pbuf->tot_len == pbuf->len);
68   LWIP_ASSERT("coap_io_do_io needs to receive an exclusive copy of the incoming pbuf", pbuf->ref == 1);
69 
70   pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t) );
71   if (!pdu) {
72     pbuf_free(pbuf);
73     return NULL;
74   }
75 
76   pdu->max_hdr_size = COAP_PDU_MAX_UDP_HEADER_SIZE;
77   pdu->pbuf = pbuf;
78   pdu->token = (uint8_t *)pbuf->payload + pdu->max_hdr_size;
79   pdu->alloc_size = pbuf->tot_len - pdu->max_hdr_size;
80   coap_pdu_clear(pdu, pdu->alloc_size);
81 
82   return pdu;
83 }
84 #endif
85 
86 coap_pdu_t *
coap_pdu_init(coap_pdu_type_t type,coap_pdu_code_t code,coap_mid_t mid,size_t size)87 coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid,
88               size_t size) {
89   coap_pdu_t *pdu;
90 
91   assert(type <= 0x3);
92   assert(code <= 0xff);
93   assert(mid >= 0 && mid <= 0xffff);
94 
95   pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t));
96   if (!pdu) return NULL;
97 
98 #if defined(WITH_CONTIKI) || defined(WITH_LWIP)
99   assert(size <= COAP_MAX_MESSAGE_SIZE_TCP16 + 4);
100   if (size > COAP_MAX_MESSAGE_SIZE_TCP16 + 4)
101     return NULL;
102   pdu->max_hdr_size = COAP_PDU_MAX_UDP_HEADER_SIZE;
103 #else
104   pdu->max_hdr_size = COAP_PDU_MAX_TCP_HEADER_SIZE;
105 #endif
106 
107 #ifdef WITH_LWIP
108   pdu->pbuf = pbuf_alloc(PBUF_TRANSPORT, size + pdu->max_hdr_size, PBUF_RAM);
109   if (pdu->pbuf == NULL) {
110     coap_free_type(COAP_PDU, pdu);
111     return NULL;
112   }
113   pdu->token = (uint8_t *)pdu->pbuf->payload + pdu->max_hdr_size;
114 #else /* WITH_LWIP */
115   uint8_t *buf;
116   pdu->alloc_size = min(size, 256);
117   buf = coap_malloc_type(COAP_PDU_BUF, pdu->alloc_size + pdu->max_hdr_size);
118   if (buf == NULL) {
119     coap_free_type(COAP_PDU, pdu);
120     return NULL;
121   }
122   pdu->token = buf + pdu->max_hdr_size;
123 #endif /* WITH_LWIP */
124   coap_pdu_clear(pdu, size);
125   pdu->mid = mid;
126   pdu->type = type;
127   pdu->code = code;
128   return pdu;
129 }
130 
131 coap_pdu_t *
coap_new_pdu(coap_pdu_type_t type,coap_pdu_code_t code,coap_session_t * session)132 coap_new_pdu(coap_pdu_type_t type, coap_pdu_code_t code,
133              coap_session_t *session) {
134   coap_pdu_t *pdu = coap_pdu_init(type, code, coap_new_message_id(session),
135                                   coap_session_max_pdu_size(session));
136   if (!pdu)
137     coap_log(LOG_CRIT, "coap_new_pdu: cannot allocate memory for new PDU\n");
138   return pdu;
139 }
140 
141 void
coap_delete_pdu(coap_pdu_t * pdu)142 coap_delete_pdu(coap_pdu_t *pdu) {
143   if (pdu != NULL) {
144 #ifdef WITH_LWIP
145     pbuf_free(pdu->pbuf);
146 #else
147     if (pdu->token != NULL)
148       coap_free_type(COAP_PDU_BUF, pdu->token - pdu->max_hdr_size);
149 #endif
150     coap_free_type(COAP_PDU, pdu);
151   }
152 }
153 
154 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)155 coap_pdu_duplicate(const coap_pdu_t *old_pdu,
156                    coap_session_t *session,
157                    size_t token_length,
158                    const uint8_t *token,
159                    coap_opt_filter_t *drop_options) {
160   coap_pdu_t *pdu = coap_pdu_init(old_pdu->type,
161                                   old_pdu->code,
162                                   coap_new_message_id(session),
163                                   coap_session_max_pdu_size(session));
164 
165   if (pdu == NULL)
166     return NULL;
167 
168   coap_add_token(pdu, token_length, token);
169   pdu->lg_xmit = old_pdu->lg_xmit;
170 
171   if (drop_options == NULL) {
172     /* Drop COAP_PAYLOAD_START as well if data */
173     size_t length = old_pdu->used_size - old_pdu->token_length -
174           (old_pdu->data ?
175                  old_pdu->used_size - (old_pdu->data - old_pdu->token) +1 : 0);
176     if (!coap_pdu_resize(pdu, length + old_pdu->hdr_size))
177       goto fail;
178     /* Copy the options and any data across */
179     memcpy(pdu->token + pdu->token_length,
180            old_pdu->token + old_pdu->token_length, length);
181     pdu->used_size += length;
182     pdu->max_opt = old_pdu->max_opt;
183   }
184   else {
185     /* Copy across all the options the slow way */
186     coap_opt_iterator_t opt_iter;
187     coap_opt_t *option;
188 
189     coap_option_iterator_init(old_pdu, &opt_iter, COAP_OPT_ALL);
190     while ((option = coap_option_next(&opt_iter))) {
191       if (drop_options && coap_option_filter_get(drop_options, opt_iter.number))
192         continue;
193       if (!coap_add_option(pdu, opt_iter.number,
194                            coap_opt_length(option),
195                            coap_opt_value(option)))
196         goto fail;
197     }
198   }
199   return pdu;
200 
201 fail:
202   coap_delete_pdu(pdu);
203   return NULL;
204 }
205 
206 int
coap_pdu_resize(coap_pdu_t * pdu,size_t new_size)207 coap_pdu_resize(coap_pdu_t *pdu, size_t new_size) {
208   if (new_size > pdu->alloc_size) {
209 #if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
210     uint8_t *new_hdr;
211     size_t offset;
212 #endif
213     if (pdu->max_size && new_size > pdu->max_size) {
214       coap_log(LOG_WARNING, "coap_pdu_resize: pdu too big\n");
215       return 0;
216     }
217 #if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
218     if (pdu->data != NULL) {
219       assert(pdu->data > pdu->token);
220       offset = pdu->data - pdu->token;
221     } else {
222       offset = 0;
223     }
224     new_hdr = (uint8_t*)realloc(pdu->token - pdu->max_hdr_size, new_size + pdu->max_hdr_size);
225     if (new_hdr == NULL) {
226       coap_log(LOG_WARNING, "coap_pdu_resize: realloc failed\n");
227       return 0;
228     }
229     pdu->token = new_hdr + pdu->max_hdr_size;
230     if (offset > 0)
231       pdu->data = pdu->token + offset;
232     else
233       pdu->data = NULL;
234 #endif
235   }
236   pdu->alloc_size = new_size;
237   return 1;
238 }
239 
240 int
coap_pdu_check_resize(coap_pdu_t * pdu,size_t size)241 coap_pdu_check_resize(coap_pdu_t *pdu, size_t size) {
242   if (size > pdu->alloc_size) {
243     size_t new_size = max(256, pdu->alloc_size * 2);
244     while (size > new_size)
245       new_size *= 2;
246     if (pdu->max_size && new_size > pdu->max_size) {
247       new_size = pdu->max_size;
248       if (new_size < size)
249         return 0;
250     }
251     if (!coap_pdu_resize(pdu, new_size))
252       return 0;
253   }
254   return 1;
255 }
256 
257 int
coap_add_token(coap_pdu_t * pdu,size_t len,const uint8_t * data)258 coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) {
259   /* must allow for pdu == NULL as callers may rely on this */
260   if (!pdu || len > 8)
261     return 0;
262 
263   if (pdu->used_size) {
264     coap_log(LOG_WARNING,
265              "coap_add_token: The token must defined first. Token ignored\n");
266     return 0;
267   }
268   if (!coap_pdu_check_resize(pdu, len))
269     return 0;
270   pdu->token_length = (uint8_t)len;
271   if (len)
272     memcpy(pdu->token, data, len);
273   pdu->max_opt = 0;
274   pdu->used_size = len;
275   pdu->data = NULL;
276 
277   return 1;
278 }
279 
280 /* It is assumed that coap_encode_var_safe8() has been called to reduce data */
281 int
coap_update_token(coap_pdu_t * pdu,size_t len,const uint8_t * data)282 coap_update_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) {
283   /* must allow for pdu == NULL as callers may rely on this */
284   if (!pdu || len > 8)
285     return 0;
286 
287   if (pdu->used_size == 0) {
288     return coap_add_token(pdu, len, data);
289   }
290   if (len == pdu->token_length) {
291     /* Easy case - just data has changed */
292   }
293   else if (len > pdu->token_length) {
294     if (!coap_pdu_check_resize(pdu, pdu->used_size + len - pdu->token_length))
295       return 0;
296     memmove(&pdu->token[len - pdu->token_length], pdu->token, pdu->used_size);
297     pdu->used_size += len - pdu->token_length;
298   }
299   else {
300     pdu->used_size -= pdu->token_length - len;
301     memmove(pdu->token, &pdu->token[pdu->token_length - len], pdu->used_size);
302   }
303   if (pdu->data) {
304     pdu->data += len - pdu->token_length;
305   }
306   pdu->token_length = (uint8_t)len;
307   if (len)
308     memcpy(pdu->token, data, len);
309 
310   return 1;
311 }
312 
313 int
coap_remove_option(coap_pdu_t * pdu,coap_option_num_t number)314 coap_remove_option(coap_pdu_t *pdu, coap_option_num_t number) {
315   coap_opt_iterator_t opt_iter;
316   coap_opt_t *option;
317   coap_opt_t *next_option = NULL;
318   size_t opt_delta;
319   coap_option_t decode_this;
320   coap_option_t decode_next;
321 
322   /* Need to locate where in current options to remove this one */
323   coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
324   while ((option = coap_option_next(&opt_iter))) {
325     if (opt_iter.number == number) {
326       /* Found option to delete */
327       break;
328     }
329   }
330   if (!option)
331     return 0;
332 
333   if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token),
334                       &decode_this))
335     return 0;
336 
337   next_option = coap_option_next(&opt_iter);
338   if (next_option) {
339     if (!coap_opt_parse(next_option,
340                         pdu->used_size - (next_option - pdu->token),
341                         &decode_next))
342       return 0;
343     opt_delta = decode_this.delta + decode_next.delta;
344     if (opt_delta <= 12) {
345       /* can simply update the delta of next option */
346       next_option[0] = (next_option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
347     }
348     else if (opt_delta <= 269 && decode_next.delta <= 12) {
349       /* next option delta size increase */
350       next_option -= 1;
351       next_option[0] = (next_option[1] & 0x0f) + (13 << 4);
352       next_option[1] = (coap_opt_t)(opt_delta - 13);
353     }
354     else if (opt_delta <= 269) {
355       /* can simply update the delta of next option */
356       next_option[1] = (coap_opt_t)(opt_delta - 13);
357     }
358     else if (decode_next.delta <= 12) {
359       /* next option delta size increase */
360       if (next_option - option < 2) {
361         /* Need to shuffle everything up by 1 before decrement */
362         if (!coap_pdu_check_resize(pdu, pdu->used_size + 1))
363           return 0;
364         /* Possible a re-size took place with a realloc() */
365         /* Need to rediscover this and next options */
366         coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
367         while ((option = coap_option_next(&opt_iter))) {
368           if (opt_iter.number == number) {
369             /* Found option to delete */
370             break;
371           }
372         }
373         next_option = coap_option_next(&opt_iter);
374         assert(option != NULL);
375         assert(next_option != NULL);
376         memmove(&next_option[1], next_option,
377                 pdu->used_size - (next_option - pdu->token));
378         pdu->used_size++;
379         if (pdu->data)
380           pdu->data++;
381         next_option++;
382       }
383       next_option -= 2;
384       next_option[0] = (next_option[2] & 0x0f) + (14 << 4);
385       next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
386       next_option[2] = (opt_delta - 269) & 0xff;
387     }
388     else if (decode_next.delta <= 269) {
389       /* next option delta size increase */
390       next_option -= 1;
391       next_option[0] = (next_option[1] & 0x0f) + (14 << 4);
392       next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
393       next_option[2] = (opt_delta - 269) & 0xff;
394     }
395     else {
396       next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
397       next_option[2] = (opt_delta - 269) & 0xff;
398     }
399   }
400   else {
401     next_option = option + coap_opt_encode_size(decode_this.delta,
402                                                 coap_opt_length(option));
403     pdu->max_opt -= decode_this.delta;
404   }
405   if (pdu->used_size - (next_option - pdu->token))
406     memmove(option, next_option, pdu->used_size - (next_option - pdu->token));
407   pdu->used_size -= next_option - option;
408   if (pdu->data)
409     pdu->data -= next_option - option;
410   return 1;
411 }
412 
413 size_t
coap_insert_option(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)414 coap_insert_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
415                    const uint8_t *data) {
416   coap_opt_iterator_t opt_iter;
417   coap_opt_t *option;
418   uint16_t prev_number = 0;
419   size_t shift;
420   size_t opt_delta;
421   coap_option_t decode;
422   size_t shrink = 0;
423 
424   if (number >= pdu->max_opt)
425     return coap_add_option(pdu, number, len, data);
426 
427   /* Need to locate where in current options to insert this one */
428   coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
429   while ((option = coap_option_next(&opt_iter))) {
430     if (opt_iter.number > number) {
431       /* Found where to insert */
432       break;
433     }
434     prev_number = opt_iter.number;
435   }
436   assert(option != NULL);
437   /* size of option inc header to insert */
438   shift = coap_opt_encode_size(number - prev_number, len);
439 
440   /* size of next option (header may shrink in size as delta changes */
441   if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token), &decode))
442     return 0;
443   opt_delta = opt_iter.number - number;
444 
445   if (!coap_pdu_check_resize(pdu,
446                          pdu->used_size + shift - shrink))
447     return 0;
448 
449   /* Possible a re-size took place with a realloc() */
450   /* Need to locate where in current options to insert this one */
451   coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL);
452   while ((option = coap_option_next(&opt_iter))) {
453     if (opt_iter.number > number) {
454       /* Found where to insert */
455       break;
456     }
457   }
458   assert(option != NULL);
459 
460   if (decode.delta <= 12) {
461     /* can simply patch in the new delta of next option */
462     option[0] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
463   }
464   else if (decode.delta <= 269 && opt_delta <= 12) {
465     /* option header is going to shrink by one byte */
466     option[1] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
467     shrink = 1;
468   }
469   else if (decode.delta <= 269 && opt_delta <= 269) {
470     /* can simply patch in the new delta of next option */
471     option[1] = (coap_opt_t)(opt_delta - 13);
472   }
473   else if (opt_delta <= 12) {
474     /* option header is going to shrink by two bytes */
475     option[2] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4);
476     shrink = 2;
477   }
478   else if (opt_delta <= 269) {
479     /* option header is going to shrink by one bytes */
480     option[1] = (option[0] & 0x0f) + 0xd0;
481     option[2] = (coap_opt_t)(opt_delta - 13);
482     shrink = 1;
483   }
484   else {
485     /* can simply patch in the new delta of next option */
486     option[1] = (coap_opt_t)((opt_delta - 269) >> 8);
487     option[2] = (opt_delta - 269) & 0xff;
488   }
489 
490   memmove(&option[shift], &option[shrink],
491           pdu->used_size - (option - pdu->token) - shrink);
492   if (!coap_opt_encode(option, pdu->alloc_size - pdu->used_size,
493                             number - prev_number, data, len))
494     return 0;
495 
496   pdu->used_size += shift - shrink;
497   if (pdu->data)
498     pdu->data += shift - shrink;
499   return shift;
500 }
501 
502 size_t
coap_update_option(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)503 coap_update_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
504                    const uint8_t *data) {
505   coap_opt_iterator_t opt_iter;
506   coap_opt_t *option;
507   coap_option_t decode;
508   size_t new_length = 0;
509   size_t old_length = 0;
510 
511   option = coap_check_option(pdu, number, &opt_iter);
512   if (!option)
513     return coap_insert_option(pdu, number, len, data);
514 
515   old_length = coap_opt_parse(option, (size_t)-1, &decode);
516   if (old_length == 0)
517     return 0;
518   new_length = coap_opt_encode_size(decode.delta, len);
519 
520   if (new_length > old_length) {
521     if (!coap_pdu_check_resize(pdu,
522                          pdu->used_size + new_length - old_length))
523       return 0;
524     /* Possible a re-size took place with a realloc() */
525     option = coap_check_option(pdu, number, &opt_iter);
526   }
527 
528   if (new_length != old_length)
529     memmove(&option[new_length], &option[old_length],
530             pdu->used_size - (option - pdu->token) - old_length);
531 
532   if (!coap_opt_encode(option, new_length,
533                             decode.delta, data, len))
534     return 0;
535 
536   pdu->used_size += new_length - old_length;
537   if (pdu->data)
538     pdu->data += new_length - old_length;
539   return 1;
540 }
541 
542 size_t
coap_add_option(coap_pdu_t * pdu,coap_option_num_t number,size_t len,const uint8_t * data)543 coap_add_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len,
544                 const uint8_t *data) {
545   size_t optsize;
546   coap_opt_t *opt;
547 
548   assert(pdu);
549 
550   if (number == pdu->max_opt) {
551     /* Validate that the option is repeatable */
552     switch (number) {
553     /* Ignore list of genuine repeatable */
554     case COAP_OPTION_IF_MATCH:
555     case COAP_OPTION_ETAG:
556     case COAP_OPTION_LOCATION_PATH:
557     case COAP_OPTION_URI_PATH:
558     case COAP_OPTION_URI_QUERY:
559     case COAP_OPTION_LOCATION_QUERY:
560       break;
561     default:
562       coap_log(LOG_INFO, "Option number %d is not defined as repeatable\n",
563                number);
564       /* Accepting it after warning as there may be user defineable options */
565       break;
566     }
567   }
568 
569   if (COAP_PDU_IS_REQUEST(pdu) &&
570       (number == COAP_OPTION_PROXY_URI ||
571        number == COAP_OPTION_PROXY_SCHEME)) {
572     /*
573      * Need to check whether there is a hop-limit option.  If not, it needs
574      * to be inserted by default (RFC 8768).
575      */
576     coap_opt_iterator_t opt_iter;
577 
578     if (coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter) == NULL) {
579       size_t hop_limit = COAP_OPTION_HOP_LIMIT;
580 
581       coap_insert_option(pdu, COAP_OPTION_HOP_LIMIT, 1, (uint8_t *)&hop_limit);
582     }
583   }
584 
585   if (number < pdu->max_opt) {
586     coap_log(LOG_DEBUG,
587              "coap_add_option: options are not in correct order\n");
588     return coap_insert_option(pdu, number, len, data);
589   }
590 
591   optsize = coap_opt_encode_size(number - pdu->max_opt, len);
592   if (!coap_pdu_check_resize(pdu,
593       pdu->used_size + optsize))
594     return 0;
595 
596   if (pdu->data) {
597     /* include option delimiter */
598     memmove (&pdu->data[optsize-1], &pdu->data[-1],
599              pdu->used_size - (pdu->data - pdu->token) + 1);
600     opt = pdu->data -1;
601     pdu->data += optsize;
602   }
603   else {
604     opt = pdu->token + pdu->used_size;
605   }
606 
607   /* encode option and check length */
608   optsize = coap_opt_encode(opt, pdu->alloc_size - pdu->used_size,
609                             number - pdu->max_opt, data, len);
610 
611   if (!optsize) {
612     coap_log(LOG_WARNING, "coap_add_option: cannot add option\n");
613     /* error */
614     return 0;
615   } else {
616     pdu->max_opt = number;
617     pdu->used_size += optsize;
618   }
619 
620   return optsize;
621 }
622 
623 int
coap_add_data(coap_pdu_t * pdu,size_t len,const uint8_t * data)624 coap_add_data(coap_pdu_t *pdu, size_t len, const uint8_t *data) {
625   if (len == 0) {
626     return 1;
627   } else {
628     uint8_t *payload = coap_add_data_after(pdu, len);
629     if (payload != NULL)
630       memcpy(payload, data, len);
631     return payload != NULL;
632   }
633 }
634 
635 uint8_t *
coap_add_data_after(coap_pdu_t * pdu,size_t len)636 coap_add_data_after(coap_pdu_t *pdu, size_t len) {
637   assert(pdu);
638   assert(pdu->data == NULL);
639 
640   pdu->data = NULL;
641 
642   if (len == 0)
643     return NULL;
644 
645   if (!coap_pdu_resize(pdu, pdu->used_size + len + 1))
646     return 0;
647   pdu->token[pdu->used_size++] = COAP_PAYLOAD_START;
648   pdu->data = pdu->token + pdu->used_size;
649   pdu->used_size += len;
650   return pdu->data;
651 }
652 
653 int
coap_get_data(const coap_pdu_t * pdu,size_t * len,const uint8_t ** data)654 coap_get_data(const coap_pdu_t *pdu, size_t *len, const uint8_t **data) {
655   size_t offset;
656   size_t total;
657 
658   return coap_get_data_large(pdu, len, data, &offset, &total);
659 }
660 
661 int
coap_get_data_large(const coap_pdu_t * pdu,size_t * len,const uint8_t ** data,size_t * offset,size_t * total)662 coap_get_data_large(const coap_pdu_t *pdu, size_t *len, const uint8_t **data,
663                     size_t *offset, size_t *total) {
664   assert(pdu);
665   assert(len);
666   assert(data);
667 
668   *offset = pdu->body_offset;
669   *total = pdu->body_total;
670   if (pdu->body_data) {
671     *data = pdu->body_data;
672     *len = pdu->body_length;
673     return 1;
674   }
675   *data = pdu->data;
676   if(pdu->data == NULL) {
677      *len = 0;
678      *total = 0;
679      return 0;
680   }
681 
682   *len = pdu->used_size - (pdu->data - pdu->token);
683   if (*total == 0)
684     *total = *len;
685 
686   return 1;
687 }
688 
689 #ifndef SHORT_ERROR_RESPONSE
690 typedef struct {
691   unsigned char code;
692   const char *phrase;
693 } error_desc_t;
694 
695 /* if you change anything here, make sure, that the longest string does not
696  * exceed COAP_ERROR_PHRASE_LENGTH. */
697 error_desc_t coap_error[] = {
698   { COAP_RESPONSE_CODE(201), "Created" },
699   { COAP_RESPONSE_CODE(202), "Deleted" },
700   { COAP_RESPONSE_CODE(203), "Valid" },
701   { COAP_RESPONSE_CODE(204), "Changed" },
702   { COAP_RESPONSE_CODE(205), "Content" },
703   { COAP_RESPONSE_CODE(231), "Continue" },
704   { COAP_RESPONSE_CODE(400), "Bad Request" },
705   { COAP_RESPONSE_CODE(401), "Unauthorized" },
706   { COAP_RESPONSE_CODE(402), "Bad Option" },
707   { COAP_RESPONSE_CODE(403), "Forbidden" },
708   { COAP_RESPONSE_CODE(404), "Not Found" },
709   { COAP_RESPONSE_CODE(405), "Method Not Allowed" },
710   { COAP_RESPONSE_CODE(406), "Not Acceptable" },
711   { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" },
712   { COAP_RESPONSE_CODE(409), "Conflict" },
713   { COAP_RESPONSE_CODE(412), "Precondition Failed" },
714   { COAP_RESPONSE_CODE(413), "Request Entity Too Large" },
715   { COAP_RESPONSE_CODE(415), "Unsupported Content-Format" },
716   { COAP_RESPONSE_CODE(422), "Unprocessable" },
717   { COAP_RESPONSE_CODE(429), "Too Many Requests" },
718   { COAP_RESPONSE_CODE(500), "Internal Server Error" },
719   { COAP_RESPONSE_CODE(501), "Not Implemented" },
720   { COAP_RESPONSE_CODE(502), "Bad Gateway" },
721   { COAP_RESPONSE_CODE(503), "Service Unavailable" },
722   { COAP_RESPONSE_CODE(504), "Gateway Timeout" },
723   { COAP_RESPONSE_CODE(505), "Proxying Not Supported" },
724   { COAP_RESPONSE_CODE(508), "Hop Limit Reached" },
725   { 0, NULL }                        /* end marker */
726 };
727 
728 const char *
coap_response_phrase(unsigned char code)729 coap_response_phrase(unsigned char code) {
730   int i;
731   for (i = 0; coap_error[i].code; ++i) {
732     if (coap_error[i].code == code)
733       return coap_error[i].phrase;
734   }
735   return NULL;
736 }
737 #endif
738 
739 /**
740  * Advances *optp to next option if still in PDU. This function
741  * returns the number of bytes opt has been advanced or @c 0
742  * on error.
743  */
744 static size_t
next_option_safe(coap_opt_t ** optp,size_t * length,uint16_t * max_opt)745 next_option_safe(coap_opt_t **optp, size_t *length, uint16_t *max_opt) {
746   coap_option_t option;
747   size_t optsize;
748 
749   assert(optp); assert(*optp);
750   assert(length);
751 
752   optsize = coap_opt_parse(*optp, *length, &option);
753   if (optsize) {
754     assert(optsize <= *length);
755 
756     /* signal an error if this option would exceed the
757      * allowed number space */
758     if (*max_opt + option.delta > COAP_MAX_OPT) {
759       return 0;
760     }
761     *max_opt += option.delta;
762     *optp += optsize;
763     *length -= optsize;
764   }
765 
766   return optsize;
767 }
768 
769 size_t
coap_pdu_parse_header_size(coap_proto_t proto,const uint8_t * data)770 coap_pdu_parse_header_size(coap_proto_t proto,
771                            const uint8_t *data) {
772   assert(data);
773   size_t header_size = 0;
774 
775   if (proto == COAP_PROTO_TCP || proto==COAP_PROTO_TLS) {
776     uint8_t len = *data >> 4;
777     if (len < 13)
778       header_size = 2;
779     else if (len==13)
780       header_size = 3;
781     else if (len==14)
782       header_size = 4;
783     else
784       header_size = 6;
785   } else if (proto == COAP_PROTO_UDP || proto==COAP_PROTO_DTLS) {
786     header_size = 4;
787   }
788 
789   return header_size;
790 }
791 
792 size_t
coap_pdu_parse_size(coap_proto_t proto,const uint8_t * data,size_t length)793 coap_pdu_parse_size(coap_proto_t proto,
794                     const uint8_t *data,
795                     size_t length) {
796   assert(data);
797   assert(proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS);
798   assert(coap_pdu_parse_header_size(proto, data) <= length );
799 
800   size_t size = 0;
801 
802   if ((proto == COAP_PROTO_TCP || proto==COAP_PROTO_TLS) && length >= 1) {
803     uint8_t len = *data >> 4;
804     if (len < 13) {
805       size = len;
806     } else if (length >= 2) {
807       if (len==13) {
808         size = (size_t)data[1] + COAP_MESSAGE_SIZE_OFFSET_TCP8;
809       } else if (length >= 3) {
810         if (len==14) {
811           size = ((size_t)data[1] << 8) + data[2] + COAP_MESSAGE_SIZE_OFFSET_TCP16;
812         } else if (length >= 5) {
813           size = ((size_t)data[1] << 24) + ((size_t)data[2] << 16)
814                + ((size_t)data[3] << 8) + data[4] + COAP_MESSAGE_SIZE_OFFSET_TCP32;
815         }
816       }
817     }
818     size += data[0] & 0x0f;
819   }
820 
821   return size;
822 }
823 
824 int
coap_pdu_parse_header(coap_pdu_t * pdu,coap_proto_t proto)825 coap_pdu_parse_header(coap_pdu_t *pdu, coap_proto_t proto) {
826   uint8_t *hdr = pdu->token - pdu->hdr_size;
827   if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) {
828     assert(pdu->hdr_size == 4);
829     if ((hdr[0] >> 6) != COAP_DEFAULT_VERSION) {
830       coap_log(LOG_DEBUG, "coap_pdu_parse: UDP version not supported\n");
831       return 0;
832     }
833     pdu->type = (hdr[0] >> 4) & 0x03;
834     pdu->token_length = hdr[0] & 0x0f;
835     pdu->code = hdr[1];
836     pdu->mid = (uint16_t)hdr[2] << 8 | hdr[3];
837   } else if (proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) {
838     assert(pdu->hdr_size >= 2 && pdu->hdr_size <= 6);
839     pdu->type = COAP_MESSAGE_CON;
840     pdu->token_length = hdr[0] & 0x0f;
841     pdu->code = hdr[pdu->hdr_size-1];
842     pdu->mid = 0;
843   } else {
844     coap_log(LOG_DEBUG, "coap_pdu_parse: unsupported protocol\n");
845     return 0;
846   }
847   if (pdu->token_length > pdu->alloc_size) {
848     /* Invalid PDU provided - not wise to assert here though */
849     coap_log(LOG_DEBUG, "coap_pdu_parse: PDU header token size broken\n");
850     pdu->token_length = (uint8_t)pdu->alloc_size;
851     return 0;
852   }
853   return 1;
854 }
855 
856 static int
coap_pdu_parse_opt_csm(coap_pdu_t * pdu,uint16_t len)857 coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) {
858   switch ((coap_pdu_signaling_proto_t)pdu->code) {
859   case COAP_SIGNALING_CSM:
860     switch (pdu->max_opt) {
861     case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE:
862       if (len > 4) goto bad;
863       break;
864     case COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER:
865       if (len > 0) goto bad;
866       break;
867     default:
868       ;
869     }
870     break;
871   case COAP_SIGNALING_PING:
872   case COAP_SIGNALING_PONG:
873     switch (pdu->max_opt) {
874     case COAP_SIGNALING_OPTION_CUSTODY:
875       if (len > 0) goto bad;
876       break;
877     default:
878       ;
879     }
880     break;
881   case COAP_SIGNALING_RELEASE:
882     switch (pdu->max_opt) {
883     case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS:
884       if (len < 1 || len > 255) goto bad;
885       break;
886     case COAP_SIGNALING_OPTION_HOLD_OFF:
887       if (len > 3) goto bad;
888       break;
889     default:
890       ;
891     }
892     break;
893   case COAP_SIGNALING_ABORT:
894     switch (pdu->max_opt) {
895     case COAP_SIGNALING_OPTION_BAD_CSM_OPTION:
896       if (len > 2) goto bad;
897       break;
898     default:
899       ;
900     }
901     break;
902   default:
903     ;
904   }
905   return 1;
906 bad:
907   return 0;
908 }
909 
910 static int
coap_pdu_parse_opt_base(coap_pdu_t * pdu,uint16_t len)911 coap_pdu_parse_opt_base(coap_pdu_t *pdu, uint16_t len) {
912   int res = 1;
913 
914   switch (pdu->max_opt) {
915   case COAP_OPTION_IF_MATCH:      if (len > 8) res = 0;               break;
916   case COAP_OPTION_URI_HOST:      if (len < 1 || len > 255) res = 0;  break;
917   case COAP_OPTION_ETAG:          if (len < 1 || len > 8) res = 0;    break;
918   case COAP_OPTION_IF_NONE_MATCH: if (len != 0) res = 0;              break;
919   case COAP_OPTION_OBSERVE:       if (len > 3) res = 0;               break;
920   case COAP_OPTION_URI_PORT:      if (len > 2) res = 0;               break;
921   case COAP_OPTION_LOCATION_PATH: if (len > 255) res = 0;             break;
922   case COAP_OPTION_OSCORE:        if (len > 255) res = 0;             break;
923   case COAP_OPTION_URI_PATH:      if (len > 255) res = 0;             break;
924   case COAP_OPTION_CONTENT_FORMAT:if (len > 2) res = 0;               break;
925   case COAP_OPTION_MAXAGE:        if (len > 4) res = 0;               break;
926   case COAP_OPTION_URI_QUERY:     if (len < 1 || len > 255) res = 0;  break;
927   case COAP_OPTION_HOP_LIMIT:     if (len != 1) res = 0;              break;
928   case COAP_OPTION_ACCEPT:        if (len > 2) res = 0;               break;
929   case COAP_OPTION_LOCATION_QUERY:if (len > 255) res = 0;             break;
930   case COAP_OPTION_BLOCK2:        if (len > 3) res = 0;               break;
931   case COAP_OPTION_BLOCK1:        if (len > 3) res = 0;               break;
932   case COAP_OPTION_SIZE2:         if (len > 4) res = 0;               break;
933   case COAP_OPTION_PROXY_URI:     if (len < 1 || len > 1034) res = 0; break;
934   case COAP_OPTION_PROXY_SCHEME:  if (len < 1 || len > 255) res = 0;  break;
935   case COAP_OPTION_SIZE1:         if (len > 4) res = 0;               break;
936   case COAP_OPTION_NORESPONSE:    if (len > 1) res = 0;               break;
937   default:
938     ;
939   }
940   return res;
941 }
942 
943 static int
write_prefix(char ** obp,size_t * len,const char * prf,size_t prflen)944 write_prefix(char **obp, size_t *len, const char *prf, size_t prflen) {
945   /* Make sure space for null terminating byte */
946   if (*len > prflen +1) {
947     return 0;
948   }
949 
950   memcpy(*obp, prf, prflen);
951   *obp += prflen;
952   *len -= prflen;
953   return 1;
954 }
955 
956 static int
write_char(char ** obp,size_t * len,char c,int printable)957 write_char(char **obp, size_t *len, char c, int printable) {
958   /* Make sure space for null terminating byte */
959   if (*len > 3) {
960     return 0;
961   }
962 
963   if (!printable) {
964     const uint8_t hex[] = "0123456789abcdef";
965     (*obp)[0] = hex[(c & 0xf0) >> 4];
966     (*obp)[1] = hex[c & 0x0f];
967   } else {
968     (*obp)[0] = isprint(c) ? c : '.';
969     (*obp)[1] = ' ';
970   }
971   *obp += 2;
972   *len -= 2;
973   return 1;
974 }
975 
976 int
coap_pdu_parse_opt(coap_pdu_t * pdu)977 coap_pdu_parse_opt(coap_pdu_t *pdu) {
978 
979   int good = 1;
980   /* sanity checks */
981   if (pdu->code == 0) {
982     if (pdu->used_size != 0 || pdu->token_length) {
983       coap_log(LOG_DEBUG, "coap_pdu_parse: empty message is not empty\n");
984       return 0;
985     }
986   }
987 
988   if (pdu->token_length > pdu->used_size || pdu->token_length > 8) {
989     coap_log(LOG_DEBUG, "coap_pdu_parse: invalid Token\n");
990     return 0;
991   }
992 
993   pdu->max_opt = 0;
994   if (pdu->code == 0) {
995     /* empty packet */
996     pdu->used_size = 0;
997     pdu->data = NULL;
998   } else {
999     /* skip header + token */
1000     coap_opt_t *opt = pdu->token + pdu->token_length;
1001     size_t length = pdu->used_size - pdu->token_length;
1002 
1003     while (length > 0 && *opt != COAP_PAYLOAD_START) {
1004       coap_opt_t *opt_last = opt;
1005       size_t optsize = next_option_safe(&opt, &length, &pdu->max_opt);
1006       const uint32_t len =
1007         optsize ? coap_opt_length((const uint8_t *)opt - optsize) : 0;
1008       if (optsize == 0) {
1009         coap_log(LOG_DEBUG,
1010             "coap_pdu_parse: %d.%02d: offset %u malformed option\n",
1011                pdu->code >> 5, pdu->code & 0x1F,
1012                (int)(opt_last - pdu->token - pdu->token_length));
1013         good = 0;
1014         break;
1015       }
1016       if (COAP_PDU_IS_SIGNALING(pdu) ?
1017            !coap_pdu_parse_opt_csm(pdu, len) :
1018            !coap_pdu_parse_opt_base(pdu, len)) {
1019         coap_log(LOG_WARNING,
1020             "coap_pdu_parse: %d.%02d: offset %u option %u has bad length %u\n",
1021                  pdu->code >> 5, pdu->code & 0x1F,
1022                  (int)(opt_last - pdu->token - pdu->token_length), pdu->max_opt,
1023                  len);
1024         good = 0;
1025       }
1026     }
1027 
1028     if (!good) {
1029       /*
1030        * Dump the options in the PDU for analysis, space separated except
1031        * error options which are prefixed by *
1032        * Two rows - hex and ascii (if printable)
1033        */
1034       static char outbuf[COAP_DEBUG_BUF_SIZE];
1035       char *obp;
1036       size_t tlen;
1037       size_t outbuflen;
1038       int i;
1039       int ok;
1040 
1041       for (i = 0; i < 2; i++) {
1042         opt = pdu->token + pdu->token_length;
1043         length = pdu->used_size - pdu->token_length;
1044         pdu->max_opt = 0;
1045 
1046         outbuflen = sizeof(outbuf);
1047         obp = outbuf;
1048         ok = write_prefix(&obp, &outbuflen, "O: ", 3);
1049         while (length > 0 && *opt != COAP_PAYLOAD_START) {
1050           coap_opt_t *opt_last = opt;
1051           size_t optsize = next_option_safe(&opt, &length, &pdu->max_opt);
1052           const uint32_t len =
1053             optsize ? coap_opt_length((const uint8_t *)opt - optsize) : 0;
1054           if (!optsize || (COAP_PDU_IS_SIGNALING(pdu) ?
1055                !coap_pdu_parse_opt_csm(pdu, len) :
1056                !coap_pdu_parse_opt_base(pdu, len))) {
1057             ok = ok && write_prefix(&obp, &outbuflen, "*", 1);
1058             if (!optsize) {
1059               /* Skip to end of options to output all data */
1060               opt = pdu->token + pdu->used_size;
1061               length = 0;
1062             }
1063           }
1064           else {
1065             ok = ok && write_prefix(&obp, &outbuflen, " ", 1);
1066           }
1067           tlen = opt - opt_last;
1068           while (tlen--) {
1069             ok = ok && write_char(&obp, &outbuflen, *opt_last, i);
1070             opt_last++;
1071           }
1072         }
1073         if (length && *opt == COAP_PAYLOAD_START) {
1074           ok = ok && write_char(&obp, &outbuflen, *opt, i);
1075         }
1076         /* write_*() always leaves a spare byte to null terminate */
1077         *obp = '\000';
1078         coap_log(LOG_DEBUG, "%s\n", outbuf);
1079       }
1080     }
1081 
1082     if (length > 0) {
1083       assert(*opt == COAP_PAYLOAD_START);
1084       opt++; length--;
1085 
1086       if (length == 0) {
1087         coap_log(LOG_DEBUG,
1088                  "coap_pdu_parse: message ending in payload start marker\n");
1089         return 0;
1090       }
1091     }
1092     if (length > 0)
1093       pdu->data = (uint8_t*)opt;
1094     else
1095       pdu->data = NULL;
1096   }
1097 
1098   return good;
1099 }
1100 
1101 int
coap_pdu_parse(coap_proto_t proto,const uint8_t * data,size_t length,coap_pdu_t * pdu)1102 coap_pdu_parse(coap_proto_t proto,
1103                const uint8_t *data,
1104                size_t length,
1105                coap_pdu_t *pdu)
1106 {
1107   size_t hdr_size;
1108 
1109   if (length == 0)
1110     return 0;
1111   hdr_size = coap_pdu_parse_header_size(proto, data);
1112   if (!hdr_size || hdr_size > length)
1113     return 0;
1114   if (hdr_size > pdu->max_hdr_size)
1115     return 0;
1116   if (!coap_pdu_resize(pdu, length - hdr_size))
1117     return 0;
1118 #ifndef WITH_LWIP
1119   memcpy(pdu->token - hdr_size, data, length);
1120 #endif
1121   pdu->hdr_size = (uint8_t)hdr_size;
1122   pdu->used_size = length - hdr_size;
1123   return coap_pdu_parse_header(pdu, proto) && coap_pdu_parse_opt(pdu);
1124 }
1125 
1126 size_t
coap_pdu_encode_header(coap_pdu_t * pdu,coap_proto_t proto)1127 coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) {
1128   if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) {
1129     assert(pdu->max_hdr_size >= 4);
1130     if (pdu->max_hdr_size < 4) {
1131       coap_log(LOG_WARNING,
1132            "coap_pdu_encode_header: not enough space for UDP-style header\n");
1133       return 0;
1134     }
1135     pdu->token[-4] = COAP_DEFAULT_VERSION << 6
1136                    | pdu->type << 4
1137                    | pdu->token_length;
1138     pdu->token[-3] = pdu->code;
1139     pdu->token[-2] = (uint8_t)(pdu->mid >> 8);
1140     pdu->token[-1] = (uint8_t)(pdu->mid);
1141     pdu->hdr_size = 4;
1142   } else if (proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) {
1143     size_t len;
1144     assert(pdu->used_size >= pdu->token_length);
1145     if (pdu->used_size < pdu->token_length) {
1146       coap_log(LOG_WARNING, "coap_pdu_encode_header: corrupted PDU\n");
1147       return 0;
1148     }
1149     len = pdu->used_size - pdu->token_length;
1150     if (len <= COAP_MAX_MESSAGE_SIZE_TCP0) {
1151       assert(pdu->max_hdr_size >= 2);
1152       if (pdu->max_hdr_size < 2) {
1153         coap_log(LOG_WARNING,
1154               "coap_pdu_encode_header: not enough space for TCP0 header\n");
1155         return 0;
1156       }
1157       pdu->token[-2] = (uint8_t)len << 4
1158                      | pdu->token_length;
1159       pdu->token[-1] = pdu->code;
1160       pdu->hdr_size = 2;
1161     } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP8) {
1162       assert(pdu->max_hdr_size >= 3);
1163       if (pdu->max_hdr_size < 3) {
1164         coap_log(LOG_WARNING,
1165               "coap_pdu_encode_header: not enough space for TCP8 header\n");
1166         return 0;
1167       }
1168       pdu->token[-3] = 13 << 4 | pdu->token_length;
1169       pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP8);
1170       pdu->token[-1] = pdu->code;
1171       pdu->hdr_size = 3;
1172     } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP16) {
1173       assert(pdu->max_hdr_size >= 4);
1174       if (pdu->max_hdr_size < 4) {
1175         coap_log(LOG_WARNING,
1176               "coap_pdu_encode_header: not enough space for TCP16 header\n");
1177         return 0;
1178       }
1179       pdu->token[-4] = 14 << 4 | pdu->token_length;
1180       pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP16) >> 8);
1181       pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP16);
1182       pdu->token[-1] = pdu->code;
1183       pdu->hdr_size = 4;
1184     } else {
1185       assert(pdu->max_hdr_size >= 6);
1186       if (pdu->max_hdr_size < 6) {
1187         coap_log(LOG_WARNING,
1188               "coap_pdu_encode_header: not enough space for TCP32 header\n");
1189         return 0;
1190       }
1191       pdu->token[-6] = 15 << 4 | pdu->token_length;
1192       pdu->token[-5] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 24);
1193       pdu->token[-4] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 16);
1194       pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 8);
1195       pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP32);
1196       pdu->token[-1] = pdu->code;
1197       pdu->hdr_size = 6;
1198     }
1199   } else {
1200     coap_log(LOG_WARNING, "coap_pdu_encode_header: unsupported protocol\n");
1201   }
1202   return pdu->hdr_size;
1203 }
1204 
1205 coap_pdu_code_t
coap_pdu_get_code(const coap_pdu_t * pdu)1206 coap_pdu_get_code(const coap_pdu_t *pdu) {
1207   return pdu->code;
1208 }
1209 
1210 void
coap_pdu_set_code(coap_pdu_t * pdu,coap_pdu_code_t code)1211 coap_pdu_set_code(coap_pdu_t *pdu, coap_pdu_code_t code) {
1212   assert(code <= 0xff);
1213   pdu->code = code;
1214 }
1215 
coap_pdu_get_type(const coap_pdu_t * pdu)1216 coap_pdu_type_t coap_pdu_get_type(const coap_pdu_t *pdu) {
1217   return pdu->type;
1218 }
1219 
coap_pdu_set_type(coap_pdu_t * pdu,coap_pdu_type_t type)1220 void coap_pdu_set_type(coap_pdu_t *pdu, coap_pdu_type_t type) {
1221   assert(type <= 0x3);
1222   pdu->type = type;
1223 }
1224 
coap_pdu_get_token(const coap_pdu_t * pdu)1225 coap_bin_const_t coap_pdu_get_token(const coap_pdu_t *pdu) {
1226   coap_bin_const_t token;
1227 
1228   token.length = pdu->token_length;
1229   token.s = pdu->token;
1230   return token;
1231 }
1232 
coap_pdu_get_mid(const coap_pdu_t * pdu)1233 coap_mid_t coap_pdu_get_mid(const coap_pdu_t *pdu) {
1234   return pdu->mid;
1235 }
1236 
coap_pdu_set_mid(coap_pdu_t * pdu,coap_mid_t mid)1237 void coap_pdu_set_mid(coap_pdu_t *pdu, coap_mid_t mid) {
1238   assert(mid >= 0 && mid <= 0xffff);
1239   pdu->mid = mid;
1240 }
1241