• 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

78.02
/src/detect.c
1
/* Copyright (C) 2007-2025 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
 * Basic detection engine
24
 */
25

26
#include "suricata-common.h"
27
#include "suricata.h"
28

29
#include "decode.h"
30
#include "packet.h"
31
#include "flow.h"
32
#include "stream-tcp.h"
33
#include "app-layer.h"
34
#include "app-layer-parser.h"
35
#include "app-layer-frames.h"
36

37
#include "detect.h"
38
#include "detect-dsize.h"
39
#include "detect-engine.h"
40
#include "detect-engine-build.h"
41
#include "detect-engine-frame.h"
42
#include "detect-engine-profile.h"
43

44
#include "detect-engine-alert.h"
45
#include "detect-engine-siggroup.h"
46
#include "detect-engine-address.h"
47
#include "detect-engine-proto.h"
48
#include "detect-engine-port.h"
49
#include "detect-engine-mpm.h"
50
#include "detect-engine-iponly.h"
51
#include "detect-engine-threshold.h"
52
#include "detect-engine-prefilter.h"
53
#include "detect-engine-state.h"
54
#include "detect-engine-analyzer.h"
55

56
#include "detect-engine-payload.h"
57
#include "detect-engine-event.h"
58

59
#include "detect-filestore.h"
60
#include "detect-flowvar.h"
61
#include "detect-replace.h"
62

63
#include "util-validate.h"
64
#include "util-detect.h"
65
#include "util-profiling.h"
66

67
#include "action-globals.h"
68

69
typedef struct DetectRunScratchpad {
70
    const AppProto alproto;
71
    const uint8_t flow_flags; /* flow/state flags: STREAM_* */
72
    const bool app_decoder_events;
73
    /**
74
     *  Either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
75
     *
76
     *  ACTION_DROP means the default policy of drop:packet is applied
77
     *  ACTION_ACCEPT means the default policy of accept:hook is applied
78
     */
79
    const uint8_t default_action;
80
    const SigGroupHead *sgh;
81
} DetectRunScratchpad;
82

83
/* prototypes */
84
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
85
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
86
        const uint8_t default_action);
87
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
88
        DetectEngineThreadCtx *det_ctx, Flow * const pflow, Packet * const p);
89
static inline void DetectRunGetRuleGroup(const DetectEngineCtx *de_ctx,
90
        Packet * const p, Flow * const pflow, DetectRunScratchpad *scratch);
91
static inline void DetectRunPrefilterPkt(ThreadVars *tv, const DetectEngineCtx *de_ctx,
92
        DetectEngineThreadCtx *det_ctx, Packet *p, DetectRunScratchpad *scratch);
93
static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
94
        const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
95
        Flow *const pflow, const DetectRunScratchpad *scratch);
96
static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx,
97
        DetectEngineThreadCtx *det_ctx, Packet *p,
98
        Flow *f, DetectRunScratchpad *scratch);
99
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
100
        Packet *p, Flow *f, DetectRunScratchpad *scratch);
101
static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_ctx,
102
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
103
        DetectRunScratchpad *scratch);
104
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
105
        Packet *p, Flow * const pflow);
106
static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p);
107

108
/** \internal
109
 */
110
static void DetectRun(ThreadVars *th_v,
111
        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
112
        Packet *p)
113
{
1,440,170✔
114
    SCEnter();
1,440,170✔
115
    SCLogDebug("pcap_cnt %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
1,440,170✔
116
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
1,440,170✔
117
                    : "noflow",
1,440,170✔
118
            PktSrcToString(p->pkt_src));
1,440,170✔
119

120
    /* Load the Packet's flow early, even though it might not be needed.
121
     * Mark as a constant pointer, although the flow itself can change. */
122
    Flow * const pflow = p->flow;
1,440,170✔
123

124
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_DROP);
1,440,170✔
125

126
    /* run the IPonly engine */
127
    DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
1,440,170✔
128

129
    /* get our rule group */
130
    DetectRunGetRuleGroup(de_ctx, p, pflow, &scratch);
1,440,170✔
131
    /* if we didn't get a sig group head, we
132
     * have nothing to do.... */
133
    if (scratch.sgh == NULL) {
1,440,170✔
134
        SCLogDebug("no sgh for this packet, nothing to match against");
610,221✔
135
        goto end;
610,221✔
136
    }
610,221✔
137

138
    /* run the prefilters for packets */
139
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
829,949✔
140

141
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
829,949✔
142
    /* inspect the rules against the packet */
143
    const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
829,949✔
144
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
829,949✔
145

146
    /* Only FW rules will already have set the action, IDS rules go through PacketAlertFinalize
147
     *
148
     * If rules told us to drop or accept:packet/accept:flow, we skip app_filter and app_td.
149
     *
150
     * accept:hook won't have set the pkt_policy, so we simply continue.
151
     *
152
     * TODO what about app state progression, cleanup and such? */
153
    if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
829,949✔
UNCOV
154
        goto end;
×
UNCOV
155
    }
×
156

157
    /* run tx/state inspection. Don't call for ICMP error msgs. */
158
    if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) {
829,949✔
159
        if (p->proto == IPPROTO_TCP) {
636,923✔
160
            if ((p->flags & PKT_STREAM_EST) == 0) {
610,145✔
161
                SCLogDebug("packet %" PRIu64 ": skip tcp non-established", PcapPacketCntGet(p));
7,126✔
162
                DetectRunAppendDefaultAccept(det_ctx, p);
7,126✔
163
                goto end;
7,126✔
164
            }
7,126✔
165
            const TcpSession *ssn = p->flow->protoctx;
603,019✔
166
            bool setting_nopayload = p->flow->alparser &&
603,019✔
167
                                     SCAppLayerParserStateIssetFlag(
603,019✔
168
                                             p->flow->alparser, APP_LAYER_PARSER_NO_INSPECTION) &&
603,019✔
169
                                     !(p->flags & PKT_NOPAYLOAD_INSPECTION);
603,019✔
170
            // we may be right after disabling app-layer (ssh)
171
            if (ssn &&
603,019✔
172
                    ((ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0 || setting_nopayload)) {
603,019✔
173
                // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
174
                DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
547,593✔
175
                // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
176
            }
547,593✔
177
            // no update to transactions
178
            if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 &&
603,019✔
179
                    ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) ||
603,019✔
180
                            (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) {
286,473✔
181
                SCLogDebug("packet %" PRIu64 ": no app-layer update", PcapPacketCntGet(p));
145,561✔
182
                DetectRunAppendDefaultAccept(det_ctx, p);
145,561✔
183
                goto end;
145,561✔
184
            }
145,561✔
185
        } else if (p->proto == IPPROTO_UDP) {
603,019✔
186
            DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch);
26,778✔
187
        }
26,778✔
188

189
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX);
484,236✔
190
        DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch);
484,236✔
191
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX);
484,236✔
192
        /* see if we need to increment the inspect_id and reset the de_state */
193
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX_UPDATE);
484,236✔
194
        AppLayerParserSetTransactionInspectId(
484,236✔
195
                pflow, pflow->alparser, pflow->alstate, scratch.flow_flags, (scratch.sgh == NULL));
484,236✔
196
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX_UPDATE);
484,236✔
197
    } else {
484,236✔
198
        SCLogDebug("packet %" PRIu64 ": no flow / app-layer", PcapPacketCntGet(p));
193,026✔
199
        DetectRunAppendDefaultAccept(det_ctx, p);
193,026✔
200
    }
193,026✔
201

202
end:
1,440,170✔
203
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
1,440,170✔
204

205
    DetectRunCleanup(det_ctx, p, pflow);
1,440,170✔
206
    SCReturn;
1,440,170✔
207
}
829,949✔
208

209
/** \internal
210
 */
211
static void DetectRunPacketHook(ThreadVars *th_v, const DetectEngineCtx *de_ctx,
212
        DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p)
UNCOV
213
{
×
UNCOV
214
    SCEnter();
×
UNCOV
215
    SCLogDebug("pcap_cnt %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
×
UNCOV
216
            p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient")
×
UNCOV
217
                    : "noflow",
×
UNCOV
218
            PktSrcToString(p->pkt_src));
×
219

220
    /* Load the Packet's flow early, even though it might not be needed.
221
     * Mark as a constant pointer, although the flow itself can change. */
UNCOV
222
    Flow *const pflow = p->flow;
×
223

UNCOV
224
    DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_ACCEPT);
×
UNCOV
225
    scratch.sgh = sgh;
×
226

227
    /* if we didn't get a sig group head, we
228
     * have nothing to do.... */
UNCOV
229
    if (scratch.sgh == NULL) {
×
UNCOV
230
        SCLogDebug("no sgh for this packet, nothing to match against");
×
UNCOV
231
        goto end;
×
UNCOV
232
    }
×
233

234
    /* run the prefilters for packets */
UNCOV
235
    DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
×
236

237
    //    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES); // TODO
238
    /* inspect the rules against the packet */
UNCOV
239
    const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
×
240
    //    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
UNCOV
241
    if (pkt_policy & (ACTION_DROP | ACTION_ACCEPT)) {
×
UNCOV
242
        goto end;
×
UNCOV
243
    }
×
244

UNCOV
245
end:
×
UNCOV
246
    DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
×
247

UNCOV
248
    DetectRunCleanup(det_ctx, p, pflow);
×
UNCOV
249
    SCReturn;
×
UNCOV
250
}
×
251

252
static void DetectRunPostMatch(ThreadVars *tv,
253
                               DetectEngineThreadCtx *det_ctx, Packet *p,
254
                               const Signature *s)
255
{
105,035✔
256
    /* run the packet match functions */
257
    const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH];
105,035✔
258
    if (smd != NULL) {
105,035✔
259
        KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH);
9,841✔
260

261
        SCLogDebug("running match functions, sm %p", smd);
9,841✔
262

263
        while (1) {
11,060✔
264
            KEYWORD_PROFILING_START;
11,060✔
265
            (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx);
11,060✔
266
            KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
11,060✔
267
            if (smd->is_last)
11,060✔
268
                break;
9,841✔
269
            smd++;
1,219✔
270
        }
1,219✔
271
    }
9,841✔
272
}
105,035✔
273

274
/**
275
 *  \brief Get the SigGroupHead for a packet.
276
 *
277
 *  \param de_ctx detection engine context
278
 *  \param p packet
279
 *
280
 *  \retval sgh the SigGroupHead or NULL if non applies to the packet
281
 */
282
const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx,
283
        const Packet *p)
284
{
276,734✔
285
    SCEnter();
276,734✔
286
    SigGroupHead *sgh = NULL;
276,734✔
287

288
    /* if the packet proto is 0 (not set), we're inspecting it against
289
     * the decoder events sgh we have. */
290
    if (p->proto == 0 && p->events.cnt > 0) {
276,734✔
291
        SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead");
147,457✔
292
    } else if (p->proto == 0) {
147,457✔
293
        if (!(PacketIsIPv4(p) || PacketIsIPv6(p))) {
13,710✔
294
            /* not IP, so nothing to do */
295
            SCReturnPtr(NULL, "SigGroupHead");
8,962✔
296
        }
8,962✔
297
    }
13,710✔
298

299
    /* select the flow_gh */
300
    const int dir = (p->flowflags & FLOW_PKT_TOCLIENT) == 0;
120,315✔
301

302
    int proto = PacketGetIPProto(p);
120,315✔
303
    if (proto == IPPROTO_TCP) {
120,315✔
304
        DetectPort *list = de_ctx->flow_gh[dir].tcp;
77,745✔
305
        SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p", de_ctx->flow_gh[1].tcp,
77,745✔
306
                de_ctx->flow_gh[0].tcp, de_ctx->flow_gh[dir].tcp);
77,745✔
307
        const uint16_t port = dir ? p->dp : p->sp;
77,745✔
308
        SCLogDebug("tcp port %u -> %u:%u", port, p->sp, p->dp);
77,745✔
309
        DetectPort *sghport = DetectPortLookupGroup(list, port);
77,745✔
310
        if (sghport != NULL)
77,745✔
311
            sgh = sghport->sh;
45,189✔
312
        SCLogDebug("TCP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
77,745✔
313
                dir ? "toserver" : "toclient", sghport, sgh);
77,745✔
314
    } else if (proto == IPPROTO_UDP) {
77,745✔
315
        DetectPort *list = de_ctx->flow_gh[dir].udp;
24,864✔
316
        uint16_t port = dir ? p->dp : p->sp;
24,864✔
317
        DetectPort *sghport = DetectPortLookupGroup(list, port);
24,864✔
318
        if (sghport != NULL)
24,864✔
319
            sgh = sghport->sh;
12,151✔
320
        SCLogDebug("UDP list %p, port %u, direction %s, sghport %p, sgh %p", list, port,
24,864✔
321
                dir ? "toserver" : "toclient", sghport, sgh);
24,864✔
322
    } else {
24,864✔
323
        sgh = de_ctx->flow_gh[dir].sgh[proto];
17,706✔
324
    }
17,706✔
325

326
    SCReturnPtr(sgh, "SigGroupHead");
120,315✔
327
}
276,734✔
328

329
static inline void DetectPrefilterCopyDeDup(
330
        const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
331
{
167,486✔
332
    SigIntId *pf_ptr = det_ctx->pmq.rule_id_array;
167,486✔
333
    uint32_t final_cnt = det_ctx->pmq.rule_id_array_cnt;
167,486✔
334
    Signature **sig_array = de_ctx->sig_array;
167,486✔
335
    Signature **match_array = det_ctx->match_array;
167,486✔
336
    SigIntId previous_id = (SigIntId)-1;
167,486✔
337
    while (final_cnt-- > 0) {
649,802✔
338
        SigIntId id = *pf_ptr++;
482,316✔
339
        Signature *s = sig_array[id];
482,316✔
340

341
        /* As the prefilter list can contain duplicates, check for that here. */
342
        if (likely(id != previous_id)) {
482,316✔
343
            *match_array++ = s;
481,996✔
344
            previous_id = id;
481,996✔
345
        }
481,996✔
346
    }
482,316✔
347

348
    det_ctx->match_array_cnt = (uint32_t)(match_array - det_ctx->match_array);
167,486✔
349
    DEBUG_VALIDATE_BUG_ON(det_ctx->pmq.rule_id_array_cnt < det_ctx->match_array_cnt);
167,486✔
350
    PMQ_RESET(&det_ctx->pmq);
167,486✔
351
}
167,486✔
352

353
/** \internal
354
 *  \brief update flow's file tracking flags based on the detection engine
355
 *         A set of flags is prepared that is sent to the File API. The
356
           File API may reject one or more based on the global force settings.
357
 */
358
static inline void
359
DetectPostInspectFileFlagsUpdate(Flow *f, const SigGroupHead *sgh, uint8_t direction)
360
{
101,055✔
361
    uint16_t flow_file_flags = FLOWFILE_INIT;
101,055✔
362

363
    if (sgh == NULL) {
101,055✔
364
        SCLogDebug("requesting disabling all file features for flow");
45,613✔
365
        flow_file_flags = FLOWFILE_NONE;
45,613✔
366
    } else {
55,442✔
367
        if (sgh->filestore_cnt == 0) {
55,442✔
368
            SCLogDebug("requesting disabling filestore for flow");
48,939✔
369
            flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC);
48,939✔
370
        }
48,939✔
371
#ifdef HAVE_MAGIC
55,442✔
372
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) {
55,442✔
373
            SCLogDebug("requesting disabling magic for flow");
51,844✔
374
            flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC);
51,844✔
375
        }
