Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/log-tcp-data.c
Line
Count
Source
1
/* Copyright (C) 2014 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
#include "suricata-common.h"
25
#include "log-tcp-data.h"
26
27
#include "threadvars.h"
28
29
#include "util-conf.h"
30
#include "util-logopenfile.h"
31
#include "util-path.h"
32
#include "util-print.h"
33
34
0
#define DEFAULT_LOG_FILENAME "tcp-data.log"
35
36
142
#define MODULE_NAME "LogTcpDataLog"
37
38
0
#define OUTPUT_BUFFER_SIZE 65535
39
40
TmEcode LogTcpDataLogThreadInit(ThreadVars *, const void *, void **);
41
TmEcode LogTcpDataLogThreadDeinit(ThreadVars *, void *);
42
static void LogTcpDataLogDeInitCtx(OutputCtx *);
43
44
int LogTcpDataLogger(ThreadVars *tv, void *thread_data, const Flow *f, const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags);
45
46
71
void LogTcpDataLogRegister (void) {
47
71
    OutputRegisterStreamingModule(LOGGER_TCP_DATA, MODULE_NAME, "tcp-data",
48
71
        LogTcpDataLogInitCtx, LogTcpDataLogger, STREAMING_TCP_DATA,
49
71
        LogTcpDataLogThreadInit, LogTcpDataLogThreadDeinit, NULL);
50
71
    OutputRegisterStreamingModule(LOGGER_TCP_DATA, MODULE_NAME, "http-body-data",
51
71
        LogTcpDataLogInitCtx, LogTcpDataLogger, STREAMING_HTTP_BODIES,
52
71
        LogTcpDataLogThreadInit, LogTcpDataLogThreadDeinit, NULL);
53
71
}
54
55
typedef struct LogTcpDataFileCtx_ {
56
    LogFileCtx *file_ctx;
57
    enum OutputStreamingType type;
58
    const char *log_dir;
59
    int file;
60
    int dir;
61
} LogTcpDataFileCtx;
62
63
typedef struct LogTcpDataLogThread_ {
64
    LogTcpDataFileCtx *tcpdatalog_ctx;
65
    /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
66
    MemBuffer *buffer;
67
} LogTcpDataLogThread;
68
69
static int LogTcpDataLoggerDir(ThreadVars *tv, void *thread_data, const Flow *f,
70
        const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
71
0
{
72
0
    SCEnter();
73
0
    LogTcpDataLogThread *aft = thread_data;
74
0
    LogTcpDataFileCtx *td = aft->tcpdatalog_ctx;
75
0
    const char *mode = "a";
76
77
0
    if (flags & OUTPUT_STREAMING_FLAG_OPEN)
78
0
        mode = "w";
79
80
0
    if (data && data_len) {
81
0
        char srcip[46] = "", dstip[46] = "";
82
0
        if (FLOW_IS_IPV4(f)) {
83
0
            PrintInet(AF_INET, (const void *)&f->src.addr_data32[0], srcip, sizeof(srcip));
84
0
            PrintInet(AF_INET, (const void *)&f->dst.addr_data32[0], dstip, sizeof(dstip));
85
0
        } else if (FLOW_IS_IPV6(f)) {
86
0
            PrintInet(AF_INET6, (const void *)f->src.addr_data32, srcip, sizeof(srcip));
87
0
            PrintInet(AF_INET6, (const void *)f->dst.addr_data32, dstip, sizeof(dstip));
88
0
        }
89
90
0
        char name[PATH_MAX];
91
92
0
        char tx[64] = { 0 };
93
0
        if (flags & OUTPUT_STREAMING_FLAG_TRANSACTION) {
94
0
            snprintf(tx, sizeof(tx), "%"PRIu64, tx_id);
95
0
        }
96
97
0
        snprintf(name, sizeof(name), "%s/%s/%s_%u-%s_%u-%s-%s.data",
98
0
                td->log_dir,
99
0
                td->type == STREAMING_HTTP_BODIES ? "http" : "tcp",
100
0
                srcip, f->sp, dstip, f->dp, tx,
101
0
                flags & OUTPUT_STREAMING_FLAG_TOSERVER ? "ts" : "tc");
102
103
0
        FILE *fp = fopen(name, mode);
104
0
        BUG_ON(fp == NULL);
105
106
        // PrintRawDataFp(stdout, (uint8_t *)data, data_len);
107
0
        fwrite(data, data_len, 1, fp);
108
109
0
        fclose(fp);
110
0
    }
111
0
    SCReturnInt(TM_ECODE_OK);
112
0
}
113
114
static int LogTcpDataLoggerFile(ThreadVars *tv, void *thread_data, const Flow *f,
115
        const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
116
0
{
117
0
    SCEnter();
118
0
    LogTcpDataLogThread *aft = thread_data;
119
0
    LogTcpDataFileCtx *td = aft->tcpdatalog_ctx;
120
121
0
    if (data && data_len) {
122
0
        MemBufferReset(aft->buffer);
123
124
0
        char srcip[46] = "", dstip[46] = "";
125
0
        if (FLOW_IS_IPV4(f)) {
126
0
            PrintInet(AF_INET, (const void *)&f->src.addr_data32[0], srcip, sizeof(srcip));
127
0
            PrintInet(AF_INET, (const void *)&f->dst.addr_data32[0], dstip, sizeof(dstip));
128
0
        } else if (FLOW_IS_IPV6(f)) {
129
0
            PrintInet(AF_INET6, (const void *)f->src.addr_data32, srcip, sizeof(srcip));
130
0
            PrintInet(AF_INET6, (const void *)f->dst.addr_data32, dstip, sizeof(dstip));
131
0
        }
132
133
0
        char name[PATH_MAX];
134
0
        snprintf(name, sizeof(name), "%s_%u-%s_%u-%s:",
135
0
                srcip, f->sp, dstip, f->dp,
136
0
                flags & OUTPUT_STREAMING_FLAG_TOSERVER ? "ts" : "tc");
137
138
0
        PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
139
0
                aft->buffer->size, (uint8_t *)name,strlen(name));
140
0
        MemBufferWriteString(aft->buffer, "\n");
141
142
0
        PrintRawDataToBuffer(aft->buffer->buffer, &aft->buffer->offset,
143
0
                aft->buffer->size, (uint8_t *)data,data_len);
144
145
0
        td->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
146
0
                MEMBUFFER_OFFSET(aft->buffer), td->file_ctx);
147
0
    }
148
0
    SCReturnInt(TM_ECODE_OK);
149
0
}
150
151
int LogTcpDataLogger(ThreadVars *tv, void *thread_data, const Flow *f,
152
        const uint8_t *data, uint32_t data_len, uint64_t tx_id, uint8_t flags)
153
0
{
154
0
    SCEnter();
155
0
    LogTcpDataLogThread *aft = thread_data;
156
0
    LogTcpDataFileCtx *td = aft->tcpdatalog_ctx;
157
158
0
    if (td->dir == 1)
159
0
        LogTcpDataLoggerDir(tv, thread_data, f, data, data_len, tx_id, flags);
160
0
    if (td->file == 1)
161
0
        LogTcpDataLoggerFile(tv, thread_data, f, data, data_len, tx_id, flags);
162
163
0
    SCReturnInt(TM_ECODE_OK);
164
0
}
165
166
TmEcode LogTcpDataLogThreadInit(ThreadVars *t, const void *initdata, void **data)
167
0
{
168
0
    LogTcpDataLogThread *aft = SCMalloc(sizeof(LogTcpDataLogThread));
169
0
    if (unlikely(aft == NULL))
170
0
        return TM_ECODE_FAILED;
171
0
    memset(aft, 0, sizeof(LogTcpDataLogThread));
172
173
0
    if(initdata == NULL)
174
0
    {
175
0
        SCLogDebug("Error getting context. \"initdata\" argument NULL");
176
0
        SCFree(aft);
177
0
        return TM_ECODE_FAILED;
178
0
    }
179
180
0
    aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
181
0
    if (aft->buffer == NULL) {
182
0
        SCFree(aft);
183
0
        return TM_ECODE_FAILED;
184
0
    }
185
186
    /* Use the Output Context (file pointer and mutex) */
