• 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

58.88
/src/detect-hostbits.c
1
/* Copyright (C) 2007-2022 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 *  \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Implements the hostbits keyword
24
 */
25

26
#include "suricata-common.h"
27
#include "decode.h"
28
#include "action-globals.h"
29
#include "detect.h"
30
#include "threads.h"
31
#include "flow.h"
32
#include "flow-util.h"
33
#include "detect-hostbits.h"
34
#include "util-spm.h"
35

36
#include "detect-engine-sigorder.h"
37

38
#include "app-layer-parser.h"
39

40
#include "detect-parse.h"
41
#include "detect-engine.h"
42
#include "detect-engine-mpm.h"
43
#include "detect-engine-state.h"
44
#include "detect-engine-build.h"
45

46
#include "flow-bit.h"
47
#include "host-bit.h"
48
#include "util-var-name.h"
49
#include "util-unittest.h"
50
#include "util-debug.h"
51

52
/*
53
    hostbits:isset,bitname;
54
    hostbits:set,bitname;
55

56
    hostbits:set,bitname,src;
57
    hostbits:set,bitname,dst;
58
TODO:
59
    hostbits:set,bitname,both;
60

61
    hostbits:set,bitname,src,3600;
62
    hostbits:set,bitname,dst,60;
63
    hostbits:set,bitname,both,120;
64
 */
65

66
#define PARSE_REGEX                                                                                \
67
    "^([a-z]+)"                         /* Action */                                               \
3✔
68
    "(?:\\s*,\\s*([^\\s,]+))?(?:\\s*)?" /* Name. */                                                \
3✔
69
    "(?:\\s*,\\s*([^,\\s]+))?(?:\\s*)?" /* Direction. */                                           \
3✔
70
    "(.+)?"                             /* Any remaining data. */
3✔
71
static DetectParseRegex parse_regex;
72

73
static int DetectHostbitMatch (DetectEngineThreadCtx *, Packet *,
74
        const Signature *, const SigMatchCtx *);
75
static int DetectHostbitSetup (DetectEngineCtx *, Signature *, const char *);
76
void DetectHostbitFree (DetectEngineCtx *, void *);
77
#ifdef UNITTESTS
78
void HostBitsRegisterTests(void);
79
#endif
80

81
void DetectHostbitsRegister (void)
82
{
3✔
83
    sigmatch_table[DETECT_HOSTBITS].name = "hostbits";
3✔
84
    sigmatch_table[DETECT_HOSTBITS].desc = "operate on host flag";
3✔
85
//    sigmatch_table[DETECT_HOSTBITS].url = "/rules/flow-keywords.html#flowbits";
86
    sigmatch_table[DETECT_HOSTBITS].Match = DetectHostbitMatch;
3✔
87
    sigmatch_table[DETECT_HOSTBITS].Setup = DetectHostbitSetup;
3✔
88
    sigmatch_table[DETECT_HOSTBITS].Free  = DetectHostbitFree;
3✔
89
#ifdef UNITTESTS
90
    sigmatch_table[DETECT_HOSTBITS].RegisterTests = HostBitsRegisterTests;
91
#endif
92
    /* this is compatible to ip-only signatures */
93
    sigmatch_table[DETECT_HOSTBITS].flags |= SIGMATCH_IPONLY_COMPAT;
3✔
94

95
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
3✔
96
}
3✔
97

