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

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

3.32
/src/output-streaming.c
1
/* Copyright (C) 2007-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
 * Logger for streaming data
24
 */
25

26
#include "suricata-common.h"
27
#include "output.h"
28
#include "output-streaming.h"
29
#include "app-layer.h"
30
#include "app-layer-parser.h"
31
#include "app-layer-htp.h"
32
#include "util-print.h"
33
#include "conf.h"
34
#include "util-profiling.h"
35
#include "stream-tcp.h"
36
#include "stream-tcp-inline.h"
37
#include "stream-tcp-reassemble.h"
38
#include "util-validate.h"
39

40
/** per thread data for this module, contains a list of per thread
41
 *  data for the packet loggers. */
42
typedef struct OutputStreamingLoggerThreadData_ {
43
    OutputLoggerThreadStore *store;
44
    uint32_t loggers;
45
} OutputStreamingLoggerThreadData;
46

47
/* logger instance, a module + a output ctx,
48
 * it's perfectly valid that have multiple instances of the same
49
 * log module (e.g. http.log) with different output ctx'. */
50
typedef struct OutputStreamingLogger_ {
51
    SCStreamingLogger LogFunc;
52
    void *initdata;
53
    struct OutputStreamingLogger_ *next;
54
    const char *name;
55
    LoggerId logger_id;
56
    enum SCOutputStreamingType type;
57
    ThreadInitFunc ThreadInit;
58
    ThreadDeinitFunc ThreadDeinit;
59
} OutputStreamingLogger;
60

61
static OutputStreamingLogger *list = NULL;
62

63
int SCOutputRegisterStreamingLogger(LoggerId id, const char *name, SCStreamingLogger LogFunc,
64
        void *initdata, enum SCOutputStreamingType type, ThreadInitFunc ThreadInit,
65
        ThreadDeinitFunc ThreadDeinit)
UNCOV
66
{
×
UNCOV
67
    OutputStreamingLogger *op = SCCalloc(1, sizeof(*op));
×
UNCOV
68
    if (op == NULL)
×
69
        return -1;
×
70

UNCOV
71
    op->LogFunc = LogFunc;
×
UNCOV
72
    op->initdata = initdata;
×
UNCOV
73
    op->name = name;
×
UNCOV
74
    op->logger_id = id;
×
UNCOV
75
    op->type = type;
×
UNCOV
76
    op->ThreadInit = ThreadInit;
×
UNCOV
77
    op->ThreadDeinit = ThreadDeinit;
×
78

UNCOV
79
    if (list == NULL)
×
UNCOV
80
        list = op;
×
UNCOV
81
    else {
×
UNCOV
82
        OutputStreamingLogger *t = list;
×
UNCOV
83
        while (t->next)
×
84
            t = t->next;
×
UNCOV
85
        t->next = op;
×
UNCOV
86
    }
×
87

UNCOV
88
    if (op->type == STREAMING_TCP_DATA) {
×
UNCOV
89
        stream_config.streaming_log_api = true;
×
UNCOV
90
    }
×
91

UNCOV
92
    SCLogDebug("OutputRegisterStreamingLogger happy");
×
UNCOV
93
    return 0;
×
UNCOV
94
}
×
95

96
typedef struct StreamerCallbackData_ {
97
    OutputStreamingLogger *logger;
98
    OutputLoggerThreadStore *store;
99
    ThreadVars *tv;
100
    Packet *p;
101
    enum SCOutputStreamingType type;
102
} StreamerCallbackData;
103