187
0
    aft->tcpdatalog_ctx= ((OutputCtx *)initdata)->data;
188
189
0
    *data = (void *)aft;
190
0
    return TM_ECODE_OK;
191
0
}
192
193
TmEcode LogTcpDataLogThreadDeinit(ThreadVars *t, void *data)
194
0
{
195
0
    LogTcpDataLogThread *aft = (LogTcpDataLogThread *)data;
196
0
    if (aft == NULL) {
197
0
        return TM_ECODE_OK;
198
0
    }
199
200
0
    MemBufferFree(aft->buffer);
201
    /* clear memory */
202
0
    memset(aft, 0, sizeof(LogTcpDataLogThread));
203
204
0
    SCFree(aft);
205
0
    return TM_ECODE_OK;
206
0
}
207
208
/** \brief Create a new http log LogFileCtx.
209
 *  \param conf Pointer to ConfNode containing this loggers configuration.
210
 *  \return NULL if failure, LogFileCtx* to the file_ctx if succesful
211
 * */
212
OutputInitResult LogTcpDataLogInitCtx(ConfNode *conf)
213
0
{
214
0
    OutputInitResult result = { NULL, false };
215
0
    char filename[PATH_MAX] = "";
216
0
    char dirname[32] = "";
217
0
    strlcpy(filename, DEFAULT_LOG_FILENAME, sizeof(filename));
218
219
0
    LogFileCtx *file_ctx = LogFileNewCtx();
220
0
    if(file_ctx == NULL) {
221
0
        SCLogError("couldn't create new file_ctx");
222
0
        return result;
223
0
    }
224
225
0
    LogTcpDataFileCtx *tcpdatalog_ctx = SCMalloc(sizeof(LogTcpDataFileCtx));
226
0
    if (unlikely(tcpdatalog_ctx == NULL)) {
227
0
        LogFileFreeCtx(file_ctx);
228
0
        return result;
229
0
    }
230
0
    memset(tcpdatalog_ctx, 0x00, sizeof(LogTcpDataFileCtx));
231
232
0
    tcpdatalog_ctx->file_ctx = file_ctx;
233
234
0
    if (conf) {
235
0
        if (conf->name) {
236
0
            if (strcmp(conf->name, "tcp-data") == 0) {
237
0
                tcpdatalog_ctx->type = STREAMING_TCP_DATA;
238
0
                snprintf(filename, sizeof(filename), "%s.log", conf->name);
239
0
                strlcpy(dirname, "tcp", sizeof(dirname));
240
0
            } else if (strcmp(conf->name, "http-body-data") == 0) {
241
0
                tcpdatalog_ctx->type = STREAMING_HTTP_BODIES;
242
0
                snprintf(filename, sizeof(filename), "%s.log", conf->name);
243
0
                strlcpy(dirname, "http", sizeof(dirname));
244
0
            }
245
0
        }
246
247
0
        const char *logtype = ConfNodeLookupChildValue(conf, "type");
248
0
        if (logtype == NULL)
249
0
            logtype = "file";
250
251
0
        if (strcmp(logtype, "file") == 0) {
252
0
            tcpdatalog_ctx->file = 1;
253
0
        } else if (strcmp(logtype, "dir") == 0) {
254
0
            tcpdatalog_ctx->dir = 1;
255
0
        } else if (strcmp(logtype, "both") == 0) {
256
0
            tcpdatalog_ctx->file = 1;
257
0
            tcpdatalog_ctx->dir = 1;
258
0
        }
259
0
    } else {
260
0
        tcpdatalog_ctx->file = 1;
261
0
        tcpdatalog_ctx->dir = 0;
262
0
    }
263
264
0
    if (tcpdatalog_ctx->file == 1) {
265
0
        SCLogInfo("opening logfile");
266
0
        if (SCConfLogOpenGeneric(conf, file_ctx, filename, 1) < 0) {
267
0
            LogFileFreeCtx(file_ctx);
268
0
            SCFree(tcpdatalog_ctx);
269
0
            return result;
270
0
        }
271
0
    }
272
273
0
    if (tcpdatalog_ctx->dir == 1) {
274
0
        tcpdatalog_ctx->log_dir = ConfigGetLogDirectory();
275
0
        char dirfull[PATH_MAX];
276
277
        /* create the filename to use */
278
0
        snprintf(dirfull, PATH_MAX, "%s/%s", tcpdatalog_ctx->log_dir, dirname);
279
280
0
        SCLogInfo("using directory %s", dirfull);
281
282
        /* if mkdir fails file open will fail, so deal with errors there */
283
0
        (void)SCMkDir(dirfull, 0700);
284
0
    }
285
286
0
    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
287
0
    if (unlikely(output_ctx == NULL)) {
288
0
        goto parsererror;
289
0
    }
290
291
0
    output_ctx->data = tcpdatalog_ctx;
292
0
    output_ctx->DeInit = LogTcpDataLogDeInitCtx;
293
294
0
    SCLogDebug("Streaming log output initialized");
295
0
    result.ctx = output_ctx;
296
0
    result.ok = true;
297
0
    return result;
298
299
0
parsererror:
300
0
    LogFileFreeCtx(file_ctx);
301
0
    SCFree(tcpdatalog_ctx);
302
0
    SCLogError("Syntax error in custom http log format string.");
303
0
    return result;
304
305
0
}
306
307
static void LogTcpDataLogDeInitCtx(OutputCtx *output_ctx)
308
0
{
309
0
    LogTcpDataFileCtx *tcpdatalog_ctx = (LogTcpDataFileCtx *)output_ctx->data;
310
0
    LogFileFreeCtx(tcpdatalog_ctx->file_ctx);
311
0
    SCFree(tcpdatalog_ctx);
312
0
    SCFree(output_ctx);
313
0
}