98
static int DetectHostbitMatchToggle (Packet *p, const DetectXbitsData *fd)
UNCOV
99
{
×
UNCOV
100
    switch (fd->tracker) {
×
UNCOV
101
        case DETECT_XBITS_TRACK_IPSRC:
×
UNCOV
102
            if (p->host_src == NULL) {
×
103
                p->host_src = HostGetHostFromHash(&p->src);
×
104
                if (p->host_src == NULL)
×
105
                    return 0;
×
106
            }
×
UNCOV
107
            else
×
UNCOV
108
                HostLock(p->host_src);
×
109

UNCOV
110
            HostBitToggle(p->host_src, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
×
UNCOV
111
            HostUnlock(p->host_src);
×
UNCOV
112
            break;
×
UNCOV
113
        case DETECT_XBITS_TRACK_IPDST:
×
UNCOV
114
            if (p->host_dst == NULL) {
×
UNCOV
115
                p->host_dst = HostGetHostFromHash(&p->dst);
×
UNCOV
116
                if (p->host_dst == NULL)
×
117
                    return 0;
×
UNCOV
118
            }
×
119
            else
×
120
                HostLock(p->host_dst);
×
121

UNCOV
122
            HostBitToggle(p->host_dst, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
×
UNCOV
123
            HostUnlock(p->host_dst);
×
UNCOV
124
            break;
×
UNCOV
125
    }
×
UNCOV
126
    return 1;
×
UNCOV
127
}
×
128

129
/* return true even if bit not found */
130
static int DetectHostbitMatchUnset (Packet *p, const DetectXbitsData *fd)
UNCOV
131
{
×
UNCOV
132
    switch (fd->tracker) {
×
UNCOV
133
        case DETECT_XBITS_TRACK_IPSRC:
×
UNCOV
134
            if (p->host_src == NULL) {
×
135
                p->host_src = HostLookupHostFromHash(&p->src);
×
136
                if (p->host_src == NULL)
×
137
                    return 1;
×
138
            } else
×
UNCOV
139
                HostLock(p->host_src);
×
140

UNCOV
141
            HostBitUnset(p->host_src,fd->idx);
×
UNCOV
142
            HostUnlock(p->host_src);
×
UNCOV
143
            break;
×
144
        case DETECT_XBITS_TRACK_IPDST:
×
145
            if (p->host_dst == NULL) {
×
146
                p->host_dst = HostLookupHostFromHash(&p->dst);
×
147
                if (p->host_dst == NULL)
×
148
                    return 1;
×
149
            } else
×
150
                HostLock(p->host_dst);
×
151

152
            HostBitUnset(p->host_dst,fd->idx);
×
153
            HostUnlock(p->host_dst);
×
154
            break;
×
UNCOV
155
    }
×
UNCOV
156
    return 1;
×
UNCOV
157
}
×
158

159
static int DetectHostbitMatchSet (Packet *p, const DetectXbitsData *fd)
160
{
340✔
161
    switch (fd->tracker) {
340✔
162
        case DETECT_XBITS_TRACK_IPSRC:
340✔
163
            if (p->host_src == NULL) {
340✔
164
                p->host_src = HostGetHostFromHash(&p->src);
315✔
165
                if (p->host_src == NULL)
315✔
166
                    return 0;
×
167
            } else
315✔
168
                HostLock(p->host_src);
25✔
169

170
            HostBitSet(p->host_src, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
340✔
171
            HostUnlock(p->host_src);
340✔
172
            break;
340✔
173
        case DETECT_XBITS_TRACK_IPDST:
×
174
            if (p->host_dst == NULL) {
×
175
                p->host_dst = HostGetHostFromHash(&p->dst);
×
176
                if (p->host_dst == NULL)
×
177
                    return 0;
×
178
            } else
×
179
                HostLock(p->host_dst);
×
180

181
            HostBitSet(p->host_dst, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
×
182
            HostUnlock(p->host_dst);
×
183
            break;
×
184
    }
340✔
185
    return 1;
340✔
186
}
340✔
187

188
static int DetectHostbitMatchIsset (Packet *p, const DetectXbitsData *fd)
UNCOV
189
{
×
UNCOV
190
    int r = 0;
×
UNCOV
191
    switch (fd->tracker) {
×
UNCOV
192
        case DETECT_XBITS_TRACK_IPSRC:
×
UNCOV
193
            if (p->host_src == NULL) {
×
UNCOV
194
                p->host_src = HostLookupHostFromHash(&p->src);
×
UNCOV
195
                if (p->host_src == NULL)
×
UNCOV
196
                    return 0;
×
UNCOV
197
            } else
×
UNCOV
198
                HostLock(p->host_src);
×
199

UNCOV
200
            r = HostBitIsset(p->host_src, fd->idx, p->ts);
×
UNCOV
201
            HostUnlock(p->host_src);
×
UNCOV
202
            return r;
×
UNCOV
203
        case DETECT_XBITS_TRACK_IPDST:
×
UNCOV
204
            if (p->host_dst == NULL) {
×
UNCOV
205
                p->host_dst = HostLookupHostFromHash(&p->dst);
×
UNCOV
206
                if (p->host_dst == NULL)
×
UNCOV
207
                    return 0;
×
UNCOV
208
            } else
×
209
                HostLock(p->host_dst);
×
210

UNCOV
211
            r = HostBitIsset(p->host_dst, fd->idx, p->ts);
×
UNCOV
212
            HostUnlock(p->host_dst);
×
UNCOV
213
            return r;
×
UNCOV
214
    }
×
215
    return 0;
×
UNCOV
216
}
×
217

218
static int DetectHostbitMatchIsnotset (Packet *p, const DetectXbitsData *fd)
219
{
2✔
220
    int r = 0;
2✔
221
    switch (fd->tracker) {
2✔
222
        case DETECT_XBITS_TRACK_IPSRC:
2✔
223
            if (p->host_src == NULL) {
2✔
224
                p->host_src = HostLookupHostFromHash(&p->src);
2✔
225
                if (p->host_src == NULL)
2✔
226
                    return 1;
2✔
227
            } else
2✔
228
                HostLock(p->host_src);
×
229

230
            r = HostBitIsnotset(p->host_src, fd->idx, p->ts);
×
231
            HostUnlock(p->host_src);
×
232
            return r;
×
UNCOV
233
        case DETECT_XBITS_TRACK_IPDST:
×
UNCOV
234
            if (p->host_dst == NULL) {
×
UNCOV
235
                p->host_dst = HostLookupHostFromHash(&p->dst);
×
UNCOV
236
                if (p->host_dst == NULL)
×
UNCOV
237
                    return 1;
×
UNCOV
238
            } else
×
239
                HostLock(p->host_dst);
×
240

241
            r = HostBitIsnotset(p->host_dst, fd->idx, p->ts);
×
242
            HostUnlock(p->host_dst);
×
243
            return r;
×
244
    }
2✔
245
    return 0;
×
246
}
2✔
247

248
int DetectXbitMatchHost(Packet *p, const DetectXbitsData *xd)
249
{
342✔
250
    switch (xd->cmd) {
342✔
UNCOV
251
        case DETECT_XBITS_CMD_ISSET:
×
UNCOV
252
            return DetectHostbitMatchIsset(p,xd);
×
253
        case DETECT_XBITS_CMD_ISNOTSET:
2✔
254
            return DetectHostbitMatchIsnotset(p,xd);
2✔
255
        case DETECT_XBITS_CMD_SET:
340✔
256
            return DetectHostbitMatchSet(p,xd);
340✔
UNCOV
257
        case DETECT_XBITS_CMD_UNSET:
×
UNCOV
258
            return DetectHostbitMatchUnset(p,xd);
×
UNCOV
259
        case DETECT_XBITS_CMD_TOGGLE:
×
UNCOV
260
            return DetectHostbitMatchToggle(p,xd);
×
261
        default:
×
262
            SCLogError("unknown cmd %" PRIu32 "", xd->cmd);
×
263
            return 0;
×
264
    }
342✔
265

266
    return 0;
×
267
}
342✔
268

269
/*
270
 * returns 0: no match
271
 *         1: match
272
 *        -1: error
273
 */
274

275
static int DetectHostbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
276
        const Signature *s, const SigMatchCtx *ctx)
277
{
342✔
278
    const DetectXbitsData *xd = (const DetectXbitsData *)ctx;
342✔
279
    if (xd == NULL)
342✔
280
        return 0;
×
281

282
    return DetectXbitMatchHost(p, xd);
342✔
283
}
342✔
284

285
static int DetectHostbitParse(const char *str, char *cmd, int cmd_len,
286
    char *name, int name_len, char *dir, int dir_len)
287
{
2,415✔
288
    int rc;
2,415✔
289
    size_t pcre2len;
2,415✔
290

291
    pcre2_match_data *match = NULL;
2,415✔
292
    int count = DetectParsePcreExec(&parse_regex, &match, str, 0, 0);
2,415✔
293
    if (count != 2 && count != 3 && count != 4) {
2,415✔
294
        SCLogError("\"%s\" is not a valid setting for hostbits.", str);
31✔
295
        goto error;
31✔
296
    }
31✔
297

298
    pcre2len = cmd_len;
2,384✔
299
    rc = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)cmd, &pcre2len);
2,384✔
300
    if (rc < 0) {
2,384✔
301
        SCLogError("pcre2_substring_copy_bynumber failed");
39✔
302
        goto error;
39✔
303
    }
39✔
304

305
    if (count >= 3) {
2,345✔
306
        pcre2len = name_len;
2,238✔
307
        rc = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)name, &pcre2len);
2,238✔
308
        if (rc < 0) {
2,238✔
309
            SCLogError("pcre2_substring_copy_bynumber failed");
1✔
310
            goto error;
1✔
311
        }
1✔
312
        if (count >= 4) {
2,237✔
313
            pcre2len = dir_len;
82✔
314
            rc = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)dir, &pcre2len);
