diff options
Diffstat (limited to '')
-rw-r--r-- | src/lua/lua_common.h | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/src/lua/lua_common.h b/src/lua/lua_common.h new file mode 100644 index 0000000..cc2b943 --- /dev/null +++ b/src/lua/lua_common.h @@ -0,0 +1,729 @@ +/* + * Copyright 2023 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RSPAMD_LUA_H +#define RSPAMD_LUA_H + +#include "config.h" + +/* Lua headers do not have __cplusplus guards... */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> +#ifdef WITH_LUAJIT +#include <luajit.h> +#endif + +#ifdef __cplusplus +} +#endif +#include <stdbool.h> + + +#include "rspamd.h" +#include "ucl.h" +#include "lua_ucl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef lua_open +#define lua_open() luaL_newstate() +#endif + +#ifndef luaL_reg +#define luaL_reg luaL_Reg +#endif + +#define LUA_ENUM(L, name, val) \ + lua_pushlstring(L, #name, sizeof(#name) - 1); \ + lua_pushinteger(L, val); \ + lua_settable(L, -3); + +#if LUA_VERSION_NUM > 501 && !defined LUA_COMPAT_MODULE +static inline void +luaL_register(lua_State *L, const gchar *name, const struct luaL_reg *methods) +{ + if (name != NULL) { + lua_newtable(L); + } + luaL_setfuncs(L, methods, 0); + if (name != NULL) { + lua_pushvalue(L, -1); + lua_setglobal(L, name); + } +} +#endif + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 + +/* Special hack to work with moonjit of specific version */ +#if !defined(MOONJIT_VERSION) && (!defined(LUAJIT_VERSION_NUM) || LUAJIT_VERSION_NUM != 20200) +static inline int lua_absindex(lua_State *L, int i) +{ + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} +#endif + +#endif + +/* Interface definitions */ +#define LUA_FUNCTION_DEF(class, name) static int lua_##class##_##name(lua_State *L) +#define LUA_PUBLIC_FUNCTION_DEF(class, name) int lua_##class##_##name(lua_State *L) +#define LUA_INTERFACE_DEF(class, name) \ + { \ + #name, lua_##class##_##name \ + } + +extern const luaL_reg null_reg[]; + +#define RSPAMD_LUA_CFG_STATE(cfg) ((lua_State *) ((cfg)->lua_state)) +/** +* Lua IP address structure +*/ +struct rspamd_lua_ip { + rspamd_inet_addr_t *addr; +}; + +#define RSPAMD_TEXT_FLAG_OWN (1u << 0u) +#define RSPAMD_TEXT_FLAG_MMAPED (1u << 1u) +#define RSPAMD_TEXT_FLAG_WIPE (1u << 2u) +#define RSPAMD_TEXT_FLAG_SYSMALLOC (1u << 3u) +#define RSPAMD_TEXT_FLAG_FAKE (1u << 4u) +#define RSPAMD_TEXT_FLAG_BINARY (1u << 5u) +struct rspamd_lua_text { + const gchar *start; + guint len; + guint flags; +}; + +struct rspamd_lua_url { + struct rspamd_url *url; +}; + +struct rspamd_lua_regexp { + rspamd_regexp_t *re; + gchar *module; + gchar *re_pattern; + gint re_flags; +}; + +struct rspamd_map; +struct lua_map_callback_data; +struct radix_tree_compressed; +struct rspamd_mime_header; + +enum rspamd_lua_map_type { + RSPAMD_LUA_MAP_RADIX = 0, + RSPAMD_LUA_MAP_SET, + RSPAMD_LUA_MAP_HASH, + RSPAMD_LUA_MAP_REGEXP, + RSPAMD_LUA_MAP_REGEXP_MULTIPLE, + RSPAMD_LUA_MAP_CALLBACK, + RSPAMD_LUA_MAP_CDB, + RSPAMD_LUA_MAP_UNKNOWN, +}; + +struct rspamd_lua_map { + struct rspamd_map *map; + enum rspamd_lua_map_type type; + guint flags; + + union { + struct rspamd_radix_map_helper *radix; + struct rspamd_hash_map_helper *hash; + struct rspamd_regexp_map_helper *re_map; + struct rspamd_cdb_map_helper *cdb_map; + struct lua_map_callback_data *cbdata; + } data; +}; + +struct rspamd_lua_upstream { + struct upstream *up; + gint upref; +}; + +/* Common utility functions */ + +/** +* Create and register new class +*/ +void rspamd_lua_new_class(lua_State *L, + const gchar *classname, + const struct luaL_reg *methods); + +/** +* Set class name for object at @param objidx position +*/ +void rspamd_lua_setclass(lua_State *L, const gchar *classname, gint objidx); + +/** +* Pushes the metatable for specific class on top of the stack +* @param L +* @param classname +*/ +void rspamd_lua_class_metatable(lua_State *L, const gchar *classname); + +/** +* Adds a new field to the class (metatable) identified by `classname` +* @param L +* @param classname +* @param meth +*/ +void rspamd_lua_add_metamethod(lua_State *L, const gchar *classname, + luaL_Reg *meth); + +/** +* Set index of table to value (like t['index'] = value) +*/ +void rspamd_lua_table_set(lua_State *L, const gchar *index, const gchar *value); + +/** +* Get string value of index in a table (return t['index']) +*/ +const gchar *rspamd_lua_table_get(lua_State *L, const gchar *index); + +/** +* Convert classname to string +*/ +gint rspamd_lua_class_tostring(lua_State *L); + +/** +* Check whether the argument at specified index is of the specified class +*/ +gpointer rspamd_lua_check_class(lua_State *L, gint index, const gchar *name); + +/** +* Initialize lua and bindings +*/ +lua_State *rspamd_lua_init(bool wipe_mem); + +/** + * Close lua_state and free remainders + * @param L + */ +void rspamd_lua_close(lua_State *L); + +void rspamd_lua_start_gc(struct rspamd_config *cfg); + +/** +* Sets field in a global variable +* @param L +* @param global_name +* @param field_name +* @param new_elt +*/ +void rspamd_plugins_table_push_elt(lua_State *L, const gchar *field_name, + const gchar *new_elt); + +/** +* Load and initialize lua plugins +*/ +gboolean +rspamd_init_lua_filters(struct rspamd_config *cfg, bool force_load, bool strict); + + +/** +* Push lua ip address +*/ +void rspamd_lua_ip_push(lua_State *L, rspamd_inet_addr_t *addr); + +/** +* Push rspamd task structure to lua +*/ +void rspamd_lua_task_push(lua_State *L, struct rspamd_task *task); + +/** +* Return lua ip structure at the specified address +*/ +struct rspamd_lua_ip *lua_check_ip(lua_State *L, gint pos); + +struct rspamd_lua_text *lua_check_text(lua_State *L, gint pos); +/** +* Checks for a text or a string. In case of string a pointer to static structure is returned. +* So it should not be reused or placed to Lua stack anyhow! +* However, you can use this function up to 4 times and have distinct static structures +* @param L +* @param pos +* @return +*/ +struct rspamd_lua_text *lua_check_text_or_string(lua_State *L, gint pos); +/** + * Create new text object + * @param L + * @param start + * @param len + * @param own + * @return + */ +struct rspamd_lua_text *lua_new_text(lua_State *L, const gchar *start, + gsize len, gboolean own); +/** + * Create new text object from task pool if allocation is needed + * @param task + * @param L + * @param start + * @param len + * @param own + * @return + */ +struct rspamd_lua_text *lua_new_text_task(lua_State *L, struct rspamd_task *task, + const gchar *start, gsize len, gboolean own); +/** + * Checks if a text has binary characters (non ascii and non-utf8 characters) + * @param t + * @return + */ +bool lua_is_text_binary(struct rspamd_lua_text *t); + +struct rspamd_lua_regexp *lua_check_regexp(lua_State *L, gint pos); + +struct rspamd_lua_upstream *lua_check_upstream(lua_State *L, int pos); + +enum rspamd_lua_task_header_type { + RSPAMD_TASK_HEADER_PUSH_SIMPLE = 0, + RSPAMD_TASK_HEADER_PUSH_RAW, + RSPAMD_TASK_HEADER_PUSH_FULL, + RSPAMD_TASK_HEADER_PUSH_COUNT, + RSPAMD_TASK_HEADER_PUSH_HAS, +}; + +gint rspamd_lua_push_header(lua_State *L, + struct rspamd_mime_header *h, + enum rspamd_lua_task_header_type how); + +/** +* Push specific header to lua +*/ +gint rspamd_lua_push_header_array(lua_State *L, + const gchar *name, + struct rspamd_mime_header *rh, + enum rspamd_lua_task_header_type how, + gboolean strong); + +/** +* Check for task at the specified position +*/ +struct rspamd_task *lua_check_task(lua_State *L, gint pos); + +struct rspamd_task *lua_check_task_maybe(lua_State *L, gint pos); + +struct rspamd_lua_map *lua_check_map(lua_State *L, gint pos); + +/** +* Push ip address from a string (nil is pushed if a string cannot be converted) +*/ +void rspamd_lua_ip_push_fromstring(lua_State *L, const gchar *ip_str); + +/** +* Create type error +*/ +int rspamd_lua_typerror(lua_State *L, int narg, const char *tname); +/** +* Open libraries functions +*/ + +/** +* Add preload function +*/ +void rspamd_lua_add_preload(lua_State *L, const gchar *name, lua_CFunction func); + +void luaopen_task(lua_State *L); + +void luaopen_config(lua_State *L); + +void luaopen_map(lua_State *L); + +void luaopen_trie(lua_State *L); + +void luaopen_textpart(lua_State *L); + +void luaopen_mimepart(lua_State *L); + +void luaopen_image(lua_State *L); + +void luaopen_url(lua_State *L); + +void luaopen_classifier(lua_State *L); + +void luaopen_statfile(lua_State *L); + +void luaopen_regexp(lua_State *L); + +void luaopen_cdb(lua_State *L); + +void luaopen_xmlrpc(lua_State *L); + +void luaopen_http(lua_State *L); + +void luaopen_redis(lua_State *L); + +void luaopen_upstream(lua_State *L); + +void luaopen_mempool(lua_State *L); + +void luaopen_dns_resolver(lua_State *L); + +void luaopen_rsa(lua_State *L); + +void luaopen_ip(lua_State *L); + +void luaopen_expression(lua_State *L); + +void luaopen_logger(lua_State *L); + +void luaopen_text(lua_State *L); + +void luaopen_util(lua_State *L); + +void luaopen_tcp(lua_State *L); + +void luaopen_html(lua_State *L); + +void luaopen_sqlite3(lua_State *L); + +void luaopen_cryptobox(lua_State *L); + +void luaopen_dns(lua_State *L); + +void luaopen_udp(lua_State *L); + +void luaopen_worker(lua_State *L); + +void luaopen_kann(lua_State *L); + +void luaopen_spf(lua_State *L); + +void luaopen_tensor(lua_State *L); + +void luaopen_parsers(lua_State *L); + +void rspamd_lua_dostring(const gchar *line); + +double rspamd_lua_normalize(struct rspamd_config *cfg, + long double score, + void *params); + +/* Config file functions */ +void rspamd_lua_post_load_config(struct rspamd_config *cfg); + +void rspamd_lua_dumpstack(lua_State *L); + +/* Set lua path according to the configuration */ +void rspamd_lua_set_path(lua_State *L, const ucl_object_t *cfg_obj, + GHashTable *vars); + +/* Set some lua globals */ +gboolean rspamd_lua_set_env(lua_State *L, GHashTable *vars, char **lua_env, + GError **err); + +void rspamd_lua_set_globals(struct rspamd_config *cfg, lua_State *L); + +struct memory_pool_s *rspamd_lua_check_mempool(lua_State *L, gint pos); + +struct rspamd_config *lua_check_config(lua_State *L, gint pos); + +struct rspamd_async_session *lua_check_session(lua_State *L, gint pos); + +struct ev_loop *lua_check_ev_base(lua_State *L, gint pos); + +struct rspamd_dns_resolver *lua_check_dns_resolver(lua_State *L, gint pos); + +struct rspamd_lua_url *lua_check_url(lua_State *L, gint pos); + +enum rspamd_lua_parse_arguments_flags { + RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT = 0, + RSPAMD_LUA_PARSE_ARGUMENTS_IGNORE_MISSING, +}; + +/** +* Extract an arguments from lua table according to format string. Supported arguments are: +* [*]key=S|I|N|B|V|U{a-z};[key=...] +* - S - const char * +* - I - gint64_t +* - i - int32_t +* - N - double +* - B - gboolean +* - V - size_t + const char * +* - U{classname} - userdata of the following class (stored in gpointer) +* - F - function +* - O - ucl_object_t * +* - D - same as N but argument is set to NAN not to 0.0 +* - u{classname} - userdata of the following class (stored directly) +* +* If any of keys is prefixed with `*` then it is treated as required argument +* @param L lua state +* @param pos at which pos start extraction +* @param err error pointer +* @param how extraction type (IGNORE_MISSING means that default values will not be set) +* @param extraction_pattern static pattern +* @return TRUE if a table has been parsed +*/ +gboolean rspamd_lua_parse_table_arguments(lua_State *L, gint pos, + GError **err, + enum rspamd_lua_parse_arguments_flags how, + const gchar *extraction_pattern, ...); + + +gint rspamd_lua_traceback(lua_State *L); + +/** +* Returns stack trace as a string. Caller should clear memory. +* @param L +* @return +*/ +void rspamd_lua_get_traceback_string(lua_State *L, luaL_Buffer *buf); + +/** +* Returns size of table at position `tbl_pos` +*/ +guint rspamd_lua_table_size(lua_State *L, gint tbl_pos); + +void lua_push_emails_address_list(lua_State *L, GPtrArray *addrs, int flags); + + +#define TRACE_POINTS 6 + +struct lua_logger_trace { + gint cur_level; + gconstpointer traces[TRACE_POINTS]; +}; + +enum lua_logger_escape_type { + LUA_ESCAPE_NONE = (0u), + LUA_ESCAPE_UNPRINTABLE = (1u << 0u), + LUA_ESCAPE_NEWLINES = (1u << 1u), + LUA_ESCAPE_8BIT = (1u << 2u), +}; + +#define LUA_ESCAPE_LOG (LUA_ESCAPE_UNPRINTABLE | LUA_ESCAPE_NEWLINES) +#define LUA_ESCAPE_ALL (LUA_ESCAPE_UNPRINTABLE | LUA_ESCAPE_NEWLINES | LUA_ESCAPE_8BIT) + +/** +* Log lua object to string +* @param L +* @param pos +* @param outbuf +* @param len +* @return +*/ +gsize lua_logger_out_type(lua_State *L, gint pos, gchar *outbuf, + gsize len, struct lua_logger_trace *trace, + enum lua_logger_escape_type esc_type); + +/** +* Safely checks userdata to match specified class +* @param L +* @param pos +* @param classname +*/ +void *rspamd_lua_check_udata(lua_State *L, gint pos, const gchar *classname); + +#define RSPAMD_LUA_CHECK_UDATA_PTR_OR_RETURN(L, pos, classname, type, dest) \ + do { \ + type **_maybe_ptr = (type **) rspamd_lua_check_udata((L), (pos), (classname)); \ + if (_maybe_ptr == NULL) { \ + return luaL_error(L, "%s: invalid arguments; pos = %d; expected = %s", G_STRFUNC, (pos), (classname)); \ + } \ + (dest) = *(_maybe_ptr); \ + } while (0) + +/** +* Safely checks userdata to match specified class +* @param L +* @param pos +* @param classname +*/ +void *rspamd_lua_check_udata_maybe(lua_State *L, gint pos, const gchar *classname); + +/** +* Call finishing script with the specified task +* @param sc +* @param task +*/ +void lua_call_finish_script(struct rspamd_config_cfg_lua_script *sc, + struct rspamd_task *task); + +/** +* Run post-load operations +* @param L +* @param cfg +* @param ev_base +*/ +void rspamd_lua_run_postloads(lua_State *L, struct rspamd_config *cfg, + struct ev_loop *ev_base, struct rspamd_worker *w); + +void rspamd_lua_run_config_post_init(lua_State *L, struct rspamd_config *cfg); + +void rspamd_lua_run_config_unload(lua_State *L, struct rspamd_config *cfg); + +/** +* Adds new destructor for a local function for specific pool +* @param L +* @param pool +* @param ref +*/ +void rspamd_lua_add_ref_dtor(lua_State *L, rspamd_mempool_t *pool, + gint ref); + +/** + * Returns a lua reference from a function like string, e.g. `return function(...) end` + * @param L + * @param str + * @return + */ +gint rspamd_lua_function_ref_from_str(lua_State *L, const gchar *str, gsize slen, + const gchar *modname, GError **err); + +/** +* Tries to load some module using `require` and get some method from it +* @param L +* @param modname +* @param funcname +* @return TRUE if function exists in that module, the function is pushed in stack, otherwise stack is unchanged and FALSE is returned +*/ +gboolean rspamd_lua_require_function(lua_State *L, const gchar *modname, + const gchar *funcname); + +/** +* Tries to load redis server definition from ucl object specified +* @param L +* @param obj +* @param cfg +* @return +*/ +gboolean rspamd_lua_try_load_redis(lua_State *L, const ucl_object_t *obj, + struct rspamd_config *cfg, gint *ref_id); + +struct rspamd_stat_token_s; + +/** +* Pushes a single word into Lua +* @param L +* @param word +*/ +void rspamd_lua_push_full_word(lua_State *L, struct rspamd_stat_token_s *word); + +enum rspamd_lua_words_type { + RSPAMD_LUA_WORDS_STEM = 0, + RSPAMD_LUA_WORDS_NORM, + RSPAMD_LUA_WORDS_RAW, + RSPAMD_LUA_WORDS_FULL, + RSPAMD_LUA_WORDS_MAX +}; + +/** +* Pushes words (rspamd_stat_token_t) to Lua +* @param L +* @param words +* @param how +*/ +gint rspamd_lua_push_words(lua_State *L, GArray *words, + enum rspamd_lua_words_type how); + +/** +* Returns newly allocated name for caller module name +* @param L +* @return +*/ +gchar *rspamd_lua_get_module_name(lua_State *L); + +/** +* Call Lua function in a universal way. Arguments string: +* - i - lua_integer, argument - gint64 +* - n - lua_number, argument - gdouble +* - s - lua_string, argument - const gchar * (zero terminated) +* - l - lua_lstring, argument - (size_t + const gchar *) pair +* - u - lua_userdata, argument - (const char * + void *) - classname + pointer +* - b - lua_boolean, argument - gboolean (not bool due to varargs promotion) +* - f - lua_function, argument - int - position of the function on stack (not lua_registry) +* - t - lua_text, argument - int - position of the lua_text on stack (not lua_registry) +* @param L lua state +* @param cbref LUA_REGISTRY reference (if it is -1 then a function on top of the stack is called - it must be removed by caller manually) +* @param strloc where this function is called from +* @param nret number of results (or LUA_MULTRET) +* @param args arguments format string +* @param err error to promote +* @param ... arguments +* @return true of pcall returned 0, false + err otherwise +*/ +bool rspamd_lua_universal_pcall(lua_State *L, gint cbref, const gchar *strloc, + gint nret, const gchar *args, GError **err, ...); + +/** + * Returns true if lua is initialised + * @return + */ +bool rspamd_lua_is_initialised(void); + +/** +* Wrapper for lua_geti from lua 5.3 +* @param L +* @param index +* @param i +* @return +*/ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 +gint rspamd_lua_geti(lua_State *L, int index, int i); +#else +#define rspamd_lua_geti lua_geti +#endif + +/* Paths defs */ +#define RSPAMD_CONFDIR_INDEX "CONFDIR" +#define RSPAMD_LOCAL_CONFDIR_INDEX "LOCAL_CONFDIR" +#define RSPAMD_RUNDIR_INDEX "RUNDIR" +#define RSPAMD_DBDIR_INDEX "DBDIR" +#define RSPAMD_LOGDIR_INDEX "LOGDIR" +#define RSPAMD_PLUGINSDIR_INDEX "PLUGINSDIR" +#define RSPAMD_SHAREDIR_INDEX "SHAREDIR" +#define RSPAMD_RULESDIR_INDEX "RULESDIR" +#define RSPAMD_LUALIBDIR_INDEX "LUALIBDIR" +#define RSPAMD_WWWDIR_INDEX "WWWDIR" +#define RSPAMD_PREFIX_INDEX "PREFIX" +#define RSPAMD_VERSION_INDEX "VERSION" + +#ifdef WITH_LUA_TRACE +extern ucl_object_t *lua_traces; +#define LUA_TRACE_POINT \ + do { \ + ucl_object_t *func_obj; \ + if (lua_traces == NULL) { lua_traces = ucl_object_typed_new(UCL_OBJECT); } \ + func_obj = (ucl_object_t *) ucl_object_lookup(lua_traces, G_STRFUNC); \ + if (func_obj == NULL) { \ + func_obj = ucl_object_typed_new(UCL_INT); \ + ucl_object_insert_key(lua_traces, func_obj, G_STRFUNC, 0, false); \ + } \ + func_obj->value.iv++; \ + } while (0) +#else +#define LUA_TRACE_POINT \ + do { \ + } while (0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* RSPAMD_LUA_H */ |