make them built-in as they are widely used in our rules. In fact this list may
be extended in future.
+@subsection Dynamic rules.
+Rspamd regexp module can use dynamic rules that can be written in json syntax.
+Dynamic rules are loaded at runtime and can be modified while rspamd is working.
+Also it is possible to turn dynamic rules for specific networks only and add rules
+that does not contain any regexp (this can be usefull for dynamic lists for example).
+Dynamic rules can be obtained like any other dynamic map via file monitoring or via
+http. Here are examples of dynamic rules definitions:
+@example
+<module name="regexp">
+ <option name="dynamic_rules">file:///tmp/rules.json</option>
+</module>
+@end example
+@noindent
+or for http map:
+@example
+<module name="regexp">
+ <option name="dynamic_rules">http://somehost/rules.json</option>
+</module>
+@end example
+@noindent
+Rules are presented as json array (in brackets @emph{'[]'}). Each rule is json object.
+This object can have several properties (properties with @strong{*} are required):
+@multitable @columnfractions 0.3 0.7
+@headitem Property @tab Mean
+@item symbol(*)
+@tab Symbol for rule.
+@item factor(*)
+@tab Factor for rule.
+@item rule
+@tab Rule itself (regexp expression).
+@item enabled
+@tab Boolean flag that define whether this rule is enabled (rule is enabled if
+this flag is not present by default).
+@item networks
+@tab Json array of networks (in CIDR format, also it is possible to add negation
+by prepending @emph{!} symbol before item.
+@end multitable
+Here is an example of dynamic rule:
+@example
+[
+ {
+ "rule": "/test/rP",
+ "symbol": "R_TMP_1",
+ "factor": 1.1,
+ "networks": ["!192.168.1.0/24", "172.16.0.0/16"],
+ "enabled": false
+ }
+]
+@end example
+Note that dynamic rules are constantly monitored for changes and are reloaded
+completely when modification is detected. If you change dynamic rules they
+would be reloaded in a minute and would be applied for new messages.
+
@subsection Conclusion.
Rspamd regexp module is powerfull tool for matching different patterns in
messages. You may use logical expressions of regexps and internal rspamd
pos = begin;
p = ip_buf;
+ if (*pos == '!') {
+ addr->negative = TRUE;
+ pos ++;
+ }
+ else {
+ addr->negative = FALSE;
+ }
+
while (*pos) {
switch (state) {
case 0:
json_error_t je;
char *cur_rule, *cur_symbol;
double score;
+ gboolean enabled;
struct regexp_module_item *cur_item;
GList *cur_networks = NULL;
struct dynamic_map_item *cur_nitem;
for (i = 0; i < nelts; i++) {
cur_networks = NULL;
cur_rule = NULL;
+ enabled = TRUE;
cur_elt = json_array_get (js, i);
if (!cur_elt || !json_is_object (cur_elt)) {
continue;
}
cur_symbol = memory_pool_strdup (new_pool, json_string_value (cur_nm));
+ /* Enabled flag */
+ cur_nm = json_object_get (cur_elt, "enabled");
+ if (cur_nm != NULL && json_is_boolean (cur_nm)) {
+ if (json_is_false (cur_nm)) {
+ msg_info ("rule %s is disabled in json", cur_symbol);
+ continue;
+ }
+ }
/* Now check other settings */
/* Rule */
cur_nm = json_object_get (cur_elt, "rule");
while (cur_opt) {
cur = cur_opt->data;
if (strcmp (cur->param, "metric") == 0 || strcmp (cur->param, "statfile_prefix") == 0) {
+ cur_opt = g_list_next (cur_opt);
continue;
}
else if (g_ascii_strncasecmp (cur->param, "autolearn", sizeof ("autolearn") - 1) == 0) {
parse_autolearn_param (cur->param, cur->value, cfg);
+ cur_opt = g_list_next (cur_opt);
+ continue;
+ }
+ else if (g_ascii_strncasecmp (cur->param, "dynamic_rules", sizeof ("dynamic_rules") - 1) == 0) {
+ cur_opt = g_list_next (cur_opt);
continue;
}
cur_item = memory_pool_alloc0 (regexp_module_ctx->regexp_pool, sizeof (struct regexp_module_item));
{
struct cache_item *item = NULL;
struct symbols_cache *pcache = *cache;
- GList **target, *t, *cur;
+ GList *t, *cur;
uintptr_t r;
uint32_t mask = 0xFFFFFFFF;
struct dynamic_map_item *it;
set_counter (item->s->symbol, 0);
if (networks == NULL) {
- target = &pcache->dynamic_items;
+ pcache->dynamic_items = g_list_prepend (pcache->dynamic_items, item);
}
else {
if (pcache->dynamic_map == NULL) {
pcache->dynamic_map = radix_tree_create ();
+ pcache->negative_dynamic_map = radix_tree_create ();
}
cur = networks;
while (cur) {
it = cur->data;
mask = mask << (32 - it->mask);
r = ntohl (it->addr.s_addr & mask);
- if ((r = radix32tree_find (pcache->dynamic_map, r)) != RADIX_NO_VALUE) {
- t = (GList *)((gpointer)r);
- target = &t;
+ if (it->negative) {
+ /* For negatve items insert into list and into negative cache map */
+ if ((r = radix32tree_find (pcache->negative_dynamic_map, r)) != RADIX_NO_VALUE) {
+ t = (GList *)((gpointer)r);
+ t = g_list_prepend (t, item);
+ /* Replace pointers in radix tree and in destructor function */
+ memory_pool_replace_destructor (dynamic_pool, (pool_destruct_func)g_list_free, (gpointer)r, t);
+ r = radix32tree_replace (pcache->negative_dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t);
+ if (r == -1) {
+ msg_warn ("cannot replace ip to tree: %s, mask %X", inet_ntoa (it->addr), mask);
+ }
+ }
+ else {
+ t = g_list_prepend (NULL, item);
+ memory_pool_add_destructor (dynamic_pool, (pool_destruct_func)g_list_free, t);
+ r = radix32tree_insert (pcache->negative_dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t);
+ if (r == -1) {
+ msg_warn ("cannot insert ip to tree: %s, mask %X", inet_ntoa (it->addr), mask);
+ }
+ else if (r == 1) {
+ msg_warn ("ip %s, mask %X, value already exists", inet_ntoa (it->addr), mask);
+ }
+ }
+ /* Insert into list */
+ pcache->dynamic_items = g_list_prepend (pcache->dynamic_items, item);
}
else {
- t = g_list_prepend (NULL, item);
- memory_pool_add_destructor (dynamic_pool, (pool_destruct_func)g_list_free, t);
- r = radix32tree_insert (pcache->dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t);
- if (r == -1) {
- msg_warn ("cannot insert ip to tree: %s, mask %X", inet_ntoa (it->addr), mask);
+ if ((r = radix32tree_find (pcache->dynamic_map, r)) != RADIX_NO_VALUE) {
+ t = (GList *)((gpointer)r);
+ t = g_list_prepend (t, item);
+ /* Replace pointers in radix tree and in destructor function */
+ memory_pool_replace_destructor (dynamic_pool, (pool_destruct_func)g_list_free, (gpointer)r, t);
+ r = radix32tree_replace (pcache->dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t);
+ if (r == -1) {
+ msg_warn ("cannot replace ip to tree: %s, mask %X", inet_ntoa (it->addr), mask);
+ }
}
- else if (r == 1) {
- msg_warn ("ip %s, mask %X, value already exists", inet_ntoa (it->addr), mask);
+ else {
+ t = g_list_prepend (NULL, item);
+ memory_pool_add_destructor (dynamic_pool, (pool_destruct_func)g_list_free, t);
+ r = radix32tree_insert (pcache->dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t);
+ if (r == -1) {
+ msg_warn ("cannot insert ip to tree: %s, mask %X", inet_ntoa (it->addr), mask);
+ }
+ else if (r == 1) {
+ msg_warn ("ip %s, mask %X, value already exists", inet_ntoa (it->addr), mask);
+ }
}
}
cur = g_list_next (cur);
}
}
- *target = g_list_prepend (*target, item);
}
void
if (cache->dynamic_map) {
radix_tree_free (cache->dynamic_map);
}
+ if (cache->negative_dynamic_map) {
+ radix_tree_free (cache->negative_dynamic_map);
+ }
}
static void
if (cache->dynamic_map) {
radix_tree_free (cache->dynamic_map);
}
+ if (cache->negative_dynamic_map) {
+ radix_tree_free (cache->negative_dynamic_map);
+ }
memory_pool_delete (cache->static_pool);
return res;
}
+static gboolean
+check_negative_dynamic_item (struct worker_task *task, struct symbols_cache *cache, struct cache_item *item)
+{
+ GList *res = NULL;
+ uintptr_t r;
+
+ if (cache->negative_dynamic_map != NULL && task->from_addr.s_addr != INADDR_NONE) {
+ if ((r = radix32tree_find (cache->negative_dynamic_map, ntohl (task->from_addr.s_addr))) != RADIX_NO_VALUE) {
+ res = (GList *)((gpointer)r);
+ while (res) {
+ if (res->data == (gpointer)item) {
+ return TRUE;
+ }
+ res = g_list_next (res);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
struct symbol_callback_data {
enum {
CACHE_STATE_NEGATIVE,
}
else {
s->saved_item = s->list_pointer->data;
+ /* Skip items that are in negative map */
+ while (s->list_pointer != NULL && check_negative_dynamic_item (task, cache, s->saved_item)) {
+ s->list_pointer = g_list_next (s->list_pointer);
+ if (s->list_pointer != NULL) {
+ s->saved_item = s->list_pointer->data;
+ }
+ }
+ if (s->list_pointer == NULL) {
+ s->state = CACHE_STATE_STATIC;
+ s->list_pointer = g_list_first (cache->static_items);
+ if (s->list_pointer) {
+ s->saved_item = s->list_pointer->data;
+ }
+ else {
+ return FALSE;
+ }
+ }
}
item = s->saved_item;
break;