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 */
38 COAP_STATIC_INLINE const uint8_t *
strnchr(const uint8_t * s,size_t len,unsigned char c)39 strnchr(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
46 typedef enum coap_uri_check_t {
47 COAP_URI_CHECK_URI,
48 COAP_URI_CHECK_PROXY
49 } coap_uri_check_t;
50
51 coap_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
62 static int
coap_split_uri_sub(const uint8_t * str_var,size_t len,coap_uri_t * uri,coap_uri_check_t check_proxy)63 coap_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
233 path: /* 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
261 end:
262 return len ? -1 : 0;
263
264 error:
265 return res;
266 }
267
268 int
coap_split_uri(const uint8_t * str_var,size_t len,coap_uri_t * uri)269 coap_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
273 int
coap_split_proxy_uri(const uint8_t * str_var,size_t len,coap_uri_t * uri)274 coap_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
278 int
coap_uri_into_options(const coap_uri_t * uri,const coap_address_t * dst,coap_optlist_t ** optlist_chain,int create_port_host_opt,uint8_t * _buf,size_t _buflen)279 coap_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
385 int
coap_host_is_unix_domain(const coap_str_const_t * host)386 coap_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 */
418 static void
decode_segment(const uint8_t * seg,size_t length,unsigned char * buf)419 decode_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 */
442 static int
check_segment(const uint8_t * s,size_t length,size_t * segment_size)443 check_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 */
485 static int
make_decoded_option(const uint8_t * s,size_t length,unsigned char * buf,size_t buflen,size_t * optionsize)486 make_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
529 typedef 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 */
534 COAP_STATIC_INLINE int
dots(const uint8_t * s,size_t len)535 dots(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 */
550 static size_t
coap_split_path_impl(const uint8_t * s,size_t length,segment_handler_t h,void * data)551 coap_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
579 struct cnt_str {
580 coap_string_t buf;
581 int n;
582 };
583
584 static void
write_option(const uint8_t * s,size_t len,void * data)585 write_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
599 int
coap_split_path(const uint8_t * s,size_t length,unsigned char * buf,size_t * buflen)600 coap_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
611 int
coap_split_query(const uint8_t * s,size_t length,unsigned char * buf,size_t * buflen)612 coap_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
637 coap_uri_t *
coap_new_uri(const uint8_t * uri,unsigned int length)638 coap_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
656 coap_uri_t *
coap_clone_uri(const coap_uri_t * uri)657 coap_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
698 void
coap_delete_uri(coap_uri_t * uri)699 coap_delete_uri(coap_uri_t *uri) {
700 coap_free_type(COAP_STRING, uri);
701 }
702
703 COAP_STATIC_INLINE int
is_unescaped_in_path(const uint8_t c)704 is_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
712 COAP_STATIC_INLINE int
is_unescaped_in_query(const uint8_t c)713 is_unescaped_in_query(const uint8_t c) {
714 return is_unescaped_in_path(c) || c=='/' || c=='?';
715 }
716
717 coap_string_t *
coap_get_query(const coap_pdu_t * request)718 coap_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
768 coap_string_t *
coap_get_uri_path(const coap_pdu_t * request)769 coap_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