]> source.dussan.org Git - rspamd.git/commitdiff
* Add lua bindings to hiredis library
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Mon, 12 Dec 2011 17:14:49 +0000 (20:14 +0300)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Mon, 12 Dec 2011 17:14:49 +0000 (20:14 +0300)
Polish some comments.

CMakeLists.txt
src/lua/CMakeLists.txt
src/lua/lua_common.c
src/lua/lua_common.h
src/lua/lua_redis.c [new file with mode: 0644]
src/view.h

index 1e8867a272dfa854e9ccac0d52a09f546f0456e0..9859fe05e7309630b1707507b141d8ac6ddab704 100644 (file)
@@ -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 ###############################
index 04fa393ad51cb6afd2933ac7175811543f7b3ba0..70b3f7bc1e265446d9fcc51c71f95a0395eae14e 100644 (file)
@@ -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")
index 1d36a6fe8e72b73f83f6fec38f3ef7b09fba0045..00fcadc936205b527f9f984fb6b16f1ca50c9cd5 100644 (file)
@@ -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);
+}
index 26710c904ba05ff8191de9bf99df65f17a1959e5..6ae8421fb499ede55ae7a094cabe187e4701b7b4 100644 (file)
@@ -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 (file)
index 0000000..e4f1f99
--- /dev/null
@@ -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;
+}
index b3f033c238300ad25580678a55c8efc86b169019..f62c65198d73bb3c31dae4b4ad9c918e67919f4f 100644 (file)
@@ -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);