Coverage Report

Created: 2025-07-23 07:29

/src/suricata7/src/detect-engine-frame.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2021-2023 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 "suricata.h"
27
28
#include "app-layer-parser.h"
29
#include "app-layer-frames.h"
30
31
#include "detect-engine.h"
32
#include "detect-engine-prefilter.h"
33
#include "detect-engine-content-inspection.h"
34
#include "detect-engine-mpm.h"
35
#include "detect-engine-frame.h"
36
37
#include "stream-tcp.h"
38
39
#include "util-profiling.h"
40
#include "util-validate.h"
41
#include "util-print.h"
42
43
struct FrameStreamData {
44
    // shared between prefilter and inspect
45
    DetectEngineThreadCtx *det_ctx;
46
    const DetectEngineTransforms *transforms;
47
    const Frame *frame;
48
    int list_id;
49
    uint32_t idx; /**< multi buffer idx, incremented for each stream chunk */
50
51
    // inspection only
52
    const DetectEngineFrameInspectionEngine *inspect_engine;
53
    const Signature *s;
54
    int inspect_result; // DETECT_ENGINE_INSPECT_SIG_MATCH / DETECT_ENGINE_INSPECT_SIG_NO_MATCH
55
    Packet *p;
56
57
    // prefilter only
58
    const MpmCtx *mpm_ctx;
59
60
    uint64_t requested_stream_offset;
61
};
62
63
static bool SetupStreamCallbackData(struct FrameStreamData *dst, const TcpSession *ssn,
64
        const TcpStream *stream, DetectEngineThreadCtx *det_ctx,
65
        const DetectEngineTransforms *transforms, const Frames *_frames, const Frame *frame,
66
        const int list_id, const bool eof);
67
68
static bool BufferSetup(struct FrameStreamData *fsd, InspectionBuffer *buffer, const uint8_t *input,
69
        const uint32_t input_len, const uint64_t input_offset);
70
static void BufferSetupUdp(InspectionBuffer *buffer, const Frame *frame, const Packet *p,
71
        const DetectEngineTransforms *transforms);
72
73
void DetectRunPrefilterFrame(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
74
        const Frames *frames, const Frame *frame, const AppProto alproto)
75
1.12k
{
76
1.12k
    SCLogDebug("pcap_cnt %" PRIu64, p->pcap_cnt);
77
1.12k
    PrefilterEngine *engine = sgh->frame_engines;
78
1.35k
    do {
79
1.35k
        BUG_ON(engine->alproto == ALPROTO_UNKNOWN);
80
1.35k
        if (engine->alproto == alproto && engine->ctx.frame_type == frame->type) {
81
143
            SCLogDebug("frame %p engine %p", frame, engine);
82
143
            PREFILTER_PROFILING_START(det_ctx);
83
143
            engine->cb.PrefilterFrame(det_ctx, engine->pectx, p, frames, frame);
84
143
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
85
143
        }
86
1.35k
        if (engine->is_last)
87
1.12k
            break;
88
231
        engine++;
89
231
    } while (1);
90
1.12k
}
91
92
/* generic mpm for frame engines */
93
94
// TODO same as Generic?
95
typedef struct PrefilterMpmFrameCtx {
96
    int list_id;
97
    const MpmCtx *mpm_ctx;
98
    const DetectEngineTransforms *transforms;
99
} PrefilterMpmFrameCtx;
100
101
static int FrameStreamDataPrefilterFunc(
102
        void *cb_data, const uint8_t *input, const uint32_t input_len, const uint64_t input_offset)
