• 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

87.16
/src/detect-file-data.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
 */
24

25
#include "suricata-common.h"
26
#include "threads.h"
27
#include "decode.h"
28

29
#include "detect.h"
30
#include "detect-parse.h"
31

32
#include "detect-engine.h"
33
#include "detect-engine-buffer.h"
34
#include "detect-engine-mpm.h"
35
#include "detect-engine-state.h"
36
#include "detect-engine-prefilter.h"
37
#include "detect-engine-content-inspection.h"
38
#include "detect-engine-file.h"
39
#include "detect-file-data.h"
40

41
#include "app-layer.h"
42
#include "app-layer-parser.h"
43
#include "app-layer-htp.h"
44
#include "app-layer-smtp.h"
45

46
#include "flow.h"
47
#include "flow-var.h"
48
#include "flow-util.h"
49

50
#include "util-debug.h"
51
#include "util-spm-bm.h"
52
#include "util-unittest.h"
53
#include "util-unittest-helper.h"
54
#include "util-file-decompression.h"
55
#include "util-profiling.h"
56

57
static int DetectFiledataSetup (DetectEngineCtx *, Signature *, const char *);
58
#ifdef UNITTESTS
59
static void DetectFiledataRegisterTests(void);
60
#endif
61
static void DetectFiledataSetupCallback(
62
        const DetectEngineCtx *de_ctx, Signature *s, const DetectBufferType *map);
63
static int g_file_data_buffer_id = 0;
64

65
/* file API */
66
int PrefilterMpmFiledataRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
67
        const DetectBufferMpmRegistry *mpm_reg, int list_id);
68

69
// file protocols with common file handling
70
typedef struct {
71
    AppProto alproto;
72
    int direction;
73
    int to_client_progress;
74
    int to_server_progress;
75
} DetectFileHandlerProtocol_t;
76

77
/* Table with all filehandler registrations */
78
DetectFileHandlerTableElmt filehandler_table[DETECT_TBLSIZE_STATIC];
79

80
#define ALPROTO_WITHFILES_MAX 16
×
81

82
// file protocols with common file handling
83
DetectFileHandlerProtocol_t al_protocols[ALPROTO_WITHFILES_MAX] = {
84
    { .alproto = ALPROTO_NFS, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
85
    { .alproto = ALPROTO_SMB, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
86
    { .alproto = ALPROTO_FTP, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
87
    { .alproto = ALPROTO_FTPDATA, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
88
    { .alproto = ALPROTO_HTTP1,
89
            .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT,
90
            .to_client_progress = HTP_RESPONSE_PROGRESS_BODY,
91
            .to_server_progress = HTP_REQUEST_PROGRESS_BODY },
92
    { .alproto = ALPROTO_HTTP2,
93
            .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT,
94
            .to_client_progress = HTTP2StateDataServer,
95
            .to_server_progress = HTTP2StateDataClient },
96
    { .alproto = ALPROTO_SMTP, .direction = SIG_FLAG_TOSERVER }, { .alproto = ALPROTO_UNKNOWN }
97
};
98

99
void DetectFileRegisterProto(
100
        AppProto alproto, int direction, int to_client_progress, int to_server_progress)
101
{
×
102
    size_t i = 0;
×
103
    while (i < ALPROTO_WITHFILES_MAX && al_protocols[i].alproto != ALPROTO_UNKNOWN) {
×
104
        i++;
×
105
    }
×
106
    if (i == ALPROTO_WITHFILES_MAX) {
×
107
        return;
×
108
    }
×
109
    al_protocols[i].alproto = alproto;
×
110
    al_protocols[i].direction = direction;
×
111
    al_protocols[i].to_client_progress = to_client_progress;
×
112
    al_protocols[i].to_server_progress = to_server_progress;
×
113
    if (i + 1 < ALPROTO_WITHFILES_MAX) {
×
114
        al_protocols[i + 1].alproto = ALPROTO_UNKNOWN;
×
115
    }
×
116
}
×
117

118
void DetectFileRegisterFileProtocols(DetectFileHandlerTableElmt *reg)
119
{
12✔
120
    for (size_t i = 0; i < g_alproto_max; i++) {
96✔
121
        if (al_protocols[i].alproto == ALPROTO_UNKNOWN) {
96✔
122
            break;
12✔
123
        }
12✔
124
        int direction = al_protocols[i].direction == 0
84✔
125
                                ? (int)(SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)
84✔
126
                                : al_protocols[i].direction;
84✔
127

128
        if (direction & SIG_FLAG_TOCLIENT) {
84✔
129
            DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOCLIENT, reg->priority, reg->PrefilterFn,
72✔
130
                    reg->GetData, al_protocols[i].alproto, al_protocols[i].to_client_progress);
72✔
131
            DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto,
72✔
132
                    SIG_FLAG_TOCLIENT, al_protocols[i].to_client_progress, reg->Callback,
72✔
133
                    reg->GetData);
72✔
134
        }
72✔
135
        if (direction & SIG_FLAG_TOSERVER) {
84✔
136
            DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOSERVER, reg->priority, reg->PrefilterFn,
84✔
137
                    reg->GetData, al_protocols[i].alproto, al_protocols[i].to_server_progress);
84✔
138
            DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto,
84✔
139
                    SIG_FLAG_TOSERVER, al_protocols[i].to_server_progress, reg->Callback,
84✔
140
                    reg->GetData);
84✔
141
        }
84✔
142
    }
84✔
143
}
12✔
144

