unit_tests/check_str.c
5ee56e41
 /*
  *  Unit tests for string functions. 
  *
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2008-2013 Sourcefire, Inc.
5ee56e41
  *
  *  Authors: Török Edvin
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
e3c912e1
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 
 #include <stdlib.h>
 #include <limits.h>
 #include <string.h>
 #include <check.h>
b2e7c931
 
e3c912e1
 #include "../libclamav/clamav.h"
 #include "../libclamav/others.h"
 #include "../libclamav/str.h"
62b7686d
 #include "../libclamav/mbox.h"
 #include "../libclamav/message.h"
e3c912e1
 #include "../libclamav/jsparse/textbuf.h"
a39b29cb
 #include "checks.h"
e3c912e1
 
288057e9
 START_TEST(test_unescape_simple)
e3c912e1
 {
288057e9
     char *str = cli_unescape("");
     fail_unless(str && strlen(str) == 0, "cli_unescape empty string");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("1");
     fail_unless(str && !strcmp(str, "1"), "cli_unescape one char");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("tesT");
     fail_unless(str && !strcmp(str, "tesT"), "cli_unescape simple string");
     free(str);
e3c912e1
 }
 END_TEST
 
288057e9
 START_TEST(test_unescape_hex)
e3c912e1
 {
288057e9
     char *str = cli_unescape("%5a");
     fail_unless(str && !strcmp(str, "\x5a"), "cli_unescape hex");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("%b5%8");
     fail_unless(str && !strcmp(str, "\xb5%8"), "cli_unescape truncated");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("%b5%");
     fail_unless(str && !strcmp(str, "\xb5%"), "cli_unescape truncated/2");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("%00");
     fail_unless(str && !strcmp(str, "\x1"), "cli_unescape %00");
     free(str);
e3c912e1
 }
 END_TEST
 
288057e9
 START_TEST(test_unescape_unicode)
e3c912e1
 {
288057e9
     char *str = cli_unescape("%u05D0");
     /* unicode is converted to utf-8 representation */
     fail_unless(str && !strcmp(str, "\xd7\x90"), "cli_unescape unicode aleph");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("%u00a2%u007f%u0080%u07ff%u0800%ue000");
     fail_unless(str && !strcmp(str, "\xc2\xa2\x7f\xc2\x80\xdf\xbf\xe0\xa0\x80\xee\x80\x80"),
                 "cli_unescape utf-8 test");
     free(str);
e3c912e1
 
288057e9
     str = cli_unescape("%%u123%u12%u1%u%u1234");
     fail_unless(str && !strcmp(str, "%%u123%u12%u1%u\xe1\x88\xb4"),
                 "cli_unescape unicode truncated");
e3c912e1
 
288057e9
     free(str);
e3c912e1
 }
 END_TEST
 
 static struct text_buffer buf;
 
 static void buf_setup(void)
 {
288057e9
     memset(&buf, 0, sizeof(buf));
e3c912e1
 }
 
 static void buf_teardown(void)
 {
288057e9
     if (buf.data)
         free(buf.data);
     memset(&buf, 0, sizeof(buf));
e3c912e1
 }
 
288057e9
 START_TEST(test_append_len)
e3c912e1
 {
288057e9
     fail_unless(textbuffer_append_len(&buf, "test", 3) != -1, "tbuf append");
     fail_unless(buf.data && !strncmp(buf.data, "tes", 3), "textbuffer_append_len");
     errmsg_expected();
     fail_unless(textbuffer_append_len(&buf, "test", CLI_MAX_ALLOCATION) == -1, "tbuf append");
     fail_unless(buf.data && !strncmp(buf.data, "tes", 3), "textbuffer_append_len");
e3c912e1
 }
 END_TEST
 
288057e9
 START_TEST(test_append)
e3c912e1
 {
288057e9
     fail_unless(textbuffer_append(&buf, "test") != -1, "tbuf append");
     fail_unless(textbuffer_putc(&buf, '\0') != -1, "tbuf putc");
     fail_unless(buf.data && !strcmp(buf.data, "test"), "textbuffer_append");
e3c912e1
 }
 END_TEST
 