104
static int Streamer(void *cbdata, Flow *f, const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
UNCOV
105
{
×
UNCOV
106
    StreamerCallbackData *streamer_cbdata = (StreamerCallbackData *)cbdata;
×
UNCOV
107
    DEBUG_VALIDATE_BUG_ON(streamer_cbdata == NULL);
×
UNCOV
108
    OutputStreamingLogger *logger = streamer_cbdata->logger;
×
UNCOV
109
    OutputLoggerThreadStore *store = streamer_cbdata->store;
×
UNCOV
110
    ThreadVars *tv = streamer_cbdata->tv;
×
111
#ifdef PROFILING
112
    Packet *p = streamer_cbdata->p;
113
#endif
UNCOV
114
    DEBUG_VALIDATE_BUG_ON(logger == NULL);
×
UNCOV
115
    DEBUG_VALIDATE_BUG_ON(store == NULL);
×
116

UNCOV
117
    while (logger && store) {
×
UNCOV
118
        DEBUG_VALIDATE_BUG_ON(logger->LogFunc == NULL);
×
119

UNCOV
120
        if (logger->type == streamer_cbdata->type) {
×
UNCOV
121
            SCLogDebug("logger %p", logger);
×
UNCOV
122
            PACKET_PROFILING_LOGGER_START(p, logger->logger_id);
×
UNCOV
123
            logger->LogFunc(tv, store->thread_data, (const Flow *)f, data, data_len, tx_id, flags);
×
UNCOV
124
            PACKET_PROFILING_LOGGER_END(p, logger->logger_id);
×
UNCOV
125
        }
×
126

UNCOV
127
        logger = logger->next;
×
UNCOV
128
        store = store->next;
×
129

UNCOV
130
        DEBUG_VALIDATE_BUG_ON(logger == NULL && store != NULL);
×
UNCOV
131
        DEBUG_VALIDATE_BUG_ON(logger != NULL && store == NULL);
×
UNCOV
132
    }
×
133

UNCOV
134
    return 0;
×
UNCOV
135
}
×
136

137
/** \brief Http Body Iterator for logging
138
 *
139
 *  Global logic:
140
 *
141
 *  - For each tx
142
 *    - For each body chunk
143
 *      - Invoke Streamer
144
 */
145

146
static int HttpBodyIterator(Flow *f, int close, void *cbdata, uint8_t iflags)
UNCOV
147
{
×
UNCOV
148
    SCLogDebug("called with %p, %d, %p, %02x", f, close, cbdata, iflags);
×
149

UNCOV
150
    HtpState *s = f->alstate;
×
UNCOV
151
    if (s == NULL || s->conn == NULL) {
×
152
        return 0;
×
153
    }
×
154

UNCOV
155
    const int tx_progress_done_value_ts =
×
UNCOV
156
            AppLayerParserGetStateProgressCompletionStatus(ALPROTO_HTTP1, STREAM_TOSERVER);
×
UNCOV
157
    const int tx_progress_done_value_tc =
×
UNCOV
158
            AppLayerParserGetStateProgressCompletionStatus(ALPROTO_HTTP1, STREAM_TOCLIENT);
×
UNCOV
159
    const uint64_t total_txs = AppLayerParserGetTxCnt(f, f->alstate);
×
160

UNCOV
161
    uint64_t tx_id = 0;
×
UNCOV
162
    for (tx_id = 0; tx_id < total_txs; tx_id++) { // TODO optimization store log tx
×
UNCOV
163
        htp_tx_t *tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, tx_id);
×
UNCOV
164
        if (tx == NULL) {
×
UNCOV
165
            continue;
×
UNCOV
166
        }
×
167

UNCOV
168
        int tx_done = 0;
×
UNCOV
169
        int tx_logged = 0;
×
UNCOV
170
        int tx_progress_ts = AppLayerParserGetStateProgress(
×
UNCOV
171
                IPPROTO_TCP, ALPROTO_HTTP1, tx, FlowGetDisruptionFlags(f, STREAM_TOSERVER));
×
UNCOV
172
        if (tx_progress_ts >= tx_progress_done_value_ts) {
×
UNCOV
173
            int tx_progress_tc = AppLayerParserGetStateProgress(
×
UNCOV
174
                    IPPROTO_TCP, ALPROTO_HTTP1, tx, FlowGetDisruptionFlags(f, STREAM_TOCLIENT));
×
UNCOV
175
            if (tx_progress_tc >= tx_progress_done_value_tc) {
×
UNCOV
176
                tx_done = 1;
×
UNCOV
177
            }
×
UNCOV
178
        }
×
179

UNCOV
180
        SCLogDebug("tx %p", tx);
×
UNCOV
181
        HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
×
UNCOV
182
        SCLogDebug("htud %p", htud);
×
UNCOV
183
        HtpBody *body = NULL;
×
UNCOV
184
        if (iflags & OUTPUT_STREAMING_FLAG_TOSERVER)
×
UNCOV
185
            body = &htud->request_body;
×
UNCOV
186
        else if (iflags & OUTPUT_STREAMING_FLAG_TOCLIENT)
×
UNCOV
187
            body = &htud->response_body;
×
188

UNCOV
189
        if (body == NULL) {
×
190
            SCLogDebug("no body");
×
191
            goto next;
×
192
        }
×
UNCOV
193
            if (body->first == NULL) {
×
UNCOV
194
                SCLogDebug("no body chunks");
×
UNCOV
195
                goto next;
×
UNCOV
196
            }
×
UNCOV
197
            if (body->last->logged == 1) {
×
198
                SCLogDebug("all logged already");
×
199
                goto next;
×
200
            }
×
201

202
            // for each chunk
UNCOV
203
            HtpBodyChunk *chunk = body->first;
×
UNCOV
204
            for ( ; chunk != NULL; chunk = chunk->next) {
×
UNCOV
205
                if (chunk->logged) {
×
UNCOV
206
                    SCLogDebug("logged %d", chunk->logged);
×
UNCOV
207
                    continue;
×
UNCOV
208
                }
×
209

UNCOV
210
                uint8_t flags = iflags | OUTPUT_STREAMING_FLAG_TRANSACTION;
×
UNCOV
211
                if (chunk->sbseg.stream_offset == 0)
×
UNCOV
212
                    flags |= OUTPUT_STREAMING_FLAG_OPEN;
×
213
                /* if we need to close and we're at the last segment in the list
214
                 * we add the 'close' flag so the logger can close up. */
UNCOV
215
                if ((tx_done || close) && chunk->next == NULL) {
×
UNCOV
216
                    flags |= OUTPUT_STREAMING_FLAG_CLOSE;
×
UNCOV
217
                }
×
218

UNCOV
219
                const uint8_t *data = NULL;
×
UNCOV
220
                uint32_t data_len = 0;
×
UNCOV
221
                StreamingBufferSegmentGetData(body->sb, &chunk->sbseg, &data, &data_len);
×
222

223
                // invoke Streamer
UNCOV
224
                Streamer(cbdata, f, data, data_len, tx_id, flags);
×
225
                //PrintRawDataFp(stdout, data, data_len);
UNCOV
226
                chunk->logged = 1;
×
UNCOV
227
                tx_logged = 1;
×
UNCOV
228
            }
×
229

UNCOV
230
        next:
×
231
            /* if we need to close we need to invoke the Streamer for sure. If we
232
             * logged no chunks, we call the Streamer with NULL data so it can
233
             * close up. */
UNCOV
234
            if (tx_logged == 0 && (close||tx_done)) {
×
235
                Streamer(cbdata, f, NULL, 0, tx_id,
×
236
                         iflags|OUTPUT_STREAMING_FLAG_CLOSE|OUTPUT_STREAMING_FLAG_TRANSACTION);
×
237
            }
×
UNCOV
238
    }
×
UNCOV
239
    return 0;
×
UNCOV
240
}
×
241

