You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rspamd_cxx_unit_utils.hxx 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * Copyright 2024 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* Detached unit tests for the utils */
  17. #ifndef RSPAMD_RSPAMD_CXX_UNIT_UTILS_HXX
  18. #define RSPAMD_RSPAMD_CXX_UNIT_UTILS_HXX
  19. #define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
  20. #include "doctest/doctest.h"
  21. #include "libmime/mime_headers.h"
  22. #include "contrib/libottery/ottery.h"
  23. #include "libcryptobox/cryptobox.h"
  24. #include "libserver/http/http_message.h"
  25. #include <vector>
  26. #include <utility>
  27. #include <string>
  28. extern "C" long rspamd_http_parse_keepalive_timeout(const rspamd_ftok_t *tok);
  29. TEST_SUITE("rspamd_utils")
  30. {
  31. TEST_CASE("rspamd_strip_smtp_comments_inplace")
  32. {
  33. std::vector<std::pair<std::string, std::string>> cases{
  34. {"abc", "abc"},
  35. {"abc(foo)", "abc"},
  36. {"abc(foo()", "abc"},
  37. {"abc(foo))", "abc)"},
  38. {"abc(foo(bar))", "abc"},
  39. {"(bar)abc(foo)", "abc"},
  40. {"ab(ololo)c(foo)", "abc"},
  41. {"ab(olo\\)lo)c(foo)", "abc"},
  42. {"ab(trol\\\1lo)c(foo)", "abc"},
  43. {"\\ab(trol\\\1lo)c(foo)", "abc"},
  44. {"", ""},
  45. {"<test_id@example.net> (added by postmaster@example.net)", "<test_id@example.net> "}};
  46. for (const auto &c: cases) {
  47. SUBCASE(("strip comments in " + c.first).c_str())
  48. {
  49. auto *cpy = new char[c.first.size()];
  50. memcpy(cpy, c.first.data(), c.first.size());
  51. auto nlen = rspamd_strip_smtp_comments_inplace(cpy, c.first.size());
  52. CHECK(std::string{cpy, nlen} == c.second);
  53. delete[] cpy;
  54. }
  55. }
  56. }
  57. TEST_CASE("rspamd_http_parse_keepalive_timeout")
  58. {
  59. std::vector<std::pair<std::string, long>> cases{
  60. {"timeout=5, max=1000", 5},
  61. {"max=1000, timeout=5", 5},
  62. {"max=1000, timeout=", -1},
  63. {"max=1000, timeout=0", 0},
  64. {"max=1000, timeout=-5", -1},
  65. {"timeout=5", 5},
  66. {" timeout=5; ", 5},
  67. {"timeout = 5", 5},
  68. };
  69. for (const auto &c: cases) {
  70. SUBCASE(("parse http keepalive header " + c.first).c_str())
  71. {
  72. rspamd_ftok_t t;
  73. t.begin = c.first.data();
  74. t.len = c.first.size();
  75. auto res = rspamd_http_parse_keepalive_timeout(&t);
  76. CHECK(res == c.second);
  77. }
  78. }
  79. }
  80. TEST_CASE("rspamd_fstring_gzip tests")
  81. {
  82. rspamd_fstring_t *fstr;
  83. // Test empty data compression
  84. SUBCASE("Empty data")
  85. {
  86. fstr = rspamd_fstring_new_init("", 0);
  87. gboolean result = rspamd_fstring_gzip(&fstr);
  88. CHECK(result == TRUE);
  89. CHECK(fstr->len == 20);
  90. result = rspamd_fstring_gunzip(&fstr);
  91. CHECK(result == TRUE);
  92. CHECK(fstr->len == 0);
  93. rspamd_fstring_free(fstr);
  94. }
  95. SUBCASE("Non empty data")
  96. {
  97. fstr = RSPAMD_FSTRING_LIT("helohelo");
  98. gboolean result = rspamd_fstring_gzip(&fstr);
  99. CHECK(result == TRUE);
  100. CHECK(fstr->len == 26);
  101. result = rspamd_fstring_gunzip(&fstr);
  102. CHECK(result == TRUE);
  103. CHECK(memcmp(fstr->str, "helohelo", fstr->len) == 0);
  104. CHECK(fstr->len == sizeof("helohelo") - 1);
  105. rspamd_fstring_free(fstr);
  106. }
  107. SUBCASE("Some real compression")
  108. {
  109. fstr = rspamd_fstring_sized_new(sizeof("helohelo") * 1024);
  110. for (int i = 0; i < 1024; i++) {
  111. fstr = rspamd_fstring_append(fstr, "helohelo", sizeof("helohelo") - 1);
  112. }
  113. gboolean result = rspamd_fstring_gzip(&fstr);
  114. CHECK(result == TRUE);
  115. CHECK(fstr->len == 49);
  116. result = rspamd_fstring_gunzip(&fstr);
  117. CHECK(result == TRUE);
  118. CHECK(memcmp(fstr->str, "helohelo", sizeof("helohelo") - 1) == 0);
  119. CHECK(fstr->len == (sizeof("helohelo") - 1) * 1024);
  120. rspamd_fstring_free(fstr);
  121. }
  122. SUBCASE("Random data compression")
  123. {
  124. rspamd_cryptobox_fast_hash_state_t hst;
  125. rspamd_cryptobox_fast_hash_init(&hst, 0);
  126. fstr = rspamd_fstring_sized_new(30 * 1024 * 1024);
  127. for (int i = 0; i < 30 * 1024; i++) {
  128. char tmp[1024];
  129. ottery_rand_bytes(tmp, sizeof(tmp));
  130. fstr = rspamd_fstring_append(fstr, tmp, sizeof(tmp));
  131. rspamd_cryptobox_fast_hash_update(&hst, tmp, sizeof(tmp));
  132. }
  133. auto crc = rspamd_cryptobox_fast_hash(fstr->str, fstr->len, 0);
  134. CHECK(crc == rspamd_cryptobox_fast_hash_final(&hst));
  135. gboolean result = rspamd_fstring_gzip(&fstr);
  136. CHECK(result == TRUE);
  137. // Assuming there are no miracles
  138. CHECK(fstr->len >= 30 * 1024 * 1024);
  139. result = rspamd_fstring_gunzip(&fstr);
  140. CHECK(result == TRUE);
  141. CHECK(fstr->len == 30 * 1024 * 1024);
  142. auto final_crc = rspamd_cryptobox_fast_hash(fstr->str, fstr->len, 0);
  143. CHECK(crc == final_crc);
  144. rspamd_fstring_free(fstr);
  145. }
  146. }
  147. TEST_CASE("rspamd_message_header_unfold_inplace")
  148. {
  149. std::vector<std::pair<std::string, std::string>> cases{
  150. {"abc", "abc"},
  151. {"abc\r\n def", "abc def"},
  152. {"abc\r\n\tdef", "abc def"},
  153. {"abc\r\n\tdef\r\n\tghi", "abc def ghi"},
  154. {"abc\r\n\tdef\r\n\tghi\r\n", "abc def ghi"},
  155. {"abc\r\n\tdef\r\n\tghi\r\n\t", "abc def ghi"},
  156. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl", "abc def ghi jkl"},
  157. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n", "abc def ghi jkl"},
  158. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\t", "abc def ghi jkl"},
  159. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno", "abc def ghi jkl mno"},
  160. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n", "abc def ghi jkl mno"},
  161. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\t", "abc def ghi jkl mno"},
  162. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr", "abc def ghi jkl mno pqr"},
  163. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n", "abc def ghi jkl mno pqr"},
  164. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n\t", "abc def ghi jkl mno pqr"},
  165. {"abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n\tstu", "abc def ghi jkl mno pqr stu"},
  166. // Newline at the end
  167. {
  168. "abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n\tstu\r\n", "abc def ghi jkl mno pqr stu"},
  169. // Spaces at the end
  170. {
  171. "abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n\tstu\r\n\t", "abc def ghi jkl mno pqr stu"},
  172. // Multiple spaces at the end
  173. {
  174. "abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n\tstu\r\n\t ", "abc def ghi jkl mno pqr stu"},
  175. // Multiple spaces in middle
  176. {
  177. "abc\r\n\tdef\r\n\tghi\r\n\tjkl\r\n\tmno\r\n\tpqr\r\n\tstu \r\n\t a", "abc def ghi jkl mno pqr stu a"},
  178. };
  179. for (const auto &c: cases) {
  180. SUBCASE(("unfold header " + c.second).c_str())
  181. {
  182. auto *cpy = new char[c.first.size()];
  183. memcpy(cpy, c.first.data(), c.first.size());
  184. auto nlen = rspamd_message_header_unfold_inplace(cpy, c.first.size());
  185. CHECK(std::string{cpy, nlen} == c.second);
  186. delete[] cpy;
  187. }
  188. }
  189. }
  190. TEST_CASE("rspamd_http_message_from_url")
  191. {
  192. std::vector<std::pair<std::string, std::string>> cases{
  193. {"http://example.com", "/"},
  194. {"http://example.com/", "/"},
  195. {"http://example.com/lol", "/lol"},
  196. {"http://example.com/lol#keke", "/lol"},
  197. {"http://example.com/lol?omg=huh&oh", "/lol?omg=huh&oh"},
  198. {"http://example.com/lol?omg=huh&oh#", "/lol?omg=huh&oh"},
  199. {"http://example.com/lol?omg=huh&oh#keke", "/lol?omg=huh&oh"},
  200. {"http://example.com/lol?", "/lol"},
  201. {"http://example.com/lol?#", "/lol"},
  202. };
  203. for (const auto &c: cases) {
  204. SUBCASE(("rspamd_http_message_from_url: " + c.first).c_str())
  205. {
  206. auto *msg = rspamd_http_message_from_url(c.first.c_str());
  207. std::size_t nlen;
  208. auto *path = rspamd_http_message_get_url(msg, &nlen);
  209. CHECK(std::string{path, nlen} == c.second);
  210. rspamd_http_message_unref(msg);
  211. }
  212. }
  213. }
  214. }
  215. #endif