51,844✔
376
#endif
55,442✔
377
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) {
55,442✔
378
            SCLogDebug("requesting disabling md5 for flow");
55,442✔
379
            flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC);
55,442✔
380
        }
55,442✔
381
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) {
55,442✔
382
            SCLogDebug("requesting disabling sha1 for flow");
55,442✔
383
            flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC);
55,442✔
384
        }
55,442✔
385
        if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) {
55,442✔
386
            SCLogDebug("requesting disabling sha256 for flow");
55,442✔
387
            flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC);
55,442✔
388
        }
55,442✔
389
    }
55,442✔
390
    if (flow_file_flags != 0) {
101,055✔
391
        FileUpdateFlowFileFlags(f, flow_file_flags, direction);
101,055✔
392
    }
101,055✔
393
}
101,055✔
394

395
static inline void
396
DetectRunPostGetFirstRuleGroup(const Packet *p, Flow *pflow, const SigGroupHead *sgh)
397
{
101,055✔
398
    if ((p->flowflags & FLOW_PKT_TOSERVER) && !(pflow->flags & FLOW_SGH_TOSERVER)) {
101,055✔
399
        /* first time we see this toserver sgh, store it */
400
        pflow->sgh_toserver = sgh;
63,887✔
401
        pflow->flags |= FLOW_SGH_TOSERVER;
63,887✔
402

403
        if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
63,887✔
404
            if (pflow->protoctx != NULL) {
36,688✔
405
                TcpSession *ssn = pflow->protoctx;
34,893✔
406
                SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.client");
34,893✔
407
                ssn->client.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW;
34,893✔
408
            }
34,893✔
409
        }
36,688✔
410

411
        DetectPostInspectFileFlagsUpdate(pflow,
63,887✔
412
                pflow->sgh_toserver, STREAM_TOSERVER);
63,887✔
413

414
    } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) {
63,887✔
415
        pflow->sgh_toclient = sgh;
37,168✔
416
        pflow->flags |= FLOW_SGH_TOCLIENT;
37,168✔
417

418
        if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) {
37,168✔
419
            if (pflow->protoctx != NULL) {
26,858✔
420
                TcpSession *ssn = pflow->protoctx;
26,746✔
421
                SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.server");
26,746✔
422
                ssn->server.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW;
26,746✔
423
            }
26,746✔
424
        }
26,858✔
425

426
        DetectPostInspectFileFlagsUpdate(pflow,
37,168✔
427
                pflow->sgh_toclient, STREAM_TOCLIENT);
37,168✔
428
    }
37,168✔
429
}
101,055✔
430

431
static inline void DetectRunGetRuleGroup(
432
    const DetectEngineCtx *de_ctx,
433
    Packet * const p, Flow * const pflow,
434
    DetectRunScratchpad *scratch)
435
{
1,440,170✔
436
    const SigGroupHead *sgh = NULL;
1,440,170✔
437

438
    if (pflow) {
1,440,170✔
439
        bool use_flow_sgh = false;
1,264,602✔
440
        /* Get the stored sgh from the flow (if any). Make sure we're not using
441
         * the sgh for icmp error packets part of the same stream. */
442
        if (PacketGetIPProto(p) == pflow->proto) { /* filter out icmp */
1,264,602✔
443
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
1,264,491✔
444
            if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) {
1,264,491✔
445
                sgh = pflow->sgh_toserver;
636,175✔
446
                SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh);
636,175✔
447
                use_flow_sgh = true;
636,175✔
448
            } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) {
636,175✔
449
                sgh = pflow->sgh_toclient;
527,261✔
450
                SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh);
527,261✔
451
                use_flow_sgh = true;
527,261✔
452
            }
527,261✔
453
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
1,264,491✔
454
        }
1,264,491✔
455

456
        if (!(use_flow_sgh)) {
1,264,602✔
457
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
101,166✔
458
            sgh = SigMatchSignaturesGetSgh(de_ctx, p);
101,166✔
459
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
101,166✔
460

461
            /* HACK: prevent the wrong sgh (or NULL) from being stored in the
462
             * flow's sgh pointers */
463
            if (PacketIsICMPv4(p) && ICMPV4_DEST_UNREACH_IS_VALID(p)) {
101,166✔
464
                ; /* no-op */
111✔
465
            } else {
101,055✔
466
                /* store the found sgh (or NULL) in the flow to save us
467
                 * from looking it up again for the next packet.
468
                 * Also run other tasks */
469
                DetectRunPostGetFirstRuleGroup(p, pflow, sgh);
101,055✔
470
            }
101,055✔
471
        }
101,166✔
472
    } else { /* p->flags & PKT_HAS_FLOW */
1,264,602✔
473
        /* no flow */
474

475
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH);
175,568✔
476
        sgh = SigMatchSignaturesGetSgh(de_ctx, p);
175,568✔
477
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH);
175,568✔
478
    }
175,568✔
479

480
    scratch->sgh = sgh;
1,440,170✔
481
}
1,440,170✔
482

483
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
484
        DetectEngineThreadCtx *det_ctx,
485
        Flow * const pflow, Packet * const p)
486
{
1,440,170✔
487
    if (pflow) {
1,440,170✔
488
        if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
1,264,602✔
489
            SCLogDebug("testing against \"ip-only\" signatures");
98,697✔
490

491
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
98,697✔
492
            IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
98,697✔
493
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
98,697✔
494
        }
98,697✔
495
    } else { /* p->flags & PKT_HAS_FLOW */
1,264,602✔
496
        /* no flow */
497

498
        /* Even without flow we should match the packet src/dst */
499
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY);
175,568✔
500
        IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p);
175,568✔
501
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY);
175,568✔
502
    }
175,568✔
503
}
1,440,170✔
504

505
/** \internal
506
 *  \brief inspect the rule header: protocol, ports, etc
507
 *  \retval bool false if no match, true if match */
508
static inline bool DetectRunInspectRuleHeader(const Packet *p, const Flow *f, const Signature *s,
509
        const uint32_t sflags, const uint8_t s_proto_flags)
510
{
524,312✔
511
    /* check if this signature has a requirement for flowvars of some type
512
     * and if so, if we actually have any in the flow. If not, the sig
513
     * can't match and we skip it. */
514
    if ((p->flags & PKT_HAS_FLOW) && (sflags & SIG_FLAG_REQUIRE_FLOWVAR)) {
524,312✔
515
        DEBUG_VALIDATE_BUG_ON(f == NULL);
16,626✔
516

517
        /* no flowvars? skip this sig */
518
        const bool fv = f->flowvar != NULL;
16,626✔
519
        if (!fv) {
16,626✔
520
            SCLogDebug("skipping sig as the flow has no flowvars and sig "
7,278✔
521
                    "has SIG_FLAG_REQUIRE_FLOWVAR flag set.");
7,278✔
522
            return false;
7,278✔
523
        }
7,278✔
524
    }
16,626✔
525

526
    if ((s_proto_flags & DETECT_PROTO_IPV4) && !PacketIsIPv4(p)) {
517,034✔
527
        SCLogDebug("ip version didn't match");
90✔
528
        return false;
90✔
529
    }
90✔
530
    if ((s_proto_flags & DETECT_PROTO_IPV6) && !PacketIsIPv6(p)) {
516,944✔
531
        SCLogDebug("ip version didn't match");
2,759✔
532
        return false;
2,759✔
533
    }
2,759✔
534

535
    if (DetectProtoContainsProto(&s->proto, PacketGetIPProto(p)) == 0) {
514,185✔
536
        SCLogDebug("proto didn't match");
48✔
537
        return false;
48✔
538
    }
48✔
539

540
    /* check the source & dst port in the sig */
541
    if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) {
514,137✔
542
        if (!(sflags & SIG_FLAG_DP_ANY)) {
445,529✔
543
            if (p->flags & PKT_IS_FRAGMENT)
11,905✔
544
                return false;
4✔
545
            const DetectPort *dport = DetectPortLookupGroup(s->dp, p->dp);
11,901✔
546
            if (dport == NULL) {
11,901✔
547
                SCLogDebug("dport didn't match.");
3,962✔
548
                return false;
3,962✔
549
            }
3,962✔
550
        }
11,901✔
551
        if (!(sflags & SIG_FLAG_SP_ANY)) {
441,563✔
552
            if (p->flags & PKT_IS_FRAGMENT)
8,594✔
553
                return false;
22✔
554
            const DetectPort *sport = DetectPortLookupGroup(s->sp, p->sp);
8,572✔
555
            if (sport == NULL) {
8,572✔
556
                SCLogDebug("sport didn't match.");
7,811✔
557
                return false;
7,811✔
558
            }
7,811✔
559
        }
8,572✔
560
    } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) {
441,563✔
561
        SCLogDebug("port-less protocol and sig needs ports");
286✔
562
        return false;
286✔
563
    }
286✔
564

565
    /* check the destination address */
566
    if (!(sflags & SIG_FLAG_DST_ANY)) {
502,052✔
567
        if (PacketIsIPv4(p)) {
2,217✔
568
            if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0)
1,088✔
569
                return false;
454✔
570
        } else if (PacketIsIPv6(p)) {
1,129✔
571
            if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0)
1,129✔
572
                return false;
10✔
573
        }
1,129✔
574
    }
2,217✔
575
    /* check the source address */
576
    if (!(sflags & SIG_FLAG_SRC_ANY)) {
501,588✔
577
        if (PacketIsIPv4(p)) {
4,846✔
578
            if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0)
4,715✔
579
                return false;
342✔
580
        } else if (PacketIsIPv6(p)) {
4,715✔
581
            if (DetectAddressMatchIPv6(s->addr_src_match6, s->addr_src_match6_cnt, &p->src) == 0)
131✔
582
                return false;
12✔
583
        }
131✔
584
    }
4,846✔
585

586
    return true;
501,234✔
587
}
501,588✔
588

589
/** \internal
590
 *  \brief run packet/stream prefilter engines
591
 */
592
static inline void DetectRunPrefilterPkt(ThreadVars *tv, const DetectEngineCtx *de_ctx,
593
        DetectEngineThreadCtx *det_ctx, Packet *p, DetectRunScratchpad *scratch)
594
{
829,949✔
595
    /* create our prefilter mask */
596
    PacketCreateMask(p, &p->sig_mask, scratch->alproto, scratch->app_decoder_events);
829,949✔
597
    /* run the prefilter engines */
598
    Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags, p->sig_mask);
829,949✔
599
    /* create match list if we have non-pf and/or pf */
600
    if (det_ctx->pmq.rule_id_array_cnt) {
829,949✔
601
#ifdef PROFILING
602
        if (tv) {
603
            StatsCounterAvgAddI64(
604
                    &tv->stats, det_ctx->counter_mpm_list, (int64_t)det_ctx->pmq.rule_id_array_cnt);
605
        }
606
#endif
607
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT2);
167,486✔
608
        DetectPrefilterCopyDeDup(de_ctx, det_ctx);
167,486✔
609
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT2);
167,486✔
610
    }
167,486✔
611
}
829,949✔
612

613
/** \internal
614
 *  \brief check if the tx whose id is given is the only one
615
 *  live transaction for the flow in the given direction
616
 *
617
 *  \param f flow
618
 *  \param txid transaction id
619
 *  \param dir direction
620
 *
621
 *  \retval bool true if we are sure this tx is the only one live in said direction
622
 */
623
static bool IsOnlyTxInDirection(Flow *f, uint64_t txid, uint8_t dir)
UNCOV
624
{
×
UNCOV
625
    uint64_t tx_cnt = AppLayerParserGetTxCnt(f, f->alstate);
×
UNCOV
626
    if (tx_cnt == txid + 1) {
×
627
        // only live tx
UNCOV
628
        return true;
×
UNCOV
629
    }
×
UNCOV
630
    if (tx_cnt == txid + 2) {
×
631
        // 2 live txs, one after us
UNCOV
632
        void *tx = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, txid + 1);
×
UNCOV
633
        if (tx) {
×
UNCOV
634
            AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, tx);
×
635
            // test if the other tx is unidirectional in the other way
UNCOV
636
            if ((dir == STREAM_TOSERVER && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TS)) ||
×
UNCOV
637
                    (dir == STREAM_TOCLIENT && (txd->flags & APP_LAYER_TX_SKIP_INSPECT_TC))) {
×
UNCOV
638
                return true;
×
UNCOV
639
            }
×
UNCOV
640
        }
×
UNCOV
641
    }
×
UNCOV
642
    return false;
×
UNCOV
643
}
×
644

645
static int SortHelper(const void *a, const void *b)
646
{
193✔
647
    const Signature *sa = *(const Signature **)a;
193✔
648
    const Signature *sb = *(const Signature **)b;
193✔
649
    if (sa->iid == sb->iid)
193✔
650
        return 0;
×
651
    return sa->iid > sb->iid ? 1 : -1;
193✔
652
}
193✔
653

654
static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
655
        const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
656
        Flow *const pflow, const DetectRunScratchpad *scratch)