103
201k
{
104
201k
    struct FrameStreamData *fsd = cb_data;
105
201k
    SCLogDebug("prefilter: fsd %p { det_ctx:%p, transforms:%p, frame:%p, list_id:%d, idx:%u, "
106
201k
               "data_offset:%" PRIu64 "}, input: %p, input_len:%u, input_offset:%" PRIu64,
107
201k
            fsd, fsd->det_ctx, fsd->transforms, fsd->frame, fsd->list_id, fsd->idx,
108
201k
            fsd->requested_stream_offset, input, input_len, input_offset);
109
    // PrintRawDataFp(stdout, input, input_len);
110
111
201k
    InspectionBuffer *buffer =
112
201k
            InspectionBufferMultipleForListGet(fsd->det_ctx, fsd->list_id, fsd->idx++);
113
201k
    if (buffer == NULL) {
114
0
        return 0;
115
0
    }
116
201k
    SCLogDebug("buffer %p idx %u", buffer, fsd->idx);
117
118
201k
    const int more_chunks = BufferSetup(fsd, buffer, input, input_len, input_offset);
119
120
201k
    const uint32_t data_len = buffer->inspect_len;
121
201k
    const uint8_t *data = buffer->inspect;
122
201k
    DetectEngineThreadCtx *det_ctx = fsd->det_ctx;
123
201k
    const MpmCtx *mpm_ctx = fsd->mpm_ctx;
124
125
201k
    if (data != NULL && data_len >= mpm_ctx->minlen) {
126
        // PrintRawDataFp(stdout, data, data_len);
127
128
9.44k
        (void)mpm_table[mpm_ctx->mpm_type].Search(
129
9.44k
                mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, data, data_len);
130
9.44k
        SCLogDebug("det_ctx->pmq.rule_id_array_cnt %u", det_ctx->pmq.rule_id_array_cnt);
131
9.44k
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
132
9.44k
    }
133
201k
    return more_chunks;
134
201k
}
135
136
/** \brief Generic Mpm prefilter callback
137
 *
138
 *  \param det_ctx detection engine thread ctx
139
 *  \param frames container for the frames
140
 *  \param frame frame to inspect
141
 *  \param pectx inspection context
142
 */
143
static void PrefilterMpmFrame(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
144
        const Frames *frames, const Frame *frame)
145
143
{
146
143
    SCEnter();
147
148
143
    const PrefilterMpmFrameCtx *ctx = (const PrefilterMpmFrameCtx *)pectx;
149
143
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
150
151
143
    SCLogDebug("packet:%" PRIu64 ", prefilter running on list %d -> frame field type %u",
152
143
            p->pcap_cnt, ctx->list_id, frame->type);
153
143
    if (p->proto == IPPROTO_UDP) {
154
        // TODO can we use single here? Could it conflict with TCP?
155
3
        InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, ctx->list_id, 0);
156
3
        if (buffer == NULL)
157
0
            return;
158
3
        DEBUG_VALIDATE_BUG_ON(frame->offset >= p->payload_len);
159
3
        if (frame->offset >= p->payload_len)
160
0
            return;
161
162
3
        BufferSetupUdp(buffer, frame, p, ctx->transforms);
163
3
        const uint32_t data_len = buffer->inspect_len;
164
3
        const uint8_t *data = buffer->inspect;
165
166
        // PrintRawDataFp(stdout, data, data_len);
167
168
3
        if (data != NULL && data_len >= mpm_ctx->minlen) {
169
3
            (void)mpm_table[mpm_ctx->mpm_type].Search(
170
3
                    mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, data, data_len);
171
3
            SCLogDebug("det_ctx->pmq.rule_id_array_cnt %u", det_ctx->pmq.rule_id_array_cnt);
172
3
            PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
173
3
        }
174
140
    } else if (p->proto == IPPROTO_TCP) {
175
140
        BUG_ON(p->flow->protoctx == NULL);
176
140
        TcpSession *ssn = p->flow->protoctx;
177
140
        TcpStream *stream;
178
140
        if (PKT_IS_TOSERVER(p)) {
179
74
            stream = &ssn->client;
180
74
        } else {
181
66
            stream = &ssn->server;
182
66
        }
183
140
        const bool eof = ssn->state == TCP_CLOSED || PKT_IS_PSEUDOPKT(p);
184
185
140
        struct FrameStreamData fsd;
186
140
        memset(&fsd, 0, sizeof(fsd));
187
140
        fsd.mpm_ctx = mpm_ctx;
188
189
140
        if (SetupStreamCallbackData(&fsd, ssn, stream, det_ctx, ctx->transforms, frames, frame,
190
140
                    ctx->list_id, eof) == true) {
191
91
            StreamReassembleForFrame(ssn, stream, FrameStreamDataPrefilterFunc, &fsd,
192
91
                    fsd.requested_stream_offset, eof);
193
91
        }
194
140
    } else {
195
0
        DEBUG_VALIDATE_BUG_ON(1);
196
0
    }
