mirror of https://github.com/rspamd/rspamd.git
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
212 lines
5.9 KiB
/* Copyright (c) 2010-2012, Vsevolod Stakhov
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
#include "main.h"
|
|
#include "roll_history.h"
|
|
|
|
|
|
/**
|
|
* Returns new roll history
|
|
* @param pool pool for shared memory
|
|
* @return new structure
|
|
*/
|
|
struct roll_history*
|
|
rspamd_roll_history_new (rspamd_mempool_t *pool)
|
|
{
|
|
struct roll_history *new;
|
|
|
|
if (pool == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
new = rspamd_mempool_alloc0_shared (pool, sizeof (struct roll_history));
|
|
new->pool = pool;
|
|
new->mtx = rspamd_mempool_get_mutex (pool);
|
|
|
|
return new;
|
|
}
|
|
|
|
struct history_metric_callback_data {
|
|
gchar *pos;
|
|
gint remain;
|
|
};
|
|
|
|
static void
|
|
roll_history_symbols_callback (gpointer key, gpointer value, void *user_data)
|
|
{
|
|
struct history_metric_callback_data *cb = user_data;
|
|
struct symbol *s = value;
|
|
guint wr;
|
|
|
|
if (cb->remain > 0) {
|
|
wr = rspamd_snprintf (cb->pos, cb->remain, "%s, ", s->name);
|
|
cb->pos += wr;
|
|
cb->remain -= wr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update roll history with data from task
|
|
* @param history roll history object
|
|
* @param task task object
|
|
*/
|
|
void
|
|
rspamd_roll_history_update (struct roll_history *history, struct rspamd_task *task)
|
|
{
|
|
gint row_num;
|
|
struct roll_history_row *row;
|
|
struct metric_result *metric_res;
|
|
struct history_metric_callback_data cbdata;
|
|
|
|
if (history->need_lock) {
|
|
/* Some process is getting history, so wait on a mutex */
|
|
rspamd_mempool_lock_mutex (history->mtx);
|
|
history->need_lock = FALSE;
|
|
rspamd_mempool_unlock_mutex (history->mtx);
|
|
}
|
|
|
|
/* First of all obtain check and obtain row number */
|
|
g_atomic_int_compare_and_exchange (&history->cur_row, HISTORY_MAX_ROWS, 0);
|
|
#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
|
|
row_num = g_atomic_int_add (&history->cur_row, 1);
|
|
#else
|
|
row_num = g_atomic_int_exchange_and_add (&history->cur_row, 1);
|
|
#endif
|
|
|
|
if (row_num < HISTORY_MAX_ROWS) {
|
|
row = &history->rows[row_num];
|
|
row->completed = FALSE;
|
|
}
|
|
else {
|
|
/* Race condition */
|
|
history->cur_row = 0;
|
|
return;
|
|
}
|
|
|
|
/* Add information from task to roll history */
|
|
memcpy (&row->from_addr, &task->from_addr, sizeof (row->from_addr));
|
|
memcpy (&row->tv, &task->tv, sizeof (row->tv));
|
|
|
|
/* Strings */
|
|
rspamd_strlcpy (row->message_id, task->message_id, sizeof (row->message_id));
|
|
if (task->user) {
|
|
rspamd_strlcpy (row->user, task->user, sizeof (row->message_id));
|
|
}
|
|
else {
|
|
row->user[0] = '\0';
|
|
}
|
|
|
|
/* Get default metric */
|
|
metric_res = g_hash_table_lookup (task->results, DEFAULT_METRIC);
|
|
if (metric_res == NULL) {
|
|
row->symbols[0] = '\0';
|
|
row->action = METRIC_ACTION_NOACTION;
|
|
}
|
|
else {
|
|
row->score = metric_res->score;
|
|
row->required_score = metric_res->metric->actions[METRIC_ACTION_REJECT].score;
|
|
row->action = check_metric_action (metric_res->score,
|
|
metric_res->metric->actions[METRIC_ACTION_REJECT].score, metric_res->metric);
|
|
cbdata.pos = row->symbols;
|
|
cbdata.remain = sizeof (row->symbols);
|
|
g_hash_table_foreach (metric_res->symbols, roll_history_symbols_callback, &cbdata);
|
|
if (cbdata.remain > 0) {
|
|
/* Remove last whitespace and comma */
|
|
*cbdata.pos-- = '\0';
|
|
*cbdata.pos-- = '\0';
|
|
*cbdata.pos = '\0';
|
|
}
|
|
}
|
|
|
|
row->scan_time = task->scan_milliseconds;
|
|
row->len = (task->msg == NULL ? 0 : task->msg->len);
|
|
row->completed = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Load previously saved history from file
|
|
* @param history roll history object
|
|
* @param filename filename to load from
|
|
* @return TRUE if history has been loaded
|
|
*/
|
|
gboolean
|
|
rspamd_roll_history_load (struct roll_history *history, const gchar *filename)
|
|
{
|
|
gint fd;
|
|
struct stat st;
|
|
|
|
if (stat (filename, &st) == -1) {
|
|
msg_info ("cannot load history from %s: %s", filename, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
if (st.st_size != sizeof (history->rows)) {
|
|
msg_info ("cannot load history from %s: size mismatch", filename);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((fd = open (filename, O_RDONLY)) == -1) {
|
|
msg_info ("cannot load history from %s: %s", filename, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
if (read (fd, history->rows, sizeof (history->rows)) == -1) {
|
|
close (fd);
|
|
msg_info ("cannot read history from %s: %s", filename, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
close (fd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Save history to file
|
|
* @param history roll history object
|
|
* @param filename filename to load from
|
|
* @return TRUE if history has been saved
|
|
*/
|
|
gboolean
|
|
rspamd_roll_history_save (struct roll_history *history, const gchar *filename)
|
|
{
|
|
gint fd;
|
|
|
|
if ((fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 00600)) == -1) {
|
|
msg_info ("cannot save history to %s: %s", filename, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
if (write (fd, history->rows, sizeof (history->rows)) == -1) {
|
|
close (fd);
|
|
msg_info ("cannot write history to %s: %s", filename, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
close (fd);
|
|
|
|
return TRUE;
|
|
}
|