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