You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_expression.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "lua_common.h"
  17. #include "expression.h"
  18. /***
  19. * @module rspamd_expression
  20. * This module can be used to implement different logic expressions in lua using
  21. * rspamd AST optimizer. There are some examples in individual methods definitions to help understanding of this module.
  22. */
  23. /***
  24. * @function rspamd_expression.create(line, {parse_func, [process_func]}, pool)
  25. * Create expression from the line using atom parsing routines and the specified memory pool
  26. * @param {string} line expression line
  27. * @param {table} atom_functions parse_atom function and optional process_atom function
  28. * @param {rspamd_mempool} memory pool to use for this function
  29. * @return {expr, err} expression object and error message of `expr` is nil
  30. * @example
  31. require "fun" ()
  32. local rspamd_expression = require "rspamd_expression"
  33. local rspamd_mempool = require "rspamd_mempool"
  34. local function parse_func(str)
  35. -- extract token till the first space character
  36. local token = table.concat(totable(take_while(function(s) return s ~= ' ' end, iter(str))))
  37. -- Return token name
  38. return token
  39. end
  40. local function process_func(token)
  41. -- Do something using token and task
  42. end
  43. local pool = rspamd_mempool.create()
  44. local expr,err = rspamd_expression.create('A & B | !C', {parse_func, process_func}, pool)
  45. -- Expression is destroyed when the corresponding pool is destroyed
  46. pool:destroy()
  47. */
  48. LUA_FUNCTION_DEF(expr, create);
  49. /***
  50. * @method rspamd_expression:to_string()
  51. * Converts rspamd expression to string
  52. * @return {string} string representation of rspamd expression
  53. */
  54. LUA_FUNCTION_DEF(expr, to_string);
  55. /***
  56. * @method rspamd_expression:process([callback, ]input[, flags])
  57. * Executes the expression and pass input to process atom callbacks
  58. * @param {function} callback if not specified on process, then callback must be here
  59. * @param {any} input input data for processing callbacks
  60. * @return {number} result of the expression evaluation
  61. */
  62. LUA_FUNCTION_DEF(expr, process);
  63. /***
  64. * @method rspamd_expression:process_traced([callback, ]input[, flags])
  65. * Executes the expression and pass input to process atom callbacks. This function also saves the full trace
  66. * @param {function} callback if not specified on process, then callback must be here
  67. * @param {any} input input data for processing callbacks
  68. * @return {number, table of matched atoms} result of the expression evaluation
  69. */
  70. LUA_FUNCTION_DEF(expr, process_traced);
  71. /***
  72. * @method rspamd_expression:atoms()
  73. * Extract all atoms from the expression as table of strings
  74. * @return {table/strings} list of all atoms in the expression
  75. */
  76. LUA_FUNCTION_DEF(expr, atoms);
  77. static const struct luaL_reg exprlib_m[] = {
  78. LUA_INTERFACE_DEF(expr, to_string),
  79. LUA_INTERFACE_DEF(expr, atoms),
  80. LUA_INTERFACE_DEF(expr, process),
  81. LUA_INTERFACE_DEF(expr, process_traced),
  82. {"__tostring", lua_expr_to_string},
  83. {NULL, NULL}};
  84. static const struct luaL_reg exprlib_f[] = {
  85. LUA_INTERFACE_DEF(expr, create),
  86. {NULL, NULL}};
  87. static rspamd_expression_atom_t *lua_atom_parse(const char *line, gsize len,
  88. rspamd_mempool_t *pool, gpointer ud, GError **err);
  89. static double lua_atom_process(gpointer runtime_ud, rspamd_expression_atom_t *atom);
  90. static const struct rspamd_atom_subr lua_atom_subr = {
  91. .parse = lua_atom_parse,
  92. .process = lua_atom_process,
  93. .priority = NULL,
  94. .destroy = NULL};
  95. struct lua_expression {
  96. struct rspamd_expression *expr;
  97. int parse_idx;
  98. int process_idx;
  99. lua_State *L;
  100. rspamd_mempool_t *pool;
  101. };
  102. static GQuark
  103. lua_expr_quark(void)
  104. {
  105. return g_quark_from_static_string("lua-expression");
  106. }
  107. struct lua_expression *
  108. rspamd_lua_expression(lua_State *L, int pos)
  109. {
  110. void *ud = rspamd_lua_check_udata(L, pos, rspamd_expr_classname);
  111. luaL_argcheck(L, ud != NULL, pos, "'expr' expected");
  112. return ud ? *((struct lua_expression **) ud) : NULL;
  113. }
  114. static void
  115. lua_expr_dtor(gpointer p)
  116. {
  117. struct lua_expression *e = (struct lua_expression *) p;
  118. if (e->parse_idx != -1) {
  119. luaL_unref(e->L, LUA_REGISTRYINDEX, e->parse_idx);
  120. }
  121. if (e->process_idx != -1) {
  122. luaL_unref(e->L, LUA_REGISTRYINDEX, e->process_idx);
  123. }
  124. }
  125. static rspamd_expression_atom_t *
  126. lua_atom_parse(const char *line, gsize len,
  127. rspamd_mempool_t *pool, gpointer ud, GError **err)
  128. {
  129. struct lua_expression *e = (struct lua_expression *) ud;
  130. rspamd_expression_atom_t *atom;
  131. gsize rlen;
  132. const char *tok;
  133. lua_rawgeti(e->L, LUA_REGISTRYINDEX, e->parse_idx);
  134. lua_pushlstring(e->L, line, len);
  135. if (lua_pcall(e->L, 1, 1, 0) != 0) {
  136. msg_info("callback call failed: %s", lua_tostring(e->L, -1));
  137. lua_pop(e->L, 1);
  138. return NULL;
  139. }
  140. if (lua_type(e->L, -1) != LUA_TSTRING) {
  141. g_set_error(err, lua_expr_quark(), 500, "cannot parse lua atom");
  142. lua_pop(e->L, 1);
  143. return NULL;
  144. }
  145. tok = lua_tolstring(e->L, -1, &rlen);
  146. atom = rspamd_mempool_alloc0(e->pool, sizeof(*atom));
  147. atom->str = rspamd_mempool_strdup(e->pool, tok);
  148. atom->len = rlen;
  149. atom->data = ud;
  150. lua_pop(e->L, 1);
  151. return atom;
  152. }
  153. struct lua_atom_process_data {
  154. lua_State *L;
  155. struct lua_expression *e;
  156. int process_cb_pos;
  157. int stack_item;
  158. };
  159. static double
  160. lua_atom_process(gpointer runtime_ud, rspamd_expression_atom_t *atom)
  161. {
  162. struct lua_atom_process_data *pd = (struct lua_atom_process_data *) runtime_ud;
  163. double ret = 0;
  164. unsigned int nargs;
  165. int err_idx;
  166. if (pd->stack_item != -1) {
  167. nargs = 2;
  168. }
  169. else {
  170. nargs = 1;
  171. }
  172. lua_pushcfunction(pd->L, &rspamd_lua_traceback);
  173. err_idx = lua_gettop(pd->L);
  174. /* Function position */
  175. lua_pushvalue(pd->L, pd->process_cb_pos);
  176. /* Atom name */
  177. lua_pushlstring(pd->L, atom->str, atom->len);
  178. /* If we have data passed */
  179. if (pd->stack_item != -1) {
  180. lua_pushvalue(pd->L, pd->stack_item);
  181. }
  182. if (lua_pcall(pd->L, nargs, 1, err_idx) != 0) {
  183. msg_info("expression process callback failed: %s", lua_tostring(pd->L, -1));
  184. }
  185. else {
  186. ret = lua_tonumber(pd->L, -1);
  187. }
  188. lua_settop(pd->L, err_idx - 1);
  189. return ret;
  190. }
  191. static int
  192. lua_expr_process(lua_State *L)
  193. {
  194. LUA_TRACE_POINT;
  195. struct lua_expression *e = rspamd_lua_expression(L, 1);
  196. struct lua_atom_process_data pd;
  197. double res;
  198. int flags = 0, old_top;
  199. pd.L = L;
  200. pd.e = e;
  201. old_top = lua_gettop(L);
  202. if (e->process_idx == -1) {
  203. if (!lua_isfunction(L, 2)) {
  204. return luaL_error(L, "expression process is called with no callback function");
  205. }
  206. pd.process_cb_pos = 2;
  207. if (lua_type(L, 3) != LUA_TNONE && lua_type(L, 3) != LUA_TNIL) {
  208. pd.stack_item = 3;
  209. }
  210. else {
  211. pd.stack_item = -1;
  212. }
  213. if (lua_isnumber(L, 4)) {
  214. flags = lua_tointeger(L, 4);
  215. }
  216. }
  217. else {
  218. lua_rawgeti(L, LUA_REGISTRYINDEX, e->process_idx);
  219. pd.process_cb_pos = lua_gettop(L);
  220. if (lua_type(L, 2) != LUA_TNONE && lua_type(L, 2) != LUA_TNIL) {
  221. pd.stack_item = 2;
  222. }
  223. else {
  224. pd.stack_item = -1;
  225. }
  226. if (lua_isnumber(L, 3)) {
  227. flags = lua_tointeger(L, 3);
  228. }
  229. }
  230. res = rspamd_process_expression(e->expr, flags, &pd);
  231. lua_settop(L, old_top);
  232. lua_pushnumber(L, res);
  233. return 1;
  234. }
  235. static int
  236. lua_expr_process_traced(lua_State *L)
  237. {
  238. LUA_TRACE_POINT;
  239. struct lua_expression *e = rspamd_lua_expression(L, 1);
  240. struct lua_atom_process_data pd;
  241. double res;
  242. int flags = 0, old_top;
  243. GPtrArray *trace;
  244. pd.L = L;
  245. pd.e = e;
  246. old_top = lua_gettop(L);
  247. if (e->process_idx == -1) {
  248. if (!lua_isfunction(L, 2)) {
  249. return luaL_error(L, "expression process is called with no callback function");
  250. }
  251. pd.process_cb_pos = 2;
  252. pd.stack_item = 3;
  253. if (lua_isnumber(L, 4)) {
  254. flags = lua_tointeger(L, 4);
  255. }
  256. }
  257. else {
  258. lua_rawgeti(L, LUA_REGISTRYINDEX, e->process_idx);
  259. pd.process_cb_pos = lua_gettop(L);
  260. pd.stack_item = 2;
  261. if (lua_isnumber(L, 3)) {
  262. flags = lua_tointeger(L, 3);
  263. }
  264. }
  265. res = rspamd_process_expression_track(e->expr, flags, &pd, &trace);
  266. lua_settop(L, old_top);
  267. lua_pushnumber(L, res);
  268. lua_createtable(L, trace->len, 0);
  269. for (unsigned int i = 0; i < trace->len; i++) {
  270. struct rspamd_expression_atom_s *atom = g_ptr_array_index(trace, i);
  271. lua_pushlstring(L, atom->str, atom->len);
  272. lua_rawseti(L, -2, i + 1);
  273. }
  274. g_ptr_array_free(trace, TRUE);
  275. return 2;
  276. }
  277. static int
  278. lua_expr_create(lua_State *L)
  279. {
  280. LUA_TRACE_POINT;
  281. struct lua_expression *e, **pe;
  282. const char *line;
  283. gsize len;
  284. gboolean no_process = FALSE;
  285. GError *err = NULL;
  286. rspamd_mempool_t *pool;
  287. /* Check sanity of the arguments */
  288. if (lua_type(L, 1) != LUA_TSTRING ||
  289. (lua_type(L, 2) != LUA_TTABLE && lua_type(L, 2) != LUA_TFUNCTION) ||
  290. rspamd_lua_check_mempool(L, 3) == NULL) {
  291. lua_pushnil(L);
  292. lua_pushstring(L, "bad arguments");
  293. }
  294. else {
  295. line = lua_tolstring(L, 1, &len);
  296. pool = rspamd_lua_check_mempool(L, 3);
  297. e = rspamd_mempool_alloc(pool, sizeof(*e));
  298. e->L = L;
  299. e->pool = pool;
  300. /* Check callbacks */
  301. if (lua_istable(L, 2)) {
  302. lua_pushvalue(L, 2);
  303. lua_pushnumber(L, 1);
  304. lua_gettable(L, -2);
  305. if (lua_type(L, -1) != LUA_TFUNCTION) {
  306. lua_pop(L, 1);
  307. lua_pushnil(L);
  308. lua_pushstring(L, "bad parse callback");
  309. return 2;
  310. }
  311. lua_pop(L, 1);
  312. lua_pushnumber(L, 2);
  313. lua_gettable(L, -2);
  314. if (lua_type(L, -1) != LUA_TFUNCTION) {
  315. if (lua_type(L, -1) != LUA_TNIL && lua_type(L, -1) != LUA_TNONE) {
  316. lua_pop(L, 1);
  317. lua_pushnil(L);
  318. lua_pushstring(L, "bad process callback");
  319. return 2;
  320. }
  321. else {
  322. no_process = TRUE;
  323. }
  324. }
  325. lua_pop(L, 1);
  326. /* Table is still on the top of stack */
  327. lua_pushnumber(L, 1);
  328. lua_gettable(L, -2);
  329. e->parse_idx = luaL_ref(L, LUA_REGISTRYINDEX);
  330. if (!no_process) {
  331. lua_pushnumber(L, 2);
  332. lua_gettable(L, -2);
  333. e->process_idx = luaL_ref(L, LUA_REGISTRYINDEX);
  334. }
  335. else {
  336. e->process_idx = -1;
  337. }
  338. lua_pop(L, 1); /* Table */
  339. }
  340. else {
  341. /* Process function is just a function, not a table */
  342. lua_pushvalue(L, 2);
  343. e->parse_idx = luaL_ref(L, LUA_REGISTRYINDEX);
  344. e->process_idx = -1;
  345. }
  346. if (!rspamd_parse_expression(line, len, &lua_atom_subr, e, pool, &err,
  347. &e->expr)) {
  348. lua_pushnil(L);
  349. lua_pushstring(L, err->message);
  350. g_error_free(err);
  351. lua_expr_dtor(e);
  352. return 2;
  353. }
  354. rspamd_mempool_add_destructor(pool, lua_expr_dtor, e);
  355. pe = lua_newuserdata(L, sizeof(struct lua_expression *));
  356. rspamd_lua_setclass(L, rspamd_expr_classname, -1);
  357. *pe = e;
  358. lua_pushnil(L);
  359. }
  360. return 2;
  361. }
  362. static int
  363. lua_expr_to_string(lua_State *L)
  364. {
  365. LUA_TRACE_POINT;
  366. struct lua_expression *e = rspamd_lua_expression(L, 1);
  367. GString *str;
  368. if (e != NULL && e->expr != NULL) {
  369. str = rspamd_expression_tostring(e->expr);
  370. if (str) {
  371. lua_pushlstring(L, str->str, str->len);
  372. g_string_free(str, TRUE);
  373. }
  374. else {
  375. lua_pushnil(L);
  376. }
  377. }
  378. else {
  379. lua_pushnil(L);
  380. }
  381. return 1;
  382. }
  383. struct lua_expr_atoms_cbdata {
  384. lua_State *L;
  385. int idx;
  386. };
  387. static void
  388. lua_exr_atom_cb(const rspamd_ftok_t *tok, gpointer ud)
  389. {
  390. struct lua_expr_atoms_cbdata *cbdata = ud;
  391. lua_pushlstring(cbdata->L, tok->begin, tok->len);
  392. lua_rawseti(cbdata->L, -2, cbdata->idx++);
  393. }
  394. static int
  395. lua_expr_atoms(lua_State *L)
  396. {
  397. LUA_TRACE_POINT;
  398. struct lua_expression *e = rspamd_lua_expression(L, 1);
  399. struct lua_expr_atoms_cbdata cbdata;
  400. if (e != NULL && e->expr != NULL) {
  401. lua_newtable(L);
  402. cbdata.L = L;
  403. cbdata.idx = 1;
  404. rspamd_expression_atom_foreach(e->expr, lua_exr_atom_cb, &cbdata);
  405. }
  406. else {
  407. lua_pushnil(L);
  408. }
  409. return 1;
  410. }
  411. static int
  412. lua_load_expression(lua_State *L)
  413. {
  414. lua_newtable(L);
  415. luaL_register(L, NULL, exprlib_f);
  416. return 1;
  417. }
  418. void luaopen_expression(lua_State *L)
  419. {
  420. rspamd_lua_new_class(L, rspamd_expr_classname, exprlib_m);
  421. lua_pop(L, 1);
  422. rspamd_lua_add_preload(L, "rspamd_expression", lua_load_expression);
  423. }