657
{
829,949✔
658
    uint8_t action = 0;
829,949✔
659
    bool fw_verdict = false;
829,949✔
660
    const bool have_fw_rules = EngineModeIsFirewall();
829,949✔
661
    const Signature *next_s = NULL;
829,949✔
662

663
    /* inspect the sigs against the packet */
664
    /* Prefetch the next signature. */
665
    SigIntId match_cnt = det_ctx->match_array_cnt;
829,949✔
666
#ifdef PROFILING
667
    if (tv) {
668
        StatsCounterAvgAddI64(&tv->stats, det_ctx->counter_match_list, (int64_t)match_cnt);
669
    }
670
#endif
671
    Signature **match_array = det_ctx->match_array;
829,949✔
672

673
    SGH_PROFILING_RECORD(det_ctx, scratch->sgh);
829,949✔
674
#ifdef PROFILING
675
    if (match_cnt >= de_ctx->profile_match_logging_threshold)
676
        RulesDumpMatchArray(det_ctx, scratch->sgh, p);
677
#endif
678

679
    bool skip_fw = false;
829,949✔
680
    uint32_t sflags, next_sflags = 0;
829,949✔
681
    if (match_cnt) {
829,949✔
682
        next_s = *match_array++;
167,486✔
683
        next_sflags = next_s->flags;
167,486✔
684
    }
167,486✔
685
    while (match_cnt--) {
1,311,992✔
686
        RULE_PROFILING_START(p);
482,043✔
687
        bool break_out_of_packet_filter = false;
482,043✔
688
        uint8_t alert_flags = 0;
482,043✔
689
#ifdef PROFILE_RULES
690
        bool smatch = false; /* signature match */
691
#endif
692
        const Signature *s = next_s;
482,043✔
693
        sflags = next_sflags;
482,043✔
694
        if (match_cnt) {
482,043✔
695
            next_s = *match_array++;
314,532✔
696
            next_sflags = next_s->flags;
314,532✔
697
        }
314,532✔
698
        const uint8_t s_proto_flags = s->proto.flags;
482,043✔
699

700
        SCLogDebug("packet %" PRIu64 ": inspecting signature id %" PRIu32 "", PcapPacketCntGet(p),
482,043✔
701
                s->id);
482,043✔
702

703
        /* if we accept:hook'd the `packet_filter` hook, we skip the rest of the firewall rules. */
704
        if (s->flags & SIG_FLAG_FIREWALL) {
482,043✔
UNCOV
705
            if (skip_fw) {
×
UNCOV
706
                SCLogDebug("skipping firewall rule %u", s->id);
×
UNCOV
707
                goto next;
×
UNCOV
708
            }
×
709
        } else if (have_fw_rules) {
482,043✔
710
            /* fw mode, we skip anything after the fw rules if:
711
             * - flow pass is set
712
             * - packet pass (e.g. exception policy) */
UNCOV
713
            if (p->flags & PKT_NOPACKET_INSPECTION ||
×
UNCOV
714
                    (pflow != NULL && pflow->flags & (FLOW_ACTION_PASS))) {
×
UNCOV
715
                SCLogDebug("skipping firewall rule %u", s->id);
×
UNCOV
716
                break_out_of_packet_filter = true;
×
UNCOV
717
                goto next;
×
UNCOV
718
            }
×
UNCOV
719
        }
×
720

721
        if (s->app_inspect != NULL) {
482,043✔
722
            goto next; // handle sig in DetectRunTx
107,120✔
723
        }
107,120✔
724
        if (s->frame_inspect != NULL) {
374,923✔
725
            goto next; // handle sig in DetectRunFrame
×
726
        }
×
727

728
        /* skip pkt sigs for flow end packets */
729
        if ((p->flags & PKT_PSEUDO_STREAM_END) != 0 && s->type == SIG_TYPE_PKT)
374,923✔
730
            goto next;
752✔
731

732
        /* don't run mask check for stateful rules.
733
         * There we depend on prefilter */
734
        if ((s->mask & p->sig_mask) != s->mask) {
374,171✔
735
            SCLogDebug("mask mismatch %x & %x != %x", s->mask, p->sig_mask, s->mask);
644✔
736
            goto next;
644✔
737
        }
644✔
738

739
        if (SigDsizePrefilter(p, s, sflags))
373,527✔
740
            goto next;
3,808✔
741

742
        /* if the sig has alproto and the session as well they should match */
743
        if (likely(sflags & SIG_FLAG_APPLAYER)) {
369,719✔
744
            if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, scratch->alproto)) {
14,783✔
745
                SCLogDebug("alproto mismatch");
796✔
746
                goto next;
796✔
747
            }
796✔
748
        }
14,783✔
749

750
        if (!DetectRunInspectRuleHeader(p, pflow, s, sflags, s_proto_flags)) {
368,923✔
751
            goto next;
21,311✔
752
        }
21,311✔
753

754
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, pflow, p, &alert_flags)) {
347,612✔
755
            goto next;
274,684✔
756
        }
274,684✔
757

758
#ifdef PROFILE_RULES
759
        smatch = true;
760
#endif
761
        DetectRunPostMatch(tv, det_ctx, p, s);
72,928✔
762

763
        uint64_t txid = PACKET_ALERT_NOTX;
72,928✔
764
        if (pflow && pflow->alstate) {
72,928✔
765
            uint8_t dir = (p->flowflags & FLOW_PKT_TOCLIENT) ? STREAM_TOCLIENT : STREAM_TOSERVER;
39,752✔
766
            txid = AppLayerParserGetTransactionInspectId(pflow->alparser, dir);
39,752✔
767
            if ((s->alproto != ALPROTO_UNKNOWN && pflow->proto == IPPROTO_UDP) ||
39,752✔
768
                    (de_ctx->guess_applayer && IsOnlyTxInDirection(pflow, txid, dir))) {
39,752✔
769
                // if there is a UDP specific app-layer signature,
770
                // or only one live transaction
771
                // try to use the good tx for the packet direction
772
                void *tx_ptr =
820✔
773
                        AppLayerParserGetTx(pflow->proto, pflow->alproto, pflow->alstate, txid);
820✔
774
                AppLayerTxData *txd =
820✔
775
                        tx_ptr ? AppLayerParserGetTxData(pflow->proto, pflow->alproto, tx_ptr)
820✔
776
                               : NULL;
820✔
777
                if (txd && txd->guessed_applayer_logged < de_ctx->guess_applayer_log_limit) {
820✔
778
                    alert_flags |= PACKET_ALERT_FLAG_TX;
700✔
779
                    if (pflow->proto != IPPROTO_UDP) {
700✔
UNCOV
780
                        alert_flags |= PACKET_ALERT_FLAG_TX_GUESSED;
×
UNCOV
781
                    }
×
782
                    txd->guessed_applayer_logged++;
700✔
783
                }
700✔
784
            }
820✔
785
        }
39,752✔
786
        AlertQueueAppend(det_ctx, s, p, txid, alert_flags);
72,928✔
787

788
        if (det_ctx->post_rule_work_queue.len > 0) {
72,928✔
789
            /* run post match prefilter engines on work queue */
790
            PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, pflow);
40✔
791

792
            if (det_ctx->pmq.rule_id_array_cnt > 0) {
40✔
793
                /* undo "prefetch" */
794
                if (next_s)
34✔
795
                    match_array--;
34✔
796
                SCLogDebug("sig_array_len %u det_ctx->pmq.rule_id_array_cnt %u",
34✔
797
                        de_ctx->sig_array_len, det_ctx->pmq.rule_id_array_cnt);
34✔
798
                const Signature **r = det_ctx->replace;
34✔
799
                for (uint32_t x = 0; x < match_cnt; x++) {
83✔
800
                    *r++ = match_array[x];
49✔
801
                    SCLogDebug("appended %u", match_array[x]->id);
49✔
802
                }
49✔
803
                /* append the prefilter results, then sort it */
804
                for (uint32_t x = 0; x < det_ctx->pmq.rule_id_array_cnt; x++) {
91✔
805
                    SCLogDebug("adding iid %u", det_ctx->pmq.rule_id_array[x]);
57✔
806
                    Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[x]];
57✔
807
                    SCLogDebug("adding id %u", ts->id);
57✔
808
                    if (ts->app_inspect == NULL) {
57✔
809
                        *r++ = ts;
52✔
810
                        match_cnt++;
52✔
811
                    }
52✔
812
                }
57✔
813
                if (match_cnt > 1) {
34✔
814
                    qsort(det_ctx->replace, match_cnt, sizeof(Signature *), SortHelper);
25✔
815
                }
25✔
816
                /* rewrite match_array to include the new additions, and deduplicate
817
                 * while at it. */
818
                Signature **m = match_array;
34✔
819
                Signature *last_sig = NULL;
34✔
820
                uint32_t skipped = 0;
34✔
821
                for (uint32_t x = 0; x < match_cnt; x++) {
135✔
822
                    /* de-duplicate */
823
                    if (last_sig == *m) {
101✔
824
                        skipped++;
5✔
825
                        continue;
5✔
826
                    }
5✔
827
                    last_sig = *m;
96✔
828
                    *m++ = (Signature *)det_ctx->replace[x];
96✔
829
                }
96✔
830
                match_cnt -= skipped;
34✔
831
                /* prefetch next */
832
                next_s = *match_array++;
34✔
833
                next_sflags = next_s->flags;
34✔
834
                SCLogDebug("%u rules added", det_ctx->pmq.rule_id_array_cnt);
34✔
835
                det_ctx->post_rule_work_queue.len = 0;
34✔
836
                PMQ_RESET(&det_ctx->pmq);
34✔
837
            }
34✔
838
        }
40✔
839

840
        /* firewall logic in the packet:filter table:
841
         * 1. firewall rules preceed the packet:td rules in the list
842
         * 2. if no rule issues an accept, we drop
843
         * 3. drop is immediate
844
         * 4. accept:
845
         *    - hook: skip rest of fw rules, inspect packet:td rules
846
         *    - packet: immediate accept, no packet:td or app:* inspect
847
         *    - flow: as packet, but applied to all future packets in the
848
         *            flow as well
849
         */
850
        if (s->flags & SIG_FLAG_FIREWALL) {
72,928✔
UNCOV
851
            if (s->action & (ACTION_ACCEPT)) {
×
UNCOV
852
                fw_verdict = true;
×
853

UNCOV
854
                enum ActionScope as = s->action_scope;
×
UNCOV
855
                if (as == ACTION_SCOPE_HOOK) {
×
856
                    /* accept:hook: jump to first TD. Implemented as:
857
                     * skip until the first TD rule.
858
                     * Don't update action as we're just continuing to the next hook. */
UNCOV
859
                    skip_fw = true;
×
860

UNCOV
861
                } else if (as == ACTION_SCOPE_PACKET) {
×
862
                    /* accept:packet: break loop, return accept */
UNCOV
863
                    action |= s->action;
×
UNCOV
864
                    break_out_of_packet_filter = true;
×
865

UNCOV
866
                } else if (as == ACTION_SCOPE_FLOW) {
×
867
                    /* accept:flow: break loop, return accept */
UNCOV
868
                    action |= s->action;
×
UNCOV
869
                    break_out_of_packet_filter = true;
×
870

871
                    /* set immediately, as we're in hook "packet_filter" */
UNCOV
872
                    if (pflow) {
×
UNCOV
873
                        pflow->flags |= FLOW_ACTION_ACCEPT;
×
UNCOV
874
                    }
×
UNCOV
875
                }
×
UNCOV
876
            } else if (s->action & ACTION_DROP) {
×
877
                /* apply a drop immediately here */
UNCOV
878
                fw_verdict = true;
×
UNCOV
879
                action |= s->action;
×
UNCOV
880
                break_out_of_packet_filter = true;
×
UNCOV
881
            }
×
UNCOV
882
        }
×
883
next:
482,043✔
884
        DetectVarProcessList(det_ctx, pflow, p);
482,043✔
885
        DetectReplaceFree(det_ctx);
482,043✔
886
        RULE_PROFILING_END(det_ctx, s, smatch, p);
482,043✔
887

888
        /* fw accept:packet or accept:flow means we're done here */
889
        if (break_out_of_packet_filter)
482,043✔
UNCOV
890
            break;
×
891

892
        continue;
482,043✔
893
    }
482,043✔
894

895
    /* if no rule told us to accept, and no rule explicitly dropped, we invoke the default drop
896
     * policy
897
     */
898
    if (have_fw_rules && scratch->default_action == ACTION_DROP) {
829,949✔
UNCOV
899
        if (!fw_verdict) {
×
UNCOV
900
            DEBUG_VALIDATE_BUG_ON(action & ACTION_DROP);
×
UNCOV
901
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
×
UNCOV
902
            action |= ACTION_DROP;
×
UNCOV
903
        } else {
×
904
            /* apply fw action */
UNCOV
905
            p->action |= action;
×
UNCOV
906
        }
×
UNCOV
907
    }
×
908
    return action;
829,949✔
909
}
829,949✔
910

911
/** \internal
912
 *  \param default_action either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
913
 *
914
 *  ACTION_DROP means the default policy of drop:packet is applied
915
 *  ACTION_ACCEPT means the default policy of accept:hook is applied
916
 */
917
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
918
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
919
        const uint8_t default_action)
920
{
1,440,170✔
921
    AppProto alproto = ALPROTO_UNKNOWN;
1,440,170✔
922
    uint8_t flow_flags = 0; /* flow/state flags */
1,440,170✔
923
    bool app_decoder_events = false;
1,440,170✔
924

925
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_SETUP);
1,440,170✔
926

927
#ifdef UNITTESTS
928
    if (RunmodeIsUnittests()) {
929
        p->alerts.cnt = 0;
930
        p->alerts.discarded = 0;
931
        p->alerts.suppressed = 0;
932
    }
933
#endif
934
    det_ctx->filestore_cnt = 0;
1,440,170✔
935
    det_ctx->base64_decoded_len = 0;
1,440,170✔
936
    det_ctx->raw_stream_progress = 0;
1,440,170✔
937
    det_ctx->match_array_cnt = 0;
1,440,170✔
938
    det_ctx->json_content_len = 0;
1,440,170✔
939

940
    det_ctx->alert_queue_size = 0;
1,440,170✔
941
    p->alerts.drop.action = 0;
1,440,170✔
942

943
#ifdef DEBUG
944
    if (p->flags & PKT_STREAM_ADD) {
945
        det_ctx->pkt_stream_add_cnt++;
946
    }
947
#endif
948

949
    /* grab the protocol state we will detect on */
950
    if (p->flags & PKT_HAS_FLOW) {
1,440,170✔
951
        DEBUG_VALIDATE_BUG_ON(pflow == NULL);
1,264,602✔
952

953
        if (p->flowflags & FLOW_PKT_TOSERVER) {
1,264,602✔
954
            flow_flags = STREAM_TOSERVER;
700,086✔
955
            SCLogDebug("flag STREAM_TOSERVER set");
700,086✔
956
        } else if (p->flowflags & FLOW_PKT_TOCLIENT) {
700,086✔
957
            flow_flags = STREAM_TOCLIENT;
564,516✔
958
            SCLogDebug("flag STREAM_TOCLIENT set");
564,516✔
959
        }
564,516✔
960
        SCLogDebug("p->flowflags 0x%02x", p->flowflags);
1,264,602✔
961

962
        if (p->flags & PKT_PSEUDO_STREAM_END) {
1,264,602✔
963
            flow_flags |= STREAM_EOF;
10,717✔
964
            SCLogDebug("STREAM_EOF set");
10,717✔
965
        }
10,717✔
966

967
        /* store tenant_id in the flow so that we can use it
968
         * for creating pseudo packets */
969
        if (p->tenant_id > 0 && pflow->tenant_id == 0) {
1,264,602✔
970
            pflow->tenant_id = p->tenant_id;
×
971
        }
×
972

973
        /* live ruleswap check for flow updates */
974
        if (pflow->de_ctx_version == 0) {
1,264,602✔
975
            /* first time this flow is inspected, set id */
976
            pflow->de_ctx_version = de_ctx->version;
63,336✔
977
        } else if (pflow->de_ctx_version != de_ctx->version) {
1,201,266✔
978
            /* first time we inspect flow with this de_ctx, reset */
979
            pflow->flags &= ~FLOW_SGH_TOSERVER;
1,473✔
980
            pflow->flags &= ~FLOW_SGH_TOCLIENT;
1,473✔
981
            pflow->sgh_toserver = NULL;
1,473✔
982
            pflow->sgh_toclient = NULL;
1,473✔
983

984
            pflow->de_ctx_version = de_ctx->version;
1,473✔
985
            SCGenericVarFree(pflow->flowvar);
1,473✔
986
            pflow->flowvar = NULL;
1,473✔
987

988
            DetectEngineStateResetTxs(pflow);
1,473✔
989
        }
1,473✔
990

991
        /* Retrieve the app layer state and protocol and the tcp reassembled
992
         * stream chunks. */
993
        if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) ||
1,264,602✔
994
                (p->proto == IPPROTO_UDP) ||
1,264,602✔
995
                (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED)))
1,264,602✔
996
        {
1,132,075✔
997
            /* update flow flags with knowledge on disruptions */
998
            flow_flags = FlowGetDisruptionFlags(pflow, flow_flags);
1,132,075✔
999
            alproto = FlowGetAppProtocol(pflow);
1,132,075✔
1000
            if (p->proto == IPPROTO_TCP && pflow->protoctx &&
1,132,075✔
1001
                    StreamReassembleRawHasDataReady(pflow->protoctx, p)) {
1,132,075✔
1002
                p->flags |= PKT_DETECT_HAS_STREAMDATA;
25,039✔
1003
            }
25,039✔
1004
            SCLogDebug("alproto %u", alproto);
1,132,075✔
1005
        } else {
1,132,075✔
1006
            SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto);