145
/**
146
 * \brief Registration function for keyword: file_data
147
 */
148
void DetectFiledataRegister(void)
149
{
3✔
150
    sigmatch_table[DETECT_FILE_DATA].name = "file.data";
3✔
151
    sigmatch_table[DETECT_FILE_DATA].alias = "file_data";
3✔
152
    sigmatch_table[DETECT_FILE_DATA].desc = "make content keywords match on file data";
3✔
153
    sigmatch_table[DETECT_FILE_DATA].url = "/rules/file-keywords.html#file-data";
3✔
154
    sigmatch_table[DETECT_FILE_DATA].Setup = DetectFiledataSetup;
3✔
155
#ifdef UNITTESTS
156
    sigmatch_table[DETECT_FILE_DATA].RegisterTests = DetectFiledataRegisterTests;
157
#endif
158
    sigmatch_table[DETECT_FILE_DATA].flags =
3✔
159
            SIGMATCH_OPTIONAL_OPT | SIGMATCH_SUPPORT_DIR | SIGMATCH_INFO_MULTI_BUFFER;
3✔
160

161
    filehandler_table[DETECT_FILE_DATA].name = "file_data";
3✔
162
    filehandler_table[DETECT_FILE_DATA].priority = 2;
3✔
163
    filehandler_table[DETECT_FILE_DATA].PrefilterFn = PrefilterMpmFiledataRegister;
3✔
164
    filehandler_table[DETECT_FILE_DATA].Callback = DetectEngineInspectFiledata;
3✔
165

166
    DetectBufferTypeRegisterSetupCallback("file_data", DetectFiledataSetupCallback);
3✔
167

168
    DetectBufferTypeSetDescriptionByName("file_data", "data from tracked files");
3✔
169
    DetectBufferTypeSupportsMultiInstance("file_data");
3✔
170

171
    g_file_data_buffer_id = DetectBufferTypeGetByName("file_data");
3✔
172
}
3✔
173

174
static void SetupDetectEngineConfig(DetectEngineCtx *de_ctx) {
65,182✔
175
    if (de_ctx->filedata_config)
65,182✔
176
        return;
62,367✔
177

178
    de_ctx->filedata_config = SCMalloc(g_alproto_max * sizeof(DetectFileDataCfg));
2,815✔
179
    if (unlikely(de_ctx->filedata_config == NULL))
2,815✔
180
        return;
×
181
    /* initialize default */
182
    for (AppProto i = 0; i < g_alproto_max; i++) {
115,415✔
183
        de_ctx->filedata_config[i].content_limit = FILEDATA_CONTENT_LIMIT;
112,600✔
184
        de_ctx->filedata_config[i].content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;
112,600✔
185
    }
112,600✔
186

187
    /* add protocol specific settings here */
188

189
    /* SMTP */
190
    de_ctx->filedata_config[ALPROTO_SMTP].content_limit = smtp_config.content_limit;
2,815✔
191
    de_ctx->filedata_config[ALPROTO_SMTP].content_inspect_min_size =
2,815✔
192
            smtp_config.content_inspect_min_size;
2,815✔
193
}
2,815✔
194

