Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/log-cf-common.c
Line
Count
Source
1
/* Copyright (C) 2007-2016 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 Paulo Pacheco <fooinha@gmail.com>
22
 * \author Victor Julien <victor@inliniac.net>
23
 * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com>
24
 *
25
 * Common custom logging format
26
 */
27
28
#include "log-cf-common.h"
29
#include "util-print.h"
30
#include "util-unittest.h"
31
#include "util-time.h"
32
#include "util-debug.h"
33
34
/**
35
 *  \brief Creates a custom format node
36
 *  \retval LogCustomFormatNode * ptr if created
37
 *  \retval NULL if failed to allocate
38
 */
39
LogCustomFormatNode *LogCustomFormatNodeAlloc(void)
40
0
{
41
0
    LogCustomFormatNode * node = SCCalloc(1, sizeof(LogCustomFormatNode));
42
0
    if (unlikely(node == NULL)) {
43
0
        SCLogError("Failed to alloc custom format node");
44
0
        return NULL;
45
0
    }
46
0
    return node;
47
0
}
48
49
/**
50
 *  \brief Creates a custom format.
51
 *  \retval LogCustomFormat * ptr if created
52
 *  \retval NULL if failed to allocate
53
 */
54
LogCustomFormat *LogCustomFormatAlloc(void)
55
0
{
56
0
    LogCustomFormat * cf = SCCalloc(1, sizeof(LogCustomFormat));
57
0
    if (unlikely(cf == NULL)) {
58
0
        SCLogError("Failed to alloc custom format");
59
0
        return NULL;
60
0
    }
61
0
    return cf;
62
0
}
63
64
/**
65
 *  \brief Frees memory held by a custom format node
66
 *  \param LogCustomFormatNode * node - node to release
67
 */
68
void LogCustomFormatNodeFree(LogCustomFormatNode *node)
69
0
{
70
0
    if (node==NULL)
71
0
        return;
72
73
0
    SCFree(node);
74
0
}
75
76
/**
77
 *  \brief Frees memory held by a custom format
78
 *  \param LogCustomFormat * cf - format to release
79
 */
80
void LogCustomFormatFree(LogCustomFormat *cf)
81
0
{
82
0
    if (cf==NULL)
83
0
        return;
84
85
0
    for (size_t i = 0; i < cf->cf_n; ++i) {
86
0
        LogCustomFormatNodeFree(cf->cf_nodes[i]);
87
0
    }
88
0
    SCFree(cf);
89
0
}
90
91
/**
92
 *  \brief Parses and saves format nodes for custom format
93
 *  \param LogCustomFormat * cf - custom format to build
94
 *  \param const char * format - string with format specification
95
 */
96
int LogCustomFormatParse(LogCustomFormat *cf, const char *format)
97
0
{
98
0
    const char *p, *np;
99
0
    uint32_t n;
100
0
    LogCustomFormatNode *node = NULL;
101
102
0
    if (cf==NULL)
103
0
        return 0;
104
105
0
    if (format==NULL)
106
0
        return 0;
107
108
0
    p=format;
109
110
0
    for (cf->cf_n = 0; cf->cf_n < LOG_MAXN_NODES-1 && p && *p != '\0';){
111
112
0
        node = LogCustomFormatNodeAlloc();
113
0
        if (node == NULL) {
114
0
            goto parsererror;
115
0
        }
116
0
        node->maxlen = 0;
117
118
0
        if (*p != '%'){
119
            /* Literal found in format string */
120
0
            node->type = LOG_CF_LITERAL;
121
0
            np = strchr(p, '%');
122
0
            if (np == NULL){
123
0
                n = LOG_NODE_STRLEN-2;
124
0
                np = NULL; /* End */
125
0
            }else{
126
0
                n = np-p;
127
0
            }
128
0
            strlcpy(node->data,p,n+1);
129
0
            p = np;
130
0
        } else {
131
            /* Non Literal found in format string */
132
0
            p++;
133
0
            if (*p == '[') { /* Check if maxlength has been specified (ie: [25]) */
134
0
                p++;
135
0
                np = strchr(p, ']');
136
0
                if (np != NULL) {
137
0
                    if (np-p > 0 && np-p < 10){
138
0
                        long maxlen = strtol(p,NULL,10);
139
0
                        if (maxlen > 0 && maxlen < LOG_NODE_MAXOUTPUTLEN) {
140
0
                            node->maxlen = (uint32_t) maxlen;
141
0
                        }
142
0
                    } else {
143
0
                        goto parsererror;
144
0
                    }
145
0
                    p = np + 1;
146
0
                } else {
147
0
                    goto parsererror;
148
0
                }
149
0
            }
150
0
            if (*p == '{') { /* Simple format char */
151
0
                np = strchr(p, '}');
152
0
                if (np != NULL && np-p > 1 && np-p < LOG_NODE_STRLEN-2) {
153
0
                    p++;
154
0
                    n = np-p;
155
0
                    strlcpy(node->data, p, n+1);
156
0
                    p = np;
157
0
                } else {
158
0
                    goto parsererror;
159
0
                }
160
0
                p++;
161
0
            } else {
162
0
                node->data[0] = '\0';
163
0
            }
164
0
            node->type = *p;
165
0
            if (*p == '%'){
166
0
                node->type = LOG_CF_LITERAL;
167
0
                strlcpy(node->data, "%", 2);
168
0
            }
169
0
            p++;
170
0
        }
171
0
        LogCustomFormatAddNode(cf, node);
172
0
    }
173
0
    return 1;
174
175
0
parsererror:
176
0
    LogCustomFormatNodeFree(node);
177
0
    return 0;
178
0
}
179
180
/**
181
 *  \brief Adds a node to custom format
182
 *  \param LogCustomFormat * cf - custom format
183
 *  \param LogCustomFormatNode * node - node to add
184
 */