82✔
315
            if (rc < 0) {
82✔
316
                SCLogError("pcre2_substring_copy_bynumber failed");
6✔
317
                goto error;
6✔
318
            }
6✔
319
        }
82✔
320
    }
2,237✔
321

322
    pcre2_match_data_free(match);
2,345✔
323
    return 1;
2,338✔
324

325
error:
77✔
326
    if (match) {
77✔
327
        pcre2_match_data_free(match);
77✔
328
    }
77✔
329
    return 0;
77✔
330
}
2,345✔
331

332
int DetectHostbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
333
{
2,415✔
334
    DetectXbitsData *cd = NULL;
2,415✔
335
    uint8_t fb_cmd = 0;
2,415✔
336
    uint8_t hb_dir = 0;
2,415✔
337
    char fb_cmd_str[16] = "", fb_name[256] = "";
2,415✔
338
    char hb_dir_str[16] = "";
2,415✔
339

340
    if (!DetectHostbitParse(rawstr, fb_cmd_str, sizeof(fb_cmd_str),
2,415✔
341
            fb_name, sizeof(fb_name), hb_dir_str, sizeof(hb_dir_str))) {
2,415✔
342
        return -1;
77✔
343
    }
77✔
344

345
    if (strlen(hb_dir_str) > 0) {
2,338✔
346
        if (strcmp(hb_dir_str, "src") == 0)
76✔
UNCOV
347
            hb_dir = DETECT_XBITS_TRACK_IPSRC;
×
348
        else if (strcmp(hb_dir_str, "dst") == 0)
76✔
349
            hb_dir = DETECT_XBITS_TRACK_IPDST;
22✔
350
        else if (strcmp(hb_dir_str, "both") == 0) {
54✔
351
            //hb_dir = DETECT_XBITS_TRACK_IPBOTH;
352
            SCLogError("'both' not implemented");
1✔
353
            goto error;
1✔
354
        } else {
53✔
355
            // TODO
356
            goto error;
53✔
357
        }
53✔
358
    }
76✔
359

360
    if (strcmp(fb_cmd_str,"noalert") == 0) {
2,284✔
361
        fb_cmd = DETECT_XBITS_CMD_NOALERT;
12✔
362
    } else if (strcmp(fb_cmd_str,"isset") == 0) {
2,272✔
363
        fb_cmd = DETECT_XBITS_CMD_ISSET;
541✔
364
    } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
1,731✔
365
        fb_cmd = DETECT_XBITS_CMD_ISNOTSET;
512✔
366
    } else if (strcmp(fb_cmd_str,"set") == 0) {
1,219✔
367
        fb_cmd = DETECT_XBITS_CMD_SET;
934✔
368
    } else if (strcmp(fb_cmd_str,"unset") == 0) {
934✔
369
        fb_cmd = DETECT_XBITS_CMD_UNSET;
35✔
370
    } else if (strcmp(fb_cmd_str,"toggle") == 0) {
250✔
371
        fb_cmd = DETECT_XBITS_CMD_TOGGLE;
5✔
372
    } else {
245✔
373
        SCLogError("ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
245✔
374
        goto error;
245✔
375
    }
245✔
376

377
    switch (fb_cmd) {
2,039✔
378
        case DETECT_XBITS_CMD_NOALERT:
12✔
379
            if (strlen(fb_name) != 0)
12✔
380
                goto error;
2✔
381
            s->action &= ~ACTION_ALERT;
10✔
382
            return 0;
10✔
383
        case DETECT_XBITS_CMD_ISNOTSET:
512✔
384
        case DETECT_XBITS_CMD_ISSET:
1,053✔
385
        case DETECT_XBITS_CMD_SET:
1,987✔
386
        case DETECT_XBITS_CMD_UNSET:
2,022✔
387
        case DETECT_XBITS_CMD_TOGGLE:
2,027✔
388
        default:
2,027✔
389
            if (strlen(fb_name) == 0)
2,027✔
390
                goto error;
85✔
391
            break;
1,942✔
392
    }
2,039✔
393

394
    cd = SCMalloc(sizeof(DetectXbitsData));
1,942✔
395
    if (unlikely(cd == NULL))
1,942✔
396
        goto error;
×
397

398
    uint32_t varname_id = VarNameStoreRegister(fb_name, VAR_TYPE_HOST_BIT);
1,942✔
399
    if (unlikely(varname_id == 0))
1,942✔
400
        goto error;
×
401
    cd->idx = varname_id;
1,942✔
402
    cd->cmd = fb_cmd;
1,942✔
403
    cd->tracker = hb_dir;
1,942✔
404
    cd->type = VAR_TYPE_HOST_BIT;
1,942✔
405
    cd->expire = 300;
1,942✔
406

407
    SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
1,942✔
408
        cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
1,942✔
409

410
    /* Okay so far so good, lets get this into a SigMatch
411
     * and put it in the Signature. */
412

413
    switch (fb_cmd) {
1,942✔
414
        /* case DETECT_XBITS_CMD_NOALERT can't happen here */
415

416
        case DETECT_XBITS_CMD_ISNOTSET:
512✔
417
        case DETECT_XBITS_CMD_ISSET:
1,053✔
418
            /* checks, so packet list */
419
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HOSTBITS, (SigMatchCtx *)cd,
1,053✔
420
                        DETECT_SM_LIST_MATCH) == NULL) {
1,053✔
421
                goto error;
×
422
            }
