Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-transform-dotprefix.c
Line
Count
Source
1
/* Copyright (C) 2020 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 Jeff Lucovsky <jeff@lucovsky.org>
22
 *
23
 * Implements the dotprefix transformation
24
 */
25
26
#include "suricata-common.h"
27
28
#include "detect.h"
29
#include "detect-engine.h"
30
#include "detect-engine-prefilter.h"
31
#include "detect-parse.h"
32
#include "detect-transform-dotprefix.h"
33
#include "detect-engine-build.h"
34
35
#include "util-unittest.h"
36
#include "util-print.h"
37
#include "util-memrchr.h"
38
#include "util-memcpy.h"
39
40
static int DetectTransformDotPrefixSetup (DetectEngineCtx *, Signature *, const char *);
41
#ifdef UNITTESTS
42
static void DetectTransformDotPrefixRegisterTests(void);
43
#endif
44
static void TransformDotPrefix(InspectionBuffer *buffer, void *options);
45
46
void DetectTransformDotPrefixRegister(void)
47
34
{
48
34
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].name = "dotprefix";
49
34
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].desc =
50
34
        "modify buffer to extract the dotprefix";
51
34
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].url =
52
34
        "/rules/transforms.html#dotprefix";
53
34
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].Transform = TransformDotPrefix;
54
34
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].Setup = DetectTransformDotPrefixSetup;
55
#ifdef UNITTESTS
56
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].RegisterTests =
57
        DetectTransformDotPrefixRegisterTests;
58
#endif
59
34
    sigmatch_table[DETECT_TRANSFORM_DOTPREFIX].flags |= SIGMATCH_NOOPT;
60
34
}
61
62
/**
63
 *  \internal
64
 *  \brief Extract the dotprefix, if any, the last pattern match, either content or uricontent
65
 *  \param det_ctx detection engine ctx
66
 *  \param s signature
67
 *  \param nullstr should be null
68
 *  \retval 0 ok
69
 *  \retval -1 failure
70
 */
71
static int DetectTransformDotPrefixSetup (DetectEngineCtx *de_ctx, Signature *s, const char *nullstr)
72
1.65k
{
73
1.65k
    SCEnter();
74
1.65k
    int r = DetectSignatureAddTransform(s, DETECT_TRANSFORM_DOTPREFIX, NULL);
75
1.65k
    SCReturnInt(r);
76
1.65k
}
77
78
/**
79
 * \brief Return the dotprefix, if any, in the last pattern match.
80
 *
81
 * Input values are modified by prefixing with a ".".
82
 *
83
 * Rule: "alert dns any any -> any any (dns_query; dotprefix; content:".google.com"; sid:1;)"
84
 * 1. hello.google.com --> match
85
 * 2. hey.agoogle.com --> no match
86
 * 3. agoogle.com --> no match
87
 * 4. something.google.com.au --> match
88
 * 5. google.com --> match
89
 *
90
 * To match on the dotprefix only:
91
 * Rule: "alert dns any any -> any any (dns_query; dotprefix; content:".google.com"; endswith; sid:1;)"
92
 *
93
 * 1. hello.google.com --> match
94
 * 2. hey.agoogle.com --> no match
95
 * 3. agoogle.com --> no match
96
 * 4. something.google.com.au --> no match
97
 * 5. google.com --> match
98
 *
99
 * To match on a TLD:
100
 * Rule: "alert dns any any -> any any (dns_query; dotprefix; content:".co.uk"; endswith; sid:1;)"
101
 *
102
 * 1. hello.google.com --> no match
103
 * 2. hey.agoogle.com --> no match
104
 * 3. agoogle.com --> no match
105
 * 4. something.google.co.uk --> match
106
 * 5. google.com --> no match
107
 */
108
static void TransformDotPrefix(InspectionBuffer *buffer, void *options)
109
553
{
110
553
    const size_t input_len = buffer->inspect_len;
111
112
553
    if (input_len) {
113
        // For the leading '.'
114
553
        uint8_t *output = InspectionBufferCheckAndExpand(buffer, input_len + 1);
115
553
        if (output == NULL) {
116
0
            return;
117
0
        }
118
119
553
        memmove(&output[1], buffer->inspect, input_len);
120
553
        output[0] = '.';
121
553
        InspectionBufferTruncate(buffer, input_len + 1);
122
553
    }
123
553
}
124
125
#ifdef UNITTESTS
126
static int DetectTransformDotPrefixTest01(void)
127
{
128
    const uint8_t *input = (const uint8_t *)"example.com";
129
    uint32_t input_len = strlen((char *)input);
130
131
    const char *result = ".example.com";
132
    uint32_t result_len = strlen((char *)result);
133
134
    InspectionBuffer buffer;
135
    InspectionBufferInit(&buffer, input_len);
136
    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
137
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
138
    TransformDotPrefix(&buffer, NULL);
139
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
140
    FAIL_IF_NOT(buffer.inspect_len == result_len);
141
    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
142
    InspectionBufferFree(&buffer);
143
    PASS;
144
}
145
146
static int DetectTransformDotPrefixTest02(void)
147
{
148
    const uint8_t *input = (const uint8_t *)"hello.example.com";
149
    uint32_t input_len = strlen((char *)input);
150
151
    const char *result = ".hello.example.com";
152
    uint32_t result_len = strlen((char *)result);
153
154
    InspectionBuffer buffer;
155
    InspectionBufferInit(&buffer, input_len);
156
    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
157
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
158
    TransformDotPrefix(&buffer, NULL);
159
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
160
    FAIL_IF_NOT(buffer.inspect_len == result_len);
161
    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
162
    InspectionBufferFree(&buffer);
163
    PASS;
164
}
165
166
static int DetectTransformDotPrefixTest03(void)
167
{
168
    const char rule[] = "alert dns any any -> any any (dns.query; dotprefix; content:\".google.com\"; sid:1;)";
169
    ThreadVars th_v;
170
    DetectEngineThreadCtx *det_ctx = NULL;
171
    memset(&th_v, 0, sizeof(th_v));
172
173
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
174
    FAIL_IF_NULL(de_ctx);
175
    Signature *s = DetectEngineAppendSig(de_ctx, rule);
176
    FAIL_IF_NULL(s);
177
    SigGroupBuild(de_ctx);
178
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
179
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
180
    DetectEngineCtxFree(de_ctx);
181
    PASS;
182
}
183
184
static void DetectTransformDotPrefixRegisterTests(void)
185
{
186
    UtRegisterTest("DetectTransformDotPrefixTest01", DetectTransformDotPrefixTest01);
187
    UtRegisterTest("DetectTransformDotPrefixTest02", DetectTransformDotPrefixTest02);
188
    UtRegisterTest("DetectTransformDotPrefixTest03", DetectTransformDotPrefixTest03);
189
}
190
#endif