• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22550237931

01 Mar 2026 06:56PM UTC coverage: 64.812% (-8.9%) from 73.687%
22550237931

Pull #14920

github

web-flow
Merge e05854a6d into 90823fa90
Pull Request #14920: draft: rust based configuration file parser and loader - v4

561 of 789 new or added lines in 4 files covered. (71.1%)

23225 existing lines in 498 files now uncovered.

131605 of 203055 relevant lines covered (64.81%)

2328818.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.7
/src/output-lua.c
1
/* Copyright (C) 2014-2022 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 */
24

25
#include "suricata-common.h"
26
#include "output-lua.h"
27
#include "action-globals.h"
28
#include "util-lua-builtins.h"
29
#include "util-debug.h"
30
#include "output.h"
31
#include "app-layer-htp.h"
32
#include "app-layer-ssl.h"
33
#include "app-layer-ssh.h"
34
#include "app-layer-parser.h"
35
#include "util-time.h"
36
#include "util-path.h"
37
#include "util-lua.h"
38
#include "util-lua-common.h"
39
#include "util-lua-http.h"
40
#include "util-lua-smtp.h"
41

42
#define MODULE_NAME "LuaLog"
29✔
43

44
/** \brief structure containing global config
45
 *  The OutputLuaLogInitSub which is run per script
46
 *  can access this to get global config info through
47
 *  it's parent_ctx->data ptr.
48
 */
49
typedef struct LogLuaMasterCtx_ {
50
    /** \brief Path to script directory. */
51
    char script_dir[PATH_MAX];
52

53
    /** \brief Lua search path for Lua modules. */
54
    char path[PATH_MAX];
55

56
    /** \brief Lua search path for C modules. */
57
    char cpath[PATH_MAX];
58
} LogLuaMasterCtx;
59

60
typedef struct LogLuaCtx_ {
61
    SCMutex m;
62
    lua_State *luastate;
63
    int deinit_once;
64
} LogLuaCtx;
65

66
typedef struct LogLuaThreadCtx_ {
67
    LogLuaCtx *lua_ctx;
68
} LogLuaThreadCtx;
69

70
static TmEcode LuaLogThreadInit(ThreadVars *t, const void *initdata, void **data);
71
static TmEcode LuaLogThreadDeinit(ThreadVars *t, void *data);
72

73
/** \internal
74
 *  \brief TX logger for lua scripts
75
 *
76
 * A single call to this function will run one script on a single
77
 * transaction.
78
 *
79
 * NOTE: The flow (f) also referenced by p->flow is locked.
80
 */
81
static int LuaTxLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
UNCOV
82
{
×
UNCOV
83
    SCEnter();
×
84

UNCOV
85
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
86

UNCOV
87
    SCMutexLock(&td->lua_ctx->m);
×
88

UNCOV
89
    LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
×
UNCOV
90
    LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
×
UNCOV
91
    LuaStateSetTX(td->lua_ctx->luastate, txptr, tx_id);
×
UNCOV
92
    LuaStateSetFlow(td->lua_ctx->luastate, f);
×
93

94
    /* prepare data to pass to script */
UNCOV
95
    lua_getglobal(td->lua_ctx->luastate, "log");
×
UNCOV
96
    lua_newtable(td->lua_ctx->luastate);
×
UNCOV
97
    LuaPushTableKeyValueInt(td->lua_ctx->luastate, "tx_id", (int)(tx_id));
×
98

UNCOV
99
    int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
×
UNCOV
100
    if (retval != 0) {
×
UNCOV
101
        SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
UNCOV
102
    }
×
103

UNCOV
104
    SCMutexUnlock(&td->lua_ctx->m);
×
UNCOV
105
    SCReturnInt(0);
×
UNCOV
106
}
×
107

108
/** \internal
109
 *  \brief Streaming logger for lua scripts
110
 *
111
 *  Hooks into the Streaming Logger API. Gets called for each chunk of new
112
 *  streaming data.
113
 */
114
static int LuaStreamingLogger(ThreadVars *tv, void *thread_data, const Flow *f,
115
        const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
UNCOV
116
{
×
UNCOV
117
    SCEnter();
×
118

UNCOV
119
    void *txptr = NULL;
×
UNCOV
120
    LuaStreamingBuffer b = { data, data_len, flags };
×
121

UNCOV
122
    SCLogDebug("flags %02x", flags);
×
123

UNCOV
124
    if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION) {
×
UNCOV
125
        if (f && f->alstate)
×
UNCOV
126
            txptr = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, tx_id);
×
UNCOV
127
    }
×
128

UNCOV
129
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
UNCOV
130
    SCMutexLock(&td->lua_ctx->m);
×
UNCOV
131
    lua_State *luastate = td->lua_ctx->luastate;
×
132

UNCOV
133
    LuaStateSetThreadVars(luastate, tv);
×
UNCOV
134
    if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION)
×
UNCOV
135
        LuaStateSetTX(luastate, txptr, tx_id);
×
UNCOV
136
    LuaStateSetFlow(luastate, (Flow *)f);
×
UNCOV
137
    LuaStateSetStreamingBuffer(luastate, &b);
×
138

139
    /* prepare data to pass to script */
UNCOV
140
    lua_getglobal(luastate, "log");
×
UNCOV
141
    lua_newtable(luastate);
×
142

UNCOV
143
    if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION)
×
UNCOV
144
        LuaPushTableKeyValueInt(luastate, "tx_id", (int)(tx_id));
×
145

146
    /* create the "stream" subtable */