242
struct StreamLogData {
243
    uint8_t flags;
244
    void *streamer_cbdata;
245
    Flow *f;
246
};
247

248
static int StreamLogFunc(
249
        void *cb_data, const uint8_t *data, const uint32_t data_len, const uint64_t _offset)
UNCOV
250
{
×
UNCOV
251
    struct StreamLogData *log = cb_data;
×
252

UNCOV
253
    Streamer(log->streamer_cbdata, log->f, data, data_len, 0, log->flags);
×
254

255
    /* hack: unset open flag after first run */
UNCOV
256
    log->flags &= ~OUTPUT_STREAMING_FLAG_OPEN;
×
257

UNCOV
258
    return 0;
×
UNCOV
259
}
×
260

261
static int TcpDataLogger (Flow *f, TcpSession *ssn, TcpStream *stream,
262
        bool eof, uint8_t iflags, void *streamer_cbdata)
UNCOV
263
{
×
UNCOV
264
    uint8_t flags = iflags;
×
UNCOV
265
    uint64_t progress = STREAM_LOG_PROGRESS(stream);
×
266

UNCOV
267
    if (progress == 0)
×
UNCOV
268
        flags |= OUTPUT_STREAMING_FLAG_OPEN;
×
269

UNCOV
270
    struct StreamLogData log_data = { flags, streamer_cbdata, f };
×
UNCOV
271
    StreamReassembleLog(ssn, stream,
×
UNCOV
272
            StreamLogFunc, &log_data,
×
UNCOV
273
            progress, &progress, eof);
×
274

UNCOV
275
    if (progress > STREAM_LOG_PROGRESS(stream)) {
×
UNCOV
276
        DEBUG_VALIDATE_BUG_ON(progress - STREAM_LOG_PROGRESS(stream) > UINT32_MAX);
×
UNCOV
277
        uint32_t slide = (uint32_t)(progress - STREAM_LOG_PROGRESS(stream));
×
UNCOV
278
        stream->log_progress_rel += slide;
×
UNCOV
279
    }
×
280

UNCOV
281
    if (eof) {
×
UNCOV
282
        Streamer(streamer_cbdata, f, NULL, 0, 0, flags|OUTPUT_STREAMING_FLAG_CLOSE);
×
UNCOV
283
    }
×
UNCOV
284
    return 0;
×
UNCOV
285
}
×
286