197
143
    SCLogDebug("packet:%" PRIu64
198
143
               ", prefilter done running on list %d -> frame field type %u; have %u matches",
199
143
            p->pcap_cnt, ctx->list_id, frame->type, det_ctx->pmq.rule_id_array_cnt);
200
143
}
201
202
static void PrefilterMpmFrameFree(void *ptr)
203
6.75k
{
204
6.75k
    SCFree(ptr);
205
6.75k
}
206
207
int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
208
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
209
6.75k
{
210
6.75k
    SCEnter();
211
6.75k
    PrefilterMpmFrameCtx *pectx = SCCalloc(1, sizeof(*pectx));
212
6.75k
    if (pectx == NULL)
213
0
        return -1;
214
6.75k
    pectx->list_id = list_id;
215
6.75k
    BUG_ON(mpm_reg->frame_v1.alproto == ALPROTO_UNKNOWN);
216
6.75k
    pectx->mpm_ctx = mpm_ctx;
217
6.75k
    pectx->transforms = &mpm_reg->transforms;
218
219
6.75k
    int r = PrefilterAppendFrameEngine(de_ctx, sgh, PrefilterMpmFrame, mpm_reg->frame_v1.alproto,
220
6.75k
            mpm_reg->frame_v1.type, pectx, PrefilterMpmFrameFree, mpm_reg->pname);
221
6.75k
    if (r != 0) {
222
0
        SCFree(pectx);
223
0
    }
224
6.75k
    return r;
225
6.75k
}
226
227
int DetectRunFrameInspectRule(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s,
228
        Flow *f, Packet *p, const Frames *frames, const Frame *frame)
229
119
{
230
119
    BUG_ON(s->frame_inspect == NULL);
231
232
119
    SCLogDebug("inspecting rule %u against frame %p/%" PRIi64 "/%s", s->id, frame, frame->id,
233
119
            AppLayerParserGetFrameNameById(f->proto, f->alproto, frame->type));
234
235
333
    for (DetectEngineFrameInspectionEngine *e = s->frame_inspect; e != NULL; e = e->next) {
236
226
        if (frame->type == e->type) {
237
            // TODO check alproto, direction?
238
239
            // TODO there should be only one inspect engine for this frame, ever?
240
241
64
            if (e->v1.Callback(det_ctx, e, s, p, frames, frame) == true) {
242
12
                SCLogDebug("sid %u: e %p Callback returned true", s->id, e);
243
12
                return true;
244
12
            }
245
52
            SCLogDebug("sid %u: e %p Callback returned false", s->id, e);
246
162
        } else {
247
162
            SCLogDebug(
248
162
                    "sid %u: e %p not for frame type %u (want %u)", s->id, e, frame->type, e->type);
249
162
        }
250
226
    }
251
107
    return false;
252
119
}
253
254
static void BufferSetupUdp(InspectionBuffer *buffer, const Frame *frame, const Packet *p,
255
        const DetectEngineTransforms *transforms)
256
132
{
257
132
    uint8_t ci_flags = DETECT_CI_FLAGS_START;
258
132
    uint32_t frame_len;
259
132
    if (frame->len == -1) {
260
0
        frame_len = p->payload_len - frame->offset;
261
132
    } else {
262
132
        frame_len = (uint32_t)frame->len;
263
132
    }
264
132
    if (frame->offset + frame_len > p->payload_len) {
265
0
        frame_len = p->payload_len - frame->offset;
266
132
    } else {
267
132
        ci_flags |= DETECT_CI_FLAGS_END;
268
132
    }
269
132
    const uint8_t *data = p->payload + frame->offset;
270
132
    const uint32_t data_len = frame_len;
271
272
132
    SCLogDebug("packet %" PRIu64 " -> frame %p/%" PRIi64 "/%s offset %" PRIu64
273
132
               " type %u len %" PRIi64,
274
132
            p->pcap_cnt, frame, frame->id,
275
132
            AppLayerParserGetFrameNameById(p->flow->proto, p->flow->alproto, frame->type),
276
132
            frame->offset, frame->type, frame->len);
277
278
132
    InspectionBufferSetupMulti(buffer, transforms, data, data_len);
279
132
    buffer->inspect_offset = 0;
280
132
    buffer->flags = ci_flags;
281
132
}
282
283
/** \internal
284
 *  \brief setup buffer based on frame in UDP payload
285
 */
