/src/suricata7/src/log-httplog.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2013 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 | | * \author Ignacio Sanchez <sanchezmartin.ji@gmail.com> |
23 | | * |
24 | | * Implements http logging portion of the engine. |
25 | | */ |
26 | | |
27 | | #include "suricata-common.h" |
28 | | #include "detect.h" |
29 | | #include "pkt-var.h" |
30 | | #include "conf.h" |
31 | | |
32 | | #include "threads.h" |
33 | | #include "threadvars.h" |
34 | | #include "tm-threads.h" |
35 | | |
36 | | #include "util-print.h" |
37 | | #include "util-unittest.h" |
38 | | |
39 | | #include "util-debug.h" |
40 | | |
41 | | #include "output.h" |
42 | | #include "log-httplog.h" |
43 | | #include "app-layer-htp.h" |
44 | | #include "app-layer.h" |
45 | | #include "app-layer-parser.h" |
46 | | #include "util-privs.h" |
47 | | #include "util-buffer.h" |
48 | | |
49 | | #include "util-logopenfile.h" |
50 | | #include "util-time.h" |
51 | | #include "log-cf-common.h" |
52 | | |
53 | 4 | #define DEFAULT_LOG_FILENAME "http.log" |
54 | | |
55 | 71 | #define MODULE_NAME "LogHttpLog" |
56 | | |
57 | 4 | #define OUTPUT_BUFFER_SIZE 65535 |
58 | | |
59 | | TmEcode LogHttpLogThreadInit(ThreadVars *, const void *, void **); |
60 | | TmEcode LogHttpLogThreadDeinit(ThreadVars *, void *); |
61 | | static void LogHttpLogDeInitCtx(OutputCtx *); |
62 | | |
63 | | int LogHttpLogger(ThreadVars *tv, void *thread_data, const Packet *, Flow *f, void *state, void *tx, uint64_t tx_id); |
64 | | |
65 | | void LogHttpLogRegister (void) |
66 | 71 | { |
67 | 71 | OutputRegisterTxModule(LOGGER_HTTP, MODULE_NAME, "http-log", LogHttpLogInitCtx, ALPROTO_HTTP1, |
68 | 71 | LogHttpLogger, LogHttpLogThreadInit, LogHttpLogThreadDeinit, NULL); |
69 | 71 | } |
70 | | |
71 | 0 | #define LOG_HTTP_CF_REQUEST_HOST 'h' |
72 | 0 | #define LOG_HTTP_CF_REQUEST_PROTOCOL 'H' |
73 | 0 | #define LOG_HTTP_CF_REQUEST_METHOD 'm' |
74 | 0 | #define LOG_HTTP_CF_REQUEST_URI 'u' |
75 | | #define LOG_HTTP_CF_REQUEST_TIME 't' |
76 | 0 | #define LOG_HTTP_CF_REQUEST_HEADER 'i' |
77 | 0 | #define LOG_HTTP_CF_REQUEST_COOKIE 'C' |
78 | 0 | #define LOG_HTTP_CF_REQUEST_LEN 'b' |
79 | 0 | #define LOG_HTTP_CF_RESPONSE_STATUS 's' |
80 | 0 | #define LOG_HTTP_CF_RESPONSE_HEADER 'o' |
81 | 0 | #define LOG_HTTP_CF_RESPONSE_LEN 'B' |
82 | | |
83 | | |
84 | | typedef struct LogHttpFileCtx_ { |
85 | | LogFileCtx *file_ctx; |
86 | | uint32_t flags; /** Store mode */ |
87 | | LogCustomFormat *cf; |
88 | | } LogHttpFileCtx; |
89 | | |
90 | 0 | #define LOG_HTTP_DEFAULT 0 |
91 | 80.0k | #define LOG_HTTP_EXTENDED 1 |
92 | 80.0k | #define LOG_HTTP_CUSTOM 2 |
93 | | |
94 | | typedef struct LogHttpLogThread_ { |
95 | | LogHttpFileCtx *httplog_ctx; |
96 | | /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */ |
97 | | uint32_t uri_cnt; |
98 | | |
99 | | MemBuffer *buffer; |
100 | | } LogHttpLogThread; |
101 | | |
102 | | /* Retrieves the selected cookie value */ |
103 | | static uint32_t GetCookieValue(uint8_t *rawcookies, uint32_t rawcookies_len, char *cookiename, |
104 | | uint8_t **cookievalue) |
105 | 0 | { |
106 | 0 | uint8_t *p = rawcookies; |
107 | 0 | uint8_t *cn = p; /* ptr to cookie name start */ |
108 | 0 | uint8_t *cv = NULL; /* ptr to cookie value start */ |
109 | 0 | while (p < rawcookies + rawcookies_len) { |
110 | 0 | if (cv == NULL && *p == '=') { |
111 | 0 | cv = p + 1; |
112 | 0 | } else if (cv != NULL && (*p == ';' || p == rawcookies + rawcookies_len - 1) ) { |
113 | | /* Found end of cookie */ |
114 | 0 | p++; |
115 | 0 | if (strlen(cookiename) == (unsigned int) (cv-cn-1) && |
116 | 0 | strncmp(cookiename, (char *) cn, cv-cn-1) == 0) { |
117 | 0 | *cookievalue = cv; |
118 | 0 | return (uint32_t) (p-cv); |
119 | 0 | } |
120 | 0 | cv = NULL; |
121 | 0 | cn = p + 1; |
122 | 0 | } |
123 | 0 | p++; |
124 | 0 | } |
125 | 0 | return 0; |
126 | 0 | } |
127 | | |
128 | | /* Custom format logging */ |
129 | | static void LogHttpLogCustom(LogHttpLogThread *aft, htp_tx_t *tx, const SCTime_t ts, char *srcip, |
130 | | Port sp, char *dstip, Port dp) |
131 | 0 | { |
132 | 0 | LogHttpFileCtx *httplog_ctx = aft->httplog_ctx; |
133 | 0 | uint32_t i; |
134 | 0 | uint32_t datalen; |
135 | 0 | char buf[128]; |
136 | |
|
137 | 0 | uint8_t *cvalue = NULL; |
138 | 0 | uint32_t cvalue_len = 0; |
139 | |
|
140 | 0 | htp_header_t *h_request_hdr; |
141 | 0 | htp_header_t *h_response_hdr; |
142 | |
|
143 | 0 | for (i = 0; i < httplog_ctx->cf->cf_n; i++) { |
144 | 0 | h_request_hdr = NULL; |
145 | 0 | h_response_hdr = NULL; |
146 | |
|
147 | 0 | LogCustomFormatNode * node = httplog_ctx->cf->cf_nodes[i]; |
148 | 0 | if (! node) /* Should never happen */ |
149 | 0 | continue; |
150 | | |
151 | 0 | switch (node->type){ |
152 | 0 | case LOG_CF_LITERAL: |
153 | | /* LITERAL */ |
154 | 0 | MemBufferWriteString(aft->buffer, "%s", node->data); |
155 | 0 | break; |
156 | 0 | case LOG_CF_TIMESTAMP: |
157 | | /* TIMESTAMP */ |
158 | 0 | LogCustomFormatWriteTimestamp(aft->buffer, node->data, ts); |
159 | 0 | break; |
160 | 0 | case LOG_CF_TIMESTAMP_U: |
161 | | /* TIMESTAMP USECONDS */ |
162 | 0 | snprintf(buf, sizeof(buf), "%06u", (unsigned int)SCTIME_USECS(ts)); |
163 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
164 | 0 | (uint8_t *)buf, MIN(strlen(buf), 6)); |
165 | 0 | break; |
166 | 0 | case LOG_CF_CLIENT_IP: |
167 | | /* CLIENT IP ADDRESS */ |
168 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
169 | 0 | aft->buffer->size, (uint8_t *)srcip,strlen(srcip)); |
170 | 0 | break; |
171 | 0 | case LOG_CF_SERVER_IP: |
172 | | /* SERVER IP ADDRESS */ |
173 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
174 | 0 | aft->buffer->size, (uint8_t *)dstip,strlen(dstip)); |
175 | 0 | break; |
176 | 0 | case LOG_CF_CLIENT_PORT: |
177 | | /* CLIENT PORT */ |
178 | 0 | MemBufferWriteString(aft->buffer, "%" PRIu16 "", sp); |
179 | 0 | break; |
180 | 0 | case LOG_CF_SERVER_PORT: |
181 | | /* SERVER PORT */ |
182 | 0 | MemBufferWriteString(aft->buffer, "%" PRIu16 "", dp); |
183 | 0 | break; |
184 | 0 | case LOG_HTTP_CF_REQUEST_METHOD: |
185 | | /* METHOD */ |
186 | 0 | if (tx->request_method != NULL) { |
187 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
188 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_method), |
189 | 0 | bstr_len(tx->request_method)); |
190 | 0 | } else { |
191 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
192 | 0 | } |
193 | 0 | break; |
194 | 0 | case LOG_HTTP_CF_REQUEST_URI: |
195 | | /* URI */ |
196 | 0 | if (tx->request_uri != NULL) { |
197 | 0 | datalen = node->maxlen; |
198 | 0 | if (datalen == 0 || datalen > bstr_len(tx->request_uri)) { |
199 | 0 | datalen = bstr_len(tx->request_uri); |
200 | 0 | } |
201 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
202 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_uri), |
203 | 0 | datalen); |
204 | 0 | } else { |
205 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
206 | 0 | } |
207 | 0 | break; |
208 | 0 | case LOG_HTTP_CF_REQUEST_HOST: |
209 | | /* HOSTNAME */ |
210 | 0 | if (tx->request_hostname != NULL) |
211 | 0 | { |
212 | 0 | datalen = node->maxlen; |
213 | 0 | if (datalen == 0 || datalen > bstr_len(tx->request_hostname)) { |
214 | 0 | datalen = bstr_len(tx->request_hostname); |
215 | 0 | } |
216 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
217 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_hostname), |
218 | 0 | datalen); |
219 | 0 | } else { |
220 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
221 | 0 | } |
222 | 0 | break; |
223 | 0 | case LOG_HTTP_CF_REQUEST_PROTOCOL: |
224 | | /* PROTOCOL */ |
225 | 0 | if (tx->request_protocol != NULL) { |
226 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
227 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_protocol), |
228 | 0 | bstr_len(tx->request_protocol)); |
229 | 0 | } else { |
230 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
231 | 0 | } |
232 | 0 | break; |
233 | 0 | case LOG_HTTP_CF_REQUEST_HEADER: |
234 | | /* REQUEST HEADER */ |
235 | 0 | if (tx->request_headers != NULL) { |
236 | 0 | h_request_hdr = htp_table_get_c(tx->request_headers, node->data); |
237 | 0 | } |
238 | 0 | if (h_request_hdr != NULL) { |
239 | 0 | datalen = node->maxlen; |
240 | 0 | if (datalen == 0 || datalen > bstr_len(h_request_hdr->value)) { |
241 | 0 | datalen = bstr_len(h_request_hdr->value); |
242 | 0 | } |
243 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
244 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(h_request_hdr->value), |
245 | 0 | datalen); |
246 | 0 | } else { |
247 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
248 | 0 | } |
249 | 0 | break; |
250 | 0 | case LOG_HTTP_CF_REQUEST_COOKIE: |
251 | | /* REQUEST COOKIE */ |
252 | 0 | if (tx->request_headers != NULL) { |
253 | 0 | h_request_hdr = htp_table_get_c(tx->request_headers, "Cookie"); |
254 | 0 | if (h_request_hdr != NULL) { |
255 | 0 | cvalue_len = GetCookieValue((uint8_t *) bstr_ptr(h_request_hdr->value), |
256 | 0 | bstr_len(h_request_hdr->value), (char *) node->data, |
257 | 0 | &cvalue); |
258 | 0 | } |
259 | 0 | } |
260 | 0 | if (cvalue_len > 0 && cvalue != NULL) { |
261 | 0 | datalen = node->maxlen; |
262 | 0 | if (datalen == 0 || datalen > cvalue_len) { |
263 | 0 | datalen = cvalue_len; |
264 | 0 | } |
265 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
266 | 0 | aft->buffer->size, cvalue, datalen); |
267 | 0 | } else { |
268 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
269 | 0 | } |
270 | 0 | break; |
271 | 0 | case LOG_HTTP_CF_REQUEST_LEN: |
272 | | /* REQUEST LEN */ |
273 | 0 | MemBufferWriteString(aft->buffer, "%"PRIuMAX"", (uintmax_t)tx->request_message_len); |
274 | 0 | break; |
275 | 0 | case LOG_HTTP_CF_RESPONSE_STATUS: |
276 | | /* RESPONSE STATUS */ |
277 | 0 | if (tx->response_status != NULL) { |
278 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
279 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(tx->response_status), |
280 | 0 | bstr_len(tx->response_status)); |
281 | 0 | } else { |
282 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
283 | 0 | } |
284 | 0 | break; |
285 | 0 | case LOG_HTTP_CF_RESPONSE_HEADER: |
286 | | /* RESPONSE HEADER */ |
287 | 0 | if (tx->response_headers != NULL) { |
288 | 0 | h_response_hdr = htp_table_get_c(tx->response_headers, |
289 | 0 | node->data); |
290 | 0 | } |
291 | 0 | if (h_response_hdr != NULL) { |
292 | 0 | datalen = node->maxlen; |
293 | 0 | if (datalen == 0 || datalen > bstr_len(h_response_hdr->value)) { |
294 | 0 | datalen = bstr_len(h_response_hdr->value); |
295 | 0 | } |
296 | 0 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, |
297 | 0 | aft->buffer->size, (uint8_t *)bstr_ptr(h_response_hdr->value), |
298 | 0 | datalen); |
299 | 0 | } else { |
300 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
301 | 0 | } |
302 | 0 | break; |
303 | 0 | case LOG_HTTP_CF_RESPONSE_LEN: |
304 | | /* RESPONSE LEN */ |
305 | 0 | MemBufferWriteString(aft->buffer, "%"PRIuMAX"", (uintmax_t)tx->response_message_len); |
306 | 0 | break; |
307 | 0 | default: |
308 | | /* NO MATCH */ |
309 | 0 | MemBufferWriteString(aft->buffer, LOG_CF_NONE); |
310 | 0 | SCLogDebug("No matching parameter %%%c for custom http log.", node->type); |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | } |
314 | 0 | MemBufferWriteString(aft->buffer, "\n"); |
315 | 0 | } |
316 | | |
317 | | static void LogHttpLogExtended(LogHttpLogThread *aft, htp_tx_t *tx) |
318 | 80.0k | { |
319 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
320 | | |
321 | | /* referer */ |
322 | 80.0k | htp_header_t *h_referer = NULL; |
323 | 80.0k | if (tx->request_headers != NULL) { |
324 | 80.0k | h_referer = htp_table_get_c(tx->request_headers, "referer"); |
325 | 80.0k | } |
326 | 80.0k | if (h_referer != NULL) { |
327 | 4.07k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
328 | 4.07k | (uint8_t *)bstr_ptr(h_referer->value), |
329 | 4.07k | bstr_len(h_referer->value)); |
330 | 75.9k | } else { |
331 | 75.9k | MemBufferWriteString(aft->buffer, "<no referer>"); |
332 | 75.9k | } |
333 | | |
334 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
335 | | |
336 | | /* method */ |
337 | 80.0k | if (tx->request_method != NULL) { |
338 | 49.8k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
339 | 49.8k | (uint8_t *)bstr_ptr(tx->request_method), |
340 | 49.8k | bstr_len(tx->request_method)); |
341 | 49.8k | } |
342 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
343 | | |
344 | | /* protocol */ |
345 | 80.0k | if (tx->request_protocol != NULL) { |
346 | 30.6k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
347 | 30.6k | (uint8_t *)bstr_ptr(tx->request_protocol), |
348 | 30.6k | bstr_len(tx->request_protocol)); |
349 | 49.4k | } else { |
350 | 49.4k | MemBufferWriteString(aft->buffer, "<no protocol>"); |
351 | 49.4k | } |
352 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
353 | | |
354 | | /* response status */ |
355 | 80.0k | if (tx->response_status != NULL) { |
356 | 25.3k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
357 | 25.3k | (uint8_t *)bstr_ptr(tx->response_status), |
358 | 25.3k | bstr_len(tx->response_status)); |
359 | | /* Redirect? */ |
360 | 25.3k | if ((tx->response_status_number > 300) && ((tx->response_status_number) < 303)) { |
361 | 479 | htp_header_t *h_location = htp_table_get_c(tx->response_headers, "location"); |
362 | 479 | if (h_location != NULL) { |
363 | 476 | MemBufferWriteString(aft->buffer, " => "); |
364 | | |
365 | 476 | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
366 | 476 | (uint8_t *)bstr_ptr(h_location->value), |
367 | 476 | bstr_len(h_location->value)); |
368 | 476 | } |
369 | 479 | } |
370 | 54.7k | } else { |
371 | 54.7k | MemBufferWriteString(aft->buffer, "<no status>"); |
372 | 54.7k | } |
373 | | |
374 | | /* length */ |
375 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
376 | 80.0k | MemBufferWriteString(aft->buffer, "%"PRIuMAX" bytes", (uintmax_t)tx->response_message_len); |
377 | 80.0k | } |
378 | | |
379 | | static TmEcode LogHttpLogIPWrapper(ThreadVars *tv, void *data, const Packet *p, Flow *f, HtpState *htp_state, htp_tx_t *tx, uint64_t tx_id, int ipproto) |
380 | 80.0k | { |
381 | 80.0k | SCEnter(); |
382 | | |
383 | 80.0k | LogHttpLogThread *aft = (LogHttpLogThread *)data; |
384 | 80.0k | LogHttpFileCtx *hlog = aft->httplog_ctx; |
385 | 80.0k | char timebuf[64]; |
386 | | |
387 | | /* check if we have HTTP state or not */ |
388 | 80.0k | CreateTimeString(p->ts, timebuf, sizeof(timebuf)); |
389 | | |
390 | 80.0k | char srcip[46], dstip[46]; |
391 | 80.0k | Port sp, dp; |
392 | 80.0k | if ((PKT_IS_TOSERVER(p))) { |
393 | 48.0k | switch (ipproto) { |
394 | 47.8k | case AF_INET: |
395 | 47.8k | PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); |
396 | 47.8k | PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); |
397 | 47.8k | break; |
398 | 282 | case AF_INET6: |
399 | 282 | PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); |
400 | 282 | PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); |
401 | 282 | break; |
402 | 0 | default: |
403 | 0 | goto end; |
404 | 48.0k | } |
405 | 48.0k | sp = p->sp; |
406 | 48.0k | dp = p->dp; |
407 | 48.0k | } else { |
408 | 31.9k | switch (ipproto) { |
409 | 31.9k | case AF_INET: |
410 | 31.9k | PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip)); |
411 | 31.9k | PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip)); |
412 | 31.9k | break; |
413 | 51 | case AF_INET6: |
414 | 51 | PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip)); |
415 | 51 | PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip)); |
416 | 51 | break; |
417 | 0 | default: |
418 | 0 | goto end; |
419 | 31.9k | } |
420 | 31.9k | sp = p->dp; |
421 | 31.9k | dp = p->sp; |
422 | 31.9k | } |
423 | | |
424 | 80.0k | SCLogDebug("got a HTTP request and now logging !!"); |
425 | | |
426 | | /* reset */ |
427 | 80.0k | MemBufferReset(aft->buffer); |
428 | | |
429 | 80.0k | if (hlog->flags & LOG_HTTP_CUSTOM) { |
430 | 0 | LogHttpLogCustom(aft, tx, p->ts, srcip, sp, dstip, dp); |
431 | 80.0k | } else { |
432 | | /* time */ |
433 | 80.0k | MemBufferWriteString(aft->buffer, "%s ", timebuf); |
434 | | |
435 | | /* hostname */ |
436 | 80.0k | if (tx->request_hostname != NULL) { |
437 | 17.4k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
438 | 17.4k | (uint8_t *)bstr_ptr(tx->request_hostname), |
439 | 17.4k | bstr_len(tx->request_hostname)); |
440 | 62.6k | } else { |
441 | 62.6k | MemBufferWriteString(aft->buffer, "<hostname unknown>"); |
442 | 62.6k | } |
443 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
444 | | |
445 | | /* uri */ |
446 | 80.0k | if (tx->request_uri != NULL) { |
447 | 66.3k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
448 | 66.3k | (uint8_t *)bstr_ptr(tx->request_uri), |
449 | 66.3k | bstr_len(tx->request_uri)); |
450 | 66.3k | } |
451 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
452 | | |
453 | | /* user agent */ |
454 | 80.0k | htp_header_t *h_user_agent = NULL; |
455 | 80.0k | if (tx->request_headers != NULL) { |
456 | 80.0k | h_user_agent = htp_table_get_c(tx->request_headers, "user-agent"); |
457 | 80.0k | } |
458 | 80.0k | if (h_user_agent != NULL) { |
459 | 12.6k | PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, |
460 | 12.6k | (uint8_t *)bstr_ptr(h_user_agent->value), |
461 | 12.6k | bstr_len(h_user_agent->value)); |
462 | 67.4k | } else { |
463 | 67.4k | MemBufferWriteString(aft->buffer, "<useragent unknown>"); |
464 | 67.4k | } |
465 | 80.0k | if (hlog->flags & LOG_HTTP_EXTENDED) { |
466 | 80.0k | LogHttpLogExtended(aft, tx); |
467 | 80.0k | } |
468 | | |
469 | | /* ip/tcp header info */ |
470 | 80.0k | LOG_CF_WRITE_STAR_SEPARATOR(aft->buffer); |
471 | 80.0k | MemBufferWriteString(aft->buffer, |
472 | 80.0k | "%s:%" PRIu16 " -> %s:%" PRIu16 "\n", |
473 | 80.0k | srcip, sp, dstip, dp); |
474 | 80.0k | } |
475 | | |
476 | 80.0k | aft->uri_cnt ++; |
477 | | |
478 | 80.0k | hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer), |
479 | 80.0k | MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx); |
480 | | |
481 | 80.0k | end: |
482 | 80.0k | SCReturnInt(0); |
483 | | |
484 | 80.0k | } |
485 | | |
486 | | int LogHttpLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state, void *tx, uint64_t tx_id) |
487 | 80.0k | { |
488 | 80.0k | SCEnter(); |
489 | | |
490 | 80.0k | if (!(PKT_IS_TCP(p))) { |
491 | 0 | SCReturnInt(TM_ECODE_OK); |
492 | 0 | } |
493 | | |
494 | 80.0k | int r = 0; |
495 | 80.0k | if (PKT_IS_IPV4(p)) { |
496 | 79.7k | r = LogHttpLogIPWrapper(tv, thread_data, p, f, (HtpState *)state, (htp_tx_t *)tx, tx_id, AF_INET); |
497 | 79.7k | } else if (PKT_IS_IPV6(p)) { |
498 | 333 | r = LogHttpLogIPWrapper(tv, thread_data, p, f, (HtpState *)state, (htp_tx_t *)tx, tx_id, AF_INET6); |
499 | 333 | } |
500 | | |
501 | 80.0k | SCReturnInt(r); |
502 | 80.0k | } |
503 | | |
504 | | TmEcode LogHttpLogThreadInit(ThreadVars *t, const void *initdata, void **data) |
505 | 4 | { |
506 | 4 | LogHttpLogThread *aft = SCMalloc(sizeof(LogHttpLogThread)); |
507 | 4 | if (unlikely(aft == NULL)) |
508 | 0 | return TM_ECODE_FAILED; |
509 | 4 | memset(aft, 0, sizeof(LogHttpLogThread)); |
510 | | |
511 | 4 | if(initdata == NULL) |
512 | 0 | { |
513 | 0 | SCLogDebug("Error getting context for LogHTTPLog. \"initdata\" argument NULL"); |
514 | 0 | SCFree(aft); |
515 | 0 | return TM_ECODE_FAILED; |
516 | 0 | } |
517 | | |
518 | 4 | aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE); |
519 | 4 | if (aft->buffer == NULL) { |
520 | 0 | SCFree(aft); |
521 | 0 | return TM_ECODE_FAILED; |
522 | 0 | } |
523 | | |
524 | | /* Use the Output Context (file pointer and mutex) */ |
525 | 4 | aft->httplog_ctx= ((OutputCtx *)initdata)->data; |
526 | | |
527 | 4 | *data = (void *)aft; |
528 | 4 | return TM_ECODE_OK; |
529 | 4 | } |
530 | | |
531 | | TmEcode LogHttpLogThreadDeinit(ThreadVars *t, void *data) |
532 | 0 | { |
533 | 0 | LogHttpLogThread *aft = (LogHttpLogThread *)data; |
534 | 0 | if (aft == NULL) { |
535 | 0 | return TM_ECODE_OK; |
536 | 0 | } |
537 | | |
538 | 0 | MemBufferFree(aft->buffer); |
539 | | /* clear memory */ |
540 | 0 | memset(aft, 0, sizeof(LogHttpLogThread)); |
541 | |
|
542 | 0 | SCFree(aft); |
543 | 0 | return TM_ECODE_OK; |
544 | 0 | } |
545 | | |
546 | | /** \brief Create a new http log LogFileCtx. |
547 | | * \param conf Pointer to ConfNode containing this loggers configuration. |
548 | | * \return NULL if failure, LogFileCtx* to the file_ctx if succesful |
549 | | * */ |
550 | | OutputInitResult LogHttpLogInitCtx(ConfNode *conf) |
551 | 4 | { |
552 | 4 | OutputInitResult result = { NULL, false }; |
553 | 4 | LogFileCtx* file_ctx = LogFileNewCtx(); |
554 | 4 | if(file_ctx == NULL) { |
555 | 0 | SCLogError("couldn't create new file_ctx"); |
556 | 0 | return result; |
557 | 0 | } |
558 | | |
559 | 4 | if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) { |
560 | 0 | LogFileFreeCtx(file_ctx); |
561 | 0 | return result; |
562 | 0 | } |
563 | | |
564 | 4 | LogHttpFileCtx *httplog_ctx = SCMalloc(sizeof(LogHttpFileCtx)); |
565 | 4 | if (unlikely(httplog_ctx == NULL)) { |
566 | 0 | LogFileFreeCtx(file_ctx); |
567 | 0 | return result; |
568 | 0 | } |
569 | 4 | memset(httplog_ctx, 0x00, sizeof(LogHttpFileCtx)); |
570 | | |
571 | 4 | httplog_ctx->file_ctx = file_ctx; |
572 | | |
573 | 4 | const char *extended = ConfNodeLookupChildValue(conf, "extended"); |
574 | 4 | const char *custom = ConfNodeLookupChildValue(conf, "custom"); |
575 | 4 | const char *customformat = ConfNodeLookupChildValue(conf, "customformat"); |
576 | | |
577 | | /* If custom logging format is selected, lets parse it */ |
578 | 4 | if (custom != NULL && customformat != NULL && ConfValIsTrue(custom)) { |
579 | |
|
580 | 0 | httplog_ctx->cf = LogCustomFormatAlloc(); |
581 | 0 | if (!httplog_ctx->cf) { |
582 | 0 | goto errorfree; |
583 | 0 | } |
584 | | |
585 | 0 | httplog_ctx->flags |= LOG_HTTP_CUSTOM; |
586 | | |
587 | | /* Parsing */ |
588 | 0 | if ( ! LogCustomFormatParse(httplog_ctx->cf, customformat)) { |
589 | 0 | goto parsererror; |
590 | 0 | } |
591 | 4 | } else { |
592 | 4 | if (extended == NULL) { |
593 | 0 | httplog_ctx->flags |= LOG_HTTP_DEFAULT; |
594 | 4 | } else { |
595 | 4 | if (ConfValIsTrue(extended)) { |
596 | 4 | httplog_ctx->flags |= LOG_HTTP_EXTENDED; |
597 | 4 | } |
598 | 4 | } |
599 | 4 | } |
600 | | |
601 | 4 | OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); |
602 | 4 | if (unlikely(output_ctx == NULL)) { |
603 | 0 | goto parsererror; |
604 | 0 | } |
605 | | |
606 | 4 | output_ctx->data = httplog_ctx; |
607 | 4 | output_ctx->DeInit = LogHttpLogDeInitCtx; |
608 | | |
609 | 4 | SCLogDebug("HTTP log output initialized"); |
610 | | |
611 | | /* enable the logger for the app layer */ |
612 | 4 | AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP1); |
613 | | |
614 | 4 | result.ctx = output_ctx; |
615 | 4 | result.ok = true; |
616 | 4 | return result; |
617 | | |
618 | 0 | parsererror: |
619 | 0 | SCLogError("Syntax error in custom http log format string."); |
620 | 0 | errorfree: |
621 | 0 | LogCustomFormatFree(httplog_ctx->cf); |
622 | 0 | LogFileFreeCtx(file_ctx); |
623 | 0 | SCFree(httplog_ctx); |
624 | 0 | return result; |
625 | |
|
626 | 0 | } |
627 | | |
628 | | static void LogHttpLogDeInitCtx(OutputCtx *output_ctx) |
629 | 0 | { |
630 | 0 | LogHttpFileCtx *httplog_ctx = (LogHttpFileCtx *)output_ctx->data; |
631 | 0 | LogCustomFormatFree(httplog_ctx->cf); |
632 | 0 | LogFileFreeCtx(httplog_ctx->file_ctx); |
633 | 0 | SCFree(httplog_ctx); |
634 | 0 | SCFree(output_ctx); |
635 | 0 | } |