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