×
423
            break;
1,053✔
424

425
        case DETECT_XBITS_CMD_SET:
1,053✔
426
        case DETECT_XBITS_CMD_UNSET:
885✔
427
        case DETECT_XBITS_CMD_TOGGLE:
889✔
428
            /* modifiers, only run when entire sig has matched */
429
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_HOSTBITS, (SigMatchCtx *)cd,
889✔
430
                        DETECT_SM_LIST_POSTMATCH) == NULL) {
889✔
431
                goto error;
×
432
            }
×
433
            break;
889✔
434

435
        // suppress coverity warning as scan-build-7 warns w/o this.
436
        // coverity[deadcode : FALSE]
437
        default:
889✔
438
            goto error;
×
439
    }
1,942✔
440

441
    return 0;
1,942✔
442

443
error:
386✔
444
    if (cd != NULL)
386✔
445
        SCFree(cd);
×
446
    return -1;
386✔
447
}
1,942✔
448

449
void DetectHostbitFree (DetectEngineCtx *de_ctx, void *ptr)
450
{
1,942✔
451
    DetectXbitsData *fd = (DetectXbitsData *)ptr;
1,942✔
452

453
    if (fd == NULL)
1,942✔
454
        return;
×
455
    VarNameStoreUnregister(fd->idx, VAR_TYPE_HOST_BIT);
1,942✔
456

457
    SCFree(fd);
1,942✔
458
}
1,942✔
459

