aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--src/lua/CMakeLists.txt4
-rw-r--r--src/lua/lua_common.c40
-rw-r--r--src/lua/lua_common.h2
-rw-r--r--src/lua/lua_redis.c335
-rw-r--r--src/view.h55
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);