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 16static void 17t_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 40static void 41t_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 63static void 64t_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 85static void 86t_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 95static void 96t_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 118static void 119t_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 141static void 142t_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 182static void 183t_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 199static void 200t_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 216static void 217t_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 239static void 240t_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 281static void 282t_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 331static void 332t_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 355static void 356t_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 378static void 379t_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 394static void 395t_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 409static void 410t_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 438static void 439t_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 459static void 460t_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 480static void 481t_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 501static void 502t_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 522static void 523t_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 544static void 545t_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 */ 570static void 571t_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 584CU_pSuite 585t_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