460
#ifdef UNITTESTS
461

462
static void HostBitsTestSetup(void)
463
{
464
    StorageCleanup();
465
    StorageInit();
466
    HostBitInitCtx();
467
    StorageFinalize();
468
    HostInitConfig(true);
469
}
470

471
static void HostBitsTestShutdown(void)
472
{
473
    HostShutdown();
474
    StorageCleanup();
475
}
476

477
static int HostBitsTestParse01(void)
478
{
479
    char cmd[16] = "", name[256] = "", dir[16] = "";
480

481
    /* No direction. */
482
    FAIL_IF(!DetectHostbitParse("isset,name", cmd, sizeof(cmd), name,
483
            sizeof(name), dir, sizeof(dir)));
484
    FAIL_IF(strcmp(cmd, "isset") != 0);
485
    FAIL_IF(strcmp(name, "name") != 0);
486
    FAIL_IF(strlen(dir));
487

488
    /* No direction, leading space. */
489
    *cmd = '\0';
490
    *name = '\0';
491
    *dir = '\0';
492
    FAIL_IF(!DetectHostbitParse("isset, name", cmd, sizeof(cmd), name,
493
            sizeof(name), dir, sizeof(dir)));
494
    FAIL_IF(strcmp(cmd, "isset") != 0);
495
    FAIL_IF(strcmp(name, "name") != 0);
496

497
    /* No direction, trailing space. */
498
    *cmd = '\0';
499
    *name = '\0';
500
    *dir = '\0';
501
    FAIL_IF(!DetectHostbitParse("isset,name ", cmd, sizeof(cmd), name,
502
            sizeof(name), dir, sizeof(dir)));
503
    FAIL_IF(strcmp(cmd, "isset") != 0);
504
    FAIL_IF(strcmp(name, "name") != 0);
505

506
    /* No direction, leading and trailing space. */
507
    *cmd = '\0';
508
    *name = '\0';
509
    *dir = '\0';
510
    FAIL_IF(!DetectHostbitParse("isset, name ", cmd, sizeof(cmd), name,
511
            sizeof(name), dir, sizeof(dir)));
512
    FAIL_IF(strcmp(cmd, "isset") != 0);
513
    FAIL_IF(strcmp(name, "name") != 0);
514

515
    /* With direction. */
516
    *cmd = '\0';
517
    *name = '\0';
518
    *dir = '\0';
519
    FAIL_IF(!DetectHostbitParse("isset,name,src", cmd, sizeof(cmd), name,
520
            sizeof(name), dir, sizeof(dir)));
521
    FAIL_IF(strcmp(cmd, "isset") != 0);
522
    FAIL_IF(strcmp(name, "name") != 0);
523
    FAIL_IF(strcmp(dir, "src") != 0);
524

525
    /* With direction - leading and trailing spaces on name. */
526
    *cmd = '\0';
527
    *name = '\0';
528
    *dir = '\0';
529
    FAIL_IF(!DetectHostbitParse("isset, name ,src", cmd, sizeof(cmd), name,
530
            sizeof(name), dir, sizeof(dir)));
531
    FAIL_IF(strcmp(cmd, "isset") != 0);
532
    FAIL_IF(strcmp(name, "name") != 0);
533
    FAIL_IF(strcmp(dir, "src") != 0);
534

535
    /* With direction - space around direction. */
536
    *cmd = '\0';
537
    *name = '\0';
538
    *dir = '\0';
539
    FAIL_IF(!DetectHostbitParse("isset, name , src ", cmd, sizeof(cmd), name,
540
            sizeof(name), dir, sizeof(dir)));
541
    FAIL_IF(strcmp(cmd, "isset") != 0);
542
    FAIL_IF(strcmp(name, "name") != 0);
543
    FAIL_IF(strcmp(dir, "src") != 0);
544

545
    /* Name with space, no direction - should fail. */
546
    *cmd = '\0';
547
    *name = '\0';
548
    *dir = '\0';
549
    FAIL_IF(DetectHostbitParse("isset, name withspace ", cmd, sizeof(cmd), name,
550
            sizeof(name), dir, sizeof(dir)));
551

552
    PASS;
553
}
554