195
/**
196
 * \brief this function is used to parse filedata options
197
 * \brief into the current signature
198
 *
199
 * \param de_ctx pointer to the Detection Engine Context
200
 * \param s pointer to the Current Signature
201
 * \param str pointer to the user provided "filestore" option
202
 *
203
 * \retval 0 on Success
204
 * \retval -1 on Failure
205
 */
206
static int DetectFiledataSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
207
{
65,378✔
208
    SCEnter();
65,378✔
209

210
    if (s->alproto != ALPROTO_UNKNOWN && !AppLayerParserSupportsFiles(IPPROTO_TCP, s->alproto) &&
65,378✔
211
            !AppLayerParserSupportsFiles(IPPROTO_UDP, s->alproto)) {
65,378✔
212
        SCLogError("The 'file_data' keyword cannot be used with protocol %s",
123✔
213
                AppLayerGetProtoName(s->alproto));
123✔
214
        return -1;
123✔
215
    }
123✔
216

217
    if (s->alproto == ALPROTO_SMTP && (s->init_data->init_flags & SIG_FLAG_INIT_FLOW) &&
65,255✔
218
        !(s->flags & SIG_FLAG_TOSERVER) && (s->flags & SIG_FLAG_TOCLIENT)) {
65,255✔
UNCOV
219
        SCLogError("The 'file-data' keyword cannot be used with SMTP flow:to_client or "
×
UNCOV
220
                   "flow:from_server.");
×
UNCOV
221
        return -1;
×
UNCOV
222
    }
×
223

224
    if (SCDetectBufferSetActiveList(de_ctx, s, DetectBufferTypeGetByName("file_data")) < 0)
65,255✔
225
        return -1;
72✔
226

227
    s->init_data->init_flags |= SIG_FLAG_INIT_FILEDATA;
65,183✔
228
    if ((s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOCLIENT) == 0) {
65,183✔
229
        // we cannot use a transactional rule with a fast pattern to client and this
230
        if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT) {
65,109✔
231
            SCLogError("fast_pattern cannot be used on to_client keyword for "
1✔
232
                       "transactional rule with a streaming buffer to server %u",
1✔
233
                    s->id);
1✔
234
            return -1;
1✔
235
        }
1✔
236
        s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER;
65,108✔
237
    }
65,108✔
238

239
    SetupDetectEngineConfig(de_ctx);
65,182✔
240
    return 0;
65,182✔
241
}
65,183✔
242

243
static void DetectFiledataSetupCallback(
244
        const DetectEngineCtx *de_ctx, Signature *s, const DetectBufferType *map)
245
{
35,999✔
246
    if (s->alproto == ALPROTO_HTTP1 || s->alproto == ALPROTO_UNKNOWN ||
35,999✔
247
            s->alproto == ALPROTO_HTTP) {
35,999✔
248
        AppLayerHtpEnableResponseBodyCallback();
33,175✔
249
    }
33,175✔
250

251
    /* server body needs to be inspected in sync with stream if possible */
252
    s->init_data->init_flags |= SIG_FLAG_INIT_NEED_FLUSH;
35,999✔
253

254
    SCLogDebug("callback invoked by %u", s->id);
35,999✔
255
}
35,999✔
256

257
/* common */
258

259
static void PrefilterMpmFiledataFree(void *ptr)
260
{
68,227✔
261
    SCFree(ptr);
68,227✔
262
}
68,227✔
263

264
/* file API based inspection */
265

266
static inline InspectionBuffer *FiledataWithXformsGetDataCallback(DetectEngineThreadCtx *det_ctx,
267
        const DetectEngineTransforms *transforms, const int list_id, int local_file_id,
268
        InspectionBuffer *base_buffer)
269
{
2,996✔
270
    InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, local_file_id);
2,996✔
271
    if (buffer == NULL) {
2,996✔
272
        SCLogDebug("list_id: %d: no buffer", list_id);
×
273
        return NULL;
×
274
    }
×
275
    if (buffer->initialized) {
2,996✔
276
        SCLogDebug("list_id: %d: returning %p", list_id, buffer);
19✔
277
        return buffer;
19✔
278
    }
19✔
279

280
    InspectionBufferSetupMulti(
2,977✔
281
            det_ctx, buffer, transforms, base_buffer->inspect, base_buffer->inspect_len);
2,977✔
282
    buffer->inspect_offset = base_buffer->inspect_offset;
2,977✔
283
    SCLogDebug("xformed buffer %p size %u", buffer, buffer->inspect_len);