288057e9
 START_TEST(test_putc)
e3c912e1
 {
288057e9
     fail_unless(textbuffer_putc(&buf, '\x5a') != -1, "tbuf putc");
     fail_unless(buf.data && buf.data[0] == '\x5a', "textbuffer_putc");
e3c912e1
 }
 END_TEST
 
288057e9
 START_TEST(test_normalize)
e3c912e1
 {
288057e9
     const char *str      = "test\\0\\b\\t\\n\\v\\f\\r\\z\\x2a\\u1234test";
     const char *expected = "test\x1\b\t\n\v\f\rz\x2a\xe1\x88\xb4test";
     int rc;
e3c912e1
 
288057e9
     rc = cli_textbuffer_append_normalize(&buf, str, strlen(str));
     fail_unless(rc != -1, "normalize");
e3c912e1
 
288057e9
     fail_unless(textbuffer_putc(&buf, '\0') != -1, "putc \\0");
     fail_unless(buf.data && !strcmp(buf.data, expected), "normalized text");
e3c912e1
 }
 END_TEST
 
288057e9
 START_TEST(hex2str)
2e64bd9f
 {
288057e9
     char *r;
     const char inp1[] = "a00026";
     const char out1[] = "\xa0\x00\x26";
     const char inp2[] = "ag0026";
 
     r = cli_hex2str(inp1);
     fail_unless(!!r, "cli_hex2str NULL");
     fail_unless(!memcmp(r, out1, sizeof(out1) - 1),
                 "cli_hex2str invalid output");
     free(r);
 
     r = cli_hex2str(inp2);
     fail_unless(!r, "cli_hex2str on invalid input");
2e64bd9f
 }
 END_TEST
 
62b7686d
 #ifdef CHECK_HAVE_LOOPS
 static struct base64lines {
     const char *line;
e4a0f2c9
     const char *decoded;
288057e9
     unsigned int len;
62b7686d
 } base64tests[] = {
     {"", "", 0},
     {"Zg==", "f", 1},
     {"Zm8=", "fo", 2},
     {"Zm9v", "foo", 3},
     {"Zm9vYg==", "foob", 4},
     {"Zm9vYmFy", "foobar", 6},
     /* with missing padding */
288057e9
     {"Zg", "f", 1},
62b7686d
     {"Zm8", "fo", 2},
288057e9
     {"Zm9vYg", "foob", 4}};
62b7686d
 
288057e9
 START_TEST(test_base64)
62b7686d
 {
     unsigned char *ret, *ret2;
     unsigned len;
     unsigned char buf[1024];
     const struct base64lines *test = &base64tests[_i];
288057e9
     message *m                     = messageCreate();
62b7686d
     fail_unless(!!m, "Unable to create message");
 
     ret = decodeLine(m, BASE64, test->line, buf, sizeof(buf));
     fail_unless(!!ret, "unable to decode line");
 
     ret2 = base64Flush(m, ret);
 
     if (!ret2)
288057e9
         ret2 = ret;
62b7686d
     *ret2 = '\0';
288057e9
     len   = ret2 - buf;
62b7686d
     fail_unless_fmt(len == test->len, "invalid base64 decoded length: %u expected %u (%s)\n",
288057e9
                     len, test->len, buf);
62b7686d
     fail_unless_fmt(!memcmp(buf, test->decoded, test->len),
288057e9
                     "invalid base64 decoded data: %s, expected:%s\n",
                     buf, test->decoded);
62b7686d
     messageDestroy(m);
 }
 END_TEST
