• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* uri.c -- helper functions for URI treatment
2  *
3  * Copyright (C) 2010--2012,2015-2016 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 #include "coap3/coap_internal.h"
12 
13 #if defined(HAVE_LIMITS_H)
14 #include <limits.h>
15 #endif
16 
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <ctype.h>
21 
22 /**
23  * A length-safe version of strchr(). This function returns a pointer
24  * to the first occurrence of @p c  in @p s, or @c NULL if not found.
25  *
26  * @param s   The string to search for @p c.
27  * @param len The length of @p s.
28  * @param c   The character to search.
29  *
30  * @return A pointer to the first occurence of @p c, or @c NULL
31  * if not found.
32  */
33 COAP_STATIC_INLINE const uint8_t *
strnchr(const uint8_t * s,size_t len,unsigned char c)34 strnchr(const uint8_t *s, size_t len, unsigned char c) {
35   while (len && *s++ != c)
36     --len;
37 
38   return len ? s : NULL;
39 }
40 
41 #define ISEQUAL_CI(a,b) \
42   ((a) == (b) || (islower(b) && ((a) == ((b) - 0x20))))
43 
44 typedef enum coap_uri_check_t {
45   COAP_URI_CHECK_URI,
46   COAP_URI_CHECK_PROXY
47 } coap_uri_check_t;
48 
49 static int
coap_split_uri_sub(const uint8_t * str_var,size_t len,coap_uri_t * uri,coap_uri_check_t check_proxy)50 coap_split_uri_sub(const uint8_t *str_var,
51                    size_t len,
52                    coap_uri_t *uri,
53                    coap_uri_check_t check_proxy) {
54   const uint8_t *p, *q;
55   int res = 0;
56   int is_http_proxy_scheme = 0;
57   size_t keep_len = len;
58 
59   if (!str_var || !uri)
60     return -1;
61 
62   memset(uri, 0, sizeof(coap_uri_t));
63   uri->port = COAP_DEFAULT_PORT;
64 
65   /* search for scheme */
66   p = str_var;
67   if (*p == '/') {
68     if (check_proxy == COAP_URI_CHECK_PROXY)
69       return -1;
70     q = p;
71     goto path;
72   }
73 
74   q = (const uint8_t *)COAP_DEFAULT_SCHEME;
75   while (len && *q && ISEQUAL_CI(*p, *q)) {
76     ++p; ++q; --len;
77   }
78   if (*q && check_proxy == COAP_URI_CHECK_PROXY) {
79     /* Scheme could be something other than coap */
80     len = keep_len;
81     p = str_var;
82     q = (const uint8_t *)"http";
83     while (len && *q && ISEQUAL_CI(*p, *q)) {
84       ++p; ++q; --len;
85     }
86     if (*q == 0) {
87       if (len && ISEQUAL_CI(*p, 's')) {
88         /* https:// */
89         ++p; --len;
90         uri->scheme = COAP_URI_SCHEME_HTTPS;
91         uri->port = 443;
92       }
93       else {
94         /* http:// */
95         uri->scheme = COAP_URI_SCHEME_HTTP;
96         uri->port = 80;
97       }
98     }
99     else {
100       /* Unknown scheme */
101       res = -1;
102       goto error;
103     }
104     is_http_proxy_scheme = 1;
105   }
106 
107   /* If q does not point to the string end marker '\0', the schema
108    * identifier is wrong. */
109   if (*q) {
110     res = -1;
111     goto error;
112   }
113 
114   if (is_http_proxy_scheme == 0) {
115     /* There might be an additional 's', indicating the secure version: */
116     if (len && (*p == 's')) {
117       ++p; --len;
118       uri->scheme = COAP_URI_SCHEME_COAPS;
119       uri->port = COAPS_DEFAULT_PORT;
120     } else {
121       uri->scheme = COAP_URI_SCHEME_COAP;
122     }
123 
124     /* There might be an addition "+tcp", indicating reliable transport: */
125     if (len>=4 && p[0] == '+' && p[1] == 't' && p[2] == 'c' && p[3] == 'p' ) {
126       p += 4;
127       len -= 4;
128       if (uri->scheme == COAP_URI_SCHEME_COAPS)
129         uri->scheme = COAP_URI_SCHEME_COAPS_TCP;
130       else
131         uri->scheme = COAP_URI_SCHEME_COAP_TCP;
132     }
133   }
134   q = (const uint8_t *)"://";
135   while (len && *q && *p == *q) {
136     ++p; ++q; --len;
137   }
138 
139   if (*q) {
140     res = -2;
141     goto error;
142   }
143 
144   /* p points to beginning of Uri-Host */
145   q = p;
146   if (len && *p == '[') {        /* IPv6 address reference */
147     ++p;
148 
149     while (len && *q != ']') {
150       ++q; --len;
151     }
152 
153     if (!len || *q != ']' || p == q) {
154       res = -3;
155       goto error;
156     }
157 
158     COAP_SET_STR(&uri->host, q - p, p);
159     ++q; --len;
160   } else {                        /* IPv4 address or FQDN */
161     while (len && *q != ':' && *q != '/' && *q != '?') {
162       ++q;
163       --len;
164     }
165 
166     if (p == q) {
167       res = -3;
168       goto error;
169     }
170 
171     COAP_SET_STR(&uri->host, q - p, p);
172   }
173 
174   /* check for Uri-Port */
175   if (len && *q == ':') {
176     p = ++q;
177     --len;
178 
179     while (len && isdigit(*q)) {
180       ++q;
181       --len;
182     }
183 
184     if (p < q) {                /* explicit port number given */
185       int uri_port = 0;
186 
187       while ((p < q) && (uri_port <= UINT16_MAX))
188               uri_port = uri_port * 10 + (*p++ - '0');
189 
190       /* check if port number is in allowed range */
191       if (uri_port > UINT16_MAX) {
192               res = -4;
193               goto error;
194       }
195 
196       uri->port = (uint16_t)uri_port;
197     }
198   }
199 
200  path:                 /* at this point, p must point to an absolute path */
201 
202   if (!len)
203     goto end;
204 
205   if (*q == '/') {
206     p = ++q;
207     --len;
208 
209     while (len && *q != '?') {
210       ++q;
211       --len;
212     }
213 
214     if (p < q) {
215       COAP_SET_STR(&uri->path, q - p, p);
216       p = q;
217     }
218   }
219 
220   /* Uri_Query */
221   if (len && *p == '?') {
222     ++p;
223     --len;
224     COAP_SET_STR(&uri->query, len, p);
225     len = 0;
226   }
227 
228   end:
229   return len ? -1 : 0;
230 
231   error:
232   return res;
233 }
234 
235 int
coap_split_uri(const uint8_t * str_var,size_t len,coap_uri_t * uri)236 coap_split_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) {
237   return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_URI);
238 }
239 
240 int
coap_split_proxy_uri(const uint8_t * str_var,size_t len,coap_uri_t * uri)241 coap_split_proxy_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) {
242   return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_PROXY);
243 }
244 
245 /**
246  * Calculates decimal value from hexadecimal ASCII character given in
247  * @p c. The caller must ensure that @p c actually represents a valid
248  * heaxdecimal character, e.g. with isxdigit(3).
249  *
250  * @hideinitializer
251  */
252 #define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
253 
254 /**
255  * Decodes percent-encoded characters while copying the string @p seg
256  * of size @p length to @p buf. The caller of this function must
257  * ensure that the percent-encodings are correct (i.e. the character
258  * '%' is always followed by two hex digits. and that @p buf provides
259  * sufficient space to hold the result. This function is supposed to
260  * be called by make_decoded_option() only.
261  *
262  * @param seg     The segment to decode and copy.
263  * @param length  Length of @p seg.
264  * @param buf     The result buffer.
265  */
266 static void
decode_segment(const uint8_t * seg,size_t length,unsigned char * buf)267 decode_segment(const uint8_t *seg, size_t length, unsigned char *buf) {
268 
269   while (length--) {
270 
271     if (*seg == '%') {
272       *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
273 
274       seg += 2; length -= 2;
275     } else {
276       *buf = *seg;
277     }
278 
279     ++buf; ++seg;
280   }
281 }
282 
283 /**
284  * Runs through the given path (or query) segment and checks if
285  * percent-encodings are correct. This function returns @c 0 on success
286  * and @c -1 on error.
287  */
288 static int
check_segment(const uint8_t * s,size_t length,size_t * segment_size)289 check_segment(const uint8_t *s, size_t length, size_t *segment_size) {
290   size_t n = 0;
291 
292   while (length) {
293     if (*s == '%') {
294       if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
295               return -1;
296 
297       s += 2;
298       length -= 2;
299     }
300 
301     ++s; ++n; --length;
302   }
303 
304   *segment_size = n;
305 
306   return 0;
307 }
308 
309 /**
310  * Writes a coap option from given string @p s to @p buf. @p s should
311  * point to a (percent-encoded) path or query segment of a coap_uri_t
312  * object.  The created option will have type @c 0, and the length
313  * parameter will be set according to the size of the decoded string.
314  * On success, this function returns @c 0 and sets @p optionsize to the option's
315  * size. On error the function returns a value less than zero. This function
316  * must be called from coap_split_path_impl() only.
317  *
318  * @param s           The string to decode.
319  * @param length      The size of the percent-encoded string @p s.
320  * @param buf         The buffer to store the new coap option.
321  * @param buflen      The maximum size of @p buf.
322  * @param optionsize  The option's size.
323  *
324  * @return @c 0 on success and @c -1 on error.
325  *
326  * @bug This function does not split segments that are bigger than 270
327  * bytes.
328  */
329 static int
make_decoded_option(const uint8_t * s,size_t length,unsigned char * buf,size_t buflen,size_t * optionsize)330 make_decoded_option(const uint8_t *s, size_t length,
331                     unsigned char *buf, size_t buflen, size_t* optionsize) {
332   int res;
333   size_t segmentlen;
334   size_t written;
335 
336   if (!buflen) {
337     coap_log(LOG_DEBUG, "make_decoded_option(): buflen is 0!\n");
338     return -1;
339   }
340 
341   res = check_segment(s, length, &segmentlen);
342   if (res < 0)
343     return -1;
344 
345   /* write option header using delta 0 and length res */
346   written = coap_opt_setheader(buf, buflen, 0, segmentlen);
347 
348   assert(written <= buflen);
349 
350   if (!written)                        /* encoding error */
351     return -1;
352 
353   buf += written;                /* advance past option type/length */
354   buflen -= written;
355 
356   if (buflen < segmentlen) {
357     coap_log(LOG_DEBUG, "buffer too small for option\n");
358     return -1;
359   }
360 
361   decode_segment(s, length, buf);
362 
363   *optionsize = written + segmentlen;
364 
365   return 0;
366 }
367 
368 
369 #ifndef min
370 #define min(a,b) ((a) < (b) ? (a) : (b))
371 #endif
372 
373 typedef void (*segment_handler_t)(const uint8_t *, size_t, void *);
374 
375 /**
376  * Checks if path segment @p s consists of one or two dots.
377  */
378 COAP_STATIC_INLINE int
dots(const uint8_t * s,size_t len)379 dots(const uint8_t *s, size_t len) {
380   return len && *s == '.' && (len == 1 || (len == 2 && *(s+1) == '.'));
381 }
382 
383 /**
384  * Splits the given string into segments. You should call one of the
385  * macros coap_split_path() or coap_split_query() instead.
386  *
387  * @param s      The URI string to be tokenized.
388  * @param length The length of @p s.
389  * @param h      A handler that is called with every token.
390  * @param data   Opaque data that is passed to @p h when called.
391  *
392  * @return The number of characters that have been parsed from @p s.
393  */
394 static size_t
coap_split_path_impl(const uint8_t * s,size_t length,segment_handler_t h,void * data)395 coap_split_path_impl(const uint8_t *s, size_t length,
396                      segment_handler_t h, void *data) {
397 
398   const uint8_t *p, *q;
399 
400   p = q = s;
401   while (length > 0 && !strnchr((const uint8_t *)"?#", 2, *q)) {
402     if (*q == '/') {                /* start new segment */
403 
404       if (!dots(p, q - p)) {
405         h(p, q - p, data);
406       }
407 
408       p = q + 1;
409     }
410 
411     q++;
412     length--;
413   }
414 
415   /* write last segment */
416   if (!dots(p, q - p)) {
417     h(p, q - p, data);
418   }
419 
420   return q - s;
421 }
422 
423 struct cnt_str {
424   coap_string_t buf;
425   int n;
426 };
427 
428 static void
write_option(const uint8_t * s,size_t len,void * data)429 write_option(const uint8_t *s, size_t len, void *data) {
430   struct cnt_str *state = (struct cnt_str *)data;
431   int res;
432   size_t optionsize;
433   assert(state);
434 
435   res = make_decoded_option(s, len, state->buf.s, state->buf.length, &optionsize);
436   if (res == 0) {
437     state->buf.s += optionsize;
438     state->buf.length -= optionsize;
439     state->n++;
440   }
441 }
442 
443 int
coap_split_path(const uint8_t * s,size_t length,unsigned char * buf,size_t * buflen)444 coap_split_path(const uint8_t *s, size_t length,
445                 unsigned char *buf, size_t *buflen) {
446   struct cnt_str tmp = { { *buflen, buf }, 0 };
447 
448   coap_split_path_impl(s, length, write_option, &tmp);
449 
450   *buflen = *buflen - tmp.buf.length;
451 
452   return tmp.n;
453 }
454 
455 int
coap_split_query(const uint8_t * s,size_t length,unsigned char * buf,size_t * buflen)456 coap_split_query(const uint8_t *s, size_t length,
457                 unsigned char *buf, size_t *buflen) {
458   struct cnt_str tmp = { { *buflen, buf }, 0 };
459   const uint8_t *p;
460 
461   p = s;
462   while (length > 0 && *s != '#') {
463     if (*s == '&') {                /* start new query element */
464       write_option(p, s - p, &tmp);
465       p = s + 1;
466     }
467 
468     s++;
469     length--;
470   }
471 
472   /* write last query element */
473   write_option(p, s - p, &tmp);
474 
475   *buflen = *buflen - tmp.buf.length;
476   return tmp.n;
477 }
478 
479 #define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
480 
481 coap_uri_t *
coap_new_uri(const uint8_t * uri,unsigned int length)482 coap_new_uri(const uint8_t *uri, unsigned int length) {
483   unsigned char *result;
484 
485   result = (unsigned char*)coap_malloc(length + 1 + sizeof(coap_uri_t));
486 
487   if (!result)
488     return NULL;
489 
490   memcpy(URI_DATA(result), uri, length);
491   URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
492 
493   if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
494     coap_free(result);
495     return NULL;
496   }
497   return (coap_uri_t *)result;
498 }
499 
500 coap_uri_t *
coap_clone_uri(const coap_uri_t * uri)501 coap_clone_uri(const coap_uri_t *uri) {
502   coap_uri_t *result;
503   uint8_t *p;
504 
505   if ( !uri )
506     return  NULL;
507 
508   result = (coap_uri_t *)coap_malloc( uri->query.length + uri->host.length +
509                                       uri->path.length + sizeof(coap_uri_t) + 1);
510 
511   if ( !result )
512     return NULL;
513 
514   memset( result, 0, sizeof(coap_uri_t) );
515 
516   result->port = uri->port;
517 
518   if ( uri->host.length ) {
519     result->host.s = p = URI_DATA(result);
520     result->host.length = uri->host.length;
521 
522     memcpy(p, uri->host.s, uri->host.length);
523   }
524 
525   if ( uri->path.length ) {
526     result->path.s = p = URI_DATA(result) + uri->host.length;
527     result->path.length = uri->path.length;
528 
529     memcpy(p, uri->path.s, uri->path.length);
530   }
531 
532   if ( uri->query.length ) {
533     result->query.s = p = URI_DATA(result) + uri->host.length + uri->path.length;
534     result->query.length = uri->query.length;
535 
536     memcpy (p, uri->query.s, uri->query.length);
537   }
538 
539   return result;
540 }
541 
542 COAP_STATIC_INLINE int
is_unescaped_in_path(const uint8_t c)543 is_unescaped_in_path(const uint8_t c) {
544   return ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' )
545       || ( c >= '0' && c <= '9' ) || c == '-' || c == '.' || c == '_'
546       || c == '~' || c == '!' || c == '$' || c == '\'' || c == '('
547       || c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || c=='='
548       || c==':' || c=='@' || c == '&';
549 }
550 
551 COAP_STATIC_INLINE int
is_unescaped_in_query(const uint8_t c)552 is_unescaped_in_query(const uint8_t c) {
553   return is_unescaped_in_path(c) || c=='/' || c=='?';
554 }
555 
coap_get_query(const coap_pdu_t * request)556 coap_string_t *coap_get_query(const coap_pdu_t *request) {
557   coap_opt_iterator_t opt_iter;
558   coap_opt_filter_t f;
559   coap_opt_t *q;
560   coap_string_t *query = NULL;
561   size_t length = 0;
562   static const uint8_t hex[] = "0123456789ABCDEF";
563 
564   coap_option_filter_clear(&f);
565   coap_option_filter_set(&f, COAP_OPTION_URI_QUERY);
566   coap_option_iterator_init(request, &opt_iter, &f);
567   while ((q = coap_option_next(&opt_iter))) {
568     uint16_t seg_len = coap_opt_length(q), i;
569     const uint8_t *seg= coap_opt_value(q);
570     for (i = 0; i < seg_len; i++) {
571       if (is_unescaped_in_query(seg[i]))
572         length += 1;
573       else
574         length += 3;
575     }
576     length += 1;
577   }
578   if (length > 0)
579     length -= 1;
580   if (length > 0) {
581     query = coap_new_string(length);
582     if (query) {
583       query->length = length;
584       unsigned char *s = query->s;
585       coap_option_iterator_init(request, &opt_iter, &f);
586       while ((q = coap_option_next(&opt_iter))) {
587         if (s != query->s)
588           *s++ = '&';
589         uint16_t seg_len = coap_opt_length(q), i;
590         const uint8_t *seg= coap_opt_value(q);
591         for (i = 0; i < seg_len; i++) {
592           if (is_unescaped_in_query(seg[i])) {
593             *s++ = seg[i];
594           } else {
595             *s++ = '%';
596             *s++ = hex[seg[i]>>4];
597             *s++ = hex[seg[i]&0x0F];
598           }
599         }
600       }
601     }
602   }
603   return query;
604 }
605 
coap_get_uri_path(const coap_pdu_t * request)606 coap_string_t *coap_get_uri_path(const coap_pdu_t *request) {
607   coap_opt_iterator_t opt_iter;
608   coap_opt_filter_t f;
609   coap_opt_t *q;
610   coap_string_t *uri_path = NULL;
611   size_t length = 0;
612   static const uint8_t hex[] = "0123456789ABCDEF";
613 
614   coap_option_filter_clear(&f);
615   coap_option_filter_set(&f, COAP_OPTION_URI_PATH);
616   coap_option_iterator_init(request, &opt_iter, &f);
617   while ((q = coap_option_next(&opt_iter))) {
618     uint16_t seg_len = coap_opt_length(q), i;
619     const uint8_t *seg= coap_opt_value(q);
620     for (i = 0; i < seg_len; i++) {
621       if (is_unescaped_in_path(seg[i]))
622         length += 1;
623       else
624         length += 3;
625     }
626     /* bump for the leading "/" */
627     length += 1;
628   }
629   /* The first entry does not have a leading "/" */
630   if (length > 0)
631     length -= 1;
632 
633   /* if 0, either no URI_PATH Option, or the first one was empty */
634   uri_path = coap_new_string(length);
635   if (uri_path) {
636     uri_path->length = length;
637     unsigned char *s = uri_path->s;
638     int n = 0;
639     coap_option_iterator_init(request, &opt_iter, &f);
640     while ((q = coap_option_next(&opt_iter))) {
641       if (n++) {
642         *s++ = '/';
643       }
644       uint16_t seg_len = coap_opt_length(q), i;
645       const uint8_t *seg= coap_opt_value(q);
646       for (i = 0; i < seg_len; i++) {
647         if (is_unescaped_in_path(seg[i])) {
648           *s++ = seg[i];
649         } else {
650           *s++ = '%';
651           *s++ = hex[seg[i]>>4];
652           *s++ = hex[seg[i]&0x0F];
653         }
654       }
655     }
656   }
657   return uri_path;
658 }
659 
660