287
static TmEcode OutputStreamingLog(ThreadVars *tv, Packet *p, void *thread_data)
UNCOV
288
{
×
UNCOV
289
    DEBUG_VALIDATE_BUG_ON(thread_data == NULL);
×
290

UNCOV
291
    if (list == NULL) {
×
292
        /* No child loggers. */
293
        return TM_ECODE_OK;
×
294
    }
×
295

UNCOV
296
    OutputStreamingLoggerThreadData *op_thread_data =
×
UNCOV
297
            (OutputStreamingLoggerThreadData *)thread_data;
×
UNCOV
298
    OutputStreamingLogger *logger = list;
×
UNCOV
299
    OutputLoggerThreadStore *store = op_thread_data->store;
×
300

UNCOV
301
    StreamerCallbackData streamer_cbdata = { logger, store, tv, p , 0};
×
302

UNCOV
303
    DEBUG_VALIDATE_BUG_ON(logger == NULL && store != NULL);
×
UNCOV
304
    DEBUG_VALIDATE_BUG_ON(logger != NULL && store == NULL);
×
UNCOV
305
    DEBUG_VALIDATE_BUG_ON(logger == NULL && store == NULL);
×
306

UNCOV
307
    uint8_t flags = 0;
×
UNCOV
308
    Flow * const f = p->flow;
×
309

310
    /* no flow, no streaming */
UNCOV
311
    if (f == NULL) {
×
UNCOV
312
        SCReturnInt(TM_ECODE_OK);
×
UNCOV
313
    }
×
314

UNCOV
315
    if (!(StreamTcpInlineMode())) {
×
UNCOV
316
        if (PKT_IS_TOCLIENT(p)) {
×
UNCOV
317
            flags |= OUTPUT_STREAMING_FLAG_TOSERVER;
×
UNCOV
318
        } else {
×
UNCOV
319
            flags |= OUTPUT_STREAMING_FLAG_TOCLIENT;
×
UNCOV
320
        }
×
UNCOV
321
    } else {
×
322
        if (PKT_IS_TOSERVER(p)) {
×
323
            flags |= OUTPUT_STREAMING_FLAG_TOSERVER;
×
324
        } else {
×
325
            flags |= OUTPUT_STREAMING_FLAG_TOCLIENT;
×
326
        }
×
327
    }
×
328

UNCOV
329
    if (op_thread_data->loggers & (1<<STREAMING_TCP_DATA)) {
×
UNCOV
330
        TcpSession *ssn = f->protoctx;
×
UNCOV
331
        if (ssn) {
×
UNCOV
332
            int close = (ssn->state >= TCP_CLOSED);
×
UNCOV
333
            close |= ((p->flags & PKT_PSEUDO_STREAM_END) ? 1 : 0);
×
UNCOV
334
            SCLogDebug("close ? %s", close ? "yes" : "no");
×
335

UNCOV
336
            TcpStream *stream = flags & OUTPUT_STREAMING_FLAG_TOSERVER ? &ssn->client : &ssn->server;
×
UNCOV
337
            streamer_cbdata.type = STREAMING_TCP_DATA;
×
UNCOV
338
            TcpDataLogger(f, ssn, stream, close, flags, (void *)&streamer_cbdata);
×
UNCOV
339
        }
×
UNCOV
340
    }
×
UNCOV
341
    if (op_thread_data->loggers & (1<<STREAMING_HTTP_BODIES)) {
×
UNCOV
342
        if (f->alproto == ALPROTO_HTTP1 && f->alstate != NULL) {
×
UNCOV
343
            int close = 0;
×
UNCOV
344
            TcpSession *ssn = f->protoctx;
×
UNCOV
345
            if (ssn) {
×
UNCOV
346
                close = (ssn->state >= TCP_CLOSED);
×
UNCOV
347
                close |= ((p->flags & PKT_PSEUDO_STREAM_END) ? 1 : 0);
×
UNCOV
348
            }
×
UNCOV
349
            SCLogDebug("close ? %s", close ? "yes" : "no");
×
UNCOV
350
            streamer_cbdata.type = STREAMING_HTTP_BODIES;
×
UNCOV
351
            HttpBodyIterator(f, close, (void *)&streamer_cbdata, flags);
×
UNCOV
352
        }
×
UNCOV
353
    }
×
354

UNCOV
355
    return TM_ECODE_OK;
×
UNCOV
356
}
×
357