UNCOV
147
    lua_pushstring(luastate, "stream");
×
UNCOV
148
    lua_newtable(luastate);
×
149

UNCOV
150
    LuaPushTableKeyValueLString(luastate, "data", (const char *)data, data_len);
×
UNCOV
151
    LuaPushTableKeyValueBoolean(luastate, "open", flags & OUTPUT_STREAMING_FLAG_OPEN);
×
UNCOV
152
    LuaPushTableKeyValueBoolean(luastate, "close", flags & OUTPUT_STREAMING_FLAG_CLOSE);
×
UNCOV
153
    LuaPushTableKeyValueBoolean(luastate, "to_server", flags & OUTPUT_STREAMING_FLAG_TOSERVER);
×
UNCOV
154
    LuaPushTableKeyValueBoolean(luastate, "to_client", flags & OUTPUT_STREAMING_FLAG_TOCLIENT);
×
155

156
    /* set the "stream" subtable into the main args table */
UNCOV
157
    lua_settable(luastate, -3);
×
158

UNCOV
159
    int retval = lua_pcall(luastate, 1, 0, 0);
×
UNCOV
160
    if (retval != 0) {
×
161
        SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
162
    }
×
163

UNCOV
164
    SCMutexUnlock(&td->lua_ctx->m);
×
165

UNCOV
166
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
167
}
×
168

169
/** \internal
170
 *  \brief Packet Logger for lua scripts, for alerts
171
 *
172
 *  A single call to this function will run one script for a single
173
 *  packet. If it is called, it means that the registered condition
174
 *  function has returned true.
175
 *
176
 *  The script is called once for each alert stored in the packet.
177
 *
178
 *  NOTE: p->flow is UNlocked
179
 */
180
static int LuaPacketLoggerAlerts(ThreadVars *tv, void *thread_data, const Packet *p)
UNCOV
181
{
×
UNCOV
182
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
183

UNCOV
184
    char timebuf[64];
×
UNCOV
185
    CreateTimeString(p->ts, timebuf, sizeof(timebuf));
×
186

UNCOV
187
    if (!(PacketIsIPv4(p)) && !(PacketIsIPv6(p))) {
×
188
        /* decoder event */
189
        goto not_supported;
×
190
    }
×
191

192
    /* loop through alerts stored in the packet */
UNCOV
193
    SCMutexLock(&td->lua_ctx->m);
×
UNCOV
194
    uint16_t cnt;
×
UNCOV
195
    for (cnt = 0; cnt < p->alerts.cnt; cnt++) {
×
UNCOV
196
        const PacketAlert *pa = &p->alerts.alerts[cnt];
×
UNCOV
197
        if (unlikely((pa->s == NULL) || (pa->action & ACTION_ALERT) == 0)) {
×
198
            continue;
×
199
        }
×
200

UNCOV
201
        lua_getglobal(td->lua_ctx->luastate, "log");
×
202

UNCOV
203
        void *txptr = NULL;
×
UNCOV
204
        if (p->flow && p->flow->alstate && (pa->flags & PACKET_ALERT_FLAG_TX))
×
205
            txptr = AppLayerParserGetTx(
×
206
                    p->flow->proto, p->flow->alproto, p->flow->alstate, pa->tx_id);
×
207

UNCOV
208
        LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
×
UNCOV
209
        LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
×
UNCOV
210
        LuaStateSetTX(td->lua_ctx->luastate, txptr, pa->tx_id);
×
UNCOV
211
        LuaStateSetFlow(td->lua_ctx->luastate, p->flow);
×
UNCOV
212
        LuaStateSetPacketAlert(td->lua_ctx->luastate, (PacketAlert *)pa);
×
213

214
        /* prepare data to pass to script */
215
        //lua_newtable(td->lua_ctx->luastate);
216

UNCOV
217
        int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0);
×
UNCOV
218
        if (retval != 0) {
×
219
            SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
220
        }
×
UNCOV
221
    }
×
UNCOV
222
    SCMutexUnlock(&td->lua_ctx->m);
×
UNCOV
223
not_supported:
×
UNCOV
224
    SCReturnInt(0);
×
UNCOV
225
}
×
226

227
static bool LuaPacketConditionAlerts(ThreadVars *tv, void *data, const Packet *p)
UNCOV
228
{
×
UNCOV
229
    return (p->alerts.cnt > 0);
×
UNCOV
230
}
×
231

232
/** \internal
233
 *  \brief Packet Logger for lua scripts, for packets
234
 *
235
 *  A single call to this function will run one script for a single
236
 *  packet. If it is called, it means that the registered condition
237
 *  function has returned true.
238
 *
239
 *  The script is called once for each packet.
240
 *
241
 *  NOTE: p->flow is UNlocked
242
 */
243
static int LuaPacketLogger(ThreadVars *tv, void *thread_data, const Packet *p)
UNCOV
244
{
×
UNCOV
245
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
246

UNCOV
247
    char timebuf[64];
×
248

UNCOV
249
    if ((!(PacketIsIPv4(p))) && (!(PacketIsIPv6(p)))) {
×
250
        goto not_supported;
×
251
    }
×
252

UNCOV
253
    CreateTimeString(p->ts, timebuf, sizeof(timebuf));
×
254

255
    /* loop through alerts stored in the packet */
UNCOV
256
    SCMutexLock(&td->lua_ctx->m);
×
UNCOV
257
    lua_getglobal(td->lua_ctx->luastate, "log");
×
258

UNCOV
259
    LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
×
UNCOV
260
    LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
×
UNCOV
261
    LuaStateSetFlow(td->lua_ctx->luastate, p->flow);
×
262

263
    /* prepare data to pass to script */
UNCOV
264
    lua_newtable(td->lua_ctx->luastate);
×
265

UNCOV
266
    int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
×
UNCOV
267
    if (retval != 0) {
×
268
        SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
269
    }
×
UNCOV
270
    SCMutexUnlock(&td->lua_ctx->m);
×
UNCOV
271
not_supported:
×
UNCOV
272
    SCReturnInt(0);
×
UNCOV
273
}
×
274

