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