diff options
Diffstat (limited to 'src/lua/lua_upstream.c')
-rw-r--r-- | src/lua/lua_upstream.c | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/src/lua/lua_upstream.c b/src/lua/lua_upstream.c new file mode 100644 index 000000000..5c72f7949 --- /dev/null +++ b/src/lua/lua_upstream.c @@ -0,0 +1,521 @@ +/* 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 "config.h" +#include "lua_common.h" +#include "upstream.h" +#include "cfg_file.h" + +/* Upstream timeouts */ +#define DEFAULT_UPSTREAM_ERROR_TIME 10 +#define DEFAULT_UPSTREAM_DEAD_TIME 300 +#define DEFAULT_UPSTREAM_MAXERRORS 10 + +/** + * This module implements upstreams manipulation from lua + */ +/* Upstream list functions */ +LUA_FUNCTION_DEF (upstream_list, create); +LUA_FUNCTION_DEF (upstream_list, destroy); +LUA_FUNCTION_DEF (upstream_list, get_upstream_by_hash); +LUA_FUNCTION_DEF (upstream_list, get_upstream_round_robin); +LUA_FUNCTION_DEF (upstream_list, get_upstream_master_slave); + +static const struct luaL_reg upstream_list_m[] = { + + LUA_INTERFACE_DEF (upstream_list, get_upstream_by_hash), + LUA_INTERFACE_DEF (upstream_list, get_upstream_round_robin), + LUA_INTERFACE_DEF (upstream_list, get_upstream_master_slave), + {"__tostring", lua_class_tostring}, + {"__gc", lua_upstream_list_destroy}, + {NULL, NULL} +}; +static const struct luaL_reg upstream_list_f[] = { + LUA_INTERFACE_DEF (upstream_list, create), + {NULL, NULL} +}; + +/* Upstream functions */ +LUA_FUNCTION_DEF (upstream, create); +LUA_FUNCTION_DEF (upstream, destroy); +LUA_FUNCTION_DEF (upstream, ok); +LUA_FUNCTION_DEF (upstream, fail); +LUA_FUNCTION_DEF (upstream, get_ip); +LUA_FUNCTION_DEF (upstream, get_port); +LUA_FUNCTION_DEF (upstream, get_ip_string); +LUA_FUNCTION_DEF (upstream, get_priority); + +static const struct luaL_reg upstream_m[] = { + LUA_INTERFACE_DEF (upstream, ok), + LUA_INTERFACE_DEF (upstream, fail), + LUA_INTERFACE_DEF (upstream, get_ip), + LUA_INTERFACE_DEF (upstream, get_ip_string), + LUA_INTERFACE_DEF (upstream, get_port), + LUA_INTERFACE_DEF (upstream, get_priority), + {"__tostring", lua_class_tostring}, + {"__gc", lua_upstream_destroy}, + {NULL, NULL} +}; +static const struct luaL_reg upstream_f[] = { + LUA_INTERFACE_DEF (upstream, create), + {NULL, NULL} +}; + +/* Upstream class */ +struct lua_upstream { + struct upstream up; + gchar *def; + guint16 port; + struct in_addr addr; +}; + +static struct lua_upstream * +lua_check_upstream (lua_State * L) +{ + void *ud = luaL_checkudata (L, 1, "rspamd{upstream}"); + + luaL_argcheck (L, ud != NULL, 1, "'upstream' expected"); + return ud ? *((struct lua_upstream **)ud) : NULL; +} + +/** + * Create new upstream from its string definition like 'ip[:port[:priority]]' or 'host[:port[:priority]]' + * @param L + * @return upstream structure + */ +static gint +lua_upstream_create (lua_State *L) +{ + struct lua_upstream *new, **pnew; + const gchar *def; + + def = luaL_checkstring (L, 1); + if (def) { + new = g_slice_alloc0 (sizeof (struct lua_upstream)); + new->def = g_strdup (def); + if (!parse_host_port_priority (new->def, &new->addr, &new->port, &new->up.priority)) { + g_free (new->def); + g_slice_free1 (sizeof (struct lua_upstream), new); + lua_pushnil (L); + } + else { + pnew = lua_newuserdata (L, sizeof (struct lua_upstream *)); + lua_setclass (L, "rspamd{upstream}", -1); + *pnew = new; + } + } + + return 1; +} + +/** + * Destroy a single upstream object + * @param L + * @return + */ +static gint +lua_upstream_destroy (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + + if (up) { + g_free (up->def); + g_slice_free1 (sizeof (struct lua_upstream), up); + } + + return 0; +} + +/** + * Get ip of upstream in numeric form (guint32) + * @param L + * @return + */ +static gint +lua_upstream_get_ip (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + + if (up) { + lua_pushinteger (L, up->addr.s_addr); + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Get ip of upstream in string form + * @param L + * @return + */ +static gint +lua_upstream_get_ip_string (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + + if (up) { + lua_pushstring (L, inet_ntoa (up->addr)); + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Get port of upstream in numeric form + * @param L + * @return + */ +static gint +lua_upstream_get_port (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + + if (up) { + lua_pushinteger (L, up->port); + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Get port of upstream in numeric form + * @param L + * @return + */ +static gint +lua_upstream_get_priority (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + + if (up) { + lua_pushinteger (L, up->up.priority); + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Make upstream fail, the second argument is time, if absent the current time is used + * @param L + * @return + */ +static gint +lua_upstream_fail (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + time_t now; + + if (up) { + if (lua_gettop (L) >= 2) { + now = luaL_checkinteger (L, 2); + } + else { + now = time (NULL); + } + upstream_fail (&up->up, now); + } + + return 0; +} + +/** + * Make upstream success, the second argument is time, if absent the current time is used + * @param L + * @return + */ +static gint +lua_upstream_ok (lua_State *L) +{ + struct lua_upstream *up = lua_check_upstream (L); + time_t now; + + if (up) { + if (lua_gettop (L) >= 2) { + now = luaL_checkinteger (L, 2); + } + else { + now = time (NULL); + } + upstream_ok (&up->up, now); + } + + return 0; +} + +/* Upstream list class */ +struct lua_upstream_list { + struct lua_upstream *upstreams; + guint count; +}; + +static struct lua_upstream_list * +lua_check_upstream_list (lua_State * L) +{ + void *ud = luaL_checkudata (L, 1, "rspamd{upstream_list}"); + + luaL_argcheck (L, ud != NULL, 1, "'upstream_list' expected"); + return ud ? *((struct lua_upstream_list **)ud) : NULL; +} + +/** + * Create new upstream list from its string definition like '<upstream>,<upstream>;<upstream>' + * @param L + * @return upstream list structure + */ +static gint +lua_upstream_list_create (lua_State *L) +{ + struct lua_upstream_list *new, **pnew; + struct lua_upstream *cur; + const gchar *def; + char **tokens; + guint i, default_port = 0; + + def = luaL_checkstring (L, 1); + if (def) { + if (lua_gettop (L) >= 2) { + default_port = luaL_checkinteger (L, 2); + } + new = g_slice_alloc0 (sizeof (struct lua_upstream_list)); + + tokens = g_strsplit_set (def, ",;", 0); + if (!tokens || !tokens[0]) { + goto err; + } + new->count = g_strv_length (tokens); + new->upstreams = g_slice_alloc0 (new->count * sizeof (struct lua_upstream)); + + for (i = 0; i < new->count; i ++) { + cur = &new->upstreams[i]; + if (!parse_host_port_priority (tokens[i], &cur->addr, &cur->port, &cur->up.priority)) { + goto err; + } + if (cur->port == 0) { + cur->port = default_port; + } + } + pnew = lua_newuserdata (L, sizeof (struct upstream_list *)); + lua_setclass (L, "rspamd{upstream_list}", -1); + *pnew = new; + } + + return 1; +err: + if (tokens) { + g_strfreev (tokens); + } + if (new->upstreams) { + g_slice_free1 (new->count * sizeof (struct lua_upstream), new->upstreams); + } + g_slice_free1 (sizeof (struct lua_upstream_list), new); + lua_pushnil (L); + return 1; +} + +/** + * Destroy a single upstream list object + * @param L + * @return + */ +static gint +lua_upstream_list_destroy (lua_State *L) +{ + struct lua_upstream_list *upl = lua_check_upstream_list (L); + + if (upl) { + if (upl->upstreams) { + g_slice_free1 (upl->count * sizeof (struct lua_upstream), upl->upstreams); + } + g_slice_free1 (sizeof (struct lua_upstream_list), upl); + } + + return 0; +} + +/** + * Get upstream by hash from key, params are: key and time (optional) + * @param L + * @return + */ +static gint +lua_upstream_list_get_upstream_by_hash (lua_State *L) +{ + struct lua_upstream_list *upl; + struct lua_upstream *selected, **pselected; + time_t now; + const gchar *key; + + upl = lua_check_upstream_list (L); + if (upl) { + key = luaL_checkstring (L, 2); + if (key) { + if (lua_gettop (L) >= 3) { + now = luaL_checkinteger (L, 3); + } + else { + now = time (NULL); + } + selected = (struct lua_upstream *)get_upstream_by_hash (upl->upstreams, upl->count, + sizeof (struct lua_upstream), now, + DEFAULT_UPSTREAM_ERROR_TIME, DEFAULT_UPSTREAM_DEAD_TIME, DEFAULT_UPSTREAM_MAXERRORS, + key, 0); + if (selected) { + pselected = lua_newuserdata (L, sizeof (struct lua_upstream *)); + lua_setclass (L, "rspamd{upstream}", -1); + *pselected = selected; + } + else { + lua_pushnil (L); + } + } + else { + lua_pushnil (L); + } + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Get upstream round robin (by current weight), params are: time (optional) + * @param L + * @return + */ +static gint +lua_upstream_list_get_upstream_round_robin (lua_State *L) +{ + struct lua_upstream_list *upl; + struct lua_upstream *selected, **pselected; + time_t now; + + upl = lua_check_upstream_list (L); + if (upl) { + if (lua_gettop (L) >= 2) { + now = luaL_checkinteger (L, 2); + } + else { + now = time (NULL); + } + selected = (struct lua_upstream *)get_upstream_round_robin (upl->upstreams, upl->count, + sizeof (struct lua_upstream), now, + DEFAULT_UPSTREAM_ERROR_TIME, DEFAULT_UPSTREAM_DEAD_TIME, DEFAULT_UPSTREAM_MAXERRORS); + if (selected) { + pselected = lua_newuserdata (L, sizeof (struct lua_upstream *)); + lua_setclass (L, "rspamd{upstream}", -1); + *pselected = selected; + } + else { + lua_pushnil (L); + } + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Get upstream master slave order (by static priority), params are: time (optional) + * @param L + * @return + */ +static gint +lua_upstream_list_get_upstream_master_slave (lua_State *L) +{ + struct lua_upstream_list *upl; + struct lua_upstream *selected, **pselected; + time_t now; + + upl = lua_check_upstream_list (L); + if (upl) { + if (lua_gettop (L) >= 2) { + now = luaL_checkinteger (L, 2); + } + else { + now = time (NULL); + } + selected = (struct lua_upstream *)get_upstream_master_slave (upl->upstreams, upl->count, + sizeof (struct lua_upstream), now, + DEFAULT_UPSTREAM_ERROR_TIME, DEFAULT_UPSTREAM_DEAD_TIME, DEFAULT_UPSTREAM_MAXERRORS); + if (selected) { + pselected = lua_newuserdata (L, sizeof (struct lua_upstream *)); + lua_setclass (L, "rspamd{upstream}", -1); + *pselected = selected; + } + else { + lua_pushnil (L); + } + } + else { + lua_pushnil (L); + } + + return 1; +} + + +gint +luaopen_upstream (lua_State * L) +{ + luaL_newmetatable (L, "rspamd{upstream_list}"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + + lua_pushstring (L, "class"); + lua_pushstring (L, "rspamd{upstream_list}"); + lua_rawset (L, -3); + + luaL_openlib (L, NULL, upstream_list_m, 0); + luaL_openlib (L, "upstream_list", upstream_list_f, 0); + + luaL_newmetatable (L, "rspamd{upstream}"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + + lua_pushstring (L, "class"); + lua_pushstring (L, "rspamd{upstream}"); + lua_rawset (L, -3); + + luaL_openlib (L, NULL, upstream_m, 0); + luaL_openlib (L, "upstream", upstream_f, 0); + + return 1; +} |