diff options
-rw-r--r-- | CMakeLists.txt | 38 | ||||
-rw-r--r-- | config.h.in | 12 | ||||
-rw-r--r-- | debian/postinst | 2 | ||||
-rw-r--r-- | doc/markdown/workers/controller.md | 69 | ||||
-rw-r--r-- | doc/markdown/workers/lua_worker.md | 3 | ||||
-rw-r--r-- | doc/markdown/workers/normal.md | 29 | ||||
-rw-r--r-- | rspamd.service | 2 | ||||
-rw-r--r-- | rspamd.socket | 1 | ||||
-rw-r--r-- | src/fuzzy_storage.c | 46 | ||||
-rw-r--r-- | src/libserver/task.c | 2 | ||||
-rw-r--r-- | src/libstat/backends/sqlite3_backend.c | 21 | ||||
-rw-r--r-- | src/libstat/stat_process.c | 19 | ||||
-rw-r--r-- | src/libutil/printf.c | 35 | ||||
-rw-r--r-- | src/libutil/printf.h | 1 | ||||
-rw-r--r-- | src/libutil/sqlite_utils.c | 2 | ||||
-rw-r--r-- | src/rspamadm/fuzzy_stat.lua | 28 |
16 files changed, 252 insertions, 58 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index add82c152..263249f38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,11 @@ SET(RSPAMD_VERSION_MAJOR 1) SET(RSPAMD_VERSION_MINOR 1) SET(RSPAMD_VERSION_PATCH 0) +IF(GIT_ID) + SET(GIT_VERSION 1) + SET(RSPAMD_ID "${GIT_ID}") +ENDIF() + SET(RSPAMD_VERSION "${RSPAMD_VERSION_MAJOR}.${RSPAMD_VERSION_MINOR}.${RSPAMD_VERSION_PATCH}") SET(RSPAMD_MASTER_SITE_URL "https://rspamd.com") @@ -984,39 +989,6 @@ IF(NOT DESTDIR) SET(DESTDIR $ENV{DESTDIR}) ENDIF(NOT DESTDIR) -# Try to detect tip version from hg -SET(ID "unknown") -FIND_PROGRAM(HG "git") - -IF(HG) - EXECUTE_PROCESS(COMMAND "${HG}" rev-parse --verify HEAD - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE HG_ID ERROR_QUIET) - IF(HG_ID) - STRING(SUBSTRING "${HG_ID}" 0 10 RSPAMD_ID) - MESSAGE(STATUS "Git revision: ${RSPAMD_ID}") - ELSE(HG_ID) - FIND_FILE(HG_ARCH ".hg_archival.txt" "${CMAKE_CURRENT_SOURCE_DIR}") - IF(HG_ARCH) - FILE(READ ${HG_ARCH} HG_ID) - IF(HG_ID) - STRING(REGEX MATCH "node: [0-9a-z]+" _tmp "${HG_ID}") - STRING(SUBSTRING "${_tmp}" 6 12 RSPAMD_ID) - ENDIF(HG_ID) - ENDIF(HG_ARCH) - ENDIF(HG_ID) -ELSE(HG) -# Now check .hg_archival.txt - FIND_FILE(HG_ARCH ".hg_archival.txt" "${CMAKE_CURRENT_SOURCE_DIR}") - IF(HG_ARCH) - FILE(READ ${HG_ARCH} HG_ID) - IF(HG_ID) - STRING(REGEX MATCH "node: [0-9a-z]+" _tmp "${HG_ID}") - STRING(SUBSTRING "${_tmp}" 6 12 RSPAMD_ID) - ENDIF(HG_ID) - ENDIF(HG_ARCH) -ENDIF(HG) - ################################ SUBDIRS SECTION ########################### diff --git a/config.h.in b/config.h.in index 908278013..daa84f723 100644 --- a/config.h.in +++ b/config.h.in @@ -4,6 +4,7 @@ #cmakedefine BUILD_STATIC 1 #cmakedefine CURL_FOUND 1 #cmakedefine DEBUG_MODE 1 +#cmakedefine GIT_VERSION 1 #cmakedefine GLIB_COMPAT 1 #cmakedefine GLIB_HASH_COMPAT 1 #cmakedefine GLIB_RE_COMPAT 1 @@ -179,8 +180,15 @@ #define RSPAMD_WWWDIR "${WWWDIR}" #define RSPAMD_PREFIX "${CMAKE_INSTALL_PREFIX}" -#define RVERSION "${RSPAMD_VERSION}" -#define RID "${RSPAMD_ID}" + +#ifdef GIT_VERSION +# define RVERSION "${RSPAMD_VERSION}" +# define RID "${RSPAMD_ID}" +#else +# define RVERSION "${RSPAMD_VERSION}" +# define RID "release" +#endif + #define RSPAMD_MASTER_SITE_URL "${RSPAMD_MASTER_SITE_URL}" #define MODULES_NUM ${RSPAMD_MODULES_NUM} diff --git a/debian/postinst b/debian/postinst index 8d96fcb18..374209ce7 100644 --- a/debian/postinst +++ b/debian/postinst @@ -16,7 +16,7 @@ case "$1" in --gecos "rspamd spam filtering system" \ --force-badname \ $SERVER_USER - + mkdir -p $SERVER_HOME $SERVER_LOG || true chown $SERVER_USER: $SERVER_HOME $SERVER_LOG ;; diff --git a/doc/markdown/workers/controller.md b/doc/markdown/workers/controller.md index e69de29bb..94c12310a 100644 --- a/doc/markdown/workers/controller.md +++ b/doc/markdown/workers/controller.md @@ -0,0 +1,69 @@ +# Controller worker + +Controller worker is used to manage rspamd stats, to learn rspamd and to serve WebUI. + +Internally, the controller worker is just a web server that accepts requests and sends replies using JSON serialization. +Each command is defined by URL. Some commands are read only and are considered as `unprivileged` whilst other commands, such as +maps modification, config modifications and learning requires higher level of privileges: `enable` level. The differece between levels is specified +by password. If only one password is specified in the configuration, it is used for both type of commands. + +## Controller configuration + +Rspamd controller worker supports the following options: + +* `password`: password for read-only commands +* `enable_password`: password for write commands +* `secure_ip`: list or map with IP addresses that are treated as `secure` so **all** commands are allowed from these IPs **without** passwords +* `static_dir`: directory where interface static files are placed (usually `${WWWDIR}`) +* `stats_path`: path where controller save persistent stats about rspamd (such as scanned messages count) + +## Encryption support + +To generate a keypair for the scanner you could use: + + rspamadm keypair -u + +After that keypair should appear as following: + +~~~nginx +keypair { + pubkey = "tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry"; + privkey = "ykkrfqbyk34i1ewdmn81ttcco1eaxoqgih38duib1e7b89h9xn3y"; +} +~~~ + +You can use its **public** part thereafter when scanning messages as following: + + rspamc --key tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry <file> + +## Passwords encryption + +Rspamd now suggests to encrypt passwords when storing them in a configuration. Currently, it uses `PBKDF2-Blake2` function to derive key from a password. To encrypt key, you can use `rspamadm pw` command as following: + + rspamadm pw + Enter passphrase: <hidden input> + $1$cybjp37q4w63iogc4erncz1tgm1ce9i5$kxfx9xc1wk9uuakw7nittbt6dgf3qyqa394cnradg191iqgxr8kb + +You can use that line as `password` and `enable_password` values. + +## Supported commands + +* `/auth` +* `/symbols` +* `/actions` +* `/maps` +* `/getmap` +* `/graph` +* `/pie` +* `/history` +* `/historyreset` (priv) +* `/learnspam` (priv) +* `/learnham` (priv) +* `/saveactions` (priv) +* `/savesymbols` (priv) +* `/savemap` (priv) +* `/scan` +* `/check` +* `/stat` +* `/statreset` (priv) +* `/counters` diff --git a/doc/markdown/workers/lua_worker.md b/doc/markdown/workers/lua_worker.md index e69de29bb..cad1ad998 100644 --- a/doc/markdown/workers/lua_worker.md +++ b/doc/markdown/workers/lua_worker.md @@ -0,0 +1,3 @@ +# Lua worker + +TODO diff --git a/doc/markdown/workers/normal.md b/doc/markdown/workers/normal.md index e69de29bb..64cf4c479 100644 --- a/doc/markdown/workers/normal.md +++ b/doc/markdown/workers/normal.md @@ -0,0 +1,29 @@ +# Rspamd normal worker + +Rspamd normal worker is intended to scan messages for spam. It has the following configuration options available: + +* `mime`: turn to `off` if you want to scan non-mime messages (e.g. forum comments or SMS), default: `on` +* `allow_learn`: turn to `on` if you want to learn messages using this worker (usually you should use [controller](controller.md) worker), default: `off` +* `timeout`: input/output timeout, default: `1min` +* `task_timeout`: maximum time to process a single task, default: `8s` +* `max_tasks`: maximum count of tasks processes simultaneously, default: `0` - no limit +* `keypair`: encryption keypair + +## Encryption support + +To generate a keypair for the scanner you could use: + + rspamadm keypair -u + +After that keypair should appear as following: + +~~~nginx +keypair { + pubkey = "tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry"; + privkey = "ykkrfqbyk34i1ewdmn81ttcco1eaxoqgih38duib1e7b89h9xn3y"; +} +~~~ + +You can use its **public** part thereafter when scanning messages as following: + + rspamc --key tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry <file> diff --git a/rspamd.service b/rspamd.service index cc295f67c..c6ad4094c 100644 --- a/rspamd.service +++ b/rspamd.service @@ -5,6 +5,8 @@ After=nss-lookup.target [Service] ExecStart=/usr/bin/rspamd -c /etc/rspamd/rspamd.conf -f User=_rspamd +RuntimeDirectory=rspamd +RuntimeDirectoryMode=0755 [Install] WantedBy=multi-user.target diff --git a/rspamd.socket b/rspamd.socket index f3b3aa0a8..e28becce0 100644 --- a/rspamd.socket +++ b/rspamd.socket @@ -4,7 +4,6 @@ Description=rapid spam filtering system [Socket] ListenStream=11333 ListenStream=[::1]:11334 - BindIPv6Only=both [Install] diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c index 70cb717a9..c70f5a148 100644 --- a/src/fuzzy_storage.c +++ b/src/fuzzy_storage.c @@ -112,6 +112,7 @@ struct rspamd_fuzzy_storage_ctx { GHashTable *keys; gboolean encrypted_only; struct rspamd_keypair_cache *keypair_cache; + rspamd_lru_hash_t *errors_ips; struct rspamd_fuzzy_backend *backend; GQueue *updates_pending; }; @@ -677,6 +678,7 @@ accept_fuzzy_socket (gint fd, short what, void *arg) rspamd_inet_addr_t *addr; gssize r; guint8 buf[512]; + guint64 *nerrors; /* Got some data */ if (what == EV_READ) { @@ -721,6 +723,20 @@ accept_fuzzy_socket (gint fd, short what, void *arg) /* Discard input */ session->ctx->stat.invalid_requests ++; msg_debug ("invalid fuzzy command of size %z received", r); + + nerrors = rspamd_lru_hash_lookup (session->ctx->errors_ips, + addr, -1); + + if (nerrors == NULL) { + nerrors = g_malloc (sizeof (*nerrors)); + *nerrors = 1; + rspamd_lru_hash_insert (session->ctx->errors_ips, + rspamd_inet_address_copy (addr), + nerrors, -1, -1); + } + else { + *nerrors = *nerrors + 1; + } } REF_RELEASE (session); @@ -845,7 +861,7 @@ rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat key_stat = key->stat; if (key_stat) { - rspamd_snprintf (keyname, sizeof (keyname), "%8xs", k); + rspamd_snprintf (keyname, sizeof (keyname), "%8bs", k); elt = rspamd_fuzzy_storage_stat_key (key_stat); @@ -890,6 +906,29 @@ rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat 0, false); + if (ctx->errors_ips && ip_stat) { + ip_hash = rspamd_lru_hash_get_htable (ctx->errors_ips); + + if (ip_hash) { + g_hash_table_iter_init (&ip_it, ip_hash); + ip_elt = ucl_object_typed_new (UCL_OBJECT); + + while (g_hash_table_iter_next (&ip_it, &k, &v)) { + lru_elt = v; + + ucl_object_insert_key (ip_elt, + ucl_object_fromint (*(guint64 *)lru_elt->data), + rspamd_inet_address_to_string (k), 0, true); + } + + ucl_object_insert_key (obj, + ip_elt, + "errors_ips", + 0, + false); + } + } + /* Checked by epoch */ elt = ucl_object_typed_new (UCL_ARRAY); @@ -1093,6 +1132,9 @@ init_fuzzy (struct rspamd_config *cfg) ctx->keypair_cache_size = DEFAULT_KEYPAIR_CACHE_SIZE; ctx->keys = g_hash_table_new_full (fuzzy_kp_hash, fuzzy_kp_equal, NULL, fuzzy_key_dtor); + ctx->errors_ips = rspamd_lru_hash_new_full (0, 1024, + (GDestroyNotify) rspamd_inet_address_destroy, g_free, + rspamd_inet_address_hash, rspamd_inet_address_equal); rspamd_rcl_register_worker_option (cfg, type, "hashfile", rspamd_rcl_parse_struct_string, ctx, @@ -1302,6 +1344,8 @@ start_fuzzy (struct rspamd_worker *worker) rspamd_keypair_cache_destroy (ctx->keypair_cache); } + rspamd_lru_hash_destroy (ctx->errors_ips); + g_hash_table_unref (ctx->keys); exit (EXIT_SUCCESS); diff --git a/src/libserver/task.c b/src/libserver/task.c index 31370a6eb..758b90807 100644 --- a/src/libserver/task.c +++ b/src/libserver/task.c @@ -1029,7 +1029,7 @@ rspamd_task_write_log (struct rspamd_task *task) } } - msg_info ("%V", logbuf); + msg_info_task ("%V", logbuf); rspamd_fstring_free (logbuf); } diff --git a/src/libstat/backends/sqlite3_backend.c b/src/libstat/backends/sqlite3_backend.c index 47318d766..c980ddf54 100644 --- a/src/libstat/backends/sqlite3_backend.c +++ b/src/libstat/backends/sqlite3_backend.c @@ -122,7 +122,8 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .args = "", .stmt = NULL, .result = SQLITE_DONE, - .ret = "" + .flags = 0, + .ret = "", }, [RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF] = { .idx = RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF, @@ -130,6 +131,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .args = "", .stmt = NULL, .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_TRANSACTION_START_EXCL] = { @@ -138,6 +140,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .args = "", .stmt = NULL, .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_TRANSACTION_COMMIT] = { @@ -146,6 +149,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .args = "", .stmt = NULL, .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_TRANSACTION_ROLLBACK] = { @@ -154,6 +158,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .args = "", .stmt = NULL, .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_GET_TOKEN] = { @@ -166,6 +171,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "III", .result = SQLITE_ROW, + .flags = 0, .ret = "I" }, [RSPAMD_STAT_BACKEND_SET_TOKEN] = { @@ -175,6 +181,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "IIII", .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_INC_LEARNS] = { @@ -184,6 +191,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "II", .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_DEC_LEARNS] = { @@ -193,6 +201,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "II", .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_GET_LEARNS] = { @@ -201,6 +210,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "", .result = SQLITE_ROW, + .flags = 0, .ret = "I" }, [RSPAMD_STAT_BACKEND_GET_LANGUAGE] = { @@ -209,6 +219,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "T", .result = SQLITE_ROW, + .flags = 0, .ret = "I" }, [RSPAMD_STAT_BACKEND_GET_USER] = { @@ -217,6 +228,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "T", .result = SQLITE_ROW, + .flags = 0, .ret = "I" }, [RSPAMD_STAT_BACKEND_INSERT_USER] = { @@ -225,6 +237,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "T", .result = SQLITE_DONE, + .flags = 0, .ret = "L" }, [RSPAMD_STAT_BACKEND_INSERT_LANGUAGE] = { @@ -233,6 +246,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "T", .result = SQLITE_DONE, + .flags = 0, .ret = "L" }, [RSPAMD_STAT_BACKEND_SAVE_TOKENIZER] = { @@ -241,6 +255,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "B", .result = SQLITE_DONE, + .flags = 0, .ret = "" }, [RSPAMD_STAT_BACKEND_LOAD_TOKENIZER] = { @@ -249,6 +264,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "", .result = SQLITE_ROW, + .flags = 0, .ret = "B" }, [RSPAMD_STAT_BACKEND_NTOKENS] = { @@ -257,6 +273,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "", .result = SQLITE_ROW, + .flags = 0, .ret = "I" }, [RSPAMD_STAT_BACKEND_NLANGUAGES] = { @@ -265,6 +282,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "", .result = SQLITE_ROW, + .flags = 0, .ret = "I" }, [RSPAMD_STAT_BACKEND_NUSERS] = { @@ -273,6 +291,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = .stmt = NULL, .args = "", .result = SQLITE_ROW, + .flags = 0, .ret = "I" } }; diff --git a/src/libstat/stat_process.c b/src/libstat/stat_process.c index 419827aeb..57482974d 100644 --- a/src/libstat/stat_process.c +++ b/src/libstat/stat_process.c @@ -301,13 +301,8 @@ preprocess_init_stat_token (gpointer k, gpointer v, gpointer d) if (cl_runtime->clcf->min_tokens > 0 && (guint32)g_tree_nnodes (cbdata->tok->tokens) < cl_runtime->clcf->min_tokens) { /* Skip this classifier */ - msg_debug_task ("<%s> contains less tokens than required for %s classifier: " - "%ud < %ud", cbdata->task->message_id, cl_runtime->clcf->name, - g_tree_nnodes (cbdata->tok->tokens), - cl_runtime->clcf->min_tokens); cur = g_list_next (cur); cl_runtime->skipped = TRUE; - continue; } @@ -750,6 +745,16 @@ rspamd_stat_learn (struct rspamd_task *task, curst = g_list_next (curst); } + if (cl_run->skipped) { + msg_info_task ( + "<%s> contains less tokens than required for %s classifier: " + "%ud < %ud", + task->message_id, + cl_run->clcf->name, + g_tree_nnodes (cl_run->tok->tokens), + cl_run->clcf->min_tokens); + } + if (cl_run->cl && !cl_run->skipped) { cl_ctx = cl_run->cl->init_func (task->task_pool, cl_run->clcf); @@ -807,8 +812,8 @@ rspamd_stat_learn (struct rspamd_task *task, } if (!learned) { - g_set_error (err, rspamd_stat_quark (), 500, "message cannot be learned" - " for any classifier defined"); + g_set_error (err, rspamd_stat_quark (), 500, "message cannot be learned as " + "it has too few tokens for any classifier defined"); } else { g_atomic_int_inc (&task->worker->srv->stat->messages_learned); diff --git a/src/libutil/printf.c b/src/libutil/printf.c index 11c464be8..047532dd2 100644 --- a/src/libutil/printf.c +++ b/src/libutil/printf.c @@ -25,6 +25,7 @@ #include "printf.h" #include "fstring.h" +#include "str_util.h" #include <math.h> /** @@ -366,7 +367,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func, glong written = 0, wr, slen; gint64 i64; guint64 ui64; - guint width, sign, hex, humanize, bytes, frac_width, i; + guint width, sign, hex, humanize, bytes, frac_width, i, b32; rspamd_fstring_t *v; rspamd_ftok_t *tok; GString *gs; @@ -398,6 +399,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func, width = 0; sign = 1; hex = 0; + b32 = 0; bytes = 0; humanize = 0; frac_width = 0; @@ -431,6 +433,11 @@ rspamd_vprintf_common (rspamd_printf_append_func func, sign = 0; fmt++; continue; + case 'b': + b32 = 1; + sign = 0; + fmt++; + continue; case 'H': humanize = 1; bytes = 1; @@ -566,23 +573,35 @@ rspamd_vprintf_common (rspamd_printf_append_func func, slen = MIN (slen, width); } - if (G_LIKELY(hex == 0)) { - RSPAMD_PRINTF_APPEND (p, slen); + if (G_UNLIKELY (b32)) { + gchar *b32buf; + + b32buf = rspamd_encode_base32 (p, slen); + + if (b32buf) { + RSPAMD_PRINTF_APPEND (b32buf, strlen (b32buf)); + g_free (b32buf); + } } - else { + else if (G_UNLIKELY (hex)) { gchar hexbuf[2]; + while (slen) { hexbuf[0] = hex == 2 ? _HEX[*p & 0xf] : _hex[*p & 0xf]; hexbuf[1] = hex == 2 ? _HEX[(*p >> 8) & 0xf] : - _hex[(*p >> 8) & 0xf]; + _hex[(*p >> 8) & 0xf]; RSPAMD_PRINTF_APPEND_BUF (hexbuf, 2); - p ++; - slen --; + p++; + slen--; } - fmt ++; + fmt++; buf_start = fmt; } + else { + RSPAMD_PRINTF_APPEND (p, slen); + } + continue; diff --git a/src/libutil/printf.h b/src/libutil/printf.h index ea73cf0a0..f2c46ffc9 100644 --- a/src/libutil/printf.h +++ b/src/libutil/printf.h @@ -50,6 +50,7 @@ * %v GString * * %s null-terminated string * %xs hex encoded string + * %bs base32 encoded string * %*s length and string * %Z '\0' * %N '\n' diff --git a/src/libutil/sqlite_utils.c b/src/libutil/sqlite_utils.c index fd7287c7f..e1e6f7f76 100644 --- a/src/libutil/sqlite_utils.c +++ b/src/libutil/sqlite_utils.c @@ -172,6 +172,7 @@ rspamd_sqlite3_run_prstmt (rspamd_mempool_t *pool, sqlite3 *db, GArray *stmts, } if (!(nst->flags & RSPAMD_SQLITE3_STMT_MULTIPLE)) { + sqlite3_clear_bindings (stmt); sqlite3_reset (stmt); } @@ -183,6 +184,7 @@ rspamd_sqlite3_run_prstmt (rspamd_mempool_t *pool, sqlite3 *db, GArray *stmts, } if (!(nst->flags & RSPAMD_SQLITE3_STMT_MULTIPLE)) { + sqlite3_clear_bindings (stmt); sqlite3_reset (stmt); } diff --git a/src/rspamadm/fuzzy_stat.lua b/src/rspamadm/fuzzy_stat.lua index 3b56f9c1b..864e149a0 100644 --- a/src/rspamadm/fuzzy_stat.lua +++ b/src/rspamadm/fuzzy_stat.lua @@ -166,8 +166,20 @@ return function(args, res) -- General stats for k,v in pairs(pr['data']) do - if k ~= 'keys' then + if k ~= 'keys' and k ~= 'errors_ips' then res_db[k] = add_result(res_db[k], v, k) + elseif k == 'errors_ips' then + -- Errors ips + if not res_db['errors_ips'] then + res_db['errors_ips'] = {} + end + for ip,nerrors in pairs(v) do + if not res_db['errors_ips'][ip] then + res_db['errors_ips'][ip] = nerrors + else + res_db['errors_ips'][ip] = nerrors + res_db['errors_ips'][ip] + end + end end end @@ -206,7 +218,7 @@ return function(args, res) print(string.format('Statistics for storage %s', db)) for k,v in pairs(st) do - if k ~= 'keys' then + if k ~= 'keys' and k ~= 'errors_ips' then print(string.format('%s: %s', k, print_result(v))) end end @@ -234,7 +246,17 @@ return function(args, res) print('') end end - + if st['errors_ips'] and not opts['no-ips'] and not opts['short'] then + print('') + print('Errors IPs statistics:') + table.sort(st['errors_ips'], function(a, b) + return a > b + end) + for i, v in pairs(st['errors_ips']) do + print(string.format('%s: %s', i, print_result(v))) + end + print('') + end end if not opts['no-ips'] and not opts['short'] then |