2,977✔
284
    SCReturnPtr(buffer, "InspectionBuffer");
2,977✔
285
}
2,996✔
286

287
static InspectionBuffer *FiledataGetDataCallback(DetectEngineThreadCtx *det_ctx,
288
        const DetectEngineTransforms *transforms, Flow *f, uint8_t flow_flags, File *cur_file,
289
        const int list_id, const int base_id, int local_file_id, void *txv)
290
{
7,487✔
291
    SCEnter();
7,487✔
292
    SCLogDebug("starting: list_id %d base_id %d", list_id, base_id);
7,487✔
293

294
    InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, base_id, local_file_id);
7,487✔
295
    SCLogDebug("base: buffer %p", buffer);
7,487✔
296
    if (buffer == NULL)
7,487✔
297
        return NULL;
×
298
    if (base_id != list_id && buffer->inspect != NULL) {
7,487✔
299
        SCLogDebug("handle xform %s", (list_id != base_id) ? "true" : "false");
975✔
300
        return FiledataWithXformsGetDataCallback(
975✔
301
                det_ctx, transforms, list_id, local_file_id, buffer);
975✔
302
    }
975✔
303
    if (buffer->initialized) {
6,512✔
304
        SCLogDebug("base_id: %d, not first: use %p", base_id, buffer);
1,703✔
305
        return buffer;
1,703✔
306
    }
1,703✔
307

308
    const uint64_t file_size = FileDataSize(cur_file);
4,809✔
309
    const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
4,809✔
310
    uint32_t content_limit = FILEDATA_CONTENT_LIMIT;
4,809✔
311
    uint32_t content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;
4,809✔
312
    if (de_ctx->filedata_config) {
4,809✔
313
        content_limit = de_ctx->filedata_config[f->alproto].content_limit;
4,617✔
314
        content_inspect_min_size = de_ctx->filedata_config[f->alproto].content_inspect_min_size;
4,617✔
315
    }
4,617✔
316

317
    SCLogDebug("[list %d] content_limit %u, content_inspect_min_size %u", list_id, content_limit,
4,809✔
318
            content_inspect_min_size);
4,809✔
319

320
    SCLogDebug("[list %d] file %p size %" PRIu64 ", state %d", list_id, cur_file, file_size,
4,809✔
321
            cur_file->state);
4,809✔
322

323
    /* no new data */
324
    if (cur_file->content_inspected == file_size) {
4,809✔
325
        SCLogDebug("no new data");
151✔
326
        goto empty_return;
151✔
327
    }
151✔
328

329
    if (file_size == 0) {
4,658✔
330
        SCLogDebug("no data to inspect for this transaction");
×
331
        goto empty_return;
×
332
    }
×
333

334
    SCLogDebug("offset %" PRIu64, StreamingBufferGetOffset(cur_file->sb));
4,658✔
335
    SCLogDebug("size %" PRIu64, cur_file->size);
4,658✔
336
    SCLogDebug("content_inspected %" PRIu64, cur_file->content_inspected);
4,658✔
337
    SCLogDebug("inspect_window %" PRIu32, cur_file->inspect_window);
4,658✔
338
    SCLogDebug("inspect_min_size %" PRIu32, cur_file->inspect_min_size);
4,658✔
339

340
    bool ips = false;
4,658✔
341
    uint64_t offset = 0;
4,658✔
342
    if (f->alproto == ALPROTO_HTTP1) {
4,658✔
343

344
        htp_tx_t *tx = txv;
1,982✔
345
        HtpState *htp_state = f->alstate;
1,982✔
346
        ips = htp_state->cfg->http_body_inline;
1,982✔
347

348
        const bool body_done = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx,
1,982✔
349
                                       flow_flags) > HTP_RESPONSE_PROGRESS_BODY;
1,982✔
350

351
        SCLogDebug("response.body_limit %u file_size %" PRIu64
1,982✔
352
                   ", cur_file->inspect_min_size %" PRIu32 ", EOF %s, progress > body? %s",
1,982✔
353
                htp_state->cfg->response.body_limit, file_size, cur_file->inspect_min_size,
1,982✔
354
                flow_flags & STREAM_EOF ? "true" : "false", BOOL2STR(body_done));
1,982✔
355