132,527✔
1007
        }
132,527✔
1008

1009
        app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
1,264,602✔
1010
    }
1,264,602✔
1011

1012
    DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, default_action, NULL };
1,440,170✔
1013
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP);
1,440,170✔
1014
    return pad;
1,440,170✔
1015
}
1,440,170✔
1016

1017
static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_ctx,
1018
        DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
1019
        DetectRunScratchpad *scratch)
1020
{
1,440,170✔
1021
    /* so now let's iterate the alerts and remove the ones after a pass rule
1022
     * matched (if any). This is done inside PacketAlertFinalize() */
1023
    /* PR: installed "tag" keywords are handled after the threshold inspection */
1024

1025
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_ALERT);
1,440,170✔
1026
    PacketAlertFinalize(de_ctx, det_ctx, p);
1,440,170✔
1027
    if (p->alerts.cnt > 0) {
1,440,170✔
1028
        StatsCounterAddI64(&tv->stats, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt);
75,653✔
1029
    }
75,653✔
1030
    if (p->alerts.discarded > 0) {
1,440,170✔
1031
        StatsCounterAddI64(
10✔
1032
                &tv->stats, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded);
10✔
1033
    }
10✔
1034
    if (p->alerts.suppressed > 0) {
1,440,170✔
1035
        StatsCounterAddI64(
3,301✔
1036
                &tv->stats, det_ctx->counter_alerts_suppressed, (uint64_t)p->alerts.suppressed);
3,301✔
1037
    }
3,301✔
1038
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT);
1,440,170✔
1039

1040
    /* firewall: "fail" closed if we don't have an ACCEPT. This can happen
1041
     * if there was no rule group. */
1042
    // TODO review packet src types here
1043
    if (EngineModeIsFirewall() && !(p->action & ACTION_ACCEPT) && p->pkt_src == PKT_SRC_WIRE &&
1,440,170✔
1044
            scratch->default_action == ACTION_DROP) {
1,440,170✔
UNCOV
1045
        SCLogDebug("packet %" PRIu64 ": droppit as no ACCEPT set %02x (pkt %s)",
×
UNCOV
1046
                PcapPacketCntGet(p), p->action, PktSrcToString(p->pkt_src));
×
UNCOV
1047
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
×
UNCOV
1048
    }
×
1049
}
1,440,170✔
1050

1051
static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx,
1052
        Packet *p, Flow * const pflow)
1053
{
1,440,170✔
1054
    PACKET_PROFILING_DETECT_START(p, PROF_DETECT_CLEANUP);
1,440,170✔
1055
    InspectionBufferClean(det_ctx);
1,440,170✔
1056

1057
    if (pflow != NULL) {
1,440,170✔
1058
        /* update inspected tracker for raw reassembly */
1059
        if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL &&
1,264,602✔
1060
                (p->flags & PKT_DETECT_HAS_STREAMDATA)) {
1,264,602✔
1061
            StreamReassembleRawUpdateProgress(pflow->protoctx, p,
25,039✔
1062
                    det_ctx->raw_stream_progress);
25,039✔
1063
        }
25,039✔
1064
    }
1,264,602✔
1065
    PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP);
1,440,170✔
1066
    SCReturn;
1,440,170✔
1067
}
1,440,170✔
1068

1069
void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size)
1070
{
20,527✔
1071
    DEBUG_VALIDATE_BUG_ON(det_ctx->tx_candidates);
20,527✔
1072
    det_ctx->tx_candidates = SCCalloc(size, sizeof(RuleMatchCandidateTx));
20,527✔
1073
    if (det_ctx->tx_candidates == NULL) {
20,527✔
1074
        FatalError("failed to allocate %" PRIu64 " bytes",
×
1075
                (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
×
1076
    }
×
1077
    det_ctx->tx_candidates_size = size;
20,527✔
1078
    SCLogDebug("array initialized to %u elements (%"PRIu64" bytes)",
20,527✔
1079
            size, (uint64_t)(size * sizeof(RuleMatchCandidateTx)));
20,527✔
1080
}
20,527✔
1081

1082
void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx)
1083
{
35,437✔
1084
    SCFree(det_ctx->tx_candidates);
35,437✔
1085
    det_ctx->tx_candidates_size = 0;
35,437✔
1086
}
35,437✔
1087

1088
/* if size >= cur_space */
1089
static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx,
1090
        const uint32_t need)
1091
{
1,153,399✔
1092
    if (det_ctx->tx_candidates_size >= need)
1,153,399✔
1093
        return 1;
1,152,954✔
1094
    return 0;
445✔
1095
}
1,153,399✔
1096

1097
/* realloc */
1098
static int RuleMatchCandidateTxArrayExpand(DetectEngineThreadCtx *det_ctx, const uint32_t needed)
1099
{
445✔
1100
    const uint32_t old_size = det_ctx->tx_candidates_size;
445✔
1101
    uint32_t new_size = needed;
445✔
1102
    void *ptmp = SCRealloc(det_ctx->tx_candidates, (new_size * sizeof(RuleMatchCandidateTx)));
445✔
1103
    if (ptmp == NULL) {
445✔
1104
        FatalError("failed to expand to %" PRIu64 " bytes",
×
1105
                (uint64_t)(new_size * sizeof(RuleMatchCandidateTx)));
×
1106
        // TODO can this be handled more gracefully?
1107
    }
×
1108
    det_ctx->tx_candidates = ptmp;
445✔
1109
    det_ctx->tx_candidates_size = new_size;
445✔
1110
    SCLogDebug("array expanded from %u to %u elements (%"PRIu64" bytes -> %"PRIu64" bytes)",
445✔
1111
            old_size, new_size, (uint64_t)(old_size * sizeof(RuleMatchCandidateTx)),
445✔
1112
            (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); (void)old_size;
445✔
1113
    return 1;
445✔
1114
}
445✔
1115

1116
/** \internal
1117
 *  \brief sort helper for sorting match candidates by id: ascending
1118
 *
1119
 *  The id field is set from Signature::num, so we sort the candidates to match the signature
1120
 *  sort order (ascending), where candidates that have flags go first.
1121
 */
1122
static int
1123
DetectRunTxSortHelper(const void *a, const void *b)
1124
{
24,104✔
1125
    const RuleMatchCandidateTx *s0 = a;
24,104✔
1126
    const RuleMatchCandidateTx *s1 = b;
24,104✔
1127
    if (s1->id == s0->id) {
24,104✔
1128
        if (s1->flags && !s0->flags)
14,651✔
1129
            return 1;
14,645✔
1130
        else if (!s1->flags && s0->flags)
6✔
1131
            return -1;
×
1132
        return 0;
6✔
1133
    } else
14,651✔
1134
        return s0->id > s1->id ? 1 : -1;
9,453✔
1135
}
24,104✔
1136

1137
#if 0
1138
#define TRACE_SID_TXS(sid,txs,...)          \
1139
    do {                                    \
1140
        char _trace_buf[2048];              \
1141
        snprintf(_trace_buf, sizeof(_trace_buf), __VA_ARGS__);  \
1142
        SCLogNotice("%p/%"PRIu64"/%u: %s", txs->tx_ptr, txs->tx_id, sid, _trace_buf);   \
1143
    } while(0)
1144
#else
1145
#define TRACE_SID_TXS(sid,txs,...)
1146
#endif
1147

1148
// Get inner transaction for engine
1149
void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags)
1150
{
1,206,019✔
1151
    if (unlikely(alproto == ALPROTO_DOH2)) {
1,206,019✔
1152
        if (engine_alproto == ALPROTO_DNS) {
62,919✔
1153
            // need to get the dns tx pointer
1154
            tx_ptr = SCDoH2GetDnsTx(tx_ptr, flow_flags);
7,691✔
1155
        } else if (engine_alproto != ALPROTO_HTTP2 && engine_alproto != ALPROTO_UNKNOWN) {
55,228✔
1156
            // incompatible engine->alproto with flow alproto
1157
            tx_ptr = NULL;
40,559✔
1158
        }
40,559✔
1159
    } else if (engine_alproto != alproto && engine_alproto != ALPROTO_UNKNOWN) {
1,143,100✔
1160
        // incompatible engine->alproto with flow alproto
1161
        tx_ptr = NULL;
735,555✔
1162
    }
735,555✔
1163
    return tx_ptr;
1,206,019✔
1164
}
1,206,019✔
1165

1166
/** \internal
1167
 *  \brief inspect a rule against a transaction
1168
 *
1169
 *  Inspect a rule. New detection or continued stateful
1170
 *  detection.
1171
 *
1172
 *  \param stored_flags pointer to stored flags or NULL.
1173
 *         If stored_flags is set it means we're continuing
1174
 *         inspection from an earlier run.
1175
 *
1176
 *  \retval bool true sig matched, false didn't match
1177
 */
1178
static bool DetectRunTxInspectRule(ThreadVars *tv,
1179
        DetectEngineCtx *de_ctx,
1180
        DetectEngineThreadCtx *det_ctx,
1181
        Packet *p,
1182
        Flow *f,
1183
        const uint8_t in_flow_flags,   // direction, EOF, etc
1184
        void *alstate,
1185
        DetectTransaction *tx,
1186
        const Signature *s,
1187
        uint32_t *stored_flags,
1188
        RuleMatchCandidateTx *can,
1189
        DetectRunScratchpad *scratch)
1190
{
127,232✔
1191
    const uint8_t flow_flags = in_flow_flags;
127,232✔
1192
    const int direction = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
127,232✔
1193
    uint32_t inspect_flags = stored_flags ? *stored_flags : 0;
127,232✔
1194
    int total_matches = 0;
127,232✔
1195
    uint16_t file_no_match = 0;
127,232✔
1196
    bool retval = false;
127,232✔
1197
    bool mpm_before_progress = false;   // is mpm engine before progress?
127,232✔
1198
    bool mpm_in_progress = false;       // is mpm engine in a buffer we will revisit?
127,232✔
1199

1200
    TRACE_SID_TXS(s->id, tx, "starting %s", direction ? "toclient" : "toserver");
127,232✔
1201

1202
    /* for a new inspection we inspect pkt header and packet matches */
1203
    if (likely(stored_flags == NULL)) {
127,232✔
1204
        TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches");
127,105✔
1205
        if (!DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags)) {
127,105✔
1206
            TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match");
1,764✔
1207
            return false;
1,764✔
1208
        }
1,764✔
1209
        if (!DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL)) {
125,341✔
1210
            TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match");
121✔
1211
            return false;
121✔
1212
        }
121✔
1213
        /* stream mpm and negated mpm sigs can end up here with wrong proto */
1214
        if (!(AppProtoEquals(s->alproto, f->alproto) || s->alproto == ALPROTO_UNKNOWN)) {
125,220✔
1215
            TRACE_SID_TXS(s->id, tx, "alproto mismatch");
185✔
1216
            return false;
185✔
1217
        }
185✔
1218
    }
125,220✔
1219

1220
    const DetectEngineAppInspectionEngine *engine = s->app_inspect;
125,162✔
1221
    do {
288,265✔
1222
        TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags);
288,265✔
1223
        // also if it is not the same direction, but
1224
        // this is a transactional signature, and we are toclient
1225
        if (!(inspect_flags & BIT_U32(engine->id)) &&
288,265✔
1226
                (direction == engine->dir || ((s->flags & SIG_FLAG_TXBOTHDIR) && direction == 1))) {
288,265✔
1227

1228
            void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags);
196,151✔
1229
            if (tx_ptr == NULL) {
196,151✔
1230
                if (engine->alproto != ALPROTO_UNKNOWN) {
69,556✔
1231
                    /* special case: file_data on 'alert tcp' will have engines
1232
                     * in the list that are not for us. */
1233
                    engine = engine->next;
69,556✔
1234
                    continue;
69,556✔
1235
                } else {
69,556✔
UNCOV
1236
                    tx_ptr = tx->tx_ptr;
×
UNCOV
1237
                }
×
1238
            }
69,556✔
1239

1240
            /* engines are sorted per progress, except that the one with
1241
             * mpm/prefilter enabled is first */
1242
            if (tx->tx_progress < engine->progress) {
126,595✔
1243
                SCLogDebug("tx progress %d < engine progress %d",
28✔
1244
                        tx->tx_progress, engine->progress);
28✔
1245
                break;
28✔
1246
            }
28✔
1247
            if (engine->mpm) {
126,567✔
1248
                if (tx->tx_progress > engine->progress) {
13,024✔
1249
                    TRACE_SID_TXS(s->id, tx,
9,610✔
1250
                            "engine->mpm: t->tx_progress %u > engine->progress %u, so set "
9,610✔
1251
                            "mpm_before_progress",
9,610✔
1252
                            tx->tx_progress, engine->progress);
9,610✔
1253
                    mpm_before_progress = true;
9,610✔
1254
                } else if (tx->tx_progress == engine->progress) {
9,610✔
1255
                    TRACE_SID_TXS(s->id, tx,
3,414✔
1256
                            "engine->mpm: t->tx_progress %u == engine->progress %u, so set "
3,414✔
1257
                            "mpm_in_progress",
3,414✔
1258
                            tx->tx_progress, engine->progress);
3,414✔
1259
                    mpm_in_progress = true;
3,414✔
1260
                }
3,414✔
1261
            }
13,024✔
1262

1263
            uint8_t engine_flags = flow_flags;
126,567✔
1264
            if (direction != engine->dir) {
126,567✔
1265
                engine_flags = flow_flags ^ (STREAM_TOCLIENT | STREAM_TOSERVER);
6✔
1266
            }
6✔
1267
            /* run callback: but bypass stream callback if we can */
1268
            uint8_t match;
126,567✔
1269
            if (unlikely(engine->stream && can->stream_stored)) {
126,567✔
1270
                match = can->stream_result;
×
1271
                TRACE_SID_TXS(s->id, tx, "stream skipped, stored result %d used instead", match);
×
1272
            } else if (engine->v2.Callback == NULL) {
126,567✔
1273
                /* TODO is this the cleanest way to support a non-app sig on a app hook? */
1274

UNCOV
1275
                if (tx->tx_progress > engine->progress) {
×
UNCOV
1276
                    mpm_before_progress = true; // TODO needs a new name now
×
UNCOV
1277
                }
×
1278

1279
                /* we don't have to store a "hook" match, also don't want to keep any state to make
1280
                 * sure the hook gets invoked again until tx progress progresses. */
UNCOV
1281
                if (tx->tx_progress <= engine->progress)
×
UNCOV
1282
                    return DETECT_ENGINE_INSPECT_SIG_MATCH;
×
1283

1284
                /* if progress > engine progress, track state to avoid additional matches */
UNCOV
1285
                match = DETECT_ENGINE_INSPECT_SIG_MATCH;
×
1286
            } else {
126,567✔
1287
                KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
126,567✔
1288
                DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
126,567✔
1289
                match = engine->v2.Callback(
126,567✔
1290
                        de_ctx, det_ctx, engine, s, f, engine_flags, alstate, tx_ptr, tx->tx_id);
126,567✔
1291
                TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
126,567✔
1292
                if (engine->stream) {
126,567✔
1293
                    can->stream_stored = true;
156✔
1294
                    can->stream_result = match;
156✔
1295
                    TRACE_SID_TXS(s->id, tx, "stream ran, store result %d for next tx (if any)", match);
156✔
1296
                }
156✔
1297
            }
