Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/detect-fast-pattern.c
Line
Count
Source
1
/* Copyright (C) 2007-2021 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 Anoop Saldanha <anoopsaldanha@gmail.com>
22
 *
23
 * Implements the fast_pattern keyword
24
 */
25
26
#include "suricata-common.h"
27
#include "detect.h"
28
#include "flow.h"
29
#include "detect-content.h"
30
#include "detect-parse.h"
31
#include "detect-engine.h"
32
#include "detect-engine-mpm.h"
33
#include "detect-engine-build.h"
34
#include "detect-fast-pattern.h"
35
36
#include "util-error.h"
37
#include "util-byte.h"
38
#include "util-debug.h"
39
#include "util-unittest.h"
40
#include "util-unittest-helper.h"
41
42
79
#define PARSE_REGEX "^(\\s*only\\s*)|\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*$"
43
44
static DetectParseRegex parse_regex;
45
46
static int DetectFastPatternSetup(DetectEngineCtx *, Signature *, const char *);
47
#ifdef UNITTESTS
48
static void DetectFastPatternRegisterTests(void);
49
#endif
50
51
/* holds the list of sm match lists that need to be searched for a keyword
52
 * that has fp support */
53
static SCFPSupportSMList *g_fp_support_smlist_list = NULL;
54
55
/**
56
 * \brief Checks if a particular buffer is in the list
57
 *        of lists that need to be searched for a keyword that has fp support.
58
 *
59
 * \param list_id The list id.
60
 *
61
 * \retval 1 If supported.
62
 * \retval 0 If not.
63
 */
64
int FastPatternSupportEnabledForSigMatchList(const DetectEngineCtx *de_ctx,
65
        const int list_id)
66
773k
{
67
773k
    if (de_ctx->fp_support_smlist_list == NULL) {
68
0
        return 0;
69
0
    }
70
71
773k
    if (list_id == DETECT_SM_LIST_PMATCH)
72
178k
        return 1;
73
74
594k
    return DetectEngineBufferTypeSupportsMpmGetById(de_ctx, list_id);
75
773k
}
76
77
static void Add(SCFPSupportSMList **list, const int list_id, const int priority)
78
456k
{
79
456k
    SCFPSupportSMList *ip = NULL;
80
    /* insertion point - ip */
81
133M
    for (SCFPSupportSMList *tmp = *list; tmp != NULL; tmp = tmp->next) {
82
133M
        if (list_id == tmp->list_id) {
83
366k
            SCLogDebug("SM list already registered.");
84
366k
            return;
85
366k
        }
86
87
        /* We need a strict check to be sure that the current list
88
         * was not already registered
89
         * and other lists with the same priority hide it.
90
         */
91
132M
        if (priority < tmp->priority)
92
89.5k
            break;
93
94
132M
        ip = tmp;
95
132M
    }
96
97
90.0k
    if (*list == NULL) {
98
79
        SCFPSupportSMList *new = SCCalloc(1, sizeof(SCFPSupportSMList));
99
79
        if (unlikely(new == NULL))
100
0
            exit(EXIT_FAILURE);
101
79
        new->list_id = list_id;
102
79
        new->priority = priority;
103
104
79
        *list = new;
105
79
        return;
106
79
    }
107
108
89.9k
    SCFPSupportSMList *new = SCCalloc(1, sizeof(SCFPSupportSMList));
109
89.9k
    if (unlikely(new == NULL))
110
0
        exit(EXIT_FAILURE);
111
89.9k
    new->list_id = list_id;
112
89.9k
    new->priority = priority;
113
89.9k
    if (ip == NULL) {
114
195
        new->next = *list;
115
195
        *list = new;
116
89.7k
    } else {
117
89.7k
        new->next = ip->next;
118
89.7k
        ip->next = new;
119
89.7k
    }
120
89.9k
}
121
122
/**
123
 * \brief Lets one add a sm list id to be searched for potential fp supported
124
 *        keywords later.
125
 *
126
 * \param list_id SM list id.
127
 * \param priority Priority for this list.
128
 */
129
void SupportFastPatternForSigMatchList(int list_id, int priority)
130
28.0k
{
131
28.0k
    Add(&g_fp_support_smlist_list, list_id, priority);
132
28.0k
}
133
134
void DetectEngineRegisterFastPatternForId(DetectEngineCtx *de_ctx, int list_id, int priority)
135
428k
{
136
428k
    Add(&de_ctx->fp_support_smlist_list, list_id, priority);
137
428k
}
138
139
/**
140
 * \brief Registers the keywords(SMs) that should be given fp support.
141
 */
142
void SupportFastPatternForSigMatchTypes(void)
143
77
{
144
77
    SupportFastPatternForSigMatchList(DETECT_SM_LIST_PMATCH, 3);
145
146
    /* other types are handled by DetectMpmAppLayerRegister() */
147
77
}
148
149
void DetectEngineInitializeFastPatternList(DetectEngineCtx *de_ctx)
150
114k
{
151
114k
    SCFPSupportSMList *last = NULL;
152
20.2M
    for (SCFPSupportSMList *tmp = g_fp_support_smlist_list; tmp != NULL; tmp = tmp->next) {
153
20.0M
        SCFPSupportSMList *n = SCCalloc(1, sizeof(*n));
154
20.0M
        if (n == NULL) {
155
0
            FatalError("out of memory: %s", strerror(errno));
156
0
        }
157
20.0M
        n->list_id = tmp->list_id;
158
20.0M
        n->priority = tmp->priority;
159
160
        // append
161
20.0M
        if (de_ctx->fp_support_smlist_list == NULL) {
162
114k
            last = de_ctx->fp_support_smlist_list = n;
163
19.9M
        } else {
164
19.9M
            BUG_ON(last == NULL);
165
19.9M
            last->next = n;
166
19.9M
            last = n;
167
19.9M
        }
168
20.0M
    }
169
114k
}
170
171
void DetectEngineFreeFastPatternList(DetectEngineCtx *de_ctx)
172
114k
{
173
20.2M
    for (SCFPSupportSMList *tmp = de_ctx->fp_support_smlist_list; tmp != NULL;) {
174
20.1M
        SCFPSupportSMList *next = tmp->next;
175
20.1M
        SCFree(tmp);
176
20.1M
        tmp = next;
177
20.1M
    }
178
114k
    de_ctx->fp_support_smlist_list = NULL;
179
114k
}
180
181
/**
182
 * \brief Registration function for fast_pattern keyword
183
 */