356
        if (!htp_state->cfg->http_body_inline) {
1,982✔
357
            /* inspect the body if the transfer is complete or we have hit
358
             * our body size limit */
359
            if ((htp_state->cfg->response.body_limit == 0 ||
1,982✔
360
                        file_size < htp_state->cfg->response.body_limit) &&
1,982✔
361
                    file_size < cur_file->inspect_min_size && !body_done &&
1,982✔
362
                    !(flow_flags & STREAM_EOF)) {
1,982✔
363
                SCLogDebug("we still haven't seen the entire response body.  "
578✔
364
                           "Let's defer body inspection till we see the "
578✔
365
                           "entire body.");
578✔
366
                goto empty_return;
578✔
367
            }
578✔
368
            SCLogDebug("inline and we're continuing");
1,404✔
369
        }
1,404✔
370

371
        bool force = (flow_flags & STREAM_EOF) || (cur_file->state > FILE_STATE_OPENED) ||
1,982✔
372
                     body_done || htp_state->cfg->http_body_inline;
1,404✔
373
        /* get the inspect buffer
374
         *
375
         * make sure that we have at least the configured inspect_win size.
376
         * If we have more, take at least 1/4 of the inspect win size before
377
         * the new data.
378
         */
379
        if (cur_file->content_inspected == 0) {
1,404✔
380
            if (!force && file_size < cur_file->inspect_min_size) {
1,026✔
UNCOV
381
                SCLogDebug("skip as file_size %" PRIu64 " < inspect_min_size %u", file_size,
×
UNCOV
382
                        cur_file->inspect_min_size);
×
UNCOV
383
                goto empty_return;
×
UNCOV
384
            }
×
385
        } else {
1,026✔
386
            uint64_t new_data = file_size - cur_file->content_inspected;
378✔
387
            DEBUG_VALIDATE_BUG_ON(new_data == 0);
378✔
388
            if (new_data < cur_file->inspect_window) {
378✔
389
                uint64_t inspect_short = cur_file->inspect_window - new_data;
304✔
390
                if (cur_file->content_inspected < inspect_short) {
304✔
391
                    offset = 0;
×
392
                    SCLogDebug("offset %" PRIu64, offset);
×
393
                } else {
304✔
394
                    offset = cur_file->content_inspected - inspect_short;
304✔
395
                    SCLogDebug("offset %" PRIu64, offset);
304✔
396
                }
304✔
397
            } else {
304✔
398
                BUG_ON(cur_file->content_inspected == 0);
74✔
399
                uint32_t margin = cur_file->inspect_window / 4;
74✔
400
                if ((uint64_t)margin <= cur_file->content_inspected) {
74✔
401
                    offset = cur_file->content_inspected - (cur_file->inspect_window / 4);
74✔
402
                } else {
74✔
403
                    offset = 0;
×
404
                }
×
405
                SCLogDebug("offset %" PRIu64 " (data from offset %" PRIu64 ")", offset,
74✔
406
                        file_size - offset);
74✔
407
            }
74✔
408
        }
378✔
409

410
    } else {
2,676✔
411
        if ((content_limit == 0 || file_size < content_limit) &&
2,676✔
412
                file_size < content_inspect_min_size && !(flow_flags & STREAM_EOF) &&
2,676✔
413
                !(cur_file->state > FILE_STATE_OPENED)) {
2,676✔
414
            SCLogDebug("we still haven't seen the entire content. "
708✔
415
                       "Let's defer content inspection till we see the "
708✔
416
                       "entire content. We've seen %ld and need at least %d",
708✔
417
                    file_size, content_inspect_min_size);
708✔
418
            goto empty_return;
708✔
419
        }
708✔
420
        offset = cur_file->content_inspected;
1,968✔
421
    }
1,968✔
422

423
    const uint8_t *data;
3,372✔
424
    uint32_t data_len;
3,372✔
425

426
    SCLogDebug("Fetching data at offset: %ld", offset);
3,372✔
427
    StreamingBufferGetDataAtOffset(cur_file->sb, &data, &data_len, offset);
3,372✔
428
    SCLogDebug("data_len %u", data_len);
3,372✔
429
    /* update inspected tracker */
430
    buffer->inspect_offset = offset;
3,372✔
431

432
    if (ips && file_size < cur_file->inspect_min_size) {
3,372✔
433
        // don't update content_inspected yet
434
    } else {
3,372✔
435
        SCLogDebug("content inspected: %" PRIu64, cur_file->content_inspected);
3,372✔
436
        cur_file->content_inspected = MAX(cur_file->content_inspected, offset + data_len);
3,372✔
437
        SCLogDebug("content inspected: %" PRIu64, cur_file->content_inspected);
3,372✔
438
    }