126,567✔
1298
            if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
126,567✔
1299
                inspect_flags |= BIT_U32(engine->id);
31,823✔
1300
                engine = engine->next;
31,823✔
1301
                total_matches++;
31,823✔
1302
                continue;
31,823✔
1303
            } else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
94,744✔
1304
                /* if the file engine matched, but indicated more
1305
                 * files are still in progress, we don't set inspect
1306
                 * flags as these would end inspection for this tx */
1307
                engine = engine->next;
×
1308
                total_matches++;
×
1309
                continue;
×
1310
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
94,744✔
1311
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
12,785✔
1312
                inspect_flags |= BIT_U32(engine->id);
12,785✔
1313
            } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES) {
81,959✔
1314
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
12,070✔
1315
                inspect_flags |= BIT_U32(engine->id);
12,070✔
1316
                file_no_match = 1;
12,070✔
1317
            }
12,070✔
1318
            /* implied DETECT_ENGINE_INSPECT_SIG_NO_MATCH */
1319
            if (engine->mpm && mpm_before_progress) {
94,744✔
1320
                inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
3,398✔
1321
                inspect_flags |= BIT_U32(engine->id);
3,398✔
1322
            }
3,398✔
1323
            break;
94,744✔
1324
        } else if (!(inspect_flags & BIT_U32(engine->id)) && s->flags & SIG_FLAG_TXBOTHDIR &&
126,567✔
1325
                   direction != engine->dir) {
92,114✔
1326
            // for transactional rules, the engines on the opposite direction
1327
            // are ordered by progress on the different side
1328
            // so we have a two mixed-up lists, and we skip the elements
1329
            if (direction == 0 && engine->next == NULL) {
6✔
1330
                // do not match yet on request only
1331
                break;
1✔
1332
            }
1✔
1333
            engine = engine->next;
5✔
1334
            continue;
5✔
1335
        }
6✔
1336

1337
        engine = engine->next;
92,108✔
1338
    } while (engine != NULL);
193,492✔
1339
    TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p",
125,162✔
1340
            inspect_flags, total_matches, engine);
125,162✔
1341

1342
    if (engine == NULL && total_matches) {
125,162✔
1343
        inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
30,389✔
1344
        TRACE_SID_TXS(s->id, tx, "MATCH");
30,389✔
1345
        retval = true;
30,389✔
1346
    }
30,389✔
1347

1348
    if (stored_flags) {
125,162✔
1349
        *stored_flags = inspect_flags;
127✔
1350
        TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags);
127✔
1351
    } else {
125,035✔
1352
        // store... or? If tx is done we might not want to come back to this tx
1353

1354
        // also... if mpmid tracking is enabled, we won't do a sig again for this tx...
1355
        TRACE_SID_TXS(s->id, tx, "start inspect flags %08x", inspect_flags);
125,035✔
1356
        if (inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
125,035✔
1357
            if (file_no_match) {
25,799✔
1358
                /* if we have a mismatch on a file sig, we need to keep state.
1359
                 * We may get another file on the same tx (for http and smtp
1360
                 * at least), so for a new file we need to re-eval the sig.
1361
                 * Thoughts / TODO:
1362
                 *  - not for some protos that have 1 file per tx (e.g. nfs)
1363
                 *  - maybe we only need this for file sigs that mix with
1364
                 *    other matches? E.g. 'POST + filename', is different than
1365
                 *    just 'filename'.
1366
                 */
1367
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
12,070✔
1368
                        inspect_flags, flow_flags, file_no_match);
12,070✔
1369
            }
12,070✔
1370
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) && mpm_before_progress) {
99,236✔
1371
            TRACE_SID_TXS(s->id, tx, "no need to store match sig, "
6,009✔
1372
                    "mpm won't trigger for it anymore");
6,009✔
1373

1374
            if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) {
6,009✔
1375
                TRACE_SID_TXS(s->id, tx, "except that for new files, "
90✔
1376
                        "we may have to revisit anyway");
90✔
1377
                DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
90✔
1378
                        inspect_flags, flow_flags, file_no_match);
90✔
1379
            }
90✔
1380
        } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) {
93,227✔
1381
            TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, "
190✔
1382
                    "mpm will revisit it");
190✔
1383
        } else if (inspect_flags != 0 || file_no_match != 0) {
93,037✔
1384
            TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags);
24,577✔
1385
            DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s,
24,577✔
1386
                    inspect_flags, flow_flags, file_no_match);
24,577✔
1387
        }
24,577✔
1388
    }
125,035✔
1389

1390
    return retval;
125,162✔
1391
}
125,162✔
1392

1393
#define NO_TX                                                                                      \
1394
    {                                                                                              \
8,438,973✔
1395
        NULL, 0, NULL, NULL, 0, 0, 0, 0,                                                           \
8,438,973✔
1396
    }
8,438,973✔
1397

1398
/** \internal
1399
 *  \brief get a DetectTransaction object
1400
 *  \retval struct filled with relevant info or all nulls/0s
1401
 */
1402
static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alproto,
1403
        const uint64_t tx_id, void *tx_ptr, const int tx_end_state, const uint8_t flow_flags)
1404
{
8,758,747✔
1405
    AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx_ptr);
8,758,747✔
1406
    const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags);
8,758,747✔
1407
    bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc;
8,758,747✔
1408
    if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) {
8,758,747✔
1409
        DetectTransaction no_tx = NO_TX;
5,617,057✔
1410
        return no_tx;
5,617,057✔
1411
    }
5,617,057✔
1412
    const uint8_t inspected_flag =
