From: Jason Ish Date: Thu, 24 Apr 2025 18:11:28 +0000 (-0600) Subject: lua: convert flowvar functions to lib X-Git-Tag: suricata-8.0.0-rc1~410 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35b03b4077c908251396f74fe0fb50e881b44e8e;p=thirdparty%2Fsuricata.git lua: convert flowvar functions to lib New Lua lib, "suricata.flowvar" for working with flowvars from Lua. Replaces functions: - SCFlowvarGet (and ScFlowvarGet) - SCFlowvarSet (and SCFlowvarSet) Of note, the DetectLuaData has been made available to the init and thread_init methods, instead of just the match. This is due to an issue that if a flow variable is not registered in init, it will not be logged, registering in thread_init is too late. Ticket: #7486 --- diff --git a/doc/userguide/lua/libs/flowvar.rst b/doc/userguide/lua/libs/flowvar.rst new file mode 100644 index 0000000000..0822bc383b --- /dev/null +++ b/doc/userguide/lua/libs/flowvar.rst @@ -0,0 +1,79 @@ +Flowvar +####### + +The ``suricata.flowvar`` library exposes flow variables to Lua +scripts. + +Initialization +-------------- + +First, the ``flowvar`` lib module must be loaded:: + + local flowvarlib = require("suricata.flow") + +Then in the ``init`` method, any flow variables used in the script +should be registered. This is optional and could be skipped if you +know for sure the flow variable will be registered by some other +means. + +Example:: + + local flowvarlib = require("suricata.flow") + + function init () + flowvarlib.register("count") + return {} + end + +Finally, in the ``thread_init`` function a handle is acquired for the +flow variables and stored as a global:: + + function thread_init () + count_flow_var = flowvarlib.get("count") + end + +Flow Variable Methods +--------------------- + +``value()`` +^^^^^^^^^^^ + +Get the current value of the flow variable as a string. Note that +``nil`` may be returned if the flow variable does not have a value. + +``set(value, len)`` +^^^^^^^^^^^^^^^^^^^ + +Set the value of the flow variable to the value provided. The length +of the value must also be provided. + +Example +------- + +:: + + local flowvarlib = require("suricata.flowvar") + + function init () + flowvarlib.register("count") + return {} + end + + function thread_init () + count_var = flowvarlib.get("count") + end + + function match () + local value = count_var:value() + if value == nil then + -- Initialize value to 1. + value = tostring(1) + count_var:set(value, #value) + else + value = tostring(tonumber(value) + 1) + count_var:set(value, #value) + fi + + -- Return 1 or 0 based on your own logic. + return 1 + end diff --git a/doc/userguide/lua/libs/index.rst b/doc/userguide/lua/libs/index.rst index c043da27ad..2279b74175 100644 --- a/doc/userguide/lua/libs/index.rst +++ b/doc/userguide/lua/libs/index.rst @@ -11,6 +11,7 @@ environment without access to additional modules. base64 dns flowlib + flowvar hashlib http packetlib diff --git a/doc/userguide/lua/lua-functions.rst b/doc/userguide/lua/lua-functions.rst index 9f02125af8..120b2ad01e 100644 --- a/doc/userguide/lua/lua-functions.rst +++ b/doc/userguide/lua/lua-functions.rst @@ -675,38 +675,6 @@ SCFlowintDecr Decrement Flowint at index given by the first parameter. -SCFlowvarGet -~~~~~~~~~~~~ - -Get the Flowvar at index given by the parameter. - -SCFlowvarSet -~~~~~~~~~~~~ - -Set a Flowvar. First parameter is the index, second is the data -and third is the length of data. - -You can use it to set string - -:: - - function init (args) - local needs = {} - needs["http.request_headers"] = tostring(true) - needs["flowvar"] = {"cnt"} - return needs - end - - function match(args) - a = SCFlowvarGet(0); - if a then - a = tostring(tonumber(a)+1) - SCFlowvarSet(0, a, #a) - else - a = tostring(1) - SCFlowvarSet(0, a, #a) - end - Misc ---- diff --git a/src/Makefile.am b/src/Makefile.am index 19f8557b39..1d02693a0c 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -526,6 +526,7 @@ noinst_HEADERS = \ util-lua-dnp3-objects.h \ util-lua-dns.h \ util-lua-flowlib.h \ + util-lua-flowvarlib.h \ util-lua.h \ util-lua-hashlib.h \ util-lua-hassh.h \ @@ -1094,6 +1095,7 @@ libsuricata_c_a_SOURCES = \ util-lua-dnp3-objects.c \ util-lua-dns.c \ util-lua-flowlib.c \ + util-lua-flowvarlib.c \ util-lua-hashlib.c \ util-lua-hassh.c \ util-lua-http.c \ diff --git a/src/detect-lua-extensions.c b/src/detect-lua-extensions.c index ba51db8c7e..dd6a9736b3 100644 --- a/src/detect-lua-extensions.c +++ b/src/detect-lua-extensions.c @@ -47,7 +47,8 @@ #include "util-lua-dnp3.h" #include "detect-lua-extensions.h" -static const char luaext_key_ld[] = "suricata:luadata"; +/* Lua registry key for DetectLuaData. */ +const char luaext_key_ld[] = "suricata:luadata"; static int GetLuaData(lua_State *luastate, DetectLuaData **ret_ld) { @@ -74,67 +75,6 @@ static int GetFlow(lua_State *luastate, Flow **ret_f) return 0; } -static int GetFlowVarById(lua_State *luastate, Flow *f, - FlowVar **ret_fv, bool fv_may_be_null, uint32_t *ret_idx) -{ - DetectLuaData *ld = NULL; - if (ret_idx) - *ret_idx = 0; - *ret_fv = NULL; - - /* need lua data for id -> idx conversion */ - int ret = GetLuaData(luastate, &ld); - if (ret != 0) - return ret; - - if (!lua_isnumber(luastate, 1)) { - LUA_ERROR("flowvar id not a number"); - } - int id = lua_tonumber(luastate, 1); - if (id < 0 || id >= DETECT_LUA_MAX_FLOWVARS) { - LUA_ERROR("flowvar id out of range"); - } - uint32_t idx = ld->flowvar[id]; - if (idx == 0) { - LUA_ERROR("flowvar id uninitialized"); - } - FlowVar *fv = FlowVarGet(f, idx); - if (!fv_may_be_null && fv == NULL) { - LUA_ERROR("no flow var"); - } - *ret_fv = fv; - if (ret_idx) - *ret_idx = idx; - return 0; -} - -static int GetFlowVarByKey(lua_State *luastate, Flow *f, FlowVar **ret_fv) -{ - *ret_fv = NULL; - - if (!lua_isstring(luastate, 1)) { - LUA_ERROR("flowvar key not a string"); - } - const char *keystr = lua_tostring(luastate, 1); - if (keystr == NULL) { - LUA_ERROR("key is null"); - } - if (!lua_isnumber(luastate, 2)) { - LUA_ERROR("key length not specified"); - } - int keylen = lua_tonumber(luastate, 2); - if (keylen < 0 || keylen > 0xff) { - LUA_ERROR("key len out of range: max 256"); - } - - FlowVar *fv = FlowVarGetByKey(f, (const uint8_t *)keystr, (uint8_t)keylen); - if (fv == NULL) { - LUA_ERROR("no flow var"); - } - *ret_fv = fv; - return 0; -} - static int GetFlowIntById(lua_State *luastate, Flow *f, FlowVar **ret_fv, bool fv_may_be_null, uint32_t *ret_idx) { @@ -169,151 +109,6 @@ static int GetFlowIntById(lua_State *luastate, Flow *f, return 0; } -static int LuaGetFlowvar(lua_State *luastate) -{ - Flow *f; - FlowVar *fv; - int ret; - - /* need flow */ - ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - if (lua_isnumber(luastate, 1)) { - ret = GetFlowVarById(luastate, f, &fv, false, NULL); - if (ret != 0 || fv == NULL) - return ret; - } else if (lua_isstring(luastate, 1)) { - ret = GetFlowVarByKey(luastate, f, &fv); - if (ret != 0 || fv == NULL) - return ret; - } else { - LUA_ERROR("invalid data type as first argument"); - } - - LuaPushStringBuffer(luastate, - (const uint8_t *)fv->data.fv_str.value, - (size_t)fv->data.fv_str.value_len); - return 1; -} - -static int LuaSetFlowvarById(lua_State *luastate) -{ - uint32_t idx = 0; - Flow *f; - const char *str; - int len; - uint8_t *buffer; - FlowVar *fv = NULL; - - /* need flow */ - int ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - ret = GetFlowVarById(luastate, f, &fv, true, &idx); - if (ret != 0) - return ret; - - if (!lua_isstring(luastate, 2)) { - LUA_ERROR("buffer not a string"); - } - str = lua_tostring(luastate, 2); - if (str == NULL) { - LUA_ERROR("buffer is null"); - } - - if (!lua_isnumber(luastate, 3)) { - LUA_ERROR("buffer length not specified"); - } - len = lua_tonumber(luastate, 3); - if (len < 0 || len > 0xffff) { - LUA_ERROR("len out of range: max 64k"); - } - - buffer = SCMalloc(len+1); - if (unlikely(buffer == NULL)) { - LUA_ERROR("out of memory"); - } - memcpy(buffer, str, len); - buffer[len] = '\0'; - - FlowVarAddIdValue(f, idx, buffer, (uint16_t)len); - return 0; -} - -static int LuaSetFlowvarByKey(lua_State *luastate) -{ - Flow *f; - const char *str; - int len; - uint8_t *buffer; - - /* need flow */ - int ret = GetFlow(luastate, &f); - if (ret != 0) - return ret; - - const char *keystr = NULL; - int keylen = 0; - - keystr = lua_tostring(luastate, 1); - if (keystr == NULL) { - LUA_ERROR("key is null"); - } - if (!lua_isnumber(luastate, 2)) { - LUA_ERROR("key length not specified"); - } - keylen = lua_tonumber(luastate, 2); - if (keylen < 0 || keylen > 0xff) { - LUA_ERROR("key len out of range: max 255"); - } - - if (!lua_isstring(luastate, 3)) { - LUA_ERROR("buffer not a string"); - } - str = lua_tostring(luastate, 3); - if (str == NULL) { - LUA_ERROR("buffer is null"); - } - - if (!lua_isnumber(luastate, 4)) { - LUA_ERROR("buffer length not specified"); - } - len = lua_tonumber(luastate, 4); - if (len < 0 || len > 0xffff) { - LUA_ERROR("len out of range: max 64k"); - } - - buffer = SCMalloc(len+1); - if (unlikely(buffer == NULL)) { - LUA_ERROR("out of memory"); - } - memcpy(buffer, str, len); - buffer[len] = '\0'; - - uint8_t *keybuf = SCMalloc(keylen+1); - if (unlikely(keybuf == NULL)) { - SCFree(buffer); - LUA_ERROR("out of memory"); - } - memcpy(keybuf, keystr, keylen); - keybuf[keylen] = '\0'; - FlowVarAddKeyValue(f, keybuf, (uint8_t)keylen, buffer, (uint16_t)len); - - return 0; -} - -static int LuaSetFlowvar(lua_State *luastate) -{ - if (lua_isnumber(luastate, 1)) { - return LuaSetFlowvarById(luastate); - } else { - return LuaSetFlowvarByKey(luastate); - } -} - static int LuaGetFlowint(lua_State *luastate) { Flow *f; @@ -478,13 +273,7 @@ void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *ld, { SCLogDebug("det_ctx %p, f %p", det_ctx, f); - /* lua keyword data */ - lua_pushlightuserdata(lua_state, (void *)&luaext_key_ld); - lua_pushlightuserdata(lua_state, (void *)ld); - lua_settable(lua_state, LUA_REGISTRYINDEX); - LuaStateSetSignature(lua_state, s); - LuaStateSetFlow(lua_state, f); LuaStateSetDetCtx(lua_state, det_ctx); @@ -508,18 +297,6 @@ void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *ld, */ int LuaRegisterExtensions(lua_State *lua_state) { - lua_pushcfunction(lua_state, LuaGetFlowvar); - lua_setglobal(lua_state, "ScFlowvarGet"); - - lua_pushcfunction(lua_state, LuaGetFlowvar); - lua_setglobal(lua_state, "SCFlowvarGet"); - - lua_pushcfunction(lua_state, LuaSetFlowvar); - lua_setglobal(lua_state, "ScFlowvarSet"); - - lua_pushcfunction(lua_state, LuaSetFlowvar); - lua_setglobal(lua_state, "SCFlowvarSet"); - lua_pushcfunction(lua_state, LuaGetFlowint); lua_setglobal(lua_state, "ScFlowintGet"); diff --git a/src/detect-lua-extensions.h b/src/detect-lua-extensions.h index 7a631a15a3..3620fd0849 100644 --- a/src/detect-lua-extensions.h +++ b/src/detect-lua-extensions.h @@ -24,6 +24,8 @@ #ifndef SURICATA_DETECT_LUA_EXT_H #define SURICATA_DETECT_LUA_EXT_H +extern const char luaext_key_ld[]; + int LuaRegisterExtensions(lua_State *); void LuaExtensionsMatchSetup(lua_State *lua_state, DetectLuaData *, DetectEngineThreadCtx *det_ctx, diff --git a/src/detect-lua.c b/src/detect-lua.c index 1ba922788b..9137afeba1 100644 --- a/src/detect-lua.c +++ b/src/detect-lua.c @@ -146,6 +146,13 @@ void LuaDumpStack(lua_State *state, const char *prefix) } } +static void LuaStateSetDetectLuaData(lua_State *state, DetectLuaData *data) +{ + lua_pushlightuserdata(state, (void *)&luaext_key_ld); + lua_pushlightuserdata(state, (void *)data); + lua_settable(state, LUA_REGISTRYINDEX); +} + /** * \brief Common function to run the Lua match function and process * the return value. @@ -406,6 +413,7 @@ static void *DetectLuaThreadInit(void *data) } LuaRegisterExtensions(t->luastate); + LuaStateSetDetectLuaData(t->luastate, lua); /* hackish, needed to allow unittests to pass buffers as scripts instead of files */ #ifdef UNITTESTS @@ -513,6 +521,7 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const } else { SCLuaSbLoadLibs(luastate); } + LuaStateSetDetectLuaData(luastate, ld); /* hackish, needed to allow unittests to pass buffers as scripts instead of files */ #ifdef UNITTESTS @@ -804,22 +813,25 @@ static int LuaMatchTest01(void) { SCConfSetFinal("security.lua.allow-rules", "true"); - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowvar\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n" + "function init (args)\n" + " flowvarlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "function thread_init (args)\n" + " cnt = flowvarlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" - " a = ScFlowvarGet(0)\n" + " a = cnt:value()\n" " if a then\n" " a = tostring(tonumber(a)+1)\n" " print (a)\n" - " ScFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " else\n" " a = tostring(1)\n" " print (a)\n" - " ScFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " end\n" " \n" " print (\"pre check: \" .. (a))\n" @@ -926,22 +938,25 @@ static int LuaMatchTest01(void) static int LuaMatchTest01a(void) { - const char script[] = "function init (args)\n" - " local needs = {}\n" - " needs[\"flowvar\"] = {\"cnt\"}\n" - " return needs\n" + const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n" + "function init (args)\n" + " flowvarlib.register(\"cnt\")\n" + " return {}\n" + "end\n" + "function thread_init (args)\n" + " cnt = flowvarlib.get(\"cnt\")\n" "end\n" "\n" "function match(args)\n" - " a = SCFlowvarGet(0)\n" + " a = cnt:value(0)\n" " if a then\n" " a = tostring(tonumber(a)+1)\n" " print (a)\n" - " SCFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " else\n" " a = tostring(1)\n" " print (a)\n" - " SCFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " end\n" " \n" " print (\"pre check: \" .. (a))\n" @@ -1047,23 +1062,27 @@ static int LuaMatchTest01a(void) /** \test payload buffer */ static int LuaMatchTest02(void) { - const char script[] = "function init (args)\n" + const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n" + "function init (args)\n" + " flowvarlib.register(\"cnt\")\n" " local needs = {}\n" " needs[\"payload\"] = tostring(true)\n" - " needs[\"flowvar\"] = {\"cnt\"}\n" " return needs\n" "end\n" + "function thread_init (args)\n" + " cnt = flowvarlib.get(\"cnt\")\n" + "end\n" "\n" "function match(args)\n" - " a = ScFlowvarGet(0)\n" + " a = cnt:value()\n" " if a then\n" " a = tostring(tonumber(a)+1)\n" " print (a)\n" - " ScFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " else\n" " a = tostring(1)\n" " print (a)\n" - " ScFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " end\n" " \n" " print (\"pre check: \" .. (a))\n" @@ -1154,23 +1173,27 @@ static int LuaMatchTest02(void) /** \test payload buffer */ static int LuaMatchTest02a(void) { - const char script[] = "function init (args)\n" + const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n" + "function init (args)\n" + " flowvarlib.register(\"cnt\")" " local needs = {}\n" " needs[\"payload\"] = tostring(true)\n" - " needs[\"flowvar\"] = {\"cnt\"}\n" " return needs\n" "end\n" + "function thread_init (args)\n" + " cnt = flowvarlib.get(\"cnt\")" + "end\n" "\n" "function match(args)\n" - " a = SCFlowvarGet(0)\n" + " a = cnt:value()\n" " if a then\n" " a = tostring(tonumber(a)+1)\n" " print (a)\n" - " SCFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " else\n" " a = tostring(1)\n" " print (a)\n" - " SCFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " end\n" " \n" " print (\"pre check: \" .. (a))\n" @@ -1260,23 +1283,28 @@ static int LuaMatchTest02a(void) /** \test packet buffer */ static int LuaMatchTest03(void) { - const char script[] = "function init (args)\n" + const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n" + "function init (args)\n" + " flowvarlib.register(\"cnt\")\n" " local needs = {}\n" " needs[\"packet\"] = tostring(true)\n" - " needs[\"flowvar\"] = {\"cnt\"}\n" " return needs\n" "end\n" "\n" + "function thread_init (args)\n" + " cnt = flowvarlib.get(\"cnt\")\n" + "end\n" + "\n" "function match(args)\n" - " a = ScFlowvarGet(0)\n" + " a = cnt:value()\n" " if a then\n" " a = tostring(tonumber(a)+1)\n" " print (a)\n" - " ScFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " else\n" " a = tostring(1)\n" " print (a)\n" - " ScFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " end\n" " \n" " print (\"pre check: \" .. (a))\n" @@ -1366,23 +1394,28 @@ static int LuaMatchTest03(void) /** \test packet buffer */ static int LuaMatchTest03a(void) { - const char script[] = "function init (args)\n" + const char script[] = "local flowvarlib = require(\"suricata.flowvar\")\n" + "function init (args)\n" + " flowvarlib.register(\"cnt\")\n" " local needs = {}\n" " needs[\"packet\"] = tostring(true)\n" - " needs[\"flowvar\"] = {\"cnt\"}\n" " return needs\n" "end\n" "\n" + "function thread_init (args)\n" + " cnt = flowvarlib.get(\"cnt\")\n" + "end\n" + "\n" "function match(args)\n" - " a = SCFlowvarGet(0)\n" + " a = cnt:value()\n" " if a then\n" " a = tostring(tonumber(a)+1)\n" " print (a)\n" - " SCFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " else\n" " a = tostring(1)\n" " print (a)\n" - " SCFlowvarSet(0, a, #a)\n" + " cnt:set(a, #a)\n" " end\n" " \n" " print (\"pre check: \" .. (a))\n" diff --git a/src/util-lua-builtins.c b/src/util-lua-builtins.c index 2bf22ba3c1..7a51749f4b 100644 --- a/src/util-lua-builtins.c +++ b/src/util-lua-builtins.c @@ -20,6 +20,7 @@ #include "util-lua-base64lib.h" #include "util-lua-dataset.h" #include "util-lua-dnp3.h" +#include "util-lua-flowvarlib.h" #include "util-lua-http.h" #include "util-lua-dns.h" #include "util-lua-ssh.h" @@ -36,6 +37,7 @@ static const luaL_Reg builtins[] = { { "suricata.dnp3", SCLuaLoadDnp3Lib }, { "suricata.dns", SCLuaLoadDnsLib }, { "suricata.flow", LuaLoadFlowLib }, + { "suricata.flowvar", LuaLoadFlowvarLib }, { "suricata.hashlib", SCLuaLoadHashlib }, { "suricata.http", SCLuaLoadHttpLib }, { "suricata.packet", LuaLoadPacketLib }, diff --git a/src/util-lua-flowvarlib.c b/src/util-lua-flowvarlib.c new file mode 100644 index 0000000000..3cd6978e6d --- /dev/null +++ b/src/util-lua-flowvarlib.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2025 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "suricata-common.h" +#include "app-layer-protos.h" +#include "flow-var.h" +#include "lauxlib.h" +#include "util-debug.h" +#include "util-lua-common.h" +#include "util-lua-flowvarlib.h" +#include "util-lua.h" +#include "util-var-name.h" +#include "detect-lua.h" +#include "detect-lua-extensions.h" + +static const char suricata_flowvar_mt[] = "suricata:flowvar:mt"; + +static DetectLuaData *GetLuaData(lua_State *luastate) +{ + DetectLuaData *ld; + lua_pushlightuserdata(luastate, (void *)&luaext_key_ld); + lua_gettable(luastate, LUA_REGISTRYINDEX); + ld = lua_touserdata(luastate, -1); + return ld; +} + +/** + * \brief Register a flowvar. + * + * Ensures that a flowvar exists for the provided name, will be + * created if needed. + * + * The flowvar ID is returned, however as this is most likely to be + * used in the scripts "init" method, this ID is unlikely to be used. + */ +static int LuaFlowvarRegister(lua_State *L) +{ + DetectLuaData *ld = GetLuaData(L); + const char *name = luaL_checkstring(L, 1); + uint32_t *flowvar_id = lua_newuserdata(L, sizeof(*flowvar_id)); + *flowvar_id = VarNameStoreRegister(name, VAR_TYPE_FLOW_VAR); + if (*flowvar_id == 0) { + return luaL_error(L, "failed to register flowvar"); + } + ld->flowvar[ld->flowvars++] = *flowvar_id; + + luaL_getmetatable(L, suricata_flowvar_mt); + lua_setmetatable(L, -2); + + return 1; +} + +static int LuaFlowvarGet(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + uint32_t *flowvar_id = lua_newuserdata(L, sizeof(*flowvar_id)); + *flowvar_id = VarNameStoreLookupByName(name, VAR_TYPE_FLOW_VAR); + if (*flowvar_id == 0) { + return luaL_error(L, "flowvar does not exist"); + } + + luaL_getmetatable(L, suricata_flowvar_mt); + lua_setmetatable(L, -2); + + return 1; +} + +static int LuaFlowvarValue(lua_State *L) +{ + uint32_t *flowvar_id = luaL_testudata(L, 1, suricata_flowvar_mt); + Flow *f = LuaStateGetFlow(L); + if (f == NULL) { + return LuaCallbackError(L, "flow is NULL"); + } + FlowVar *fv = FlowVarGet(f, *flowvar_id); + if (fv == NULL) { + lua_pushnil(L); + } else { + LuaPushStringBuffer( + L, (const uint8_t *)fv->data.fv_str.value, (size_t)fv->data.fv_str.value_len); + } + return 1; +} + +static int LuaFlowvarSet(lua_State *L) +{ + const char *value = luaL_checkstring(L, 2); + const int len = luaL_checkinteger(L, 3); + uint32_t *flowvar_id = luaL_checkudata(L, 1, suricata_flowvar_mt); + Flow *f = LuaStateGetFlow(L); + if (f == NULL) { + return luaL_error(L, "no flow"); + } + + uint8_t *buf = SCMalloc(len + 1); + memcpy(buf, value, len); + buf[len] = '\0'; + FlowVarAddIdValue(f, *flowvar_id, buf, (uint16_t)len); + + return 1; +} + +static const luaL_Reg flowvarlib[] = { + // clang-format off + { "register", LuaFlowvarRegister, }, +{ "get", LuaFlowvarGet }, + { NULL, NULL, }, + // clang-format on +}; + +static const luaL_Reg flowvarmt[] = { + // clang-format off + { "value", LuaFlowvarValue, }, + { "set", LuaFlowvarSet, }, + { NULL, NULL, }, + // clang-format on +}; + +int LuaLoadFlowvarLib(lua_State *L) +{ + luaL_newmetatable(L, suricata_flowvar_mt); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, flowvarmt, 0); + + luaL_newlib(L, flowvarlib); + return 1; +} diff --git a/src/util-lua-flowvarlib.h b/src/util-lua-flowvarlib.h new file mode 100644 index 0000000000..51b23a1c02 --- /dev/null +++ b/src/util-lua-flowvarlib.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2025 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef SURICATA_UTIL_LUA_FLOWVARLIB_H +#define SURICATA_UTIL_LUA_FLOWVARLIB_H + +#include "lua.h" + +int LuaLoadFlowvarLib(lua_State *L); + +#endif /* SURICATA_UTIL_LUA_FLOWVARLIB_H */