555
/**
556
 * \test HostBitsTestSig01 is a test for a valid noalert flowbits option
557
 *
558
 *  \retval 1 on success
559
 *  \retval 0 on failure
560
 */
561

562
static int HostBitsTestSig01(void)
563
{
564
    uint8_t *buf = (uint8_t *)
565
                    "GET /one/ HTTP/1.1\r\n"
566
                    "Host: one.example.org\r\n"
567
                    "\r\n";
568
    uint16_t buflen = strlen((char *)buf);
569
    Packet *p = PacketGetFromAlloc();
570
    FAIL_IF_NULL(p);
571
    Signature *s = NULL;
572
    ThreadVars th_v;
573
    DetectEngineThreadCtx *det_ctx = NULL;
574
    DetectEngineCtx *de_ctx = NULL;
575

576
    memset(&th_v, 0, sizeof(th_v));
577
    StatsThreadInit(&th_v.stats);
578
    p->src.family = AF_INET;
579
    p->dst.family = AF_INET;
580
    p->payload = buf;
581
    p->payload_len = buflen;
582
    p->proto = IPPROTO_TCP;
583

584
    HostBitsTestSetup();
585

586
    de_ctx = DetectEngineCtxInit();
587
    FAIL_IF_NULL(de_ctx);
588

589
    de_ctx->flags |= DE_QUIET;
590

591
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (hostbits:set,abc; content:\"GET \"; sid:1;)");
592
    FAIL_IF_NULL(s);
593

594
    SigGroupBuild(de_ctx);
595
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
596

597
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
598

599
    PacketFree(p);
600
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
601
    DetectEngineCtxFree(de_ctx);
602
    HostBitsTestShutdown();
603
    StatsThreadCleanup(&th_v.stats);
604
    PASS;
605
}
606

607
/**
608
 * \test various options
609
 *
610
 *  \retval 1 on success
611
 *  \retval 0 on failure
612
 */
613

