FreeRDP
Loading...
Searching...
No Matches
TestString.c
1
2#include <stdio.h>
3#include <winpr/crt.h>
4#include <winpr/string.h>
5#include <winpr/windows.h>
6#include <winpr/endian.h>
7
8static const CHAR testStringA[] = { 'T', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b',
9 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u',
10 'm', 'p', 's', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h',
11 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g', '\0' };
12
13#define testStringA_Length ((sizeof(testStringA) / sizeof(CHAR)) - 1)
14
15static const CHAR testToken1A[] = { 'q', 'u', 'i', 'c', 'k', '\0' };
16static const CHAR testToken2A[] = { 'b', 'r', 'o', 'w', 'n', '\0' };
17static const CHAR testToken3A[] = { 'f', 'o', 'x', '\0' };
18
19#define testToken1A_Length ((sizeof(testToken1A) / sizeof(CHAR)) - 1)
20#define testToken2A_Length ((sizeof(testToken2A) / sizeof(CHAR)) - 1)
21#define testToken3A_Length ((sizeof(testToken3A) / sizeof(CHAR)) - 1)
22
23static const CHAR testTokensA[] = { 'q', 'u', 'i', 'c', 'k', '\r', '\n', 'b', 'r', 'o',
24 'w', 'n', '\r', '\n', 'f', 'o', 'x', '\r', '\n', '\0' };
25
26#define testTokensA_Length ((sizeof(testTokensA) / sizeof(CHAR)) - 1)
27
28static const CHAR testDelimiterA[] = { '\r', '\n', '\0' };
29
30#define testDelimiterA_Length ((sizeof(testDelimiter) / sizeof(CHAR)) - 1)
31
32struct url_test_pair
33{
34 const char* what;
35 const char* escaped;
36};
37
38static const struct url_test_pair url_tests[] = {
39 { "xxx%bar ga<ka>ee#%%#%{h}g{f{e%d|c\\b^a~p[q]r`s;t/u?v:w@x=y&z$xxx",
40 "xxx%25bar%20ga%3Cka%3Eee%23%25%25%23%25%7Bh%7Dg%7Bf%7Be%25d%7Cc%5Cb%5Ea~p%5Bq%5Dr%60s%3Bt%"
41 "2Fu%3Fv%3Aw%40x%3Dy%26z%24xxx" },
42 { "äöúëü", "%C3%A4%C3%B6%C3%BA%C3%AB%C3%BC" },
43 { "🎅🏄🤘😈", "%F0%9F%8E%85%F0%9F%8F%84%F0%9F%A4%98%F0%9F%98%88" },
44 { "foo$.%.^.&.\\.txt+", "foo%24.%25.%5E.%26.%5C.txt%2B" }
45};
46
47static BOOL test_url_escape(void)
48{
49 for (size_t x = 0; x < ARRAYSIZE(url_tests); x++)
50 {
51 const struct url_test_pair* cur = &url_tests[x];
52
53 char* escaped = winpr_str_url_encode(cur->what, strlen(cur->what) + 1);
54 char* what = winpr_str_url_decode(cur->escaped, strlen(cur->escaped) + 1);
55
56 const size_t elen = strlen(escaped);
57 const size_t wlen = strlen(what);
58 const size_t pelen = strlen(cur->escaped);
59 const size_t pwlen = strlen(cur->what);
60 BOOL rc = TRUE;
61 if (!escaped || (elen != pelen) || (strcmp(escaped, cur->escaped) != 0))
62 {
63 printf("expected: [%" PRIuz "] %s\n", pelen, cur->escaped);
64 printf("got : [%" PRIuz "] %s\n", elen, escaped);
65 rc = FALSE;
66 }
67 else if (!what || (wlen != pwlen) || (strcmp(what, cur->what) != 0))
68 {
69 printf("expected: [%" PRIuz "] %s\n", pwlen, cur->what);
70 printf("got : [%" PRIuz "] %s\n", wlen, what);
71 rc = FALSE;
72 }
73
74 free(escaped);
75 free(what);
76 if (!rc)
77 return FALSE;
78 }
79
80 return TRUE;
81}
82
83WINPR_ATTR_NODISCARD
84static BOOL test_winpr_asprintf(void)
85{
86 BOOL rc = FALSE;
87 const char test[] = "test string case";
88 const size_t len = strnlen(test, sizeof(test));
89
90 char* str = nullptr;
91 size_t slen = 0;
92 const int res = winpr_asprintf(&str, &slen, "%s", test);
93 if (!str)
94 goto fail;
95 if (res < 0)
96 goto fail;
97 if ((size_t)res != len)
98 goto fail;
99 if (len != slen)
100 goto fail;
101 if (strnlen(str, slen + 10) != slen)
102 goto fail;
103 rc = TRUE;
104fail:
105 free(str);
106 return rc;
107}
108
109WINPR_ATTR_NODISCARD
110static BOOL test_winpr_strnchr(void)
111{
112 struct strnchr_test
113 {
114 const char* haystack;
115 const char needle;
116 size_t hlen;
117 int expected; /* offset of the match, or -1 when NULL is expected */
118 };
119 const struct strnchr_test tests[] = { { "ab", '\r', 0, -1 },
120 { "x!", 'y', 1, -1 },
121 { "abc", 'c', 2, -1 },
122 { "abc", '\0', 4, 3 },
123 { "a\r\nb", '\r', 4, 1 },
124 { "xxab", 'a', 4, 2 },
125 { "xxxx", 'b', 4, -1 },
126 { "abc", 'b', 3, 1 },
127 /* hlen == 0 finds nothing */
128 { "abc", 'a', 0, -1 } };
129
130 union
131 {
132 char c[2];
133 WCHAR wc;
134 } cnv;
135
136 const char backslash = '\\';
137 cnv.c[0] = backslash;
138 cnv.c[1] = 0;
139
140 const WCHAR tle = winpr_wchar_ToLE(backslash);
141 if (tle != cnv.wc)
142 {
143 WLog_ERR(__func__, "test winpr_wchar_ToLE failed");
144 return FALSE;
145 }
146
147 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
148 {
149 const struct strnchr_test* t = &tests[x];
150 WCHAR haystack[32] = WINPR_C_ARRAY_INIT;
151 const SSIZE_T cr = ConvertUtf8ToWChar(t->haystack, haystack, ARRAYSIZE(haystack));
152 WINPR_ASSERT(cr >= 0);
153
154 const WCHAR needle = winpr_wchar_ToLE(t->needle);
155
156 const WCHAR* rc = winpr_wcsnchr(haystack, t->hlen, needle);
157 const WCHAR* rrc = winpr_wcsnrchr(haystack, t->hlen, needle);
158
159 if (t->expected < 0)
160 {
161 if (rc || rrc)
162 {
163 WLog_ERR(__func__,
164 "test %" PRIuz
165 " failed: %s -> winpr_wcsnchr %p, winpr_wcsnrchr %p : expected NULL",
166 x, t->haystack, rc, rrc);
167 return FALSE;
168 }
169 }
170 else
171 {
172 if (!rc || !rrc)
173 {
174 WLog_ERR(__func__,
175 "test %" PRIuz
176 " failed: %s -> winpr_wcsnchr %p, winpr_wcsnrchr %p : expected %d",
177 x, t->haystack, rc, rrc, t->expected);
178 return FALSE;
179 }
180
181 const size_t off = rc - haystack;
182 const size_t roff = rrc - haystack;
183 if (off != t->expected)
184 {
185 WLog_ERR(__func__,
186 "test %" PRIuz " failed: %s -> winpr_wcsnchr offset %" PRIuz
187 ", expected %d",
188 x, t->haystack, rc, t->expected);
189 return FALSE;
190 }
191 if (roff != t->expected)
192 {
193 WLog_ERR(__func__,
194 "test %" PRIuz " failed: %s -> winpr_wcsnrchr offset %" PRIuz
195 ", expected %d",
196 x, t->haystack, rrc, t->expected);
197 return FALSE;
198 }
199 }
200 }
201 return TRUE;
202}
203
204WINPR_ATTR_NODISCARD
205static BOOL test_winpr_strnstr(void)
206{
207 struct strnstr_test
208 {
209 const char* haystack;
210 const char* needle;
211 size_t hlen;
212 int expected; /* offset of the match, or -1 when NULL is expected */
213 };
214 const struct strnstr_test tests[] = {
215 /* regression: a needle longer than hlen must never match on a prefix.
216 * winpr_strnstr used to clamp the needle length to hlen, so these
217 * returned the haystack instead of NULL (see gateway http_response_recv
218 * SIGSEGV). */
219 { "ab", "\r\n", 0, -1 },
220 { "x!", "xy", 1, -1 },
221 { "abc", "abc", 2, -1 },
222 /* an empty needle returns the haystack */
223 { "abc", "", 5, 0 },
224 /* basic matches */
225 { "a\r\nb", "\r\n", 4, 1 },
226 { "xxab", "ab", 4, 2 },
227 { "xxxx", "ab", 4, -1 },
228 { "abc", "abc", 3, 0 },
229 /* hlen == 0 finds nothing */
230 { "abc", "a", 0, -1 },
231 /* an hlen larger than the string just searches the whole string */
232 { "abc", "a", SIZE_MAX, 0 },
233 };
234
235 for (size_t i = 0; i < ARRAYSIZE(tests); i++)
236 {
237 const struct strnstr_test* t = &tests[i];
238 char haystack[32] = WINPR_C_ARRAY_INIT;
239 strncpy(haystack, t->haystack, sizeof(haystack) - 1);
240
241 char* res = winpr_strnstr(haystack, t->needle, t->hlen);
242 char* exp = (t->expected < 0) ? nullptr : haystack + t->expected;
243 if (res != exp)
244 {
245 printf("winpr_strnstr error: case %" PRIuz " (\"%s\", \"%s\", %" PRIuz "): "
246 "actual %p, expected %p\n",
247 i, t->haystack, t->needle, t->hlen, (void*)res, (void*)exp);
248 return FALSE;
249 }
250 }
251
252 /* An embedded NUL terminates the search, matching native strnstr: "cd" sits
253 * past the NUL, so it must not be found even though hlen spans it. */
254 char embedded[8] = { 'a', 'b', '\0', 'c', 'd', '\0', '\0', '\0' };
255 if (winpr_strnstr(embedded, "cd", 5) != nullptr)
256 {
257 printf("winpr_strnstr error: matched past an embedded NUL terminator\n");
258 return FALSE;
259 }
260
261 return TRUE;
262}
263
264static BOOL test_valid_url(void)
265{
266 struct test_t
267 {
268 char* string;
269 size_t len;
270 BOOL isUrl;
271 };
272
273 static const struct test_t tests[] = { { "foo.bar.blabla", 15, TRUE },
274 { "somehostname", 12, TRUE },
275 { "somehostname:1234", 17, TRUE },
276 { "tsv://somehostname", 18, TRUE },
277 { "tsv://somehostname/path/foo/gaga", 32, TRUE },
278 { "tsv://somehostname:1234", 23, TRUE },
279 { "tsv://somehostname:1234/path/foo", 33, TRUE },
280 { "lala\rgaga\n", 12, FALSE },
281 { "192.168.0.1", 11, TRUE },
282 //{ "192.168.0.1:1234", 16, FALSE },
283 //{ "192.168.0.1:1234/foo/", 21, FALSE },
284 { "192.168.0.1/bar/foo/", 20, TRUE },
285 { "[::1]", 5, FALSE },
286 { "[::1]:1234", 8, FALSE },
287 { "[2001:db8:3333:4444:5555:6666:7777:8888]", 40,
288 FALSE },
289 { "[2001:db8::1]", 13, FALSE } };
290 BOOL rc = TRUE;
291 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
292 {
293 const struct test_t* cur = &tests[x];
294
295 const BOOL rc1 = winpr_str_is_valid_url(cur->string);
296 const BOOL rc2 = winpr_str_is_valid_urlN(cur->string, cur->len);
297
298#if defined(WINPR_HAVE_REGCOMP) || defined(WITH_URIPARSER)
299 if (rc1 != rc2)
300 {
301 (void)fprintf(stderr, "[%.*s] %s is not valid\n", __func__, cur->len, cur->string);
302 rc = FALSE;
303 }
304 else if (rc1 != cur->isUrl)
305 {
306 (void)fprintf(stderr, "[%.*s] %s is not valid\n", __func__, cur->len, cur->string);
307 rc = FALSE;
308 }
309#else
310 fprintf(stderr, "[%s] TODO: !defined(WINPR_HAVE_REGCOMP) && !defined(WITH_URIPARSER)\n",
311 __func__);
312#endif
313 }
314
315 return rc;
316}
317
318static BOOL test_newline(void)
319{
320 struct test_t
321 {
322 char* string;
323 size_t len;
324 BOOL hasNewlines;
325 };
326
327 const struct test_t tests[] = { { "foo.bar.blabla", 15, FALSE },
328 { "somehostname", 12, FALSE },
329 { "lala\rgaga\n", 13, TRUE } };
330
331 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
332 {
333 const struct test_t* cur = &tests[x];
334
335 const BOOL rc1 = winpr_str_has_newlines(cur->string);
336 if (rc1 != cur->hasNewlines)
337 return FALSE;
338 }
339
340 return TRUE;
341}
342
343static BOOL test_crlf(void)
344{
345 struct test_t
346 {
347 const char* string;
348 size_t len;
349 const char* expected;
350 size_t expectedLen;
351 };
352
353 /* input lengths exclude the terminator, like text/plain from the X11 and
354 * SDL clients; the last case includes it */
355 const struct test_t tests[] = {
356 { "\nabc", 4, "\r\nabc", 5 }, { "abc\n", 4, "abc\r\n", 5 },
357 { "\n", 1, "\r\n", 2 }, { "\r", 1, "\r\n", 2 },
358 { "a\r\nb", 4, "a\r\nb", 4 }, { "a\n\nb", 4, "a\r\n\r\nb", 6 },
359 { "\r\n", 2, "\r\n", 2 }, { "a\nb", 4, "a\r\nb", 5 },
360 };
361
362 BOOL rc = TRUE;
363 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
364 {
365 const struct test_t* cur = &tests[x];
366
367 size_t size = cur->len;
368 char* cnv = ConvertLineEndingToCRLF(cur->string, &size);
369 if (!cnv || (size != cur->expectedLen) || (memcmp(cnv, cur->expected, size) != 0) ||
370 (cnv[size] != '\0'))
371 {
372 printf("ConvertLineEndingToCRLF error: case %" PRIuz ": size %" PRIuz
373 ", expected %" PRIuz "\n",
374 x, size, cur->expectedLen);
375 rc = FALSE;
376 }
377 free(cnv);
378 }
379
380 size_t size = 0;
381 char* cnv = ConvertLineEndingToCRLF("", &size);
382 if (cnv || (size != 0))
383 {
384 printf("ConvertLineEndingToCRLF error: empty input returned data\n");
385 rc = FALSE;
386 }
387 free(cnv);
388
389 return rc;
390}
391
392WINPR_ATTR_NODISCARD
393static BOOL test_hostname(const char** tests, size_t count, BOOL shouldSucceed)
394{
395 for (size_t x = 0; x < count; x++)
396 {
397 const char* test = tests[x];
398 size_t len = strlen(test);
399
400 const BOOL a = winpr_str_is_valid_hostname(test);
401 const BOOL b = winpr_str_is_valid_hostnameN(test, len);
402 if (a != b)
403 return FALSE;
404 if (a != shouldSucceed)
405 return FALSE;
406 }
407 return TRUE;
408}
409
410int TestString(int argc, char* argv[])
411{
412 const WCHAR* p = nullptr;
413 size_t pos = 0;
414 size_t length = 0;
415 WCHAR* context = nullptr;
416
417 WINPR_UNUSED(argc);
418 WINPR_UNUSED(argv);
419
420 if (!test_valid_url())
421 return -1;
422
423 if (!test_newline())
424 return -1;
425
426 if (!test_winpr_asprintf())
427 return -1;
428
429 if (!test_url_escape())
430 return -1;
431
432 if (!test_winpr_strnstr())
433 return -1;
434
435 if (!test_winpr_strnchr())
436 return -1;
437
438 if (!test_crlf())
439 return -1;
440
441 /* _wcslen */
442 WCHAR testStringW[ARRAYSIZE(testStringA)] = WINPR_C_ARRAY_INIT;
443 (void)ConvertUtf8NToWChar(testStringA, ARRAYSIZE(testStringA), testStringW,
444 ARRAYSIZE(testStringW));
445 const size_t testStringW_Length = testStringA_Length;
446 length = _wcslen(testStringW);
447
448 if (length != testStringW_Length)
449 {
450 printf("_wcslen error: length mismatch: Actual: %" PRIuz ", Expected: %" PRIuz "\n", length,
451 testStringW_Length);
452 return -1;
453 }
454
455 /* _wcschr */
456 union
457 {
458 char c[2];
459 WCHAR w;
460 } search;
461 search.c[0] = 'r';
462 search.c[1] = '\0';
463
464#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
465 p = _wcschr(testStringW, search.w);
466 pos = (p - testStringW);
467
468 if (pos != 11)
469 {
470 printf("_wcschr error: position mismatch: Actual: %" PRIuz ", Expected: 11\n", pos);
471 return -1;
472 }
473
474 p = _wcschr(&testStringW[pos + 1], search.w);
475 pos = (p - testStringW);
476
477 if (pos != 29)
478 {
479 printf("_wcschr error: position mismatch: Actual: %" PRIuz ", Expected: 29\n", pos);
480 return -1;
481 }
482
483 p = _wcschr(&testStringW[pos + 1], search.w);
484
485 if (p != nullptr)
486 {
487 printf("_wcschr error: return value mismatch: Actual: %p, Expected: nullptr\n",
488 (const void*)p);
489 return -1;
490 }
491#endif
492
493 /* wcstok_s */
494 WCHAR testDelimiterW[ARRAYSIZE(testDelimiterA)] = WINPR_C_ARRAY_INIT;
495 WCHAR testTokensW[ARRAYSIZE(testTokensA)] = WINPR_C_ARRAY_INIT;
496 (void)ConvertUtf8NToWChar(testTokensA, ARRAYSIZE(testTokensA), testTokensW,
497 ARRAYSIZE(testTokensW));
498 (void)ConvertUtf8NToWChar(testDelimiterA, ARRAYSIZE(testDelimiterA), testDelimiterW,
499 ARRAYSIZE(testDelimiterW));
500 p = wcstok_s(testTokensW, testDelimiterW, &context);
501
502 WCHAR testToken1W[ARRAYSIZE(testToken1A)] = WINPR_C_ARRAY_INIT;
503 (void)ConvertUtf8NToWChar(testToken1A, ARRAYSIZE(testToken1A), testToken1W,
504 ARRAYSIZE(testToken1W));
505 if (memcmp(p, testToken1W, sizeof(testToken1W)) != 0)
506 {
507 printf("wcstok_s error: token #1 mismatch\n");
508 return -1;
509 }
510
511 p = wcstok_s(nullptr, testDelimiterW, &context);
512
513 WCHAR testToken2W[ARRAYSIZE(testToken2A)] = WINPR_C_ARRAY_INIT;
514 (void)ConvertUtf8NToWChar(testToken2A, ARRAYSIZE(testToken2A), testToken2W,
515 ARRAYSIZE(testToken2W));
516 if (memcmp(p, testToken2W, sizeof(testToken2W)) != 0)
517 {
518 printf("wcstok_s error: token #2 mismatch\n");
519 return -1;
520 }
521
522 p = wcstok_s(nullptr, testDelimiterW, &context);
523
524 WCHAR testToken3W[ARRAYSIZE(testToken3A)] = WINPR_C_ARRAY_INIT;
525 (void)ConvertUtf8NToWChar(testToken3A, ARRAYSIZE(testToken3A), testToken3W,
526 ARRAYSIZE(testToken3W));
527 if (memcmp(p, testToken3W, sizeof(testToken3W)) != 0)
528 {
529 printf("wcstok_s error: token #3 mismatch\n");
530 return -1;
531 }
532
533 p = wcstok_s(nullptr, testDelimiterW, &context);
534
535 if (p != nullptr)
536 {
537 printf("wcstok_s error: return value is not nullptr\n");
538 return -1;
539 }
540
541 {
542 char test[] = "foo\r\nbar\r\nhaha\r\n";
543 const size_t slen = strnlen(test, sizeof(test));
544
545 const size_t rc = ConvertLineEndingToLF(test, sizeof(test));
546 if (rc != sizeof(test) - 3)
547 return -1;
548
549 const size_t rlen = strnlen(test, sizeof(test));
550 if (rlen != slen - 3)
551 return -1;
552 }
553 {
554 const char* tests[] = { "some.host.somewhere.tld",
555 "somehost",
556 "a",
557 "xn--mnchen-3ya.de",
558 "foo.xn--fiqs8s",
559 "test.xn--h2brj9c",
560 "sh25",
561 "as-sh25",
562 "AS-SH25",
563 "a.co",
564 "some-x.tld" };
565 const char* failtests[] = {
566 "", ".some.tld", "some.tld.", "-some.tld", "some-.tld", "some.-tld", "some.tld-",
567 "some-xx.t", "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.tld",
568 // NOLINTBEGIN(bugprone-suspicious-missing-comma)
569 "abcdef0123456789abcdef0123456789abcdef.0123456789abcdef0123456789abcdef."
570 "0123456789abcdef0123456789abcdef.0123456789abcdef0123456789abcdef."
571 "0123456789abcdef0123456789abcdef.0123456789abcdef0123456789."
572 "0123456789abcdef0123456789abcdef.0123456789abcdef0123456789abcdef"
573 // NOLINTEND(bugprone-suspicious-missing-comma)
574 };
575 if (!test_hostname(tests, ARRAYSIZE(tests), TRUE))
576 return -1;
577 if (!test_hostname(failtests, ARRAYSIZE(failtests), FALSE))
578 return -1;
579 }
580 return 0;
581}