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