275
static bool LuaPacketCondition(ThreadVars *tv, void *data, const Packet *p)
UNCOV
276
{
×
UNCOV
277
    return true;
×
UNCOV
278
}
×
279

280
/** \internal
281
 *  \brief File API Logger function for Lua scripts
282
 *
283
 *  Executes a script once for one file.
284
 *
285
 * NOTE p->flow is locked at this point
286
 */
287
static int LuaFileLogger(ThreadVars *tv, void *thread_data, const Packet *p, const File *ff,
288
        void *tx, const uint64_t tx_id, uint8_t dir)
UNCOV
289
{
×
UNCOV
290
    SCEnter();
×
UNCOV
291
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
292

UNCOV
293
    if ((!(PacketIsIPv4(p))) && (!(PacketIsIPv6(p))))
×
294
        return 0;
×
295

UNCOV
296
    DEBUG_VALIDATE_BUG_ON(ff->flags & FILE_LOGGED);
×
297

UNCOV
298
    SCLogDebug("ff %p", ff);
×
299

UNCOV
300
    SCMutexLock(&td->lua_ctx->m);
×
301

UNCOV
302
    LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
×
UNCOV
303
    LuaStateSetPacket(td->lua_ctx->luastate, (Packet *)p);
×
UNCOV
304
    LuaStateSetTX(td->lua_ctx->luastate, tx, tx_id);
×
UNCOV
305
    LuaStateSetFlow(td->lua_ctx->luastate, p->flow);
×
UNCOV
306
    LuaStateSetFile(td->lua_ctx->luastate, (File *)ff);
×
307

308
    /* get the lua function to call */
UNCOV
309
    lua_getglobal(td->lua_ctx->luastate, "log");
×
310

UNCOV
311
    int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0);
×
UNCOV
312
    if (retval != 0) {
×
313
        SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
314
    }
×
UNCOV
315
    SCMutexUnlock(&td->lua_ctx->m);
×
UNCOV
316
    return 0;
×
UNCOV
317
}
×
318

319
/** \internal
320
 *  \brief Flow API Logger function for Lua scripts
321
 *
322
 *  Executes a script once for one flow
323
 *
324
 *  Note: flow 'f' is locked
325
 */
326
static int LuaFlowLogger(ThreadVars *tv, void *thread_data, Flow *f)
UNCOV
327
{
×
UNCOV
328
    SCEnter();
×
UNCOV
329
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
330

UNCOV
331
    SCLogDebug("f %p", f);
×
332

UNCOV
333
    SCMutexLock(&td->lua_ctx->m);
×
334

UNCOV
335
    LuaStateSetThreadVars(td->lua_ctx->luastate, tv);
×
UNCOV
336
    LuaStateSetFlow(td->lua_ctx->luastate, f);
×
337

338
    /* get the lua function to call */
UNCOV
339
    lua_getglobal(td->lua_ctx->luastate, "log");
×
340

UNCOV
341
    int retval = lua_pcall(td->lua_ctx->luastate, 0, 0, 0);
×
UNCOV
342
    if (retval != 0) {
×
343
        SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
344
    }
×
UNCOV
345
    SCMutexUnlock(&td->lua_ctx->m);
×
UNCOV
346
    return 0;
×
UNCOV
347
}
×
348

349

350

351
static int LuaStatsLogger(ThreadVars *tv, void *thread_data, const StatsTable *st)
UNCOV
352
{
×
UNCOV
353
    SCEnter();
×
UNCOV
354
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data;
×
355

UNCOV
356
    SCMutexLock(&td->lua_ctx->m);
×
357

UNCOV
358
    lua_State *luastate = td->lua_ctx->luastate;
×
359
    /* get the lua function to call */
UNCOV
360
    lua_getglobal(td->lua_ctx->luastate, "log");
×
361

362
    /* create lua array, which is really just a table. The key is an int (1-x),
363
     * the value another table with named fields: name, tm_name, value, pvalue.
364
     * { 1, { name=<name>, tmname=<tm_name>, value=<value>, pvalue=<pvalue>}}
365
     * { 2, { name=<name>, tmname=<tm_name>, value=<value>, pvalue=<pvalue>}}
366
     * etc
367
     */
UNCOV
368
    lua_newtable(luastate);
×
UNCOV
369
    uint32_t u = 0;
×
UNCOV
370
    for (; u < st->nstats; u++) {
×
UNCOV
371
        lua_pushinteger(luastate, u + 1);
×
372

UNCOV
373
        lua_newtable(luastate);
×
374

UNCOV
375
        lua_pushstring(luastate, "name");
×
UNCOV
376
        lua_pushstring(luastate, st->stats[u].name);
×
UNCOV
377
        lua_settable(luastate, -3);
×
378

UNCOV
379
        lua_pushstring(luastate, "tmname");
×
UNCOV
380
        lua_pushstring(luastate, st->stats[u].tm_name);
×
UNCOV
381
        lua_settable(luastate, -3);
×
382

UNCOV
383
        lua_pushstring(luastate, "value");
×
UNCOV
384
        lua_pushinteger(luastate, st->stats[u].value);
×
UNCOV
385
        lua_settable(luastate, -3);
×
386

UNCOV
387
        lua_pushstring(luastate, "pvalue");
×
UNCOV
388
        lua_pushinteger(luastate, st->stats[u].pvalue);
×
UNCOV
389
        lua_settable(luastate, -3);
×
390

UNCOV
391
        lua_settable(luastate, -3);
×
UNCOV
392
    }
×
393

UNCOV
394
    int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0);