0eafa898
 
 static struct {
288057e9
     const char *u16;
     const char *u8;
0eafa898
 } u16_tests[] = {
     {"\x74\x00\x65\x00\x73\x00\x74\x00\x00\x00", "test"},
288057e9
     {"\xff\xfe\x00", ""},
     {"\x80\x00\x00", "\xc2\x80"},
     {"\xff\x07\x00", "\xdf\xbf"},
     {"\x00\x08\x00", "\xe0\xa0\x80"},
     {"\xff\x0f\x00", "\xe0\xbf\xbf"},
     {"\x00\x10\x00", "\xe1\x80\x80"},
     {"\xff\xcf\x00", "\xec\xbf\xbf"},
     {"\x00\xd0\x00", "\xed\x80\x80"},
     {"\xff\xd7\x00", "\xed\x9f\xbf"},
     {"\x00\xe0\x00", "\xee\x80\x80"},
     {"\xff\xff\x00", "\xef\xbf\xbf"},
     {"\x00\xd8\x00\xdc\x00", "\xf0\x90\x80\x80"},
     {"\xbf\xd8\xff\xdf\x00", "\xf0\xbf\xbf\xbf"},
     {"\xc0\xd8\x00\xdc\x00", "\xf1\x80\x80\x80"},
     {"\xbf\xdb\xff\xdf\x00", "\xf3\xbf\xbf\xbf"},
     {"\xc0\xdb\x00\xdc\x00", "\xf4\x80\x80\x80"},
     {"\xff\xdb\xff\xdf\x00", "\xf4\x8f\xbf\xbf"},
     {"\x00\xdc\x00\xd8\x00", "\xef\xbf\xbd\xef\xbf\xbd"}};
0eafa898
 
 static unsigned u16_len(const char *s)
 {
     unsigned i;
288057e9
     for (i = 0; s[i] || s[i + 1]; i += 2) {
     }
0eafa898
     return i;
 }
 
 START_TEST(test_u16_u8)
 {
569bdeeb
     char *result = cli_utf16_to_utf8(u16_tests[_i].u16, u16_len(u16_tests[_i].u16), UTF16_LE);
0eafa898
     fail_unless(!!result, "cli_utf16_to_utf8 non-null");
     fail_unless_fmt(!strcmp(result, u16_tests[_i].u8), "utf16_to_8 %d failed, expected: %s, got %s", _i, u16_tests[_i].u8, result);
     free(result);
 }
 END_TEST
 
62b7686d
 #endif
 
e3c912e1
 Suite *test_str_suite(void)
 {
     Suite *s = suite_create("str");
62b7686d
     TCase *tc_cli_unescape, *tc_tbuf, *tc_str, *tc_decodeline;
e3c912e1
 
     tc_cli_unescape = tcase_create("cli_unescape");
288057e9
     suite_add_tcase(s, tc_cli_unescape);
e3c912e1
     tcase_add_test(tc_cli_unescape, test_unescape_simple);
     tcase_add_test(tc_cli_unescape, test_unescape_unicode);
     tcase_add_test(tc_cli_unescape, test_unescape_hex);
 
     tc_tbuf = tcase_create("jsnorm textbuf functions");
288057e9
     suite_add_tcase(s, tc_tbuf);
     tcase_add_checked_fixture(tc_tbuf, buf_setup, buf_teardown);
e3c912e1
     tcase_add_test(tc_tbuf, test_append_len);
     tcase_add_test(tc_tbuf, test_append);
     tcase_add_test(tc_tbuf, test_putc);
     tcase_add_test(tc_tbuf, test_normalize);
 
2e64bd9f
     tc_str = tcase_create("str functions");
288057e9
     suite_add_tcase(s, tc_str);
2e64bd9f
     tcase_add_test(tc_str, hex2str);
0eafa898
 #ifdef CHECK_HAVE_LOOPS
288057e9
     tcase_add_loop_test(tc_str, test_u16_u8, 0, sizeof(u16_tests) / sizeof(u16_tests[0]));
0eafa898
 #endif
2e64bd9f
 
62b7686d
     tc_decodeline = tcase_create("decodeline");
288057e9
     suite_add_tcase(s, tc_decodeline);
62b7686d
 #ifdef CHECK_HAVE_LOOPS
288057e9
     tcase_add_loop_test(tc_decodeline, test_base64, 0, sizeof(base64tests) / sizeof(base64tests[0]));
62b7686d
 #endif
e3c912e1
     return s;
 }