diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libmime/content_type.c | 6 | ||||
-rw-r--r-- | src/libserver/cfg_file.h | 2 | ||||
-rw-r--r-- | src/libserver/cfg_rcl.c | 12 | ||||
-rw-r--r-- | src/libserver/cfg_utils.c | 12 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend_redis.c | 35 | ||||
-rw-r--r-- | src/libserver/redis_pool.cxx | 808 | ||||
-rw-r--r-- | src/libserver/redis_pool.h | 12 | ||||
-rw-r--r-- | src/libutil/upstream.c | 17 | ||||
-rw-r--r-- | src/libutil/upstream.h | 12 | ||||
-rw-r--r-- | src/plugins/lua/history_redis.lua | 6 | ||||
-rw-r--r-- | src/plugins/lua/mime_types.lua | 3 | ||||
-rw-r--r-- | src/ragel/content_disposition_parser.rl | 2 |
12 files changed, 505 insertions, 422 deletions
diff --git a/src/libmime/content_type.c b/src/libmime/content_type.c index 88102ad2c..40f0fd351 100644 --- a/src/libmime/content_type.c +++ b/src/libmime/content_type.c @@ -832,6 +832,12 @@ rspamd_content_disposition_parse (const gchar *in, struct rspamd_content_disposition *res = NULL, val; if (rspamd_content_disposition_parser (in, len, &val, pool)) { + + if (val.type == RSPAMD_CT_UNKNOWN) { + /* 'Fix' type to attachment as MUA does */ + val.type = RSPAMD_CT_ATTACHMENT; + } + res = rspamd_mempool_alloc (pool, sizeof (val)); memcpy (res, &val, sizeof (val)); res->lc_data = rspamd_mempool_alloc (pool, len + 1); diff --git a/src/libserver/cfg_file.h b/src/libserver/cfg_file.h index 745f0fb22..2aad328f7 100644 --- a/src/libserver/cfg_file.h +++ b/src/libserver/cfg_file.h @@ -363,7 +363,6 @@ struct rspamd_config { GHashTable *groups; /**< groups of symbols */ struct rspamd_action *actions; /**< all actions of the metric */ - gboolean raw_mode; /**< work in raw mode instead of utf one */ gboolean one_shot_mode; /**< rules add only one symbol */ gboolean check_text_attachements; /**< check text attachements as text */ gboolean check_all_filters; /**< check all filters */ @@ -480,7 +479,6 @@ struct rspamd_config { guint max_blas_threads; /**< maximum threads for openblas when learning ANN */ guint max_opts_len; /**< maximum length for all options for a symbol */ - GList *classify_headers; /**< list of headers using for statistics */ struct module_s **compiled_modules; /**< list of compiled C modules */ struct worker_s **compiled_workers; /**< list of compiled C modules */ struct rspamd_log_format *log_format; /**< parsed log format */ diff --git a/src/libserver/cfg_rcl.c b/src/libserver/cfg_rcl.c index e3c69c343..06b7edee2 100644 --- a/src/libserver/cfg_rcl.c +++ b/src/libserver/cfg_rcl.c @@ -1760,12 +1760,6 @@ rspamd_rcl_config_init (struct rspamd_config *cfg, GHashTable *skip_sections) RSPAMD_CL_FLAG_INT_32, "Maximum DNS requests per task (default: 64)"); rspamd_rcl_add_default_handler (sub, - "classify_headers", - rspamd_rcl_parse_struct_string_list, - G_STRUCT_OFFSET (struct rspamd_config, classify_headers), - 0, - "List of headers used for classifiers"); - rspamd_rcl_add_default_handler (sub, "control_socket", rspamd_rcl_parse_struct_string, G_STRUCT_OFFSET (struct rspamd_config, control_socket_path), @@ -1784,12 +1778,6 @@ rspamd_rcl_config_init (struct rspamd_config *cfg, GHashTable *skip_sections) 0, "Allow non MIME input for rspamd"); rspamd_rcl_add_default_handler (sub, - "raw_mode", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET (struct rspamd_config, raw_mode), - 0, - "Don't try to convert all messages to utf8"); - rspamd_rcl_add_default_handler (sub, "one_shot", rspamd_rcl_parse_struct_boolean, G_STRUCT_OFFSET (struct rspamd_config, one_shot_mode), diff --git a/src/libserver/cfg_utils.c b/src/libserver/cfg_utils.c index 8344ecc72..98b47efdc 100644 --- a/src/libserver/cfg_utils.c +++ b/src/libserver/cfg_utils.c @@ -334,14 +334,8 @@ rspamd_config_free (struct rspamd_config *cfg) g_hash_table_unref (cfg->trusted_keys); rspamd_re_cache_unref (cfg->re_cache); - rspamd_upstreams_library_unref (cfg->ups_ctx); g_ptr_array_free (cfg->c_modules, TRUE); - if (cfg->lua_state && cfg->own_lua_state) { - lua_thread_pool_free (cfg->lua_thread_pool); - lua_close (cfg->lua_state); - } - #ifdef WITH_HIREDIS if (cfg->redis_pool) { rspamd_redis_pool_destroy (cfg->redis_pool); @@ -352,6 +346,12 @@ rspamd_config_free (struct rspamd_config *cfg) rspamd_monitored_ctx_destroy (cfg->monitored_ctx); } + if (cfg->lua_state && cfg->own_lua_state) { + lua_thread_pool_free (cfg->lua_thread_pool); + lua_close (cfg->lua_state); + } + + rspamd_upstreams_library_unref (cfg->ups_ctx); HASH_CLEAR (hh, cfg->actions); rspamd_mempool_destructors_enforce (cfg->cfg_pool); diff --git a/src/libserver/fuzzy_backend/fuzzy_backend_redis.c b/src/libserver/fuzzy_backend/fuzzy_backend_redis.c index f16040d8f..18e049926 100644 --- a/src/libserver/fuzzy_backend/fuzzy_backend_redis.c +++ b/src/libserver/fuzzy_backend/fuzzy_backend_redis.c @@ -163,6 +163,7 @@ rspamd_fuzzy_redis_session_dtor (struct rspamd_fuzzy_redis_session *session, rspamd_fuzzy_redis_session_free_args (session); REF_RELEASE (session->backend); + rspamd_upstream_unref (session->up); g_free (session); } @@ -171,7 +172,7 @@ rspamd_fuzzy_backend_redis_dtor (struct rspamd_fuzzy_backend_redis *backend) { lua_State *L = backend->L; - if (backend->conf_ref) { + if (backend->conf_ref != -1) { luaL_unref (L, LUA_REGISTRYINDEX, backend->conf_ref); } @@ -341,7 +342,7 @@ rspamd_fuzzy_redis_shingles_callback (redisAsyncContext *c, gpointer r, ev_timer_stop (session->event_loop, &session->timeout); memset (&rep, 0, sizeof (rep)); - if (c->err == 0) { + if (c->err == 0 && reply != NULL) { rspamd_upstream_ok (session->up); if (reply->type == REDIS_REPLY_ARRAY && @@ -462,9 +463,8 @@ rspamd_fuzzy_redis_shingles_callback (redisAsyncContext *c, gpointer r, if (c->errstr) { msg_err_redis_session ("error getting shingles: %s", c->errstr); + rspamd_upstream_fail (session->up, FALSE, c->errstr); } - - rspamd_upstream_fail (session->up, FALSE, strerror (errno)); } rspamd_fuzzy_redis_session_dtor (session, FALSE); @@ -541,7 +541,7 @@ rspamd_fuzzy_redis_check_callback (redisAsyncContext *c, gpointer r, ev_timer_stop (session->event_loop, &session->timeout); memset (&rep, 0, sizeof (rep)); - if (c->err == 0) { + if (c->err == 0 && reply != NULL) { rspamd_upstream_ok (session->up); if (reply->type == REDIS_REPLY_ARRAY && reply->elements >= 2) { @@ -609,9 +609,8 @@ rspamd_fuzzy_redis_check_callback (redisAsyncContext *c, gpointer r, msg_err_redis_session ("error getting hashes on %s: %s", rspamd_inet_address_to_string_pretty (rspamd_upstream_addr_cur (session->up)), c->errstr); + rspamd_upstream_fail (session->up, FALSE, c->errstr); } - - rspamd_upstream_fail (session->up, FALSE, strerror (errno)); } rspamd_fuzzy_redis_session_dtor (session, FALSE); @@ -680,7 +679,7 @@ rspamd_fuzzy_backend_check_redis (struct rspamd_fuzzy_backend *bk, NULL, 0); - session->up = up; + session->up = rspamd_upstream_ref (up); addr = rspamd_upstream_addr_next (up); g_assert (addr != NULL); session->ctx = rspamd_redis_pool_connect (backend->pool, @@ -730,7 +729,7 @@ rspamd_fuzzy_redis_count_callback (redisAsyncContext *c, gpointer r, ev_timer_stop (session->event_loop, &session->timeout); - if (c->err == 0) { + if (c->err == 0 && reply != NULL) { rspamd_upstream_ok (session->up); if (reply->type == REDIS_REPLY_INTEGER) { @@ -764,9 +763,9 @@ rspamd_fuzzy_redis_count_callback (redisAsyncContext *c, gpointer r, msg_err_redis_session ("error getting count on %s: %s", rspamd_inet_address_to_string_pretty (rspamd_upstream_addr_cur (session->up)), c->errstr); + rspamd_upstream_fail (session->up, FALSE, c->errstr); } - rspamd_upstream_fail (session->up, FALSE, strerror (errno)); } rspamd_fuzzy_redis_session_dtor (session, FALSE); @@ -820,7 +819,7 @@ rspamd_fuzzy_backend_count_redis (struct rspamd_fuzzy_backend *bk, NULL, 0); - session->up = up; + session->up = rspamd_upstream_ref (up); addr = rspamd_upstream_addr_next (up); g_assert (addr != NULL); session->ctx = rspamd_redis_pool_connect (backend->pool, @@ -868,7 +867,7 @@ rspamd_fuzzy_redis_version_callback (redisAsyncContext *c, gpointer r, ev_timer_stop (session->event_loop, &session->timeout); - if (c->err == 0) { + if (c->err == 0 && reply != NULL) { rspamd_upstream_ok (session->up); if (reply->type == REDIS_REPLY_INTEGER) { @@ -902,9 +901,8 @@ rspamd_fuzzy_redis_version_callback (redisAsyncContext *c, gpointer r, msg_err_redis_session ("error getting version on %s: %s", rspamd_inet_address_to_string_pretty (rspamd_upstream_addr_cur (session->up)), c->errstr); + rspamd_upstream_fail (session->up, FALSE, c->errstr); } - - rspamd_upstream_fail (session->up, FALSE, strerror (errno)); } rspamd_fuzzy_redis_session_dtor (session, FALSE); @@ -959,7 +957,7 @@ rspamd_fuzzy_backend_version_redis (struct rspamd_fuzzy_backend *bk, NULL, 0); - session->up = up; + session->up = rspamd_upstream_ref (up); addr = rspamd_upstream_addr_next (up); g_assert (addr != NULL); session->ctx = rspamd_redis_pool_connect (backend->pool, @@ -1374,7 +1372,7 @@ rspamd_fuzzy_redis_update_callback (redisAsyncContext *c, gpointer r, ev_timer_stop (session->event_loop, &session->timeout); - if (c->err == 0) { + if (c->err == 0 && reply != NULL) { rspamd_upstream_ok (session->up); if (reply->type == REDIS_REPLY_ARRAY) { @@ -1407,9 +1405,8 @@ rspamd_fuzzy_redis_update_callback (redisAsyncContext *c, gpointer r, msg_err_redis_session ("error sending update to redis %s: %s", rspamd_inet_address_to_string_pretty (rspamd_upstream_addr_cur (session->up)), c->errstr); + rspamd_upstream_fail (session->up, FALSE, c->errstr); } - - rspamd_upstream_fail (session->up, FALSE, strerror (errno)); } rspamd_fuzzy_redis_session_dtor (session, FALSE); @@ -1534,7 +1531,7 @@ rspamd_fuzzy_backend_update_redis (struct rspamd_fuzzy_backend *bk, NULL, 0); - session->up = up; + session->up = rspamd_upstream_ref (up); addr = rspamd_upstream_addr_next (up); g_assert (addr != NULL); session->ctx = rspamd_redis_pool_connect (backend->pool, diff --git a/src/libserver/redis_pool.cxx b/src/libserver/redis_pool.cxx index a81039d6a..8a460b7fe 100644 --- a/src/libserver/redis_pool.cxx +++ b/src/libserver/redis_pool.cxx @@ -24,7 +24,32 @@ #include "cryptobox.h" #include "logger.h" -struct rspamd_redis_pool_elt; +#include <list> +#include "contrib/robin-hood/robin_hood.h" +#include "libutil/cxx/local_shared_ptr.hxx" + +namespace rspamd { +class redis_pool_elt; +class redis_pool; + +#define msg_err_rpool(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + "redis_pool", conn->tag, \ + __FUNCTION__, \ + __VA_ARGS__) +#define msg_warn_rpool(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + "redis_pool", conn->tag, \ + __FUNCTION__, \ + __VA_ARGS__) +#define msg_info_rpool(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + "redis_pool", conn->tag, \ + __FUNCTION__, \ + __VA_ARGS__) +#define msg_debug_rpool(...) rspamd_conditional_debug_fast (NULL, NULL, \ + rspamd_redis_pool_log_id, "redis_pool", conn->tag, \ + __FUNCTION__, \ + __VA_ARGS__) + +INIT_LOG_MODULE(redis_pool) enum rspamd_redis_pool_connection_state { RSPAMD_REDIS_POOL_CONN_INACTIVE = 0, @@ -32,158 +57,248 @@ enum rspamd_redis_pool_connection_state { RSPAMD_REDIS_POOL_CONN_FINALISING }; -struct rspamd_redis_pool_connection { +struct redis_pool_connection { + using redis_pool_connection_ptr = std::unique_ptr<redis_pool_connection>; + using conn_iter_t = std::list<redis_pool_connection_ptr>::iterator; struct redisAsyncContext *ctx; - struct rspamd_redis_pool_elt *elt; - GList *entry; + redis_pool_elt *elt; + redis_pool *pool; + conn_iter_t elt_pos; ev_timer timeout; enum rspamd_redis_pool_connection_state state; gchar tag[MEMPOOL_UID_LEN]; - ref_entry_t ref; -}; -struct rspamd_redis_pool_elt { - struct rspamd_redis_pool *pool; - guint64 key; - GQueue *active; - GQueue *inactive; -}; + auto schedule_timeout() -> void; + ~redis_pool_connection(); -struct rspamd_redis_pool { - struct ev_loop *event_loop; - struct rspamd_config *cfg; - GHashTable *elts_by_key; - GHashTable *elts_by_ctx; - gdouble timeout; - guint max_conns; + explicit redis_pool_connection(redis_pool *_pool, + redis_pool_elt *_elt, + const std::string &db, + const std::string &password, + struct redisAsyncContext *_ctx); + +private: + static auto redis_conn_timeout_cb(EV_P_ ev_timer *w, int revents) -> void; + static auto redis_quit_cb(redisAsyncContext *c, void *r, void *priv) -> void; + static auto redis_on_disconnect(const struct redisAsyncContext *ac, int status) -> auto; }; -static const gdouble default_timeout = 10.0; -static const guint default_max_conns = 100; -#define msg_err_rpool(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ - "redis_pool", conn->tag, \ - G_STRFUNC, \ - __VA_ARGS__) -#define msg_warn_rpool(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ - "redis_pool", conn->tag, \ - G_STRFUNC, \ - __VA_ARGS__) -#define msg_info_rpool(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ - "redis_pool", conn->tag, \ - G_STRFUNC, \ - __VA_ARGS__) -#define msg_debug_rpool(...) rspamd_conditional_debug_fast (NULL, NULL, \ - rspamd_redis_pool_log_id, "redis_pool", conn->tag, \ - G_STRFUNC, \ - __VA_ARGS__) - -INIT_LOG_MODULE(redis_pool) +using redis_pool_key_t = std::uint64_t; +class redis_pool; -static inline guint64 -rspamd_redis_pool_get_key (const gchar *db, const gchar *password, - const char *ip, int port) -{ - rspamd_cryptobox_fast_hash_state_t st; +class redis_pool_elt { + using redis_pool_connection_ptr = std::unique_ptr<redis_pool_connection>; + redis_pool *pool; + /* + * These lists owns connections, so if an element is removed from both + * lists, it is destructed + */ + std::list<redis_pool_connection_ptr> active; + std::list<redis_pool_connection_ptr> inactive; + std::list<redis_pool_connection_ptr> terminating; + std::string ip; + std::string db; + std::string password; + int port; + redis_pool_key_t key; + bool is_unix; +public: + explicit redis_pool_elt(redis_pool *_pool, + const gchar *_db, const gchar *_password, + const char *_ip, int _port) + : pool(_pool), ip(_ip), port(_port), + key(redis_pool_elt::make_key(_db, _password, _ip, _port)) + { + is_unix = ip[0] == '.' || ip[0] == '/'; + + if (_db) { + db = _db; + } + if (_password) { + password = _password; + } + } - rspamd_cryptobox_fast_hash_init (&st, rspamd_hash_seed ()); + auto new_connection() -> redisAsyncContext *; + + auto release_connection(const redis_pool_connection *conn) -> void + { + switch(conn->state) { + case RSPAMD_REDIS_POOL_CONN_ACTIVE: + active.erase(conn->elt_pos); + break; + case RSPAMD_REDIS_POOL_CONN_INACTIVE: + inactive.erase(conn->elt_pos); + break; + case RSPAMD_REDIS_POOL_CONN_FINALISING: + terminating.erase(conn->elt_pos); + break; + } + } - if (db) { - rspamd_cryptobox_fast_hash_update (&st, db, strlen (db)); + auto move_to_inactive(redis_pool_connection *conn) -> void + { + inactive.splice(std::end(inactive), active, conn->elt_pos); + conn->elt_pos = std::prev(std::end(inactive)); } - if (password) { - rspamd_cryptobox_fast_hash_update (&st, password, strlen (password)); + + auto move_to_terminating(redis_pool_connection *conn) -> void + { + terminating.splice(std::end(terminating), terminating, conn->elt_pos); + conn->elt_pos = std::prev(std::end(terminating)); } - rspamd_cryptobox_fast_hash_update (&st, ip, strlen (ip)); - rspamd_cryptobox_fast_hash_update (&st, &port, sizeof (port)); + inline static auto make_key(const gchar *db, const gchar *password, + const char *ip, int port) -> redis_pool_key_t + { + rspamd_cryptobox_fast_hash_state_t st; - return rspamd_cryptobox_fast_hash_final (&st); -} + rspamd_cryptobox_fast_hash_init(&st, rspamd_hash_seed()); + if (db) { + rspamd_cryptobox_fast_hash_update(&st, db, strlen(db)); + } + if (password) { + rspamd_cryptobox_fast_hash_update(&st, password, strlen(password)); + } -static void -rspamd_redis_pool_conn_dtor (struct rspamd_redis_pool_connection *conn) -{ - if (conn->state == RSPAMD_REDIS_POOL_CONN_ACTIVE) { - msg_debug_rpool ("active connection removed"); + rspamd_cryptobox_fast_hash_update(&st, ip, strlen(ip)); + rspamd_cryptobox_fast_hash_update(&st, &port, sizeof(port)); - if (conn->ctx) { - if (!(conn->ctx->c.flags & REDIS_FREEING)) { - redisAsyncContext *ac = conn->ctx; + return rspamd_cryptobox_fast_hash_final(&st); + } - conn->ctx = NULL; - g_hash_table_remove (conn->elt->pool->elts_by_ctx, ac); - ac->onDisconnect = NULL; - redisAsyncFree (ac); - } + auto num_active() const -> auto + { + return active.size(); + } + + ~redis_pool_elt() { + rspamd_explicit_memzero(password.data(), password.size()); + } + +private: + auto redis_async_new() -> redisAsyncContext * + { + struct redisAsyncContext *ctx; + + if (is_unix) { + ctx = redisAsyncConnectUnix(ip.c_str()); + } + else { + ctx = redisAsyncConnect(ip.c_str(), port); } - if (conn->entry) { - g_queue_unlink (conn->elt->active, conn->entry); + if (ctx && ctx->err != REDIS_OK) { + msg_err("cannot connect to redis %s (port %d): %s", ip.c_str(), port, + ctx->errstr); + redisAsyncFree(ctx); + + return nullptr; } + + return ctx; } - else { - msg_debug_rpool ("inactive connection removed"); +}; - ev_timer_stop (conn->elt->pool->event_loop, &conn->timeout); +class redis_pool final { + static constexpr const double default_timeout = 10.0; + static constexpr const unsigned default_max_conns = 100; + + /* We want to have references integrity */ + robin_hood::unordered_flat_map<redisAsyncContext *, + redis_pool_connection *> conns_by_ctx; + robin_hood::unordered_node_map<redis_pool_key_t, redis_pool_elt> elts_by_key; + bool wanna_die = false; /* Hiredis is 'clever' so we can call ourselves from destructor */ +public: + double timeout = default_timeout; + unsigned max_conns = default_max_conns; + struct ev_loop *event_loop; + struct rspamd_config *cfg; - if (conn->ctx && !(conn->ctx->c.flags & REDIS_FREEING)) { - redisAsyncContext *ac = conn->ctx; +public: + explicit redis_pool() : event_loop(nullptr), cfg(nullptr) + { + conns_by_ctx.reserve(max_conns); + } - /* To prevent on_disconnect here */ - conn->state = RSPAMD_REDIS_POOL_CONN_FINALISING; - g_hash_table_remove (conn->elt->pool->elts_by_ctx, ac); - conn->ctx = NULL; - ac->onDisconnect = NULL; - redisAsyncFree (ac); - } + /* Legacy stuff */ + auto do_config(struct ev_loop *_loop, struct rspamd_config *_cfg) -> void + { + event_loop = _loop; + cfg = _cfg; + } - if (conn->entry) { - g_queue_unlink (conn->elt->inactive, conn->entry); - } + auto new_connection(const gchar *db, const gchar *password, + const char *ip, int port) -> redisAsyncContext *; + + auto release_connection(redisAsyncContext *ctx, + enum rspamd_redis_pool_release_type how) -> void; + + auto unregister_context(redisAsyncContext *ctx) -> void + { + conns_by_ctx.erase(ctx); } + auto register_context(redisAsyncContext *ctx, redis_pool_connection *conn) + { + conns_by_ctx.emplace(ctx, conn); + } - if (conn->entry) { - g_list_free (conn->entry); + ~redis_pool() { + /* + * XXX: this will prevent hiredis to unregister connections that + * are already destroyed during redisAsyncFree... + */ + wanna_die = true; } +}; - g_free (conn); -} -static void -rspamd_redis_pool_elt_dtor (gpointer p) +redis_pool_connection::~redis_pool_connection() { - GList *cur; - struct rspamd_redis_pool_elt *elt = (struct rspamd_redis_pool_elt *)p; - struct rspamd_redis_pool_connection *c; - - for (cur = elt->active->head; cur != NULL; cur = g_list_next (cur)) { - c = (struct rspamd_redis_pool_connection *)cur->data; - c->entry = NULL; - REF_RELEASE (c); + const auto *conn = this; /* For debug */ + + if (state == RSPAMD_REDIS_POOL_CONN_ACTIVE) { + msg_debug_rpool ("active connection destructed"); + + if (ctx) { + if (!(ctx->c.flags & REDIS_FREEING)) { + auto *ac = ctx; + ctx = nullptr; + pool->unregister_context(ac); + ac->onDisconnect = nullptr; + redisAsyncFree(ac); + } + } } + else { + msg_debug_rpool("inactive connection destructed"); - for (cur = elt->inactive->head; cur != NULL; cur = g_list_next (cur)) { - c = (struct rspamd_redis_pool_connection *)cur->data; - c->entry = NULL; - REF_RELEASE (c); - } + ev_timer_stop(pool->event_loop, &timeout); + + if (ctx && !(ctx->c.flags & REDIS_FREEING)) { + redisAsyncContext *ac = ctx; - g_queue_free (elt->active); - g_queue_free (elt->inactive); - g_free (elt); + /* To prevent on_disconnect here */ + state = RSPAMD_REDIS_POOL_CONN_FINALISING; + pool->unregister_context(ac); + ctx = nullptr; + ac->onDisconnect = nullptr; + redisAsyncFree(ac); + } + } } -static void -rspamd_redis_on_quit (redisAsyncContext *c, gpointer r, gpointer priv) +auto +redis_pool_connection::redis_quit_cb(redisAsyncContext *c, void *r, void *priv) -> void { - struct rspamd_redis_pool_connection *conn = - (struct rspamd_redis_pool_connection *)priv; + struct redis_pool_connection *conn = + (struct redis_pool_connection *) priv; - msg_debug_rpool ("quit command reply for the connection %p, refcount: %d", - conn->ctx, conn->ref.refcount); + msg_debug_rpool("quit command reply for the connection %p", + conn->ctx); /* * The connection will be freed by hiredis itself as we are here merely after * quit command has succeeded and we have timer being set already. @@ -199,68 +314,40 @@ rspamd_redis_on_quit (redisAsyncContext *c, gpointer r, gpointer priv) */ } -static void -rspamd_redis_conn_timeout (EV_P_ ev_timer *w, int revents) +/* + * Called for inactive connections that due to be removed + */ +auto +redis_pool_connection::redis_conn_timeout_cb(EV_P_ ev_timer *w, int revents) -> void { - struct rspamd_redis_pool_connection *conn = - (struct rspamd_redis_pool_connection *)w->data; + auto *conn = (struct redis_pool_connection *) w->data; g_assert (conn->state != RSPAMD_REDIS_POOL_CONN_ACTIVE); if (conn->state == RSPAMD_REDIS_POOL_CONN_INACTIVE) { - msg_debug_rpool ("scheduled soft removal of connection %p, refcount: %d", - conn->ctx, conn->ref.refcount); - /* Prevent reusing */ - if (conn->entry) { - g_queue_delete_link (conn->elt->inactive, conn->entry); - conn->entry = NULL; - } - + msg_debug_rpool("scheduled soft removal of connection %p", + conn->ctx); conn->state = RSPAMD_REDIS_POOL_CONN_FINALISING; - ev_timer_again (EV_A_ w); - redisAsyncCommand (conn->ctx, rspamd_redis_on_quit, conn, "QUIT"); + ev_timer_again(EV_A_ w); + redisAsyncCommand(conn->ctx, redis_pool_connection::redis_quit_cb, conn, "QUIT"); + conn->elt->move_to_terminating(conn); } else { /* Finalising by timeout */ - ev_timer_stop (EV_A_ w); - msg_debug_rpool ("final removal of connection %p, refcount: %d", - conn->ctx, conn->ref.refcount); - REF_RELEASE (conn); - } - -} - -static void -rspamd_redis_pool_schedule_timeout (struct rspamd_redis_pool_connection *conn) -{ - gdouble real_timeout; - guint active_elts; + ev_timer_stop(EV_A_ w); + msg_debug_rpool("final removal of connection %p, refcount: %d", + conn->ctx); - active_elts = g_queue_get_length (conn->elt->active); - - if (active_elts > conn->elt->pool->max_conns) { - real_timeout = conn->elt->pool->timeout / 2.0; - real_timeout = rspamd_time_jitter (real_timeout, real_timeout / 4.0); - } - else { - real_timeout = conn->elt->pool->timeout; - real_timeout = rspamd_time_jitter (real_timeout, real_timeout / 2.0); + /* Erasure of shared pointer will cause it to be removed */ + conn->elt->release_connection(conn); } - msg_debug_rpool ("scheduled connection %p cleanup in %.1f seconds", - conn->ctx, real_timeout); - - conn->timeout.data = conn; - ev_timer_init (&conn->timeout, - rspamd_redis_conn_timeout, - real_timeout, real_timeout / 2.0); - ev_timer_start (conn->elt->pool->event_loop, &conn->timeout); } -static void -rspamd_redis_pool_on_disconnect (const struct redisAsyncContext *ac, int status) +auto +redis_pool_connection::redis_on_disconnect(const struct redisAsyncContext *ac, int status) -> auto { - struct rspamd_redis_pool_connection *conn = (struct rspamd_redis_pool_connection *)ac->data; + auto *conn = (struct redis_pool_connection *) ac->data; /* * Here, we know that redis itself will free this connection @@ -269,279 +356,264 @@ rspamd_redis_pool_on_disconnect (const struct redisAsyncContext *ac, int status) if (conn->state != RSPAMD_REDIS_POOL_CONN_ACTIVE) { /* Do nothing for active connections as it is already handled somewhere */ if (conn->ctx) { - msg_debug_rpool ("inactive connection terminated: %s, refs: %d", - conn->ctx->errstr, conn->ref.refcount); + msg_debug_rpool("inactive connection terminated: %s", + conn->ctx->errstr); } - REF_RELEASE (conn); + /* Erasure of shared pointer will cause it to be removed */ + conn->elt->release_connection(conn); } } -static struct rspamd_redis_pool_connection * -rspamd_redis_pool_new_connection (struct rspamd_redis_pool *pool, - struct rspamd_redis_pool_elt *elt, - const char *db, - const char *password, - const char *ip, - gint port) +auto +redis_pool_connection::schedule_timeout() -> void { - struct rspamd_redis_pool_connection *conn; - struct redisAsyncContext *ctx; + const auto *conn = this; /* For debug */ + double real_timeout; + auto active_elts = elt->num_active(); - if (*ip == '/' || *ip == '.') { - ctx = redisAsyncConnectUnix (ip); + if (active_elts > pool->max_conns) { + real_timeout = pool->timeout / 2.0; + real_timeout = rspamd_time_jitter(real_timeout, real_timeout / 4.0); } else { - ctx = redisAsyncConnect (ip, port); + real_timeout = pool->timeout; + real_timeout = rspamd_time_jitter(real_timeout, real_timeout / 2.0); } - if (ctx) { + msg_debug_rpool("scheduled connection %p cleanup in %.1f seconds", + ctx, real_timeout); - if (ctx->err != REDIS_OK) { - msg_err ("cannot connect to redis %s (port %d): %s", ip, port, ctx->errstr); - redisAsyncFree (ctx); - - return NULL; - } - else { - conn = (struct rspamd_redis_pool_connection *)g_malloc0 (sizeof (*conn)); - conn->entry = g_list_prepend (NULL, conn); - conn->elt = elt; - conn->state = RSPAMD_REDIS_POOL_CONN_ACTIVE; - - g_hash_table_insert (elt->pool->elts_by_ctx, ctx, conn); - g_queue_push_head_link (elt->active, conn->entry); - conn->ctx = ctx; - ctx->data = conn; - rspamd_random_hex ((guchar *)conn->tag, sizeof (conn->tag)); - REF_INIT_RETAIN (conn, rspamd_redis_pool_conn_dtor); - msg_debug_rpool ("created new connection to %s:%d: %p", ip, port, ctx); - - redisLibevAttach (pool->event_loop, ctx); - redisAsyncSetDisconnectCallback (ctx, rspamd_redis_pool_on_disconnect); - - if (password) { - redisAsyncCommand (ctx, NULL, NULL, - "AUTH %s", password); - } - if (db) { - redisAsyncCommand (ctx, NULL, NULL, - "SELECT %s", db); - } - } - - return conn; - } - - return NULL; + timeout.data = this; + ev_timer_init(&timeout, + redis_pool_connection::redis_conn_timeout_cb, + real_timeout, real_timeout / 2.0); + ev_timer_start(pool->event_loop, &timeout); } -static struct rspamd_redis_pool_elt * -rspamd_redis_pool_new_elt (struct rspamd_redis_pool *pool) -{ - struct rspamd_redis_pool_elt *elt; - - elt = (struct rspamd_redis_pool_elt *)g_malloc0 (sizeof (*elt)); - elt->active = g_queue_new (); - elt->inactive = g_queue_new (); - elt->pool = pool; - return elt; -} - -struct rspamd_redis_pool * -rspamd_redis_pool_init (void) +redis_pool_connection::redis_pool_connection(redis_pool *_pool, + redis_pool_elt *_elt, + const std::string &db, + const std::string &password, + struct redisAsyncContext *_ctx) + : ctx(_ctx), elt(_elt), pool(_pool) { - struct rspamd_redis_pool *pool; - pool = (struct rspamd_redis_pool *)g_malloc0 (sizeof (*pool)); - pool->elts_by_key = g_hash_table_new_full (g_int64_hash, g_int64_equal, - NULL, rspamd_redis_pool_elt_dtor); - pool->elts_by_ctx = g_hash_table_new (g_direct_hash, g_direct_equal); + state = RSPAMD_REDIS_POOL_CONN_ACTIVE; - return pool; -} + pool->register_context(ctx, this); + ctx->data = this; + memset(tag, 0, sizeof(tag)); + rspamd_random_hex((guchar *)tag, sizeof(tag) - 1); -void -rspamd_redis_pool_config (struct rspamd_redis_pool *pool, - struct rspamd_config *cfg, - struct ev_loop *ev_base) -{ - g_assert (pool != NULL); + redisLibevAttach(pool->event_loop, ctx); + redisAsyncSetDisconnectCallback(ctx, redis_pool_connection::redis_on_disconnect); - pool->event_loop = ev_base; - pool->cfg = cfg; - pool->timeout = default_timeout; - pool->max_conns = default_max_conns; + if (!password.empty()) { + redisAsyncCommand(ctx, nullptr, nullptr, + "AUTH %s", password.c_str()); + } + if (!db.empty()) { + redisAsyncCommand(ctx, nullptr, nullptr, + "SELECT %s", db.c_str()); + } } - -struct redisAsyncContext* -rspamd_redis_pool_connect (struct rspamd_redis_pool *pool, - const gchar *db, const gchar *password, - const char *ip, int port) +auto +redis_pool_elt::new_connection() -> redisAsyncContext * { - guint64 key; - struct rspamd_redis_pool_elt *elt; - GList *conn_entry; - struct rspamd_redis_pool_connection *conn; - - g_assert (pool != NULL); - g_assert (pool->event_loop != NULL); - g_assert (ip != NULL); - - key = rspamd_redis_pool_get_key (db, password, ip, port); - elt = (struct rspamd_redis_pool_elt *)g_hash_table_lookup (pool->elts_by_key, &key); - - if (elt) { - if (g_queue_get_length (elt->inactive) > 0) { - conn_entry = g_queue_pop_head_link (elt->inactive); - conn = (struct rspamd_redis_pool_connection *)conn_entry->data; - g_assert (conn->state != RSPAMD_REDIS_POOL_CONN_ACTIVE); - - if (conn->ctx->err == REDIS_OK) { - /* Also check SO_ERROR */ - gint err; - socklen_t len = sizeof (gint); - - if (getsockopt (conn->ctx->c.fd, SOL_SOCKET, SO_ERROR, - (void *) &err, &len) == -1) { - err = errno; - } - - if (err != 0) { - g_list_free (conn->entry); - conn->entry = NULL; - REF_RELEASE (conn); - conn = rspamd_redis_pool_new_connection (pool, elt, - db, password, ip, port); - } - else { + if (!inactive.empty()) { + decltype(inactive)::value_type conn; + conn.swap(inactive.back()); + inactive.pop_back(); + + g_assert (conn->state != RSPAMD_REDIS_POOL_CONN_ACTIVE); + if (conn->ctx->err == REDIS_OK) { + /* Also check SO_ERROR */ + gint err; + socklen_t len = sizeof(gint); + + if (getsockopt(conn->ctx->c.fd, SOL_SOCKET, SO_ERROR, + (void *) &err, &len) == -1) { + err = errno; + } - ev_timer_stop (elt->pool->event_loop, &conn->timeout); - conn->state = RSPAMD_REDIS_POOL_CONN_ACTIVE; - g_queue_push_tail_link (elt->active, conn_entry); - msg_debug_rpool ("reused existing connection to %s:%d: %p", - ip, port, conn->ctx); - } + if (err != 0) { + /* + * We cannot reuse connection, so we just recursively call + * this function one more time + */ + return new_connection(); } else { - g_list_free (conn->entry); - conn->entry = NULL; - REF_RELEASE (conn); - conn = rspamd_redis_pool_new_connection (pool, elt, - db, password, ip, port); + /* Reuse connection */ + ev_timer_stop(pool->event_loop, &conn->timeout); + conn->state = RSPAMD_REDIS_POOL_CONN_ACTIVE; + msg_debug_rpool("reused existing connection to %s:%d: %p", + ip.c_str(), port, conn->ctx); + active.emplace_front(std::move(conn)); + active.front()->elt_pos = active.begin(); + + return active.front()->ctx; } - } else { - /* Need to create connection */ - conn = rspamd_redis_pool_new_connection (pool, elt, - db, password, ip, port); + auto *nctx = redis_async_new(); + if (nctx) { + active.emplace_front(std::make_unique<redis_pool_connection>(pool, this, + db.c_str(), password.c_str(), nctx)); + active.front()->elt_pos = active.begin(); + } + + return nctx; } } else { - /* Need to create a pool */ - elt = rspamd_redis_pool_new_elt (pool); - elt->key = key; - g_hash_table_insert (pool->elts_by_key, &elt->key, elt); - - conn = rspamd_redis_pool_new_connection (pool, elt, - db, password, ip, port); - } + auto *nctx = redis_async_new(); + if (nctx) { + active.emplace_front(std::make_unique<redis_pool_connection>(pool, this, + db.c_str(), password.c_str(), nctx)); + active.front()->elt_pos = active.begin(); + } - if (!conn) { - return NULL; + return nctx; } - REF_RETAIN (conn); - - return conn->ctx; + RSPAMD_UNREACHABLE; } - -void -rspamd_redis_pool_release_connection (struct rspamd_redis_pool *pool, - struct redisAsyncContext *ctx, enum rspamd_redis_pool_release_type how) +auto +redis_pool::new_connection(const gchar *db, const gchar *password, + const char *ip, int port) -> redisAsyncContext * { - struct rspamd_redis_pool_connection *conn; - g_assert (pool != NULL); - g_assert (ctx != NULL); + if (!wanna_die) { + auto key = redis_pool_elt::make_key(db, password, ip, port); + auto found_elt = elts_by_key.find(key); - conn = (struct rspamd_redis_pool_connection *)g_hash_table_lookup (pool->elts_by_ctx, ctx); - if (conn != NULL) { - g_assert (conn->state == RSPAMD_REDIS_POOL_CONN_ACTIVE); + if (found_elt != elts_by_key.end()) { + auto &elt = found_elt->second; - if (ctx->err != REDIS_OK) { - /* We need to terminate connection forcefully */ - msg_debug_rpool ("closed connection %p due to an error", conn->ctx); - REF_RELEASE (conn); + return elt.new_connection(); } else { - if (how == RSPAMD_REDIS_RELEASE_DEFAULT) { - /* Ensure that there are no callbacks attached to this conn */ - if (ctx->replies.head == NULL) { - /* Just move it to the inactive queue */ - g_queue_unlink (conn->elt->active, conn->entry); - g_queue_push_head_link (conn->elt->inactive, conn->entry); - conn->state = RSPAMD_REDIS_POOL_CONN_INACTIVE; - rspamd_redis_pool_schedule_timeout (conn); - msg_debug_rpool ("mark connection %p inactive", conn->ctx); - } - else { - msg_debug_rpool ("closed connection %p due to callbacks left", - conn->ctx); - REF_RELEASE (conn); - } + /* Need to create a pool */ + auto nelt = elts_by_key.emplace(std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(this, db, password, ip, port)); + + return nelt.first->second.new_connection(); + } + } + + return nullptr; +} + +auto redis_pool::release_connection(redisAsyncContext *ctx, + enum rspamd_redis_pool_release_type how) -> void +{ + if (!wanna_die) { + auto conn_it = conns_by_ctx.find(ctx); + if (conn_it != conns_by_ctx.end()) { + auto *conn = conn_it->second; + g_assert (conn->state == RSPAMD_REDIS_POOL_CONN_ACTIVE); + + if (ctx->err != REDIS_OK) { + /* We need to terminate connection forcefully */ + msg_debug_rpool ("closed connection %p due to an error", conn->ctx); } else { - if (how == RSPAMD_REDIS_RELEASE_FATAL) { - msg_debug_rpool ("closed connection %p due to an fatal termination", - conn->ctx); + if (how == RSPAMD_REDIS_RELEASE_DEFAULT) { + /* Ensure that there are no callbacks attached to this conn */ + if (ctx->replies.head == nullptr) { + /* Just move it to the inactive queue */ + conn->state = RSPAMD_REDIS_POOL_CONN_INACTIVE; + conn->elt->move_to_inactive(conn); + conn->schedule_timeout(); + msg_debug_rpool("mark connection %p inactive", conn->ctx); + + return; + } + else { + msg_debug_rpool("closed connection %p due to callbacks left", + conn->ctx); + } } else { - msg_debug_rpool ("closed connection %p due to explicit termination", - conn->ctx); + if (how == RSPAMD_REDIS_RELEASE_FATAL) { + msg_debug_rpool("closed connection %p due to an fatal termination", + conn->ctx); + } + else { + msg_debug_rpool("closed connection %p due to explicit termination", + conn->ctx); + } } - - REF_RELEASE (conn); } - } - REF_RELEASE (conn); - } - else { - g_assert_not_reached (); + conn->elt->release_connection(conn); + } + else { + RSPAMD_UNREACHABLE; + } } } +} + +void * +rspamd_redis_pool_init(void) +{ + return new rspamd::redis_pool{}; +} void -rspamd_redis_pool_destroy (struct rspamd_redis_pool *pool) +rspamd_redis_pool_config(void *p, + struct rspamd_config *cfg, + struct ev_loop *ev_base) { - struct rspamd_redis_pool_elt *elt; - GHashTableIter it; - gpointer k, v; + g_assert (p != NULL); + auto *pool = reinterpret_cast<class rspamd::redis_pool *>(p); - g_assert (pool != NULL); + pool->do_config(ev_base, cfg); +} - g_hash_table_iter_init (&it, pool->elts_by_key); - while (g_hash_table_iter_next (&it, &k, &v)) { - elt = (struct rspamd_redis_pool_elt *)v; - rspamd_redis_pool_elt_dtor (elt); - g_hash_table_iter_steal (&it); - } +struct redisAsyncContext * +rspamd_redis_pool_connect(void *p, + const gchar *db, const gchar *password, + const char *ip, int port) +{ + g_assert (p != NULL); + auto *pool = reinterpret_cast<class rspamd::redis_pool *>(p); + + return pool->new_connection(db, password, ip, port); +} - g_hash_table_unref (pool->elts_by_ctx); - g_hash_table_unref (pool->elts_by_key); - g_free (pool); +void +rspamd_redis_pool_release_connection(void *p, + struct redisAsyncContext *ctx, enum rspamd_redis_pool_release_type how) +{ + g_assert (p != NULL); + g_assert (ctx != NULL); + auto *pool = reinterpret_cast<class rspamd::redis_pool *>(p); + + pool->release_connection(ctx, how); +} + + +void +rspamd_redis_pool_destroy(void *p) +{ + auto *pool = reinterpret_cast<class rspamd::redis_pool *>(p); + + delete pool; } -const gchar* -rspamd_redis_type_to_string (int type) +const gchar * +rspamd_redis_type_to_string(int type) { const gchar *ret = "unknown"; diff --git a/src/libserver/redis_pool.h b/src/libserver/redis_pool.h index 0df650326..eb3de7194 100644 --- a/src/libserver/redis_pool.h +++ b/src/libserver/redis_pool.h @@ -21,8 +21,6 @@ #ifdef __cplusplus extern "C" { #endif - -struct rspamd_redis_pool; struct rspamd_config; struct redisAsyncContext; struct ev_loop; @@ -31,14 +29,14 @@ struct ev_loop; * Creates new redis pool * @return */ -struct rspamd_redis_pool *rspamd_redis_pool_init (void); +void* rspamd_redis_pool_init (void); /** * Configure redis pool and binds it to a specific event base * @param cfg * @param ev_base */ -void rspamd_redis_pool_config (struct rspamd_redis_pool *pool, +void rspamd_redis_pool_config (void *pool, struct rspamd_config *cfg, struct ev_loop *ev_base); @@ -53,7 +51,7 @@ void rspamd_redis_pool_config (struct rspamd_redis_pool *pool, * @return */ struct redisAsyncContext *rspamd_redis_pool_connect ( - struct rspamd_redis_pool *pool, + void *pool, const gchar *db, const gchar *password, const char *ip, int port); @@ -68,7 +66,7 @@ enum rspamd_redis_pool_release_type { * @param pool * @param ctx */ -void rspamd_redis_pool_release_connection (struct rspamd_redis_pool *pool, +void rspamd_redis_pool_release_connection (void *pool, struct redisAsyncContext *ctx, enum rspamd_redis_pool_release_type how); @@ -76,7 +74,7 @@ void rspamd_redis_pool_release_connection (struct rspamd_redis_pool *pool, * Stops redis pool and destroys it * @param pool */ -void rspamd_redis_pool_destroy (struct rspamd_redis_pool *pool); +void rspamd_redis_pool_destroy (void *pool); /** * Missing in hiredis diff --git a/src/libutil/upstream.c b/src/libutil/upstream.c index 889b524ac..8c194eb79 100644 --- a/src/libutil/upstream.c +++ b/src/libutil/upstream.c @@ -775,7 +775,7 @@ rspamd_upstream_fail (struct upstream *upstream, upstream->name, reason); - if (upstream->ctx && upstream->active_idx != -1) { + if (upstream->ctx && upstream->active_idx != -1 && upstream->ls) { sec_cur = rspamd_get_ticks (FALSE); RSPAMD_UPSTREAM_LOCK (upstream); @@ -885,7 +885,7 @@ rspamd_upstream_ok (struct upstream *upstream) struct upstream_list_watcher *w; RSPAMD_UPSTREAM_LOCK (upstream); - if (upstream->errors > 0 && upstream->active_idx != -1) { + if (upstream->errors > 0 && upstream->active_idx != -1 && upstream->ls) { /* We touch upstream if and only if it is active */ msg_debug_upstream ("reset errors on upstream %s (was %ud)", upstream->name, upstream->errors); upstream->errors = 0; @@ -1714,3 +1714,16 @@ void rspamd_upstreams_add_watch_callback (struct upstream_list *ups, DL_APPEND (ups->watchers, nw); } + +struct upstream* +rspamd_upstream_ref (struct upstream *up) +{ + REF_RETAIN (up); + return up; +} + +void +rspamd_upstream_unref (struct upstream *up) +{ + REF_RELEASE (up); +} diff --git a/src/libutil/upstream.h b/src/libutil/upstream.h index 89bcd9b52..72a768892 100644 --- a/src/libutil/upstream.h +++ b/src/libutil/upstream.h @@ -299,6 +299,18 @@ struct upstream *rspamd_upstream_get_except (struct upstream_list *ups, */ void rspamd_upstream_reresolve (struct upstream_ctx *ctx); +/** + * Share ownership on upstream + * @param up + * @return + */ +struct upstream* rspamd_upstream_ref (struct upstream *up); +/** + * Unshare ownership on upstream + * @param up + */ +void rspamd_upstream_unref (struct upstream *up); + #ifdef __cplusplus } #endif diff --git a/src/plugins/lua/history_redis.lua b/src/plugins/lua/history_redis.lua index b05df4e5d..c14b8d32a 100644 --- a/src/plugins/lua/history_redis.lua +++ b/src/plugins/lua/history_redis.lua @@ -91,7 +91,8 @@ local function normalise_results(tbl, task) local seconds = task:get_timeval()['tv_sec'] tbl.unix_time = seconds - tbl.subject = task:get_header('subject') or 'unknown' + local subject = task:get_header('subject') or 'unknown' + tbl.subject = lua_util.maybe_obfuscate_string(subject, settings, 'subject') tbl.size = task:get_size() local ip = task:get_from_ip() if ip and ip:is_valid() then @@ -219,9 +220,6 @@ local function handle_history_request(task, conn, from, to, reset) (rspamd_util:get_ticks() - t1) * 1000.0) collectgarbage() t1 = rspamd_util:get_ticks() - fun.each(function(e) - e.subject = lua_util.maybe_obfuscate_string(e.subject, settings, 'subject') - end, data) reply.rows = data conn:send_ucl(reply) lua_util.debugm(N, task, 'process + sending took %s ms', diff --git a/src/plugins/lua/mime_types.lua b/src/plugins/lua/mime_types.lua index 2e5dee967..fd1c7e2d5 100644 --- a/src/plugins/lua/mime_types.lua +++ b/src/plugins/lua/mime_types.lua @@ -380,7 +380,8 @@ local function check_mime_type(task) end if not found then - task:insert_result(settings['symbol_attachment'], mult, ext) + task:insert_result(settings['symbol_attachment'], mult, string.format('%s:%s', + ext, ct)) end end end diff --git a/src/ragel/content_disposition_parser.rl b/src/ragel/content_disposition_parser.rl index cdef4db46..f1b0172b7 100644 --- a/src/ragel/content_disposition_parser.rl +++ b/src/ragel/content_disposition_parser.rl @@ -122,5 +122,5 @@ rspamd_content_disposition_parser (const char *data, size_t len, struct rspamd_c free (st_storage.data); } - return cd->type != RSPAMD_CT_UNKNOWN; + return cd->attrs != NULL || cd->type != RSPAMD_CT_UNKNOWN; } |