1 /* libcoap unit tests
2 *
3 * Copyright (C) 2012,2015,2022-2023 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 "test_common.h"
12 #include "test_uri.h"
13
14 #include <stdio.h>
15
16 static void
t_parse_uri1(void)17 t_parse_uri1(void) {
18 char teststr[] = "coap://[::1]/.well-known/core";
19
20 int result;
21 coap_uri_t uri;
22
23 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
24 if (result == 0) {
25 CU_ASSERT(uri.host.length == 3);
26 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "::1", 3);
27
28 CU_ASSERT(uri.port == COAP_DEFAULT_PORT);
29
30 CU_ASSERT(uri.path.length == 16);
31 CU_ASSERT_NSTRING_EQUAL(uri.path.s, ".well-known/core", 16);
32
33 CU_ASSERT(uri.query.length == 0);
34 CU_ASSERT(uri.query.s == NULL);
35 } else {
36 CU_FAIL("uri parser error");
37 }
38 }
39
40 static void
t_parse_uri2(void)41 t_parse_uri2(void) {
42 char teststr[] = "coap://[::1]:8000/.well-known/core";
43 int result;
44 coap_uri_t uri;
45
46 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
47 if (result == 0) {
48 CU_ASSERT(uri.host.length == 3);
49 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "::1", 3);
50
51 CU_ASSERT(uri.port == 8000);
52
53 CU_ASSERT(uri.path.length == 16);
54 CU_ASSERT_NSTRING_EQUAL(uri.path.s, ".well-known/core", 16);
55
56 CU_ASSERT(uri.query.length == 0);
57 CU_ASSERT(uri.query.s == NULL);
58 } else {
59 CU_FAIL("uri parser error");
60 }
61 }
62
63 static void
t_parse_uri3(void)64 t_parse_uri3(void) {
65 char teststr[] = "coap://localhost/?foo&bla=fasel";
66 int result;
67 coap_uri_t uri;
68
69 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
70 if (result == 0) {
71 CU_ASSERT(uri.host.length == 9);
72 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "localhost", 9);
73
74 CU_ASSERT(uri.port == COAP_DEFAULT_PORT);
75
76 CU_ASSERT(uri.path.length == 0);
77
78 CU_ASSERT(uri.query.length == 13);
79 CU_ASSERT_NSTRING_EQUAL(uri.query.s, "foo&bla=fasel", 13);
80 } else {
81 CU_FAIL("uri parser error");
82 }
83 }
84
85 static void
t_parse_uri4(void)86 t_parse_uri4(void) {
87 char teststr[] = "coap://:100000";
88 int result;
89 coap_uri_t uri;
90
91 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
92 CU_ASSERT(result < 0);
93 }
94
95 static void
t_parse_uri5(void)96 t_parse_uri5(void) {
97 char teststr[] = "coap://foo:100000";
98 int result;
99 coap_uri_t uri;
100
101 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
102 if (result == 0) {
103 CU_ASSERT(uri.host.length == 3);
104 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "foo", 3);
105
106 CU_ASSERT(uri.path.length == 0);
107 CU_ASSERT(uri.path.s == NULL);
108
109 CU_ASSERT(uri.query.length == 0);
110 CU_ASSERT(uri.query.s == NULL);
111
112 CU_FAIL("invalid port not detected");
113 } else {
114 CU_PASS("detected invalid port");
115 }
116 }
117
118 static void
t_parse_uri6(void)119 t_parse_uri6(void) {
120 char teststr[] = "coap://134.102.218.2/.well-known/core";
121 int result;
122 coap_uri_t uri;
123
124 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
125 if (result == 0) {
126 CU_ASSERT(uri.host.length == 13);
127 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "134.102.218.2", 13);
128
129 CU_ASSERT(uri.port == COAP_DEFAULT_PORT);
130
131 CU_ASSERT(uri.path.length == 16);
132 CU_ASSERT_NSTRING_EQUAL(uri.path.s, ".well-known/core", 16);
133
134 CU_ASSERT(uri.query.length == 0);
135 CU_ASSERT(uri.query.s == NULL);
136 } else {
137 CU_FAIL("uri parser error");
138 }
139 }
140
141 static void
t_parse_uri7(void)142 t_parse_uri7(void) {
143 char teststr[] = "coap://foo.bar:5683/some_resource/with/multiple/segments";
144 int result;
145 coap_uri_t uri;
146 unsigned char buf[40];
147 size_t buflen = sizeof(buf);
148
149 /* The list of path segments to check against. Each segment is
150 preceded by a dummy option indicating that holds the (dummy)
151 delta value 0 and the actual segment length. */
152 const uint8_t checkbuf[] = {
153 0x0d, 0x00, 's', 'o', 'm', 'e', '_', 'r', 'e', 's', 'o', 'u', 'r', 'c', 'e',
154 0x04, 'w', 'i', 't', 'h',
155 0x08, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e',
156 0x08, 's', 'e', 'g', 'm', 'e', 'n', 't', 's'
157 };
158
159 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
160 if (result == 0) {
161 CU_ASSERT(uri.host.length == 7);
162 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "foo.bar", 7);
163
164 CU_ASSERT(uri.port == 5683);
165
166 CU_ASSERT(uri.path.length == 36);
167 CU_ASSERT_NSTRING_EQUAL(uri.path.s, "some_resource/with/multiple/segments", 36);
168
169 CU_ASSERT(uri.query.length == 0);
170 CU_ASSERT(uri.query.s == NULL);
171
172 /* check path segments */
173 result = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
174 CU_ASSERT(result == 4);
175 CU_ASSERT(buflen == sizeof(checkbuf));
176 CU_ASSERT_NSTRING_EQUAL(buf, checkbuf, buflen);
177 } else {
178 CU_FAIL("uri parser error");
179 }
180 }
181
182 static void
t_parse_uri8(void)183 t_parse_uri8(void) {
184 coap_log_t level = coap_get_log_level();
185 char teststr[] = "http://example.com/%7E%AB%13";
186 int result;
187 coap_uri_t uri;
188
189 coap_set_log_level(COAP_LOG_CRIT);
190 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
191 coap_set_log_level(level);
192 if (result < 0) {
193 CU_PASS("detected non-coap URI");
194 } else {
195 CU_FAIL("non-coap URI not recognized");
196 }
197 }
198
199 static void
t_parse_uri9(void)200 t_parse_uri9(void) {
201 coap_log_t level = coap_get_log_level();
202 char teststr[] = "http://example.com/%x";
203 int result;
204 coap_uri_t uri;
205
206 coap_set_log_level(COAP_LOG_CRIT);
207 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
208 coap_set_log_level(level);
209 if (result < 0) {
210 CU_PASS("detected non-coap URI");
211 } else {
212 CU_FAIL("non-coap URI not recognized");
213 }
214 }
215
216 static void
t_parse_uri10(void)217 t_parse_uri10(void) {
218 char teststr[] = "/absolute/path";
219 int result;
220 coap_uri_t uri;
221
222 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
223 if (result == 0) {
224 CU_ASSERT(uri.host.length == 0);
225 CU_ASSERT(uri.host.s == NULL);
226
227 CU_ASSERT(uri.port == COAP_DEFAULT_PORT);
228
229 CU_ASSERT(uri.path.length == 13);
230 CU_ASSERT_NSTRING_EQUAL(uri.path.s, "absolute/path", 13);
231
232 CU_ASSERT(uri.query.length == 0);
233 CU_ASSERT(uri.query.s == NULL);
234 } else {
235 CU_FAIL("uri parser error");
236 }
237 }
238
239 static void
t_parse_uri11(void)240 t_parse_uri11(void) {
241 char teststr[] =
242 "coap://xn--18j4d.example/%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF";
243 int result;
244 coap_uri_t uri;
245 unsigned char buf[40];
246 size_t buflen = sizeof(buf);
247
248 /* The list of path segments to check against. Each segment is
249 preceded by a dummy option indicating that holds the (dummy)
250 delta value 0 and the actual segment length. */
251 const uint8_t checkbuf[] = {
252 0x0d, 0x02, 0xE3, 0x81, 0x93, 0xE3, 0x82, 0x93,
253 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xA1, 0xE3, 0x81,
254 0xAF
255 };
256
257 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
258 if (result == 0) {
259 CU_ASSERT(uri.host.length == 17);
260 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "xn--18j4d.example", 17);
261
262 CU_ASSERT(uri.port == COAP_DEFAULT_PORT);
263
264 CU_ASSERT(uri.path.length == 45);
265 CU_ASSERT_NSTRING_EQUAL(uri.path.s,
266 "%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF", 45);
267
268 CU_ASSERT(uri.query.length == 0);
269 CU_ASSERT(uri.query.s == NULL);
270
271 /* check path segments */
272 result = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
273 CU_ASSERT(result == 1);
274 CU_ASSERT(buflen == sizeof(checkbuf));
275 CU_ASSERT_NSTRING_EQUAL(buf, checkbuf, buflen);
276 } else {
277 CU_FAIL("uri parser error");
278 }
279 }
280
281 static void
t_parse_uri12(void)282 t_parse_uri12(void) {
283 char teststr[] = "coap://198.51.100.1:61616//%2F//?%2F%2F&?%26";
284 int result;
285 coap_uri_t uri;
286 unsigned char buf[40];
287 size_t buflen = sizeof(buf);
288
289 /* The list of path segments to check against. Each segment is
290 preceded by a dummy option indicating that holds the (dummy)
291 delta value 0 and the actual segment length. */
292 const uint8_t uricheckbuf[] = { 0x00, 0x01, 0x2f, 0x00, 0x00 };
293 const uint8_t querycheckbuf[] = { 0x02, 0x2f, 0x2f, 0x02, 0x3f, 0x26 };
294
295 result = coap_split_uri((unsigned char *)teststr, strlen(teststr), &uri);
296 if (result == 0) {
297 CU_ASSERT(uri.host.length == 12);
298 CU_ASSERT_NSTRING_EQUAL(uri.host.s, "198.51.100.1", 12);
299
300 CU_ASSERT(uri.port == 61616);
301
302 CU_ASSERT(uri.path.length == 6);
303 CU_ASSERT_NSTRING_EQUAL(uri.path.s, "/%2F//", 6);
304
305 CU_ASSERT(uri.query.length == 11);
306 CU_ASSERT_NSTRING_EQUAL(uri.query.s, "%2F%2F&?%26", 11);
307
308 /* check path segments */
309 result = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
310 CU_ASSERT(result == 4);
311 CU_ASSERT(buflen == sizeof(uricheckbuf));
312 CU_ASSERT_NSTRING_EQUAL(buf, uricheckbuf, buflen);
313
314 /* check query segments */
315 buflen = sizeof(buf);
316 result = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
317 CU_ASSERT(result == 2);
318 CU_ASSERT(buflen == sizeof(querycheckbuf));
319 CU_ASSERT_NSTRING_EQUAL(buf, querycheckbuf, buflen);
320 } else {
321 CU_FAIL("uri parser error");
322 }
323 }
324
325 #ifdef _MSC_VER
326 # define ALIGNED(x)
327 #else
328 # define ALIGNED(x) __attribute__ ((aligned (x)))
329 #endif
330
331 static void
t_parse_uri13(void)332 t_parse_uri13(void) {
333 uint8_t teststr[] ALIGNED(8) = {
334 0x80, 0x03, 'f', 'o',
335 'o', 0x3b, '.', 'w', 'e', 'l', 'l', '-',
336 'k', 'n', 'o', 'w', 'n', 0x04, 'c', 'o',
337 'r', 'e'
338 };
339
340 coap_pdu_t pdu = {
341 .max_size = sizeof(teststr),
342 .e_token_length = 0,
343 .token = teststr,
344 .used_size = sizeof(teststr)
345 };
346
347 coap_string_t *uri_path = coap_get_uri_path(&pdu);
348
349 CU_ASSERT(uri_path->length == sizeof(COAP_DEFAULT_URI_WELLKNOWN)-1);
350 CU_ASSERT_NSTRING_EQUAL(uri_path->s, COAP_DEFAULT_URI_WELLKNOWN,
351 sizeof(COAP_DEFAULT_URI_WELLKNOWN)-1);
352 coap_delete_string(uri_path);
353 }
354
355 static void
t_parse_uri14(void)356 t_parse_uri14(void) {
357 char teststr[] =
358 "longerthan13lessthan270=0123456789012345678901234567890123456789";
359 int result;
360
361 /* buf is large enough to hold sizeof(teststr) - 1 bytes content and
362 * 2 bytes for the option header. */
363 unsigned char buf[sizeof(teststr) + 1];
364 size_t buflen = sizeof(buf);
365
366 result = coap_split_query((unsigned char *)teststr, strlen(teststr),
367 buf, &buflen);
368 if (result >= 0) {
369 CU_ASSERT(buf[0] == 0x0d);
370 CU_ASSERT(buf[1] == strlen(teststr) - 13);
371
372 CU_ASSERT_NSTRING_EQUAL(buf+2, teststr, strlen(teststr));
373 } else {
374 CU_FAIL("uri parser error");
375 }
376 }
377
378 static void
t_parse_uri15(void)379 t_parse_uri15(void) {
380 char teststr[] =
381 "longerthan13lessthan270=0123456789012345678901234567890123456789";
382 int result;
383
384 /* buf is too small to hold sizeof(teststr) - 1 bytes content and 2
385 * bytes for the option header. */
386 unsigned char buf[sizeof(teststr) - 1];
387 size_t buflen = sizeof(buf);
388
389 result = coap_split_query((unsigned char *)teststr, strlen(teststr),
390 buf, &buflen);
391 CU_ASSERT(result == 0);
392 }
393
394 static void
t_parse_uri16(void)395 t_parse_uri16(void) {
396 char teststr[] =
397 "longerthan13lessthan270=0123456789012345678901234567890123456789";
398 int result;
399
400 /* buf is too small to hold the option header. */
401 unsigned char buf[1];
402 size_t buflen = sizeof(buf);
403
404 result = coap_split_query((unsigned char *)teststr, strlen(teststr),
405 buf, &buflen);
406 CU_ASSERT(result == 0);
407 }
408
409 static void
t_parse_uri17(void)410 t_parse_uri17(void) {
411 char teststr[] =
412 "thisislongerthan269="
413 "01234567890123456789012345678901234567890123456789"
414 "01234567890123456789012345678901234567890123456789"
415 "01234567890123456789012345678901234567890123456789"
416 "01234567890123456789012345678901234567890123456789"
417 "01234567890123456789012345678901234567890123456789";
418 int result;
419
420 /* buf is large enough to hold sizeof(teststr) - 1 bytes content and
421 * 3 bytes for the option header. */
422 unsigned char buf[sizeof(teststr) + 2];
423 size_t buflen = sizeof(buf);
424
425 result = coap_split_query((unsigned char *)teststr, strlen(teststr),
426 buf, &buflen);
427 if (result >= 0) {
428 CU_ASSERT(buf[0] == 0x0e);
429 CU_ASSERT(buf[1] == (((strlen(teststr) - 269) >> 8) & 0xff));
430 CU_ASSERT(buf[2] == ((strlen(teststr) - 269) & 0xff));
431
432 CU_ASSERT_NSTRING_EQUAL(buf+3, teststr, strlen(teststr));
433 } else {
434 CU_FAIL("uri parser error");
435 }
436 }
437
438 static void
t_parse_uri18(void)439 t_parse_uri18(void) {
440 uint8_t token[1] = "";
441 coap_pdu_t pdu = {
442 .max_size = 0,
443 .e_token_length = 0,
444 .token = token,
445 .used_size = 0
446 };
447
448 coap_string_t *uri_path = coap_get_uri_path(&pdu);
449
450 CU_ASSERT(uri_path->length == 0);
451 #if 0
452 /* Currently this is not the case - Issue #167 */
453 /* strings are stored with terminating zero */
454 CU_ASSERT_NSTRING_EQUAL(uri_path->s, "", 1);
455 #endif
456 coap_delete_string(uri_path);
457 }
458
459 static void
t_parse_uri19(void)460 t_parse_uri19(void) {
461 uint8_t teststr[] ALIGNED(8) = {
462 0xb3, 'f', 'o', 'o',
463 0x00 /* "foo/" as Uri-Path options */
464 };
465
466 coap_pdu_t pdu = {
467 .max_size = sizeof(teststr),
468 .e_token_length = 0,
469 .token = teststr,
470 .used_size = sizeof(teststr)
471 };
472
473 coap_string_t *uri_path = coap_get_uri_path(&pdu);
474
475 CU_ASSERT(uri_path->length == 4);
476 CU_ASSERT_NSTRING_EQUAL(uri_path->s, "foo/", 4);
477 coap_delete_string(uri_path);
478 }
479
480 static void
t_parse_uri20(void)481 t_parse_uri20(void) {
482 uint8_t teststr[] ALIGNED(8) = {
483 0xb0, 0x00 /* "//" as Uri-Path options */
484 };
485
486 coap_pdu_t pdu = {
487 .max_size = sizeof(teststr),
488 .e_token_length = 0,
489 .token = teststr,
490 .used_size = sizeof(teststr)
491 };
492
493 coap_string_t *uri_path = coap_get_uri_path(&pdu);
494
495 /* The leading '/' is stripped hence only one '/' remains. */
496 CU_ASSERT(uri_path->length == 1);
497 CU_ASSERT_NSTRING_EQUAL(uri_path->s, "/", 1);
498 coap_delete_string(uri_path);
499 }
500
501 static void
t_parse_uri21(void)502 t_parse_uri21(void) {
503 uint8_t teststr[] ALIGNED(8) = {
504 0xb0, 0x03, 'f', 'o', 'o' /* "//foo" as Uri-Path options */
505 };
506
507 coap_pdu_t pdu = {
508 .max_size = sizeof(teststr),
509 .e_token_length = 0,
510 .token = teststr,
511 .used_size = sizeof(teststr)
512 };
513
514 coap_string_t *uri_path = coap_get_uri_path(&pdu);
515
516 /* The leading '/' is stripped hence only one '/' remains. */
517 CU_ASSERT(uri_path->length == 4);
518 CU_ASSERT_NSTRING_EQUAL(uri_path->s, "/foo", 4);
519 coap_delete_string(uri_path);
520 }
521
522 static void
t_parse_uri22(void)523 t_parse_uri22(void) {
524 uint8_t teststr[] ALIGNED(8) = {
525 /* characters that are not percent-encoded in a path segment */
526 0xba, '-', '.', '_', '~', '!', '$', '&', '\'', '(', ')',
527 0x05, '*', '+', ',', ';', '='
528 };
529
530 coap_pdu_t pdu = {
531 .max_size = sizeof(teststr),
532 .e_token_length = 0,
533 .token = teststr,
534 .used_size = sizeof(teststr)
535 };
536
537 coap_string_t *uri_path = coap_get_uri_path(&pdu);
538
539 CU_ASSERT(uri_path->length == 16);
540 CU_ASSERT_NSTRING_EQUAL(uri_path->s, "-._~!$&'()/*+,;=", 16);
541 coap_delete_string(uri_path);
542 }
543
544 static void
t_parse_uri23(void)545 t_parse_uri23(void) {
546 uint8_t teststr[] ALIGNED(8) = {
547 /* characters that must be percent-encoded in a path segment */
548 0xb5, '%', ' ', '#', '[', ']'
549 };
550
551 coap_pdu_t pdu = {
552 .max_size = sizeof(teststr),
553 .e_token_length = 0,
554 .token = teststr,
555 .used_size = sizeof(teststr)
556 };
557
558 coap_string_t *uri_path = coap_get_uri_path(&pdu);
559
560 CU_ASSERT(uri_path->length == 15);
561 CU_ASSERT_NSTRING_EQUAL(uri_path->s, "%25%20%23%5B%5D", 15);
562 coap_delete_string(uri_path);
563 }
564
565 /*
566 * To test Issue #212 which reads off the end of the input buffer when looking
567 * for . or .. in the path.
568 * Credit to OSS-Fuzz for finding this, work done by Bhargava Shastry
569 */
570 static void
t_parse_uri24(void)571 t_parse_uri24(void) {
572 /* coap://\206cap:// */
573 uint8_t teststr[] = { 0x63, 0x6f, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x86, 0x63, 0x6f, 0x61, 0x70, 0x3a, 0x2f, 0x2f };
574 int result;
575 unsigned char buf[40];
576 size_t buflen = sizeof(buf);
577
578 result = coap_split_path(teststr, sizeof(teststr), buf, &buflen);
579 CU_ASSERT(result == 5);
580 CU_ASSERT(buflen == 16);
581 }
582
583
584 CU_pSuite
t_init_uri_tests(void)585 t_init_uri_tests(void) {
586 CU_pSuite suite;
587
588 suite = CU_add_suite("uri parser", NULL, NULL);
589 if (!suite) { /* signal error */
590 fprintf(stderr, "W: cannot add uri parser test suite (%s)\n",
591 CU_get_error_msg());
592
593 return NULL;
594 }
595
596 #define URI_TEST(s,t) \
597 if (!CU_ADD_TEST(s,t)) { \
598 fprintf(stderr, "W: cannot add uri parser test (%s)\n", \
599 CU_get_error_msg()); \
600 }
601
602 URI_TEST(suite, t_parse_uri1);
603 URI_TEST(suite, t_parse_uri2);
604 URI_TEST(suite, t_parse_uri3);
605 URI_TEST(suite, t_parse_uri4);
606 URI_TEST(suite, t_parse_uri5);
607 URI_TEST(suite, t_parse_uri6);
608 URI_TEST(suite, t_parse_uri7);
609 URI_TEST(suite, t_parse_uri8);
610 URI_TEST(suite, t_parse_uri9);
611 URI_TEST(suite, t_parse_uri10);
612 URI_TEST(suite, t_parse_uri11);
613 URI_TEST(suite, t_parse_uri12);
614 URI_TEST(suite, t_parse_uri13);
615 URI_TEST(suite, t_parse_uri14);
616 URI_TEST(suite, t_parse_uri15);
617 URI_TEST(suite, t_parse_uri16);
618 URI_TEST(suite, t_parse_uri17);
619 URI_TEST(suite, t_parse_uri18);
620 URI_TEST(suite, t_parse_uri19);
621 URI_TEST(suite, t_parse_uri20);
622 URI_TEST(suite, t_parse_uri21);
623 URI_TEST(suite, t_parse_uri22);
624 URI_TEST(suite, t_parse_uri23);
625 URI_TEST(suite, t_parse_uri24);
626
627 return suite;
628 }
629