184
void DetectFastPatternRegister(void)
185
79
{
186
79
    sigmatch_table[DETECT_FAST_PATTERN].name = "fast_pattern";
187
79
    sigmatch_table[DETECT_FAST_PATTERN].desc = "force using preceding content in the multi pattern matcher";
188
79
    sigmatch_table[DETECT_FAST_PATTERN].url = "/rules/prefilter-keywords.html#fast-pattern";
189
79
    sigmatch_table[DETECT_FAST_PATTERN].Match = NULL;
190
79
    sigmatch_table[DETECT_FAST_PATTERN].Setup = DetectFastPatternSetup;
191
79
    sigmatch_table[DETECT_FAST_PATTERN].Free  = NULL;
192
#ifdef UNITTESTS
193
    sigmatch_table[DETECT_FAST_PATTERN].RegisterTests = DetectFastPatternRegisterTests;
194
#endif
195
79
    sigmatch_table[DETECT_FAST_PATTERN].flags |= SIGMATCH_OPTIONAL_OPT;
196
197
79
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
198
79
}
199
200
/**
201
 * \brief Configures the previous content context for a fast_pattern modifier
202
 *        keyword used in the rule.
203
 *
204
 * \param de_ctx   Pointer to the Detection Engine Context.
205
 * \param s        Pointer to the Signature to which the current keyword belongs.
206
 * \param arg      May hold an argument
207
 *
208
 * \retval  0 On success.
209
 * \retval -1 On failure.
210
 */
211
static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
212
36.7k
{
213
36.7k
    int res = 0;
214
36.7k
    size_t pcre2len;
215
36.7k
    char arg_substr[128] = "";
216
36.7k
    DetectContentData *cd = NULL;
217
36.7k
    pcre2_match_data *match = NULL;
218
219
36.7k
    SigMatch *pm1 = DetectGetLastSMFromMpmLists(de_ctx, s);
220
36.7k
    SigMatch *pm2 = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
221
36.7k
    if (pm1 == NULL && pm2 == NULL) {
222
544
        SCLogError("fast_pattern found inside "
223
544
                   "the rule, without a content context. Please use a "
224
544
                   "content based keyword before using fast_pattern");
225
544
        return -1;
226
544
    }
227
228
36.1k
    SigMatch *pm = NULL;
229
36.1k
    if (pm1 && pm2) {
230
32.4k
        if (pm1->idx > pm2->idx)
231
158
            pm = pm1;
232
32.3k
        else
233
32.3k
            pm = pm2;
234
32.4k
    } else if (pm1 && !pm2) {
235
615
        pm = pm1;
236
3.08k
    } else {
237
3.08k
        pm = pm2;
238
3.08k
    }
239
240
36.1k
    if (s->flags & SIG_FLAG_TXBOTHDIR && s->init_data->curbuf != NULL) {
241
3.38k
        if (DetectBufferToClient(de_ctx, s->init_data->curbuf->id, s->alproto)) {
242
1.66k
            if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER) {
243
0
                SCLogError("fast_pattern cannot be used on to_client keyword for "
244
0
                           "transactional rule with a streaming buffer to server %u",
245
0
                        s->id);
246
0
                goto error;
247
0
            }
248
1.66k
            s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT;
249
1.66k
        }
250
3.38k
    }
251
252
36.1k
    cd = (DetectContentData *)pm->ctx;
253
36.1k
    if ((cd->flags & DETECT_CONTENT_NEGATED) &&
254
610
        ((cd->flags & DETECT_CONTENT_DISTANCE) ||
255
609
         (cd->flags & DETECT_CONTENT_WITHIN) ||
256
608
         (cd->flags & DETECT_CONTENT_OFFSET) ||
257
607
         (cd->flags & DETECT_CONTENT_DEPTH))) {
258
259
        /* we can't have any of these if we are having "only" */
260
66
        SCLogError("fast_pattern; cannot be "
261
66
                   "used with negated content, along with relative modifiers");
262
66
        goto error;
263
66
    }
264
265
36.1k
    if (arg == NULL|| strcmp(arg, "") == 0) {
266
29.4k
        if (cd->flags & DETECT_CONTENT_FAST_PATTERN) {
267
238
            SCLogError("can't use multiple fast_pattern "
268
238
                       "options for the same content");
269
238
            goto error;
270
238
        }
271
29.1k
        else { /*allow only one content to have fast_pattern modifier*/
272
233k
            for (uint32_t list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
273
204k
                SigMatch *sm = NULL;
274
236k
                for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
275
31.9k
                    if (sm->type == DETECT_CONTENT) {
276
3.12k
                        DetectContentData *tmp_cd = (DetectContentData *)sm->ctx;
277
3.12k
                        if (tmp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
278
15
                            SCLogError("fast_pattern "
279
15
                                       "can be used on only one content in a rule");
280
15
                            goto error;
281
15
                        }
282
3.12k
                    }
283
31.9k
                }
284
204k
            }
285
29.1k
        }
286
29.1k
        if (SigMatchListSMBelongsTo(s, pm) == DETECT_SM_LIST_BASE64_DATA) {
287
5
            SCLogError("fast_pattern cannot be used with base64_data");
288
5
            goto error;
289
5
        }
290
29.1k
        cd->flags |= DETECT_CONTENT_FAST_PATTERN;
291
29.1k
        return 0;
292
29.1k
    }
293
294
    /* Execute the regex and populate args with captures. */
295
6.67k
    int ret = DetectParsePcreExec(&parse_regex, &match, arg, 0, 0);
296
    /* fast pattern only */
297
6.67k
    if (ret == 2) {
298
6.35k
        if ((cd->flags & DETECT_CONTENT_NEGATED) ||
299
6.35k
            (cd->flags & DETECT_CONTENT_DISTANCE) ||
300
6.35k
            (cd->flags & DETECT_CONTENT_WITHIN) ||
301
6.35k
            (cd->flags & DETECT_CONTENT_OFFSET) ||
302
6.35k
            (cd->flags & DETECT_CONTENT_DEPTH)) {
303
304
            /* we can't have any of these if we are having "only" */
305
104
            SCLogError("fast_pattern: only; cannot be "
306
104
                       "used with negated content or with any of the relative "
307
104
                       "modifiers like distance, within, offset, depth");
308
104
            goto error;
309
104
        }
310
6.25k
        cd->flags |= DETECT_CONTENT_FAST_PATTERN_ONLY;
311
312
        /* fast pattern chop */
313
6.25k
    } else if (ret == 4) {
314
126
        pcre2len = sizeof(arg_substr);
315
126
        res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)arg_substr, &pcre2len);