3,141,690✔
1413
            (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_INSPECTED_TS : APP_LAYER_TX_INSPECTED_TC;
3,141,690✔
1414
    if (unlikely(txd->flags & inspected_flag)) {
3,141,690✔
1415
        SCLogDebug("%" PRIu64 " tx already fully inspected for %s. Flags %02x", tx_id,
2,820,140✔
1416
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
2,820,140✔
1417
        DetectTransaction no_tx = NO_TX;
2,820,140✔
1418
        return no_tx;
2,820,140✔
1419
    }
2,820,140✔
1420
    const uint8_t skip_flag = (flow_flags & STREAM_TOSERVER) ? APP_LAYER_TX_SKIP_INSPECT_TS
321,550✔
1421
                                                             : APP_LAYER_TX_SKIP_INSPECT_TC;
321,550✔
1422
    if (unlikely(txd->flags & skip_flag)) {
321,550✔
1423
        SCLogDebug("%" PRIu64 " tx should not be inspected in direction %s. Flags %02x", tx_id,
1,776✔
1424
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", txd->flags);
1,776✔
1425
        DetectTransaction no_tx = NO_TX;
1,776✔
1426
        return no_tx;
1,776✔
1427
    }
1,776✔
1428

1429
    const uint8_t detect_progress =
319,774✔
1430
            (flow_flags & STREAM_TOSERVER) ? txd->detect_progress_ts : txd->detect_progress_tc;
319,774✔
1431

1432
    const int dir_int = (flow_flags & STREAM_TOSERVER) ? 0 : 1;
319,774✔
1433
    DetectEngineState *tx_de_state = txd->de_state;
319,774✔
1434
    DetectEngineStateDirection *tx_dir_state =
319,774✔
1435
            tx_de_state ? &tx_de_state->dir_state[dir_int] : NULL;
319,774✔
1436
    DetectTransaction tx = {
319,774✔
1437
        .tx_ptr = tx_ptr,
319,774✔
1438
        .tx_id = tx_id,
319,774✔
1439
        .tx_data_ptr = (struct AppLayerTxData *)txd,
319,774✔
1440
        .de_state = tx_dir_state,
319,774✔
1441
        .detect_progress = detect_progress,
319,774✔
1442
        .detect_progress_orig = detect_progress,
319,774✔
1443
        .tx_progress = tx_progress,
319,774✔
1444
        .tx_end_state = tx_end_state,
319,774✔
1445
    };
319,774✔
1446
    return tx;
319,774✔
1447
}
321,550✔
1448

1449
static inline void StoreDetectProgress(
1450
        DetectTransaction *tx, const uint8_t flow_flags, const uint8_t progress)
1451
{
149,153✔
1452
    AppLayerTxData *txd = (AppLayerTxData *)tx->tx_data_ptr;
149,153✔
1453
    if (flow_flags & STREAM_TOSERVER) {
149,153✔
1454
        txd->detect_progress_ts = progress;
79,416✔
1455
    } else {
79,416✔
1456
        txd->detect_progress_tc = progress;
69,737✔
1457
    }
69,737✔
1458
}
149,153✔
1459

1460
// Merge 'state' rules from the regular prefilter
1461
// updates array_idx on the way
1462
static inline void RuleMatchCandidateMergeStateRules(
1463
        DetectEngineThreadCtx *det_ctx, uint32_t *array_idx)
1464
{
319,774✔
1465
    // Now, we will merge 2 sorted lists :
1466
    // the one in det_ctx->tx_candidates
1467
    // and the one in det_ctx->match_array
1468
    // For match_array, we take only the relevant elements where s->app_inspect != NULL
1469

1470
    // Basically, we iterate at the same time over the 2 lists
1471
    // comparing and taking an element from either.
1472

1473
    // Trick is to do so in place in det_ctx->tx_candidates,
1474
    // so as to minimize the number of moves in det_ctx->tx_candidates.
1475
    // For this, the algorithm traverses the lists in reverse order.
1476
    // Otherwise, if the first element of match_array was to be put before
1477
    // all tx_candidates, we would need to shift all tx_candidates
1478

1479
    // Retain the number of elements sorted in tx_candidates before merge
1480
    uint32_t j = *array_idx;
319,774✔
1481
    // First loop only counting the number of elements to add
1482
    for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
423,724✔
1483
        const Signature *s = det_ctx->match_array[i];
103,950✔
1484
        if (s->app_inspect != NULL) {
103,950✔
1485
            (*array_idx)++;
14,629✔
1486
        }
14,629✔
1487
    }
103,950✔
1488
    // Future number of elements in tx_candidates after merge
1489
    uint32_t k = *array_idx;
319,774✔
1490

1491
    if (k == j) {
319,774✔
1492
        // no new element from match_array to merge in tx_candidates
1493
        return;
313,083✔
1494
    }
313,083✔
1495

1496
    // variable i is for all elements of match_array (even not relevant ones)
1497
    // variable j is for elements of tx_candidates before merge
1498
    // variable k is for elements of tx_candidates after merge
1499
    for (uint32_t i = det_ctx->match_array_cnt; i > 0;) {
25,080✔
1500
        const Signature *s = det_ctx->match_array[i - 1];
18,389✔
1501
        if (s->app_inspect == NULL) {
18,389✔
1502
            // no relevant element, get the next one from match_array
1503
            i--;
3,694✔
1504
            continue;
3,694✔
1505
        }
3,694✔
1506
        // we have one element from match_array to merge in tx_candidates
1507
        k--;
14,695✔
1508
        if (j > 0) {
14,695✔
1509
            // j > 0 means there is still at least one element in tx_candidates to merge
1510
            const RuleMatchCandidateTx *s0 = &det_ctx->tx_candidates[j - 1];
504✔
1511
            if (s->iid <= s0->id) {
504✔
1512
                // get next element from previous tx_candidates
1513
                j--;
66✔
1514
                // take the element from tx_candidates before merge
1515
                det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s;
66✔
1516
                det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id;
66✔
1517
                det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags;
66✔
1518
                det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset;
66✔
1519
                continue;
66✔
1520
            }
66✔
1521
        } // otherwise
504✔
1522
        // get next element from match_array
1523
        i--;
14,629✔
1524
        // take the element from match_array
1525
        det_ctx->tx_candidates[k].s = s;
14,629✔
1526
        det_ctx->tx_candidates[k].id = s->iid;
14,629✔
1527
        det_ctx->tx_candidates[k].flags = NULL;
14,629✔
1528
        det_ctx->tx_candidates[k].stream_reset = 0;
14,629✔
1529
    }
14,629✔
1530
    // Even if k > 0 or j > 0, the loop is over. (Note that j == k now)
1531
    // The remaining elements in tx_candidates up to k were already sorted
1532
    // and come before any other element later in the list
1533
}
6,691✔
1534

1535
/**
1536
 * \internal
1537
 * \brief Check and update firewall rules state.
1538
 *
1539
 * \param skip_fw_hook bool to indicate firewall rules skips
1540
 * For state `skip_before_progress` should be skipped.
1541
 *
1542
 * \param skip_before_progress progress value to skip rules before.
1543
 * Only used if `skip_fw_hook` is set.
1544
 *
1545
 * \param last_for_progress[out] set to true if this is the last rule for a progress value
1546
 *
1547
 * \param fw_next_progress_missing[out] set to true if the next fw rule does not target the next
1548
 * progress value, or there is no fw rule for that value.
1549
 *
1550
 * \retval 0 no action needed
1551
 * \retval 1 rest of rules shouldn't inspected
1552
 * \retval -1 skip this rule
1553
 */
1554
static int DetectRunTxCheckFirewallPolicy(DetectEngineThreadCtx *det_ctx, Packet *p, Flow *f,
1555
        DetectTransaction *tx, const Signature *s, const uint32_t can_idx, const uint32_t can_size,
1556
        bool *skip_fw_hook, const uint8_t skip_before_progress, bool *last_for_progress,
1557
        bool *fw_next_progress_missing)
UNCOV
1558
{
×
UNCOV
1559
    if (s->flags & SIG_FLAG_FIREWALL) {
×
1560
        /* check if the next sig is on the same progress hook. If not, we need to apply our
1561
         * default policy in case the current sig doesn't apply one. If the next sig has a
1562
         * progress beyond our progress + 1, it means the next progress has no rules and needs
1563
         * the default policy applied. But only after we evaluate the current rule first, as
1564
         * that may override it.
1565
         * TODO should we do this after dedup below? */
1566

UNCOV
1567
        if (can_idx + 1 < can_size) {
×
UNCOV
1568
            const Signature *next_s = det_ctx->tx_candidates[can_idx + 1].s;
×
UNCOV
1569
            SCLogDebug(
×
UNCOV
1570
                    "peek: peeking at sid %u / progress %u", next_s->id, next_s->app_progress_hook);
×
UNCOV
1571
            if (next_s->flags & SIG_FLAG_FIREWALL) {
×
UNCOV
1572
                if (s->app_progress_hook != next_s->app_progress_hook) {
×
UNCOV
1573
                    SCLogDebug("peek: next sid progress %u != current progress %u, so current "
×
UNCOV
1574
                               "is last for progress",
×
UNCOV
1575
                            next_s->app_progress_hook, s->app_progress_hook);
×
UNCOV
1576
                    *last_for_progress = true;
×
1577

UNCOV
1578
                    if (next_s->app_progress_hook - s->app_progress_hook > 1) {
×
UNCOV
1579
                        SCLogDebug("peek: missing progress, so we'll drop that unless we get a "
×
UNCOV
1580
                                   "sweeping accept first");
×
UNCOV
1581
                        *fw_next_progress_missing = true;
×
UNCOV
1582
                    }
×
UNCOV
1583
                }
×
UNCOV
1584
            } else {
×
UNCOV
1585
                SCLogDebug("peek: next sid not a fw rule, so current is last for progress");
×
UNCOV
1586
                *last_for_progress = true;
×
UNCOV
1587
            }
×
UNCOV
1588
        } else {
×
UNCOV
1589
            SCLogDebug("peek: no peek beyond last rule");
×
UNCOV
1590
            if (s->app_progress_hook < tx->tx_progress) {
×
UNCOV
1591
                SCLogDebug("peek: there are no rules to allow the state after this rule");
×
UNCOV
1592
                *fw_next_progress_missing = true;
×
UNCOV
1593
            }
×
UNCOV
1594
        }
×
1595

UNCOV
1596
        if ((*skip_fw_hook) == true) {
×
UNCOV
1597
            if (s->app_progress_hook <= skip_before_progress) {
×
UNCOV
1598
                return -1;
×
UNCOV
1599
            }
×
UNCOV
1600
            *skip_fw_hook = false;
×
UNCOV
1601
        }
×
UNCOV
1602
    } else {
×
1603
        /* fw mode, we skip anything after the fw rules if:
1604
         * - flow pass is set
1605
         * - packet pass (e.g. exception policy) */
UNCOV
1606
        if (p->flags & PKT_NOPACKET_INSPECTION || (f->flags & (FLOW_ACTION_PASS))) {
×
1607
            SCLogDebug("skipping firewall rule %u", s->id);
×
1608
            return 1;
×
1609
        }
×
UNCOV
1610
    }
×
UNCOV
1611
    return 0;
×
UNCOV
1612
}
×
1613

1614
// TODO move into det_ctx?
1615
thread_local Signature default_accept;
1616
static inline void DetectRunAppendDefaultAccept(DetectEngineThreadCtx *det_ctx, Packet *p)
1617
{
345,713✔
1618
    if (EngineModeIsFirewall()) {
345,713✔
UNCOV
1619
        memset(&default_accept, 0, sizeof(default_accept));
×
UNCOV
1620
        default_accept.action = ACTION_ACCEPT;
×
UNCOV
1621
        default_accept.action_scope = ACTION_SCOPE_PACKET;
×
UNCOV
1622
        default_accept.iid = UINT32_MAX;
×
UNCOV
1623
        default_accept.type = SIG_TYPE_PKT;
×
UNCOV
1624
        default_accept.flags = SIG_FLAG_FIREWALL;
×
UNCOV
1625
        AlertQueueAppend(det_ctx, &default_accept, p, 0, PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET);
×
UNCOV
1626
    }
×
1627
}
345,713✔
1628

1629
/** \internal
1630
 * \brief see if the accept rule needs to apply to the packet
1631
 */
1632
static inline bool ApplyAcceptToPacket(
1633
        const uint64_t total_txs, const DetectTransaction *tx, const Signature *s)
1634
{
30,389✔
1635
    if ((s->flags & SIG_FLAG_FIREWALL) == 0) {
30,389✔
1636
        return false;
30,389✔
1637
    }
30,389✔
UNCOV
1638
    if ((s->action & ACTION_ACCEPT) == 0) {
×
UNCOV
1639
        return false;
×
UNCOV
1640
    }
×
1641

1642
    /* for accept:tx we need:
1643
     * - packet will only be accepted if this is set on the last tx
1644
     */
UNCOV
1645
    if (s->action_scope == ACTION_SCOPE_TX) {
×
UNCOV
1646
        if (total_txs == tx->tx_id + 1) {
×
UNCOV
1647
            return true;
×
UNCOV
1648
        }
×
UNCOV
1649
    }
×
1650
    /* for accept:hook we need a bit more checking:
1651
     * - packet will only be accepted if this is set on the last tx
1652
     * - the hook accepted should be the last progress available. */
UNCOV
1653
    if (s->action_scope == ACTION_SCOPE_HOOK) {
×
UNCOV
1654
        if ((total_txs == tx->tx_id + 1) && /* last tx */
×
UNCOV
1655
                (s->app_progress_hook == tx->tx_progress)) {
×
UNCOV
1656
            return true;
×
UNCOV
1657
        }
×
UNCOV
1658
    }
×
UNCOV
1659
    return false;
×
UNCOV
1660
}
×
1661

1662
/** \internal
1663
 * \retval bool true: break_out_of_app_filter, false: don't break out */
1664
static bool ApplyAccept(Packet *p, const uint8_t flow_flags, const Signature *s,
1665
        DetectTransaction *tx, const int tx_end_state, const bool fw_next_progress_missing,
1666
        bool *tx_fw_verdict, bool *skip_fw_hook, uint8_t *skip_before_progress)
UNCOV
1667
{
×
UNCOV
1668
    *tx_fw_verdict = true;
×
1669

UNCOV
1670
    const enum ActionScope as = s->action_scope;
×
1671
    /* accept:hook: jump to first rule of next state.
1672
     * Implemented as skip until the first rule of next state. */
UNCOV
1673
    if (as == ACTION_SCOPE_HOOK) {
×
UNCOV
1674
        *skip_fw_hook = true;
×
UNCOV
1675
        *skip_before_progress = s->app_progress_hook;
×
1676

1677
        /* if there is no fw rule for the next progress value,
1678
         * we invoke the default drop policy. */
UNCOV
1679
        if (fw_next_progress_missing) {
×
UNCOV
1680
            SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
×
UNCOV
1681
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
×
UNCOV
1682
            PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
×
UNCOV
1683
            p->flow->flags |= FLOW_ACTION_DROP;
×
UNCOV
1684
            return true;
×
UNCOV
1685
        }
×
UNCOV
1686
        return false;
×
UNCOV
1687
    } else if (as == ACTION_SCOPE_TX) {
×
UNCOV
1688
        tx->tx_data_ptr->flags |= APP_LAYER_TX_ACCEPT;
×
UNCOV
1689
        *skip_fw_hook = true;
×
UNCOV
1690
        *skip_before_progress = (uint8_t)tx_end_state + 1; // skip all hooks
×
UNCOV
1691
        SCLogDebug(
×
UNCOV
1692
                "accept:tx applied, skip_fw_hook, skip_before_progress %u", *skip_before_progress);
×
UNCOV
1693
        return false;
×
UNCOV
1694
    } else if (as == ACTION_SCOPE_PACKET) {
×
UNCOV
1695
        return true;
×
UNCOV
1696
    } else if (as == ACTION_SCOPE_FLOW) {
×
UNCOV
1697
        return true;
×
UNCOV
1698
    }
×
1699
    return false;
×
UNCOV
1700
}
×
1701

1702
static void DetectRunTx(ThreadVars *tv,
1703
                    DetectEngineCtx *de_ctx,
1704
                    DetectEngineThreadCtx *det_ctx,
1705
                    Packet *p,
1706
                    Flow *f,
1707
                    DetectRunScratchpad *scratch)
1708
{
484,236✔
1709
    const uint8_t flow_flags = scratch->flow_flags;
484,236✔
1710
    const SigGroupHead * const sgh = scratch->sgh;
484,236✔
1711
    void * const alstate = f->alstate;
484,236✔
1712
    const uint8_t ipproto = f->proto;
484,236✔
1713
    const AppProto alproto = f->alproto;
484,236✔
1714

1715
    const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
484,236✔
1716
    uint64_t tx_id_min = AppLayerParserGetTransactionInspectId(f->alparser, flow_flags);
484,236✔
1717
    const int tx_end_state = AppLayerParserGetStateProgressCompletionStatus(alproto, flow_flags);
484,236✔
1718

1719
    AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(ipproto, alproto);
484,236✔
1720
    AppLayerGetTxIterState state = { 0 };
484,236✔
1721

1722
    uint32_t fw_verdicted = 0;
484,236✔
1723
    uint32_t tx_inspected = 0;
484,236✔
1724
    const bool have_fw_rules = EngineModeIsFirewall();
484,236✔
1725

1726
    SCLogDebug("packet %" PRIu64, PcapPacketCntGet(p));
484,236✔
1727

1728
    while (1) {
8,973,600✔
1729
        AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state);
8,973,600✔
1730
        if (ires.tx_ptr == NULL)
8,973,600✔
1731
            break;
214,853✔
1732

1733
        DetectTransaction tx =
8,758,747✔
1734
                GetDetectTx(ipproto, alproto, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags);
8,758,747✔
1735
        if (tx.tx_ptr == NULL) {
8,758,747✔
1736
            SCLogDebug("%p/%"PRIu64" no transaction to inspect",
8,438,973✔
1737
                    tx.tx_ptr, tx_id_min);
8,438,973✔
1738

1739
            tx_id_min++; // next (if any) run look for +1
8,438,973✔
1740
            goto next;
8,438,973✔
1741
        }
8,438,973✔
1742
        tx_id_min = tx.tx_id + 1; // next look for cur + 1
319,774✔
1743
        tx_inspected++;
319,774✔
1744

1745
        SCLogDebug("%p/%" PRIu64 " txd flags %02x", tx.tx_ptr, tx_id_min, tx.tx_data_ptr->flags);
319,774✔
1746

1747
        det_ctx->tx_id = tx.tx_id;
319,774✔
1748
        det_ctx->tx_id_set = true;
319,774✔
1749
        det_ctx->p = p;
319,774✔
1750

1751
        bool do_sort = false; // do we need to sort the tx candidate list?
319,774✔
1752
        uint32_t array_idx = 0;
319,774✔
1753
        uint32_t total_rules = det_ctx->match_array_cnt;
319,774✔
1754
        total_rules += (tx.de_state ? tx.de_state->cnt : 0);
319,774✔
1755

1756
        /* run prefilter engines and merge results into a candidates array */
1757
        if (sgh->tx_engines) {
319,774✔
1758
            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
266,304✔
1759
            DetectRunPrefilterTx(det_ctx, sgh, p, ipproto, flow_flags, alproto,
266,304✔
1760
                    alstate, &tx);
266,304✔
1761
            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
266,304✔
1762
            SCLogDebug("%p/%"PRIu64" rules added from prefilter: %u candidates",
266,304✔
1763
                    tx.tx_ptr, tx.tx_id, det_ctx->pmq.rule_id_array_cnt);
266,304✔
1764

1765
            total_rules += det_ctx->pmq.rule_id_array_cnt;
266,304✔
1766
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
266,304✔
1767
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
430✔
1768
            }
430✔
1769

1770
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
387,330✔
1771
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
121,026✔
1772
                const SigIntId id = s->iid;
121,026✔
1773
                det_ctx->tx_candidates[array_idx].s = s;
121,026✔
1774
                det_ctx->tx_candidates[array_idx].id = id;
121,026✔
1775
                det_ctx->tx_candidates[array_idx].flags = NULL;
121,026✔
1776
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
121,026✔
1777
                array_idx++;
121,026✔
1778
            }
121,026✔
1779
            PMQ_RESET(&det_ctx->pmq);
266,304✔
1780
        } else {
266,304✔
1781
            if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) {
53,470✔
1782
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
6✔
1783
            }
6✔
1784
        }
53,470✔
1785

1786
        /* merge 'state' rules from the regular prefilter */
1787
#ifdef PROFILING
1788
        uint32_t x = array_idx;
1789
#endif
1790
        RuleMatchCandidateMergeStateRules(det_ctx, &array_idx);
319,774✔
1791

1792
        /* merge stored state into results */
1793
        if (tx.de_state != NULL) {
319,774✔
1794
            const uint32_t old = array_idx;
21,885✔
1795

1796
            /* if tx.de_state->flags has 'new file' set and sig below has
1797
             * 'file inspected' flag, reset the file part of the state */
1798
            const bool have_new_file = (tx.de_state->flags & DETECT_ENGINE_STATE_FLAG_FILE_NEW);
21,885✔
1799
            if (have_new_file) {
21,885✔
1800
                SCLogDebug("%p/%"PRIu64" destate: need to consider new file",
116✔
1801
                        tx.tx_ptr, tx.tx_id);
116✔
1802
                tx.de_state->flags &= ~DETECT_ENGINE_STATE_FLAG_FILE_NEW;
116✔
1803
            }
116✔
1804

1805
            SigIntId state_cnt = 0;
21,885✔
1806
            DeStateStore *tx_store = tx.de_state->head;
21,885✔
1807
            for (; tx_store != NULL; tx_store = tx_store->next) {
30,707✔
1808
                SCLogDebug("tx_store %p", tx_store);
8,822✔
1809

1810
                SigIntId store_cnt = 0;
8,822✔
1811
                for (store_cnt = 0;
8,822✔
1812
                        store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx.de_state->cnt;
18,855✔
1813
                        store_cnt++, state_cnt++)
10,033✔
1814
                {
10,033✔
1815
                    DeStateStoreItem *item = &tx_store->store[store_cnt];
10,033✔
1816
                    SCLogDebug("rule id %u, inspect_flags %u", item->sid, item->flags);
10,033✔
1817
                    if (have_new_file && (item->flags & DE_STATE_FLAG_FILE_INSPECT)) {
10,033✔
1818
                        /* remove part of the state. File inspect engine will now
1819
                         * be able to run again */
1820
                        item->flags &= ~(DE_STATE_FLAG_SIG_CANT_MATCH|DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_FILE_INSPECT);
2✔
1821
                        SCLogDebug("rule id %u, post file reset inspect_flags %u", item->sid, item->flags);
2✔
1822
                    }
2✔
1823
                    det_ctx->tx_candidates[array_idx].s = de_ctx->sig_array[item->sid];
10,033✔
1824
                    det_ctx->tx_candidates[array_idx].id = item->sid;
10,033✔
1825
                    det_ctx->tx_candidates[array_idx].flags = &item->flags;
10,033✔
1826
                    det_ctx->tx_candidates[array_idx].stream_reset = 0;
10,033✔
1827
                    array_idx++;
10,033✔
1828
                }
10,033✔
1829
            }
8,822✔
1830
            do_sort |= (old && old != array_idx); // sort if continue list adds sids
21,885✔
1831
            SCLogDebug("%p/%" PRIu64 " rules added from 'continue' list: %u", tx.tx_ptr, tx.tx_id,
21,885✔
1832
                    array_idx - old);
21,885✔
1833
        }
21,885✔
1834
        if (do_sort) {
319,774✔
1835
            qsort(det_ctx->tx_candidates, array_idx, sizeof(RuleMatchCandidateTx),
7,366✔
1836
                    DetectRunTxSortHelper);
7,366✔
1837
        }
7,366✔
1838

1839
#ifdef PROFILING
1840
        if (array_idx >= de_ctx->profile_match_logging_threshold)
1841
            RulesDumpTxMatchArray(det_ctx, scratch->sgh, p, tx.tx_id, array_idx, x);
1842
#endif
1843

1844
#ifdef DEBUG
1845
        for (uint32_t i = 0; i < array_idx; i++) {
1846
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
1847
            const Signature *s = det_ctx->tx_candidates[i].s;
1848
            SCLogDebug("%u: sid %u flags %p", i, s->id, can->flags);
1849
        }
1850
#endif
1851
        bool skip_fw_hook = false;
319,774✔
1852
        uint8_t skip_before_progress = 0;
319,774✔
1853
        bool fw_next_progress_missing = false;
