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