libcoap 4.3.1
uri.c
Go to the documentation of this file.
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
16#include "coap3/coap_internal.h"
17
18#if defined(HAVE_LIMITS_H)
19#include <limits.h>
20#endif
21
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
25#include <ctype.h>
26
38COAP_STATIC_INLINE const uint8_t *
39strnchr(const uint8_t *s, size_t len, unsigned char c) {
40 while (len && *s++ != c)
41 --len;
42
43 return len ? s : NULL;
44}
45
46#define ISEQUAL_CI(a,b) \
47 ((a) == (b) || (islower(b) && ((a) == ((b) - 0x20))))
48
49typedef enum coap_uri_check_t {
53
54static int
55coap_split_uri_sub(const uint8_t *str_var,
56 size_t len,
57 coap_uri_t *uri,
58 coap_uri_check_t check_proxy) {
59 const uint8_t *p, *q;
60 int res = 0;
61 int is_http_proxy_scheme = 0;
62 size_t keep_len = len;
63
64 if (!str_var || !uri)
65 return -1;
66
67 memset(uri, 0, sizeof(coap_uri_t));
69
70 /* search for scheme */
71 p = str_var;
72 if (*p == '/') {
73 if (check_proxy == COAP_URI_CHECK_PROXY)
74 return -1;
75 q = p;
76 goto path;
77 }
78
79 q = (const uint8_t *)COAP_DEFAULT_SCHEME;
80 while (len && *q && ISEQUAL_CI(*p, *q)) {
81 ++p; ++q; --len;
82 }
83 if (*q && check_proxy == COAP_URI_CHECK_PROXY) {
84 /* Scheme could be something other than coap */
85 len = keep_len;
86 p = str_var;
87 q = (const uint8_t *)"http";
88 while (len && *q && ISEQUAL_CI(*p, *q)) {
89 ++p; ++q; --len;
90 }
91 if (*q == 0) {
92 if (len && ISEQUAL_CI(*p, 's')) {
93 /* https:// */
94 ++p; --len;
96 uri->port = 443;
97 }
98 else {
99 /* http:// */
101 uri->port = 80;
102 }
103 }
104 else {
105 /* Unknown scheme */
106 res = -1;
107 goto error;
108 }
109 is_http_proxy_scheme = 1;
110 }
111
112 /* If q does not point to the string end marker '\0', the schema
113 * identifier is wrong. */
114 if (*q) {
115 res = -1;
116 goto error;
117 }
118
119 if (is_http_proxy_scheme == 0) {
120 /* There might be an additional 's', indicating the secure version: */
121 if (len && (*p == 's')) {
122 ++p; --len;
125 } else {
127 }
128
129 /* There might be an addition "+tcp", indicating reliable transport: */
130 if (len>=4 && p[0] == '+' && p[1] == 't' && p[2] == 'c' && p[3] == 'p' ) {
131 p += 4;
132 len -= 4;
133 if (uri->scheme == COAP_URI_SCHEME_COAPS)
135 else
137 }
138 }
139 q = (const uint8_t *)"://";
140 while (len && *q && *p == *q) {
141 ++p; ++q; --len;
142 }
143
144 if (*q) {
145 res = -2;
146 goto error;
147 }
148
149 /* p points to beginning of Uri-Host */
150 q = p;
151 if (len && *p == '[') { /* IPv6 address reference */
152 ++p;
153
154 while (len && *q != ']') {
155 ++q; --len;
156 }
157
158 if (!len || *q != ']' || p == q) {
159 res = -3;
160 goto error;
161 }
162
163 COAP_SET_STR(&uri->host, q - p, p);
164 ++q; --len;
165 } else { /* IPv4 address or FQDN */
166 while (len && *q != ':' && *q != '/' && *q != '?') {
167 ++q;
168 --len;
169 }
170
171 if (p == q) {
172 res = -3;
173 goto error;
174 }
175
176 COAP_SET_STR(&uri->host, q - p, p);
177 }
178
179 /* check for Uri-Port */
180 if (len && *q == ':') {
181 p = ++q;
182 --len;
183
184 while (len && isdigit(*q)) {
185 ++q;
186 --len;
187 }
188
189 if (p < q) { /* explicit port number given */
190 int uri_port = 0;
191
192 while ((p < q) && (uri_port <= UINT16_MAX))
193 uri_port = uri_port * 10 + (*p++ - '0');
194
195 /* check if port number is in allowed range */
196 if (uri_port > UINT16_MAX) {
197 res = -4;
198 goto error;
199 }
200
201 uri->port = (uint16_t)uri_port;
202 }
203 }
204
205 path: /* at this point, p must point to an absolute path */
206
207 if (!len)
208 goto end;
209
210 if (*q == '/') {
211 p = ++q;
212 --len;
213
214 while (len && *q != '?') {
215 ++q;
216 --len;
217 }
218
219 if (p < q) {
220 COAP_SET_STR(&uri->path, q - p, p);
221 p = q;
222 }
223 }
224
225 /* Uri_Query */
226 if (len && *p == '?') {
227 ++p;
228 --len;
229 COAP_SET_STR(&uri->query, len, p);
230 len = 0;
231 }
232
233 end:
234 return len ? -1 : 0;
235
236 error:
237 return res;
238}
239
240int
241coap_split_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_URI);
243}
244
245int
246coap_split_proxy_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri) {
247 return coap_split_uri_sub(str_var, len, uri, COAP_URI_CHECK_PROXY);
248}
249
257#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
258
271static void
272decode_segment(const uint8_t *seg, size_t length, unsigned char *buf) {
273
274 while (length--) {
275
276 if (*seg == '%') {
277 *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
278
279 seg += 2; length -= 2;
280 } else {
281 *buf = *seg;
282 }
283
284 ++buf; ++seg;
285 }
286}
287
293static int
294check_segment(const uint8_t *s, size_t length, size_t *segment_size) {
295 size_t n = 0;
296
297 while (length) {
298 if (*s == '%') {
299 if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
300 return -1;
301
302 s += 2;
303 length -= 2;
304 }
305
306 ++s; ++n; --length;
307 }
308
309 *segment_size = n;
310
311 return 0;
312}
313
334static int
335make_decoded_option(const uint8_t *s, size_t length,
336 unsigned char *buf, size_t buflen, size_t* optionsize) {
337 int res;
338 size_t segmentlen;
339 size_t written;
340
341 if (!buflen) {
342 coap_log(LOG_DEBUG, "make_decoded_option(): buflen is 0!\n");
343 return -1;
344 }
345
346 res = check_segment(s, length, &segmentlen);
347 if (res < 0)
348 return -1;
349
350 /* write option header using delta 0 and length res */
351 written = coap_opt_setheader(buf, buflen, 0, segmentlen);
352
353 assert(written <= buflen);
354
355 if (!written) /* encoding error */
356 return -1;
357
358 buf += written; /* advance past option type/length */
359 buflen -= written;
360
361 if (buflen < segmentlen) {
362 coap_log(LOG_DEBUG, "buffer too small for option\n");
363 return -1;
364 }
365
366 decode_segment(s, length, buf);
367
368 *optionsize = written + segmentlen;
369
370 return 0;
371}
372
373
374#ifndef min
375#define min(a,b) ((a) < (b) ? (a) : (b))
376#endif
377
378typedef void (*segment_handler_t)(const uint8_t *, size_t, void *);
379
384dots(const uint8_t *s, size_t len) {
385 return len && *s == '.' && (len == 1 || (len == 2 && *(s+1) == '.'));
386}
387
399static size_t
400coap_split_path_impl(const uint8_t *s, size_t length,
401 segment_handler_t h, void *data) {
402
403 const uint8_t *p, *q;
404
405 p = q = s;
406 while (length > 0 && !strnchr((const uint8_t *)"?#", 2, *q)) {
407 if (*q == '/') { /* start new segment */
408
409 if (!dots(p, q - p)) {
410 h(p, q - p, data);
411 }
412
413 p = q + 1;
414 }
415
416 q++;
417 length--;
418 }
419
420 /* write last segment */
421 if (!dots(p, q - p)) {
422 h(p, q - p, data);
423 }
424
425 return q - s;
426}
427
428struct cnt_str {
430 int n;
431};
432
433static void
434write_option(const uint8_t *s, size_t len, void *data) {
435 struct cnt_str *state = (struct cnt_str *)data;
436 int res;
437 size_t optionsize;
438 assert(state);
439
440 res = make_decoded_option(s, len, state->buf.s, state->buf.length, &optionsize);
441 if (res == 0) {
442 state->buf.s += optionsize;
443 state->buf.length -= optionsize;
444 state->n++;
445 }
446}
447
448int
449coap_split_path(const uint8_t *s, size_t length,
450 unsigned char *buf, size_t *buflen) {
451 struct cnt_str tmp = { { *buflen, buf }, 0 };
452
453 coap_split_path_impl(s, length, write_option, &tmp);
454
455 *buflen = *buflen - tmp.buf.length;
456
457 return tmp.n;
458}
459
460int
461coap_split_query(const uint8_t *s, size_t length,
462 unsigned char *buf, size_t *buflen) {
463 struct cnt_str tmp = { { *buflen, buf }, 0 };
464 const uint8_t *p;
465
466 p = s;
467 while (length > 0 && *s != '#') {
468 if (*s == '&') { /* start new query element */
469 write_option(p, s - p, &tmp);
470 p = s + 1;
471 }
472
473 s++;
474 length--;
475 }
476
477 /* write last query element */
478 write_option(p, s - p, &tmp);
479
480 *buflen = *buflen - tmp.buf.length;
481 return tmp.n;
482}
483
484#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
485
487coap_new_uri(const uint8_t *uri, unsigned int length) {
488 unsigned char *result;
489
490 result = (unsigned char*)coap_malloc(length + 1 + sizeof(coap_uri_t));
491
492 if (!result)
493 return NULL;
494
495 memcpy(URI_DATA(result), uri, length);
496 URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
497
498 if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
499 coap_free(result);
500 return NULL;
501 }
502 return (coap_uri_t *)result;
503}
504
507 coap_uri_t *result;
508 uint8_t *p;
509
510 if ( !uri )
511 return NULL;
512
513 result = (coap_uri_t *)coap_malloc( uri->query.length + uri->host.length +
514 uri->path.length + sizeof(coap_uri_t) + 1);
515
516 if ( !result )
517 return NULL;
518
519 memset( result, 0, sizeof(coap_uri_t) );
520
521 result->port = uri->port;
522
523 if ( uri->host.length ) {
524 result->host.s = p = URI_DATA(result);
525 result->host.length = uri->host.length;
526
527 memcpy(p, uri->host.s, uri->host.length);
528 }
529
530 if ( uri->path.length ) {
531 result->path.s = p = URI_DATA(result) + uri->host.length;
532 result->path.length = uri->path.length;
533
534 memcpy(p, uri->path.s, uri->path.length);
535 }
536
537 if ( uri->query.length ) {
538 result->query.s = p = URI_DATA(result) + uri->host.length + uri->path.length;
539 result->query.length = uri->query.length;
540
541 memcpy (p, uri->query.s, uri->query.length);
542 }
543
544 return result;
545}
546
548is_unescaped_in_path(const uint8_t c) {
549 return ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' )
550 || ( c >= '0' && c <= '9' ) || c == '-' || c == '.' || c == '_'
551 || c == '~' || c == '!' || c == '$' || c == '\'' || c == '('
552 || c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || c=='='
553 || c==':' || c=='@' || c == '&';
554}
555
557is_unescaped_in_query(const uint8_t c) {
558 return is_unescaped_in_path(c) || c=='/' || c=='?';
559}
560
562 coap_opt_iterator_t opt_iter;
564 coap_opt_t *q;
565 coap_string_t *query = NULL;
566 size_t length = 0;
567 static const uint8_t hex[] = "0123456789ABCDEF";
568
571 coap_option_iterator_init(request, &opt_iter, &f);
572 while ((q = coap_option_next(&opt_iter))) {
573 uint16_t seg_len = coap_opt_length(q), i;
574 const uint8_t *seg= coap_opt_value(q);
575 for (i = 0; i < seg_len; i++) {
576 if (is_unescaped_in_query(seg[i]))
577 length += 1;
578 else
579 length += 3;
580 }
581 length += 1;
582 }
583 if (length > 0)
584 length -= 1;
585 if (length > 0) {
586 query = coap_new_string(length);
587 if (query) {
588 query->length = length;
589 unsigned char *s = query->s;
590 coap_option_iterator_init(request, &opt_iter, &f);
591 while ((q = coap_option_next(&opt_iter))) {
592 if (s != query->s)
593 *s++ = '&';
594 uint16_t seg_len = coap_opt_length(q), i;
595 const uint8_t *seg= coap_opt_value(q);
596 for (i = 0; i < seg_len; i++) {
597 if (is_unescaped_in_query(seg[i])) {
598 *s++ = seg[i];
599 } else {
600 *s++ = '%';
601 *s++ = hex[seg[i]>>4];
602 *s++ = hex[seg[i]&0x0F];
603 }
604 }
605 }
606 }
607 }
608 return query;
609}
610
612 coap_opt_iterator_t opt_iter;
614 coap_opt_t *q;
615 coap_string_t *uri_path = NULL;
616 size_t length = 0;
617 static const uint8_t hex[] = "0123456789ABCDEF";
618
619 q = coap_check_option(request, COAP_OPTION_PROXY_URI, &opt_iter);
620 if (q) {
621 coap_uri_t uri;
622
624 coap_opt_length(q), &uri) < 0) {
625 return NULL;
626 }
627 uri_path = coap_new_string(uri.path.length);
628 if (uri_path) {
629 memcpy(uri_path->s, uri.path.s, uri.path.length);
630 }
631 return uri_path;
632 }
633
636 coap_option_iterator_init(request, &opt_iter, &f);
637 while ((q = coap_option_next(&opt_iter))) {
638 uint16_t seg_len = coap_opt_length(q), i;
639 const uint8_t *seg= coap_opt_value(q);
640 for (i = 0; i < seg_len; i++) {
641 if (is_unescaped_in_path(seg[i]))
642 length += 1;
643 else
644 length += 3;
645 }
646 /* bump for the leading "/" */
647 length += 1;
648 }
649 /* The first entry does not have a leading "/" */
650 if (length > 0)
651 length -= 1;
652
653 /* if 0, either no URI_PATH Option, or the first one was empty */
654 uri_path = coap_new_string(length);
655 if (uri_path) {
656 uri_path->length = length;
657 unsigned char *s = uri_path->s;
658 int n = 0;
659 coap_option_iterator_init(request, &opt_iter, &f);
660 while ((q = coap_option_next(&opt_iter))) {
661 if (n++) {
662 *s++ = '/';
663 }
664 uint16_t seg_len = coap_opt_length(q), i;
665 const uint8_t *seg= coap_opt_value(q);
666 for (i = 0; i < seg_len; i++) {
667 if (is_unescaped_in_path(seg[i])) {
668 *s++ = seg[i];
669 } else {
670 *s++ = '%';
671 *s++ = hex[seg[i]>>4];
672 *s++ = hex[seg[i]&0x0F];
673 }
674 }
675 }
676 }
677 return uri_path;
678}
679
Pulls together all the internal only header files.
uint8_t coap_opt_t
Use byte-oriented access methods here because sliding a complex struct coap_opt_t over the data buffe...
Definition: coap_option.h:26
#define LOG_DEBUG
Definition: coap_debug.h:81
#define coap_log(level,...)
Logging function.
Definition: coap_debug.h:165
coap_opt_t * coap_option_next(coap_opt_iterator_t *oi)
Updates the iterator oi to point to the next option.
Definition: coap_option.c:152
uint32_t coap_opt_length(const coap_opt_t *opt)
Returns the length of the given option.
Definition: coap_option.c:215
coap_opt_iterator_t * coap_option_iterator_init(const coap_pdu_t *pdu, coap_opt_iterator_t *oi, const coap_opt_filter_t *filter)
Initializes the given option iterator oi to point to the beginning of the pdu's option list.
Definition: coap_option.c:116
void coap_option_filter_clear(coap_opt_filter_t *filter)
Clears filter filter.
Definition: coap_option.c:492
coap_opt_t * coap_check_option(const coap_pdu_t *pdu, coap_option_num_t number, coap_opt_iterator_t *oi)
Retrieves the first option of number number from pdu.
Definition: coap_option.c:202
const uint8_t * coap_opt_value(const coap_opt_t *opt)
Returns a pointer to the value of the given option.
Definition: coap_option.c:252
int coap_option_filter_set(coap_opt_filter_t *filter, coap_option_num_t option)
Sets the corresponding entry for number in filter.
Definition: coap_option.c:497
size_t coap_opt_setheader(coap_opt_t *opt, size_t maxlen, uint16_t delta, size_t length)
Encodes the given delta and length values into opt.
Definition: coap_option.c:295
#define COAP_DEFAULT_PORT
Definition: pdu.h:37
#define COAP_OPTION_URI_QUERY
Definition: pdu.h:124
#define COAP_OPTION_URI_PATH
Definition: pdu.h:119
#define COAP_DEFAULT_SCHEME
Definition: pdu.h:50
#define COAPS_DEFAULT_PORT
Definition: pdu.h:38
#define COAP_OPTION_PROXY_URI
Definition: pdu.h:131
#define COAP_SET_STR(st, l, v)
Definition: str.h:51
coap_string_t * coap_new_string(size_t size)
Returns a new string object with at least size+1 bytes storage allocated.
Definition: str.c:20
coap_string_t * coap_get_uri_path(const coap_pdu_t *request)
Extract uri_path string from request PDU.
Definition: uri.c:611
int coap_split_path(const uint8_t *s, size_t length, unsigned char *buf, size_t *buflen)
Splits the given URI path into segments.
Definition: uri.c:449
int coap_split_query(const uint8_t *s, size_t length, unsigned char *buf, size_t *buflen)
Splits the given URI query into segments.
Definition: uri.c:461
int coap_split_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri)
Parses a given string into URI components.
Definition: uri.c:241
int coap_split_proxy_uri(const uint8_t *str_var, size_t len, coap_uri_t *uri)
Parses a given string into URI components.
Definition: uri.c:246
coap_string_t * coap_get_query(const coap_pdu_t *request)
Extract query string from request PDU according to escape rules in 6.5.8.
Definition: uri.c:561
#define COAP_STATIC_INLINE
Definition: libcoap.h:45
COAP_STATIC_INLINE void coap_free(void *object)
Wrapper function to coap_free_type() for backwards compatibility.
Definition: mem.h:110
COAP_STATIC_INLINE void * coap_malloc(size_t size)
Wrapper function to coap_malloc_type() for backwards compatibility.
Definition: mem.h:103
const uint32_t n
Definition: murmur3.c:56
Definition: uri.c:428
int n
Definition: uri.c:430
coap_string_t buf
Definition: uri.c:429
Iterator to run through PDU options.
Definition: coap_option.h:171
structure for CoAP PDUs
const uint8_t * s
read-only string data
Definition: str.h:48
size_t length
length of string
Definition: str.h:47
CoAP string data definition.
Definition: str.h:38
uint8_t * s
string data
Definition: str.h:40
size_t length
length of string
Definition: str.h:39
Representation of parsed URI.
Definition: uri.h:45
enum coap_uri_scheme_t scheme
The parsed scheme specifier.
Definition: uri.h:53
coap_str_const_t path
Beginning of the first path segment.
Definition: uri.h:48
uint16_t port
The port in host byte order.
Definition: uri.h:47
coap_str_const_t query
The query part if present.
Definition: uri.h:50
coap_str_const_t host
host part of the URI
Definition: uri.h:46
COAP_STATIC_INLINE int is_unescaped_in_path(const uint8_t c)
Definition: uri.c:548
static void decode_segment(const uint8_t *seg, size_t length, unsigned char *buf)
Decodes percent-encoded characters while copying the string seg of size length to buf.
Definition: uri.c:272
COAP_STATIC_INLINE int is_unescaped_in_query(const uint8_t c)
Definition: uri.c:557
COAP_STATIC_INLINE int dots(const uint8_t *s, size_t len)
Checks if path segment s consists of one or two dots.
Definition: uri.c:384
static size_t coap_split_path_impl(const uint8_t *s, size_t length, segment_handler_t h, void *data)
Splits the given string into segments.
Definition: uri.c:400
COAP_STATIC_INLINE const uint8_t * strnchr(const uint8_t *s, size_t len, unsigned char c)
A length-safe version of strchr().
Definition: uri.c:39
static int check_segment(const uint8_t *s, size_t length, size_t *segment_size)
Runs through the given path (or query) segment and checks if percent-encodings are correct.
Definition: uri.c:294
#define ISEQUAL_CI(a, b)
Definition: uri.c:46
static void write_option(const uint8_t *s, size_t len, void *data)
Definition: uri.c:434
static int coap_split_uri_sub(const uint8_t *str_var, size_t len, coap_uri_t *uri, coap_uri_check_t check_proxy)
Definition: uri.c:55
#define hexchar_to_dec(c)
Calculates decimal value from hexadecimal ASCII character given in c.
Definition: uri.c:257
static int make_decoded_option(const uint8_t *s, size_t length, unsigned char *buf, size_t buflen, size_t *optionsize)
Writes a coap option from given string s to buf.
Definition: uri.c:335
coap_uri_check_t
Definition: uri.c:49
@ COAP_URI_CHECK_URI
Definition: uri.c:50
@ COAP_URI_CHECK_PROXY
Definition: uri.c:51
coap_uri_t * coap_clone_uri(const coap_uri_t *uri)
Clones the specified coap_uri_t object.
Definition: uri.c:506
#define URI_DATA(uriobj)
Definition: uri.c:484
coap_uri_t * coap_new_uri(const uint8_t *uri, unsigned int length)
Creates a new coap_uri_t object from the specified URI.
Definition: uri.c:487
void(* segment_handler_t)(const uint8_t *, size_t, void *)
Definition: uri.c:378
@ COAP_URI_SCHEME_COAPS_TCP
Definition: uri.h:32
@ COAP_URI_SCHEME_COAPS
Definition: uri.h:30
@ COAP_URI_SCHEME_COAP_TCP
Definition: uri.h:31
@ COAP_URI_SCHEME_HTTPS
Definition: uri.h:34
@ COAP_URI_SCHEME_COAP
Definition: uri.h:29
@ COAP_URI_SCHEME_HTTP
Definition: uri.h:33