614
static int HostBitsTestSig02(void)
615
{
616
    Signature *s = NULL;
617
    ThreadVars th_v;
618
    DetectEngineCtx *de_ctx = NULL;
619

620
    memset(&th_v, 0, sizeof(th_v));
621

622
    de_ctx = DetectEngineCtxInit();
623
    FAIL_IF_NULL(de_ctx);
624

625
    de_ctx->flags |= DE_QUIET;
626

627
    s = DetectEngineAppendSig(de_ctx,
628
            "alert ip any any -> any any (hostbits:isset,abc,src; content:\"GET \"; sid:1;)");
629
    FAIL_IF_NULL(s);
630

631
    s = DetectEngineAppendSig(de_ctx,
632
            "alert ip any any -> any any (hostbits:isnotset,abc,dst; content:\"GET \"; sid:2;)");
633
    FAIL_IF_NULL(s);
634

635
    s = DetectEngineAppendSig(de_ctx,
636
            "alert ip any any -> any any (hostbits:!isset,abc,dst; content:\"GET \"; sid:3;)");
637
    FAIL_IF_NOT_NULL(s);
638

639
/* TODO reenable after both is supported
640
    s = DetectEngineAppendSig(de_ctx,
641
            "alert ip any any -> any any (hostbits:set,abc,both; content:\"GET \"; sid:3;)");
642
    FAIL_IF_NULL(s);
643
*/
644
    s = DetectEngineAppendSig(de_ctx,
645
            "alert ip any any -> any any (hostbits:unset,abc,src; content:\"GET \"; sid:4;)");
646
    FAIL_IF_NULL(s);
647

648
    s = DetectEngineAppendSig(de_ctx,
649
            "alert ip any any -> any any (hostbits:toggle,abc,dst; content:\"GET \"; sid:5;)");
650
    FAIL_IF_NULL(s);
651

652
    DetectEngineCtxFree(de_ctx);
653
    PASS;
654
}
655

656
/**
657
 * \test HostBitsTestSig03 is a test check idx value
658
 *
659
 *  \retval 1 on success
660
 *  \retval 0 on failure
661
 */
662

663
static int HostBitsTestSig03(void)
664
{
665
    uint8_t *buf = (uint8_t *)
666
                    "GET /one/ HTTP/1.1\r\n"
667
                    "Host: one.example.org\r\n"
668
                    "\r\n";
669
    uint16_t buflen = strlen((char *)buf);
670
    Packet *p = PacketGetFromAlloc();
671
    if (unlikely(p == NULL))
672
        return 0;
673
    Signature *s = NULL;
674
    ThreadVars th_v;
675
    DetectEngineThreadCtx *det_ctx = NULL;
676
    DetectEngineCtx *de_ctx = NULL;
677
    int idx = 0;
678

679
    memset(&th_v, 0, sizeof(th_v));
680
    StatsThreadInit(&th_v.stats);
681
    p->src.family = AF_INET;
682
    p->dst.family = AF_INET;
683
    p->payload = buf;
684
    p->payload_len = buflen;
685
    p->proto = IPPROTO_TCP;
686

687
    HostBitsTestSetup();
688

689
    de_ctx = DetectEngineCtxInit();
690
    FAIL_IF_NULL(de_ctx);
691

692
    de_ctx->flags |= DE_QUIET;
693

694
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; hostbits:isset,fbt; content:\"GET \"; sid:1;)");
695
    FAIL_IF_NULL(s);
696

697
    idx = VarNameStoreRegister("fbt", VAR_TYPE_HOST_BIT);
698
    FAIL_IF(idx == 0);
699

700
    SigGroupBuild(de_ctx);
701
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
702

703
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
704

705
    PacketFree(p);
706
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
707
    DetectEngineCtxFree(de_ctx);
708
    HostBitsTestShutdown();
709
    StatsThreadCleanup(&th_v.stats);
710
    PASS;
711
}
712

713
/**
714
 * \brief this function registers unit tests for HostBits
715
 */
716
void HostBitsRegisterTests(void)
717
{
718
    UtRegisterTest("HostBitsTestParse01", HostBitsTestParse01);
719
    UtRegisterTest("HostBitsTestSig01", HostBitsTestSig01);
720
    UtRegisterTest("HostBitsTestSig02", HostBitsTestSig02);
721
    UtRegisterTest("HostBitsTestSig03", HostBitsTestSig03);
722
}
723
#endif /* UNITTESTS */
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