• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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