aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2009-10-02 20:23:16 +0400
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2009-10-02 20:23:16 +0400
commitce482fbf8e2c1b9bf0cdba0ab61bc26985ee8977 (patch)
treeaa9db8b8c40ce43d504750f03f781652c16ea727 /src
parentf3ad9c6f1e91c9912dbe730fdec350b5fc908672 (diff)
downloadrspamd-ce482fbf8e2c1b9bf0cdba0ab61bc26985ee8977.tar.gz
rspamd-ce482fbf8e2c1b9bf0cdba0ab61bc26985ee8977.zip
* Add reject score setting for hard rejecting of mail (by Anton Nekhoroshin)
* Add rspamc protocol 1.1 (3 marks instead of 2)
Diffstat (limited to 'src')
-rw-r--r--src/cfg_file.h3
-rw-r--r--src/cfg_file.l1
-rw-r--r--src/cfg_file.y18
-rw-r--r--src/cfg_utils.c1
-rw-r--r--src/filter.c12
-rw-r--r--src/filter.h1
-rw-r--r--src/main.h1
-rw-r--r--src/protocol.c61
-rw-r--r--src/protocol.h7
-rw-r--r--src/settings.c34
-rw-r--r--src/settings.h5
11 files changed, 117 insertions, 27 deletions
diff --git a/src/cfg_file.h b/src/cfg_file.h
index 527c3f7c6..afa150a4c 100644
--- a/src/cfg_file.h
+++ b/src/cfg_file.h
@@ -29,6 +29,9 @@
/* 1 worker by default */
#define DEFAULT_WORKERS_NUM 1
+#define DEFAULT_SCORE 10.0
+#define DEFAULT_REJECT_SCORE 999.0
+
#define yyerror parse_err
#define yywarn parse_warn
diff --git a/src/cfg_file.l b/src/cfg_file.l
index 7646c87cf..671146be1 100644
--- a/src/cfg_file.l
+++ b/src/cfg_file.l
@@ -66,6 +66,7 @@ factors return FACTORS;
metric return METRIC;
name return NAME;
required_score return REQUIRED_SCORE;
+reject_score return REJECT_SCORE;
function return FUNCTION;
cache_file return CACHE_FILE;
control return CONTROL;
diff --git a/src/cfg_file.y b/src/cfg_file.y
index 7d9316b60..85dcb9df5 100644
--- a/src/cfg_file.y
+++ b/src/cfg_file.y
@@ -52,7 +52,7 @@ struct rspamd_view *cur_view = NULL;
%token MEMCACHED WORKER TYPE MODULES MODULE_PATH
%token MODULE_OPT PARAM VARIABLE
%token FILTERS FACTORS METRIC NAME CACHE_FILE
-%token REQUIRED_SCORE FUNCTION FRACT COMPOSITES CONTROL PASSWORD
+%token REQUIRED_SCORE REJECT_SCORE FUNCTION FRACT COMPOSITES CONTROL PASSWORD
%token LOGGING LOG_TYPE LOG_TYPE_CONSOLE LOG_TYPE_SYSLOG LOG_TYPE_FILE
%token LOG_LEVEL LOG_LEVEL_DEBUG LOG_LEVEL_INFO LOG_LEVEL_WARNING LOG_LEVEL_ERROR LOG_FACILITY LOG_FILENAME
%token STATFILE ALIAS PATTERN WEIGHT STATFILE_POOL_SIZE SIZE TOKENIZER CLASSIFIER
@@ -365,6 +365,7 @@ metriccmd:
| metricname
| metricfunction
| metricscore
+ | metricrjscore
| metricclassifier
| metriccache
;
@@ -409,6 +410,21 @@ metricscore:
}
;
+metricrjscore:
+ REJECT_SCORE EQSIGN NUMBER {
+ if (cur_metric == NULL) {
+ cur_metric = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
+ }
+ cur_metric->reject_score = $3;
+ }
+ | REQUIRED_SCORE EQSIGN FRACT {
+ if (cur_metric == NULL) {
+ cur_metric = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
+ }
+ cur_metric->reject_score = $3;
+ }
+ ;
+
metricclassifier:
CLASSIFIER EQSIGN QUOTEDSTRING {
if (cur_metric == NULL) {
diff --git a/src/cfg_utils.c b/src/cfg_utils.c
index 9387ca36f..a1113f516 100644
--- a/src/cfg_utils.c
+++ b/src/cfg_utils.c
@@ -520,6 +520,7 @@ post_load_config (struct config_file *cfg)
def_metric->func_name = "factors";
def_metric->func = factor_consolidation_func;
def_metric->required_score = DEFAULT_SCORE;
+ def_metric->reject_score = DEFAULT_REJECT_SCORE;
def_metric->classifier = get_classifier ("winnow");
cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
diff --git a/src/filter.c b/src/filter.c
index 0ffec02d5..efda9fa72 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -234,12 +234,12 @@ static gboolean
check_metric_is_spam (struct worker_task *task, struct metric *metric)
{
struct metric_result *res;
- double ms;
+ double ms, rs;
res = g_hash_table_lookup (task->results, metric->name);
if (res) {
metric_process_callback_forced (metric->name, res, task);
- if (!check_metric_settings (task, metric, &ms)) {
+ if (!check_metric_settings (task, metric, &ms, &rs)) {
ms = metric->required_score;
}
return res->score >= ms;
@@ -572,18 +572,18 @@ insert_metric_header (gpointer metric_name, gpointer metric_value, gpointer data
char header_name[128], outbuf[1000];
GList *symbols = NULL, *cur;
struct metric_result *metric_res = (struct metric_result *)metric_value;
- double ms;
+ double ms, rs;
snprintf (header_name, sizeof (header_name), "X-Spam-%s", metric_res->metric->name);
- if (!check_metric_settings (task, metric_res->metric, &ms)) {
+ if (!check_metric_settings (task, metric_res->metric, &ms, &rs)) {
ms = metric_res->metric->required_score;
}
if (metric_res->score >= ms) {
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "yes; %.2f/%.2f; ", metric_res->score, ms);
+ r += snprintf (outbuf + r, sizeof (outbuf) - r, "yes; %.2f/%.2f/%.2f; ", metric_res->score, ms, rs);
}
else {
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "no; %.2f/%.2f; ", metric_res->score, ms);
+ r += snprintf (outbuf + r, sizeof (outbuf) - r, "no; %.2f/%.2f/%.2f; ", metric_res->score, ms, rs);
}
symbols = g_hash_table_get_keys (metric_res->symbols);
diff --git a/src/filter.h b/src/filter.h
index 6ff3ae1d0..edc9523c6 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -41,6 +41,7 @@ struct metric {
char *func_name; /**< name of consolidation function */
metric_cons_func func; /**< c consolidation function */
double required_score; /**< required score for this metric */
+ double reject_score; /**< reject score for this metric */
struct classifier *classifier; /**< classifier that is used for metric */
struct symbols_cache *cache; /**< symbols cache for metric */
char *cache_filename; /**< filename of cache file */
diff --git a/src/main.h b/src/main.h
index cb922252c..55a62c86c 100644
--- a/src/main.h
+++ b/src/main.h
@@ -169,6 +169,7 @@ struct worker_task {
} state; /**< current session state */
size_t content_length; /**< length of user's input */
enum rspamd_protocol proto; /**< protocol (rspamc or spamc) */
+ const char *proto_ver; /**< protocol version */
enum rspamd_command cmd; /**< command */
struct custom_command *custom_cmd; /**< custom command if any */
int sock; /**< socket descriptor */
diff --git a/src/protocol.c b/src/protocol.c
index a902cb505..6a8f9b8f0 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -195,14 +195,25 @@ parse_command (struct worker_task *task, f_str_t * line)
if (strncasecmp (line->begin, RSPAMC_GREETING, sizeof (RSPAMC_GREETING) - 1) == 0) {
task->proto = RSPAMC_PROTO;
+ task->proto_ver = RSPAMC_PROTO_1_0;
+ if (*(line->begin + sizeof (RSPAMC_GREETING) - 1) == '/') {
+ /* Extract protocol version */
+ token = line->begin + sizeof (RSPAMC_GREETING);
+ if (strncmp (token, RSPAMC_PROTO_1_1, sizeof (RSPAMC_PROTO_1_1) - 1) == 0) {
+ task->proto_ver = RSPAMC_PROTO_1_1;
+ }
+ }
}
else if (strncasecmp (line->begin, SPAMC_GREETING, sizeof (SPAMC_GREETING) - 1) == 0) {
task->proto = SPAMC_PROTO;
+ task->proto_ver = RSPAMC_PROTO_1_1;
}
else {
return -1;
}
+
task->state = READ_HEADER;
+
return 0;
}
@@ -505,25 +516,32 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data
struct metric_result *metric_res = (struct metric_result *)metric_value;
struct metric *m;
int is_spam = 0;
- double ms;
+ double ms = 0, rs = 0;
if (metric_name == NULL || metric_value == NULL) {
m = g_hash_table_lookup (task->cfg->metrics, "default");
- if (!check_metric_settings (task, m, &ms)) {
+ if (!check_metric_settings (task, m, &ms, &rs)) {
ms = m->required_score;
+ rs = m->reject_score;
}
if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "Spam: False ; 0 / %.2f" CRLF, m != NULL ? ms : 0);
+ r = snprintf (outbuf, sizeof (outbuf), "Spam: False ; 0 / %.2f" CRLF, ms);
}
else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f" CRLF, m != NULL ? ms : 0);
+ if (strcmp (task->proto_ver, RSPAMC_PROTO_1_1) == 0) {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f / %.2f" CRLF, ms, rs);
+ }
+ else {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f" CRLF, ms);
+ }
}
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: F: [0/%.2f] [", "default", m != NULL ? ms : 0);
+ cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: F: [0/%.2f/%.2f] [", "default", ms, rs);
}
else {
- if (!check_metric_settings (task, metric_res->metric, &ms)) {
+ if (!check_metric_settings (task, metric_res->metric, &ms, &rs)) {
ms = metric_res->metric->required_score;
+ rs = metric_res->metric->reject_score;
}
if (metric_res->score >= ms) {
is_spam = 1;
@@ -532,9 +550,17 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data
r = snprintf (outbuf, sizeof (outbuf), "Spam: %s ; %.2f / %.2f" CRLF, (is_spam) ? "True" : "False", metric_res->score, ms);
}
else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f" CRLF, (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms);
+ if (strcmp (task->proto_ver, RSPAMC_PROTO_1_1) == 0) {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f / %.2f" CRLF,
+ (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms, rs);
+ }
+ else {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f" CRLF,
+ (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms);
+ }
}
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f] [", (char *)metric_name, is_spam ? "T" : "F", metric_res->score, ms);
+ cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f/%.2f] [",
+ (char *)metric_name, is_spam ? "T" : "F", metric_res->score, ms, rs);
}
if (task->cmd == CMD_PROCESS) {
#ifndef GMIME24
@@ -562,7 +588,8 @@ write_check_reply (struct worker_task *task)
struct metric_result *metric_res;
struct metric_callback_data cd;
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, "OK");
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER,
+ task->proto_ver, "OK");
rspamd_dispatcher_write (task->dispatcher, outbuf, r, TRUE, FALSE);
cd.task = task;
@@ -613,7 +640,9 @@ write_process_reply (struct worker_task *task)
struct metric_result *metric_res;
struct metric_callback_data cd;
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF "Content-Length: %zd" CRLF CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, "OK", task->msg->len);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF "Content-Length: %zd" CRLF CRLF,
+ (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER,
+ task->proto_ver, "OK", task->msg->len);
cd.task = task;
cd.log_buf = logbuf;
@@ -669,11 +698,13 @@ write_reply (struct worker_task *task)
if (task->error_code != 0) {
/* Write error message and error code to reply */
if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "%s %d %s" CRLF CRLF, SPAMD_REPLY_BANNER, task->error_code, SPAMD_ERROR);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s %d %s" CRLF CRLF,
+ SPAMD_REPLY_BANNER, task->proto_ver, task->error_code, SPAMD_ERROR);
msg_debug ("write_reply: writing error: %s", outbuf);
}
else {
- r = snprintf (outbuf, sizeof (outbuf), "%s %d %s" CRLF "%s: %s" CRLF CRLF, RSPAMD_REPLY_BANNER, task->error_code, SPAMD_ERROR, ERROR_HEADER, task->last_error);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s %d %s" CRLF "%s: %s" CRLF CRLF,
+ RSPAMD_REPLY_BANNER, task->proto_ver, task->error_code, SPAMD_ERROR, ERROR_HEADER, task->last_error);
msg_debug ("write_reply: writing error: %s", outbuf);
}
/* Write to bufferevent error message */
@@ -691,11 +722,13 @@ write_reply (struct worker_task *task)
return write_process_reply (task);
break;
case CMD_SKIP:
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, SPAMD_OK);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF,
+ (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, task->proto_ver, SPAMD_OK);
rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
break;
case CMD_PING:
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 PONG" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 PONG" CRLF,
+ (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, task->proto_ver);
rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
break;
case CMD_OTHER:
diff --git a/src/protocol.h b/src/protocol.h
index b4783cb30..4c12cc481 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -13,11 +13,14 @@
#define RSPAMD_PROTOCOL_ERROR 3
#define RSPAMD_LENGTH_ERROR 4
+#define RSPAMC_PROTO_1_0 "1.0"
+#define RSPAMC_PROTO_1_1 "1.1"
+
/*
* Reply messages
*/
-#define RSPAMD_REPLY_BANNER "RSPAMD/1.0"
-#define SPAMD_REPLY_BANNER "SPAMD/1.1"
+#define RSPAMD_REPLY_BANNER "RSPAMD"
+#define SPAMD_REPLY_BANNER "SPAMD"
#define SPAMD_OK "EX_OK"
/* XXX: try to convert rspamd errors to spamd errors */
#define SPAMD_ERROR "EX_ERROR"
diff --git a/src/settings.c b/src/settings.c
index a4dca10dd..bcb40505c 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -50,6 +50,9 @@ settings_free (gpointer data)
if (s->metric_scores) {
g_hash_table_destroy (s->metric_scores);
}
+ if (s->reject_scores) {
+ g_hash_table_destroy (s->reject_scores);
+ }
g_free (s);
}
@@ -149,6 +152,7 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data)
for (i = 0; i < nelts; i++) {
cur_settings = g_malloc (sizeof (struct rspamd_settings));
cur_settings->metric_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ cur_settings->reject_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
cur_settings->factors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
cur_settings->statfile_alias = NULL;
cur_settings->want_spam = FALSE;
@@ -198,6 +202,21 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data)
json_it = json_object_iter_next (cur_nm, json_it);
}
}
+ /* Rejects object */
+ cur_nm = json_object_get (cur_elt, "rejects");
+ if (cur_nm != NULL && json_is_object (cur_nm)) {
+ json_it = json_object_iter (cur_nm);
+ while (json_it) {
+ it_val = json_object_iter_value (json_it);
+ if (it_val && json_is_number (it_val)) {
+ score = g_malloc (sizeof (double));
+ *score = json_number_value (it_val);
+ g_hash_table_insert (cur_settings->reject_scores, g_strdup (json_object_iter_key (json_it)),
+ score);
+ }
+ json_it = json_object_iter_next(cur_nm, json_it);
+ }
+ }
/* Want spam */
cur_nm = json_object_get (cur_elt, "want_spam");
if (cur_nm != NULL) {
@@ -284,25 +303,36 @@ check_setting (struct worker_task *task, struct rspamd_settings **user_settings,
}
gboolean
-check_metric_settings (struct worker_task * task, struct metric * metric, double *score)
+check_metric_settings (struct worker_task * task, struct metric * metric, double *score, double *rscore)
{
struct rspamd_settings *us, *ds;
- double *sc;
+ double *sc, *rs;
+
+ *rscore = DEFAULT_REJECT_SCORE;
if (check_setting (task, &us, &ds)) {
if (us != NULL) {
/* First search in user's settings */
+ if ((rs = g_hash_table_lookup (us->reject_scores, metric->name)) != NULL) {
+ *rscore = *rs;
+ }
if ((sc = g_hash_table_lookup (us->metric_scores, metric->name)) != NULL) {
*score = *sc;
return TRUE;
}
/* Now check in domain settings */
+ if (ds && ((rs = g_hash_table_lookup (ds->reject_scores, metric->name)) != NULL)) {
+ *rscore = *rs;
+ }
if (ds && (sc = g_hash_table_lookup (ds->metric_scores, metric->name)) != NULL) {
*score = *sc;
return TRUE;
}
}
else if (ds != NULL) {
+ if ((rs = g_hash_table_lookup (ds->reject_scores, metric->name)) != NULL) {
+ *rscore = *rs;
+ }
if ((sc = g_hash_table_lookup (ds->metric_scores, metric->name)) != NULL) {
*score = *sc;
return TRUE;
diff --git a/src/settings.h b/src/settings.h
index 696bd9d76..58e877570 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -5,7 +5,8 @@
#include "main.h"
struct rspamd_settings {
- GHashTable *metric_scores; /**< hash table of metric scores for this setting */
+ GHashTable *metric_scores; /**< hash table of metric require scores for this setting */
+ GHashTable *reject_scores; /**< hash table of metric reject scores for this setting */
GHashTable *factors; /**< hash table of new factors for this setting */
char *statfile_alias; /**< alias for statfile used */
gboolean want_spam; /**< if true disable rspamd checks */
@@ -14,7 +15,7 @@ struct rspamd_settings {
gboolean read_settings (const char *path, struct config_file *cfg, GHashTable *table);
void init_settings (struct config_file *cfg);
-gboolean check_metric_settings (struct worker_task *task, struct metric *metric, double *score);
+gboolean check_metric_settings (struct worker_task *task, struct metric *metric, double *score, double *rscore);
gboolean check_factor_settings (struct worker_task *task, const char *symbol, double *factor);
gboolean check_want_spam (struct worker_task *task);