3,372✔
439

440
    InspectionBufferSetupMulti(det_ctx, buffer, NULL, data, data_len);
3,372✔
441
    SCLogDebug("[list %d] [before] buffer offset %" PRIu64 "; buffer len %" PRIu32
3,372✔
442
               "; data_len %" PRIu32 "; file_size %" PRIu64,
3,372✔
443
            list_id, buffer->inspect_offset, buffer->inspect_len, data_len, file_size);
3,372✔
444

445
    if (f->alproto == ALPROTO_HTTP1 && flow_flags & STREAM_TOCLIENT) {
3,372✔
446
        HtpState *htp_state = f->alstate;
1,328✔
447
        /* built-in 'transformation' */
448
        if (htp_state->cfg->swf_decompression_enabled) {
1,328✔
449
            int swf_file_type = FileIsSwfFile(data, data_len);
1,328✔
450
            if (swf_file_type == FILE_SWF_ZLIB_COMPRESSION ||
1,328✔
451
                    swf_file_type == FILE_SWF_LZMA_COMPRESSION) {
1,328✔
UNCOV
452
                SCLogDebug("decompressing ...");
×
UNCOV
453
                (void)FileSwfDecompression(data, data_len, det_ctx, buffer,
×
UNCOV
454
                        htp_state->cfg->swf_compression_type, htp_state->cfg->swf_decompress_depth,
×
UNCOV
455
                        htp_state->cfg->swf_compress_depth);
×
UNCOV
456
                SCLogDebug("uncompressed buffer %p size %u; buf: \"%s\"", buffer,
×
UNCOV
457
                        buffer->inspect_len, (char *)buffer->inspect);
×
UNCOV
458
            }
×
459
        }
1,328✔
460
    }
1,328✔
461

462
    SCLogDebug("content inspected: %" PRIu64, cur_file->content_inspected);
3,372✔
463

464
    /* get buffer for the list id if it is different from the base id */
465
    if (list_id != base_id) {
3,372✔
466
        SCLogDebug("regular %d has been set up: now handle xforms id %d", base_id, list_id);
2,021✔
467
        InspectionBuffer *tbuffer = FiledataWithXformsGetDataCallback(
2,021✔
468
                det_ctx, transforms, list_id, local_file_id, buffer);
2,021✔
469
        SCReturnPtr(tbuffer, "InspectionBuffer");
2,021✔
470
    }
2,021✔
471
    SCReturnPtr(buffer, "InspectionBuffer");
3,372✔
472

473
empty_return:
1,437✔
474
    InspectionBufferSetupMultiEmpty(buffer);
1,437✔
475
    return NULL;
1,437✔
476
}
3,372✔
477

478
uint8_t DetectEngineInspectFiledata(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
479
        const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
480
        void *alstate, void *txv, uint64_t tx_id)
481
{
1,719✔
482
    const DetectEngineTransforms *transforms = NULL;
1,719✔
483
    if (!engine->mpm) {
1,719✔
484
        transforms = engine->v2.transforms;
208✔
485
    }
208✔
486

487
    AppLayerGetFileState files = AppLayerParserGetTxFiles(f, txv, flags);
1,719✔
488
    FileContainer *ffc = files.fc;
1,719✔
489
    if (ffc == NULL) {
1,719✔
490
        return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES;
×
491
    }
×
492
    if (ffc->head == NULL) {
1,719✔
493
        const bool eof = (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) >
468✔
494
                          engine->progress);
468✔
495
        if (eof && engine->match_on_null) {
468✔
496
            return DETECT_ENGINE_INSPECT_SIG_MATCH;
18✔
497
        }
18✔
498
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
450✔
499
    }
468✔
500

501
    int local_file_id = 0;
1,251✔
502
    File *file = ffc->head;