×
UNCOV
395
    if (retval != 0) {
×
396
        SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1));
×
397
    }
×
UNCOV
398
    SCMutexUnlock(&td->lua_ctx->m);
×
UNCOV
399
    return 0;
×
400

UNCOV
401
}
×
402

403
typedef struct LogLuaScriptOptions_ {
404
    AppProto alproto;
405
    int packet;
406
    int alerts;
407
    int file;
408
    int streaming;
409
    int tcp_data;
410
    int http_body;
411
    int flow;
412
    int stats;
413
} LogLuaScriptOptions;
414

415
/** \brief Setup or clear Lua module search paths.
416
 *
417
 * If search paths are provided by the configuration, set them up,
418
 * otherwise clear the default search paths.
419
 */
420
static void LuaSetPaths(lua_State *L, LogLuaMasterCtx *ctx)
UNCOV
421
{
×
UNCOV
422
    lua_getglobal(L, "package");
×
423

UNCOV
424
    if (strlen(ctx->path) > 0) {
×
425
        lua_pushstring(L, ctx->path);
×
UNCOV
426
    } else {
×
UNCOV
427
        lua_pushstring(L, "");
×
UNCOV
428
    }
×
UNCOV
429
    lua_setfield(L, -2, "path");
×
430

UNCOV
431
    if (strlen(ctx->cpath) > 0) {
×
432
        lua_pushstring(L, ctx->cpath);
×
UNCOV
433
    } else {
×
UNCOV
434
        lua_pushstring(L, "");
×
UNCOV
435
    }
×
UNCOV
436
    lua_setfield(L, -2, "cpath");
×
437

438
    /* Pop package. */
UNCOV
439
    lua_pop(L, 1);
×
UNCOV
440
}
×
441

442
/** \brief load and evaluate the script
443
 *
444
 *  This function parses the script, checks if all the required functions
445
 *  are defined and runs the 'init' function. The init function will inform
446
 *  us what the scripts needs are.
447
 *
448
 *  \param filename filename of lua script file
449
 *  \param options struct to pass script requirements/options back to caller
450
 *  \retval errcode 0 ok, -1 error
451
 */
