1 /* uri.c -- helper functions for URI treatment
2  *
3  * Copyright (C) 2010--2012,2015 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_config.h"
10 
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
12 # include <assert.h>
13 #endif
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18 
19 #include "mem.h"
20 #include "debug.h"
21 #include "pdu.h"
22 #include "option.h"
23 #include "uri.h"
24 
25 /**
26  * A length-safe version of strchr(). This function returns a pointer
27  * to the first occurrence of @p c  in @p s, or @c NULL if not found.
28  *
29  * @param s   The string to search for @p c.
30  * @param len The length of @p s.
31  * @param c   The character to search.
32  *
33  * @return A pointer to the first occurence of @p c, or @c NULL
34  * if not found.
35  */
36 static inline unsigned char *
strnchr(unsigned char * s,size_t len,unsigned char c)37 strnchr(unsigned char *s, size_t len, unsigned char c) {
38   while (len && *s++ != c)
39     --len;
40 
41   return len ? s : NULL;
42 }
43 
44 int
coap_split_uri(unsigned char * str_var,size_t len,coap_uri_t * uri)45 coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri) {
46   unsigned char *p, *q;
47   int secure = 0, res = 0;
48 
49   if (!str_var || !uri)
50     return -1;
51 
52   memset(uri, 0, sizeof(coap_uri_t));
53   uri->port = COAP_DEFAULT_PORT;
54 
55   /* search for scheme */
56   p = str_var;
57   if (*p == '/') {
58     q = p;
59     goto path;
60   }
61 
62   q = (unsigned char *)COAP_DEFAULT_SCHEME;
63   while (len && *q && tolower(*p) == *q) {
64     ++p; ++q; --len;
65   }
66 
67   /* If q does not point to the string end marker '\0', the schema
68    * identifier is wrong. */
69   if (*q) {
70     res = -1;
71     goto error;
72   }
73 
74   /* There might be an additional 's', indicating the secure version: */
75   if (len && (secure = tolower(*p) == 's')) {
76     ++p; --len;
77   }
78 
79   q = (unsigned char *)"://";
80   while (len && *q && tolower(*p) == *q) {
81     ++p; ++q; --len;
82   }
83 
84   if (*q) {
85     res = -2;
86     goto error;
87   }
88 
89   /* p points to beginning of Uri-Host */
90   q = p;
91   if (len && *p == '[') {	/* IPv6 address reference */
92     ++p;
93 
94     while (len && *q != ']') {
95       ++q; --len;
96     }
97 
98     if (!len || *q != ']' || p == q) {
99       res = -3;
100       goto error;
101     }
102 
103     COAP_SET_STR(&uri->host, q - p, p);
104     ++q; --len;
105   } else {			/* IPv4 address or FQDN */
106     while (len && *q != ':' && *q != '/' && *q != '?') {
107       *q = tolower(*q);
108       ++q;
109       --len;
110     }
111 
112     if (p == q) {
113       res = -3;
114       goto error;
115     }
116 
117     COAP_SET_STR(&uri->host, q - p, p);
118   }
119 
120   /* check for Uri-Port */
121   if (len && *q == ':') {
122     p = ++q;
123     --len;
124 
125     while (len && isdigit(*q)) {
126       ++q;
127       --len;
128     }
129 
130     if (p < q) {		/* explicit port number given */
131       int uri_port = 0;
132 
133       while (p < q)
134 	uri_port = uri_port * 10 + (*p++ - '0');
135 
136       /* check if port number is in allowed range */
137       if (uri_port > 65535) {
138 	res = -4;
139 	goto error;
140       }
141 
142       uri->port = uri_port;
143     }
144   }
145 
146  path:		 /* at this point, p must point to an absolute path */
147 
148   if (!len)
149     goto end;
150 
151   if (*q == '/') {
152     p = ++q;
153     --len;
154 
155     while (len && *q != '?') {
156       ++q;
157       --len;
158     }
159 
160     if (p < q) {
161       COAP_SET_STR(&uri->path, q - p, p);
162       p = q;
163     }
164   }
165 
166   /* Uri_Query */
167   if (len && *p == '?') {
168     ++p;
169     --len;
170     COAP_SET_STR(&uri->query, len, p);
171     len = 0;
172   }
173 
174   end:
175   return len ? -1 : 0;
176 
177   error:
178   return res;
179 }
180 
181 /**
182  * Calculates decimal value from hexadecimal ASCII character given in
183  * @p c. The caller must ensure that @p c actually represents a valid
184  * heaxdecimal character, e.g. with isxdigit(3).
185  *
186  * @hideinitializer
187  */
188 #define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
189 
190 /**
191  * Decodes percent-encoded characters while copying the string @p seg
192  * of size @p length to @p buf. The caller of this function must
193  * ensure that the percent-encodings are correct (i.e. the character
194  * '%' is always followed by two hex digits. and that @p buf provides
195  * sufficient space to hold the result. This function is supposed to
196  * be called by make_decoded_option() only.
197  *
198  * @param seg     The segment to decode and copy.
199  * @param length  Length of @p seg.
200  * @param buf     The result buffer.
201  */
202 static void
decode_segment(const unsigned char * seg,size_t length,unsigned char * buf)203 decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
204 
205   while (length--) {
206 
207     if (*seg == '%') {
208       *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
209 
210       seg += 2; length -= 2;
211     } else {
212       *buf = *seg;
213     }
214 
215     ++buf; ++seg;
216   }
217 }
218 
219 /**
220  * Runs through the given path (or query) segment and checks if
221  * percent-encodings are correct. This function returns @c -1 on error
222  * or the length of @p s when decoded.
223  */
224 static int
check_segment(const unsigned char * s,size_t length)225 check_segment(const unsigned char *s, size_t length) {
226 
227   size_t n = 0;
228 
229   while (length) {
230     if (*s == '%') {
231       if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
232 	return -1;
233 
234       s += 2;
235       length -= 2;
236     }
237 
238     ++s; ++n; --length;
239   }
240 
241   return n;
242 }
243 
244 /**
245  * Writes a coap option from given string @p s to @p buf. @p s should
246  * point to a (percent-encoded) path or query segment of a coap_uri_t
247  * object.  The created option will have type @c 0, and the length
248  * parameter will be set according to the size of the decoded string.
249  * On success, this function returns the option's size, or a value
250  * less than zero on error. This function must be called from
251  * coap_split_path_impl() only.
252  *
253  * @param s       The string to decode.
254  * @param length  The size of the percent-encoded string @p s.
255  * @param buf     The buffer to store the new coap option.
256  * @param buflen  The maximum size of @p buf.
257  *
258  * @return The option's size, or @c -1 on error.
259  *
260  * @bug This function does not split segments that are bigger than 270
261  * bytes.
262  */
263 static int
make_decoded_option(const unsigned char * s,size_t length,unsigned char * buf,size_t buflen)264 make_decoded_option(const unsigned char *s, size_t length,
265 		    unsigned char *buf, size_t buflen) {
266   int res;
267   size_t written;
268 
269   if (!buflen) {
270     debug("make_decoded_option(): buflen is 0!\n");
271     return -1;
272   }
273 
274   res = check_segment(s, length);
275   if (res < 0)
276     return -1;
277 
278   /* write option header using delta 0 and length res */
279   written = coap_opt_setheader(buf, buflen, 0, res);
280 
281   assert(written <= buflen);
282 
283   if (!written)			/* encoding error */
284     return -1;
285 
286   buf += written;		/* advance past option type/length */
287   buflen -= written;
288 
289   if (buflen < (size_t)res) {
290     debug("buffer too small for option\n");
291     return -1;
292   }
293 
294   decode_segment(s, length, buf);
295 
296   return written + res;
297 }
298 
299 
300 #ifndef min
301 #define min(a,b) ((a) < (b) ? (a) : (b))
302 #endif
303 
304 typedef void (*segment_handler_t)(unsigned char *, size_t, void *);
305 
306 /**
307  * Checks if path segment @p s consists of one or two dots.
308  */
309 static inline int
dots(unsigned char * s,size_t len)310 dots(unsigned char *s, size_t len) {
311   return *s == '.' && (len == 1 || (*(s+1) == '.' && len == 2));
312 }
313 
314 /**
315  * Splits the given string into segments. You should call one of the
316  * macros coap_split_path() or coap_split_query() instead.
317  *
318  * @param s      The URI string to be tokenized.
319  * @param length The length of @p s.
320  * @param h      A handler that is called with every token.
321  * @param data   Opaque data that is passed to @p h when called.
322  *
323  * @return The number of characters that have been parsed from @p s.
324  */
325 static size_t
coap_split_path_impl(const unsigned char * s,size_t length,segment_handler_t h,void * data)326 coap_split_path_impl(const unsigned char *s, size_t length,
327 		     segment_handler_t h, void *data) {
328 
329   const unsigned char *p, *q;
330 
331   p = q = s;
332   while (length > 0 && !strnchr((unsigned char *)"?#", 2, *q)) {
333     if (*q == '/') {		/* start new segment */
334 
335       if (!dots((unsigned char *)p, q - p)) {
336 	h((unsigned char *)p, q - p, data);
337       }
338 
339       p = q + 1;
340     }
341 
342     q++;
343     length--;
344   }
345 
346   /* write last segment */
347   if (!dots((unsigned char *)p, q - p)) {
348     h((unsigned char *)p, q - p, data);
349   }
350 
351   return q - s;
352 }
353 
354 struct cnt_str {
355   str buf;
356   int n;
357 };
358 
359 static void
write_option(unsigned char * s,size_t len,void * data)360 write_option(unsigned char *s, size_t len, void *data) {
361   struct cnt_str *state = (struct cnt_str *)data;
362   int res;
363   assert(state);
364 
365   res = make_decoded_option(s, len, state->buf.s, state->buf.length);
366   if (res > 0) {
367     state->buf.s += res;
368     state->buf.length -= res;
369     state->n++;
370   }
371 }
372 
373 int
coap_split_path(const unsigned char * s,size_t length,unsigned char * buf,size_t * buflen)374 coap_split_path(const unsigned char *s, size_t length,
375 		unsigned char *buf, size_t *buflen) {
376   struct cnt_str tmp = { { *buflen, buf }, 0 };
377 
378   coap_split_path_impl(s, length, write_option, &tmp);
379 
380   *buflen = *buflen - tmp.buf.length;
381 
382   return tmp.n;
383 }
384 
385 int
coap_split_query(const unsigned char * s,size_t length,unsigned char * buf,size_t * buflen)386 coap_split_query(const unsigned char *s, size_t length,
387 		unsigned char *buf, size_t *buflen) {
388   struct cnt_str tmp = { { *buflen, buf }, 0 };
389   const unsigned char *p;
390 
391   p = s;
392   while (length > 0 && *s != '#') {
393     if (*s == '&') {		/* start new query element */
394       write_option((unsigned char *)p, s - p, &tmp);
395       p = s + 1;
396     }
397 
398     s++;
399     length--;
400   }
401 
402   /* write last query element */
403   write_option((unsigned char *)p, s - p, &tmp);
404 
405   *buflen = *buflen - tmp.buf.length;
406   return tmp.n;
407 }
408 
409 #define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
410 
411 coap_uri_t *
coap_new_uri(const unsigned char * uri,unsigned int length)412 coap_new_uri(const unsigned char *uri, unsigned int length) {
413   unsigned char *result;
414 
415   result = coap_malloc(length + 1 + sizeof(coap_uri_t));
416 
417   if (!result)
418     return NULL;
419 
420   memcpy(URI_DATA(result), uri, length);
421   URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
422 
423   if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
424     coap_free(result);
425     return NULL;
426   }
427   return (coap_uri_t *)result;
428 }
429 
430 coap_uri_t *
coap_clone_uri(const coap_uri_t * uri)431 coap_clone_uri(const coap_uri_t *uri) {
432   coap_uri_t *result;
433 
434   if ( !uri )
435     return  NULL;
436 
437   result = (coap_uri_t *)coap_malloc( uri->query.length + uri->host.length +
438 				      uri->path.length + sizeof(coap_uri_t) + 1);
439 
440   if ( !result )
441     return NULL;
442 
443   memset( result, 0, sizeof(coap_uri_t) );
444 
445   result->port = uri->port;
446 
447   if ( uri->host.length ) {
448     result->host.s = URI_DATA(result);
449     result->host.length = uri->host.length;
450 
451     memcpy(result->host.s, uri->host.s, uri->host.length);
452   }
453 
454   if ( uri->path.length ) {
455     result->path.s = URI_DATA(result) + uri->host.length;
456     result->path.length = uri->path.length;
457 
458     memcpy(result->path.s, uri->path.s, uri->path.length);
459   }
460 
461   if ( uri->query.length ) {
462     result->query.s = URI_DATA(result) + uri->host.length + uri->path.length;
463     result->query.length = uri->query.length;
464 
465     memcpy(result->query.s, uri->query.s, uri->query.length);
466   }
467 
468   return result;
469 }
470 
471 /* hash URI path segments */
472 
473 /* The function signature of coap_hash() is different from
474  * segment_handler_t hence we use this wrapper as safe typecast. */
475 static inline void
hash_segment(unsigned char * s,size_t len,void * data)476 hash_segment(unsigned char *s, size_t len, void *data) {
477   coap_hash(s, len, data);
478 }
479 
480 int
coap_hash_path(const unsigned char * path,size_t len,coap_key_t key)481 coap_hash_path(const unsigned char *path, size_t len, coap_key_t key) {
482   if (!path)
483     return 0;
484 
485   memset(key, 0, sizeof(coap_key_t));
486 
487   coap_split_path_impl(path, len, hash_segment, key);
488 
489   return 1;
490 }
491