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