452
static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options, LogLuaMasterCtx *ctx)
UNCOV
453
{
×
UNCOV
454
    lua_State *luastate = LuaGetState();
×
UNCOV
455
    if (luastate == NULL)
×
456
        goto error;
×
UNCOV
457
    luaL_openlibs(luastate);
×
UNCOV
458
    SCLuaRequirefBuiltIns(luastate);
×
UNCOV
459
    LuaSetPaths(luastate, ctx);
×
460

UNCOV
461
    int status = luaL_loadfile(luastate, filename);
×
UNCOV
462
    if (status) {
×
463
        SCLogError("couldn't load file: %s", lua_tostring(luastate, -1));
×
464
        goto error;
×
465
    }
×
466

467
    /* prime the script (or something) */
UNCOV
468
    if (lua_pcall(luastate, 0, 0, 0) != 0) {
×
469
        SCLogError("couldn't prime file: %s", lua_tostring(luastate, -1));
×
470
        goto error;
×
471
    }
×
472

UNCOV
473
    lua_getglobal(luastate, "init");
×
UNCOV
474
    if (lua_type(luastate, -1) != LUA_TFUNCTION) {
×
475
        SCLogError("no init function in script");
×
476
        goto error;
×
477
    }
×
478

UNCOV
479
    if (lua_pcall(luastate, 0, 1, 0) != 0) {
×
480
        SCLogError("couldn't run script 'init' function: %s", lua_tostring(luastate, -1));
×
481
        goto error;
×
482
    }
×
483

484
    /* process returns from script */
UNCOV
485
    if (lua_gettop(luastate) == 0) {
×
486
        SCLogError("init function in script should return table, nothing returned");
×
487
        goto error;
×
488
    }
×
UNCOV
489
    if (lua_type(luastate, 1) != LUA_TTABLE) {
×
490
        SCLogError("init function in script should return table, returned is not table");
×
491
        goto error;
×
492
    }
×
493

UNCOV
494
    lua_pushnil(luastate);
×
UNCOV
495
    const char *k, *v;
×
UNCOV
496
    while (lua_next(luastate, -2)) {
×
UNCOV
497
        k = lua_tostring(luastate, -2);
×
UNCOV
498
        if (k == NULL)
×
499
            continue;
×
500

UNCOV
501
        v = lua_tostring(luastate, -1);
×
UNCOV
502
        lua_pop(luastate, 1);
×
UNCOV
503
        if (v == NULL)
×
504
            continue;
×
505

UNCOV
506
        SCLogDebug("k='%s', v='%s'", k, v);
×
507

UNCOV
508
        if (strcmp(k, "streaming") == 0) {
×
UNCOV
509
            options->streaming = 1;
×
UNCOV
510
            if (strcmp(v, "http") == 0) {
×
UNCOV
511
                options->alproto = ALPROTO_HTTP1;
×
UNCOV
512
            } else if (strcmp(v, "tcp") == 0) {
×
UNCOV
513
                options->tcp_data = 1;
×
UNCOV
514
            } else {
×
515
                SCLogError("unsupported streaming argument: %s", v);
×
516
                goto error;
×
517
            }
×
UNCOV
518
        } else if (strcmp(k, "protocol") == 0 && strcmp(v, "http") == 0)
×
UNCOV
519
            options->alproto = ALPROTO_HTTP1;
×
UNCOV
520
        else if (strcmp(k,"protocol") == 0 && strcmp(v, "dns") == 0)
×
UNCOV
521
            options->alproto = ALPROTO_DNS;
×
UNCOV
522
        else if (strcmp(k,"protocol") == 0 && strcmp(v, "tls") == 0)
×
UNCOV
523
            options->alproto = ALPROTO_TLS;
×
UNCOV
524
        else if (strcmp(k,"protocol") == 0 && strcmp(v, "ssh") == 0)
×
UNCOV
525
            options->alproto = ALPROTO_SSH;
×
UNCOV
526
        else if (strcmp(k,"protocol") == 0 && strcmp(v, "smtp") == 0)
×
UNCOV
527
            options->alproto = ALPROTO_SMTP;
×
UNCOV
528
        else if (strcmp(k, "type") == 0 && strcmp(v, "packet") == 0)
×
UNCOV
529
            options->packet = 1;
×
UNCOV
530
        else if (strcmp(k, "filter") == 0 && strcmp(v, "alerts") == 0)
×
UNCOV
531
            options->alerts = 1;
×
UNCOV
532
        else if (strcmp(k, "type") == 0 && strcmp(v, "file") == 0)
×
UNCOV
533
            options->file = 1;
×
UNCOV
534
        else if (strcmp(k, "type") == 0 && strcmp(v, "streaming") == 0)
×
535
            options->streaming = 1;
×
UNCOV
536
        else if (strcmp(k, "type") == 0 && strcmp(v, "flow") == 0)
×
UNCOV
537
            options->flow = 1;
×
UNCOV
538
        else if (strcmp(k, "filter") == 0 && strcmp(v, "tcp") == 0)
×
539
            options->tcp_data = 1;
×
UNCOV
540
        else if (strcmp(k, "type") == 0 && strcmp(v, "stats") == 0)
×
UNCOV
541
            options->stats = 1;
×
542
        else {
×
543
            SCLogError("unknown key and/or value: k='%s', v='%s'", k, v);
×
544
            goto error;
×
545
        }
×
UNCOV
546
    }
×
547

UNCOV
548
    if (((options->alproto != ALPROTO_UNKNOWN)) + options->packet + options->file > 1) {
×
549
        SCLogError("invalid combination of 'needs' in the script");
×
550
        goto error;
×
551
    }
×
552

UNCOV
553
    lua_getglobal(luastate, "setup");
×
UNCOV
554
    if (lua_type(luastate, -1) != LUA_TFUNCTION) {
×
555
        SCLogError("no setup function in script");
×
556
        goto error;
×
557
    }
×
558

UNCOV
559
    lua_getglobal(luastate, "log");
×
UNCOV
560
    if (lua_type(luastate, -1) != LUA_TFUNCTION) {
×
561
        SCLogError("no log function in script");
×
562
        goto error;
×
563
    }
×
564

UNCOV
565
    lua_getglobal(luastate, "deinit");
×
UNCOV
566
    if (lua_type(luastate, -1) != LUA_TFUNCTION) {
×
567
        SCLogError("no deinit function in script");
×
568
        goto error;
×
569
    }
×
570

UNCOV
571
    LuaReturnState(luastate);
×
UNCOV
572
    return 0;
×
573
error:
×
574
    if (luastate)
×
575
        LuaReturnState(luastate);
×
576
    return -1;
×
UNCOV
577
}
×
578

579
/** \brief setup a luastate for use at runtime
580
 *
581
 *  This loads the script, primes it and then runs the 'setup' function.
582
 *
583
 *  \retval state Returns the set up luastate on success, NULL on error
584
 */
585
static lua_State *LuaScriptSetup(const char *filename, LogLuaMasterCtx *ctx)
UNCOV
586
{
×
UNCOV
587
    lua_State *luastate = LuaGetState();
×
UNCOV
588
    if (luastate == NULL) {
×
589
        SCLogError("luaL_newstate failed");
×
590
        goto error;
×
591
    }
×
592

UNCOV
593
    luaL_openlibs(luastate);
×
UNCOV
594
    SCLuaRequirefBuiltIns(luastate);
×
UNCOV
595
    LuaSetPaths(luastate, ctx);
×
596

UNCOV
597
    int status = luaL_loadfile(luastate, filename);
×
UNCOV
598
    if (status) {
×
599
        SCLogError("couldn't load file: %s", lua_tostring(luastate, -1));
×
600
        goto error;
×
601
    }
×
602

603
    /* prime the script */
UNCOV
604
    if (lua_pcall(luastate, 0, 0, 0) != 0) {
×
605
        SCLogError("couldn't prime file: %s", lua_tostring(luastate, -1));
×
606
        goto error;
×
607
    }
×
608

UNCOV
609
    lua_getglobal(luastate, "setup");
×
610

UNCOV
611
    if (lua_pcall(luastate, 0, 0, 0) != 0) {
×
612
        SCLogError("couldn't run script 'setup' function: %s", lua_tostring(luastate, -1));
×
613
        goto error;
×
614
    }
×
615

UNCOV
616
    SCLogDebug("lua_State %p is set up", luastate);
×
UNCOV
617
    return luastate;
×
618
error:
×
619
    if (luastate)
×
620
        LuaReturnState(luastate);
×
621
    return NULL;
×
UNCOV
622
}
×
623

