Rapid spam filtering system https://rspamd.com/
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.

212 lines
5.9 KiB

  1. /* Copyright (c) 2010-2012, Vsevolod Stakhov
  2. * All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above copyright
  9. * notice, this list of conditions and the following disclaimer in the
  10. * documentation and/or other materials provided with the distribution.
  11. *
  12. * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  13. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  14. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  15. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  16. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  18. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  20. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. #include "config.h"
  24. #include "main.h"
  25. #include "roll_history.h"
  26. /**
  27. * Returns new roll history
  28. * @param pool pool for shared memory
  29. * @return new structure
  30. */
  31. struct roll_history*
  32. rspamd_roll_history_new (rspamd_mempool_t *pool)
  33. {
  34. struct roll_history *new;
  35. if (pool == NULL) {
  36. return NULL;
  37. }
  38. new = rspamd_mempool_alloc0_shared (pool, sizeof (struct roll_history));
  39. new->pool = pool;
  40. new->mtx = rspamd_mempool_get_mutex (pool);
  41. return new;
  42. }
  43. struct history_metric_callback_data {
  44. gchar *pos;
  45. gint remain;
  46. };
  47. static void
  48. roll_history_symbols_callback (gpointer key, gpointer value, void *user_data)
  49. {
  50. struct history_metric_callback_data *cb = user_data;
  51. struct symbol *s = value;
  52. guint wr;
  53. if (cb->remain > 0) {
  54. wr = rspamd_snprintf (cb->pos, cb->remain, "%s, ", s->name);
  55. cb->pos += wr;
  56. cb->remain -= wr;
  57. }
  58. }
  59. /**
  60. * Update roll history with data from task
  61. * @param history roll history object
  62. * @param task task object
  63. */
  64. void
  65. rspamd_roll_history_update (struct roll_history *history, struct rspamd_task *task)
  66. {
  67. gint row_num;
  68. struct roll_history_row *row;
  69. struct metric_result *metric_res;
  70. struct history_metric_callback_data cbdata;
  71. if (history->need_lock) {
  72. /* Some process is getting history, so wait on a mutex */
  73. rspamd_mempool_lock_mutex (history->mtx);
  74. history->need_lock = FALSE;
  75. rspamd_mempool_unlock_mutex (history->mtx);
  76. }
  77. /* First of all obtain check and obtain row number */
  78. g_atomic_int_compare_and_exchange (&history->cur_row, HISTORY_MAX_ROWS, 0);
  79. #if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
  80. row_num = g_atomic_int_add (&history->cur_row, 1);
  81. #else
  82. row_num = g_atomic_int_exchange_and_add (&history->cur_row, 1);
  83. #endif
  84. if (row_num < HISTORY_MAX_ROWS) {
  85. row = &history->rows[row_num];
  86. row->completed = FALSE;
  87. }
  88. else {
  89. /* Race condition */
  90. history->cur_row = 0;
  91. return;
  92. }
  93. /* Add information from task to roll history */
  94. memcpy (&row->from_addr, &task->from_addr, sizeof (row->from_addr));
  95. memcpy (&row->tv, &task->tv, sizeof (row->tv));
  96. /* Strings */
  97. rspamd_strlcpy (row->message_id, task->message_id, sizeof (row->message_id));
  98. if (task->user) {
  99. rspamd_strlcpy (row->user, task->user, sizeof (row->message_id));
  100. }
  101. else {
  102. row->user[0] = '\0';
  103. }
  104. /* Get default metric */
  105. metric_res = g_hash_table_lookup (task->results, DEFAULT_METRIC);
  106. if (metric_res == NULL) {
  107. row->symbols[0] = '\0';
  108. row->action = METRIC_ACTION_NOACTION;
  109. }
  110. else {
  111. row->score = metric_res->score;
  112. row->required_score = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
  113. row->action = check_metric_action (metric_res->score,
  114. metric_res->metric->actions[METRIC_ACTION_REJECT].score, metric_res->metric);
  115. cbdata.pos = row->symbols;
  116. cbdata.remain = sizeof (row->symbols);
  117. g_hash_table_foreach (metric_res->symbols, roll_history_symbols_callback, &cbdata);
  118. if (cbdata.remain > 0) {
  119. /* Remove last whitespace and comma */
  120. *cbdata.pos-- = '\0';
  121. *cbdata.pos-- = '\0';
  122. *cbdata.pos = '\0';
  123. }
  124. }
  125. row->scan_time = task->scan_milliseconds;
  126. row->len = (task->msg == NULL ? 0 : task->msg->len);
  127. row->completed = TRUE;
  128. }
  129. /**
  130. * Load previously saved history from file
  131. * @param history roll history object
  132. * @param filename filename to load from
  133. * @return TRUE if history has been loaded
  134. */
  135. gboolean
  136. rspamd_roll_history_load (struct roll_history *history, const gchar *filename)
  137. {
  138. gint fd;
  139. struct stat st;
  140. if (stat (filename, &st) == -1) {
  141. msg_info ("cannot load history from %s: %s", filename, strerror (errno));
  142. return FALSE;
  143. }
  144. if (st.st_size != sizeof (history->rows)) {
  145. msg_info ("cannot load history from %s: size mismatch", filename);
  146. return FALSE;
  147. }
  148. if ((fd = open (filename, O_RDONLY)) == -1) {
  149. msg_info ("cannot load history from %s: %s", filename, strerror (errno));
  150. return FALSE;
  151. }
  152. if (read (fd, history->rows, sizeof (history->rows)) == -1) {
  153. close (fd);
  154. msg_info ("cannot read history from %s: %s", filename, strerror (errno));
  155. return FALSE;
  156. }
  157. close (fd);
  158. return TRUE;
  159. }
  160. /**
  161. * Save history to file
  162. * @param history roll history object
  163. * @param filename filename to load from
  164. * @return TRUE if history has been saved
  165. */
  166. gboolean
  167. rspamd_roll_history_save (struct roll_history *history, const gchar *filename)
  168. {
  169. gint fd;
  170. if ((fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 00600)) == -1) {
  171. msg_info ("cannot save history to %s: %s", filename, strerror (errno));
  172. return FALSE;
  173. }
  174. if (write (fd, history->rows, sizeof (history->rows)) == -1) {
  175. close (fd);
  176. msg_info ("cannot write history to %s: %s", filename, strerror (errno));
  177. return FALSE;
  178. }
  179. close (fd);
  180. return TRUE;
  181. }