1/* coap_uri.c -- helper functions for URI treatment 2 * 3 * Copyright (C) 2010--2012,2015-2016,2022-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_uri.c 13 * @brief URI handling functions 14 */ 15 16#include "coap3/coap_internal.h" 17 18#if defined(HAVE_LIMITS_H) 19#include <limits.h> 20#endif 21 22#include <stdint.h> 23#include <stdio.h> 24#include <string.h> 25#include <ctype.h> 26 27/** 28 * A length-safe version of strchr(). This function returns a pointer 29 * to the first occurrence of @p c in @p s, or @c NULL if not found. 30 * 31 * @param s The string to search for @p c. 32 * @param len The length of @p s. 33 * @param c The character to search. 34 * 35 * @return A pointer to the first occurence of @p c, or @c NULL 36 * if not found. 37 */ 38COAP_STATIC_INLINE const uint8_t * 39strnchr(const uint8_t *s, size_t len, unsigned char c) { 40 while (len && *s++ != c) 41 --len; 42 43 return len ? s : NULL; 44} 45 46typedef enum coap_uri_check_t { 47 COAP_URI_CHECK_URI, 48 COAP_URI_CHECK_PROXY 49} coap_uri_check_t; 50 51coap_uri_info_t coap_uri_scheme[COAP_URI_SCHEME_LAST] = { 52 { "coap", COAP_DEFAULT_PORT, 0, COAP_URI_SCHEME_COAP }, 53 { "coaps", COAPS_DEFAULT_PORT, 0, COAP_URI_SCHEME_COAPS }, 54 { "coap+tcp", COAP_DEFAULT_PORT, 0, COAP_URI_SCHEME_COAP_TCP }, 55 { "coaps+tcp", COAPS_DEFAULT_PORT, 0, COAP_URI_SCHEME_COAPS_TCP }, 56 { "http", 80, 1, COAP_URI_SCHEME_HTTP }, 57 { "https", 443, 1, COAP_URI_SCHEME_HTTPS }, 58 { "coap+ws", 80, 0, COAP_URI_SCHEME_COAP_WS }, 59 { "coaps+ws", 443, 0, COAP_URI_SCHEME_COAPS_WS } 60}; 61 62static int 63coap_split_uri_sub(const uint8_t *str_var, 64 size_t len, 65 coap_uri_t *uri, 66 coap_uri_check_t check_proxy) { 67 const uint8_t *p, *q; 68 int res = 0; 69 size_t i; 70 int is_unix_domain = 0; 71 72 if (!str_var || !uri || len == 0) 73 return -1; 74 75 memset(uri, 0, sizeof(coap_uri_t)); 76 uri->port = COAP_DEFAULT_PORT; 77 78 /* search for scheme */ 79 p = str_var; 80 if (*p == '/') { 81 /* no scheme, host or port */ 82 if (check_proxy == COAP_URI_CHECK_PROXY) { 83 /* Must have ongoing host if proxy definition */ 84 return -1; 85 } 86 q = p; 87 goto path; 88 } 89 90 /* find scheme terminating :// */ 91 while (len >= 3 && !(p[0] == ':' && p[1] == '/' && p[2] == '/')) { 92 ++p; 93 --len; 94 } 95 if (len < 3) { 96 /* scheme not defined with a :// terminator */ 97 res = -2; 98 goto error; 99 } 100 for (i = 0; i < COAP_URI_SCHEME_LAST; i++) { 101 if ((p - str_var) == (int)strlen(coap_uri_scheme[i].name) && 102 memcmp(str_var, coap_uri_scheme[i].name, p - str_var) == 0) { 103 if (check_proxy != COAP_URI_CHECK_PROXY && coap_uri_scheme[i].proxy_only) { 104 coap_log_err("%.*s URI scheme not enabled (not a proxy)\n", 105 (int)(p - str_var), str_var); 106 return -1; 107 } 108 uri->scheme = coap_uri_scheme[i].scheme; 109 uri->port = coap_uri_scheme[i].port; 110 break; 111 } 112 } 113 if (i == COAP_URI_SCHEME_LAST) { 114 /* scheme unknown */ 115 coap_log_err("%.*s URI scheme unknown\n", (int)(p - str_var), str_var); 116 res = -1; 117 goto error; 118 } 119 switch (uri->scheme) { 120 case COAP_URI_SCHEME_COAP: 121 break; 122 case COAP_URI_SCHEME_COAPS: 123 if (!coap_dtls_is_supported()) { 124 coap_log_err("coaps URI scheme not supported in this version of libcoap\n"); 125 return -1; 126 } 127 break; 128 case COAP_URI_SCHEME_COAP_TCP: 129 if (!coap_tcp_is_supported()) { 130 coap_log_err("coap+tcp URI scheme not supported in this version of libcoap\n"); 131 return -1; 132 } 133 break; 134 case COAP_URI_SCHEME_COAPS_TCP: 135 if (!coap_tcp_is_supported()) { 136 coap_log_err("coaps+tcp URI scheme not supported in this version of libcoap\n"); 137 return -1; 138 } 139 break; 140 case COAP_URI_SCHEME_COAP_WS: 141 if (!coap_ws_is_supported()) { 142 coap_log_err("coap+ws URI scheme not supported in this version of libcoap\n"); 143 return -1; 144 } 145 break; 146 case COAP_URI_SCHEME_COAPS_WS: 147 if (!coap_wss_is_supported()) { 148 coap_log_err("coaps+ws URI scheme not supported in this version of libcoap\n"); 149 return -1; 150 } 151 break; 152 case COAP_URI_SCHEME_HTTP: 153 case COAP_URI_SCHEME_HTTPS: 154 case COAP_URI_SCHEME_LAST: 155 default: 156 coap_log_warn("Unsupported URI type %d\n", uri->scheme); 157 return -1; 158 } 159 /* skip :// */ 160 p += 3; 161 len -= 3; 162 163 /* p points to beginning of Uri-Host */ 164 q = p; 165 if (len && *p == '[') { 166 /* IPv6 address reference */ 167 ++p; 168 169 while (len && *q != ']') { 170 ++q; 171 --len; 172 } 173 174 if (!len || *q != ']' || p == q) { 175 res = -3; 176 goto error; 177 } 178 179 COAP_SET_STR(&uri->host, q - p, p); 180 ++q; 181 --len; 182 } else { 183 /* IPv4 address, FQDN or Unix domain socket */ 184 if (len >= 3 && p[0] == '%' && p[1] == '2' && 185 (p[2] == 'F' || p[2] == 'f')) { 186 /* Unix domain definition */ 187 uri->port = 0; 188 is_unix_domain = 1; 189 } 190 while (len && *q != ':' && *q != '/' && *q != '?') { 191 ++q; 192 --len; 193 } 194 195 if (p == q) { 196 res = -3; 197 goto error; 198 } 199 200 COAP_SET_STR(&uri->host, q - p, p); 201 } 202 203 /* check for Uri-Port (invalid for Unix) */ 204 if (len && *q == ':') { 205 if (is_unix_domain) { 206 res = -5; 207 goto error; 208 } 209 p = ++q; 210 --len; 211 212 while (len && isdigit(*q)) { 213 ++q; 214 --len; 215 } 216 217 if (p < q) { /* explicit port number given */ 218 int uri_port = 0; 219 220 while ((p < q) && (uri_port <= UINT16_MAX)) 221 uri_port = uri_port * 10 + (*p++ - '0'); 222 223 /* check if port number is in allowed range */ 224 if (uri_port > UINT16_MAX) { 225 res = -4; 226 goto error; 227 } 228 229 uri->port = (uint16_t)uri_port; 230 } 231 } 232 233path: /* at this point, p must point to an absolute path */ 234 235 if (!len) 236 goto end; 237 238 if (*q == '/') { 239 p = ++q; 240 --len; 241 242 while (len && *q != '?') { 243 ++q; 244 --len; 245 } 246 247 if (p < q) { 248 COAP_SET_STR(&uri->path, q - p, p); 249 p = q; 250 } 251 } 252 253 /* Uri_Query */ 254 if (len && *p == '?') { 255 ++p; 256 --len; 257 COAP_SET_STR(&uri->query, len, p); 258 len = 0; 259 } 260 261end: 262 return len ? -1 : 0; 263 264error: 265 return res; 266} 267 268int 269coap_split_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) { 270 return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_URI); 271} 272 273int 274coap_split_proxy_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) { 275 return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_PROXY); 276} 277 278int 279coap_uri_into_options(const coap_uri_t *uri, const coap_address_t *dst, 280 coap_optlist_t **optlist_chain, int create_port_host_opt, 281 uint8_t *_buf, size_t _buflen) { 282 int res; 283 unsigned char *buf = _buf; 284 size_t buflen = _buflen; 285 286 if (create_port_host_opt && !coap_host_is_unix_domain(&uri->host)) { 287 int add_option = 0; 288 289 if (dst && uri->host.length) { 290#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI) 291 char addr[INET6_ADDRSTRLEN]; 292#else /* WITH_LWIP || WITH_CONTIKI */ 293 char addr[40]; 294#endif /* WITH_LWIP || WITH_CONTIKI */ 295 296 /* Add in UriHost if not match (need to strip off &iface) */ 297 size_t uri_host_len = uri->host.length; 298 const uint8_t *cp = uri->host.s; 299 300 /* Unfortunately not null terminated */ 301 for (size_t i = 0; i < uri_host_len; i++) { 302 if (cp[i] == '%') { 303 /* %iface specified in host name */ 304 uri_host_len = i; 305 break; 306 } 307 } 308 309 if (coap_print_ip_addr(dst, addr, sizeof(addr)) && 310 (strlen(addr) != uri_host_len || 311 memcmp(addr, uri->host.s, uri_host_len) != 0)) { 312 /* add Uri-Host */ 313 coap_insert_optlist(optlist_chain, 314 coap_new_optlist(COAP_OPTION_URI_HOST, 315 uri->host.length, 316 uri->host.s)); 317 } 318 } 319 /* Add in UriPort if not default */ 320 switch ((int)uri->scheme) { 321 case COAP_URI_SCHEME_HTTP: 322 case COAP_URI_SCHEME_COAP_WS: 323 if (uri->port != 80) 324 add_option = 1; 325 break; 326 case COAP_URI_SCHEME_HTTPS: 327 case COAP_URI_SCHEME_COAPS_WS: 328 if (uri->port != 443) 329 add_option = 1; 330 break; 331 default: 332 if (uri->port != (coap_uri_scheme_is_secure(uri) ? COAPS_DEFAULT_PORT : 333 COAP_DEFAULT_PORT)) 334 add_option = 1; 335 break; 336 } 337 if (add_option) 338 coap_insert_optlist(optlist_chain, 339 coap_new_optlist(COAP_OPTION_URI_PORT, 340 coap_encode_var_safe(buf, 4, 341 (uri->port & 0xffff)), 342 buf)); 343 } 344 345 if (uri->path.length) { 346 if (uri->path.length > buflen) 347 coap_log_warn("URI path will be truncated (max buffer %zu)\n", 348 buflen); 349 res = coap_split_path(uri->path.s, uri->path.length, buf, &buflen); 350 if (res < 0) 351 return -1; 352 353 while (res--) { 354 coap_insert_optlist(optlist_chain, 355 coap_new_optlist(COAP_OPTION_URI_PATH, 356 coap_opt_length(buf), 357 coap_opt_value(buf))); 358 359 buf += coap_opt_size(buf); 360 } 361 } 362 363 if (uri->query.length) { 364 buflen = _buflen; 365 buf = _buf; 366 if (uri->query.length > buflen) 367 coap_log_warn("URI query will be truncated (max buffer %zu)\n", 368 buflen); 369 res = coap_split_query(uri->query.s, uri->query.length, buf, &buflen); 370 if (res < 0) 371 return -1; 372 373 while (res--) { 374 coap_insert_optlist(optlist_chain, 375 coap_new_optlist(COAP_OPTION_URI_QUERY, 376 coap_opt_length(buf), 377 coap_opt_value(buf))); 378 379 buf += coap_opt_size(buf); 380 } 381 } 382 return 0; 383} 384 385int 386coap_host_is_unix_domain(const coap_str_const_t *host) { 387 if (host->length >= 3 && host->s[0] == '%' && 388 host->s[1] == '2' && 389 (host->s[2] == 'F' || host->s[2] == 'f')) { 390 return 1; 391 } 392 if (host->length >= 1 && host->s[0] == '/') 393 return 1; 394 return 0; 395} 396 397/** 398 * Calculates decimal value from hexadecimal ASCII character given in 399 * @p c. The caller must ensure that @p c actually represents a valid 400 * heaxdecimal character, e.g. with isxdigit(3). 401 * 402 * @hideinitializer 403 */ 404#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F)) 405 406/** 407 * Decodes percent-encoded characters while copying the string @p seg 408 * of size @p length to @p buf. The caller of this function must 409 * ensure that the percent-encodings are correct (i.e. the character 410 * '%' is always followed by two hex digits. and that @p buf provides 411 * sufficient space to hold the result. This function is supposed to 412 * be called by make_decoded_option() only. 413 * 414 * @param seg The segment to decode and copy. 415 * @param length Length of @p seg. 416 * @param buf The result buffer. 417 */ 418static void 419decode_segment(const uint8_t *seg, size_t length, unsigned char *buf) { 420 421 while (length--) { 422 423 if (*seg == '%') { 424 *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]); 425 426 seg += 2; 427 length -= 2; 428 } else { 429 *buf = *seg; 430 } 431 432 ++buf; 433 ++seg; 434 } 435} 436 437/** 438 * Runs through the given path (or query) segment and checks if 439 * percent-encodings are correct. This function returns @c 0 on success 440 * and @c -1 on error. 441 */ 442static int 443check_segment(const uint8_t *s, size_t length, size_t *segment_size) { 444 size_t n = 0; 445 446 while (length) { 447 if (*s == '%') { 448 if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2]))) 449 return -1; 450 451 s += 2; 452 length -= 2; 453 } 454 455 ++s; 456 ++n; 457 --length; 458 } 459 460 *segment_size = n; 461 462 return 0; 463} 464 465/** 466 * Writes a coap option from given string @p s to @p buf. @p s should 467 * point to a (percent-encoded) path or query segment of a coap_uri_t 468 * object. The created option will have type @c 0, and the length 469 * parameter will be set according to the size of the decoded string. 470 * On success, this function returns @c 0 and sets @p optionsize to the option's 471 * size. On error the function returns a value less than zero. This function 472 * must be called from coap_split_path_impl() only. 473 * 474 * @param s The string to decode. 475 * @param length The size of the percent-encoded string @p s. 476 * @param buf The buffer to store the new coap option. 477 * @param buflen The maximum size of @p buf. 478 * @param optionsize The option's size. 479 * 480 * @return @c 0 on success and @c -1 on error. 481 * 482 * @bug This function does not split segments that are bigger than 270 483 * bytes. 484 */ 485static int 486make_decoded_option(const uint8_t *s, size_t length, 487 unsigned char *buf, size_t buflen, size_t *optionsize) { 488 int res; 489 size_t segmentlen; 490 size_t written; 491 492 if (!buflen) { 493 coap_log_debug("make_decoded_option(): buflen is 0!\n"); 494 return -1; 495 } 496 497 res = check_segment(s, length, &segmentlen); 498 if (res < 0) 499 return -1; 500 501 /* write option header using delta 0 and length res */ 502 written = coap_opt_setheader(buf, buflen, 0, segmentlen); 503 504 assert(written <= buflen); 505 506 if (!written) /* encoding error */ 507 return -1; 508 509 buf += written; /* advance past option type/length */ 510 buflen -= written; 511 512 if (buflen < segmentlen) { 513 coap_log_debug("buffer too small for option\n"); 514 return -1; 515 } 516 517 decode_segment(s, length, buf); 518 519 *optionsize = written + segmentlen; 520 521 return 0; 522} 523 524 525#ifndef min 526#define min(a,b) ((a) < (b) ? (a) : (b)) 527#endif 528 529typedef void (*segment_handler_t)(const uint8_t *, size_t, void *); 530 531/** 532 * Checks if path segment @p s consists of one or two dots. 533 */ 534COAP_STATIC_INLINE int 535dots(const uint8_t *s, size_t len) { 536 return len && *s == '.' && (len == 1 || (len == 2 && *(s+1) == '.')); 537} 538 539/** 540 * Splits the given string into segments. You should call one of the 541 * macros coap_split_path() or coap_split_query() instead. 542 * 543 * @param s The URI string to be tokenized. 544 * @param length The length of @p s. 545 * @param h A handler that is called with every token. 546 * @param data Opaque data that is passed to @p h when called. 547 * 548 * @return The number of characters that have been parsed from @p s. 549 */ 550static size_t 551coap_split_path_impl(const uint8_t *s, size_t length, 552 segment_handler_t h, void *data) { 553 554 const uint8_t *p, *q; 555 556 p = q = s; 557 while (length > 0 && !strnchr((const uint8_t *)"?#", 2, *q)) { 558 if (*q == '/') { /* start new segment */ 559 560 if (!dots(p, q - p)) { 561 h(p, q - p, data); 562 } 563 564 p = q + 1; 565 } 566 567 q++; 568 length--; 569 } 570 571 /* write last segment */ 572 if (!dots(p, q - p)) { 573 h(p, q - p, data); 574 } 575 576 return q - s; 577} 578 579struct cnt_str { 580 coap_string_t buf; 581 int n; 582}; 583 584static void 585write_option(const uint8_t *s, size_t len, void *data) { 586 struct cnt_str *state = (struct cnt_str *)data; 587 int res; 588 size_t optionsize; 589 assert(state); 590 591 res = make_decoded_option(s, len, state->buf.s, state->buf.length, &optionsize); 592 if (res == 0) { 593 state->buf.s += optionsize; 594 state->buf.length -= optionsize; 595 state->n++; 596 } 597} 598 599int 600coap_split_path(const uint8_t *s, size_t length, 601 unsigned char *buf, size_t *buflen) { 602 struct cnt_str tmp = { { *buflen, buf }, 0 }; 603 604 coap_split_path_impl(s, length, write_option, &tmp); 605 606 *buflen = *buflen - tmp.buf.length; 607 608 return tmp.n; 609} 610 611int 612coap_split_query(const uint8_t *s, size_t length, 613 unsigned char *buf, size_t *buflen) { 614 struct cnt_str tmp = { { *buflen, buf }, 0 }; 615 const uint8_t *p; 616 617 p = s; 618 while (length > 0 && *s != '#') { 619 if (*s == '&') { /* start new query element */ 620 write_option(p, s - p, &tmp); 621 p = s + 1; 622 } 623 624 s++; 625 length--; 626 } 627 628 /* write last query element */ 629 write_option(p, s - p, &tmp); 630 631 *buflen = *buflen - tmp.buf.length; 632 return tmp.n; 633} 634 635#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t)) 636 637coap_uri_t * 638coap_new_uri(const uint8_t *uri, unsigned int length) { 639 unsigned char *result; 640 641 result = (unsigned char *)coap_malloc_type(COAP_STRING, length + 1 + sizeof(coap_uri_t)); 642 643 if (!result) 644 return NULL; 645 646 memcpy(URI_DATA(result), uri, length); 647 URI_DATA(result)[length] = '\0'; /* make it zero-terminated */ 648 649 if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) { 650 coap_free_type(COAP_STRING, result); 651 return NULL; 652 } 653 return (coap_uri_t *)result; 654} 655 656coap_uri_t * 657coap_clone_uri(const coap_uri_t *uri) { 658 coap_uri_t *result; 659 uint8_t *p; 660 661 if (!uri) 662 return NULL; 663 664 result = (coap_uri_t *)coap_malloc_type(COAP_STRING, uri->query.length + uri->host.length + 665 uri->path.length + sizeof(coap_uri_t) + 1); 666 667 if (!result) 668 return NULL; 669 670 memset(result, 0, sizeof(coap_uri_t)); 671 672 result->port = uri->port; 673 674 if (uri->host.length) { 675 result->host.s = p = URI_DATA(result); 676 result->host.length = uri->host.length; 677 678 memcpy(p, uri->host.s, uri->host.length); 679 } 680 681 if (uri->path.length) { 682 result->path.s = p = URI_DATA(result) + uri->host.length; 683 result->path.length = uri->path.length; 684 685 memcpy(p, uri->path.s, uri->path.length); 686 } 687 688 if (uri->query.length) { 689 result->query.s = p = URI_DATA(result) + uri->host.length + uri->path.length; 690 result->query.length = uri->query.length; 691 692 memcpy(p, uri->query.s, uri->query.length); 693 } 694 695 return result; 696} 697 698void 699coap_delete_uri(coap_uri_t *uri) { 700 coap_free_type(COAP_STRING, uri); 701} 702 703COAP_STATIC_INLINE int 704is_unescaped_in_path(const uint8_t c) { 705 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || 706 (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || 707 c == '~' || c == '!' || c == '$' || c == '\'' || c == '(' || 708 c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || 709 c=='=' || c==':' || c=='@' || c == '&'; 710} 711 712COAP_STATIC_INLINE int 713is_unescaped_in_query(const uint8_t c) { 714 return is_unescaped_in_path(c) || c=='/' || c=='?'; 715} 716 717coap_string_t * 718coap_get_query(const coap_pdu_t *request) { 719 coap_opt_iterator_t opt_iter; 720 coap_opt_filter_t f; 721 coap_opt_t *q; 722 coap_string_t *query = NULL; 723 size_t length = 0; 724 static const uint8_t hex[] = "0123456789ABCDEF"; 725 726 coap_option_filter_clear(&f); 727 coap_option_filter_set(&f, COAP_OPTION_URI_QUERY); 728 coap_option_iterator_init(request, &opt_iter, &f); 729 while ((q = coap_option_next(&opt_iter))) { 730 uint16_t seg_len = coap_opt_length(q), i; 731 const uint8_t *seg= coap_opt_value(q); 732 for (i = 0; i < seg_len; i++) { 733 if (is_unescaped_in_query(seg[i])) 734 length += 1; 735 else 736 length += 3; 737 } 738 length += 1; 739 } 740 if (length > 0) 741 length -= 1; 742 if (length > 0) { 743 query = coap_new_string(length); 744 if (query) { 745 query->length = length; 746 unsigned char *s = query->s; 747 coap_option_iterator_init(request, &opt_iter, &f); 748 while ((q = coap_option_next(&opt_iter))) { 749 if (s != query->s) 750 *s++ = '&'; 751 uint16_t seg_len = coap_opt_length(q), i; 752 const uint8_t *seg= coap_opt_value(q); 753 for (i = 0; i < seg_len; i++) { 754 if (is_unescaped_in_query(seg[i])) { 755 *s++ = seg[i]; 756 } else { 757 *s++ = '%'; 758 *s++ = hex[seg[i]>>4]; 759 *s++ = hex[seg[i]&0x0F]; 760 } 761 } 762 } 763 } 764 } 765 return query; 766} 767 768coap_string_t * 769coap_get_uri_path(const coap_pdu_t *request) { 770 coap_opt_iterator_t opt_iter; 771 coap_opt_filter_t f; 772 coap_opt_t *q; 773 coap_string_t *uri_path = NULL; 774 size_t length = 0; 775 static const uint8_t hex[] = "0123456789ABCDEF"; 776 777 q = coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter); 778 if (q) { 779 coap_uri_t uri; 780 781 if (coap_split_proxy_uri(coap_opt_value(q), 782 coap_opt_length(q), &uri) < 0) { 783 return NULL; 784 } 785 uri_path = coap_new_string(uri.path.length); 786 if (uri_path) { 787 memcpy(uri_path->s, uri.path.s, uri.path.length); 788 } 789 return uri_path; 790 } 791 792 coap_option_filter_clear(&f); 793 coap_option_filter_set(&f, COAP_OPTION_URI_PATH); 794 coap_option_iterator_init(request, &opt_iter, &f); 795 while ((q = coap_option_next(&opt_iter))) { 796 uint16_t seg_len = coap_opt_length(q), i; 797 const uint8_t *seg= coap_opt_value(q); 798 for (i = 0; i < seg_len; i++) { 799 if (is_unescaped_in_path(seg[i])) 800 length += 1; 801 else 802 length += 3; 803 } 804 /* bump for the leading "/" */ 805 length += 1; 806 } 807 /* The first entry does not have a leading "/" */ 808 if (length > 0) 809 length -= 1; 810 811 /* if 0, either no URI_PATH Option, or the first one was empty */ 812 uri_path = coap_new_string(length); 813 if (uri_path) { 814 uri_path->length = length; 815 unsigned char *s = uri_path->s; 816 int n = 0; 817 coap_option_iterator_init(request, &opt_iter, &f); 818 while ((q = coap_option_next(&opt_iter))) { 819 if (n++) { 820 *s++ = '/'; 821 } 822 uint16_t seg_len = coap_opt_length(q), i; 823 const uint8_t *seg= coap_opt_value(q); 824 for (i = 0; i < seg_len; i++) { 825 if (is_unescaped_in_path(seg[i])) { 826 *s++ = seg[i]; 827 } else { 828 *s++ = '%'; 829 *s++ = hex[seg[i]>>4]; 830 *s++ = hex[seg[i]&0x0F]; 831 } 832 } 833 } 834 } 835 return uri_path; 836} 837