316
126
        if (res < 0) {
317
1
            SCLogError("pcre2_substring_copy_bynumber failed "
318
1
                       "for fast_pattern offset");
319
1
            goto error;
320
1
        }
321
125
        uint16_t offset;
322
125
        if (StringParseUint16(&offset, 10, 0, (const char *)arg_substr) <= 0) {
323
1
            SCLogError("Invalid fast pattern offset:"
324
1
                       " \"%s\"",
325
1
                    arg_substr);
326
1
            goto error;
327
1
        }
328
329
124
        pcre2len = sizeof(arg_substr);
330
124
        res = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)arg_substr, &pcre2len);
331
124
        if (res < 0) {
332
1
            SCLogError("pcre2_substring_copy_bynumber failed "
333
1
                       "for fast_pattern offset");
334
1
            goto error;
335
1
        }
336
123
        uint16_t length;
337
123
        if (StringParseUint16(&length, 10, 0, (const char *)arg_substr) <= 0) {
338
2
            SCLogError("Invalid value for fast "
339
2
                       "pattern: \"%s\"",
340
2
                    arg_substr);
341
2
            goto error;
342
2
        }
343
344
        // Avoiding integer overflow
345
121
        if (offset > (65535 - length)) {
346
1
            SCLogError("Fast pattern (length + offset) "
347
1
                       "exceeds limit pattern length limit");
348
1
            goto error;
349
1
        }
350
351
120
        if (offset + length > cd->content_len) {
352
2
            SCLogError("Fast pattern (length + "
353
2
                       "offset (%u)) exceeds pattern length (%u)",
354
2
                    offset + length, cd->content_len);
355
2
            goto error;
356
2
        }
357
358
118
        cd->fp_chop_offset = offset;
359
118
        cd->fp_chop_len = length;
360
118
        cd->flags |= DETECT_CONTENT_FAST_PATTERN_CHOP;
361
362
189
    } else {
363
189
        SCLogError("parse error, ret %" PRId32 ", string %s", ret, arg);
364
189
        goto error;
365
189
    }
366
367
6.37k
    cd->flags |= DETECT_CONTENT_FAST_PATTERN;
368
369
6.37k
    pcre2_match_data_free(match);
370
6.37k
    return 0;
371
372
625
 error:
373
625
     if (match) {
374
         pcre2_match_data_free(match);
375
301
     }
376
625
    return -1;
377
6.67k
}
378
379
/*----------------------------------Unittests---------------------------------*/
380
381
#ifdef UNITTESTS
382
#include "detect-engine-alert.h"
383
#include "detect-engine-buffer.h"
384
static SigMatch *GetMatches(Signature *s, const int list)
385
{
386
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, list);
387
    if (sm == NULL && list < DETECT_SM_LIST_MAX) {
388
        sm = s->init_data->smlists[list];
389
    }
390
    return sm;
391
}
392
393
static int DetectFastPatternStickySingle(const char *sticky, const int list)
394
{
395
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
396
    FAIL_IF_NULL(de_ctx);
397
    char string[1024];
398
    snprintf(string, sizeof(string),
399
            "alert tcp any any -> any any "
400
            "(%s%scontent:\"one\"; fast_pattern; sid:1;)",
401
            sticky ? sticky : "", sticky ? "; " : " ");
402
    Signature *s = DetectEngineAppendSig(de_ctx, string);
403
    FAIL_IF_NULL(s);
404
    SigMatch *sm = GetMatches(s, list);
405
    FAIL_IF_NULL(sm);
406
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
407
    DetectContentData *cd = (DetectContentData *)sm->ctx;
408
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
409
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == DETECT_CONTENT_FAST_PATTERN);
410
    DetectEngineCtxFree(de_ctx);
411
    PASS;
412
}
413
414
static int DetectFastPatternModifierSingle(const char *sticky, const int list)
415
{
416
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
417
    FAIL_IF_NULL(de_ctx);
418
    char string[1024];
419
    snprintf(string, sizeof(string),
420
            "alert tcp any any -> any any "
421
            "(content:\"one\"; %s%sfast_pattern; sid:1;)",
422
            sticky ? sticky : "", sticky ? "; " : " ");
423
    Signature *s = DetectEngineAppendSig(de_ctx, string);
424
    FAIL_IF_NULL(s);
425
    SigMatch *sm = GetMatches(s, list);
426
    FAIL_IF_NULL(sm);
427
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
428
    DetectContentData *cd = (DetectContentData *)sm->ctx;
429
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
430
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == DETECT_CONTENT_FAST_PATTERN);
431
    DetectEngineCtxFree(de_ctx);
432
    PASS;
433
}
434
435
static int DetectFastPatternStickySingleNoFP(const char *sticky, const int list)
436
{
437
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
438
    FAIL_IF_NULL(de_ctx);
439
    char string[1024];
440
    snprintf(string, sizeof(string),
441
            "alert tcp any any -> any any "
442
            "(%s%scontent:\"one\"; sid:1;)",
443
            sticky ? sticky : "", sticky ? "; " : " ");
444
    Signature *s = DetectEngineAppendSig(de_ctx, string);
445
    FAIL_IF_NULL(s);
446
    SigMatch *sm = GetMatches(s, list);
447
    FAIL_IF_NULL(sm);
448
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
449
    DetectContentData *cd = (DetectContentData *)sm->ctx;
450
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
451
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == 0);
452
    DetectEngineCtxFree(de_ctx);
453
    PASS;
454
}
455
456
static int DetectFastPatternModifierSingleNoFP(const char *sticky, const int list)
457
{
458
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
459
    FAIL_IF_NULL(de_ctx);
460
    char string[1024];
461
    snprintf(string, sizeof(string),
462
            "alert tcp any any -> any any "
463
            "(content:\"one\"; %s%ssid:1;)",
464
            sticky ? sticky : "", sticky ? "; " : " ");
465
    Signature *s = DetectEngineAppendSig(de_ctx, string);
466
    FAIL_IF_NULL(s);
467
    SigMatch *sm = GetMatches(s, list);
468
    FAIL_IF_NULL(sm);
469
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
470
    DetectContentData *cd = (DetectContentData *)sm->ctx;
471
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
472
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == 0);
473
    DetectEngineCtxFree(de_ctx);
474
    PASS;