286
static int DetectFrameInspectUdp(DetectEngineThreadCtx *det_ctx,
287
        const DetectEngineFrameInspectionEngine *engine, const Signature *s,
288
        const DetectEngineTransforms *transforms, Packet *p, const Frames *_frames,
289
        const Frame *frame, const int list_id)
290
4
{
291
4
    SCLogDebug("packet:%" PRIu64 ", inspect: s:%p s->id:%u, transforms:%p", p->pcap_cnt, s, s->id,
292
4
            transforms);
293
294
    // TODO can we use single here? Could it conflict with TCP?
295
4
    InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, 0);
296
4
    if (buffer == NULL)
297
0
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
298
299
4
    DEBUG_VALIDATE_BUG_ON(frame->offset >= p->payload_len);
300
4
    if (frame->offset >= p->payload_len)
301
0
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
302
303
4
    if (!buffer->initialized)
304
0
        BufferSetupUdp(buffer, frame, p, transforms);
305
4
    DEBUG_VALIDATE_BUG_ON(!buffer->initialized);
306
4
    if (buffer->inspect == NULL)
307
0
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
308
309
4
    const uint32_t data_len = buffer->inspect_len;
310
4
    const uint8_t *data = buffer->inspect;
311
312
    // PrintRawDataFp(stdout, data, data_len);
313
314
4
    int r = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx, s, engine->smd, p, p->flow,
315
4
            (uint8_t *)data, data_len, 0, buffer->flags,
316
4
            DETECT_ENGINE_CONTENT_INSPECTION_MODE_FRAME);
317
4
    if (r == 1) {
318
4
        SCLogDebug("match!");
319
4
        return DETECT_ENGINE_INSPECT_SIG_MATCH;
320
4
    } else {
321
0
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
322
0
    }
323
4
}
324
325
/**
326
 *  \retval bool true if callback should run again */
327
static bool BufferSetup(struct FrameStreamData *fsd, InspectionBuffer *buffer, const uint8_t *input,
328
        const uint32_t input_len, const uint64_t input_offset)
