xref: /third_party/libcoap/tests/test_uri.c (revision c87c5fba)
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