SET(PLUGINSSRC src/plugins/surbl.c
src/plugins/regexp.c
src/plugins/chartable.c
- src/plugins/emails.c
src/plugins/fuzzy_check.c
src/plugins/spf.c)
local msgid_dollars_ok = 'Message-Id=/[0-9a-f]{4,}\\$[0-9a-f]{4,}\\$[0-9a-f]{4,}\\@\\S+/Hr'
local mimeole_ms = 'X-MimeOLE=/^Produced By Microsoft MimeOLE/H'
local rcvd_with_exchange = 'Received=/with Microsoft Exchange Server/H'
-reconf['R_MUA_EXCHANGE'] = 'X-MimeOLE=/Microsoft Exchange/H'
reconf['RATWARE_MS_HASH'] = string.format('(%s) & !(%s) & !(%s)', msgid_dollars_ok, mimeole_ms, rcvd_with_exchange)
-- Reply-type in content-type
<action>reject</action>
<action>greylist:5</action>
<action>add_header:5</action>
- <symbol weight="8.00">R_SPAM_FROM_MTU</symbol>
- <symbol weight="10.00">R_WWW_EKONF_COM</symbol>
- <symbol weight="2.00">R_TINYURL</symbol>
<symbol weight="2.00">MISSING_SUBJECT</symbol>
<symbol weight="2.10">FORGED_OUTLOOK_TAGS</symbol>
- <symbol weight="8.00">R_FAKE_THEBAT</symbol>
<symbol weight="5.00">FORGED_SENDER</symbol>
<symbol weight="2.00">DRUGS_MANYKINDS</symbol>
<symbol weight="3.30">ADVANCE_FEE_2</symbol>
<symbol weight="2.00">RCVD_DOUBLE_IP_SPAM</symbol>
<symbol weight="5.50">OB_SURBL_MULTI</symbol>
<symbol weight="5.00">FORGED_OUTLOOK_HTML</symbol>
- <symbol weight="2.00">HTML_MIME_NO_HTML_TAG</symbol>
- <symbol weight="10.50">R_BAD_EMAIL</symbol>
- <symbol weight="10.00">R_SPAM_FROM_LIBERO</symbol>
<symbol weight="-2.00">WHITELIST_IP</symbol>
<symbol weight="5.00">R_UNDISC_RCPT</symbol>
<symbol weight="2.00">DRUGS_ANXIETY</symbol>
+ <symbol weight="2.00">DRUGS_MUSCLE</symbol>
<symbol weight="2.00">DRUGS_ANXIETY_EREC</symbol>
<symbol weight="5.50">PH_SURBL_MULTI</symbol>
<symbol weight="9.00">R_WHITE_ON_WHITE</symbol>
- <symbol weight="1.00">FAKE_HTML</symbol>
- <symbol weight="10.00">R_SPAM_FROM_VERSATEL</symbol>
<symbol weight="3.00">HTML_SHORT_LINK_IMG_2</symbol>
<symbol weight="3.00">FORGED_MUA_OUTLOOK</symbol>
- <symbol weight="4.00">R_FREE_HOSTING</symbol>
<symbol weight="2.00">DRUGS_ERECTILE</symbol>
- <symbol weight="3.00">R_FREE_HOSTING_NAROD</symbol>
- <symbol weight="10.00">R_SPAM_FROM_ONO</symbol>
<symbol weight="2.00">FM_FAKE_HELO_VERIZON</symbol>
<symbol weight="2.00">REPTO_QUOTE_YAHOO</symbol>
<symbol weight="5.00">MISSING_MIMEOLE</symbol>
- <symbol weight="0.50">RAMBLER_URIBL</symbol>
+ <symbol weight="9.50">RAMBLER_URIBL</symbol>
+ <symbol weight="2.00">MISSING_TO</symbol>
+ <symbol weight="0.33">FROM_EXCESS_BASE64</symbol>
+ <symbol weight="-5.00">FROM_WORLDBANK</symbol>
+ <symbol weight="-5.00">FROM_CBR</symbol>
+ <symbol weight="-5.00">FROM_CSHOP</symbol>
+ <symbol weight="-5.00">FROM_MIRHOSTING</symbol>
+ <symbol weight="-5.00">FROM_PASSIFLORA</symbol>
<symbol weight="10.00">R_SPAM_FROM_VALUEHOST</symbol>
<symbol weight="5.00">R_MIXED_CHARSET</symbol>
<symbol weight="3.50">SORTED_RECIPS</symbol>
<symbol weight="3.00">R_TO_SEEMS_AUTO</symbol>
<symbol weight="1.00">SUBJECT_NEEDS_ENCODING</symbol>
<symbol weight="3.84">TRACKER_ID</symbol>
- <symbol weight="7.00">KAM_LOTTO1</symbol>
+ <symbol weight="8.00">R_LOTTO</symbol>
<symbol weight="3.00">R_NO_SPACE_IN_FROM</symbol>
<symbol weight="8.00">R_SAJDING</symbol>
<symbol weight="6.00">R_BAD_CTE_7BIT</symbol>
<symbol weight="5.50">WS_SURBL_MULTI</symbol>
- <symbol weight="10.00">R_POCHTA_RU</symbol>
<symbol weight="10.00">R_FLASH_REDIR_IMGSHACK</symbol>
<symbol weight="5.00">INVALID_MSGID</symbol>
- <symbol weight="8.00">R_FORGED_MPOP_WEBMAIL</symbol>
<symbol weight="3.00">MISSING_MID</symbol>
<symbol weight="2.00">DRUGS_DIET</symbol>
<symbol weight="3.00">FORGED_RECIPIENTS</symbol>
<symbol weight="2.00">RATWARE_MS_HASH</symbol>
- <symbol weight="5.00">HTML_TAG_BALANCE_HEAD</symbol>
<symbol weight="1.00">STOX_REPLY_TYPE</symbol>
<symbol weight="3.00">BAYES_SPAM</symbol>
<symbol weight="-3.00">BAYES_HAM</symbol>
+ <symbol weight="1.00">R_FUZZY</symbol>
+ <symbol weight="1.00">R_FUZZY1</symbol>
+ <symbol weight="1.00">R_FUZZY2</symbol>
+ <symbol weight="1.00">R_FUZZY3</symbol>
+
+ <symbol weight="3.00">R_SPF_FAIL</symbol>
+ <symbol weight="1.00">R_SPF_SOFTFAIL</symbol>
+ <symbol weight="-3.00">R_SPF_ALLOW</symbol>
+
+ <symbol weight="-2.00">MAILLIST</symbol>
+
+ <symbol weight="3.00">R_IP_PBL</symbol>
+
+
+ <symbol weight="1.00">ONCE_RECEIVED</symbol>
+ <symbol weight="4.00">ONCE_RECEIVED_STRICT</symbol>
+
+ <symbol weight="1.00">RECEIVED_RBL</symbol>
+
+ <symbol weight="3.00">R_PARTS_DIFFER</symbol>
<symbol weight="2.00">MIME_HEADER_CTYPE_ONLY</symbol>
- <symbol weight="8.00">R_FAKE_OUTLOOK</symbol>
</metric>
<!-- End of factors section -->
<maxfiles>2048</maxfiles>
<maxcore>0</maxcore>
<!-- Other params -->
- <param name="hashfile">/tmp/fuzzy.db</param>
- <param name="use_judy">yes</param>
+ <hashfile>/tmp/fuzzy.db</hashfile>
+ <use_judy>yes</use_judy>
</worker>
<worker>
<type>controller</type>
<maxfiles>2048</maxfiles>
<maxcore>0</maxcore>
<!-- Other params -->
- <param name="password">q1</param>
+ <password>q1</password>
</worker>
<worker>
<type>normal</type>
<!-- Modules section -->
<!-- fuzzy_check -->
<module name="fuzzy_check">
- <option name="servers">localhost:11335</option>
- <option name="symbol">R_FUZZY</option>
- <option name="min_length">300</option>
- <option name="max_score">10</option>
- <option name="mime_types">application/pdf</option>
- <option name="metric">default</option>
- <option name="fuzzy_map">1:R_FUZZY1:10,2:R_FUZZY2:5,3:R_FUZZY3:-2.1</option>
+ <servers>localhost:11335</servers>
+ <symbol>R_FUZZY</symbol>
+ <min_bytes>300</min_bytes>
+ <max_score>10</max_score>
+ <mime_types>application/pdf</mime_types>
+ <fuzzy_map>1:R_FUZZY1:10,2:R_FUZZY2:5,3:R_FUZZY3:-2.1</fuzzy_map>
</module>
<!-- forged_recipients -->
<module name="forged_recipients">
- <option name="symbol_sender">FORGED_SENDER</option>
- <option name="symbol_rcpt">FORGED_RECIPIENTS</option>
+ <symbol_sender>FORGED_SENDER</symbol_sender>
+ <symbol_rcpt>FORGED_RECIPIENTS</symbol_rcpt>
</module>
<!-- maillist -->
<module name="maillist">
- <option name="symbol">MAILLIST</option>
+ <symbol>MAILLIST</symbol>
</module>
<!-- surbl -->
<module name="surbl">
- <option name="whitelist">file://@ETC_PREFIX@/rspamd/surbl-whitelist.inc</option>
- <option name="2tld">file://@ETC_PREFIX@/rspamd/2tld.inc</option>
- <option name="metric">default</option>
- <option name="bit_64">JP</option>
- <option name="bit_32">AB</option>
- <option name="bit_16">OB</option>
- <option name="bit_8">PH</option>
- <option name="bit_4">WS</option>
- <option name="bit_2">SC</option>
- <option name="suffix_RAMBLER_URIBL">uribl.rambler.ru</option>
+ <whitelist>file://@ETC_PREFIX@/rspamd/surbl-whitelist.inc</whitelist>
+ <exceptions>file://@ETC_PREFIX@/rspamd/2tld.inc</exceptions>
+ <bit_64>JP</bit_64>
+ <bit_32>AB</bit_32>
+ <bit_16>OB</bit_16>
+ <bit_8>PH</bit_8>
+ <bit_4>WS</bit_4>
+ <bit_2>SC</bit_2>
+ <suffix_RAMBLER_URIBL>uribl.rambler.ru</suffix_RAMBLER_URIBL>
<option name="suffix_%b_SURBL_MULTI">multi.surbl.org</option>
- <option name="redirector_read_timeout">10s</option>
- <option name="redirector_connect_timeout">1s</option>
- <option name="redirector">localhost:8080</option>
+ <redirector_read_timeout>10s</redirector_read_timeout>
+ <redirector_connect_timeout>1s</redirector_connect_timeout>
+ <redirector>localhost:8080</redirector>
</module>
<!-- received_rbl -->
<module name="received_rbl">
- <option name="symbol">RECEIVED_RBL</option>
- <option name="rbl">pbl.spamhaus.org</option>
- <option name="rbl">xbl.spamhaus.org</option>
- <option name="rbl">insecure-bl.rambler.ru</option>
+ <symbol>RECEIVED_RBL</symbol>
+ <rbl>pbl.spamhaus.org</rbl>
+ <rbl>xbl.spamhaus.org</rbl>
+ <rbl>insecure-bl.rambler.ru</rbl>
</module>
<!-- whitelist -->
<module name="whitelist">
- <option name="ip_whitelist">http://cebka.pp.ru/stuff/grey_whitelist.conf</option>
- <option name="symbol_ip">WHITELIST_IP</option>
+ <ip_whitelist>http://cebka.pp.ru/stuff/grey_whitelist.conf</ip_whitelist>
+ <symbol_ip>WHITELIST_IP</symbol_ip>
</module>
<!-- chartable -->
<module name="chartable">
- <option name="threshold">0.1</option>
- <option name="symbol">R_MIXED_CHARSET</option>
- <option name="metric">default</option>
+ <threshold>0.1</threshold>
+ <symbol>R_MIXED_CHARSET</symbol>
</module>
<!-- once_received -->
<module name="once_received">
- <option name="good_host">mail</option>
- <option name="bad_host">static</option>
- <option name="bad_host">dynamic</option>
- <option name="symbol_strict">ONCE_RECEIVED_STRICT</option>
- <option name="symbol">ONCE_RECEIVED</option>
+ <good_host>mail</good_host>
+ <bad_host>static</bad_host>
+ <bad_host>dynamic</bad_host>
+ <symbol_strict>ONCE_RECEIVED_STRICT</symbol_strict>
+ <symbol>ONCE_RECEIVED</symbol>
</module>
<!-- multimap -->
<module name="multimap">
<!--
- <option name="rule">type = header, header = To, pattern = @(.+)>?$, map = file://@ETC_PREFIX@/rspamd/rcpt_test, symbol = R_RCPT_WHITELIST</option>
- <option name="rule">type = ip, map = file://@ETC_PREFIX@/rspamd/ip_test, symbol = R_IP_WHITELIST</option>
+ <rule>type = header, header = To, pattern = @(.+)>?$, map = file://@ETC_PREFIX@/rspamd/rcpt_test, symbol = R_RCPT_WHITELIST</rule>
+ <rule>type = ip, map = file://@ETC_PREFIX@/rspamd/ip_test, symbol = R_IP_WHITELIST</rule>
-->
- <option name="rule">type = dnsbl, map = pbl.spamhaus.org, symbol = R_IP_PBL</option>
+ <rule>type = dnsbl, map = pbl.spamhaus.org, symbol = R_IP_PBL</rule>
</module>
<classifier type="winnow">
<tokenizer>osb-text</tokenizer>
<metric>default</metric>
- <option name="min_tokens">20</option>
+ <min_tokens>20</min_tokens>
<statfile>
<symbol>WINNOW_HAM</symbol>
<size>100M</size>
<classifier type="bayes">
<tokenizer>osb-text</tokenizer>
<metric>default</metric>
- <option name="min_tokens">10</option>
- <option name="learn_threshold">0.2</option>
+ <min_tokens>10</min_tokens>
+ <learn_threshold>0.2</learn_threshold>
<statfile>
<symbol>BAYES_HAM</symbol>
<size>10M</size>
<classifier type="bayes">
<tokenizer>osb-text</tokenizer>
<metric>default</metric>
- <option name="min_tokens">10</option>
- <option name="learn_threshold">0.2</option>
+ <min_tokens>10</min_tokens>
<statfile>
<symbol>BAYES_HAM</symbol>
<size>10M</size>
gboolean parse_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line);
gboolean read_xml_config (struct config_file *cfg, const gchar *filename);
gboolean check_modules_config (struct config_file *cfg);
+void insert_classifier_symbols (struct config_file *cfg);
#endif /* ifdef CFG_FILE_H */
/*
return res;
}
+static void
+symbols_classifiers_callback (gpointer key, gpointer value, gpointer ud)
+{
+ struct config_file *cfg = ud;
+
+ register_virtual_symbol (&cfg->cache, key, 1.0);
+}
+
+void
+insert_classifier_symbols (struct config_file *cfg)
+{
+ g_hash_table_foreach (cfg->classifiers_symbols, symbols_classifiers_callback, cfg);
+}
+
/*
* vi:ts=4
*/
gchar *val;
if (g_ascii_strcasecmp (data, "file") == 0) {
/* Find filename attribute */
- if ((val = g_hash_table_lookup (attrs, "filename")) == NULL) {
+ if (attrs == NULL || (val = g_hash_table_lookup (attrs, "filename")) == NULL) {
msg_err ("cannot log to file that is not specified");
return FALSE;
}
cfg->log_type = RSPAMD_LOG_CONSOLE;
}
else if (g_ascii_strcasecmp (data, "syslog") == 0) {
- if ((val = g_hash_table_lookup (attrs, "facility")) == NULL) {
+ if (attrs == NULL || (val = g_hash_table_lookup (attrs, "facility")) == NULL) {
msg_err ("cannot log to syslog when facility is not specified");
return FALSE;
}
GHashTable *worker_config;
if (g_ascii_strcasecmp (tag, "option") == 0 || g_ascii_strcasecmp (tag, "param") == 0) {
- if ((name = g_hash_table_lookup (attrs, "name")) == NULL) {
+ if (attrs == NULL || (name = g_hash_table_lookup (attrs, "name")) == NULL) {
msg_err ("worker param tag must have \"name\" attribute");
return FALSE;
}
}
else {
- name = tag;
+ name = memory_pool_strdup (cfg->cfg_pool, tag);
}
if (!worker_options ||
struct metric *metric = ctx->section_pointer;
value = memory_pool_alloc (cfg->cfg_pool, sizeof (double));
- if ((strval = g_hash_table_lookup (attrs, "weight")) == NULL) {
+ if (attrs == NULL || (strval = g_hash_table_lookup (attrs, "weight")) == NULL) {
msg_info ("symbol tag should have \"weight\" attribute, assume weight 1.0");
*value = 1.0;
}
}
}
else {
- name = tag;
+ name = memory_pool_strdup (cfg->cfg_pool, tag);
}
/* Check for lua */
- if ((val = g_hash_table_lookup (attrs, "lua")) != NULL) {
+ if (attrs != NULL && (val = g_hash_table_lookup (attrs, "lua")) != NULL) {
if (g_ascii_strcasecmp (val, "yes") == 0) {
is_lua = TRUE;
}
/* Now config table can be used for configuring rspamd */
}
/* First check "src" attribute */
- if ((val = g_hash_table_lookup (attrs, "src")) != NULL) {
+ if (attrs != NULL && (val = g_hash_table_lookup (attrs, "src")) != NULL) {
/* Chdir */
tmp1 = g_strdup (val);
tmp2 = g_strdup (val);
{
gchar *val;
- if ((val = g_hash_table_lookup (attrs, "name")) == NULL) {
+ if (attrs == NULL || (val = g_hash_table_lookup (attrs, "name")) == NULL) {
msg_err ("'name' attribute is required for tag 'variable'");
return FALSE;
}
gchar *val;
struct expression *expr;
- if ((val = g_hash_table_lookup (attrs, "name")) == NULL) {
+ if (attrs == NULL || (val = g_hash_table_lookup (attrs, "name")) == NULL) {
msg_err ("'name' attribute is required for tag 'composite'");
return FALSE;
}
GHashTable *classifier_config;
if (g_ascii_strcasecmp (tag, "option") == 0 || g_ascii_strcasecmp (tag, "param") == 0) {
- if ((name = g_hash_table_lookup (attrs, "name")) == NULL) {
+ if (attrs == NULL || (name = g_hash_table_lookup (attrs, "name")) == NULL) {
msg_err ("worker param tag must have \"name\" attribute");
return FALSE;
}
}
else {
- name = tag;
+ name = memory_pool_strdup (cfg->cfg_pool, tag);
}
if (!classifier_options ||
g_queue_push_head (ud->if_stack, GSIZE_TO_POINTER ((gsize)ud->state));
/* Now get attributes */
ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values);
- if ((condition = g_hash_table_lookup (ud->cur_attrs, "condition")) == NULL) {
+ if (ud->cur_attrs == NULL || (condition = g_hash_table_lookup (ud->cur_attrs, "condition")) == NULL) {
msg_err ("unknown condition attribute for if tag");
*error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'condition' is required for tag 'if'");
ud->state = XML_ERROR;
LUA_FUNCTION_DEF (config, add_hash_map);
LUA_FUNCTION_DEF (config, get_classifier);
LUA_FUNCTION_DEF (config, register_symbol);
+LUA_FUNCTION_DEF (config, register_virtual_symbol);
+LUA_FUNCTION_DEF (config, register_callback_symbol);
LUA_FUNCTION_DEF (config, register_post_filter);
LUA_FUNCTION_DEF (config, register_module_option);
LUA_INTERFACE_DEF (config, add_hash_map),
LUA_INTERFACE_DEF (config, get_classifier),
LUA_INTERFACE_DEF (config, register_symbol),
+ LUA_INTERFACE_DEF (config, register_virtual_symbol),
+ LUA_INTERFACE_DEF (config, register_callback_symbol),
LUA_INTERFACE_DEF (config, register_module_option),
LUA_INTERFACE_DEF (config, register_post_filter),
{"__tostring", lua_class_tostring},
struct lua_callback_data *cd;
if (cfg) {
- name = g_strdup (luaL_checkstring (L, 2));
+ name = memory_pool_strdup (cfg->cfg_pool, luaL_checkstring (L, 2));
weight = luaL_checknumber (L, 3);
callback = luaL_checkstring (L, 4);
if (name) {
return 1;
}
+static gint
+lua_config_register_virtual_symbol (lua_State * L)
+{
+ struct config_file *cfg = lua_check_config (L);
+ const gchar *name;
+ double weight;
+
+ if (cfg) {
+ name = memory_pool_strdup (cfg->cfg_pool, luaL_checkstring (L, 2));
+ weight = luaL_checknumber (L, 3);
+ if (name) {
+ register_virtual_symbol (&cfg->cache, name, weight);
+ }
+ }
+ return 1;
+}
+
+static gint
+lua_config_register_callback_symbol (lua_State * L)
+{
+ struct config_file *cfg = lua_check_config (L);
+ const gchar *name, *callback;
+ double weight;
+ struct lua_callback_data *cd;
+
+ if (cfg) {
+ name = memory_pool_strdup (cfg->cfg_pool, luaL_checkstring (L, 2));
+ weight = luaL_checknumber (L, 3);
+ callback = luaL_checkstring (L, 4);
+ if (name) {
+ cd = g_malloc (sizeof (struct lua_callback_data));
+ cd->name = g_strdup (callback);
+ cd->L = L;
+ register_callback_symbol (&cfg->cache, name, weight, lua_metric_symbol_callback, cd);
+ }
+ }
+ return 1;
+}
+
/* Radix and hash table functions */
static gint
lua_radix_get_key (lua_State * L)
#include "map.h"
#include "fuzzy_storage.h"
#include "cfg_xml.h"
+#include "symbols_cache.h"
#ifndef WITHOUT_PERL
cur = cfg->cache->negative_items;
while (cur) {
item = cur->data;
- printf ("-----------------------------------------------------------------\n");
- printf ("| %3d | %22s | %6.1f | %9d | %9.3f |\n", i, item->s->symbol, item->s->weight, item->s->frequency, item->s->avg_time);
+ if (!item->is_callback) {
+ printf ("-----------------------------------------------------------------\n");
+ printf ("| %3d | %22s | %6.1f | %9d | %9.3f |\n", i, item->s->symbol, item->s->weight, item->s->frequency, item->s->avg_time);
+ }
cur = g_list_next (cur);
i ++;
}
cur = cfg->cache->static_items;
while (cur) {
item = cur->data;
- printf ("-----------------------------------------------------------------\n");
- printf ("| %3d | %22s | %6.1f | %9d | %9.3f |\n", i, item->s->symbol, item->s->weight, item->s->frequency, item->s->avg_time);
+ if (!item->is_callback) {
+ printf ("-----------------------------------------------------------------\n");
+ printf ("| %3d | %22s | %6.1f | %9d | %9.3f |\n", i, item->s->symbol, item->s->weight, item->s->frequency, item->s->avg_time);
+ }
cur = g_list_next (cur);
i ++;
}
}
l = g_list_next (l);
}
+ /* Insert classifiers symbols */
+ (void)insert_classifier_symbols (rspamd->cfg);
+
+ if (! validate_cache (rspamd->cfg->cache, rspamd->cfg, TRUE)) {
+ res = FALSE;
+ }
if (dump_vars) {
dump_cfg_vars (rspamd->cfg);
}
/* Check configuration for modules */
(void)check_modules_config (rspamd->cfg);
+ /* Insert classifiers symbols */
+ (void)insert_classifier_symbols (rspamd->cfg);
+
/* Perform modules configuring */
l = g_list_first (rspamd->cfg->filters);
/* Init config cache */
init_cfg_cache (rspamd->cfg);
+ /* Validate cache */
+ (void)validate_cache (rspamd->cfg->cache, rspamd->cfg, FALSE);
+
/* Flush log */
flush_log_buf ();
+++ /dev/null
-/*
- * Copyright (c) 2009, Rambler media
- * 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 BY Rambler media ''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 Rambler 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.
- */
-
-/***MODULE:email
- * rspamd module that extracts emails from messages and check them via blacklist
- *
- * Allowed options:
- * - symbol (string): symbol to insert (default: 'R_BAD_EMAIL')
- * - blacklist (map string): map that contains list of bad emails
- */
-
-#include "../config.h"
-#include "../main.h"
-#include "../message.h"
-#include "../modules.h"
-#include "../cfg_file.h"
-#include "../expressions.h"
-#include "../util.h"
-#include "../view.h"
-#include "../map.h"
-
-#define DEFAULT_SYMBOL "R_BAD_EMAIL"
-
-static const gchar *email_re_text =
- "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\\b";
-
-struct email_ctx {
- gint (*filter) (struct worker_task * task);
- gchar *symbol;
- GRegex *email_re;
-
- GHashTable *blacklist;
- gchar *blacklist_file;
-
- memory_pool_t *email_pool;
-};
-
-static struct email_ctx *email_module_ctx = NULL;
-
-static gint emails_mime_filter (struct worker_task *task);
-static void emails_symbol_callback (struct worker_task *task, void *unused);
-static gint emails_command_handler (struct worker_task *task);
-
-gint
-emails_module_init (struct config_file *cfg, struct module_ctx **ctx)
-{
- GError *err = NULL;
-
- email_module_ctx = g_malloc (sizeof (struct email_ctx));
-
- email_module_ctx->filter = emails_mime_filter;
- email_module_ctx->email_pool = memory_pool_new (memory_pool_get_size ());
- email_module_ctx->email_re = g_regex_new (email_re_text, G_REGEX_RAW | G_REGEX_OPTIMIZE | G_REGEX_CASELESS, 0, &err);
- email_module_ctx->blacklist = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal);
-
- *ctx = (struct module_ctx *)email_module_ctx;
-
- register_protocol_command ("emails", emails_command_handler);
-
- return 0;
-}
-
-
-gint
-emails_module_config (struct config_file *cfg)
-{
- gchar *value;
- gint res = TRUE;
-
- if ((value = get_module_opt (cfg, "emails", "symbol")) != NULL) {
- email_module_ctx->symbol = memory_pool_strdup (email_module_ctx->email_pool, value);
- }
- else {
- email_module_ctx->symbol = DEFAULT_SYMBOL;
- }
- if ((value = get_module_opt (cfg, "emails", "blacklist")) != NULL) {
- if (add_map (value, read_host_list, fin_host_list, (void **)&email_module_ctx->blacklist)) {
- email_module_ctx->blacklist_file = memory_pool_strdup (email_module_ctx->email_pool, value + sizeof ("file://") - 1);
- }
- }
-
-
- register_symbol (&cfg->cache, email_module_ctx->symbol, 1, emails_symbol_callback, NULL);
-
- return res;
-}
-
-gint
-emails_module_reconfig (struct config_file *cfg)
-{
- memory_pool_delete (email_module_ctx->email_pool);
- email_module_ctx->email_pool = memory_pool_new (memory_pool_get_size ());
-
- return emails_module_config (cfg);
-}
-
-static GList *
-extract_emails (struct worker_task *task)
-{
- GList *res = NULL, *cur;
- GMatchInfo *info;
- GError *err = NULL;
- struct mime_text_part *part;
- gchar *email_str;
- gint rc;
-
- cur = g_list_first (task->text_parts);
- while (cur) {
- part = cur->data;
-
- if (part->is_empty) {
- cur = g_list_next (cur);
- continue;
- }
-
- rc = g_regex_match_full (email_module_ctx->email_re, (const gchar *)part->orig->data, part->orig->len, 0, 0, &info, &err);
- if (rc) {
- while (g_match_info_matches (info)) {
- email_str = g_match_info_fetch (info, 0);
- if (email_str != NULL) {
- res = g_list_prepend (res, email_str);
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, email_str);
- }
- /* Get next match */
- g_match_info_next (info, &err);
- }
- }
- else if (err != NULL) {
- debug_task ("error matching regexp: %s", err->message);
- }
- else {
- debug_task ("cannot find url pattern in given string");
- }
- g_match_info_free (info);
-
- cur = g_list_next (cur);
- }
- if (res != NULL) {
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_list_free, res);
- }
-
- return res;
-}
-
-static gint
-emails_command_handler (struct worker_task *task)
-{
- GList *emails, *cur;
- gchar outbuf[BUFSIZ];
- gint r, num = 0;
-
- emails = extract_emails (task);
-
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, "OK");
-
- r += snprintf (outbuf + r, sizeof (outbuf) - r - 2, "Emails: ");
-
- cur = g_list_first (emails);
-
- while (cur) {
- num++;
- if (g_list_next (cur) != NULL) {
- r += snprintf (outbuf + r, sizeof (outbuf) - r - 2, "%s, ", (gchar *)cur->data);
- }
- else {
- r += snprintf (outbuf + r, sizeof (outbuf) - r - 2, "%s", (gchar *)cur->data);
- }
- cur = g_list_next (cur);
- }
-
- outbuf[r++] = '\r';
- outbuf[r++] = '\n';
-
- if (! rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE)) {
- return -1;
- }
- msg_info ("msg ok, id: <%s>, %d emails extracted", task->message_id, num);
-
- return 0;
-}
-
-static void
-emails_symbol_callback (struct worker_task *task, void *unused)
-{
- GList *emails, *cur;
-
-
- if (check_view (task->cfg->views, email_module_ctx->symbol, task)) {
- emails = extract_emails (task);
- if (email_module_ctx->blacklist && emails) {
- cur = g_list_first (emails);
-
- while (cur) {
- if (g_hash_table_lookup (email_module_ctx->blacklist, cur->data) != NULL) {
- insert_result (task, email_module_ctx->symbol, 1, g_list_prepend (NULL, memory_pool_strdup (task->task_pool, (gchar *)cur->data)));
-
- }
- cur = g_list_next (cur);
- }
- }
- }
-
-}
-
-static gint
-emails_mime_filter (struct worker_task *task)
-{
- /* XXX: remove this */
- return 0;
-}
/* Flags string is in format <numeric_flag>:<SYMBOL>:weight[, <numeric_flag>:<SYMBOL>:weight...] */
static void
-parse_flags_string (gchar *str)
+parse_flags_string (struct config_file *cfg, gchar *str)
{
gchar **strvec, *item, *err_str, **map_str;
gint num, i, t;
map->fuzzy_flag = strtol (map_str[0], &err_str, 10);
if (errno != 0 || (err_str && *err_str != '\0')) {
msg_info ("cannot parse flag %s: %s", map_str[0], strerror (errno));
+ continue;
}
else if (t == 2) {
/* Weight is skipped in definition */
}
else {
map->weight = strtol (map_str[2], &err_str, 10);
- /* Add flag to hash table */
- g_hash_table_insert (fuzzy_module_ctx->mappings, GINT_TO_POINTER(map->fuzzy_flag), map);
+
}
+ /* Add flag to hash table */
+ g_hash_table_insert (fuzzy_module_ctx->mappings, GINT_TO_POINTER(map->fuzzy_flag), map);
+ register_virtual_symbol (&cfg->cache, map->symbol, map->weight);
}
g_strfreev (map_str);
}
parse_servers_string (value);
}
if ((value = get_module_opt (cfg, "fuzzy_check", "fuzzy_map")) != NULL) {
- parse_flags_string (value);
+ parse_flags_string (cfg, value);
}
register_symbol (&cfg->cache, fuzzy_module_ctx->symbol, fuzzy_module_ctx->max_score, fuzzy_symbol_callback, NULL);
if opts['symbol_rcpt'] or opts['symbol_sender'] then
if opts['symbol_rcpt'] then
symbol_rcpt = opts['symbol_rcpt']
+ rspamd_config:register_virtual_symbol(symbol_rcpt, 1.0, 'check_forged_headers')
end
if opts['symbol_sender'] then
symbol_sender = opts['symbol_sender']
+ rspamd_config:register_virtual_symbol(symbol_sender, 1.0)
end
- rspamd_config:register_symbol(symbol_rcpt, 1.0, 'check_forged_headers')
+ rspamd_config:register_callback_symbol('FORGED_RECIPIENTS', 1.0, 'check_forged_headers')
+
end
end
local _,_,name,value = string.find(param, '(%w+)%s*=%s*(.+)')
if not name or not value then
rspamd_logger:err('invalid rule: '..param)
- return 0
+ return nil
end
if name == 'type' then
if value == 'ip' then
newrule['type'] = 'header'
else
rspamd_logger:err('invalid rule type: '.. value)
- return 0
+ return nil
end
elseif name == 'header' then
newrule['header'] = value
newrule['symbol'] = value
else
rspamd_logger:err('invalid rule option: '.. name)
- return 0
+ return nil
end
end
if not newrule['symbol'] or not newrule['map'] or not newrule['symbol'] then
rspamd_logger:err('incomplete rule')
- return 0
+ return nil
end
if newrule['type'] == 'ip' then
newrule['ips'] = rspamd_config:add_radix_map (newrule['map'])
newrule['hash'] = rspamd_config:add_hash_map (newrule['map'])
end
table.insert(rules, newrule)
- return 1
+ return newrule
end
-- Registration
if type(strrules) == 'table' then
for _,value in ipairs(strrules) do
local params = split(value, ',')
- if not add_rule (params) then
+ local rule = add_rule (params)
+ if not rule then
rspamd_logger:err('cannot add rule: "'..value..'"')
+ else
+ rspamd_config:register_virtual_symbol(rule['symbol'], 1.0)
end
end
elseif type(strrules) == 'string' then
local params = split(strrules, ',')
- if not add_rule (params) then
+ local rule = add_rule (params)
+ if not rule then
rspamd_logger:err('cannot add rule: "'..strrules..'"')
+ else
+ rspamd_config:register_virtual_symbol(rule['symbol'], 1.0)
end
end
end
if table.maxn(rules) > 0 then
-- add fake symbol to check all maps inside a single callback
- rspamd_config:register_symbol('MULTIMAP', 1.0, 'check_multimap')
+ rspamd_config:register_callback_symbol('MULTIMAP', 1.0, 'check_multimap')
end
for n,v in pairs(opts) do
if n == 'symbol_strict' then
symbol_strict = v
+ rspamd_config:register_virtual_symbol(symbol_strict, 1.0)
elseif n == 'bad_host' then
bad_hosts = v
elseif n == 'good_host' then
}
register_symbol (&cfg->cache, spf_module_ctx->symbol_fail, 1, spf_symbol_callback, NULL);
+ register_virtual_symbol (&cfg->cache, spf_module_ctx->symbol_softfail, 1);
+ register_virtual_symbol (&cfg->cache, spf_module_ctx->symbol_allow, 1);
return res;
}
return 0;
}
+/*
+ * Register virtual symbols for suffixes with bit wildcard
+ */
+static void
+register_bit_symbols (struct config_file *cfg)
+{
+ gchar *c, *symbol;
+ GList *symit, *cur;
+ struct surbl_bit_item *bit;
+ struct suffix_item *suffix;
+ gint len;
+
+ symit = surbl_module_ctx->suffixes;
+
+ while (symit) {
+ suffix = symit->data;
+ if ((c = strchr (suffix->symbol, '%')) != NULL && *(c + 1) == 'b') {
+ cur = g_list_first (surbl_module_ctx->bits);
+ while (cur) {
+ bit = (struct surbl_bit_item *)cur->data;
+ len = strlen (suffix->symbol) - 2 + strlen (bit->symbol) + 1;
+ *c = '\0';
+ symbol = memory_pool_alloc (cfg->cfg_pool, len);
+ rspamd_snprintf (symbol, len, "%s%s%s", suffix->symbol, bit->symbol, c + 2);
+ *c = '%';
+ register_virtual_symbol (&cfg->cache, symbol, 1);
+ cur = g_list_next (cur);
+ }
+ }
+ else {
+ register_virtual_symbol (&cfg->cache, suffix->symbol, 1);
+ }
+ symit = g_list_next (symit);
+ }
+}
+
gint
surbl_module_config (struct config_file *cfg)
{
msg_debug ("add new surbl suffix: %s with symbol: %s", new_suffix->suffix, new_suffix->symbol);
*str = '_';
surbl_module_ctx->suffixes = g_list_prepend (surbl_module_ctx->suffixes, new_suffix);
- register_symbol (&cfg->cache, new_suffix->symbol, 1, surbl_test_url, new_suffix);
+ register_callback_symbol (&cfg->cache, new_suffix->symbol, 1, surbl_test_url, new_suffix);
}
}
if (!g_strncasecmp (cur->param, "bit", sizeof ("bit") - 1)) {
register_symbol (&cfg->cache, new_suffix->symbol, 1, surbl_test_url, new_suffix);
}
+ register_bit_symbols (cfg);
+
return TRUE;
}
return mmap_cache_file (cache, fd, pool);
}
-void
-register_symbol (struct symbols_cache **cache, const gchar *name, double weight, symbol_func_t func, gpointer user_data)
+enum rspamd_symbol_type {
+ SYMBOL_TYPE_NORMAL,
+ SYMBOL_TYPE_VIRTUAL,
+ SYMBOL_TYPE_CALLBACK
+};
+
+static void
+register_symbol_common (struct symbols_cache **cache, const gchar *name, double weight,
+ symbol_func_t func, gpointer user_data, enum rspamd_symbol_type type)
{
struct cache_item *item = NULL;
struct symbols_cache *pcache = *cache;
item->func = func;
item->user_data = user_data;
+ switch (type) {
+ case SYMBOL_TYPE_NORMAL:
+ break;
+ case SYMBOL_TYPE_VIRTUAL:
+ item->is_virtual = TRUE;
+ break;
+ case SYMBOL_TYPE_CALLBACK:
+ item->is_callback = TRUE;
+ break;
+ }
+
/* Handle weight using default metric */
if (pcache->cfg && pcache->cfg->default_metric && (w = g_hash_table_lookup (pcache->cfg->default_metric->symbols, name)) != NULL) {
item->s->weight = weight * (*w);
*target = g_list_prepend (*target, item);
}
+void
+register_symbol (struct symbols_cache **cache, const gchar *name, double weight,
+ symbol_func_t func, gpointer user_data)
+{
+ register_symbol_common (cache, name, weight, func, user_data, SYMBOL_TYPE_NORMAL);
+}
+
+void
+register_virtual_symbol (struct symbols_cache **cache, const gchar *name, double weight)
+{
+ register_symbol_common (cache, name, weight, NULL, NULL, SYMBOL_TYPE_VIRTUAL);
+}
+
+void
+register_callback_symbol (struct symbols_cache **cache, const gchar *name, double weight,
+ symbol_func_t func, gpointer user_data)
+{
+ register_symbol_common (cache, name, weight, func, user_data, SYMBOL_TYPE_CALLBACK);
+}
+
void
register_dynamic_symbol (memory_pool_t *dynamic_pool, struct symbols_cache **cache,
const gchar *name, double weight, symbol_func_t func,
g_checksum_get_digest (cksum, mem_sum, &cklen);
/* Now try to read file sum */
if (lseek (fd, -(cklen), SEEK_END) == -1) {
+ if (errno == EINVAL) {
+ /* Try to create file */
+ msg_info ("recreate cache file");
+ if ((fd = open (filename, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) {
+ msg_info ("cannot create file %s, error %d, %s", filename, errno, strerror (errno));
+ return FALSE;
+ }
+ else {
+ return create_cache_file (cache, filename, fd, pool);
+ }
+ }
close (fd);
g_free (mem_sum);
g_checksum_free (cksum);
return FALSE;
}
+
+gboolean
+validate_cache (struct symbols_cache *cache, struct config_file *cfg, gboolean strict)
+{
+ struct cache_item *item;
+ GList *cur, *p, *metric_symbols;
+ gboolean res;
+
+ if (cache == NULL) {
+ msg_err ("empty cache is invalid");
+ return FALSE;
+ }
+
+ /* Check each symbol in a cache and find its weight definition */
+ cur = cache->negative_items;
+ while (cur) {
+ item = cur->data;
+ if (!item->is_callback) {
+ if (g_hash_table_lookup (cfg->metrics_symbols, item->s->symbol) == NULL) {
+ if (strict) {
+ msg_warn ("no weight registered for symbol %s", item->s->symbol);
+ return FALSE;
+ }
+ else {
+ msg_info ("no weight registered for symbol %s", item->s->symbol);
+ }
+ }
+ }
+ cur = g_list_next (cur);
+ }
+ cur = cache->static_items;
+ while (cur) {
+ item = cur->data;
+ if (!item->is_callback) {
+ if (g_hash_table_lookup (cfg->metrics_symbols, item->s->symbol) == NULL) {
+ if (strict) {
+ msg_warn ("no weight registered for symbol %s", item->s->symbol);
+ return FALSE;
+ }
+ else {
+ msg_info ("no weight registered for symbol %s", item->s->symbol);
+ }
+ }
+ }
+ cur = g_list_next (cur);
+ }
+ /* Now check each metric item and find corresponding symbol in a cache */
+ metric_symbols = g_hash_table_get_keys (cfg->metrics_symbols);
+ cur = metric_symbols;
+ while (cur) {
+ res = FALSE;
+ p = cache->negative_items;
+ while (p) {
+ item = p->data;
+ if (strcmp (item->s->symbol, cur->data) == 0) {
+ res = TRUE;
+ break;
+ }
+ p = g_list_next (p);
+ }
+ if (!res) {
+ p = cache->static_items;
+ while (p) {
+ item = p->data;
+ if (strcmp (item->s->symbol, cur->data) == 0) {
+ res = TRUE;
+ break;
+ }
+ p = g_list_next (p);
+ }
+ }
+ if (!res) {
+ msg_warn ("symbol '%s' is registered in metric but not found in cache", cur->data);
+ if (strict) {
+ return FALSE;
+ }
+ }
+ cur = g_list_next (cur);
+ }
+
+ return TRUE;
+}
+
struct symbol_callback_data {
enum {
CACHE_STATE_NEGATIVE,
if (!item) {
return FALSE;
}
- if (check_view (task->cfg->views, item->s->symbol, task)) {
+ if (!item->is_virtual && check_view (task->cfg->views, item->s->symbol, task)) {
#ifdef HAVE_CLOCK_GETTIME
# ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts1);
/* Callback data */
symbol_func_t func;
gpointer user_data;
+
+ /* Flags of virtual symbols */
+ gboolean is_virtual;
+ gboolean is_callback;
};
*/
void register_symbol (struct symbols_cache **cache, const gchar *name, double weight, symbol_func_t func, gpointer user_data);
+/**
+ * Register virtual symbol
+ * @param name name of symbol
+ */
+void register_virtual_symbol (struct symbols_cache **cache, const gchar *name, double weight);
+
+/**
+ * Register callback function for symbols parsing
+ * @param name name of symbol
+ * @param func pointer to handler
+ * @param user_data pointer to user_data
+ */
+void register_callback_symbol (struct symbols_cache **cache, const gchar *name, double weight, symbol_func_t func, gpointer user_data);
+
/**
* Register function for dynamic symbols parsing
* @param name name of symbol
*/
void remove_dynamic_rules (struct symbols_cache *cache);
+/**
+ * Validate cache items agains theirs weights defined in metrics
+ * @param cache symbols cache
+ * @param cfg configuration
+ * @param strict do strict checks - symbols MUST be described in metrics
+ */
+gboolean validate_cache (struct symbols_cache *cache, struct config_file *cfg, gboolean strict);
+
#endif