329
102
{
330
102
    const Frame *frame = fsd->frame;
331
    /* so: relative to start of stream */
332
102
    const uint64_t so_input_re = input_offset + input_len;
333
102
    const uint64_t so_frame_re =
334
102
            frame->offset + (uint64_t)frame->len; // TODO if eof, set to available data?
335
102
    SCLogDebug("frame offset:%" PRIu64, frame->offset);
336
102
    const uint8_t *data = input;
337
102
    uint8_t ci_flags = 0;
338
102
    uint32_t data_len;
339
340
    /* fo: frame offset; offset relative to start of the frame */
341
102
    uint64_t fo_inspect_offset = 0;
342
343
102
    if (frame->offset == 0 && input_offset == 0) {
344
7
        ci_flags |= DETECT_CI_FLAGS_START;
345
7
        SCLogDebug("have frame data start");
346
347
7
        if (frame->len >= 0) {
348
4
            data_len = MIN(input_len, frame->len);
349
4
            if (data_len == frame->len) {
350
4
                ci_flags |= DETECT_CI_FLAGS_END;
351
4
                SCLogDebug("have frame data end");
352
4
            }
353
4
        } else {
354
3
            data_len = input_len;
355
3
        }
356
95
    } else {
357
95
        const uint64_t so_frame_inspect_offset = frame->inspect_progress + frame->offset;
358
95
        const uint64_t so_inspect_offset = MAX(input_offset, so_frame_inspect_offset);
359
95
        fo_inspect_offset = so_inspect_offset - frame->offset;
360
361
95
        if (frame->offset >= input_offset) {
362
86
            ci_flags |= DETECT_CI_FLAGS_START;
363
86
            SCLogDebug("have frame data start");
364
86
        }
365
95
        if (frame->len >= 0) {
366
85
            if (fo_inspect_offset >= (uint64_t)frame->len) {
367
0
                SCLogDebug("data entirely past frame (%" PRIu64 " > %" PRIi64 ")",
368
0
                        fo_inspect_offset, frame->len);
369
0
                InspectionBufferSetupMultiEmpty(buffer);
370
0
                return false;
371
0
            }
372
373
            /* in: relative to start of input data */
374
85
            BUG_ON(so_inspect_offset < input_offset);
375
85
            const uint32_t in_data_offset = so_inspect_offset - input_offset;
376
85
            data += in_data_offset;
377
378
85
            uint32_t in_data_excess = 0;
379
85
            if (so_input_re >= so_frame_re) {
380
85
                ci_flags |= DETECT_CI_FLAGS_END;
381
85
                SCLogDebug("have frame data end");
382
85
                in_data_excess = so_input_re - so_frame_re;
383
85
            }
384
85
            data_len = input_len - in_data_offset - in_data_excess;
385
85
        } else {
386
            /* in: relative to start of input data */
387
10
            BUG_ON(so_inspect_offset < input_offset);
388
10
            const uint32_t in_data_offset = so_inspect_offset - input_offset;
389
10
            data += in_data_offset;
390
10
            data_len = input_len - in_data_offset;
391
10
        }
392
95
    }
393
    // PrintRawDataFp(stdout, data, data_len);
394
102
    SCLogDebug("fsd->transforms %p", fsd->transforms);
395
102
    InspectionBufferSetupMulti(buffer, fsd->transforms, data, data_len);
396
102
    SCLogDebug("inspect_offset %" PRIu64, fo_inspect_offset);
397
102
    buffer->inspect_offset = fo_inspect_offset;
398
102
    buffer->flags = ci_flags;
399
400
102
    if (frame->len >= 0 && so_input_re >= so_frame_re) {
401
89
        SCLogDebug("have the full frame, we can set progress accordingly (%" PRIu64 " > %" PRIu64
402
89
                   ")",
403
89
                so_input_re, so_frame_re);
404
89
        fsd->det_ctx->frame_inspect_progress =
405
89
                MAX(fo_inspect_offset + data_len, fsd->det_ctx->frame_inspect_progress);
406
89
    } else {
407
13
        fsd->det_ctx->frame_inspect_progress =
408
13
                MAX(fo_inspect_offset + data_len, fsd->det_ctx->frame_inspect_progress);
409
        /* in IPS mode keep a sliding window */
410
13
        const bool ips = StreamTcpInlineMode();
411
13
        if (ips) {
412
0
            if (fsd->det_ctx->frame_inspect_progress < 2500)
413
0
                fsd->det_ctx->frame_inspect_progress = 0;
414
0
            else
415
0
                fsd->det_ctx->frame_inspect_progress -= 2500;
416
0
        }
417
13
        SCLogDebug("ips %s inspect_progress %" PRIu64, BOOL2STR(ips),
418
13
                fsd->det_ctx->frame_inspect_progress);
419
13
    }
420
421
    /* keep going as long as there is possibly still data for this frame */
422
102
    const bool ret = (frame->len >= 0 && so_input_re >= so_frame_re);
423
102
    SCLogDebug("buffer set up, more to do: %s", BOOL2STR(ret));
424
102
    return ret;
425
102
}
426
427
static int FrameStreamDataInspectFunc(
428
        void *cb_data, const uint8_t *input, const uint32_t input_len, const uint64_t input_offset)
