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