UNCOV
624
static void LogLuaSubFree(OutputCtx *oc) {
×
UNCOV
625
    if (oc->data)
×
UNCOV
626
        SCFree(oc->data);
×
UNCOV
627
    SCFree(oc);
×
UNCOV
628
}
×
629

630
/** \brief initialize output for a script instance
631
 *
632
 *  Runs script 'setup' function.
633
 */
634
static OutputInitResult OutputLuaLogInitSub(SCConfNode *conf, OutputCtx *parent_ctx)
UNCOV
635
{
×
UNCOV
636
    OutputInitResult result = { NULL, false };
×
UNCOV
637
    if (conf == NULL)
×
638
        return result;
×
639

UNCOV
640
    LogLuaCtx *lua_ctx = SCCalloc(1, sizeof(LogLuaCtx));
×
UNCOV
641
    if (unlikely(lua_ctx == NULL))
×
642
        return result;
×
643

UNCOV
644
    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
×
UNCOV
645
    if (unlikely(output_ctx == NULL)) {
×
646
        SCFree(lua_ctx);
×
647
        return result;
×
648
    }
×
649

UNCOV
650
    SCMutexInit(&lua_ctx->m, NULL);
×
651

UNCOV
652
    BUG_ON(parent_ctx == NULL);
×
UNCOV
653
    LogLuaMasterCtx *mc = parent_ctx->data;
×
UNCOV
654
    BUG_ON(mc == NULL);
×
655

UNCOV
656
    const char *dir = mc->script_dir;
×
UNCOV
657
    char path[PATH_MAX] = "";
×
UNCOV
658
    if (strlen(dir) > 0) {
×
UNCOV
659
        if (PathMerge(path, sizeof(path), dir, conf->val) < 0) {
×
660
            SCLogError("failed to construct lua script path");
×
661
            goto error;
×
662
        }
×
UNCOV
663
    } else {
×
664
        strlcpy(path, conf->val, sizeof(path));
×
665
    }
×
UNCOV
666
    SCLogDebug("script full path %s", path);
×
667

UNCOV
668
    SCMutexLock(&lua_ctx->m);
×
UNCOV
669
    lua_ctx->luastate = LuaScriptSetup(path, mc);
×
UNCOV
670
    SCMutexUnlock(&lua_ctx->m);
×
UNCOV
671
    if (lua_ctx->luastate == NULL)
×
672
        goto error;
×
673

UNCOV
674
    SCLogDebug("lua_ctx %p", lua_ctx);
×
675

UNCOV
676
    output_ctx->data = lua_ctx;
×
UNCOV
677
    output_ctx->DeInit = LogLuaSubFree;
×
678

UNCOV
679
    result.ctx = output_ctx;
×
UNCOV
680
    result.ok = true;
×
UNCOV
681
    return result;
×
682
error:
×
683
    SCMutexDestroy(&lua_ctx->m);
×
684
    SCFree(lua_ctx);
×
685
    SCFree(output_ctx);
×
686
    return result;
×
UNCOV
687
}
×
688

689
static void LogLuaMasterFree(OutputCtx *oc)
UNCOV
690
{
×
UNCOV
691
    if (oc->data)
×
UNCOV
692
        SCFree(oc->data);
×
693

UNCOV
694
    OutputModule *om, *tom;
×
UNCOV
695
    TAILQ_FOREACH_SAFE(om, &oc->submodules, entries, tom) {
×
UNCOV
696
        SCFree(om);
×
UNCOV
697
    }
×
UNCOV
698
    SCFree(oc);
×
UNCOV
699
}
×
700

701
/** \internal
702
 *  \brief initialize output instance for lua module
703
 *
704
 *  Parses nested script list, primes them to find out what they
705
 *  inspect, then fills the OutputCtx::submodules list with the
706
 *  proper Logger function for the data type the script needs.
707
 */