475
}
476
477
static int DetectFastPatternStickySingleBadArg(const char *sticky)
478
{
479
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
480
    FAIL_IF_NULL(de_ctx);
481
    char string[1024];
482
    /* bogus argument to fast_pattern */
483
    snprintf(string, sizeof(string),
484
            "alert tcp any any -> any any "
485
            "(%s%scontent:\"one\"; fast_pattern:boo; sid:1;)",
486
            sticky ? sticky : "", sticky ? "; " : " ");
487
    Signature *s = DetectEngineAppendSig(de_ctx, string);
488
    FAIL_IF_NOT_NULL(s);
489
    /* fast_pattern only with distance */
490
    snprintf(string, sizeof(string),
491
            "alert tcp any any -> any any "
492
            "(%s%scontent:\"one\"; fast_pattern:only; content:\"two\"; distance:10; sid:1;)",
493
            sticky ? sticky : "", sticky ? "; " : " ");
494
    s = DetectEngineAppendSig(de_ctx, string);
495
    FAIL_IF_NOT_NULL(s);
496
    /* fast_pattern only with distance */
497
    snprintf(string, sizeof(string),
498
            "alert tcp any any -> any any "
499
            "(%s%scontent:\"one\"; content:\"two\"; fast_pattern:only; distance:10; sid:1;)",
500
            sticky ? sticky : "", sticky ? "; " : " ");
501
    s = DetectEngineAppendSig(de_ctx, string);
502
    FAIL_IF_NOT_NULL(s);
503
    /* fast_pattern only with distance */
504
    snprintf(string, sizeof(string),
505
            "alert tcp any any -> any any "
506
            "(%s%scontent:\"one\"; content:\"two\"; distance:10; fast_pattern:only; sid:1;)",
507
            sticky ? sticky : "", sticky ? "; " : " ");
508
    s = DetectEngineAppendSig(de_ctx, string);
509
    FAIL_IF_NOT_NULL(s);
510
    /* fast_pattern chop with invalid values */
511
    snprintf(string, sizeof(string),
512
            "alert tcp any any -> any any "
513
            "(%s%scontent:\"one\"; fast_pattern:5,6; sid:1;)",
514
            sticky ? sticky : "", sticky ? "; " : " ");
515
    s = DetectEngineAppendSig(de_ctx, string);
516
    FAIL_IF_NOT_NULL(s);
517
    DetectEngineCtxFree(de_ctx);
518
    PASS;
519
}
520
521
static int DetectFastPatternModifierBadRules(const char *sticky)
522
{
523
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
524
    FAIL_IF_NULL(de_ctx);
525
    char string[1024];
526
    /* bogus argument to fast_pattern */
527
    snprintf(string, sizeof(string),
528
            "alert tcp any any -> any any "
529
            "(content:\"one\"; %s%sfast_pattern:boo; sid:1;)",
530
            sticky ? sticky : "", sticky ? "; " : " ");
531
    Signature *s = DetectEngineAppendSig(de_ctx, string);
532
    FAIL_IF_NOT_NULL(s);
533
    /* fast_pattern only with distance */
534
    snprintf(string, sizeof(string),
535
            "alert tcp any any -> any any "
536
            "(content:\"one\"; %s%sfast_pattern:only; content:\"two\"; %s%sdistance:10; sid:1;)",
537
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
538
    s = DetectEngineAppendSig(de_ctx, string);
539
    FAIL_IF_NOT_NULL(s);
540
#if 0 // TODO bug?
541
    /* fast_pattern only with distance */
542
    snprintf(string, sizeof(string), "alert tcp any any -> any any "
543
            "(content:\"one\"; %s%s content:\"two\"; %s%sdistance:10; fast_pattern:only; sid:1;)",
544
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
545
    s = DetectEngineAppendSig(de_ctx, string);
546
    FAIL_IF_NOT_NULL(s);
547
#endif
548
    /* fast_pattern only with within */
549
    snprintf(string, sizeof(string),
550
            "alert tcp any any -> any any "
551
            "(content:\"one\"; %s%sfast_pattern:only; content:\"two\"; %s%swithin:10; sid:1;)",
552
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
553
    s = DetectEngineAppendSig(de_ctx, string);
554
    FAIL_IF_NOT_NULL(s);
555
    /* fast_pattern only with within */
556
    snprintf(string, sizeof(string),
557
            "alert tcp any any -> any any "
558
            "(content:\"one\"; %s%s content:\"two\"; %s%swithin:10; fast_pattern:only; sid:1;)",
559
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
560
    s = DetectEngineAppendSig(de_ctx, string);
561
    FAIL_IF_NOT_NULL(s);
562
    /* fast_pattern only with offset */
563
    snprintf(string, sizeof(string),
564
            "alert tcp any any -> any any "
565
            "(content:\"one\"; %s%sfast_pattern:only; offset:10; sid:1;)",
566
            sticky ? sticky : "", sticky ? "; " : " ");
567
    s = DetectEngineAppendSig(de_ctx, string);
568
    FAIL_IF_NOT_NULL(s);
569
    /* fast_pattern only with offset */
570
    snprintf(string, sizeof(string),
571
            "alert tcp any any -> any any "
572
            "(content:\"one\"; %s%s offset:10; fast_pattern:only; sid:1;)",
573
            sticky ? sticky : "", sticky ? "; " : " ");
574
    s = DetectEngineAppendSig(de_ctx, string);
575
    FAIL_IF_NOT_NULL(s);
576
    /* fast_pattern only with depth */
577
    snprintf(string, sizeof(string),
578
            "alert tcp any any -> any any "
579
            "(content:\"one\"; %s%sfast_pattern:only; depth:10; sid:1;)",
580
            sticky ? sticky : "", sticky ? "; " : " ");
581
    s = DetectEngineAppendSig(de_ctx, string);
582
    FAIL_IF_NOT_NULL(s);
583
    /* fast_pattern only with depth */
584
    snprintf(string, sizeof(string),
585
            "alert tcp any any -> any any "
586
            "(content:\"one\"; %s%s depth:10; fast_pattern:only; sid:1;)",
587
            sticky ? sticky : "", sticky ? "; " : " ");
588
    s = DetectEngineAppendSig(de_ctx, string);
589
    FAIL_IF_NOT_NULL(s);
590
    /* fast_pattern only negate */
591
    snprintf(string, sizeof(string),
592
            "alert tcp any any -> any any "
593
            "(content:\"one\"; %s%s content:!\"two\"; %s%sfast_pattern:only; sid:1;)",
594
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
595
    s = DetectEngineAppendSig(de_ctx, string);
596
    FAIL_IF_NOT_NULL(s);
597
    /* fast_pattern chop with invalid values */
598
    snprintf(string, sizeof(string),
599
            "alert tcp any any -> any any "
600
            "(content:\"one\"; %s%sfast_pattern:5,6; sid:1;)",
601
            sticky ? sticky : "", sticky ? "; " : " ");
602
    s = DetectEngineAppendSig(de_ctx, string);
603
    FAIL_IF_NOT_NULL(s);
604
    /* fast_pattern chop with invalid values */
605
    snprintf(string, sizeof(string),
606
            "alert tcp any any -> any any "
607
            "(content:\"one\"; %s%sfast_pattern:65977,2; sid:1;)",
608
            sticky ? sticky : "", sticky ? "; " : " ");
609
    s = DetectEngineAppendSig(de_ctx, string);
610
    FAIL_IF_NOT_NULL(s);
611
    /* fast_pattern chop with invalid values */
612
    snprintf(string, sizeof(string),
613
            "alert tcp any any -> any any "
614
            "(content:\"one\"; %s%sfast_pattern:2,65977; sid:1;)",
615
            sticky ? sticky : "", sticky ? "; " : " ");
616
    s = DetectEngineAppendSig(de_ctx, string);
617
    FAIL_IF_NOT_NULL(s);
618
    /* fast_pattern chop with invalid values */
619
    snprintf(string, sizeof(string),
620
            "alert tcp any any -> any any "
621
            "(content:\"one\"; %s%sfast_pattern:2,65534; sid:1;)",
622
            sticky ? sticky : "", sticky ? "; " : " ");
623
    s = DetectEngineAppendSig(de_ctx, string);
624
    FAIL_IF_NOT_NULL(s);
625
    /* fast_pattern chop with invalid values */
626
    snprintf(string, sizeof(string),
627
            "alert tcp any any -> any any "
628
            "(content:\"one\"; %s%sfast_pattern:65534,2; sid:1;)",
629
            sticky ? sticky : "", sticky ? "; " : " ");
630
    s = DetectEngineAppendSig(de_ctx, string);
631
    FAIL_IF_NOT_NULL(s);
632
    /* negated fast_pattern with distance */
633
    snprintf(string, sizeof(string),
634
            "alert tcp any any -> any any "
635
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%sdistance:10; sid:1;)",
636
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
637
    s = DetectEngineAppendSig(de_ctx, string);
638
    FAIL_IF_NOT_NULL(s);
639
    /* negated fast_pattern with within */
640
    snprintf(string, sizeof(string),
641
            "alert tcp any any -> any any "
642
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%swithin:10; sid:1;)",
643
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
644
    s = DetectEngineAppendSig(de_ctx, string);
645
    FAIL_IF_NOT_NULL(s);
646
    /* negated fast_pattern with depth */
647
    snprintf(string, sizeof(string),
648
            "alert tcp any any -> any any "
649
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%sdepth:10; sid:1;)",
650
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
651
    s = DetectEngineAppendSig(de_ctx, string);
652
    FAIL_IF_NOT_NULL(s);
653
    /* negated fast_pattern with offset */
654
    snprintf(string, sizeof(string),
655
            "alert tcp any any -> any any "
656
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%soffset:10; sid:1;)",
657
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
658
    s = DetectEngineAppendSig(de_ctx, string);
659
    FAIL_IF_NOT_NULL(s);
660
    DetectEngineCtxFree(de_ctx);
661
    PASS;
662
}
663
664
static int DetectFastPatternStickySingleFPOnly(const char *sticky, const int list)
665
{
666
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
667
    FAIL_IF_NULL(de_ctx);
668
    char string[1024];
669
    snprintf(string, sizeof(string),
670
            "alert tcp any any -> any any "
671
            "(%s%scontent:\"one\"; fast_pattern:only; sid:1;)",
672
            sticky ? sticky : "", sticky ? "; " : " ");
673
    Signature *s = DetectEngineAppendSig(de_ctx, string);
674
    FAIL_IF_NULL(s);
675
    SigMatch *sm = GetMatches(s, list);
676
    FAIL_IF_NULL(sm);
677
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
678
    DetectContentData *cd = (DetectContentData *)sm->ctx;
679
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
680
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
681
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
682
    DetectEngineCtxFree(de_ctx);
683
    PASS;
684
}
685
686
static int DetectFastPatternModifierFPOnly(const char *sticky, const int list)
687
{
688
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
689
    FAIL_IF_NULL(de_ctx);
690
    char string[1024];
691
    snprintf(string, sizeof(string),
692
            "alert tcp any any -> any any "
693
            "(content:\"one\"; %s%sfast_pattern:only; sid:1;)",
694
            sticky ? sticky : "", sticky ? "; " : " ");
695
    Signature *s = DetectEngineAppendSig(de_ctx, string);
696
    FAIL_IF_NULL(s);
697
    SigMatch *sm = GetMatches(s, list);
698
    FAIL_IF_NULL(sm);
699
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
700
    DetectContentData *cd = (DetectContentData *)sm->ctx;
701
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
702
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
703
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
704
705
    snprintf(string, sizeof(string),
706
            "alert tcp any any -> any any "
707
            "(content:\"one\"; %s%scontent:\"two\"; %s%sfast_pattern:only; sid:2;)",
708
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
709
    s = DetectEngineAppendSig(de_ctx, string);
710
    FAIL_IF_NULL(s);
711
    sm = GetMatches(s, list);
712
    FAIL_IF_NULL(sm);
713
    FAIL_IF_NULL(sm->next);
714
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
715
    cd = (DetectContentData *)sm->ctx;
716
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
717
    FAIL_IF_NOT(
718
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
719
    sm = sm->next;
720
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
721
    cd = (DetectContentData *)sm->ctx;
722
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
723
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
724
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
725
726
    snprintf(string, sizeof(string),
727
            "alert tcp any any -> any any "
728
            "(content:\"one\"; %s%scontent:\"two\"; distance:10; %s%scontent:\"three\"; "
729
            "%s%sfast_pattern:only; sid:3;)",
730
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
731
            sticky ? sticky : "", sticky ? "; " : " ");
732
    s = DetectEngineAppendSig(de_ctx, string);
733
    FAIL_IF_NULL(s);
734
    sm = GetMatches(s, list);
735
    FAIL_IF_NULL(sm);
736
    FAIL_IF_NULL(sm->next);
737
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
738
    cd = (DetectContentData *)sm->ctx;
739
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
740
    FAIL_IF_NOT(
741
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
742
    sm = sm->next;
743
    FAIL_IF_NULL(sm->next);
744
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
745
    cd = (DetectContentData *)sm->ctx;
746
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
747
    FAIL_IF_NOT(
748
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
749
    sm = sm->next;
750
    FAIL_IF_NOT_NULL(sm->next);
751
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
752
    cd = (DetectContentData *)sm->ctx;
753
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
754
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
755
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
756
757
    snprintf(string, sizeof(string),
758
            "alert tcp any any -> any any "
759
            "(content:\"one\"; %s%scontent:\"two\"; within:10; %s%scontent:\"three\"; "
760
            "%s%sfast_pattern:only; sid:4;)",
761
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
762
            sticky ? sticky : "", sticky ? "; " : " ");
763
    s = DetectEngineAppendSig(de_ctx, string);
764
    FAIL_IF_NULL(s);
765
    sm = GetMatches(s, list);
766
    FAIL_IF_NULL(sm);
767
    FAIL_IF_NULL(sm->next);
768
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
769
    cd = (DetectContentData *)sm->ctx;
770
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
771
    FAIL_IF_NOT(
772
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
773
    sm = sm->next;
774
    FAIL_IF_NULL(sm->next);
775
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
776
    cd = (DetectContentData *)sm->ctx;
777
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
778
    FAIL_IF_NOT(
779
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
780
    sm = sm->next;
781
    FAIL_IF_NOT_NULL(sm->next);
782
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
783
    cd = (DetectContentData *)sm->ctx;
784
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
785
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
786
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
787
788
    snprintf(string, sizeof(string),
789
            "alert tcp any any -> any any "
790
            "(content:\"one\"; %s%scontent:\"two\"; offset:10; %s%scontent:\"three\"; "
791
            "%s%sfast_pattern:only; sid:5;)",
792
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
793
            sticky ? sticky : "", sticky ? "; " : " ");
794
    s = DetectEngineAppendSig(de_ctx, string);
795
    FAIL_IF_NULL(s);
796
    sm = GetMatches(s, list);
797
    FAIL_IF_NULL(sm);
798
    FAIL_IF_NULL(sm->next);
799
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
800
    cd = (DetectContentData *)sm->ctx;
801
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
802
    FAIL_IF_NOT(
803
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
804
    sm = sm->next;
805
    FAIL_IF_NULL(sm->next);
806
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
807
    cd = (DetectContentData *)sm->ctx;
808
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
809
    FAIL_IF_NOT(
810
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
811
    sm = sm->next;
812
    FAIL_IF_NOT_NULL(sm->next);
813
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
814
    cd = (DetectContentData *)sm->ctx;
815
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
816
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
817
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
818
819
    snprintf(string, sizeof(string),
820
            "alert tcp any any -> any any "
821
            "(content:\"one\"; %s%scontent:\"two\"; depth:10; %s%scontent:\"three\"; "
822
            "%s%sfast_pattern:only; sid:6;)",
823
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
824
            sticky ? sticky : "", sticky ? "; " : " ");
825
    s = DetectEngineAppendSig(de_ctx, string);
826
    FAIL_IF_NULL(s);
827
    sm = GetMatches(s, list);
828
    FAIL_IF_NULL(sm);
829
    FAIL_IF_NULL(sm->next);
830
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
831
    cd = (DetectContentData *)sm->ctx;
832
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
833
    FAIL_IF_NOT(
834
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
835
    sm = sm->next;
836
    FAIL_IF_NULL(sm->next);
837
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
838
    cd = (DetectContentData *)sm->ctx;
839
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
840
    FAIL_IF_NOT(
841
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
842
    sm = sm->next;
843
    FAIL_IF_NOT_NULL(sm->next);
844
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
845
    cd = (DetectContentData *)sm->ctx;
846
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
847
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
848
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
849
850
    snprintf(string, sizeof(string),
851
            "alert tcp any any -> any any "
852
            "(content:!\"one\"; %s%sfast_pattern; content:\"two\"; depth:10; %s%ssid:7;)",
853
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
854
    s = DetectEngineAppendSig(de_ctx, string);
855
    FAIL_IF_NULL(s);
856
    sm = GetMatches(s, list);
857
    FAIL_IF_NULL(sm);
858
    FAIL_IF_NULL(sm->next);
859
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
860
    cd = (DetectContentData *)sm->ctx;
861
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
862
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY |
863
                                     DETECT_CONTENT_NEGATED)) ==
864
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_NEGATED));
865
    sm = sm->next;
866
    FAIL_IF_NOT_NULL(sm->next);
867
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
868
    cd = (DetectContentData *)sm->ctx;
869
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
870
    FAIL_IF_NOT(
871
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
872
873
    DetectEngineCtxFree(de_ctx);
874
    PASS;
875
}
876
877
static int DetectFastPatternStickyFPChop(const char *sticky, const int list)
878
{
879
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
880
    FAIL_IF_NULL(de_ctx);
881
    char string[1024];
882
    snprintf(string, sizeof(string),
883
            "alert tcp any any -> any any "
884
            "(%s%scontent:\"onetwothree\"; fast_pattern:3,4; sid:1;)",
885
            sticky ? sticky : "", sticky ? "; " : " ");
886
    Signature *s = DetectEngineAppendSig(de_ctx, string);
887
    FAIL_IF_NULL(s);
888
    SigMatch *sm = GetMatches(s, list);
889
    FAIL_IF_NULL(sm);
890
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
891
    DetectContentData *cd = (DetectContentData *)sm->ctx;
892
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
893
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP |
894
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
895
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP));
896
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
897
    FAIL_IF_NOT(cd->fp_chop_len == 4);
898
899
    snprintf(string, sizeof(string),
900
            "alert tcp any any -> any any "
901
            "(%s%scontent:\"onetwothree\"; fast_pattern:3,4; content:\"xyz\"; distance:10; sid:2;)",
902
            sticky ? sticky : "", sticky ? "; " : " ");
903
    s = DetectEngineAppendSig(de_ctx, string);
904
    FAIL_IF_NULL(s);
905
    sm = GetMatches(s, list);
906
    FAIL_IF_NULL(sm);
907
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
908
    cd = (DetectContentData *)sm->ctx;
909
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
910
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP |
911
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
912
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP));
913
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
914
    FAIL_IF_NOT(cd->fp_chop_len == 4);
915
916
    DetectEngineCtxFree(de_ctx);
917
    PASS;
918
}
919
920
static int DetectFastPatternModifierFPChop(const char *sticky, const int list)
921
{
922
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
923
    FAIL_IF_NULL(de_ctx);
924
    char string[1024];
925
    snprintf(string, sizeof(string),
926
            "alert tcp any any -> any any "
927
            "(content:\"onetwothree\"; %s%sfast_pattern:3,4; sid:1;)",
928
            sticky ? sticky : "", sticky ? "; " : " ");
929
    Signature *s = DetectEngineAppendSig(de_ctx, string);
930
    FAIL_IF_NULL(s);
931
    SigMatch *sm = GetMatches(s, list);
932
    FAIL_IF_NULL(sm);
933
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
934
    DetectContentData *cd = (DetectContentData *)sm->ctx;
935
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
936
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP |
937
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
938
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP));
939
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
940
    FAIL_IF_NOT(cd->fp_chop_len == 4);
941
942
    snprintf(string, sizeof(string),
943
            "alert tcp any any -> any any "
944
            "(content:!\"onetwothree\"; %s%sfast_pattern:3,4; sid:2;)",
945
            sticky ? sticky : "", sticky ? "; " : " ");
946
    s = DetectEngineAppendSig(de_ctx, string);
947
    FAIL_IF_NULL(s);
948
    sm = GetMatches(s, list);
949
    FAIL_IF_NULL(sm);
950
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
951
    cd = (DetectContentData *)sm->ctx;
952
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
953
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_NEGATED | DETECT_CONTENT_FAST_PATTERN |
954
                                     DETECT_CONTENT_FAST_PATTERN_CHOP |
955
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
956
                (DETECT_CONTENT_NEGATED | DETECT_CONTENT_FAST_PATTERN |
957
                        DETECT_CONTENT_FAST_PATTERN_CHOP));
958
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
959
    FAIL_IF_NOT(cd->fp_chop_len == 4);
960
961
    DetectEngineCtxFree(de_ctx);
962
    PASS;
963
}
964
965
/**
966
 * \test Checks if a fast_pattern is registered in a Signature
967
 */
968
static int DetectFastPatternTest01(void)
969
{
970
    FAIL_IF_NOT(DetectFastPatternStickySingle(NULL, DETECT_SM_LIST_PMATCH));
971
    FAIL_IF_NOT(DetectFastPatternModifierSingle(NULL, DETECT_SM_LIST_PMATCH));
972
    FAIL_IF_NOT(DetectFastPatternStickySingleNoFP(NULL, DETECT_SM_LIST_PMATCH));
973
    FAIL_IF_NOT(DetectFastPatternModifierSingleNoFP(NULL, DETECT_SM_LIST_PMATCH));
974
    FAIL_IF_NOT(DetectFastPatternStickySingleBadArg(NULL));
975
    FAIL_IF_NOT(DetectFastPatternModifierBadRules(NULL));
976
    FAIL_IF_NOT(DetectFastPatternStickySingleFPOnly(NULL, DETECT_SM_LIST_PMATCH));
977
    FAIL_IF_NOT(DetectFastPatternModifierFPOnly(NULL, DETECT_SM_LIST_PMATCH));
978
    FAIL_IF_NOT(DetectFastPatternStickyFPChop(NULL, DETECT_SM_LIST_PMATCH));
979
    FAIL_IF_NOT(DetectFastPatternModifierFPChop(NULL, DETECT_SM_LIST_PMATCH));
980
981
    struct {
982
        const char *buffer_name;
983
        const char *sb_name;
984
        const char *mod_name;
985
    } keywords[] = {
986
        { "file_data", "file.data", NULL },
987
        { "http_uri", "http.uri", "http_uri" },
988
        { "http_raw_uri", "http.uri.raw", "http_raw_uri" },
989
        { "http_user_agent", "http.user_agent", "http_user_agent" },
990
        { "http_header", "http.header", "http_header" },
991
        // http_raw_header requires sigs to have a direction
992
        //{ "http_raw_header", "http.header.raw", "http_raw_header" },
993
        { "http_method", "http.method", "http_method" },
994
        { "http_cookie", "http.cookie", "http_cookie" },
995
        { "http_host", "http.host", "http_host" },
996
        { "http_raw_host", "http.host.raw", "http_raw_host" },
997
        { "http_stat_code", "http.stat_code", "http_stat_code" },
998
        { "http_stat_msg", "http.stat_msg", "http_stat_msg" },
999
        { "http_client_body", "http.request_body", "http_client_body" },
1000
        { NULL, NULL, NULL },
1001
    };
1002
1003
    for (int i = 0; keywords[i].buffer_name != NULL; i++) {
1004
        const int list_id = DetectBufferTypeGetByName(keywords[i].buffer_name);
1005
        FAIL_IF(list_id == -1);
1006
1007
        const char *k = keywords[i].sb_name;
1008
        if (k) {
1009
            FAIL_IF_NOT(DetectFastPatternStickySingle(k, list_id));
1010
            FAIL_IF_NOT(DetectFastPatternStickySingleNoFP(k, list_id));
1011
            FAIL_IF_NOT(DetectFastPatternStickySingleBadArg(k));
1012
            FAIL_IF_NOT(DetectFastPatternStickySingleFPOnly(k, list_id));
1013
            FAIL_IF_NOT(DetectFastPatternStickyFPChop(k, list_id));
1014
        }
1015
        k = keywords[i].mod_name;
1016
        if (k) {
1017
            FAIL_IF_NOT(DetectFastPatternModifierSingle(k, list_id));
1018
            FAIL_IF_NOT(DetectFastPatternModifierSingleNoFP(k, list_id));
1019
            FAIL_IF_NOT(DetectFastPatternModifierBadRules(k));
1020
            FAIL_IF_NOT(DetectFastPatternModifierFPOnly(k, list_id));
1021
            FAIL_IF_NOT(DetectFastPatternModifierFPChop(k, list_id));
1022
        }
1023
    }
1024
1025
    PASS;
1026
}
1027
1028
/**
1029
 * \test Checks to make sure that other sigs work that should when fast_pattern is inspecting on the
1030
 * same payload
1031
 *
1032
 */
1033
static int DetectFastPatternTest14(void)
1034
{
1035
    uint8_t *buf = (uint8_t *)"Dummy is our name.  Oh yes.  From right here "
1036
                              "right now, all the way to hangover.  right.  strings5_imp now here "
1037
                              "comes our dark knight strings_string5.  Yes here is our dark knight";
1038
    uint16_t buflen = strlen((char *)buf);
1039
    ThreadVars th_v;
1040
    DetectEngineThreadCtx *det_ctx = NULL;
1041
1042
    memset(&th_v, 0, sizeof(th_v));
1043
    Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
1044
    FAIL_IF_NULL(p);
1045
1046
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1047
    FAIL_IF_NULL(de_ctx);
1048
    de_ctx->flags |= DE_QUIET;
1049
1050
    FlowInitConfig(FLOW_QUIET);
1051
1052
    Signature *s = DetectEngineAppendSig(de_ctx,
1053
            "alert tcp any any -> any any "
1054
            "(msg:\"fast_pattern test\"; content:\"strings_string5\"; content:\"knight\"; "
1055
            "fast_pattern; sid:1;)");
1056
    FAIL_IF_NULL(s);
1057
1058
    s = DetectEngineAppendSig(de_ctx,
1059
            "alert tcp any any -> any any "
1060
            "(msg:\"test different content\"; content:\"Dummy is our name\"; sid:2;)");
1061
    FAIL_IF_NULL(s);
1062
1063
    SigGroupBuild(de_ctx);
1064
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1065
1066
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1067
    FAIL_IF_NOT(PacketAlertCheck(p, 1));
1068
    FAIL_IF_NOT(PacketAlertCheck(p, 2));
1069
1070
    UTHFreePackets(&p, 1);
1071
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1072
    DetectEngineCtxFree(de_ctx);
1073
    FlowShutdown();
1074
    PASS;
1075
}
1076
1077
/**
1078
 * Unittest to check
1079
 * - if we assign different content_ids to duplicate patterns, but one of the
1080
 *   patterns has a fast_pattern chop set.
1081
 * - if 2 unique patterns get unique ids.
1082
 * - if 2 duplicate patterns, with no chop set get unique ids.
1083
 */
1084
static int DetectFastPatternTest671(void)
1085
{
1086
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1087
    FAIL_IF_NULL(de_ctx);
1088
    de_ctx->flags |= DE_QUIET;
1089
1090
    Signature *s[6];
1091
    s[0] = DetectEngineAppendSig(
1092
            de_ctx, "alert tcp any any -> any any (content:\"onetwothreefour\"; sid:1;)");
1093
    FAIL_IF_NULL(s[0]);
1094
    s[1] = DetectEngineAppendSig(
1095
            de_ctx, "alert tcp any any -> any any (content:\"onetwothreefour\"; sid:2;)");
1096
    FAIL_IF_NULL(s[1]);
1097
    s[2] = DetectEngineAppendSig(
1098
            de_ctx, "alert tcp any any -> any any (content:\"uniquepattern\"; sid:3;)");
1099
    FAIL_IF_NULL(s[2]);
1100
    s[3] = DetectEngineAppendSig(de_ctx,
1101
            "alert tcp any any -> any any (content:\"onetwothreefour\"; fast_pattern:3,5; sid:4;)");
1102
    FAIL_IF_NULL(s[3]);
1103
    s[4] = DetectEngineAppendSig(
1104
            de_ctx, "alert tcp any any -> any any (content:\"twoth\"; sid:5;)");
1105
    FAIL_IF_NULL(s[4]);
1106
    s[5] = DetectEngineAppendSig(de_ctx,
1107
            "alert tcp any any -> any any (content:\"onetwothreefour\"; fast_pattern:0,15; "
1108
            "sid:6;)");
1109
    FAIL_IF_NULL(s[5]);
1110
1111
    SigGroupBuild(de_ctx);
1112
1113
    SigMatchData *smd = s[0]->sm_arrays[DETECT_SM_LIST_PMATCH];
1114
    DetectContentData *cd = (DetectContentData *)smd->ctx;
1115
    FAIL_IF(cd->id != 0);
1116
1117
    smd = s[1]->sm_arrays[DETECT_SM_LIST_PMATCH];
1118
    cd = (DetectContentData *)smd->ctx;
1119
    FAIL_IF(cd->id != 0);
1120
1121
    smd = s[2]->sm_arrays[DETECT_SM_LIST_PMATCH];
1122
    cd = (DetectContentData *)smd->ctx;
1123
    FAIL_IF(cd->id != 2);
1124
1125
    smd = s[3]->sm_arrays[DETECT_SM_LIST_PMATCH];
1126
    cd = (DetectContentData *)smd->ctx;
1127
    FAIL_IF(cd->id != 1);
1128
1129
    smd = s[4]->sm_arrays[DETECT_SM_LIST_PMATCH];
1130
    cd = (DetectContentData *)smd->ctx;
1131
    FAIL_IF(cd->id != 1);
1132
1133
    smd = s[5]->sm_arrays[DETECT_SM_LIST_PMATCH];
1134
    cd = (DetectContentData *)smd->ctx;
1135
    FAIL_IF(cd->id != 0);
1136
1137
    DetectEngineCtxFree(de_ctx);
1138
    PASS;
1139
}
1140
1141
static int DetectFastPatternPrefilter(void)
1142
{
1143
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1144
    FAIL_IF_NULL(de_ctx);
1145
    const char *string = "alert tcp any any -> any any "
1146
                         "(content:\"one\"; prefilter; sid:1;)";
1147
    Signature *s = DetectEngineAppendSig(de_ctx, string);
1148
    FAIL_IF_NULL(s);
1149
    SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH];
1150
    FAIL_IF_NULL(sm);
1151
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
1152
    DetectContentData *cd = (DetectContentData *)sm->ctx;
1153
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
1154
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == DETECT_CONTENT_FAST_PATTERN);
1155
    DetectEngineCtxFree(de_ctx);
1156
    PASS;
1157
}
1158
1159
static void DetectFastPatternRegisterTests(void)
1160
{
1161
    UtRegisterTest("DetectFastPatternTest01", DetectFastPatternTest01);
1162
    UtRegisterTest("DetectFastPatternTest14", DetectFastPatternTest14);
1163
    /* Unittest to check
1164
     * - if we assign different content_ids to duplicate patterns, but one of the
1165
     *   patterns has a fast_pattern chop set.
1166
     * - if 2 unique patterns get unique ids.
1167
     * - if 2 duplicate patterns, with no chop set get unique ids.
1168
     */
1169
    UtRegisterTest("DetectFastPatternTest671", DetectFastPatternTest671);
1170
1171
    UtRegisterTest("DetectFastPatternPrefilter", DetectFastPatternPrefilter);
1172
}
1173
#endif