185
void LogCustomFormatAddNode(LogCustomFormat *cf, LogCustomFormatNode *node)
186
0
{
187
0
    if (cf == NULL || node == NULL)
188
0
        return;
189
190
0
    if (cf->cf_n == LOG_MAXN_NODES) {
191
0
        SCLogWarning("Too many options for custom format");
192
0
        return;
193
0
    }
194
195
#ifdef DEBUG
196
    SCLogDebug("%d-> n.type=[%d] n.maxlen=[%d] n.data=[%s]",
197
            cf->cf_n, node->type, node->maxlen, node->data);
198
#endif
199
200
0
    cf->cf_nodes[cf->cf_n] = node;
201
0
    cf->cf_n++;
202
0
}
203
204
/**
205
 *  \brief Writes a timestamp with given format into a MemBuffer
206
 *  \param MemBuffer * buffer - where to write
207
 *  \param const char * fmt - format to be used write timestamp
208
 *  \param const struct timeveal *ts  - the timestamp
209
 *
210
 */
211
void LogCustomFormatWriteTimestamp(MemBuffer *buffer, const char *fmt, const SCTime_t ts)
212
0
{
213
214
0
    time_t time = SCTIME_SECS(ts);
215
0
    struct tm local_tm;
216
0
    struct tm *timestamp = SCLocalTime(time, &local_tm);
217
0
    char buf[128] = {0};
218
0
    const char * fmt_to_use = TIMESTAMP_DEFAULT_FORMAT;
219
220
0
    if (fmt && *fmt != '\0') {
221
0
        fmt_to_use = fmt;
222
0
    }
223
224
0
    CreateFormattedTimeString (timestamp, fmt_to_use, buf, sizeof(buf));
225
0
    PrintRawUriBuf((char *)buffer->buffer, &buffer->offset,
226
0
                   buffer->size, (uint8_t *)buf,strlen(buf));
227
0
}
228
229
#ifdef UNITTESTS
230
/**
231
 * \internal
232
 * \brief This test tests default timestamp format
233
 */
234
static int LogCustomFormatTest01(void)
235
{
236
    // strftime which underpins LogCustomFormatWriteTimestamp doesn't
237
    // seem to function properly on MinGW
238
#ifndef __MINGW32__
239
    struct tm tm;
240
    tm.tm_sec = 0;
241
    tm.tm_min = 30;
242
    tm.tm_hour = 4;
243
    tm.tm_mday = 13;
244
    tm.tm_mon = 0;
245
    tm.tm_year = 114;
246
    tm.tm_wday = 1;
247
    tm.tm_yday = 13;
248
    tm.tm_isdst = 0;
249
    SCTime_t ts = SCTIME_FROM_SECS(mktime(&tm));
250
251
    MemBuffer *buffer = MemBufferCreateNew(62);
252
    if (!buffer) {
253
        return 0;
254
    }
255
256
    LogCustomFormatWriteTimestamp(buffer, "", ts);
257
    /*
258
     * {buffer = "01/13/14-04:30:00", size = 62, offset = 17}
259
     */
260
    FAIL_IF_NOT( buffer->offset == 17);
261
    FAIL_IF(strcmp((char *)buffer->buffer, "01/13/14-04:30:00") != 0);
262
263
    MemBufferFree(buffer);
264
#endif
265
    return 1;
266
}
267
268
static void LogCustomFormatRegisterTests(void)
269
{
270
    UtRegisterTest("LogCustomFormatTest01", LogCustomFormatTest01);
271
}
272
#endif /* UNITTESTS */
273
274
void LogCustomFormatRegister(void)
275
71
{
276
#ifdef UNITTESTS
277
    LogCustomFormatRegisterTests();
278
#endif /* UNITTESTS */
279
71
}