1,251✔
503
    for (; file != NULL; file = file->next) {
1,575✔
504
        InspectionBuffer *buffer = FiledataGetDataCallback(det_ctx, transforms, f, flags, file,
1,272✔
505
                engine->sm_list, engine->sm_list_base, local_file_id, txv);
1,272✔
506
        if (buffer == NULL) {
1,272✔
507
            local_file_id++;
84✔
508
            continue;
84✔
509
        }
84✔
510

511
        bool eof = (file->state == FILE_STATE_CLOSED);
1,272✔
512
        uint8_t ciflags = eof ? DETECT_CI_FLAGS_END : 0;
1,188✔
513
        if (buffer->inspect_offset == 0)
1,188✔
514
            ciflags |= DETECT_CI_FLAGS_START;
1,177✔
515

516
        const bool match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f,
1,188✔
517
                buffer->inspect, buffer->inspect_len, buffer->inspect_offset, ciflags,
1,188✔
518
                DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
1,188✔
519
        if (match) {
1,188✔
520
            return DETECT_ENGINE_INSPECT_SIG_MATCH;
948✔
521
        }
948✔
522
        local_file_id++;
240✔
523
    }
240✔
524

525
    return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
303✔
526
}
1,251✔
527

528
/** \brief Filedata Filedata Mpm prefilter callback
529
 *
530
 *  \param det_ctx detection engine thread ctx
531
 *  \param pectx inspection context
532
 *  \param p packet to inspect
533
 *  \param f flow to inspect
534
 *  \param txv tx to inspect
535
 *  \param idx transaction id
536
 *  \param flags STREAM_* flags including direction
537
 */
538
static void PrefilterTxFiledata(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
539
        Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *txd, const uint8_t flags)
540
{
60,015✔
541
    SCEnter();
60,015✔
542

543
    if (!AppLayerParserHasFilesInDir(txd, flags))
60,015✔
544
        return;
52,691✔
545

546
    const PrefilterMpmFiledata *ctx = (const PrefilterMpmFiledata *)pectx;
7,324✔
547
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
7,324✔
548
    const int list_id = ctx->list_id;
7,324✔
549

550
    AppLayerGetFileState files = AppLayerParserGetTxFiles(f, txv, flags);
7,324✔
551
    FileContainer *ffc = files.fc;
7,324✔
552
    if (ffc != NULL) {
7,324✔
553
        int local_file_id = 0;
7,323✔
554
        for (File *file = ffc->head; file != NULL; file = file->next) {
13,538✔
555
            InspectionBuffer *buffer = FiledataGetDataCallback(det_ctx, ctx->transforms, f, flags,
6,215✔
556
                    file, list_id, ctx->base_list_id, local_file_id, txv);
6,215✔
557
            if (buffer == NULL) {
6,215✔
558
                local_file_id++;
1,353✔
559
                continue;
1,353✔
560
            }
1,353✔
561
            SCLogDebug("[%" PRIu64 "] buffer size %u", PcapPacketCntGet(p), buffer->inspect_len);
4,862✔
562

563
            if (buffer->inspect_len >= mpm_ctx->minlen) {
4,862✔
564
                uint32_t prev_rule_id_array_cnt = det_ctx->pmq.rule_id_array_cnt;
4,184✔
565
                (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, &det_ctx->mtc, &det_ctx->pmq,
4,184✔
566
                        buffer->inspect, buffer->inspect_len);
4,184✔
567
                PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
4,184✔
568

569
                if (det_ctx->pmq.rule_id_array_cnt > prev_rule_id_array_cnt) {
4,184✔
570
                    SCLogDebug(
298✔
571
                            "%u matches", det_ctx->pmq.rule_id_array_cnt - prev_rule_id_array_cnt);
298✔
572
                }
298✔
573
            }
4,184✔
574
            local_file_id++;
4,862✔
575
        }
4,862✔
576
    }
7,323✔
577
}
7,324✔
578

579
int PrefilterMpmFiledataRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
580
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
581
{
68,721✔
582
    PrefilterMpmFiledata *pectx = SCCalloc(1, sizeof(*pectx));
68,721✔
583
    if (pectx == NULL)
68,721✔
584
        return -1;
×
585
    pectx->list_id = list_id;
68,721✔
586
    pectx->base_list_id = mpm_reg->sm_list_base;
68,721✔
587
    pectx->mpm_ctx = mpm_ctx;
68,721✔
588
    pectx->transforms = &mpm_reg->transforms;
68,721✔
589

590
    return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxFiledata,
68,721✔
591
            mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
68,721✔
592
            pectx, PrefilterMpmFiledataFree, mpm_reg->pname);
68,721✔
593
}
68,721✔
594

595
#ifdef UNITTESTS
596
#include "tests/detect-file-data.c"
597
#endif
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