@@ -7,7 +7,7 @@ PROJECT(rspamd C) | |||
SET(RSPAMD_VERSION_MAJOR 0) | |||
SET(RSPAMD_VERSION_MINOR 2) | |||
SET(RSPAMD_VERSION_PATCH 1) | |||
SET(RSPAMD_VERSION_PATCH 2) | |||
SET(RSPAMD_VERSION "${RSPAMD_VERSION_MAJOR}.${RSPAMD_VERSION_MINOR}.${RSPAMD_VERSION_PATCH}") | |||
SET(RSPAMD_MASTER_SITE_URL "http://cebka.pp.ru/hg/rspamd") | |||
@@ -312,7 +312,9 @@ SET(RSPAMDSRC src/modules.c | |||
src/buffer.c | |||
src/html.c | |||
src/lmtp.c | |||
src/lmtp_proto.c) | |||
src/lmtp_proto.c | |||
src/radix.c | |||
src/view.c) | |||
IF(ENABLE_PERL MATCHES "ON") | |||
LIST(APPEND RSPAMDSRC src/perl.c) | |||
@@ -344,6 +346,7 @@ SET(TESTDEPENDS src/mem_pool.c | |||
src/hash.c | |||
src/url.c | |||
src/util.c | |||
src/radix.c | |||
src/fuzzy.c | |||
src/memcached.c | |||
src/message.c | |||
@@ -361,7 +364,8 @@ SET(UTILSDEPENDS src/mem_pool.c | |||
src/expressions.c | |||
src/message.c | |||
src/html.c | |||
src/util.c) | |||
src/util.c | |||
src/radix.c) | |||
LIST(LENGTH PLUGINSSRC RSPAMD_MODULES_NUM) | |||
@@ -294,3 +294,17 @@ raw_mode = yes; | |||
url_filters = "surbl"; | |||
header_filters = "regexp"; | |||
mime_filters = "chartable,emails"; | |||
# Definition of view, views may allow to customize rules for different messages | |||
view { | |||
# All directives here may be duplicated to add specific elements or regexp/files | |||
# List of ip/mask for this view | |||
ip = "file://@CMAKE_INSTALL_PREFIX@/etc/rspamd/ip_internal.inc"; | |||
# From addresses for this view: | |||
# list is placed in file: | |||
#from = "file://@CMAKE_INSTALL_PREFIX@/etc/rspamd/from_internal.inc"; | |||
# list is regexp: | |||
#from = "/^.+@example.com$/i"; | |||
# Symbols to check, can also be list of files or regexp: | |||
symbols = "/^[A-Z]{2}_SURBL_MULTI$/i"; | |||
}; |
@@ -219,6 +219,7 @@ struct config_file { | |||
GHashTable* statfiles; /**< hash of defined statfiles indexed by alias */ | |||
GHashTable* cfg_params; /**< all cfg params indexed by its name in this structure */ | |||
int clock_res; /**< resolution of clock used */ | |||
GList *views; /**< views */ | |||
}; | |||
/** |
@@ -38,6 +38,10 @@ composites return COMPOSITES; | |||
tempdir return TEMPDIR; | |||
pidfile return PIDFILE; | |||
view return VIEW; | |||
ip return IP; | |||
from return FROM; | |||
symbols return SYMBOLS; | |||
error_time return ERROR_TIME; | |||
dead_time return DEAD_TIME; |
@@ -8,6 +8,7 @@ | |||
#include "expressions.h" | |||
#include "classifiers/classifiers.h" | |||
#include "tokenizers/tokenizers.h" | |||
#include "view.h" | |||
#ifdef WITH_LUA | |||
#include "lua-rspamd.h" | |||
#else | |||
@@ -25,6 +26,8 @@ struct statfile *cur_statfile = NULL; | |||
struct statfile_section *cur_section = NULL; | |||
struct worker_conf *cur_worker = NULL; | |||
struct rspamd_view *cur_view = NULL; | |||
%} | |||
%union | |||
@@ -51,6 +54,7 @@ struct worker_conf *cur_worker = NULL; | |||
%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 | |||
%token DELIVERY LMTP ENABLED AGENT SECTION LUACODE RAW_MODE PROFILE_FILE COUNT | |||
%token VIEW IP FROM SYMBOLS | |||
%type <string> STRING | |||
%type <string> VARIABLE | |||
@@ -93,6 +97,7 @@ command : | |||
| luacode | |||
| raw_mode | |||
| profile_file | |||
| view | |||
; | |||
tempdir : | |||
@@ -832,6 +837,62 @@ profile_file: | |||
} | |||
; | |||
view: | |||
VIEW OBRACE viewbody EBRACE { | |||
if (cur_view == NULL) { | |||
yyerror ("yyparse: not enough arguments in view definition"); | |||
YYERROR; | |||
} | |||
cfg->views = g_list_prepend (cfg->views, cur_view); | |||
cur_view = NULL; | |||
} | |||
; | |||
viewbody: | |||
| viewcmd SEMICOLON | |||
| viewbody viewcmd SEMICOLON | |||
; | |||
viewcmd: | |||
| viewip | |||
| viewfrom | |||
| viewsymbols | |||
; | |||
viewip: | |||
IP EQSIGN QUOTEDSTRING { | |||
if (cur_view == NULL) { | |||
cur_view = init_view (cfg->cfg_pool); | |||
} | |||
if (!add_view_ip (cur_view, $3)) { | |||
yyerror ("yyparse: invalid ip line in view definition: ip = '%s'", $3); | |||
YYERROR; | |||
} | |||
} | |||
; | |||
viewfrom: | |||
FROM EQSIGN QUOTEDSTRING { | |||
if (cur_view == NULL) { | |||
cur_view = init_view (cfg->cfg_pool); | |||
} | |||
if (!add_view_from (cur_view, $3)) { | |||
yyerror ("yyparse: invalid from line in view definition: from = '%s'", $3); | |||
YYERROR; | |||
} | |||
} | |||
; | |||
viewsymbols: | |||
SYMBOLS EQSIGN QUOTEDSTRING { | |||
if (cur_view == NULL) { | |||
cur_view = init_view (cfg->cfg_pool); | |||
} | |||
if (!add_view_symbols (cur_view, $3)) { | |||
yyerror ("yyparse: invalid symbols line in view definition: symbols = '%s'", $3); | |||
YYERROR; | |||
} | |||
} | |||
; | |||
%% | |||
/* | |||
* vi:ts=4 |
@@ -81,6 +81,7 @@ struct config_file; | |||
struct tokenizer; | |||
struct classifier; | |||
struct mime_part; | |||
struct rspamd_view; | |||
/** | |||
* Server statistics | |||
@@ -198,6 +199,8 @@ struct worker_task { | |||
int error_code; /**< code of last error */ | |||
memory_pool_t *task_pool; /**< memory pool for task */ | |||
struct timespec ts; /**< time of connection */ | |||
struct rspamd_view *view; /**< matching view */ | |||
gboolean view_checked; | |||
}; | |||
/** |
@@ -32,6 +32,7 @@ | |||
#include "../modules.h" | |||
#include "../cfg_file.h" | |||
#include "../expressions.h" | |||
#include "../view.h" | |||
#define DEFAULT_SYMBOL "R_CHARSET_MIXED" | |||
#define DEFAULT_THRESHOLD 0.1 | |||
@@ -182,12 +183,14 @@ chartable_mime_filter (struct worker_task *task) | |||
{ | |||
GList *cur; | |||
cur = g_list_first (task->text_parts); | |||
while (cur) { | |||
if (check_part ((struct mime_text_part *)cur->data, task->cfg->raw_mode)) { | |||
insert_result (task, chartable_module_ctx->metric, chartable_module_ctx->symbol, 1, NULL); | |||
if (check_view (task->cfg->views, chartable_module_ctx->symbol, task)) { | |||
cur = g_list_first (task->text_parts); | |||
while (cur) { | |||
if (check_part ((struct mime_text_part *)cur->data, task->cfg->raw_mode)) { | |||
insert_result (task, chartable_module_ctx->metric, chartable_module_ctx->symbol, 1, NULL); | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
return 0; |
@@ -33,6 +33,7 @@ | |||
#include "../cfg_file.h" | |||
#include "../expressions.h" | |||
#include "../util.h" | |||
#include "../view.h" | |||
#define DEFAULT_SYMBOL "R_BAD_EMAIL" | |||
@@ -204,16 +205,18 @@ emails_mime_filter (struct worker_task *task) | |||
emails = extract_emails (task); | |||
if (email_module_ctx->blacklist && emails) { | |||
cur = g_list_first (emails); | |||
if (check_view (task->cfg->views, email_module_ctx->symbol, 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->metric, email_module_ctx->symbol, 1, | |||
g_list_prepend (NULL, memory_pool_strdup (task->task_pool, (char *)cur->data))); | |||
while (cur) { | |||
if (g_hash_table_lookup (email_module_ctx->blacklist, cur->data) != NULL) { | |||
insert_result (task, email_module_ctx->metric, email_module_ctx->symbol, 1, | |||
g_list_prepend (NULL, memory_pool_strdup (task->task_pool, (char *)cur->data))); | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
} | |||
@@ -36,6 +36,7 @@ | |||
#include "../cfg_file.h" | |||
#include "../util.h" | |||
#include "../expressions.h" | |||
#include "../view.h" | |||
#define DEFAULT_STATFILE_PREFIX "./" | |||
@@ -591,28 +592,29 @@ process_regexp_item (struct regexp_module_item *item, struct worker_task *task) | |||
struct timespec ts1, ts2; | |||
uint64_t diff; | |||
if (check_view (task->cfg->views, item->symbol, task)) { | |||
#ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID | |||
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts1); | |||
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts1); | |||
#elif defined(HAVE_CLOCK_VIRTUAL) | |||
clock_gettime (CLOCK_VIRTUAL, &ts1); | |||
clock_gettime (CLOCK_VIRTUAL, &ts1); | |||
#else | |||
clock_gettime (CLOCK_REALTIME, &ts1); | |||
clock_gettime (CLOCK_REALTIME, &ts1); | |||
#endif | |||
if (process_regexp_expression (item->expr, task)) { | |||
insert_result (task, regexp_module_ctx->metric, item->symbol, 1, NULL); | |||
} | |||
if (process_regexp_expression (item->expr, task)) { | |||
insert_result (task, regexp_module_ctx->metric, item->symbol, 1, NULL); | |||
} | |||
#ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID | |||
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts2); | |||
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts2); | |||
#elif defined(HAVE_CLOCK_VIRTUAL) | |||
clock_gettime (CLOCK_VIRTUAL, &ts2); | |||
clock_gettime (CLOCK_VIRTUAL, &ts2); | |||
#else | |||
clock_gettime (CLOCK_REALTIME, &ts2); | |||
clock_gettime (CLOCK_REALTIME, &ts2); | |||
#endif | |||
diff = (ts2.tv_sec - ts1.tv_sec) * 1000000 + (ts2.tv_nsec - ts1.tv_nsec) / 1000; | |||
set_counter (item->symbol, diff); | |||
diff = (ts2.tv_sec - ts1.tv_sec) * 1000000 + (ts2.tv_nsec - ts1.tv_nsec) / 1000; | |||
set_counter (item->symbol, diff); | |||
} | |||
} | |||
static int |
@@ -29,6 +29,7 @@ | |||
#include "../config.h" | |||
#include "../util.h" | |||
#include "../message.h" | |||
#include "../view.h" | |||
#include <evdns.h> | |||
#include "surbl.h" | |||
@@ -371,27 +372,32 @@ make_surbl_requests (struct uri* url, struct worker_task *task, GTree *tree) | |||
while (cur) { | |||
suffix = (struct suffix_item *)cur->data; | |||
if ((surbl_req = format_surbl_request (task->task_pool, &f, suffix, &host_end, TRUE, &err)) != NULL) { | |||
if (g_tree_lookup (tree, surbl_req) == NULL) { | |||
g_tree_insert (tree, surbl_req, surbl_req); | |||
param = memory_pool_alloc (task->task_pool, sizeof (struct dns_param)); | |||
param->url = url; | |||
param->task = task; | |||
param->suffix = suffix; | |||
*host_end = '\0'; | |||
param->host_resolve = memory_pool_strdup (task->task_pool, surbl_req); | |||
*host_end = '.'; | |||
msg_debug ("surbl_test_url: send surbl dns request %s", surbl_req); | |||
evdns_resolve_ipv4 (surbl_req, DNS_QUERY_NO_SEARCH, dns_callback, (void *)param); | |||
param->task->save.saved ++; | |||
if (check_view (task->cfg->views, suffix->symbol, task)) { | |||
if ((surbl_req = format_surbl_request (task->task_pool, &f, suffix, &host_end, TRUE, &err)) != NULL) { | |||
if (g_tree_lookup (tree, surbl_req) == NULL) { | |||
g_tree_insert (tree, surbl_req, surbl_req); | |||
param = memory_pool_alloc (task->task_pool, sizeof (struct dns_param)); | |||
param->url = url; | |||
param->task = task; | |||
param->suffix = suffix; | |||
*host_end = '\0'; | |||
param->host_resolve = memory_pool_strdup (task->task_pool, surbl_req); | |||
*host_end = '.'; | |||
msg_debug ("surbl_test_url: send surbl dns request %s", surbl_req); | |||
evdns_resolve_ipv4 (surbl_req, DNS_QUERY_NO_SEARCH, dns_callback, (void *)param); | |||
param->task->save.saved ++; | |||
} | |||
else { | |||
msg_debug ("make_surbl_requests: request %s is already sent", surbl_req); | |||
} | |||
} | |||
else { | |||
msg_debug ("make_surbl_requests: request %s is already sent", surbl_req); | |||
else if (err != NULL && err->code != WHITELIST_ERROR) { | |||
msg_info ("surbl_test_url: cannot format url string for surbl %s, %s", struri (url), err->message); | |||
return; | |||
} | |||
} | |||
else if (err != NULL && err->code != WHITELIST_ERROR) { | |||
msg_info ("surbl_test_url: cannot format url string for surbl %s, %s", struri (url), err->message); | |||
return; | |||
else { | |||
msg_debug ("make_surbl_requests: skipping symbol that is not in view: %s", suffix->symbol); | |||
} | |||
cur = g_list_next (cur); | |||
} |
@@ -0,0 +1,266 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#include "config.h" | |||
#include "radix.h" | |||
#include "mem_pool.h" | |||
static void *radix_alloc (radix_tree_t *tree); | |||
radix_tree_t * | |||
radix_tree_create (memory_pool_t *pool) | |||
{ | |||
radix_tree_t *tree; | |||
tree = memory_pool_alloc (pool, sizeof(radix_tree_t)); | |||
if (tree == NULL) { | |||
return NULL; | |||
} | |||
tree->size = 0; | |||
tree->root = radix_alloc (tree); | |||
if (tree->root == NULL) { | |||
return NULL; | |||
} | |||
tree->root->right = NULL; | |||
tree->root->left = NULL; | |||
tree->root->parent = NULL; | |||
tree->root->value = RADIX_NO_VALUE; | |||
tree->pool = pool; | |||
return tree; | |||
} | |||
int | |||
radix32tree_insert (radix_tree_t *tree, uint32_t key, uint32_t mask, | |||
unsigned char value) | |||
{ | |||
uint32_t bit; | |||
radix_node_t *node, *next; | |||
bit = 0x80000000; | |||
node = tree->root; | |||
next = tree->root; | |||
/* Find a place in trie to insert */ | |||
while (bit & mask) { | |||
if (key & bit) { | |||
next = node->right; | |||
} else { | |||
next = node->left; | |||
} | |||
if (next == NULL) { | |||
break; | |||
} | |||
bit >>= 1; | |||
node = next; | |||
} | |||
if (next) { | |||
if (node->value != RADIX_NO_VALUE) { | |||
return 1; | |||
} | |||
node->value = value; | |||
return 0; | |||
} | |||
/* Inserting value in trie creating all path components */ | |||
while (bit & mask) { | |||
next = radix_alloc(tree); | |||
if (next == NULL) { | |||
return -1; | |||
} | |||
next->right = NULL; | |||
next->left = NULL; | |||
next->parent = node; | |||
next->value = RADIX_NO_VALUE; | |||
if (key & bit) { | |||
node->right = next; | |||
} else { | |||
node->left = next; | |||
} | |||
bit >>= 1; | |||
node = next; | |||
} | |||
node->value = value; | |||
return 0; | |||
} | |||
int | |||
radix32tree_delete (radix_tree_t *tree, uint32_t key, uint32_t mask) | |||
{ | |||
uint32_t bit; | |||
radix_node_t *node; | |||
radix_node_t *tmp; | |||
bit = 0x80000000; | |||
node = tree->root; | |||
while (node && (bit & mask)) { | |||
if (key & bit) { | |||
node = node->right; | |||
} else { | |||
node = node->left; | |||
} | |||
bit >>= 1; | |||
} | |||
if (node == NULL || node->parent == NULL) { | |||
return -1; | |||
} | |||
if (node->right || node->left) { | |||
if (node->value != RADIX_NO_VALUE) { | |||
node->value = RADIX_NO_VALUE; | |||
return 0; | |||
} | |||
return -1; | |||
} | |||
for ( ;; ) { | |||
if (node->parent->right == node) { | |||
node->parent->right = NULL; | |||
} else { | |||
node->parent->left = NULL; | |||
} | |||
tmp = node; | |||
node = node->parent; | |||
if (node->right || node->left) { | |||
break; | |||
} | |||
if (node->value != RADIX_NO_VALUE) { | |||
break; | |||
} | |||
if (node->parent == NULL) { | |||
break; | |||
} | |||
} | |||
return 0; | |||
} | |||
unsigned char | |||
radix32tree_find (radix_tree_t *tree, uint32_t key) | |||
{ | |||
uint32_t bit; | |||
uintptr_t value; | |||
radix_node_t *node; | |||
bit = 0x80000000; | |||
value = RADIX_NO_VALUE; | |||
node = tree->root; | |||
while (node) { | |||
if (node->value != RADIX_NO_VALUE) { | |||
value = node->value; | |||
} | |||
if (key & bit) { | |||
node = node->right; | |||
} else { | |||
node = node->left; | |||
} | |||
bit >>= 1; | |||
} | |||
return value; | |||
} | |||
static void * | |||
radix_alloc (radix_tree_t *tree) | |||
{ | |||
char *p; | |||
p = memory_pool_alloc (tree->pool, sizeof(radix_node_t)); | |||
tree->size += sizeof (radix_node_t); | |||
return p; | |||
} | |||
void | |||
radix_tree_free (radix_tree_t *tree) | |||
{ | |||
radix_node_t *node, *tmp; | |||
node = tree->root; | |||
for (;;) { | |||
/* We are at the trie root and we have no more leaves, end of algorithm */ | |||
if (!node->left && !node->right && !node->parent) { | |||
break; | |||
} | |||
/* Traverse to the end of trie */ | |||
while (node->left || node->right) { | |||
if (node->left) { | |||
node = node->left; | |||
} | |||
else { | |||
node = node->right; | |||
} | |||
} | |||
/* Found leaf node, free it */ | |||
if (node->parent->right == node) { | |||
node->parent->right = NULL; | |||
} else { | |||
node->parent->left = NULL; | |||
} | |||
tmp = node; | |||
/* Go up */ | |||
node = node->parent; | |||
} | |||
} | |||
/* | |||
* vi:ts=4 | |||
*/ |
@@ -0,0 +1,32 @@ | |||
#ifndef RADIX_H | |||
#define RADIX_H | |||
#include "config.h" | |||
#include "mem_pool.h" | |||
#define RADIX_NO_VALUE (unsigned char)-1 | |||
typedef struct radix_node_s radix_node_t; | |||
struct radix_node_s { | |||
radix_node_t *right; | |||
radix_node_t *left; | |||
radix_node_t *parent; | |||
unsigned char value; | |||
}; | |||
typedef struct { | |||
radix_node_t *root; | |||
size_t size; | |||
memory_pool_t *pool; | |||
} radix_tree_t; | |||
radix_tree_t *radix_tree_create (); | |||
int radix32tree_insert (radix_tree_t *tree, uint32_t key, uint32_t mask, unsigned char value); | |||
int radix32tree_delete (radix_tree_t *tree, uint32_t key, uint32_t mask); | |||
unsigned char radix32tree_find (radix_tree_t *tree, uint32_t key); | |||
void radix_tree_free (radix_tree_t *tree); | |||
#endif |
@@ -878,8 +878,10 @@ set_counter (const char *name, long int value) | |||
} | |||
} | |||
gboolean | |||
parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename) | |||
typedef void (*insert_func)(gpointer st, gconstpointer key, gpointer value); | |||
static gboolean | |||
abstract_parse_list (memory_pool_t *pool, void *arg, insert_func func, const char *filename) | |||
{ | |||
int fd; | |||
char buf[BUFSIZ], str[BUFSIZ], *s, *p; | |||
@@ -917,7 +919,7 @@ parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename) | |||
if (s != str) { | |||
*s = '\0'; | |||
s = memory_pool_strdup (pool, str); | |||
g_hash_table_insert (tbl, s, hash_fill); | |||
func (arg, s, hash_fill); | |||
s = str; | |||
} | |||
state = SKIP_COMMENT; | |||
@@ -926,7 +928,7 @@ parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename) | |||
if (s != str) { | |||
*s = '\0'; | |||
s = memory_pool_strdup (pool, str); | |||
g_hash_table_insert (tbl, s, hash_fill); | |||
func (arg, s, hash_fill); | |||
s = str; | |||
} | |||
while (*p == '\r' || *p == '\n') { | |||
@@ -963,6 +965,59 @@ parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename) | |||
return TRUE; | |||
} | |||
static void | |||
radix_tree_insert_helper (gpointer st, gconstpointer key, gpointer value) | |||
{ | |||
radix_tree_t *tree = st; | |||
uint32_t mask = 0xFFFFFFFF; | |||
uint32_t ip; | |||
char *token, *ipnet; | |||
struct in_addr ina; | |||
int k; | |||
k = strlen ((char *)key) + 1; | |||
ipnet = alloca (k); | |||
g_strlcpy (ipnet, key, k); | |||
token = strsep (&ipnet, "/"); | |||
if (ipnet != NULL) { | |||
k = atoi (ipnet); | |||
if (k > 32 || k < 0) { | |||
msg_warn ("radix_tree_insert_helper: invalid netmask value: %d", k); | |||
k = 32; | |||
} | |||
k = 32 - k; | |||
mask = mask << k; | |||
} | |||
if (inet_aton (token, &ina) == 0) { | |||
msg_err ("radix_tree_insert_helper: invalid ip address: %s", token); | |||
return; | |||
} | |||
ip = ntohl ((uint32_t)ina.s_addr); | |||
k = radix32tree_insert (tree, ip, mask, 1); | |||
if (k == -1) { | |||
msg_warn ("radix_tree_insert_helper: cannot insert ip to tree: %s, mask %X", inet_ntoa (ina), mask); | |||
} | |||
else if (k == 1) { | |||
msg_warn ("add_ip_radix: ip %s, mask %X, value already exists", inet_ntoa (ina), mask); | |||
} | |||
} | |||
gboolean | |||
parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename) | |||
{ | |||
return abstract_parse_list (pool, (void *)tbl, (insert_func)g_hash_table_insert, filename); | |||
} | |||
gboolean | |||
parse_radix_list (memory_pool_t *pool, radix_tree_t *tree, const char *filename) | |||
{ | |||
return abstract_parse_list (pool, (void *)tree, (insert_func)radix_tree_insert_helper, filename); | |||
} | |||
gboolean | |||
maybe_parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename) | |||
{ | |||
@@ -990,6 +1045,31 @@ maybe_parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filenam | |||
return TRUE; | |||
} | |||
#ifndef g_tolower | |||
#define g_tolower(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x)) | |||
#endif | |||
gint | |||
rspamd_strcase_equal (gconstpointer v, gconstpointer v2) | |||
{ | |||
return g_ascii_strcasecmp ((const char *) v, (const char *) v2) == 0; | |||
} | |||
guint | |||
rspamd_strcase_hash (gconstpointer key) | |||
{ | |||
const char *p = key; | |||
guint h = 0; | |||
while (*p != '\0') { | |||
h = (h << 5) - h + g_tolower (*p); | |||
p++; | |||
} | |||
return h; | |||
} | |||
/* | |||
* vi:ts=4 | |||
*/ |
@@ -3,6 +3,7 @@ | |||
#include "config.h" | |||
#include "mem_pool.h" | |||
#include "radix.h" | |||
struct config_file; | |||
struct rspamd_main; | |||
@@ -65,6 +66,10 @@ void set_counter (const char *name, long int value); | |||
gboolean parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename); | |||
gboolean maybe_parse_host_list (memory_pool_t *pool, GHashTable *tbl, const char *filename); | |||
gboolean parse_radix_list (memory_pool_t *pool, radix_tree_t *tree, const char *filename); | |||
guint rspamd_strcase_hash (gconstpointer key); | |||
gint rspamd_strcase_equal (gconstpointer v, gconstpointer v2); | |||
#endif |
@@ -0,0 +1,217 @@ | |||
/* | |||
* 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. | |||
*/ | |||
#include "config.h" | |||
#include "main.h" | |||
#include "util.h" | |||
#include "view.h" | |||
#include "expressions.h" | |||
#include "cfg_file.h" | |||
struct rspamd_view* | |||
init_view (memory_pool_t *pool) | |||
{ | |||
struct rspamd_view *new; | |||
new = memory_pool_alloc0 (pool, sizeof (struct rspamd_view)); | |||
new->pool = pool; | |||
new->from_hash = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal); | |||
new->symbols_hash = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal); | |||
memory_pool_add_destructor (new->pool, (pool_destruct_func)g_hash_table_destroy, new->from_hash); | |||
memory_pool_add_destructor (new->pool, (pool_destruct_func)g_hash_table_destroy, new->symbols_hash); | |||
return new; | |||
} | |||
gboolean | |||
add_view_from (struct rspamd_view *view, char *line) | |||
{ | |||
struct rspamd_regexp *re = NULL; | |||
if (g_ascii_strncasecmp (line, "file://", sizeof ("file://") - 1) == 0) { | |||
if (parse_host_list (view->pool, view->from_hash, line + sizeof ("file://") - 1)) { | |||
return TRUE; | |||
} | |||
} | |||
else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) { | |||
view->from_re_list = g_list_prepend (view->from_re_list, re); | |||
return TRUE; | |||
} | |||
return FALSE; | |||
} | |||
gboolean | |||
add_view_symbols (struct rspamd_view *view, char *line) | |||
{ | |||
struct rspamd_regexp *re = NULL; | |||
if (g_ascii_strncasecmp (line, "file://", sizeof ("file://") - 1) == 0) { | |||
if (parse_host_list (view->pool, view->symbols_hash, line + sizeof ("file://") - 1)) { | |||
return TRUE; | |||
} | |||
} | |||
else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) { | |||
view->symbols_re_list = g_list_prepend (view->symbols_re_list, re); | |||
return TRUE; | |||
} | |||
else { | |||
/* Try to parse symbols line as comma separated list */ | |||
} | |||
return FALSE; | |||
} | |||
gboolean | |||
add_view_ip (struct rspamd_view *view, char *line) | |||
{ | |||
if (g_ascii_strncasecmp (line, "file://", sizeof ("file://") - 1) == 0) { | |||
if (parse_radix_list (view->pool, view->ip_tree, line + sizeof ("file://") - 1)) { | |||
return TRUE; | |||
} | |||
} | |||
return FALSE; | |||
} | |||
struct rspamd_view * | |||
find_view_by_ip (GList *views, struct worker_task *task) | |||
{ | |||
GList *cur; | |||
struct rspamd_view *v; | |||
if (task->from_addr.s_addr == INADDR_NONE) { | |||
return NULL; | |||
} | |||
cur = views; | |||
while (cur) { | |||
v = cur->data; | |||
if (radix32tree_find (v->ip_tree, task->from_addr.s_addr) != RADIX_NO_VALUE) { | |||
return v; | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
return NULL; | |||
} | |||
struct rspamd_view * | |||
find_view_by_from (GList *views, struct worker_task *task) | |||
{ | |||
GList *cur, *cur_re; | |||
struct rspamd_view *v; | |||
struct rspamd_regexp *re; | |||
if (task->from == NULL) { | |||
return NULL; | |||
} | |||
cur = views; | |||
while (cur) { | |||
v = cur->data; | |||
/* First try to lookup in hashtable */ | |||
if (g_hash_table_lookup (v->from_hash, task->from) != NULL) { | |||
return v; | |||
} | |||
/* Then try to match re */ | |||
cur_re = v->from_re_list; | |||
while (cur_re) { | |||
re = cur_re->data; | |||
if (g_regex_match (re->regexp, task->from, 0, NULL) == TRUE) { | |||
return v; | |||
} | |||
cur_re = g_list_next (cur_re); | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
return NULL; | |||
} | |||
static gboolean | |||
match_view_symbol (struct rspamd_view *v, const char *symbol) | |||
{ | |||
GList *cur; | |||
struct rspamd_regexp *re; | |||
/* First try to lookup in hashtable */ | |||
if (g_hash_table_lookup (v->symbols_hash, symbol) != NULL) { | |||
return TRUE; | |||
} | |||
/* Then try to match re */ | |||
cur = v->symbols_re_list; | |||
while (cur) { | |||
re = cur->data; | |||
if (g_regex_match (re->regexp, symbol, 0, NULL) == TRUE) { | |||
return TRUE; | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
return FALSE; | |||
} | |||
gboolean | |||
check_view (GList *views, const char *symbol, struct worker_task *task) | |||
{ | |||
struct rspamd_view *selected = NULL; | |||
if (views == NULL || (task->view == NULL && task->view_checked == TRUE)) { | |||
/* If now views defined just return TRUE to check each symbol */ | |||
return TRUE; | |||
} | |||
if (task->view != NULL) { | |||
goto check_symbol; | |||
} | |||
if ((selected = find_view_by_ip (views, task)) == NULL) { | |||
if ((selected = find_view_by_from (views, task)) == NULL) { | |||
/* No matching view for this task */ | |||
task->view_checked = TRUE; | |||
return TRUE; | |||
} | |||
} | |||
task->view_checked = TRUE; | |||
task->view = selected; | |||
check_symbol: | |||
/* selected is now not NULL */ | |||
if (match_view_symbol (task->view, symbol)) { | |||
return TRUE; | |||
} | |||
return FALSE; | |||
} |
@@ -0,0 +1,28 @@ | |||
#ifndef RSPAMD_VIEW_H | |||
#define RSPAMD_VIEW_H | |||
#include "config.h" | |||
#include "main.h" | |||
#include "radix.h" | |||
struct rspamd_view { | |||
GList *from_re_list; | |||
GHashTable *from_hash; | |||
radix_tree_t *ip_tree; | |||
GHashTable *symbols_hash; | |||
GList *symbols_re_list; | |||
memory_pool_t *pool; | |||
}; | |||
struct rspamd_view* init_view (memory_pool_t *pool); | |||
gboolean add_view_from (struct rspamd_view *view, char *line); | |||
gboolean add_view_ip (struct rspamd_view *view, char *line); | |||
gboolean add_view_symbols (struct rspamd_view *view, char *line); | |||
gboolean check_view (GList *views, const char *symbol, struct worker_task *task); | |||
#endif |
@@ -287,6 +287,8 @@ accept_socket (int fd, short what, void *arg) | |||
new_task->state = READ_COMMAND; | |||
new_task->sock = nfd; | |||
new_task->cfg = worker->srv->cfg; | |||
new_task->from_addr.s_addr = INADDR_NONE; | |||
new_task->view_checked = FALSE; | |||
#ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID | |||
clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &new_task->ts); | |||
#elif defined(HAVE_CLOCK_VIRTUAL) |