708
static OutputInitResult OutputLuaLogInit(SCConfNode *conf)
UNCOV
709
{
×
UNCOV
710
    OutputInitResult result = { NULL, false };
×
UNCOV
711
    const char *dir = SCConfNodeLookupChildValue(conf, "scripts-dir");
×
UNCOV
712
    if (dir == NULL)
×
713
        dir = "";
×
714

UNCOV
715
    SCConfNode *scripts = SCConfNodeLookupChild(conf, "scripts");
×
UNCOV
716
    if (scripts == NULL) {
×
717
        /* No "outputs" section in the configuration. */
718
        SCLogInfo("scripts not defined");
×
719
        return result;
×
720
    }
×
721

722
    /* global output ctx setup */
UNCOV
723
    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
×
UNCOV
724
    if (unlikely(output_ctx == NULL)) {
×
725
        return result;
×
726
    }
×
UNCOV
727
    output_ctx->DeInit = LogLuaMasterFree;
×
UNCOV
728
    output_ctx->data = SCCalloc(1, sizeof(LogLuaMasterCtx));
×
UNCOV
729
    if (unlikely(output_ctx->data == NULL)) {
×
730
        SCFree(output_ctx);
×
731
        return result;
×
732
    }
×
UNCOV
733
    LogLuaMasterCtx *master_config = output_ctx->data;
×
UNCOV
734
    strlcpy(master_config->script_dir, dir, sizeof(master_config->script_dir));
×
735

UNCOV
736
    const char *lua_path = SCConfNodeLookupChildValue(conf, "path");
×
UNCOV
737
    if (lua_path && strlen(lua_path) > 0) {
×
738
        strlcpy(master_config->path, lua_path, sizeof(master_config->path));
×
739
    }
×
740

UNCOV
741
    const char *lua_cpath = SCConfNodeLookupChildValue(conf, "cpath");
×
UNCOV
742
    if (lua_cpath && strlen(lua_cpath) > 0) {
×
743
        strlcpy(master_config->cpath, lua_cpath, sizeof(master_config->cpath));
×
744
    }
×
745

UNCOV
746
    TAILQ_INIT(&output_ctx->submodules);
×
747

748
    /* check the enables scripts and set them up as submodules */
UNCOV
749
    SCConfNode *script;
×
UNCOV
750
    TAILQ_FOREACH(script, &scripts->head, next) {
×
UNCOV
751
        SCLogInfo("enabling script %s", script->val);
×
UNCOV
752
        LogLuaScriptOptions opts;
×
UNCOV
753
        memset(&opts, 0x00, sizeof(opts));
×
754

UNCOV
755
        char path[PATH_MAX] = "";
×
UNCOV
756
        snprintf(path, sizeof(path),"%s%s%s", dir, strlen(dir) ? "/" : "", script->val);
×
UNCOV
757
        SCLogDebug("script full path %s", path);
×
758

UNCOV
759
        int r = LuaScriptInit(path, &opts, master_config);
×
UNCOV
760
        if (r != 0) {
×
761
            SCLogError("couldn't initialize script");
×
762
            goto error;
×
763
        }
×
764

765
        /* create an OutputModule for this script, based
766
         * on it's needs. */
UNCOV
767
        OutputModule *om = SCCalloc(1, sizeof(*om));
×
UNCOV
768
        if (om == NULL) {
×
769
            SCLogError("calloc() failed");
×
770
            goto error;
×
771
        }
×
772

UNCOV
773
        om->name = MODULE_NAME;
×
UNCOV
774
        om->conf_name = script->val;
×
UNCOV
775
        om->InitSubFunc = OutputLuaLogInitSub;
×
UNCOV
776
        om->ThreadInit = LuaLogThreadInit;
×
UNCOV
777
        om->ThreadDeinit = LuaLogThreadDeinit;
×
778

UNCOV
779
        if (opts.alproto == ALPROTO_HTTP1 && opts.streaming) {
×
UNCOV
780
            om->StreamingLogFunc = LuaStreamingLogger;
×
UNCOV
781
            om->stream_type = STREAMING_HTTP_BODIES;
×
UNCOV
782
            om->alproto = ALPROTO_HTTP1;
×
UNCOV
783
            AppLayerHtpEnableRequestBodyCallback();
×
UNCOV
784
            AppLayerHtpEnableResponseBodyCallback();
×
UNCOV
785
        } else if (opts.alproto == ALPROTO_HTTP1) {
×
UNCOV
786
            om->TxLogFunc = LuaTxLogger;
×
UNCOV
787
            om->alproto = ALPROTO_HTTP1;
×
UNCOV
788
            om->ts_log_progress = -1;
×
UNCOV
789
            om->tc_log_progress = -1;
×
UNCOV
790
            SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP1);
×
UNCOV
791
        } else if (opts.alproto == ALPROTO_TLS) {
×
UNCOV
792
            om->TxLogFunc = LuaTxLogger;
×
UNCOV
793
            om->alproto = ALPROTO_TLS;
×
UNCOV
794
            om->tc_log_progress = TLS_STATE_SERVER_HANDSHAKE_DONE;
×
UNCOV
795
            om->ts_log_progress = TLS_STATE_CLIENT_HANDSHAKE_DONE;
×
UNCOV
796
            SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_TLS);
×
UNCOV
797
        } else if (opts.alproto == ALPROTO_DNS) {
×
UNCOV
798
            om->TxLogFunc = LuaTxLogger;
×
UNCOV
799
            om->alproto = ALPROTO_DNS;
×
UNCOV
800
            om->ts_log_progress = -1;
×
UNCOV
801
            om->tc_log_progress = -1;
×
UNCOV
802
            SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DNS);
×
UNCOV
803
            SCAppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_DNS);
×
UNCOV
804
        } else if (opts.alproto == ALPROTO_SSH) {
×
UNCOV
805
            om->TxLogFunc = LuaTxLogger;
×
UNCOV
806
            om->alproto = ALPROTO_SSH;
×
UNCOV
807
            om->TxLogCondition = SSHTxLogCondition;
×
UNCOV
808
            SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SSH);
×
UNCOV
809
        } else if (opts.alproto == ALPROTO_SMTP) {
×
UNCOV
810
            om->TxLogFunc = LuaTxLogger;
×
UNCOV
811
            om->alproto = ALPROTO_SMTP;
×
UNCOV
812
            om->ts_log_progress = -1;
×
UNCOV
813
            om->tc_log_progress = -1;
×
UNCOV
814
            SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_SMTP);