319,774✔
1854

1855
        /* if there are no rules / rule candidates, make sure we don't
1856
         * invoke the default drop */
1857
        if (have_fw_rules && array_idx == 0 && (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT)) {
319,774✔
UNCOV
1858
            fw_verdicted++;
×
1859

1860
            /* current tx is the last we have, append a blank accept:packet */
UNCOV
1861
            if (total_txs == tx.tx_id + 1) {
×
UNCOV
1862
                DetectRunAppendDefaultAccept(det_ctx, p);
×
UNCOV
1863
                return;
×
UNCOV
1864
            }
×
1865
            goto next;
×
UNCOV
1866
        }
×
1867

1868
        bool tx_fw_verdict = false;
319,774✔
1869
        /* run rules: inspect the match candidates */
1870
        for (uint32_t i = 0; i < array_idx; i++) {
456,912✔
1871
            RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
137,138✔
1872
            const Signature *s = det_ctx->tx_candidates[i].s;
137,138✔
1873
            uint32_t *inspect_flags = det_ctx->tx_candidates[i].flags;
137,138✔
1874
            bool break_out_of_app_filter = false;
137,138✔
1875

1876
            SCLogDebug("%" PRIu64 ": sid:%u: %s tx %u/%u/%u sig %u", PcapPacketCntGet(p), s->id,
137,138✔
1877
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.tx_progress,
137,138✔
1878
                    tx.detect_progress, tx.detect_progress_orig, s->app_progress_hook);
137,138✔
1879

1880
            /* deduplicate: rules_array is sorted, but not deduplicated:
1881
             * both mpm and stored state could give us the same sid.
1882
             * As they are back to back in that case we can check for it
1883
             * here. We select the stored state one as that comes first
1884
             * in the array. */
1885
            while ((i + 1) < array_idx &&
145,706✔
1886
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
145,706✔
1887
                SCLogDebug("%p/%" PRIu64 " inspecting SKIP NEXT: sid %u (%u), flags %08x",
8,568✔
1888
                        tx.tx_ptr, tx.tx_id, s->id, s->iid, inspect_flags ? *inspect_flags : 0);
8,568✔
1889
                i++;
8,568✔
1890
            }
8,568✔
1891

1892
            /* skip fw rules if we're in accept:tx mode */
1893
            if (have_fw_rules && (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT)) {
137,138✔
1894
                /* append a blank accept:packet action for the APP_LAYER_TX_ACCEPT,
1895
                 * if this is the last tx */
UNCOV
1896
                if (!tx_fw_verdict) {
×
UNCOV
1897
                    const bool accept_tx_applies_to_packet = total_txs == tx.tx_id + 1;
×
UNCOV
1898
                    if (accept_tx_applies_to_packet) {
×
UNCOV
1899
                        SCLogDebug("accept:(tx|hook): should be applied to the packet");
×
UNCOV
1900
                        DetectRunAppendDefaultAccept(det_ctx, p);
×
UNCOV
1901
                    }
×
UNCOV
1902
                }
×
UNCOV
1903
                tx_fw_verdict = true;
×
1904

UNCOV
1905
                if (s->flags & SIG_FLAG_FIREWALL) {
×
UNCOV
1906
                    SCLogDebug("APP_LAYER_TX_ACCEPT, so skip rule");
×
UNCOV
1907
                    continue;
×
UNCOV
1908
                }
×
1909

1910
                /* threat detect rules will be inspected */
UNCOV
1911
            }
×
1912

1913
            SCLogDebug("%p/%" PRIu64 " inspecting: sid %u (%u), flags %08x", tx.tx_ptr, tx.tx_id,
137,138✔
1914
                    s->id, s->iid, inspect_flags ? *inspect_flags : 0);
137,138✔
1915

1916
            if (inspect_flags) {
137,138✔
1917
                if (*inspect_flags & DE_STATE_FLAG_FULL_INSPECT) {
10,033✔
1918
                    SCLogDebug("%p/%" PRIu64
4,839✔
1919
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_FULL_INSPECT",
4,839✔
1920
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
4,839✔
1921

1922
                    /* if we're still in the same progress state as an earlier full
1923
                     * match, we need to apply the same accept */
1924
                    if (have_fw_rules && (s->flags & SIG_FLAG_FIREWALL) &&
4,839✔
1925
                            (s->action & ACTION_ACCEPT) && s->app_progress_hook == tx.tx_progress) {
4,839✔
UNCOV
1926
                        const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
×
UNCOV
1927
                        break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
×
UNCOV
1928
                                fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
×
UNCOV
1929
                                &skip_before_progress);
×
UNCOV
1930
                        if (fw_accept_to_packet)
×
UNCOV
1931
                            DetectRunAppendDefaultAccept(det_ctx, p);
×
UNCOV
1932
                        if (break_out_of_app_filter)
×
1933
                            break;
×
UNCOV
1934
                    }
×
1935
                    continue;
4,839✔
1936
                }
4,839✔
1937
                if (*inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
5,194✔
1938
                    SCLogDebug("%p/%" PRIu64
5,067✔
1939
                               " inspecting: sid %u (%u), flags %08x DE_STATE_FLAG_SIG_CANT_MATCH",
5,067✔
1940
                            tx.tx_ptr, tx.tx_id, s->id, s->iid, *inspect_flags);
5,067✔
1941
                    continue;
5,067✔
1942
                }
5,067✔
1943
            }
5,194✔
1944

1945
            if (inspect_flags) {
127,232✔
1946
                /* continue previous inspection */
1947
                SCLogDebug("%p/%" PRIu64 " Continuing sid %u", tx.tx_ptr, tx.tx_id, s->id);
127✔
1948
            } else {
127,105✔
1949
                /* start new inspection */
1950
                SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id);
127,105✔
1951
            }
127,105✔
1952

1953
            bool last_for_progress = false;
127,232✔
1954
            if (have_fw_rules) {
127,232✔
UNCOV
1955
                int fw_r = DetectRunTxCheckFirewallPolicy(det_ctx, p, f, &tx, s, i, array_idx,
×
UNCOV
1956
                        &skip_fw_hook, skip_before_progress, &last_for_progress,
×
UNCOV
1957
                        &fw_next_progress_missing);
×
UNCOV
1958
                if (fw_r == -1)
×
UNCOV
1959
                    continue;
×
UNCOV
1960
                if (fw_r == 1)
×
1961
                    break;
×
UNCOV
1962
            }
×
1963

1964
            /* call individual rule inspection */
1965
            RULE_PROFILING_START(p);
127,232✔
1966
            const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags,
127,232✔
1967
                    alstate, &tx, s, inspect_flags, can, scratch);
127,232✔
1968
            if (r == 1) {
127,232✔
1969
                /* match */
1970
                DetectRunPostMatch(tv, det_ctx, p, s);
30,389✔
1971

1972
                /* see if we need to apply tx/hook accept to the packet. This can be needed when
1973
                 * we've completed the inspection so far for an incomplete tx, and an accept:tx or
1974
                 * accept:hook is the last match.*/
1975
                const bool fw_accept_to_packet = ApplyAcceptToPacket(total_txs, &tx, s);
30,389✔
1976

1977
                uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_TX);
30,389✔
1978
                if (fw_accept_to_packet) {
30,389✔
UNCOV
1979
                    SCLogDebug("accept:(tx|hook): should be applied to the packet");
×
UNCOV
1980
                    alert_flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET;
×
UNCOV
1981
                }
×
1982

1983
                SCLogDebug(
30,389✔
1984
                        "%p/%" PRIu64 " sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->iid);
30,389✔
1985
                AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags);
30,389✔
1986

1987
                if ((s->flags & SIG_FLAG_FIREWALL) && (s->action & ACTION_ACCEPT)) {
30,389✔
UNCOV
1988
                    break_out_of_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
×
UNCOV
1989
                            fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
×
UNCOV
1990
                            &skip_before_progress);
×
UNCOV
1991
                }
×
1992
            } else if (last_for_progress) {
96,843✔
UNCOV
1993
                SCLogDebug("sid %u: not a match: %s rule, last_for_progress %s", s->id,
×
UNCOV
1994
                        (s->flags & SIG_FLAG_FIREWALL) ? "firewall" : "regular",
×
UNCOV
1995
                        BOOL2STR(last_for_progress));
×
UNCOV
1996
                if (s->flags & SIG_FLAG_FIREWALL) {
×
UNCOV
1997
                    SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
×
UNCOV
1998
                            flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
×
1999
                    /* if this rule was the last for our progress state, and it didn't match,
2000
                     * we have to invoke the default drop policy. */
UNCOV
2001
                    PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
×
UNCOV
2002
                    p->flow->flags |= FLOW_ACTION_DROP;
×
UNCOV
2003
                    break_out_of_app_filter = true;
×
UNCOV
2004
                    tx_fw_verdict = true;
×
UNCOV
2005
                }
×
UNCOV
2006
            }
×
2007
            DetectVarProcessList(det_ctx, p->flow, p);
127,232✔
2008
            RULE_PROFILING_END(det_ctx, s, r, p);
127,232✔
2009

2010
            if (det_ctx->post_rule_work_queue.len > 0) {
127,232✔
2011
                SCLogDebug("%p/%" PRIu64 " post_rule_work_queue len %u", tx.tx_ptr, tx.tx_id,
17✔
2012
                        det_ctx->post_rule_work_queue.len);
17✔
2013
                /* run post match prefilter engines on work queue */
2014
                PrefilterPostRuleMatch(det_ctx, scratch->sgh, p, f);
17✔
2015

2016
                uint32_t prev_array_idx = array_idx;
17✔
2017
                for (uint32_t j = 0; j < det_ctx->pmq.rule_id_array_cnt; j++) {
35✔
2018
                    const Signature *ts = de_ctx->sig_array[det_ctx->pmq.rule_id_array[j]];
18✔
2019
                    if (ts->app_inspect != NULL) {
18✔
2020
                        const SigIntId id = ts->iid;
18✔
2021
                        det_ctx->tx_candidates[array_idx].s = ts;
18✔
2022
                        det_ctx->tx_candidates[array_idx].id = id;
18✔
2023
                        det_ctx->tx_candidates[array_idx].flags = NULL;
18✔
2024
                        det_ctx->tx_candidates[array_idx].stream_reset = 0;
18✔
2025
                        array_idx++;
18✔
2026

2027
                        SCLogDebug("%p/%" PRIu64 " rule %u (%u) added from 'post match' prefilter",
18✔
2028
                                tx.tx_ptr, tx.tx_id, ts->id, id);
18✔
2029
                    }
18✔
2030
                }
18✔
2031
                SCLogDebug("%p/%" PRIu64 " rules added from 'post match' prefilter: %u", tx.tx_ptr,
17✔
2032
                        tx.tx_id, array_idx - prev_array_idx);
17✔
2033
                if (prev_array_idx != array_idx) {
17✔
2034
                    /* sort, but only part of array we're still going to process */
2035
                    qsort(det_ctx->tx_candidates + i, array_idx - i, sizeof(RuleMatchCandidateTx),
17✔
2036
                            DetectRunTxSortHelper);
17✔
2037
                }
17✔
2038
                det_ctx->post_rule_work_queue.len = 0;
17✔
2039
                PMQ_RESET(&det_ctx->pmq);
17✔
2040
            }
17✔
2041

2042
            if (break_out_of_app_filter)
127,232✔
UNCOV
2043
                break;
×
2044
        }
127,232✔
2045
        if (tx_fw_verdict)
319,774✔
UNCOV
2046
            fw_verdicted++;
×
2047

2048
        det_ctx->tx_id = 0;
319,774✔
2049
        det_ctx->tx_id_set = false;
319,774✔
2050
        det_ctx->p = NULL;
319,774✔
2051

2052
        /* see if we have any updated state to store in the tx */
2053

2054
        /* this side of the tx is done */
2055
        if (tx.tx_progress >= tx.tx_end_state) {
319,774✔
2056
            SCLogDebug("%" PRIu64 ": %s tx done", PcapPacketCntGet(p),
206,745✔
2057
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
206,745✔
2058
            const uint8_t inspected_flag = (flow_flags & STREAM_TOSERVER)
206,745✔
2059
                                                   ? APP_LAYER_TX_INSPECTED_TS
206,745✔
2060
                                                   : APP_LAYER_TX_INSPECTED_TC;
206,745✔
2061
            tx.tx_data_ptr->flags |= inspected_flag;
206,745✔
2062
            SCLogDebug("%p/%" PRIu64 " tx is done for direction %s. Progress %02x", tx.tx_ptr,
206,745✔
2063
                    tx.tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
206,745✔
2064
                    tx.detect_progress);
206,745✔
2065
        }
206,745✔
2066

2067
        if (tx.detect_progress != tx.detect_progress_orig) {
319,774✔
2068
            SCLogDebug("%" PRIu64 ": %s tx state change %u -> %u", PcapPacketCntGet(p),
149,153✔
2069
                    flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", tx.detect_progress_orig,
149,153✔
2070
                    tx.detect_progress);
149,153✔
2071
            SCLogDebug("%p/%" PRIu64 " Storing new progress %02x (was %02x)", tx.tx_ptr, tx.tx_id,
149,153✔
2072
                    tx.detect_progress, tx.detect_progress_orig);
149,153✔
2073

2074
            StoreDetectProgress(&tx, flow_flags, tx.detect_progress);
149,153✔
2075
        }
149,153✔
2076

2077
        InspectionBufferClean(det_ctx);
319,774✔
2078

2079
    next:
8,758,747✔
2080
        if (!ires.has_next)
8,758,747✔
2081
            break;
269,383✔
2082
    }
8,758,747✔
2083

2084
    /* apply default policy if there were txs to inspect, we have fw rules and non of the rules
2085
     * applied a policy. */
2086
    SCLogDebug("packet %" PRIu64 ": tx_inspected %u fw_verdicted %u", PcapPacketCntGet(p),
484,236✔
2087
            tx_inspected, fw_verdicted);
484,236✔
2088
    if (tx_inspected && have_fw_rules && tx_inspected != fw_verdicted) {
484,236✔
UNCOV
2089
        SCLogDebug("%" PRIu64 ": %s default drop", PcapPacketCntGet(p),
×
UNCOV
2090
                flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
×
UNCOV
2091
        PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
×
UNCOV
2092
        p->flow->flags |= FLOW_ACTION_DROP;
×
UNCOV
2093
        return;
×
UNCOV
2094
    }
×
2095
    /* if all tables have been bypassed, we accept:packet */
2096
    if (tx_inspected == 0 && fw_verdicted == 0 && have_fw_rules) {
484,236✔
UNCOV
2097
        DetectRunAppendDefaultAccept(det_ctx, p);
×
UNCOV
2098
    }
×
2099
}
484,236✔
2100

2101
static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2102
        Packet *p, Flow *f, DetectRunScratchpad *scratch)
