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