From cd85daafe383de85701d726461e3e464f7f70249 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 13 Aug 2012 20:40:50 +0400 Subject: [PATCH] * Add DNS resolver lua bindings. Make lua http library working without task object. Fix a problem with resolver in lua_worker. Added some utility functions to lua api. --- src/lua/CMakeLists.txt | 3 +- src/lua/lua_buffer.c | 4 +- src/lua/lua_common.c | 22 +++ src/lua/lua_common.h | 10 +- src/lua/lua_dns.c | 292 ++++++++++++++++++++++++++++++++++++++++ src/lua/lua_http.c | 295 ++++++++++++++++++++++++++++++++++------- src/lua/lua_session.c | 10 +- src/lua_worker.c | 8 +- 8 files changed, 586 insertions(+), 58 deletions(-) create mode 100644 src/lua/lua_dns.c diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt index 92cf3df4b..10609f782 100644 --- a/src/lua/CMakeLists.txt +++ b/src/lua/CMakeLists.txt @@ -13,7 +13,8 @@ SET(LUASRC lua_common.c lua_upstream.c lua_mempool.c lua_session.c - lua_buffer.c) + lua_buffer.c + lua_dns.c) ADD_LIBRARY(rspamd-lua ${LINK_TYPE} ${LUASRC}) SET_TARGET_PROPERTIES(rspamd-lua PROPERTIES VERSION ${RSPAMD_VERSION}) diff --git a/src/lua/lua_buffer.c b/src/lua/lua_buffer.c index 9c18ef5cf..43df6a6d8 100644 --- a/src/lua/lua_buffer.c +++ b/src/lua/lua_buffer.c @@ -100,7 +100,7 @@ lua_io_read_cb (f_str_t * in, void *arg) msg_info ("call to session finalizer failed: %s", lua_tostring (cbdata->L, -1)); } - res = lua_toboolean (cbdata->L, 1); + res = lua_toboolean (cbdata->L, -1); lua_pop (cbdata->L, 1); if (need_unlock) { @@ -133,7 +133,7 @@ lua_io_write_cb (void *arg) msg_info ("call to session finalizer failed: %s", lua_tostring (cbdata->L, -1)); } - res = lua_toboolean (cbdata->L, 1); + res = lua_toboolean (cbdata->L, -1); lua_pop (cbdata->L, 1); if (need_unlock) { diff --git a/src/lua/lua_common.c b/src/lua/lua_common.c index 3f66c3fcd..cb114bb34 100644 --- a/src/lua/lua_common.c +++ b/src/lua/lua_common.c @@ -444,6 +444,7 @@ init_lua (struct config_file *cfg) (void)lua_add_actions_global (L); (void)luaopen_session (L); (void)luaopen_io_dispatcher (L); + (void)luaopen_dns_resolver (L); cfg->lua_state = L; memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)lua_close, L); @@ -789,3 +790,24 @@ lua_dumpstack (lua_State *L) } msg_info (buf); } + +gpointer +lua_check_class (lua_State *L, gint index, const gchar *name) +{ + gpointer p; + + if (lua_type (L, index) == LUA_TUSERDATA) { + p = lua_touserdata (L, index); + if (p) { + if (lua_getmetatable (L, index)) { + lua_getfield (L, LUA_REGISTRYINDEX, name); /* get correct metatable */ + if (lua_rawequal (L, -1, -2)) { /* does it have the correct mt? */ + lua_pop (L, 2); /* remove both metatables */ + return p; + } + lua_pop (L, 2); + } + } + } + return NULL; +} diff --git a/src/lua/lua_common.h b/src/lua/lua_common.h index f9ab43d91..0ec0466e0 100644 --- a/src/lua/lua_common.h +++ b/src/lua/lua_common.h @@ -46,6 +46,11 @@ void lua_set_table_index (lua_State *L, const gchar *index, const gchar *value); */ gint lua_class_tostring (lua_State *L); +/** + * Check whether the argument at specified index is of the specified class + */ +gpointer lua_check_class (lua_State *L, gint index, const gchar *name); + /** * Open libraries functions */ @@ -71,11 +76,8 @@ gint luaopen_upstream (lua_State * L); gint luaopen_mempool (lua_State * L); gint luaopen_session (lua_State * L); gint luaopen_io_dispatcher (lua_State * L); +gint luaopen_dns_resolver (lua_State * L); -void init_lua (struct config_file *cfg); -gboolean init_lua_filters (struct config_file *cfg); - -/* Filters functions */ gint lua_call_filter (const gchar *function, struct worker_task *task); gint lua_call_chain_filter (const gchar *function, struct worker_task *task, gint *marks, guint number); double lua_consolidation_func (struct worker_task *task, const gchar *metric_name, const gchar *function_name); diff --git a/src/lua/lua_dns.c b/src/lua/lua_dns.c new file mode 100644 index 000000000..b31cac60a --- /dev/null +++ b/src/lua/lua_dns.c @@ -0,0 +1,292 @@ +/* Copyright (c) 2010-2012, 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" + +/* Public prototypes */ +struct rspamd_dns_resolver *lua_check_dns_resolver (lua_State * L); +gint luaopen_dns_resolver (lua_State * L); + +/* Lua bindings */ +LUA_FUNCTION_DEF (dns_resolver, init); +LUA_FUNCTION_DEF (dns_resolver, resolve_a); +LUA_FUNCTION_DEF (dns_resolver, resolve_ptr); +LUA_FUNCTION_DEF (dns_resolver, resolve_txt); + +static const struct luaL_reg dns_resolverlib_f[] = { + LUA_INTERFACE_DEF (dns_resolver, init), + {NULL, NULL} +}; + +static const struct luaL_reg dns_resolverlib_m[] = { + LUA_INTERFACE_DEF (dns_resolver, resolve_a), + LUA_INTERFACE_DEF (dns_resolver, resolve_ptr), + LUA_INTERFACE_DEF (dns_resolver, resolve_txt), + {"__tostring", lua_class_tostring}, + {NULL, NULL} +}; + +struct rspamd_dns_resolver * +lua_check_dns_resolver (lua_State * L) +{ + void *ud = luaL_checkudata (L, 1, "rspamd{resolver}"); + luaL_argcheck (L, ud != NULL, 1, "'resolver' expected"); + return ud ? *((struct rspamd_dns_resolver **)ud) : NULL; +} + +struct lua_dns_cbdata { + lua_State *L; + struct rspamd_dns_resolver *resolver; + gint cbref; + const gchar *to_resolve; +}; + +static void +lua_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) +{ + struct lua_dns_cbdata *cd = arg; + gint i = 0; + struct in_addr ina; + struct rspamd_dns_resolver **presolver; + union rspamd_reply_element *elt; + GList *cur; + gboolean need_unlock = FALSE; + + /* Avoid LOR here as mutex can be acquired before in lua_call */ + if (g_mutex_trylock (lua_mtx)) { + need_unlock = TRUE; + } + + lua_rawgeti (cd->L, LUA_REGISTRYINDEX, cd->cbref); + presolver = lua_newuserdata (cd->L, sizeof (gpointer)); + lua_setclass (cd->L, "rspamd{resolver}", -1); + + *presolver = cd->resolver; + lua_pushstring (cd->L, cd->to_resolve); + + if (reply->code == DNS_RC_NOERROR) { + if (reply->type == DNS_REQUEST_A) { + + lua_newtable (cd->L); + cur = reply->elements; + while (cur) { + elt = cur->data; + memcpy (&ina, &elt->a.addr[0], sizeof (struct in_addr)); + /* Actually this copy memory, so using of inet_ntoa is valid */ + lua_pushstring (cd->L, inet_ntoa (ina)); + lua_rawseti (cd->L, -2, ++i); + cur = g_list_next (cur); + } + lua_pushnil (cd->L); + } + else if (reply->type == DNS_REQUEST_PTR) { + lua_newtable (cd->L); + cur = reply->elements; + while (cur) { + elt = cur->data; + lua_pushstring (cd->L, elt->ptr.name); + lua_rawseti (cd->L, -2, ++i); + cur = g_list_next (cur); + } + lua_pushnil (cd->L); + + } + else if (reply->type == DNS_REQUEST_TXT) { + lua_newtable (cd->L); + cur = reply->elements; + while (cur) { + elt = cur->data; + lua_pushstring (cd->L, elt->txt.data); + lua_rawseti (cd->L, -2, ++i); + cur = g_list_next (cur); + } + lua_pushnil (cd->L); + + } + else { + lua_pushnil (cd->L); + lua_pushstring (cd->L, "Unknown reply type"); + } + } + else { + lua_pushnil (cd->L); + lua_pushstring (cd->L, dns_strerror (reply->code)); + } + + if (lua_pcall (cd->L, 4, 0, 0) != 0) { + msg_info ("call to dns_callback failed: %s", lua_tostring (cd->L, -1)); + } + + /* Unref function */ + luaL_unref (cd->L, LUA_REGISTRYINDEX, cd->cbref); + + if (need_unlock) { + g_mutex_unlock (lua_mtx); + } +} + +static int +lua_dns_resolver_init (lua_State *L) +{ + struct rspamd_dns_resolver *resolver, **presolver; + struct config_file *cfg, **pcfg; + struct event_base *base, **pbase; + + /* Check args */ + pbase = luaL_checkudata (L, 1, "rspamd{ev_base}"); + luaL_argcheck (L, pbase != NULL, 1, "'ev_base' expected"); + base = pbase ? *(pbase) : NULL; + pcfg = luaL_checkudata (L, 2, "rspamd{config}"); + luaL_argcheck (L, pcfg != NULL, 2, "'config' expected"); + cfg = pcfg ? *(pcfg) : NULL; + + if (base != NULL && cfg != NULL) { + resolver = dns_resolver_init (base, cfg); + if (resolver) { + presolver = lua_newuserdata (L, sizeof (gpointer)); + lua_setclass (L, "rspamd{resolver}", -1); + *presolver = resolver; + } + else { + lua_pushnil (L); + } + } + else { + lua_pushnil (L); + } + + return 1; +} + +static int +lua_dns_resolver_resolve_common (lua_State *L, struct rspamd_dns_resolver *resolver, enum rspamd_request_type type) +{ + struct rspamd_async_session *session, **psession; + memory_pool_t *pool, **ppool; + const gchar *to_resolve; + struct in_addr ina; + struct lua_dns_cbdata *cbdata; + + /* Check arguments */ + psession = luaL_checkudata (L, 2, "rspamd{session}"); + luaL_argcheck (L, psession != NULL, 2, "'session' expected"); + session = psession ? *(psession) : NULL; + ppool = luaL_checkudata (L, 3, "rspamd{mempool}"); + luaL_argcheck (L, ppool != NULL, 3, "'mempool' expected"); + pool = ppool ? *(ppool) : NULL; + to_resolve = luaL_checkstring (L, 4); + + if (pool != NULL && session != NULL && to_resolve != NULL && lua_isfunction (L, 5)) { + if (type == DNS_REQUEST_PTR) { + if (inet_aton (to_resolve, &ina) == 0) { + msg_err ("wrong resolve string to PTR request: %s", to_resolve); + lua_pushnil (L); + return 1; + } + } + cbdata = memory_pool_alloc (pool, sizeof (struct lua_dns_cbdata)); + cbdata->L = L; + cbdata->resolver = resolver; + lua_pushvalue (L, 5); + cbdata->cbref = luaL_ref (L, LUA_REGISTRYINDEX); + if (type == DNS_REQUEST_PTR) { + make_dns_request (resolver, session, pool, lua_dns_callback, cbdata, type, &ina); + } + else { + make_dns_request (resolver, session, pool, lua_dns_callback, cbdata, type, to_resolve); + } + lua_pushboolean (L, TRUE); + } + else { + msg_err ("invalid arguments to lua_resolve"); + lua_pushnil (L); + } + + return 1; + +} + +static int +lua_dns_resolver_resolve_a (lua_State *L) +{ + struct rspamd_dns_resolver *dns_resolver = lua_check_dns_resolver (L); + + if (dns_resolver) { + return lua_dns_resolver_resolve_common (L, dns_resolver, DNS_REQUEST_A); + } + else { + lua_pushnil (L); + } + + return 1; +} + +static int +lua_dns_resolver_resolve_ptr (lua_State *L) +{ + struct rspamd_dns_resolver *dns_resolver = lua_check_dns_resolver (L); + + if (dns_resolver) { + return lua_dns_resolver_resolve_common (L, dns_resolver, DNS_REQUEST_PTR); + } + else { + lua_pushnil (L); + } + + return 1; +} + +static int +lua_dns_resolver_resolve_txt (lua_State *L) +{ + struct rspamd_dns_resolver *dns_resolver = lua_check_dns_resolver (L); + + if (dns_resolver) { + return lua_dns_resolver_resolve_common (L, dns_resolver, DNS_REQUEST_TXT); + } + else { + lua_pushnil (L); + } + + return 1; +} + +gint +luaopen_dns_resolver (lua_State * L) +{ + luaL_newmetatable (L, "rspamd{resolver}"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + + lua_pushstring (L, "class"); + lua_pushstring (L, "rspamd{resolver}"); + lua_rawset (L, -3); + + luaL_openlib (L, NULL, dns_resolverlib_m, 0); + luaL_openlib(L, "rspamd_resolver", dns_resolverlib_f, 0); + + lua_pop (L, 1); /* remove metatable from stack */ + return 1; +} diff --git a/src/lua/lua_http.c b/src/lua/lua_http.c index 68faee2e7..8f85ebee1 100644 --- a/src/lua/lua_http.c +++ b/src/lua/lua_http.c @@ -43,10 +43,15 @@ struct lua_http_header { }; struct lua_http_ud { - gint parser_state; struct worker_task *task; + gint parser_state; + struct rspamd_async_session *s; + memory_pool_t *pool; + struct rspamd_dns_resolver *resolver; + struct event_base *ev_base; lua_State *L; const gchar *callback; + gint cbref; gchar *req_buf; gint req_len; gint port; @@ -58,19 +63,15 @@ struct lua_http_ud { GList *headers; }; -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_http_fin (void *arg) { struct lua_http_ud *ud = arg; + if (ud->callback == NULL) { + /* Unref callback */ + luaL_unref (ud->L, LUA_REGISTRYINDEX, ud->cbref); + } rspamd_remove_dispatcher (ud->io_dispatcher); close (ud->fd); } @@ -79,21 +80,35 @@ static void lua_http_push_error (gint code, struct lua_http_ud *ud) { struct worker_task **ptask; + gint num; + gboolean need_unlock = FALSE; + + /* Avoid LOR here as mutex can be acquired before in lua_call */ + if (g_mutex_trylock (lua_mtx)) { + need_unlock = TRUE; + } /* Push error */ - lua_getglobal (ud->L, ud->callback); - ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); - lua_setclass (ud->L, "rspamd{task}", -1); + if (ud->callback) { + lua_getglobal (ud->L, ud->callback); + ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); + lua_setclass (ud->L, "rspamd{task}", -1); + *ptask = ud->task; + num = 4; + } + else { + lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref); + num = 3; + } - *ptask = ud->task; /* Code */ lua_pushnumber (ud->L, code); /* Headers */ lua_pushnil (ud->L); /* Reply */ lua_pushnil (ud->L); - if (lua_pcall (ud->L, 4, 0, 0) != 0) { - msg_info ("call to %s failed: %s", ud->callback, lua_tostring (ud->L, -1)); + if (lua_pcall (ud->L, num, 0, 0) != 0) { + msg_info ("call to %s failed: %s", ud->callback ? ud->callback : "local function", lua_tostring (ud->L, -1)); } if (ud->headers != NULL) { @@ -102,7 +117,10 @@ lua_http_push_error (gint code, struct lua_http_ud *ud) } ud->parser_state = 3; - remove_normal_event (ud->task->s, lua_http_fin, ud); + remove_normal_event (ud->s, lua_http_fin, ud); + if (need_unlock) { + g_mutex_unlock (lua_mtx); + } } static void @@ -111,13 +129,27 @@ lua_http_push_reply (f_str_t *in, struct lua_http_ud *ud) GList *cur; struct lua_http_header *header; struct worker_task **ptask; + gint num; + gboolean need_unlock = FALSE; - /* Push error */ - lua_getglobal (ud->L, ud->callback); - ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); - lua_setclass (ud->L, "rspamd{task}", -1); + /* Avoid LOR here as mutex can be acquired before in lua_call */ + if (g_mutex_trylock (lua_mtx)) { + need_unlock = TRUE; + } + + if (ud->callback) { + /* Push error */ + lua_getglobal (ud->L, ud->callback); + ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *)); + lua_setclass (ud->L, "rspamd{task}", -1); - *ptask = ud->task; + *ptask = ud->task; + num = 4; + } + else { + lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref); + num = 3; + } /* Code */ lua_pushnumber (ud->L, ud->code); /* Headers */ @@ -134,8 +166,8 @@ lua_http_push_reply (f_str_t *in, struct lua_http_ud *ud) /* Reply */ lua_pushlstring (ud->L, in->begin, in->len); - if (lua_pcall (ud->L, 4, 0, 0) != 0) { - msg_info ("call to %s failed: %s", ud->callback, lua_tostring (ud->L, -1)); + if (lua_pcall (ud->L, num, 0, 0) != 0) { + msg_info ("call to %s failed: %s", ud->callback ? ud->callback : "local function", lua_tostring (ud->L, -1)); } if (ud->headers != NULL) { @@ -143,7 +175,10 @@ lua_http_push_reply (f_str_t *in, struct lua_http_ud *ud) ud->headers = NULL; } - remove_normal_event (ud->task->s, lua_http_fin, ud); + remove_normal_event (ud->s, lua_http_fin, ud); + if (need_unlock) { + g_mutex_unlock (lua_mtx); + } } /* @@ -184,8 +219,8 @@ lua_http_parse_header_line (struct lua_http_ud *ud, f_str_t *in) return FALSE; } /* Copy name */ - new = memory_pool_alloc (ud->task->task_pool, sizeof (struct lua_http_header)); - new->name = memory_pool_alloc (ud->task->task_pool, p - in->begin + 1); + new = memory_pool_alloc (ud->pool, sizeof (struct lua_http_header)); + new->name = memory_pool_alloc (ud->pool, p - in->begin + 1); rspamd_strlcpy (new->name, in->begin, p - in->begin + 1); p ++; @@ -193,7 +228,7 @@ lua_http_parse_header_line (struct lua_http_ud *ud, f_str_t *in) while (p < in->begin + in->len && g_ascii_isspace (*p)) { p ++; } - new->value = memory_pool_alloc (ud->task->task_pool, in->begin + in->len - p + 1); + new->value = memory_pool_alloc (ud->pool, in->begin + in->len - p + 1); rspamd_strlcpy (new->value, p, in->begin + in->len - p + 1); /* Check content-length */ @@ -260,7 +295,7 @@ lua_http_err_cb (GError * err, void *arg) lua_http_push_error (500, ud); } else { - remove_normal_event (ud->task->s, lua_http_fin, ud); + remove_normal_event (ud->s, lua_http_fin, ud); } } @@ -292,10 +327,10 @@ lua_http_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) /* Create dispatcher for HTTP protocol */ msec_to_tv (ud->timeout, &tv); - ud->io_dispatcher = rspamd_create_dispatcher (ud->task->ev_base, ud->fd, BUFFER_LINE, lua_http_read_cb, NULL, + ud->io_dispatcher = rspamd_create_dispatcher (ud->ev_base, ud->fd, BUFFER_LINE, lua_http_read_cb, NULL, lua_http_err_cb, &tv, ud); /* Write request */ - register_async_event (ud->task->s, lua_http_fin, ud, g_quark_from_static_string ("lua http")); + register_async_event (ud->s, lua_http_fin, ud, g_quark_from_static_string ("lua http")); if (!rspamd_dispatcher_write (ud->io_dispatcher, ud->req_buf, ud->req_len, TRUE, TRUE)) { lua_http_push_error (450, ud); @@ -320,6 +355,9 @@ lua_http_make_request_common (lua_State *L, struct worker_task *task, const gcha ud = memory_pool_alloc0 (task->task_pool, sizeof (struct lua_http_ud)); ud->L = L; + ud->s = task->s; + ud->pool = task->task_pool; + ud->ev_base = task->ev_base; ud->task = task; /* Preallocate buffer */ ud->req_buf = memory_pool_alloc (task->task_pool, s); @@ -376,6 +414,106 @@ lua_http_make_request_common (lua_State *L, struct worker_task *task, const gcha return 0; } +/** + * Common request function (new version) + */ +static gint +lua_http_make_request_common_new (lua_State *L, struct rspamd_async_session *session, memory_pool_t *pool, struct event_base *base, gint cbref, + const gchar *hostname, const gchar *path, const gchar *data, gint top) +{ + gint r, s, datalen; + struct lua_http_ud *ud; + struct in_addr ina; + struct timeval tv; + + /* Calculate buffer size */ + datalen = (data != NULL) ? strlen (data) : 0; + s = MAX_HEADERS_SIZE + sizeof (CRLF) * 3 + strlen (hostname) + strlen (path) + datalen + + sizeof ("POST HTTP/1.1"); + + ud = memory_pool_alloc0 (pool, sizeof (struct lua_http_ud)); + ud->L = L; + ud->pool = pool; + ud->s = session; + ud->ev_base = base; + /* Preallocate buffer */ + ud->req_buf = memory_pool_alloc (pool, s); + ud->callback = NULL; + ud->cbref = cbref; + + /* Print request */ + r = rspamd_snprintf (ud->req_buf, s, "%s %s HTTP/1.1" CRLF + "Connection: close" CRLF, + (data != NULL) ? "POST" : "GET", path); + if (datalen > 0) { + r += rspamd_snprintf (ud->req_buf + r, s - r, "Content-Length: %d" CRLF, datalen); + } + /* Now assume that we have a table with headers at the top of the stack */ + + if (lua_gettop (L) > top && lua_istable (L, top + 1)) { + /* Add headers */ + lua_pushnil (L); /* first key */ + while (lua_next (L, top + 1) != 0) { + r += rspamd_snprintf (ud->req_buf + r, s - r, "%s: %s" CRLF, lua_tostring (L, -2), lua_tostring (L, -1)); + lua_pop (L, 1); + } + } + /* Now check port and timeout */ + if (lua_gettop (L) > top + 1) { + ud->port = lua_tonumber (L, top + 2); + } + else { + ud->port = 80; + } + if (lua_gettop (L) > top + 2) { + ud->timeout = lua_tonumber (L, top + 3); + } + else { + /* Assume default timeout as 1000 msec */ + ud->timeout = 1000; + } + + if (datalen > 0) { + r += rspamd_snprintf (ud->req_buf + r, s - r, CRLF "%s", data); + } + else { + r += rspamd_snprintf (ud->req_buf + r, s - r, CRLF); + } + + ud->req_len = r; + + if (inet_aton (hostname, &ina) == 0) { + msg_err ("%s is not valid ip address", hostname); + luaL_unref (L, LUA_REGISTRYINDEX, cbref); + lua_pushnil (L); + return 1; + } + + ud->fd = make_tcp_socket (&ina, ud->port, FALSE, TRUE); + + if (ud->fd == -1) { + luaL_unref (L, LUA_REGISTRYINDEX, cbref); + lua_pushnil (L); + return 1; + } + + /* Create dispatcher for HTTP protocol */ + msec_to_tv (ud->timeout, &tv); + ud->io_dispatcher = rspamd_create_dispatcher (ud->ev_base, ud->fd, BUFFER_LINE, lua_http_read_cb, NULL, + lua_http_err_cb, &tv, ud); + /* Write request */ + register_async_event (ud->s, lua_http_fin, ud, g_quark_from_static_string ("lua http")); + + if (!rspamd_dispatcher_write (ud->io_dispatcher, ud->req_buf, ud->req_len, TRUE, TRUE)) { + luaL_unref (L, LUA_REGISTRYINDEX, cbref); + lua_pushnil (L); + return 1; + } + + return 0; +} + + /* * Typical usage: * rspamd_http.post_request(task, 'callback', 'hostname', 'path', 'data'[, headers -> { name = 'value' }]) @@ -383,21 +521,55 @@ lua_http_make_request_common (lua_State *L, struct worker_task *task, const gcha static gint lua_http_make_post_request (lua_State *L) { - struct worker_task *task = lua_check_task (L); + struct worker_task *task, **ptask; + memory_pool_t *pool, **ppool; + struct rspamd_async_session *session, **psession; + struct event_base *base, **pbase; const gchar *hostname, *path, *data, *callback; + gint cbref; + + + /* Check whether we have a task object */ + ptask = lua_check_class (L, 1, "rspamd{task}"); + task = ptask ? *(ptask) : NULL; + + if (!task) { + psession = luaL_checkudata (L, 1, "rspamd{session}"); + luaL_argcheck (L, psession != NULL, 1, "'session' expected"); + session = psession ? *(psession) : NULL; + ppool = luaL_checkudata (L, 2, "rspamd{mempool}"); + luaL_argcheck (L, ppool != NULL, 2, "'mempool' expected"); + pool = ppool ? *(ppool) : NULL; + pbase = luaL_checkudata (L, 3, "rspamd{ev_base}"); + luaL_argcheck (L, ppool != NULL, 3, "'ev_base' expected"); + base = pbase ? *(pbase) : NULL; + } /* Now extract hostname, path and data */ - callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); - hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); - path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); - data = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 5)); + if (task) { + callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); + hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); + path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); + data = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 5)); - if (callback != NULL && hostname != NULL && path != NULL && data != NULL) { - return lua_http_make_request_common (L, task, callback, hostname, path, data, 5); + if (callback != NULL && hostname != NULL && path != NULL && data != NULL) { + return lua_http_make_request_common (L, task, callback, hostname, path, data, 5); + } + else { + msg_info ("invalid arguments number"); + } } else { - msg_info ("invalid arguments number"); + /* Common version */ + hostname = memory_pool_strdup (pool, luaL_checkstring (L, 4)); + path = memory_pool_strdup (pool, luaL_checkstring (L, 5)); + data = memory_pool_strdup (pool, luaL_checkstring (L, 6)); + if (session != NULL && pool != NULL && hostname != NULL && path != NULL && data != NULL && lua_isfunction (L, 7)) { + lua_pushvalue (L, 7); + cbref = luaL_ref (L, LUA_REGISTRYINDEX); + return lua_http_make_request_common_new (L, session, pool, base, cbref, hostname, path, data, 7); + } } return 0; @@ -410,20 +582,53 @@ lua_http_make_post_request (lua_State *L) static gint lua_http_make_get_request (lua_State *L) { - struct worker_task *task = lua_check_task (L); + struct worker_task *task, **ptask; + memory_pool_t *pool, **ppool; + struct rspamd_async_session *session, **psession; + struct event_base *base, **pbase; const gchar *hostname, *path, *callback; + gint cbref; + + + /* Check whether we have a task object */ + ptask = lua_check_class (L, 1, "rspamd{task}"); + task = ptask ? *(ptask) : NULL; + + if (!task) { + psession = luaL_checkudata (L, 1, "rspamd{session}"); + luaL_argcheck (L, psession != NULL, 1, "'session' expected"); + session = psession ? *(psession) : NULL; + ppool = luaL_checkudata (L, 2, "rspamd{mempool}"); + luaL_argcheck (L, ppool != NULL, 2, "'mempool' expected"); + pool = ppool ? *(ppool) : NULL; + pbase = luaL_checkudata (L, 3, "rspamd{ev_base}"); + luaL_argcheck (L, ppool != NULL, 3, "'ev_base' expected"); + base = pbase ? *(pbase) : NULL; + } /* Now extract hostname, path and data */ - callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); - hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); - path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); + if (task) { + callback = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 2)); + hostname = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 3)); + path = memory_pool_strdup (task->task_pool, luaL_checkstring (L, 4)); - if (callback != NULL && hostname != NULL && path != NULL) { - return lua_http_make_request_common (L, task, callback, hostname, path, NULL, 4); + if (callback != NULL && hostname != NULL && path != NULL) { + return lua_http_make_request_common (L, task, callback, hostname, path, NULL, 4); + } + else { + msg_info ("invalid arguments number"); + } } else { - msg_info ("invalid arguments number"); + /* Common version */ + hostname = memory_pool_strdup (pool, luaL_checkstring (L, 4)); + path = memory_pool_strdup (pool, luaL_checkstring (L, 5)); + if (session != NULL && pool != NULL && hostname != NULL && path != NULL && lua_isfunction (L, 6)) { + lua_pushvalue (L, 6); + cbref = luaL_ref (L, LUA_REGISTRYINDEX); + return lua_http_make_request_common_new (L, session, pool, base, cbref, hostname, path, NULL, 6); + } } return 0; diff --git a/src/lua/lua_session.c b/src/lua/lua_session.c index 9f4af2882..a25363430 100644 --- a/src/lua/lua_session.c +++ b/src/lua/lua_session.c @@ -38,7 +38,7 @@ static const struct luaL_reg sessionlib_m[] = { LUA_INTERFACE_DEF (session, register_async_event), LUA_INTERFACE_DEF (session, remove_normal_event), LUA_INTERFACE_DEF (session, check_session_pending), - {"__gc", lua_session_delete}, + LUA_INTERFACE_DEF (session, delete), {"__tostring", lua_class_tostring}, {NULL, NULL} }; @@ -89,7 +89,7 @@ static gboolean lua_session_finalizer (gpointer ud) { struct lua_session_udata *cbdata = ud; - gboolean need_unlock = FALSE; + gboolean need_unlock = FALSE, res; /* Avoid LOR here as mutex can be acquired before in lua_call */ if (g_mutex_trylock (lua_mtx)) { @@ -98,15 +98,17 @@ lua_session_finalizer (gpointer ud) /* Call finalizer function */ lua_rawgeti (cbdata->L, LUA_REGISTRYINDEX, cbdata->cbref_fin); - if (lua_pcall (cbdata->L, 0, 0, 0) != 0) { + if (lua_pcall (cbdata->L, 0, 1, 0) != 0) { msg_info ("call to session finalizer failed: %s", lua_tostring (cbdata->L, -1)); } + res = lua_toboolean (cbdata->L, -1); + lua_pop (cbdata->L, 1); luaL_unref (cbdata->L, LUA_REGISTRYINDEX, cbdata->cbref_fin); if (need_unlock) { g_mutex_unlock (lua_mtx); } - return TRUE; + return res; } static void diff --git a/src/lua_worker.c b/src/lua_worker.c index b7daf7ce7..51066624b 100644 --- a/src/lua_worker.c +++ b/src/lua_worker.c @@ -225,9 +225,12 @@ static int lua_worker_get_resolver (lua_State *L) { struct rspamd_lua_worker_ctx *ctx = lua_check_lua_worker (L); + struct rspamd_dns_resolver **presolver; if (ctx) { - /* XXX: implement resolver API */ + presolver = lua_newuserdata (L, sizeof (gpointer)); + lua_setclass (L, "rspamd{resolver}", -1); + *presolver = ctx->resolver; } else { lua_pushnil (L); @@ -460,6 +463,8 @@ start_lua_worker (struct rspamd_worker *worker) event_base_set (ctx->ev_base, &worker->bind_ev); event_add (&worker->bind_ev, NULL); + ctx->resolver = dns_resolver_init (ctx->ev_base, worker->srv->cfg); + /* Open worker's lib */ luaopen_lua_worker (L); @@ -489,7 +494,6 @@ start_lua_worker (struct rspamd_worker *worker) /* Maps events */ start_map_watch (ctx->ev_base); - ctx->resolver = dns_resolver_init (ctx->ev_base, worker->srv->cfg); event_base_loop (ctx->ev_base, 0); luaL_unref (L, LUA_REGISTRYINDEX, ctx->cbref_accept); -- 2.39.5