×
UNCOV
815
        } else if (opts.packet && opts.alerts) {
×
UNCOV
816
            om->PacketLogFunc = LuaPacketLoggerAlerts;
×
UNCOV
817
            om->PacketConditionFunc = LuaPacketConditionAlerts;
×
UNCOV
818
        } else if (opts.packet && opts.alerts == 0) {
×
UNCOV
819
            om->PacketLogFunc = LuaPacketLogger;
×
UNCOV
820
            om->PacketConditionFunc = LuaPacketCondition;
×
UNCOV
821
        } else if (opts.file) {
×
UNCOV
822
            om->FileLogFunc = LuaFileLogger;
×
UNCOV
823
            AppLayerHtpNeedFileInspection();
×
UNCOV
824
        } else if (opts.streaming && opts.tcp_data) {
×
UNCOV
825
            om->StreamingLogFunc = LuaStreamingLogger;
×
UNCOV
826
            om->stream_type = STREAMING_TCP_DATA;
×
UNCOV
827
        } else if (opts.flow) {
×
UNCOV
828
            om->FlowLogFunc = LuaFlowLogger;
×
UNCOV
829
        } else if (opts.stats) {
×
UNCOV
830
            om->StatsLogFunc = LuaStatsLogger;
×
UNCOV
831
        } else {
×
832
            SCLogError("failed to setup thread module");
×
833
            SCFree(om);
×
834
            goto error;
×
835
        }
×
836

UNCOV
837
        TAILQ_INSERT_TAIL(&output_ctx->submodules, om, entries);
×
UNCOV
838
    }
×
839

UNCOV
840
    result.ctx = output_ctx;
×
UNCOV
841
    result.ok = true;
×
UNCOV
842
    return result;
×
843

844
error:
×
845
    if (output_ctx->DeInit)
×
846
        output_ctx->DeInit(output_ctx);
×
847

848
    int failure_fatal = 0;
×
849
    if (SCConfGetBool("engine.init-failure-fatal", &failure_fatal) != 1) {
×
850
        SCLogDebug("ConfGetBool could not load the value.");
×
851
    }
×
852
    if (failure_fatal) {
×
853
        FatalError("Error during setup of lua output. Details should be "
×
854
                   "described in previous error messages. Shutting down...");
×
855
    }
×
856

857
    return result;
×
858
}
×
859

860
/** \internal
861
 *  \brief Run the scripts 'deinit' function
862
 */
863
static void OutputLuaLogDoDeinit(LogLuaCtx *lua_ctx)
UNCOV
864
{
×
UNCOV
865
    lua_State *luastate = lua_ctx->luastate;
×
866

UNCOV
867
    lua_getglobal(luastate, "deinit");
×
UNCOV
868
    if (lua_type(luastate, -1) != LUA_TFUNCTION) {
×
869
        SCLogError("no deinit function in script");
×
870
        return;
×
871
    }
×
872
    //LuaPrintStack(luastate);
873

UNCOV
874
    if (lua_pcall(luastate, 0, 0, 0) != 0) {
×
875
        SCLogError("couldn't run script 'deinit' function: %s", lua_tostring(luastate, -1));
×
876
        return;
×
877
    }
×
UNCOV
878
    LuaReturnState(luastate);
×
UNCOV
879
}
×
880

881
/** \internal
882
 *  \brief Initialize the thread storage for lua
883
 *
884
 *  Currently only stores a pointer to the global LogLuaCtx
885
 */
886
static TmEcode LuaLogThreadInit(ThreadVars *t, const void *initdata, void **data)
UNCOV
887
{
×
UNCOV
888
    LogLuaThreadCtx *td = SCCalloc(1, sizeof(*td));
×
UNCOV
889
    if (unlikely(td == NULL))
×
890
        return TM_ECODE_FAILED;
×
891

UNCOV
892
    if (initdata == NULL) {
×
893
        SCLogDebug("Error getting context for LuaLog. \"initdata\" argument NULL");
×
894
        SCFree(td);
×
895
        return TM_ECODE_FAILED;
×
896
    }
×
897

UNCOV
898
    LogLuaCtx *lua_ctx = ((OutputCtx *)initdata)->data;
×
UNCOV
899
    SCLogDebug("lua_ctx %p", lua_ctx);
×
UNCOV
900
    td->lua_ctx = lua_ctx;
×
UNCOV
901
    *data = (void *)td;
×
UNCOV
902
    return TM_ECODE_OK;
×
UNCOV
903
}
×
904

905
/** \internal
906
 *  \brief Deinit the thread storage for lua
907
 *
908
 *  Calls OutputLuaLogDoDeinit if no-one else already did.
909
 */
910
static TmEcode LuaLogThreadDeinit(ThreadVars *t, void *data)
UNCOV
911
{
×
UNCOV
912
    LogLuaThreadCtx *td = (LogLuaThreadCtx *)data;
×
UNCOV
913
    if (td == NULL) {
×
914
        return TM_ECODE_OK;
×
915
    }
×
916

UNCOV
917
    SCMutexLock(&td->lua_ctx->m);
×
UNCOV
918
    if (td->lua_ctx->deinit_once == 0) {
×
UNCOV
919
        OutputLuaLogDoDeinit(td->lua_ctx);
×
UNCOV
920
        td->lua_ctx->deinit_once = 1;
×
UNCOV
921
    }
×
UNCOV
922
    SCMutexUnlock(&td->lua_ctx->m);
×
923

924
    /* clear memory */
UNCOV
925
    memset(td, 0, sizeof(*td));
×
926

UNCOV
927
    SCFree(td);
×
UNCOV
928
    return TM_ECODE_OK;
×
UNCOV
929
}
×
930

931
void LuaLogRegister(void) {
29✔
932
    /* register as separate module */
933
    OutputRegisterModule(MODULE_NAME, "lua", OutputLuaLogInit);
29✔
934
}
29✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc