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.

radix.c 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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. #include "config.h"
  17. #include "radix.h"
  18. #include "rspamd.h"
  19. #include "mem_pool.h"
  20. #include "btrie.h"
  21. #define msg_err_radix(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  22. "radix", tree->pool->tag.uid, \
  23. G_STRFUNC, \
  24. __VA_ARGS__)
  25. #define msg_warn_radix(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING, \
  26. "radix", tree->pool->tag.uid, \
  27. G_STRFUNC, \
  28. __VA_ARGS__)
  29. #define msg_info_radix(...) rspamd_default_log_function(G_LOG_LEVEL_INFO, \
  30. "radix", tree->pool->tag.uid, \
  31. G_STRFUNC, \
  32. __VA_ARGS__)
  33. #define msg_debug_radix(...) rspamd_conditional_debug_fast(NULL, NULL, \
  34. rspamd_radix_log_id, "radix", tree->pool->tag.uid, \
  35. G_STRFUNC, \
  36. __VA_ARGS__)
  37. INIT_LOG_MODULE(radix)
  38. struct radix_tree_compressed {
  39. rspamd_mempool_t *pool;
  40. struct btrie *tree;
  41. const char *name;
  42. size_t size;
  43. unsigned int duplicates;
  44. gboolean own_pool;
  45. };
  46. uintptr_t
  47. radix_find_compressed(radix_compressed_t *tree, const uint8_t *key, gsize keylen)
  48. {
  49. gconstpointer ret;
  50. g_assert(tree != NULL);
  51. ret = btrie_lookup(tree->tree, key, keylen * NBBY);
  52. if (ret == NULL) {
  53. return RADIX_NO_VALUE;
  54. }
  55. return (uintptr_t) ret;
  56. }
  57. uintptr_t
  58. radix_insert_compressed(radix_compressed_t *tree,
  59. uint8_t *key, gsize keylen,
  60. gsize masklen,
  61. uintptr_t value)
  62. {
  63. static const unsigned int max_duplicates = 32;
  64. unsigned int keybits = keylen * NBBY;
  65. uintptr_t old;
  66. char ip_str[INET6_ADDRSTRLEN + 1];
  67. int ret;
  68. g_assert(tree != NULL);
  69. g_assert(keybits >= masklen);
  70. msg_debug_radix("%s: want insert value %p with mask %z, key: %*xs",
  71. tree->name, (gpointer) value, keybits - masklen, (int) keylen, key);
  72. old = radix_find_compressed(tree, key, keylen);
  73. ret = btrie_add_prefix(tree->tree, key, keybits - masklen,
  74. (gconstpointer) value);
  75. if (ret != BTRIE_OKAY) {
  76. tree->duplicates++;
  77. if (tree->duplicates == max_duplicates) {
  78. msg_err_radix("%s: maximum duplicates limit reached: %d, "
  79. "suppress further errors",
  80. tree->name, max_duplicates);
  81. }
  82. else if (tree->duplicates < max_duplicates) {
  83. memset(ip_str, 0, sizeof(ip_str));
  84. if (keybits == 32) {
  85. msg_err_radix("%s: cannot insert %p, key: %s/%d, duplicate value",
  86. tree->name,
  87. (gpointer) value,
  88. inet_ntop(AF_INET, key, ip_str, sizeof(ip_str) - 1),
  89. (int) (keybits - masklen));
  90. }
  91. else if (keybits == 128) {
  92. msg_err_radix("%s: cannot insert %p, key: [%s]/%d, duplicate value",
  93. tree->name,
  94. (gpointer) value,
  95. inet_ntop(AF_INET6, key, ip_str, sizeof(ip_str) - 1),
  96. (int) (keybits - masklen));
  97. }
  98. else {
  99. msg_err_radix("%s: cannot insert %p with mask %z, key: %*xs, duplicate value",
  100. tree->name,
  101. (gpointer) value,
  102. keybits - masklen,
  103. (int) keylen, key);
  104. }
  105. }
  106. }
  107. else {
  108. tree->size++;
  109. }
  110. return old;
  111. }
  112. radix_compressed_t *
  113. radix_create_compressed(const char *tree_name)
  114. {
  115. radix_compressed_t *tree;
  116. tree = g_malloc(sizeof(*tree));
  117. if (tree == NULL) {
  118. return NULL;
  119. }
  120. tree->pool = rspamd_mempool_new(rspamd_mempool_suggest_size(), NULL, 0);
  121. tree->size = 0;
  122. tree->duplicates = 0;
  123. tree->tree = btrie_init(tree->pool);
  124. tree->own_pool = TRUE;
  125. tree->name = tree_name;
  126. return tree;
  127. }
  128. radix_compressed_t *
  129. radix_create_compressed_with_pool(rspamd_mempool_t *pool, const char *tree_name)
  130. {
  131. radix_compressed_t *tree;
  132. tree = rspamd_mempool_alloc(pool, sizeof(*tree));
  133. tree->pool = pool;
  134. tree->size = 0;
  135. tree->duplicates = 0;
  136. tree->tree = btrie_init(tree->pool);
  137. tree->own_pool = FALSE;
  138. tree->name = tree_name;
  139. return tree;
  140. }
  141. void radix_destroy_compressed(radix_compressed_t *tree)
  142. {
  143. if (tree) {
  144. if (tree->own_pool) {
  145. rspamd_mempool_delete(tree->pool);
  146. g_free(tree);
  147. }
  148. }
  149. }
  150. uintptr_t
  151. radix_find_compressed_addr(radix_compressed_t *tree,
  152. const rspamd_inet_addr_t *addr)
  153. {
  154. const unsigned char *key;
  155. unsigned int klen = 0;
  156. unsigned char buf[16];
  157. if (addr == NULL) {
  158. return RADIX_NO_VALUE;
  159. }
  160. key = rspamd_inet_address_get_hash_key(addr, &klen);
  161. if (key && klen) {
  162. if (klen == 4) {
  163. /* Map to ipv6 */
  164. memset(buf, 0, 10);
  165. buf[10] = 0xffu;
  166. buf[11] = 0xffu;
  167. memcpy(buf + 12, key, klen);
  168. key = buf;
  169. klen = sizeof(buf);
  170. }
  171. return radix_find_compressed(tree, key, klen);
  172. }
  173. return RADIX_NO_VALUE;
  174. }
  175. int rspamd_radix_add_iplist(const char *list, const char *separators,
  176. radix_compressed_t *tree, gconstpointer value,
  177. gboolean resolve, const char *tree_name)
  178. {
  179. char *token, *ipnet, *err_str, **strv, **cur, *brace;
  180. union {
  181. struct in_addr ina;
  182. struct in6_addr ina6;
  183. unsigned char buf[16];
  184. } addr_buf;
  185. unsigned int k = G_MAXINT;
  186. int af;
  187. int res = 0, r;
  188. struct addrinfo hints, *ai_res, *cur_ai;
  189. /* Split string if there are multiple items inside a single string */
  190. strv = g_strsplit_set(list, separators, 0);
  191. cur = strv;
  192. while (*cur) {
  193. af = AF_UNSPEC;
  194. if (**cur == '\0') {
  195. cur++;
  196. continue;
  197. }
  198. /* Extract ipnet */
  199. ipnet = g_strstrip(*cur);
  200. token = strsep(&ipnet, "/");
  201. if (ipnet != NULL) {
  202. errno = 0;
  203. /* Get mask */
  204. k = strtoul(ipnet, &err_str, 10);
  205. if (errno != 0) {
  206. msg_warn_radix(
  207. "%s: invalid netmask, error detected on symbol: %s, error: %s",
  208. tree_name,
  209. err_str,
  210. strerror(errno));
  211. k = G_MAXINT;
  212. }
  213. }
  214. /* Check IP */
  215. if (token[0] == '[') {
  216. /* Braced IPv6 */
  217. brace = strrchr(token, ']');
  218. if (brace != NULL) {
  219. token++;
  220. *brace = '\0';
  221. if (inet_pton(AF_INET6, token, &addr_buf.ina6) == 1) {
  222. af = AF_INET6;
  223. }
  224. else {
  225. msg_warn_radix("invalid IP address: %s", token);
  226. cur++;
  227. continue;
  228. }
  229. }
  230. else {
  231. msg_warn_radix("invalid IP address: %s", token);
  232. cur++;
  233. continue;
  234. }
  235. }
  236. else {
  237. if (inet_pton(AF_INET, token, &addr_buf.ina) == 1) {
  238. af = AF_INET;
  239. }
  240. else if (inet_pton(AF_INET6, token, &addr_buf.ina6) == 1) {
  241. af = AF_INET6;
  242. }
  243. else {
  244. if (resolve) {
  245. memset(&hints, 0, sizeof(hints));
  246. hints.ai_socktype = SOCK_STREAM; /* Type of the socket */
  247. hints.ai_flags = AI_NUMERICSERV;
  248. hints.ai_family = AF_UNSPEC;
  249. if ((r = getaddrinfo(token, NULL, &hints, &ai_res)) == 0) {
  250. for (cur_ai = ai_res; cur_ai != NULL;
  251. cur_ai = cur_ai->ai_next) {
  252. if (cur_ai->ai_family == AF_INET) {
  253. struct sockaddr_in *sin;
  254. sin = (struct sockaddr_in *) cur_ai->ai_addr;
  255. if (k > 32) {
  256. k = 32;
  257. }
  258. /* Convert to IPv4 mapped IPv6 */
  259. memset(addr_buf.buf, 0, 10);
  260. addr_buf.buf[10] = 0xffu;
  261. addr_buf.buf[11] = 0xffu;
  262. memcpy(addr_buf.buf + 12,
  263. &sin->sin_addr, 4);
  264. k += 96;
  265. radix_insert_compressed(tree,
  266. addr_buf.buf,
  267. sizeof(addr_buf.buf),
  268. 128 - k, (uintptr_t) value);
  269. res++;
  270. }
  271. else if (cur_ai->ai_family == AF_INET6) {
  272. struct sockaddr_in6 *sin6;
  273. sin6 = (struct sockaddr_in6 *) cur_ai->ai_addr;
  274. if (k > 128) {
  275. k = 128;
  276. }
  277. memcpy(addr_buf.buf, &sin6->sin6_addr,
  278. sizeof(sin6->sin6_addr));
  279. radix_insert_compressed(tree,
  280. addr_buf.buf,
  281. sizeof(addr_buf.buf),
  282. 128 - k, (uintptr_t) value);
  283. res++;
  284. }
  285. }
  286. freeaddrinfo(ai_res);
  287. }
  288. else {
  289. msg_warn_radix("getaddrinfo failed for %s: %s", token,
  290. gai_strerror(r));
  291. }
  292. cur++;
  293. continue;
  294. }
  295. else {
  296. msg_warn_radix("invalid IP address: %s", token);
  297. cur++;
  298. continue;
  299. }
  300. }
  301. }
  302. if (af == AF_INET) {
  303. if (k > 32) {
  304. k = 32;
  305. }
  306. /* Move to the last part of the address */
  307. memmove(addr_buf.buf + 12, &addr_buf.ina, 4);
  308. memset(addr_buf.buf, 0, 10);
  309. addr_buf.buf[10] = 0xffu;
  310. addr_buf.buf[11] = 0xffu;
  311. k += 96;
  312. radix_insert_compressed(tree, addr_buf.buf, sizeof(addr_buf.buf),
  313. 128 - k, (uintptr_t) value);
  314. res++;
  315. }
  316. else if (af == AF_INET6) {
  317. if (k > 128) {
  318. k = 128;
  319. }
  320. radix_insert_compressed(tree, addr_buf.buf, sizeof(addr_buf),
  321. 128 - k, (uintptr_t) value);
  322. res++;
  323. }
  324. cur++;
  325. }
  326. g_strfreev(strv);
  327. return res;
  328. }
  329. gboolean
  330. radix_add_generic_iplist(const char *ip_list, radix_compressed_t **tree,
  331. gboolean resolve, const char *tree_name)
  332. {
  333. static const char fill_ptr[] = "1";
  334. if (*tree == NULL) {
  335. *tree = radix_create_compressed(tree_name);
  336. }
  337. return (rspamd_radix_add_iplist(ip_list, ",; ", *tree,
  338. fill_ptr, resolve, tree_name) > 0);
  339. }
  340. gsize radix_get_size(radix_compressed_t *tree)
  341. {
  342. if (tree != NULL) {
  343. return tree->size;
  344. }
  345. return 0;
  346. }
  347. rspamd_mempool_t *
  348. radix_get_pool(radix_compressed_t *tree)
  349. {
  350. if (tree != NULL) {
  351. return tree->pool;
  352. }
  353. return NULL;
  354. }
  355. const char *
  356. radix_get_info(radix_compressed_t *tree)
  357. {
  358. if (tree == NULL) {
  359. return NULL;
  360. }
  361. return btrie_stats(tree->tree, tree->duplicates);
  362. }