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 8.7KB

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