2103
{
574,371✔
2104
    const SigGroupHead *const sgh = scratch->sgh;
574,371✔
2105
    const AppProto alproto = f->alproto;
574,371✔
2106

2107
    /* for TCP, limit inspection to pseudo packets or real packet that did
2108
     * an app-layer update. */
2109
    if (p->proto == IPPROTO_TCP && !PKT_IS_PSEUDOPKT(p) &&
574,371✔
2110
            ((PKT_IS_TOSERVER(p) && (f->flags & FLOW_TS_APP_UPDATED) == 0) ||
574,371✔
2111
                    (PKT_IS_TOCLIENT(p) && (f->flags & FLOW_TC_APP_UPDATED) == 0))) {
539,174✔
2112
        SCLogDebug("pcap_cnt %" PRIu64 ": %s: skip frame inspection for TCP w/o APP UPDATE",
113,369✔
2113
                PcapPacketCntGet(p), PKT_IS_TOSERVER(p) ? "toserver" : "toclient");
113,369✔
2114
        return;
113,369✔
2115
    }
113,369✔
2116
    FramesContainer *frames_container = AppLayerFramesGetContainer(f);
461,002✔
2117
    if (frames_container == NULL) {
461,002✔
2118
        return;
31,474✔
2119
    }
31,474✔
2120
    Frames *frames;
429,528✔
2121
    if (PKT_IS_TOSERVER(p)) {
429,528✔
2122
        frames = &frames_container->toserver;
220,160✔
2123
    } else {
220,160✔
2124
        frames = &frames_container->toclient;
209,368✔
2125
    }
209,368✔
2126

2127
    for (uint32_t idx = 0; idx < frames->cnt; idx++) {
11,592,691✔
2128
        SCLogDebug("frame %u", idx);
11,163,163✔
2129
        Frame *frame = FrameGetByIndex(frames, idx);
11,163,163✔
2130
        if (frame == NULL) {
11,163,163✔
2131
            continue;
×
2132
        }
×
2133

2134
        det_ctx->frame_inspect_progress = 0;
11,163,163✔
2135
        uint32_t array_idx = 0;
11,163,163✔
2136
        uint32_t total_rules = det_ctx->match_array_cnt;
11,163,163✔
2137

2138
        /* run prefilter engines and merge results into a candidates array */
2139
        if (sgh->frame_engines) {
11,163,163✔
2140
            //            PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
2141
            DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto);
833,625✔
2142
            //            PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX);
2143
            SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id,
833,625✔
2144
                    det_ctx->pmq.rule_id_array_cnt);
833,625✔
2145

2146
            total_rules += det_ctx->pmq.rule_id_array_cnt;
833,625✔
2147

2148
            if (!(RuleMatchCandidateTxArrayHasSpace(
833,625✔
2149
                        det_ctx, total_rules))) { // TODO is it safe to overload?
833,625✔
2150
                RuleMatchCandidateTxArrayExpand(det_ctx, total_rules);
9✔
2151
            }
9✔
2152

2153
            for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) {
861,991✔
2154
                const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]];
28,366✔
2155
                const SigIntId id = s->iid;
28,366✔
2156
                det_ctx->tx_candidates[array_idx].s = s;
28,366✔
2157
                det_ctx->tx_candidates[array_idx].id = id;
28,366✔
2158
                det_ctx->tx_candidates[array_idx].flags = NULL;
28,366✔
2159
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
28,366✔
2160
                array_idx++;
28,366✔
2161
            }
28,366✔
2162
            PMQ_RESET(&det_ctx->pmq);
833,625✔
2163
        }
833,625✔
2164
        /* merge 'state' rules from the regular prefilter */
2165
        uint32_t x = array_idx;
11,163,163✔
2166
        for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) {
12,653,514✔
2167
            const Signature *s = det_ctx->match_array[i];
1,490,351✔
2168
            if (s->frame_inspect != NULL) {
1,490,351✔
2169
                const SigIntId id = s->iid;
×
2170
                det_ctx->tx_candidates[array_idx].s = s;
×
2171
                det_ctx->tx_candidates[array_idx].id = id;
×
2172
                det_ctx->tx_candidates[array_idx].flags = NULL;
×
2173
                det_ctx->tx_candidates[array_idx].stream_reset = 0;
×
2174
                array_idx++;
×
2175

2176
                SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id,
×
2177
                        s->id, id);
×
2178
            }
×
2179
        }
1,490,351✔
2180
        SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id,
11,163,163✔
2181
                array_idx - x);
11,163,163✔
2182
        (void)x;
11,163,163✔
2183

2184
        /* run rules: inspect the match candidates */
2185
        for (uint32_t i = 0; i < array_idx; i++) {
11,191,447✔
2186
            const Signature *s = det_ctx->tx_candidates[i].s;
28,284✔
2187

2188
            /* deduplicate: rules_array is sorted, but not deduplicated.
2189
             * As they are back to back in that case we can check for it
2190
             * here. We select the stored state one as that comes first
2191
             * in the array. */
2192
            while ((i + 1) < array_idx &&
28,366✔
2193
                    det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) {
28,366✔
2194
                i++;
82✔
2195
            }
82✔
2196
            SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->iid);
28,284✔
2197

2198
            /* start new inspection */
2199
            SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id);
28,284✔
2200

2201
            /* call individual rule inspection */
2202
            RULE_PROFILING_START(p);
28,284✔
2203
            bool r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags);
28,284✔
2204
            if (r) {
28,284✔
2205
                r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame);
28,281✔
2206
                if (r) {
28,281✔
2207
                    /* match */
2208
                    DetectRunPostMatch(tv, det_ctx, p, s);
1,718✔
2209

2210
                    uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME);
1,718✔
2211
                    det_ctx->frame_id = frame->id;
1,718✔
2212
                    SCLogDebug(
1,718✔
2213
                            "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->iid);
1,718✔
2214
                    if (frame->flags & FRAME_FLAG_TX_ID_SET) {
1,718✔
2215
                        alert_flags |= PACKET_ALERT_FLAG_TX;
414✔
2216
                    }
414✔
2217
                    AlertQueueAppend(det_ctx, s, p, frame->tx_id, alert_flags);
1,718✔
2218
                }
1,718✔
2219
            }
28,281✔
2220
            DetectVarProcessList(det_ctx, p->flow, p);
28,284✔
2221
            RULE_PROFILING_END(det_ctx, s, r, p);
28,284✔
2222
        }
28,284✔
2223

2224
        /* update Frame::inspect_progress here instead of in the code above. The reason is that a
2225
         * frame might be used more than once in buffers with transforms. */
2226
        if (frame->inspect_progress < det_ctx->frame_inspect_progress) {
11,163,163✔
2227
            frame->inspect_progress = det_ctx->frame_inspect_progress;
8,825✔
2228
            SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress);
8,825✔
2229
        } else {
11,154,338✔
2230
            SCLogDebug(
11,154,338✔
2231
                    "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress);
11,154,338✔
2232
        }
11,154,338✔
2233

2234
        SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id);
11,163,163✔
2235
        InspectionBufferClean(det_ctx);
11,163,163✔
2236
    }
11,163,163✔
2237
}
429,528✔
2238

2239
static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id)
2240
{
×
2241
    /* technically we need to pass a DetectEngineThreadCtx struct with the
2242
     * tenant_id member. But as that member is the first in the struct, we
2243
     * can use the id directly. */
2244
    return HashTableLookup(h, &id, 0);
×
2245
}
×
2246

2247
static void DetectFlow(ThreadVars *tv,
2248
                       DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2249
                       Packet *p)
2250
{
1,278,951✔
2251
    Flow *const f = p->flow;
1,278,951✔
2252

2253
    /* we check the flow drop here, and not the packet drop. This is
2254
     * to allow stream engine "invalid" drop packets to still be
2255
     * evaluated by the stream event rules. */
2256
    if (f->flags & FLOW_ACTION_DROP) {
1,278,951✔
2257
        DEBUG_VALIDATE_BUG_ON(!(PKT_IS_PSEUDOPKT(p)) && !PacketCheckAction(p, ACTION_DROP));
2,944✔
2258
        SCReturn;
2,944✔
2259
    }
2,944✔
2260

2261
    /* in firewall mode, we still need to run the fw rulesets even for exception policy pass */
2262
    bool skip = false;
1,278,951✔
2263
    if (EngineModeIsFirewall()) {
1,276,007✔
UNCOV
2264
        skip = (f->flags & (FLOW_ACTION_ACCEPT));
×
2265

2266
    } else {
1,276,007✔
2267
        skip = (p->flags & PKT_NOPACKET_INSPECTION || f->flags & (FLOW_ACTION_PASS));
1,276,007✔
2268
    }
1,276,007✔
2269
    if (skip) {
1,276,007✔
2270
        /* enforce prior accept:flow */
2271
        if (f->flags & FLOW_ACTION_ACCEPT) {
11,405✔
UNCOV
2272
            p->action |= ACTION_ACCEPT;
×
UNCOV
2273
        }
×
2274
        /* hack: if we are in pass the entire flow mode, we need to still
2275
         * update the inspect_id forward. So test for the condition here,
2276
         * and call the update code if necessary. */
2277
        const int pass = (f->flags & (FLOW_ACTION_PASS | FLOW_ACTION_ACCEPT));
11,405✔
2278
        if (pass) {
11,405✔
2279
            uint8_t flags = STREAM_FLAGS_FOR_PACKET(p);
11,405✔
2280
            flags = FlowGetDisruptionFlags(f, flags);
11,405✔
2281
            if (f->alstate) {
11,405✔
2282
                AppLayerParserSetTransactionInspectId(f, f->alparser, f->alstate, flags, true);
8,969✔
2283
            }
8,969✔
2284
        }
11,405✔
2285
        SCLogDebug("p->pcap %" PRIu64 ": no detection on packet, "
11,405✔
2286
                   "PKT_NOPACKET_INSPECTION is set",
11,405✔
2287
                PcapPacketCntGet(p));
11,405✔
2288
        return;
11,405✔
2289
    }
11,405✔
2290

2291
    /* see if the packet matches one or more of the sigs */
2292
    DetectRun(tv, de_ctx, det_ctx, p);
1,264,602✔
2293
}
1,264,602✔
2294

2295

2296
static void DetectNoFlow(ThreadVars *tv,
2297
                         DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2298
                         Packet *p)
2299
{
175,568✔
2300
    /* No need to perform any detection on this packet, if the given flag is set.*/
2301
    if ((p->flags & PKT_NOPACKET_INSPECTION) || (PacketCheckAction(p, ACTION_DROP))) {
175,568✔
UNCOV
2302
        return;
×
UNCOV
2303
    }
×
2304

2305
    /* see if the packet matches one or more of the sigs */
2306
    DetectRun(tv, de_ctx, det_ctx, p);
175,568✔
2307
}
175,568✔
2308

2309
uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
UNCOV
2310
{
×
UNCOV
2311
    const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
×
UNCOV
2312
    const SigGroupHead *sgh = de_ctx->pre_flow_sgh;
×
2313

UNCOV
2314
    SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
×
UNCOV
2315
    DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
×
UNCOV
2316
    return p->action;
×
UNCOV
2317
}
×
2318

2319
uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
UNCOV
2320
{
×
UNCOV
2321
    const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
×
UNCOV
2322
    const int direction = (PKT_IS_TOCLIENT(p) != 0);
×
UNCOV
2323
    const SigGroupHead *sgh = de_ctx->pre_stream_sgh[direction];
×
2324

UNCOV
2325
    SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
×
UNCOV
2326
    DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
×
UNCOV
2327
    return p->action;
×
UNCOV
2328
}
×
2329

2330
/** \brief Detection engine thread wrapper.
2331
 *  \param tv thread vars
2332
 *  \param p packet to inspect
2333
 *  \param data thread specific data
2334
 *  \param pq packet queue
2335
 *  \retval TM_ECODE_FAILED error
2336
 *  \retval TM_ECODE_OK ok
2337
 */
2338
TmEcode Detect(ThreadVars *tv, Packet *p, void *data)
2339
{
1,454,519✔
2340
    DEBUG_VALIDATE_PACKET(p);
1,454,519✔
2341

2342
    DetectEngineCtx *de_ctx = NULL;
1,454,519✔
2343
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
1,454,519✔
2344
    if (det_ctx == NULL) {
1,454,519✔
2345
        printf("ERROR: Detect has no thread ctx\n");
×
2346
        goto error;
×
2347
    }
×
2348

2349
    if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
1,454,519✔
2350
        (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
18,012✔
2351
        SCLogDebug("Detect Engine using new det_ctx - %p",
18,012✔
2352
                  det_ctx);
18,012✔
2353
    }
18,012✔
2354

2355
    /* if in MT mode _and_ we have tenants registered, use
2356
     * MT logic. */
2357
    if (det_ctx->mt_det_ctxs_cnt > 0 && det_ctx->TenantGetId != NULL)
1,454,519✔
UNCOV
2358
    {
×
UNCOV
2359
        uint32_t tenant_id = p->tenant_id;
×
UNCOV
2360
        if (tenant_id == 0)
×
UNCOV
2361
            tenant_id = det_ctx->TenantGetId(det_ctx, p);
×
UNCOV
2362
        if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) {
×
2363
            p->tenant_id = tenant_id;
×
2364
            det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id);
×
2365
            if (det_ctx == NULL)
×
2366
                return TM_ECODE_OK;
×
2367
            de_ctx = det_ctx->de_ctx;
×
2368
            if (de_ctx == NULL)
×
2369
                return TM_ECODE_OK;
×
2370

2371
            if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) {
×
2372
                (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1);
×
2373
                SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id);
×
2374
            }
×
UNCOV
2375
        } else {
×
2376
            /* use default if no tenants are registered for this packet */
UNCOV
2377
            de_ctx = det_ctx->de_ctx;
×
UNCOV
2378
        }
×
2379
    } else {
1,454,519✔
2380
        de_ctx = det_ctx->de_ctx;
1,454,519✔
2381
    }
1,454,519✔
2382

2383
    if (p->flow) {
1,454,519✔
2384
        DetectFlow(tv, de_ctx, det_ctx, p);
1,278,951✔
2385
    } else {
1,278,951✔
2386
        DetectNoFlow(tv, de_ctx, det_ctx, p);
175,568✔
2387
    }
175,568✔
2388

2389
#ifdef PROFILE_RULES
2390
    /* aggregate statistics */
2391
    struct timeval ts;
2392
    gettimeofday(&ts, NULL);
2393
    if (ts.tv_sec != det_ctx->rule_perf_last_sync) {
2394
        SCProfilingRuleThreatAggregate(det_ctx);
2395
        det_ctx->rule_perf_last_sync = ts.tv_sec;
2396
    }
2397
#endif
2398

2399
    return TM_ECODE_OK;
1,454,519✔
2400
error:
×
2401
    return TM_ECODE_FAILED;
×
2402
}
1,454,519✔
2403

2404
/** \brief disable file features we don't need
2405
 *  Called if we have no detection engine.
2406
 */
2407
void DisableDetectFlowFileFlags(Flow *f)
UNCOV
2408
{
×
UNCOV
2409
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOSERVER);
×
UNCOV
2410
    DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOCLIENT);
×
UNCOV
2411
}
×
2412

2413
#ifdef UNITTESTS
2414
/**
2415
 *  \brief wrapper for old tests
2416
 */
2417
void SigMatchSignatures(
2418
        ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
2419
{
2420
    if (p->flow) {
2421
        DetectFlow(tv, de_ctx, det_ctx, p);
2422
    } else {
2423
        DetectNoFlow(tv, de_ctx, det_ctx, p);
2424
    }
2425
}
2426
#endif
2427

2428
/*
2429
 * TESTS
2430
 */
2431

2432
#ifdef UNITTESTS
2433
#include "tests/detect.c"
2434
#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