429
9
{
430
9
    struct FrameStreamData *fsd = cb_data;
431
9
    SCLogDebug("inspect: fsd %p { det_ctx:%p, transforms:%p, s:%p, s->id:%u, frame:%p, list_id:%d, "
432
9
               "idx:%u, "
433
9
               "requested_stream_offset:%" PRIu64
434
9
               "}, input: %p, input_len:%u, input_offset:%" PRIu64,
435
9
            fsd, fsd->det_ctx, fsd->transforms, fsd->s, fsd->s->id, fsd->frame, fsd->list_id,
436
9
            fsd->idx, fsd->requested_stream_offset, input, input_len, input_offset);
437
    // PrintRawDataFp(stdout, input, input_len);
438
439
9
    InspectionBuffer *buffer =
440
9
            InspectionBufferMultipleForListGet(fsd->det_ctx, fsd->list_id, fsd->idx++);
441
9
    if (buffer == NULL) {
442
0
        return 0;
443
0
    }
444
9
    SCLogDebug("buffer %p idx %u", buffer, fsd->idx);
445
446
    /* if we've not done so already, set up the buffer */
447
9
    int more_chunks = 1;
448
9
    if (!buffer->initialized) {
449
9
        more_chunks = BufferSetup(fsd, buffer, input, input_len, input_offset);
450
9
    }
451
9
    DEBUG_VALIDATE_BUG_ON(!buffer->initialized);
452
9
    if (buffer->inspect == NULL) {
453
0
        return more_chunks;
454
0
    }
455
456
9
    const uint32_t data_len = buffer->inspect_len;
457
9
    const uint8_t *data = buffer->inspect;
458
9
    const uint64_t data_offset = buffer->inspect_offset;
459
9
    DetectEngineThreadCtx *det_ctx = fsd->det_ctx;
460
9
    det_ctx->discontinue_matching = 0;
461
9
    det_ctx->buffer_offset = 0;
462
9
    det_ctx->inspection_recursion_counter = 0;
463
464
9
    const DetectEngineFrameInspectionEngine *engine = fsd->inspect_engine;
465
9
    const Signature *s = fsd->s;
466
9
    Packet *p = fsd->p;
467
468
#ifdef DEBUG
469
    const uint8_t ci_flags = buffer->flags;
470
    SCLogDebug("frame %p offset %" PRIu64 " type %u len %" PRIi64
471
               " ci_flags %02x (start:%s, end:%s)",
472
            fsd->frame, fsd->frame->offset, fsd->frame->type, fsd->frame->len, ci_flags,
473
            (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false",
474
            (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false");
475
    SCLogDebug("buffer %p offset %" PRIu64 " len %u ci_flags %02x (start:%s, end:%s)", buffer,
476
            buffer->inspect_offset, buffer->inspect_len, ci_flags,
477
            (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false",
478
            (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false");
479
    // PrintRawDataFp(stdout, data, data_len);
480
    // PrintRawDataFp(stdout, data, MIN(64, data_len));
481
#endif
482
9
    BUG_ON(fsd->frame->len > 0 && (int64_t)data_len > fsd->frame->len);
483
484
9
    int r = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx, s, engine->smd, p, p->flow,
485
9
            (uint8_t *)data, data_len, data_offset, buffer->flags,
486
9
            DETECT_ENGINE_CONTENT_INSPECTION_MODE_FRAME);
487
9
    if (r == 1) {
488
9
        SCLogDebug("DETECT_ENGINE_INSPECT_SIG_MATCH");
489
9
        fsd->inspect_result = DETECT_ENGINE_INSPECT_SIG_MATCH;
490
9
    } else {
491
0
        SCLogDebug("DETECT_ENGINE_INSPECT_SIG_NO_MATCH");
492
0
    }
493
9
    return more_chunks;
494
9
}
495
496
static bool SetupStreamCallbackData(struct FrameStreamData *dst, const TcpSession *ssn,
497
        const TcpStream *stream, DetectEngineThreadCtx *det_ctx,
498
        const DetectEngineTransforms *transforms, const Frames *_frames, const Frame *frame,
499
        const int list_id, const bool eof)
500
2.18M
{
501
2.18M
    SCLogDebug("frame %" PRIi64 ", len %" PRIi64 ", offset %" PRIu64 ", inspect_progress %" PRIu64,
502
2.18M
            frame->id, frame->len, frame->offset, frame->inspect_progress);
503
504
2.18M
    const uint64_t frame_offset = frame->offset;
505
2.18M
    const uint64_t usable = StreamDataRightEdge(stream, eof);
506
2.18M
    if (usable <= frame_offset)
507
12
        return false;
508
509
2.18M
    uint64_t want = frame->inspect_progress;
510
2.18M
    if (frame->len == -1) {
511
82.8k
        if (eof) {
512
353
            want = usable;
513
82.5k
        } else {
514
82.5k
            want += 2500;
515
82.5k
        }
516
2.10M
    } else {
517
        /* don't have the full frame yet */
518
2.10M
        if (frame->offset + frame->len > usable) {
519
19.1k
            want += 2500;
520
2.08M
        } else {
521
2.08M
            want = frame->offset + frame->len;
522
2.08M
        }
523
2.10M
    }
524
525
2.18M
    const bool ips = StreamTcpInlineMode();
526
527
2.18M
    const uint64_t have = usable;
528
2.18M
    if (!ips && have < want) {
529
60.9k
        SCLogDebug("wanted %" PRIu64 " bytes, got %" PRIu64, want, have);
530
60.9k
        return false;
531
60.9k
    }
532
533
2.12M
    const uint64_t available_data = usable - STREAM_BASE_OFFSET(stream);
534
2.12M
    SCLogDebug("check inspection for having 2500 bytes: %" PRIu64, available_data);
535
2.12M
    if (!ips && !eof && available_data < 2500 &&
536
2.12M
            (frame->len < 0 || frame->len > (int64_t)available_data)) {
537
0
        SCLogDebug("skip inspection until we have 2500 bytes (have %" PRIu64 ")", available_data);
538
0
        return false;
539
0
    }
540
541
2.12M
    const uint64_t offset =
542
2.12M
            MAX(STREAM_BASE_OFFSET(stream), frame->offset + frame->inspect_progress);
543
544
2.12M
    dst->det_ctx = det_ctx;
545
2.12M
    dst->transforms = transforms;
546
2.12M
    dst->frame = frame;
547
2.12M
    dst->list_id = list_id;
548
2.12M
    dst->requested_stream_offset = offset;
549
2.12M
    return true;
550
2.12M
}
551
552
/**
553
 * \brief Do the content inspection & validation for a signature
554
 *
555
 * \param de_ctx Detection engine context
556
 * \param det_ctx Detection engine thread context
557
 * \param s Signature to inspect
558
 * \param p Packet
559
 * \param frame stream frame to inspect
560
 *
561
 * \retval 0 no match.
562
 * \retval 1 match.
563
 */
564
int DetectEngineInspectFrameBufferGeneric(DetectEngineThreadCtx *det_ctx,
565
        const DetectEngineFrameInspectionEngine *engine, const Signature *s, Packet *p,
566
        const Frames *frames, const Frame *frame)
567
64
{
568
    /* if prefilter didn't already run, we need to consider transformations */
569
64
    const DetectEngineTransforms *transforms = NULL;
570
64
    if (!engine->mpm) {
571
60
        transforms = engine->v1.transforms;
572
60
    }
573
64
    const int list_id = engine->sm_list;
574
64
    SCLogDebug("running inspect on %d", list_id);
575
576
64
    if (p->proto == IPPROTO_UDP) {
577
4
        return DetectFrameInspectUdp(det_ctx, engine, s, transforms, p, frames, frame, list_id);
578
4
    }
579
60
    DEBUG_VALIDATE_BUG_ON(p->proto != IPPROTO_TCP);
580
581
60
    SCLogDebug("packet:%" PRIu64 ", frame->id:%" PRIu64
582
60
               ", list:%d, transforms:%p, s:%p, s->id:%u, engine:%p",
583
60
            p->pcap_cnt, frame->id, engine->sm_list, engine->v1.transforms, s, s->id, engine);
584
585
60
    BUG_ON(p->flow->protoctx == NULL);
586
60
    TcpSession *ssn = p->flow->protoctx;
587
60
    TcpStream *stream;
588
60
    if (PKT_IS_TOSERVER(p)) {
589
0
        stream = &ssn->client;
590
60
    } else {
591
60
        stream = &ssn->server;
592
60
    }
593
60
    const bool eof = ssn->state == TCP_CLOSED || PKT_IS_PSEUDOPKT(p);
594
595
60
    struct FrameStreamData fsd;
596
60
    memset(&fsd, 0, sizeof(fsd));
597
60
    fsd.inspect_engine = engine;
598
60
    fsd.s = s;
599
60
    fsd.inspect_result = DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
600
60
    fsd.p = p;
601
602
60
    if (SetupStreamCallbackData(
603
60
                &fsd, ssn, stream, det_ctx, transforms, frames, frame, list_id, eof) == false) {
604
50
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
605
50
    }
606
10
    StreamReassembleForFrame(
607
10
            ssn, stream, FrameStreamDataInspectFunc, &fsd, fsd.requested_stream_offset, eof);
608
609
10
    return fsd.inspect_result;
610
60
}