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