358
/** \brief thread init for the tx logger
359
 *  This will run the thread init functions for the individual registered
360
 *  loggers */
UNCOV
361
static TmEcode OutputStreamingLogThreadInit(ThreadVars *tv, const void *initdata, void **data) {
×
UNCOV
362
    OutputStreamingLoggerThreadData *td = SCCalloc(1, sizeof(*td));
×
UNCOV
363
    if (td == NULL)
×
364
        return TM_ECODE_FAILED;
×
365

UNCOV
366
    *data = (void *)td;
×
367

UNCOV
368
    SCLogDebug("OutputStreamingLogThreadInit happy (*data %p)", *data);
×
369

UNCOV
370
    OutputStreamingLogger *logger = list;
×
UNCOV
371
    while (logger) {
×
UNCOV
372
        if (logger->ThreadInit) {
×
UNCOV
373
            void *retptr = NULL;
×
UNCOV
374
            if (logger->ThreadInit(tv, logger->initdata, &retptr) == TM_ECODE_OK) {
×
UNCOV
375
                OutputLoggerThreadStore *ts = SCCalloc(1, sizeof(*ts));
×
UNCOV
376
                /* todo */ BUG_ON(ts == NULL);
×
377

378
                /* store thread handle */
UNCOV
379
                ts->thread_data = retptr;
×
380

UNCOV
381
                if (td->store == NULL) {
×
UNCOV
382
                    td->store = ts;
×
UNCOV
383
                } else {
×
UNCOV
384
                    OutputLoggerThreadStore *tmp = td->store;
×
UNCOV
385
                    while (tmp->next != NULL)
×
386
                        tmp = tmp->next;
×
UNCOV
387
                    tmp->next = ts;
×
UNCOV
388
                }
×
389

UNCOV
390
                SCLogDebug("%s is now set up", logger->name);
×
UNCOV
391
            }
×
UNCOV
392
        }
×
393

UNCOV
394
        td->loggers |= (1<<logger->type);
×
395

UNCOV
396
        logger = logger->next;
×
UNCOV
397
    }
×
398

UNCOV
399
    return TM_ECODE_OK;
×
UNCOV
400
}
×
401

UNCOV
402
static TmEcode OutputStreamingLogThreadDeinit(ThreadVars *tv, void *thread_data) {
×
UNCOV
403
    OutputStreamingLoggerThreadData *op_thread_data =
×
UNCOV
404
            (OutputStreamingLoggerThreadData *)thread_data;
×
UNCOV
405
    OutputLoggerThreadStore *store = op_thread_data->store;
×
UNCOV
406
    OutputStreamingLogger *logger = list;
×
407

UNCOV
408
    while (logger && store) {
×
UNCOV
409
        if (logger->ThreadDeinit) {
×
UNCOV
410
            logger->ThreadDeinit(tv, store->thread_data);
×
UNCOV
411
        }
×
412

UNCOV
413
        OutputLoggerThreadStore *next_store = store->next;
×
UNCOV
414
        SCFree(store);
×
UNCOV
415
        logger = logger->next;
×
UNCOV
416
        store = next_store;
×
UNCOV
417
    }
×
418

UNCOV
419
    SCFree(op_thread_data);
×
UNCOV
420
    return TM_ECODE_OK;
×
UNCOV
421
}
×
422

423
static uint32_t OutputStreamingLoggerGetActiveCount(void)
424
{
1✔
425
    uint32_t cnt = 0;
1✔
426
    for (OutputStreamingLogger *p = list; p != NULL; p = p->next) {
1✔
UNCOV
427
        cnt++;
×
UNCOV
428
    }
×
429
    return cnt;
1✔
430
}
1✔
431

432
void OutputStreamingLoggerRegister(void) {
2✔
433
    OutputRegisterRootLogger(OutputStreamingLogThreadInit, OutputStreamingLogThreadDeinit,
2✔
434
            OutputStreamingLog, OutputStreamingLoggerGetActiveCount);
2✔
435
}
2✔
436

437
void OutputStreamingShutdown(void)
UNCOV
438
{
×
UNCOV
439
    OutputStreamingLogger *logger = list;
×
UNCOV
440
    while (logger) {
×
UNCOV
441
        OutputStreamingLogger *next_logger = logger->next;
×
UNCOV
442
        SCFree(logger);
×
UNCOV
443
        logger = next_logger;
×
UNCOV
444
    }
×
445
    list = NULL;
UNCOV
446
}
×
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