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.3KB

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