diff options
-rw-r--r-- | CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/lua/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/lua/lua_common.c | 40 | ||||
-rw-r--r-- | src/lua/lua_common.h | 2 | ||||
-rw-r--r-- | src/lua/lua_redis.c | 335 | ||||
-rw-r--r-- | src/view.h | 55 |
6 files changed, 428 insertions, 18 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e8867a27..9859fe05e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -818,6 +818,11 @@ IF(GLIB_COMPAT) INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/contrib/lgpl") ENDIF(GLIB_COMPAT) +IF(NOT HIREDIS_FOUND) + ADD_SUBDIRECTORY(contrib/hiredis) + INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/contrib/hiredis") +ENDIF(NOT HIREDIS_FOUND) + ADD_SUBDIRECTORY(src/lua) ADD_SUBDIRECTORY(src/json) ADD_SUBDIRECTORY(src/cdb) @@ -828,11 +833,6 @@ ADD_SUBDIRECTORY(src/client) ADD_SUBDIRECTORY(utils) ADD_SUBDIRECTORY(test) -IF(NOT HIREDIS_FOUND) - ADD_SUBDIRECTORY(contrib/hiredis) - INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/contrib/hiredis") -ENDIF(NOT HIREDIS_FOUND) - LIST(LENGTH PLUGINSSRC RSPAMD_MODULES_NUM) ############################ TARGETS SECTION ############################### diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt index 04fa393ad..70b3f7bc1 100644 --- a/src/lua/CMakeLists.txt +++ b/src/lua/CMakeLists.txt @@ -8,8 +8,10 @@ SET(LUASRC lua_common.c lua_regexp.c lua_cdb.c lua_xmlrpc.c - lua_http.c) + lua_http.c + lua_redis.c) ADD_LIBRARY(rspamd_lua STATIC ${LUASRC}) TARGET_LINK_LIBRARIES(rspamd_lua ${LUALIB}) +TARGET_LINK_LIBRARIES(rspamd_lua hiredis) SET_TARGET_PROPERTIES(rspamd_lua PROPERTIES COMPILE_FLAGS "-DRSPAMD_LIB") diff --git a/src/lua/lua_common.c b/src/lua/lua_common.c index 1d36a6fe8..00fcadc93 100644 --- a/src/lua/lua_common.c +++ b/src/lua/lua_common.c @@ -241,6 +241,7 @@ init_lua (struct config_file *cfg) (void)luaopen_cdb (L); (void)luaopen_xmlrpc (L); (void)luaopen_http (L); + (void)luaopen_redis (L); cfg->lua_state = L; memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)lua_close, L); @@ -509,7 +510,7 @@ double lua_normalizer_func (struct config_file *cfg, long double score, void *params) { GList *p = params; - long double res = score; + long double res = score; lua_State *L = cfg->lua_state; /* Call specified function and put input score on stack */ @@ -534,3 +535,40 @@ lua_normalizer_func (struct config_file *cfg, long double score, void *params) return res; } + + +void +lua_dumpstack (lua_State *L) +{ + gint i, t, r = 0; + gint top = lua_gettop (L); + gchar buf[BUFSIZ]; + + r += rspamd_snprintf (buf + r, sizeof (buf) - r, "lua stack: "); + for (i = 1; i <= top; i++) { /* repeat for each level */ + t = lua_type (L, i); + switch (t) + { + case LUA_TSTRING: /* strings */ + r += rspamd_snprintf (buf + r, sizeof (buf) - r, "str: %s", lua_tostring(L, i)); + break; + + case LUA_TBOOLEAN: /* booleans */ + r += rspamd_snprintf (buf + r, sizeof (buf) - r,lua_toboolean (L, i) ? "bool: true" : "bool: false"); + break; + + case LUA_TNUMBER: /* numbers */ + r += rspamd_snprintf (buf + r, sizeof (buf) - r, "number: %.2f", lua_tonumber (L, i)); + break; + + default: /* other values */ + r += rspamd_snprintf (buf + r, sizeof (buf) - r, "type: %s", lua_typename (L, t)); + break; + + } + if (i < top) { + r += rspamd_snprintf (buf + r, sizeof (buf) - r, " -> "); /* put a separator */ + } + } + msg_info (buf); +} diff --git a/src/lua/lua_common.h b/src/lua/lua_common.h index 26710c904..6ae8421fb 100644 --- a/src/lua/lua_common.h +++ b/src/lua/lua_common.h @@ -39,6 +39,7 @@ gint luaopen_glib_regexp (lua_State *L); gint luaopen_cdb (lua_State *L); gint luaopen_xmlrpc (lua_State * L); gint luaopen_http (lua_State * L); +gint luaopen_redis (lua_State * L); void init_lua (struct config_file *cfg); gboolean init_lua_filters (struct config_file *cfg); @@ -62,6 +63,7 @@ void lua_process_element (struct config_file *cfg, const gchar *name, struct mod gboolean lua_handle_param (struct worker_task *task, gchar *mname, gchar *optname, enum lua_var_type expected_type, gpointer *res); gboolean lua_check_condition (struct config_file *cfg, const gchar *condition); +void lua_dumpstack (lua_State *L); #endif /* WITH_LUA */ diff --git a/src/lua/lua_redis.c b/src/lua/lua_redis.c new file mode 100644 index 000000000..e4f1f996c --- /dev/null +++ b/src/lua/lua_redis.c @@ -0,0 +1,335 @@ +/* Copyright (c) 2010-2011, Vsevolod Stakhov + * 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 ''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 AUTHOR 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 "lua_common.h" +#include "dns.h" +#include "hiredis.h" +#include "async.h" +#include "adapters/libevent.h" + +/** + * Redis access API for lua from task object + */ + +LUA_FUNCTION_DEF (redis, make_request); + +static const struct luaL_reg redislib_m[] = { + LUA_INTERFACE_DEF (redis, make_request), + {"__tostring", lua_class_tostring}, + {NULL, NULL} +}; + +/** + * Struct for userdata representation + */ +struct lua_redis_userdata { + redisAsyncContext *ctx; + lua_State *L; + struct worker_task *task; + gint cbref; + gchar *server; + struct in_addr ina; + gchar *reqline; + guint16 port; + f_str_t *args; + guint args_num; +}; + +/** + * Utility function to extract worker task from lua arguments + * @param L lua stack + * @return worker task object + */ +static struct worker_task * +lua_check_task (lua_State * L) +{ + void *ud = luaL_checkudata (L, 1, "rspamd{task}"); + luaL_argcheck (L, ud != NULL, 1, "'task' expected"); + return ud ? *((struct worker_task **)ud) : NULL; +} + +static void +lua_redis_fin (void *arg) +{ + struct lua_redis_userdata *ud = arg; + + if (ud->ctx) { + redisAsyncDisconnect (ud->ctx); + redisAsyncFree (ud->ctx); + luaL_unref (ud->L, LUA_REGISTRYINDEX, ud->cbref); + } +} + +/** + * Push error of redis request to lua callback + * @param code + * @param ud + */ +static void +lua_redis_push_error (const gchar *err, struct lua_redis_userdata *ud) +{ + struct worker_task **ptask; + + /* Push error */ + lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref); + lua_dumpstack (ud->L); + ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); + lua_setclass (ud->L, "rspamd{task}", -1); + + *ptask = ud->task; + /* String of error */ + lua_pushstring (ud->L, err); + /* Data */ + lua_pushnil (ud->L); + lua_dumpstack (ud->L); + if (lua_pcall (ud->L, 3, 0, 0) != 0) { + msg_info ("call to callback failed: %s", lua_tostring (ud->L, -1)); + } + + remove_normal_event (ud->task->s, lua_redis_fin, ud); + + ud->task->save.saved--; + if (ud->task->save.saved == 0) { + /* Call other filters */ + ud->task->save.saved = 1; + process_filters (ud->task); + } +} + +/** + * Push data of redis request to lua callback + * @param r redis reply data + * @param ud + */ +static void +lua_redis_push_data (const redisReply *r, struct lua_redis_userdata *ud) +{ + struct worker_task **ptask; + + /* Push error */ + lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref); + ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); + lua_setclass (ud->L, "rspamd{task}", -1); + + *ptask = ud->task; + /* String of error */ + lua_pushstring (ud->L, ud->ctx->errstr); + /* Data */ + lua_pushlstring (ud->L, r->str, r->len); + + if (lua_pcall (ud->L, 3, 0, 0) != 0) { + msg_info ("call to callback failed: %s", lua_tostring (ud->L, -1)); + } + + remove_normal_event (ud->task->s, lua_redis_fin, ud); + + ud->task->save.saved--; + if (ud->task->save.saved == 0) { + /* Call other filters */ + ud->task->save.saved = 1; + process_filters (ud->task); + } +} + +/** + * Callback for redis replies + * @param c context of redis connection + * @param r redis reply + * @param priv userdata + */ +static void +lua_redis_callback (redisAsyncContext *c, gpointer r, gpointer priv) +{ + redisReply *reply = r; + struct lua_redis_userdata *ud = priv; + + if (c->err == 0) { + if (r != NULL) { + lua_redis_push_data (reply, ud); + } + else { + lua_redis_push_error ("received no data from server", ud); + } + } + else { + lua_redis_push_error (c->errstr, ud); + } +} +/** + * Make a real request to redis server and attach it to libevent cycle + * @param ud userdata object + * @return + */ +static gboolean +lua_redis_make_request_real (struct lua_redis_userdata *ud) +{ + ud->ctx = redisAsyncConnect (inet_ntoa (ud->ina), ud->port); + if (ud->ctx == NULL || ud->ctx->err) { + lua_redis_push_error (ud->ctx ? ud->ctx->errstr : "unknown error", ud); + return FALSE; + } + else { + register_async_event (ud->task->s, lua_redis_fin, ud, FALSE); + ud->task->save.saved ++; + } + redisLibeventAttach (ud->ctx, ud->task->ev_base); + /* Make a request now */ + switch (ud->args_num) { + case 0: + redisAsyncCommand (ud->ctx, lua_redis_callback, ud, ud->reqline); + break; + case 1: + redisAsyncCommand (ud->ctx, lua_redis_callback, ud, ud->reqline, ud->args[0].begin, ud->args[0].len); + break; + case 2: + redisAsyncCommand (ud->ctx, lua_redis_callback, ud, ud->reqline, ud->args[0].begin, ud->args[0].len, + ud->args[1].begin, ud->args[1].len); + break; + default: + /* XXX: cannot handle more than 3 arguments */ + redisAsyncCommand (ud->ctx, lua_redis_callback, ud, ud->reqline, ud->args[0].begin, ud->args[0].len, + ud->args[1].begin, ud->args[1].len, + ud->args[2].begin, ud->args[2].len); + break; + } + + return TRUE; +} + +/** + * Get result of dns error + * @param reply dns reply object + * @param arg user data + */ +static void +lua_redis_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +{ + struct lua_redis_userdata *ud = arg; + union rspamd_reply_element *elt; + + + if (reply->code != DNS_RC_NOERROR) { + lua_redis_push_error (dns_strerror (reply->code), ud); + return; + } + else { + elt = reply->elements->data; + memcpy (&ud->ina, &elt->a.addr[0], sizeof (struct in_addr)); + /* Make real request */ + lua_redis_make_request_real (ud); + } +} + +/** + * Make request to redis server + * @param task worker task object + * @param server server to check + * @param port port of redis server + * @param callback callback to be called + * @param request request line + * @param args list of arguments + * @return + */ +static int +lua_redis_make_request (lua_State *L) +{ + struct worker_task *task; + struct lua_redis_userdata *ud; + const gchar *server, *tmp; + guint port, i; + + if ((task = lua_check_task (L)) != NULL) { + server = luaL_checkstring (L, 2); + port = luaL_checkint (L, 3); + /* Now get callback */ + if (lua_isfunction (L, 4) && server != NULL && port > 0 && port < G_MAXUINT16) { + /* Create userdata */ + ud = memory_pool_alloc (task->task_pool, sizeof (struct lua_redis_userdata)); + ud->server = memory_pool_strdup (task->task_pool, server); + ud->port = port; + ud->task = task; + ud->L = L; + ud->ctx = NULL; + /* Pop other arguments */ + lua_dumpstack (L); + lua_pushvalue (L, 4); + lua_dumpstack (L); + /* Get a reference */ + ud->cbref = luaL_ref (L, LUA_REGISTRYINDEX); + lua_dumpstack (L); + ud->reqline = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 5)); + lua_dumpstack (L); + /* Now get remaining args */ + ud->args_num = lua_gettop (L) - 5; + ud->args = memory_pool_alloc (task->task_pool, ud->args_num * sizeof (f_str_t)); + for (i = 0; i < ud->args_num - 1; i ++) { + tmp = lua_tolstring (L, i + 6, &ud->args[i].len); + /* Make a copy of argument */ + ud->args[i].begin = memory_pool_alloc (task->task_pool, ud->args[i].len); + memcpy (ud->args[i].begin, tmp, ud->args[i].len); + } + /* Now check whether we need to perform DNS request */ + if (inet_aton (ud->server, &ud->ina) == 0) { + /* Need to make dns request */ + /* Resolve hostname */ + if (make_dns_request (task->resolver, task->s, task->task_pool, lua_redis_dns_callback, ud, + DNS_REQUEST_A, ud->server)) { + task->dns_requests ++; + lua_pushboolean (L, TRUE); + } + else { + msg_info ("failed to resolve %s", ud->server); + lua_pushboolean (L, FALSE); + } + } + else { + if (! lua_redis_make_request_real (ud)) { + lua_pushboolean (L, FALSE); + } + else { + lua_pushboolean (L, TRUE); + } + } + } + else { + msg_info ("function requred as 4-th argument"); + lua_pushboolean (L, FALSE); + } + } + + return 1; +} + +/** + * Open redis library + * @param L lua stack + * @return + */ +gint +luaopen_redis (lua_State * L) +{ + + luaL_openlib (L, "rspamd_redis", redislib_m, 0); + + return 1; +} diff --git a/src/view.h b/src/view.h index b3f033c23..f62c65198 100644 --- a/src/view.h +++ b/src/view.h @@ -1,3 +1,5 @@ +/** @file view.h **/ + #ifndef RSPAMD_VIEW_H #define RSPAMD_VIEW_H @@ -22,38 +24,69 @@ struct rspamd_view { memory_pool_t *pool; }; -/* + +/** * Init a new view + * @param pool pool for view + * @return */ struct rspamd_view* init_view (memory_pool_t *pool); -/* +/** * Add from option for this view + * @param view view + * @param line from line for this view + * @return */ gboolean add_view_from (struct rspamd_view *view, gchar *line); -/* - * Add from recipient for this view + + +/** + * Add recipient for this view + * @param view view object + * @param line recipient description + * @return */ gboolean add_view_rcpt (struct rspamd_view *view, gchar *line); -/* + +/** * Add ip option for this view + * @param view view object + * @param line ip description + * @return */ gboolean add_view_ip (struct rspamd_view *view, gchar *line); -/* + +/** * Add client ip option for this view + * @param view view object + * @param line ip description + * @return */ gboolean add_view_client_ip (struct rspamd_view *view, gchar *line); -/* + +/** * Add symbols option for this view + * @param view view object + * @param line symbols description + * @return */ gboolean add_view_symbols (struct rspamd_view *view, gchar *line); -/* - * Check view for this task +/** + * Check view for this task for specified symbol + * @param views list of defined views + * @param symbol symbol to check + * @param task task object + * @return whether to check this symbol for this task */ gboolean check_view (GList *views, const gchar *symbol, struct worker_task *task); -/* - * Check whether this task should be skipped from checking + +/** + * Check whether this task should be skipped from checks + * @param views list of defined views + * @param task task object + * @return */ gboolean check_skip (GList *views, struct worker_task *task); |