]> git.ipfire.org Git - people/ms/suricata.git/blob - src/app-layer-htp.c
http: adds events for each libhtp log
[people/ms/suricata.git] / src / app-layer-htp.c
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 * \ingroup httplayer
20 *
21 * @{
22 */
23
24 /**
25 * \file
26 *
27 * \author Victor Julien <victor@inliniac.net>
28 * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
29 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
30 * \author Brian Rectanus <brectanu@gmail.com>
31 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
32 *
33 * This file provides a HTTP protocol support for the engine using HTP library.
34 */
35
36 #include "suricata.h"
37 #include "suricata-common.h"
38 #include "conf.h"
39 #include "debug.h"
40 #include "decode.h"
41 #include "threads.h"
42 #include "counters.h"
43
44 #include "util-print.h"
45 #include "util-pool.h"
46 #include "util-radix-tree.h"
47 #include "util-file.h"
48
49 #include "stream-tcp-private.h"
50 #include "stream-tcp-reassemble.h"
51 #include "stream-tcp.h"
52 #include "stream.h"
53
54 #include "app-layer-protos.h"
55 #include "app-layer-parser.h"
56
57 #include "app-layer.h"
58 #include "app-layer-htp.h"
59 #include "app-layer-htp-body.h"
60 #include "app-layer-htp-file.h"
61 #include "app-layer-htp-libhtp.h"
62 #include "app-layer-htp-xff.h"
63
64 #include "util-spm.h"
65 #include "util-debug.h"
66 #include "util-time.h"
67 #include "util-misc.h"
68
69 #include "util-unittest.h"
70 #include "util-unittest-helper.h"
71 #include "flow-util.h"
72
73 #include "detect-engine.h"
74 #include "detect-engine-state.h"
75 #include "detect-parse.h"
76
77 #include "decode-events.h"
78
79 #include "util-memcmp.h"
80 #include "util-random.h"
81 #include "util-validate.h"
82
83 //#define PRINT
84
85 /** Fast lookup tree (radix) for the various HTP configurations */
86 static SCRadixTree *cfgtree;
87 /** List of HTP configurations. */
88 static HTPCfgRec cfglist;
89
90 #ifdef DEBUG
91 static SCMutex htp_state_mem_lock = SCMUTEX_INITIALIZER;
92 static uint64_t htp_state_memuse = 0;
93 static uint64_t htp_state_memcnt = 0;
94 #endif
95
96 SCEnumCharMap http_decoder_event_table[ ] = {
97 { "UNKNOWN_ERROR",
98 HTTP_DECODER_EVENT_UNKNOWN_ERROR},
99 { "GZIP_DECOMPRESSION_FAILED",
100 HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
101 { "REQUEST_FIELD_MISSING_COLON",
102 HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON},
103 { "RESPONSE_FIELD_MISSING_COLON",
104 HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON},
105 { "INVALID_REQUEST_CHUNK_LEN",
106 HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN},
107 { "INVALID_RESPONSE_CHUNK_LEN",
108 HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN},
109 { "INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST",
110 HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST},
111 { "INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE",
112 HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE},
113 { "INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST",
114 HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST},
115 { "INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE",
116 HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE},
117 { "100_CONTINUE_ALREADY_SEEN",
118 HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN},
119 { "UNABLE_TO_MATCH_RESPONSE_TO_REQUEST",
120 HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST},
121 { "INVALID_SERVER_PORT_IN_REQUEST",
122 HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST},
123 { "INVALID_AUTHORITY_PORT",
124 HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT},
125 { "REQUEST_HEADER_INVALID",
126 HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
127 { "RESPONSE_HEADER_INVALID",
128 HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
129 { "MISSING_HOST_HEADER",
130 HTTP_DECODER_EVENT_MISSING_HOST_HEADER},
131 { "HOST_HEADER_AMBIGUOUS",
132 HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS},
133 { "INVALID_REQUEST_FIELD_FOLDING",
134 HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING},
135 { "INVALID_RESPONSE_FIELD_FOLDING",
136 HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING},
137 { "REQUEST_FIELD_TOO_LONG",
138 HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG},
139 { "RESPONSE_FIELD_TOO_LONG",
140 HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG},
141 { "REQUEST_SERVER_PORT_TCP_PORT_MISMATCH",
142 HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH},
143 { "REQUEST_URI_HOST_INVALID",
144 HTTP_DECODER_EVENT_URI_HOST_INVALID},
145 { "REQUEST_HEADER_HOST_INVALID",
146 HTTP_DECODER_EVENT_HEADER_HOST_INVALID},
147 { "REQUEST_AUTH_UNRECOGNIZED",
148 HTTP_DECODER_EVENT_AUTH_UNRECOGNIZED},
149 { "REQUEST_HEADER_REPETITION",
150 HTTP_DECODER_EVENT_REQUEST_HEADER_REPETITION},
151 { "RESPONSE_HEADER_REPETITION",
152 HTTP_DECODER_EVENT_RESPONSE_HEADER_REPETITION},
153 { "URI_DELIM_NON_COMPLIANT",
154 HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT},
155 { "METHOD_DELIM_NON_COMPLIANT",
156 HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT},
157 { "REQUEST_LINE_LEADING_WHITESPACE",
158 HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE},
159 { "TOO_MANY_ENCODING_LAYERS",
160 HTTP_DECODER_EVENT_TOO_MANY_ENCODING_LAYERS},
161 { "ABNORMAL_CE_HEADER",
162 HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
163 { "RESPONSE_MULTIPART_BYTERANGES",
164 HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES},
165 { "RESPONSE_ABNORMAL_TRANSFER_ENCODING",
166 HTTP_DECODER_EVENT_RESPONSE_ABNORMAL_TRANSFER_ENCODING},
167 { "RESPONSE_CHUNKED_OLD_PROTO",
168 HTTP_DECODER_EVENT_RESPONSE_CHUNKED_OLD_PROTO},
169 { "RESPONSE_INVALID_PROTOCOL",
170 HTTP_DECODER_EVENT_RESPONSE_INVALID_PROTOCOL},
171 { "RESPONSE_INVALID_STATUS",
172 HTTP_DECODER_EVENT_RESPONSE_INVALID_STATUS},
173 { "REQUEST_LINE_INCOMPLETE",
174 HTTP_DECODER_EVENT_REQUEST_LINE_INCOMPLETE},
175
176 /* suricata warnings/errors */
177 { "MULTIPART_GENERIC_ERROR",
178 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR},
179 { "MULTIPART_NO_FILEDATA",
180 HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA},
181 { "MULTIPART_INVALID_HEADER",
182 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER},
183
184 { NULL, -1 },
185 };
186
187 static void *HTPStateGetTx(void *alstate, uint64_t tx_id);
188 static int HTPStateGetAlstateProgress(void *tx, uint8_t direction);
189 static uint64_t HTPStateGetTxCnt(void *alstate);
190 static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction);
191
192 #ifdef DEBUG
193 /**
194 * \internal
195 *
196 * \brief Lookup the HTP personality string from the numeric personality.
197 *
198 * \todo This needs to be a libhtp function.
199 */
200 static const char *HTPLookupPersonalityString(int p)
201 {
202 #define CASE_HTP_PERSONALITY_STRING(p) \
203 case HTP_SERVER_ ## p: return #p
204
205 switch (p) {
206 CASE_HTP_PERSONALITY_STRING(MINIMAL);
207 CASE_HTP_PERSONALITY_STRING(GENERIC);
208 CASE_HTP_PERSONALITY_STRING(IDS);
209 CASE_HTP_PERSONALITY_STRING(IIS_4_0);
210 CASE_HTP_PERSONALITY_STRING(IIS_5_0);
211 CASE_HTP_PERSONALITY_STRING(IIS_5_1);
212 CASE_HTP_PERSONALITY_STRING(IIS_6_0);
213 CASE_HTP_PERSONALITY_STRING(IIS_7_0);
214 CASE_HTP_PERSONALITY_STRING(IIS_7_5);
215 CASE_HTP_PERSONALITY_STRING(APACHE_2);
216 }
217
218 return NULL;
219 }
220 #endif /* DEBUG */
221
222 /**
223 * \internal
224 *
225 * \brief Lookup the numeric HTP personality from a string.
226 *
227 * \todo This needs to be a libhtp function.
228 */
229 static int HTPLookupPersonality(const char *str)
230 {
231 #define IF_HTP_PERSONALITY_NUM(p) \
232 if (strcasecmp(#p, str) == 0) return HTP_SERVER_ ## p
233
234 IF_HTP_PERSONALITY_NUM(MINIMAL);
235 IF_HTP_PERSONALITY_NUM(GENERIC);
236 IF_HTP_PERSONALITY_NUM(IDS);
237 IF_HTP_PERSONALITY_NUM(IIS_4_0);
238 IF_HTP_PERSONALITY_NUM(IIS_5_0);
239 IF_HTP_PERSONALITY_NUM(IIS_5_1);
240 IF_HTP_PERSONALITY_NUM(IIS_6_0);
241 IF_HTP_PERSONALITY_NUM(IIS_7_0);
242 IF_HTP_PERSONALITY_NUM(IIS_7_5);
243 IF_HTP_PERSONALITY_NUM(APACHE_2);
244 if (strcasecmp("TOMCAT_6_0", str) == 0) {
245 SCLogError(SC_WARN_OPTION_OBSOLETE, "Personality %s no "
246 "longer supported by libhtp.", str);
247 return -1;
248 } else if ((strcasecmp("APACHE", str) == 0) ||
249 (strcasecmp("APACHE_2_2", str) == 0))
250 {
251 SCLogWarning(SC_WARN_OPTION_OBSOLETE, "Personality %s no "
252 "longer supported by libhtp, failing back to "
253 "Apache2 personality.", str);
254 return HTP_SERVER_APACHE_2;
255 }
256
257 return -1;
258 }
259
260 static void HTPSetEvent(HtpState *s, HtpTxUserData *htud, uint8_t e)
261 {
262 SCLogDebug("setting event %u", e);
263
264 if (htud) {
265 AppLayerDecoderEventsSetEventRaw(&htud->decoder_events, e);
266 s->events++;
267 return;
268 }
269
270 htp_tx_t *tx = HTPStateGetTx(s, s->transaction_cnt);
271 if (tx == NULL && s->transaction_cnt > 0)
272 tx = HTPStateGetTx(s, s->transaction_cnt - 1);
273 if (tx != NULL) {
274 htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
275 if (htud != NULL) {
276 AppLayerDecoderEventsSetEventRaw(&htud->decoder_events, e);
277 s->events++;
278 return;
279 }
280 }
281 SCLogDebug("couldn't set event %u", e);
282 }
283
284 static AppLayerDecoderEvents *HTPGetEvents(void *state, uint64_t tx_id)
285 {
286 SCLogDebug("get HTTP events for TX %"PRIu64, tx_id);
287
288 HtpState *s = (HtpState *)state;
289 htp_tx_t *tx = HTPStateGetTx(s, tx_id);
290 if (tx != NULL) {
291 HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
292 if (htud != NULL) {
293 SCLogDebug("has htud, htud->decoder_events %p", htud->decoder_events);
294 return htud->decoder_events;
295 }
296 }
297 return NULL;
298 }
299
300 /** \brief Function to allocates the HTTP state memory and also creates the HTTP
301 * connection parser to be used by the HTP library
302 */
303 static void *HTPStateAlloc(void)
304 {
305 SCEnter();
306
307 HtpState *s = HTPMalloc(sizeof(HtpState));
308 if (unlikely(s == NULL)) {
309 SCReturnPtr(NULL, "void");
310 }
311
312 memset(s, 0x00, sizeof(HtpState));
313
314 #ifdef DEBUG
315 SCMutexLock(&htp_state_mem_lock);
316 htp_state_memcnt++;
317 htp_state_memuse += sizeof(HtpState);
318 SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
319 SCMutexUnlock(&htp_state_mem_lock);
320 #endif
321
322 SCReturnPtr((void *)s, "void");
323 }
324
325 static void HtpTxUserDataFree(HtpState *state, HtpTxUserData *htud)
326 {
327 if (likely(htud)) {
328 HtpBodyFree(&htud->request_body);
329 HtpBodyFree(&htud->response_body);
330 bstr_free(htud->request_uri_normalized);
331 if (htud->request_headers_raw)
332 HTPFree(htud->request_headers_raw, htud->request_headers_raw_len);
333 if (htud->response_headers_raw)
334 HTPFree(htud->response_headers_raw, htud->response_headers_raw_len);
335 AppLayerDecoderEventsFreeEvents(&htud->decoder_events);
336 if (htud->boundary)
337 HTPFree(htud->boundary, htud->boundary_len);
338 if (htud->de_state != NULL) {
339 DetectEngineStateFree(htud->de_state);
340 }
341 HTPFree(htud, sizeof(HtpTxUserData));
342 }
343 }
344
345 /** \brief Function to frees the HTTP state memory and also frees the HTTP
346 * connection parser memory which was used by the HTP library
347 */
348 void HTPStateFree(void *state)
349 {
350 SCEnter();
351
352 HtpState *s = (HtpState *)state;
353 if (s == NULL) {
354 SCReturn;
355 }
356
357 /* free the connection parser memory used by HTP library */
358 if (s->connp != NULL) {
359 SCLogDebug("freeing HTP state");
360
361 uint64_t tx_id;
362 uint64_t total_txs = HTPStateGetTxCnt(state);
363 /* free the list of body chunks */
364 if (s->conn != NULL) {
365 for (tx_id = 0; tx_id < total_txs; tx_id++) {
366 htp_tx_t *tx = HTPStateGetTx(s, tx_id);
367 if (tx != NULL) {
368 HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
369 HtpTxUserDataFree(s, htud);
370 htp_tx_set_user_data(tx, NULL);
371 }
372 }
373 }
374 htp_connp_destroy_all(s->connp);
375 }
376
377 FileContainerFree(s->files_ts);
378 FileContainerFree(s->files_tc);
379 HTPFree(s, sizeof(HtpState));
380
381 #ifdef DEBUG
382 SCMutexLock(&htp_state_mem_lock);
383 htp_state_memcnt--;
384 htp_state_memuse -= sizeof(HtpState);
385 SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
386 SCMutexUnlock(&htp_state_mem_lock);
387 #endif
388
389 SCReturn;
390 }
391
392 /**
393 * \brief HTP transaction cleanup callback
394 *
395 * \warning We cannot actually free the transactions here. It seems that
396 * HTP only accepts freeing of transactions in the response callback.
397 */
398 static void HTPStateTransactionFree(void *state, uint64_t id)
399 {
400 SCEnter();
401
402 HtpState *s = (HtpState *)state;
403
404 SCLogDebug("state %p, id %"PRIu64, s, id);
405
406 htp_tx_t *tx = HTPStateGetTx(s, id);
407 if (tx != NULL) {
408 /* This will remove obsolete body chunks */
409 HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
410 HtpTxUserDataFree(s, htud);
411 htp_tx_set_user_data(tx, NULL);
412
413 /* hack: even if libhtp considers the tx incomplete, we want to
414 * free it here. htp_tx_destroy however, will refuse to do this.
415 * As htp_tx_destroy_incomplete isn't available in the public API,
416 * we hack around it here. */
417 if (unlikely(!(
418 tx->request_progress == HTP_REQUEST_COMPLETE &&
419 tx->response_progress == HTP_RESPONSE_COMPLETE)))
420 {
421 tx->request_progress = HTP_REQUEST_COMPLETE;
422 tx->response_progress = HTP_RESPONSE_COMPLETE;
423 }
424 htp_tx_destroy(tx);
425 }
426 }
427
428 /**
429 * \brief Sets a flag that informs the HTP app layer that some module in the
430 * engine needs the http request body data.
431 * \initonly
432 */
433 void AppLayerHtpEnableRequestBodyCallback(void)
434 {
435 SCEnter();
436
437 SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_BODY);
438 SCReturn;
439 }
440
441 /**
442 * \brief Sets a flag that informs the HTP app layer that some module in the
443 * engine needs the http request body data.
444 * \initonly
445 */
446 void AppLayerHtpEnableResponseBodyCallback(void)
447 {
448 SCEnter();
449
450 SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_RESPONSE_BODY);
451 SCReturn;
452 }
453
454 /**
455 * \brief Sets a flag that informs the HTP app layer that some module in the
456 * engine needs the http request multi part header.
457 *
458 * \initonly
459 */
460 static void AppLayerHtpNeedMultipartHeader(void)
461 {
462 SCEnter();
463 AppLayerHtpEnableRequestBodyCallback();
464
465 SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_MULTIPART);
466 SCReturn;
467 }
468
469 /**
470 * \brief Sets a flag that informs the HTP app layer that some module in the
471 * engine needs the http request file.
472 *
473 * \initonly
474 */
475 void AppLayerHtpNeedFileInspection(void)
476 {
477 SCEnter();
478 AppLayerHtpNeedMultipartHeader();
479 AppLayerHtpEnableRequestBodyCallback();
480 AppLayerHtpEnableResponseBodyCallback();
481
482 SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_FILE);
483 SCReturn;
484 }
485
486 /* below error messages updated up to libhtp 0.5.7 (git 379632278b38b9a792183694a4febb9e0dbd1e7a) */
487 struct {
488 const char *msg;
489 int de;
490 } htp_errors[] = {
491 { "GZip decompressor: inflateInit2 failed", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
492 { "Request field invalid: colon missing", HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON},
493 { "Response field invalid: missing colon", HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON},
494 { "Request chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN},
495 { "Response chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN},
496 /* { "Invalid T-E value in request", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_T_E
497 { "Invalid T-E value in response", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE}, <- nothing to replace it */
498 /* { "Invalid C-L field in request", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_C_L */
499 { "Invalid C-L field in response", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE},
500 { "Already seen 100-Continue", HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN},
501 { "Unable to match response to request", HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST},
502 { "Invalid server port information in request", HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST},
503 /* { "Invalid authority port", HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT}, htp no longer returns this error */
504 { "Request buffer over", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG},
505 { "Response buffer over", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG},
506 { "C-T multipart/byteranges in responses not supported", HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES},
507 };
508
509 struct {
510 const char *msg;
511 int de;
512 } htp_warnings[] = {
513 { "GZip decompressor:", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
514 { "Request field invalid", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
515 { "Response field invalid", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
516 { "Request header name is not a token", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
517 { "Response header name is not a token", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
518 /* { "Host information in request headers required by HTTP/1.1", HTTP_DECODER_EVENT_MISSING_HOST_HEADER}, <- tx flag HTP_HOST_MISSING
519 { "Host information ambiguous", HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS}, <- tx flag HTP_HOST_AMBIGUOUS */
520 { "Invalid request field folding", HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING},
521 { "Invalid response field folding", HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING},
522 /* line is now: htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request server port=%d number differs from the actual TCP port=%d", port, connp->conn->server_port);
523 * luckily, "Request server port=" is unique */
524 /* { "Request server port number differs from the actual TCP port", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH}, */
525 { "Request server port=", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH},
526 { "Request line: URI contains non-compliant delimiter", HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT},
527 { "Request line: non-compliant delimiter between Method and URI", HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT},
528 { "Request line: leading whitespace", HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE},
529 { "Too many response content encoding layers", HTTP_DECODER_EVENT_TOO_MANY_ENCODING_LAYERS},
530 { "C-E gzip has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
531 { "C-E deflate has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
532 { "C-E unknown setting", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
533 { "Excessive request header repetitions", HTTP_DECODER_EVENT_REQUEST_HEADER_REPETITION},
534 { "Excessive response header repetitions", HTTP_DECODER_EVENT_RESPONSE_HEADER_REPETITION},
535 { "Transfer-encoding has abnormal chunked value", HTTP_DECODER_EVENT_RESPONSE_ABNORMAL_TRANSFER_ENCODING},
536 { "Chunked transfer-encoding on HTTP/0.9 or HTTP/1.0", HTTP_DECODER_EVENT_RESPONSE_CHUNKED_OLD_PROTO},
537 { "Invalid response line: invalid protocol", HTTP_DECODER_EVENT_RESPONSE_INVALID_PROTOCOL},
538 { "Invalid response line: invalid response status", HTTP_DECODER_EVENT_RESPONSE_INVALID_STATUS},
539 { "Request line incomplete", HTTP_DECODER_EVENT_REQUEST_LINE_INCOMPLETE},
540 };
541
542 #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0]))
543 #define HTP_WARNING_MAX (sizeof(htp_warnings) / sizeof(htp_warnings[0]))
544
545 /**
546 * \internal
547 *
548 * \brief Get the warning id for the warning msg.
549 *
550 * \param msg warning message
551 *
552 * \retval id the id or 0 in case of not found
553 */
554 static int HTPHandleWarningGetId(const char *msg)
555 {
556 SCLogDebug("received warning \"%s\"", msg);
557 size_t idx;
558 for (idx = 0; idx < HTP_WARNING_MAX; idx++) {
559 if (strncmp(htp_warnings[idx].msg, msg,
560 strlen(htp_warnings[idx].msg)) == 0)
561 {
562 return htp_warnings[idx].de;
563 }
564 }
565
566 return 0;
567 }
568
569 /**
570 * \internal
571 *
572 * \brief Get the error id for the error msg.
573 *
574 * \param msg error message
575 *
576 * \retval id the id or 0 in case of not found
577 */
578 static int HTPHandleErrorGetId(const char *msg)
579 {
580 SCLogDebug("received error \"%s\"", msg);
581
582 size_t idx;
583 for (idx = 0; idx < HTP_ERROR_MAX; idx++) {
584 if (strncmp(htp_errors[idx].msg, msg,
585 strlen(htp_errors[idx].msg)) == 0)
586 {
587 return htp_errors[idx].de;
588 }
589 }
590
591 return 0;
592 }
593
594 /**
595 * \internal
596 *
597 * \brief Check state for errors, warnings and add any as events
598 *
599 * \param s state
600 */
601 static void HTPHandleError(HtpState *s)
602 {
603 if (s == NULL || s->conn == NULL ||
604 s->conn->messages == NULL) {
605 return;
606 }
607
608 size_t size = htp_list_size(s->conn->messages);
609 size_t msg;
610
611 for (msg = s->htp_messages_offset; msg < size; msg++) {
612 htp_log_t *log = htp_list_get(s->conn->messages, msg);
613 if (log == NULL)
614 continue;
615
616 HtpTxUserData *htud = NULL;
617 htp_tx_t *tx = log->tx; // will be NULL in <=0.5.9
618 if (tx != NULL)
619 htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
620
621 SCLogDebug("message %s", log->msg);
622
623 int id = HTPHandleErrorGetId(log->msg);
624 if (id == 0) {
625 id = HTPHandleWarningGetId(log->msg);
626 if (id == 0)
627 id = HTTP_DECODER_EVENT_UNKNOWN_ERROR;
628 }
629
630 if (id > 0) {
631 HTPSetEvent(s, htud, id);
632 }
633 }
634 s->htp_messages_offset = (uint16_t)msg;
635 SCLogDebug("s->htp_messages_offset %u", s->htp_messages_offset);
636 }
637
638 static inline void HTPErrorCheckTxRequestFlags(HtpState *s, htp_tx_t *tx)
639 {
640 #ifdef DEBUG
641 BUG_ON(s == NULL || tx == NULL);
642 #endif
643 if (tx->flags & ( HTP_REQUEST_INVALID_T_E|HTP_REQUEST_INVALID_C_L|
644 HTP_HOST_MISSING|HTP_HOST_AMBIGUOUS|HTP_HOSTU_INVALID|
645 HTP_HOSTH_INVALID))
646 {
647 HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
648 if (htud == NULL)
649 return;
650
651 if (tx->flags & HTP_REQUEST_INVALID_T_E)
652 HTPSetEvent(s, htud,
653 HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST);
654 if (tx->flags & HTP_REQUEST_INVALID_C_L)
655 HTPSetEvent(s, htud,
656 HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST);
657 if (tx->flags & HTP_HOST_MISSING)
658 HTPSetEvent(s, htud,
659 HTTP_DECODER_EVENT_MISSING_HOST_HEADER);
660 if (tx->flags & HTP_HOST_AMBIGUOUS)
661 HTPSetEvent(s, htud,
662 HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS);
663 if (tx->flags & HTP_HOSTU_INVALID)
664 HTPSetEvent(s, htud,
665 HTTP_DECODER_EVENT_URI_HOST_INVALID);
666 if (tx->flags & HTP_HOSTH_INVALID)
667 HTPSetEvent(s, htud,
668 HTTP_DECODER_EVENT_HEADER_HOST_INVALID);
669 }
670 if (tx->request_auth_type == HTP_AUTH_UNRECOGNIZED) {
671 HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
672 if (htud == NULL)
673 return;
674
675 HTPSetEvent(s, htud, HTTP_DECODER_EVENT_AUTH_UNRECOGNIZED);
676 }
677 }
678
679 static int Setup(Flow *f, HtpState *hstate)
680 {
681 /* store flow ref in state so callbacks can access it */
682 hstate->f = f;
683
684 HTPCfgRec *htp_cfg_rec = &cfglist;
685 htp_cfg_t *htp = cfglist.cfg; /* Default to the global HTP config */
686 void *user_data = NULL;
687
688 if (FLOW_IS_IPV4(f)) {
689 SCLogDebug("Looking up HTP config for ipv4 %08x", *GET_IPV4_DST_ADDR_PTR(f));
690 (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(f), cfgtree, &user_data);
691 }
692 else if (FLOW_IS_IPV6(f)) {
693 SCLogDebug("Looking up HTP config for ipv6");
694 (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)GET_IPV6_DST_ADDR(f), cfgtree, &user_data);
695 }
696 else {
697 SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown address family, bug!");
698 goto error;
699 }
700
701 if (user_data != NULL) {
702 htp_cfg_rec = user_data;
703 htp = htp_cfg_rec->cfg;
704 SCLogDebug("LIBHTP using config: %p", htp);
705 } else {
706 SCLogDebug("Using default HTP config: %p", htp);
707 }
708
709 if (NULL == htp) {
710 #ifdef DEBUG_VALIDATION
711 BUG_ON(htp == NULL);
712 #endif
713 /* should never happen if HTPConfigure is properly invoked */
714 goto error;
715 }
716
717 hstate->connp = htp_connp_create(htp);
718 if (hstate->connp == NULL) {
719 goto error;
720 }
721
722 hstate->conn = htp_connp_get_connection(hstate->connp);
723
724 htp_connp_set_user_data(hstate->connp, (void *)hstate);
725 hstate->cfg = htp_cfg_rec;
726
727 SCLogDebug("New hstate->connp %p", hstate->connp);
728
729 htp_connp_open(hstate->connp, NULL, f->sp, NULL, f->dp, &f->startts);
730
731 StreamTcpReassemblySetMinInspectDepth(f->protoctx, STREAM_TOSERVER,
732 htp_cfg_rec->request.inspect_min_size);
733 StreamTcpReassemblySetMinInspectDepth(f->protoctx, STREAM_TOCLIENT,
734 htp_cfg_rec->response.inspect_min_size);
735 return 0;
736 error:
737 return -1;
738 }
739
740 /**
741 * \brief Function to handle the reassembled data from client and feed it to
742 * the HTP library to process it.
743 *
744 * \param flow Pointer to the flow the data belong to
745 * \param htp_state Pointer the state in which the parsed value to be stored
746 * \param pstate Application layer parser state for this session
747 * \param input Pointer the received HTTP client data
748 * \param input_len Length in bytes of the received data
749 * \param output Pointer to the output (not used in this function)
750 *
751 * \retval On success returns 1 or on failure returns -1.
752 */
753 static int HTPHandleRequestData(Flow *f, void *htp_state,
754 AppLayerParserState *pstate,
755 uint8_t *input, uint32_t input_len,
756 void *local_data, const uint8_t flags)
757 {
758 SCEnter();
759 int ret = 1;
760 HtpState *hstate = (HtpState *)htp_state;
761
762 /* On the first invocation, create the connection parser structure to
763 * be used by HTP library. This is looked up via IP in the radix
764 * tree. Failing that, the default HTP config is used.
765 */
766 if (NULL == hstate->conn) {
767 if (Setup(f, hstate) != 0) {
768 goto error;
769 }
770 }
771 DEBUG_VALIDATE_BUG_ON(hstate->connp == NULL);
772
773 htp_time_t ts = { f->lastts.tv_sec, f->lastts.tv_usec };
774 /* pass the new data to the htp parser */
775 if (input_len > 0) {
776 const int r = htp_connp_req_data(hstate->connp, &ts, input, input_len);
777 switch (r) {
778 case HTP_STREAM_ERROR:
779 ret = -1;
780 break;
781 default:
782 break;
783 }
784 HTPHandleError(hstate);
785 }
786
787 /* if the TCP connection is closed, then close the HTTP connection */
788 if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF) &&
789 !(hstate->flags & HTP_FLAG_STATE_CLOSED_TS))
790 {
791 htp_connp_close(hstate->connp, &ts);
792 hstate->flags |= HTP_FLAG_STATE_CLOSED_TS;
793 SCLogDebug("stream eof encountered, closing htp handle for ts");
794 }
795
796 SCLogDebug("hstate->connp %p", hstate->connp);
797 SCReturnInt(ret);
798
799 error:
800 SCReturnInt(-1);
801 }
802
803 /**
804 * \brief Function to handle the reassembled data from server and feed it to
805 * the HTP library to process it.
806 *
807 * \param flow Pointer to the flow the data belong to
808 * \param htp_state Pointer the state in which the parsed value to be stored
809 * \param pstate Application layer parser state for this session
810 * \param input Pointer the received HTTP server data
811 * \param input_len Length in bytes of the received data
812 * \param output Pointer to the output (not used in this function)
813 *
814 * \retval On success returns 1 or on failure returns -1
815 */
816 static int HTPHandleResponseData(Flow *f, void *htp_state,
817 AppLayerParserState *pstate,
818 uint8_t *input, uint32_t input_len,
819 void *local_data, const uint8_t flags)
820 {
821 SCEnter();
822 int ret = 1;
823 HtpState *hstate = (HtpState *)htp_state;
824
825 /* On the first invocation, create the connection parser structure to
826 * be used by HTP library. This is looked up via IP in the radix
827 * tree. Failing that, the default HTP config is used.
828 */
829 if (NULL == hstate->conn) {
830 if (Setup(f, hstate) != 0) {
831 goto error;
832 }
833 }
834 DEBUG_VALIDATE_BUG_ON(hstate->connp == NULL);
835
836 htp_time_t ts = { f->lastts.tv_sec, f->lastts.tv_usec };
837 if (input_len > 0) {
838 const int r = htp_connp_res_data(hstate->connp, &ts, input, input_len);
839 switch (r) {
840 case HTP_STREAM_ERROR:
841 ret = -1;
842 break;
843 default:
844 break;
845 }
846 HTPHandleError(hstate);
847 }
848
849 /* if we the TCP connection is closed, then close the HTTP connection */
850 if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF) &&
851 !(hstate->flags & HTP_FLAG_STATE_CLOSED_TC))
852 {
853 htp_connp_close(hstate->connp, &ts);
854 hstate->flags |= HTP_FLAG_STATE_CLOSED_TC;
855 }
856
857 SCLogDebug("hstate->connp %p", hstate->connp);
858 SCReturnInt(ret);
859 error:
860 SCReturnInt(-1);
861 }
862
863 /**
864 * \param name /Lowercase/ version of the variable name
865 */
866 static int HTTPParseContentDispositionHeader(uint8_t *name, size_t name_len,
867 uint8_t *data, size_t len, uint8_t **retptr, size_t *retlen)
868 {
869 #ifdef PRINT
870 printf("DATA START: \n");
871 PrintRawDataFp(stdout, data, len);
872 printf("DATA END: \n");
873 #endif
874 size_t x;
875 int quote = 0;
876
877 for (x = 0; x < len; x++) {
878 if (!(isspace(data[x])))
879 break;
880 }
881
882 if (x >= len)
883 return 0;
884
885 uint8_t *line = data+x;
886 size_t line_len = len-x;
887 size_t offset = 0;
888 #ifdef PRINT
889 printf("LINE START: \n");
890 PrintRawDataFp(stdout, line, line_len);
891 printf("LINE END: \n");
892 #endif
893 for (x = 0 ; x < line_len; x++) {
894 if (x > 0) {
895 if (line[x - 1] != '\\' && line[x] == '\"') {
896 quote++;
897 }
898
899 if (((line[x - 1] != '\\' && line[x] == ';') || ((x + 1) == line_len)) && (quote == 0 || quote % 2 == 0)) {
900 uint8_t *token = line + offset;
901 size_t token_len = x - offset;
902
903 if ((x + 1) == line_len) {
904 token_len++;
905 }
906
907 offset = x + 1;
908
909 while (offset < line_len && isspace(line[offset])) {
910 x++;
911 offset++;
912 }
913 #ifdef PRINT
914 printf("TOKEN START: \n");
915 PrintRawDataFp(stdout, token, token_len);
916 printf("TOKEN END: \n");
917 #endif
918 if (token_len > name_len) {
919 if (name == NULL || SCMemcmpLowercase(name, token, name_len) == 0) {
920 uint8_t *value = token + name_len;
921 size_t value_len = token_len - name_len;
922
923 if (value[0] == '\"') {
924 value++;
925 value_len--;
926 }
927 if (value[value_len-1] == '\"') {
928 value_len--;
929 }
930 #ifdef PRINT
931 printf("VALUE START: \n");
932 PrintRawDataFp(stdout, value, value_len);
933 printf("VALUE END: \n");
934 #endif
935 *retptr = value;
936 *retlen = value_len;
937 return 1;
938 }
939 }
940 }
941 }
942 }
943
944 return 0;
945 }
946
947 /**
948 * \param name /Lowercase/ version of the variable name
949 */
950 static int HTTPParseContentTypeHeader(uint8_t *name, size_t name_len,
951 uint8_t *data, size_t len, uint8_t **retptr, size_t *retlen)
952 {
953 SCEnter();
954 #ifdef PRINT
955 printf("DATA START: \n");
956 PrintRawDataFp(stdout, data, len);
957 printf("DATA END: \n");
958 #endif
959 size_t x;
960 int quote = 0;
961
962 for (x = 0; x < len; x++) {
963 if (!(isspace(data[x])))
964 break;
965 }
966
967 if (x >= len) {
968 SCReturnInt(0);
969 }
970
971 uint8_t *line = data+x;
972 size_t line_len = len-x;
973 size_t offset = 0;
974 #ifdef PRINT
975 printf("LINE START: \n");
976 PrintRawDataFp(stdout, line, line_len);
977 printf("LINE END: \n");
978 #endif
979 for (x = 0 ; x < line_len; x++) {
980 if (x > 0) {
981 if (line[x - 1] != '\\' && line[x] == '\"') {
982 quote++;
983 }
984
985 if (((line[x - 1] != '\\' && line[x] == ';') || ((x + 1) == line_len)) && (quote == 0 || quote % 2 == 0)) {
986 uint8_t *token = line + offset;
987 size_t token_len = x - offset;
988
989 if ((x + 1) == line_len) {
990 token_len++;
991 }
992
993 offset = x + 1;
994
995 while (offset < line_len && isspace(line[offset])) {
996 x++;
997 offset++;
998 }
999 #ifdef PRINT
1000 printf("TOKEN START: \n");
1001 PrintRawDataFp(stdout, token, token_len);
1002 printf("TOKEN END: \n");
1003 #endif
1004 if (token_len > name_len) {
1005 if (name == NULL || SCMemcmpLowercase(name, token, name_len) == 0) {
1006 uint8_t *value = token + name_len;
1007 size_t value_len = token_len - name_len;
1008
1009 if (value[0] == '\"') {
1010 value++;
1011 value_len--;
1012 }
1013 if (value[value_len-1] == '\"') {
1014 value_len--;
1015 }
1016 #ifdef PRINT
1017 printf("VALUE START: \n");
1018 PrintRawDataFp(stdout, value, value_len);
1019 printf("VALUE END: \n");
1020 #endif
1021 *retptr = value;
1022 *retlen = value_len;
1023 SCReturnInt(1);
1024 }
1025 }
1026 }
1027 }
1028 }
1029
1030 SCReturnInt(0);
1031 }
1032
1033 /**
1034 * \brief setup multipart parsing: extract boundary and store it
1035 *
1036 * \param d HTTP transaction
1037 * \param htud transaction userdata
1038 *
1039 * \retval 1 ok, multipart set up
1040 * \retval 0 ok, not multipart though
1041 * \retval -1 error: problem with the boundary
1042 *
1043 * If the request contains a multipart message, this function will
1044 * set the HTP_BOUNDARY_SET in the transaction.
1045 */
1046 static int HtpRequestBodySetupMultipart(htp_tx_data_t *d, HtpTxUserData *htud)
1047 {
1048 htp_header_t *h = (htp_header_t *)htp_table_get_c(d->tx->request_headers,
1049 "Content-Type");
1050 if (h != NULL && bstr_len(h->value) > 0) {
1051 uint8_t *boundary = NULL;
1052 size_t boundary_len = 0;
1053
1054 int r = HTTPParseContentTypeHeader((uint8_t *)"boundary=", 9,
1055 (uint8_t *) bstr_ptr(h->value), bstr_len(h->value),
1056 &boundary, &boundary_len);
1057 if (r == 1) {
1058 #ifdef PRINT
1059 printf("BOUNDARY START: \n");
1060 PrintRawDataFp(stdout, boundary, boundary_len);
1061 printf("BOUNDARY END: \n");
1062 #endif
1063 if (boundary_len < HTP_BOUNDARY_MAX) {
1064 htud->boundary = HTPMalloc(boundary_len);
1065 if (htud->boundary == NULL) {
1066 return -1;
1067 }
1068 htud->boundary_len = (uint8_t)boundary_len;
1069 memcpy(htud->boundary, boundary, boundary_len);
1070
1071 htud->tsflags |= HTP_BOUNDARY_SET;
1072 } else {
1073 SCLogDebug("invalid boundary");
1074 return -1;
1075 }
1076 SCReturnInt(1);
1077 }
1078 //SCReturnInt(1);
1079 }
1080 SCReturnInt(0);
1081 }
1082
1083 #define C_D_HDR "content-disposition:"
1084 #define C_D_HDR_LEN 20
1085 #define C_T_HDR "content-type:"
1086 #define C_T_HDR_LEN 13
1087
1088 static void HtpRequestBodyMultipartParseHeader(HtpState *hstate,
1089 HtpTxUserData *htud,
1090 uint8_t *header, uint32_t header_len,
1091 uint8_t **filename, uint16_t *filename_len,
1092 uint8_t **filetype, uint16_t *filetype_len)
1093 {
1094 uint8_t *fn = NULL;
1095 size_t fn_len = 0;
1096 uint8_t *ft = NULL;
1097 size_t ft_len = 0;
1098
1099 #ifdef PRINT
1100 printf("HEADER START: \n");
1101 PrintRawDataFp(stdout, header, header_len);
1102 printf("HEADER END: \n");
1103 #endif
1104
1105 while (header_len > 0) {
1106 uint8_t *next_line = Bs2bmSearch(header, header_len, (uint8_t *)"\r\n", 2);
1107 uint8_t *line = header;
1108 uint32_t line_len;
1109
1110 if (next_line == NULL) {
1111 line_len = header_len;
1112 } else {
1113 line_len = next_line - header;
1114 }
1115 uint8_t *sc = (uint8_t *)memchr(line, ':', line_len);
1116 if (sc == NULL) {
1117 HTPSetEvent(hstate, htud,
1118 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER);
1119 /* if the : we found is the final char, it means we have
1120 * no value */
1121 } else if (line_len > 0 && sc == &line[line_len - 1]) {
1122 HTPSetEvent(hstate, htud,
1123 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER);
1124 } else {
1125 #ifdef PRINT
1126 printf("LINE START: \n");
1127 PrintRawDataFp(stdout, line, line_len);
1128 printf("LINE END: \n");
1129 #endif
1130 if (line_len >= C_D_HDR_LEN &&
1131 SCMemcmpLowercase(C_D_HDR, line, C_D_HDR_LEN) == 0) {
1132 uint8_t *value = line + C_D_HDR_LEN;
1133 uint32_t value_len = line_len - C_D_HDR_LEN;
1134
1135 /* parse content-disposition */
1136 (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1137 value, value_len, &fn, &fn_len);
1138 } else if (line_len >= C_T_HDR_LEN &&
1139 SCMemcmpLowercase(C_T_HDR, line, C_T_HDR_LEN) == 0) {
1140 SCLogDebug("content-type line");
1141 uint8_t *value = line + C_T_HDR_LEN;
1142 uint32_t value_len = line_len - C_T_HDR_LEN;
1143
1144 (void)HTTPParseContentTypeHeader(NULL, 0,
1145 value, value_len, &ft, &ft_len);
1146 }
1147 }
1148
1149 if (next_line == NULL) {
1150 SCLogDebug("no next_line");
1151 break;
1152 }
1153 header_len -= ((next_line + 2) - header);
1154 header = next_line + 2;
1155 } /* while (header_len > 0) */
1156
1157 if (fn_len > USHRT_MAX)
1158 fn_len = USHRT_MAX;
1159 if (ft_len > USHRT_MAX)
1160 ft_len = USHRT_MAX;
1161
1162 *filename = fn;
1163 *filename_len = fn_len;
1164 *filetype = ft;
1165 *filetype_len = ft_len;
1166 }
1167
1168 /**
1169 * \brief Create a single buffer from the HtpBodyChunks in our list
1170 *
1171 * \param htud transaction user data
1172 * \param chunks_buffers pointer to pass back the buffer to the caller
1173 * \param chunks_buffer_len pointer to pass back the buffer length to the caller
1174 */
1175 static void HtpRequestBodyReassemble(HtpTxUserData *htud,
1176 const uint8_t **chunks_buffer, uint32_t *chunks_buffer_len)
1177 {
1178 StreamingBufferGetDataAtOffset(htud->request_body.sb,
1179 chunks_buffer, chunks_buffer_len,
1180 htud->request_body.body_parsed);
1181 }
1182
1183 static void FlagDetectStateNewFile(HtpTxUserData *tx, int dir)
1184 {
1185 SCEnter();
1186 if (tx && tx->de_state) {
1187 if (dir == STREAM_TOSERVER) {
1188 SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1189 tx->de_state->dir_state[0].flags |= DETECT_ENGINE_STATE_FLAG_FILE_NEW;
1190 } else if (STREAM_TOCLIENT) {
1191 SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1192 tx->de_state->dir_state[1].flags |= DETECT_ENGINE_STATE_FLAG_FILE_NEW;
1193 }
1194 }
1195 }
1196
1197 /**
1198 * \brief Setup boundary buffers
1199 */
1200 static void HtpRequestBodySetupBoundary(HtpTxUserData *htud,
1201 uint8_t *boundary, uint32_t boundary_len)
1202 {
1203 memset(boundary, '-', boundary_len);
1204 memcpy(boundary + 2, htud->boundary, htud->boundary_len);
1205 }
1206
1207 static int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud, void *tx,
1208 const uint8_t *chunks_buffer, uint32_t chunks_buffer_len)
1209 {
1210 int result = 0;
1211 uint8_t boundary[htud->boundary_len + 4]; /**< size limited to HTP_BOUNDARY_MAX + 4 */
1212 uint8_t expected_boundary_len = htud->boundary_len + 2;
1213 uint8_t expected_boundary_end_len = htud->boundary_len + 4;
1214 int tx_progress = 0;
1215
1216 #ifdef PRINT
1217 printf("CHUNK START: \n");
1218 PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
1219 printf("CHUNK END: \n");
1220 #endif
1221
1222 HtpRequestBodySetupBoundary(htud, boundary, htud->boundary_len + 4);
1223
1224 /* search for the header start, header end and form end */
1225 uint8_t *header_start = Bs2bmSearch(chunks_buffer, chunks_buffer_len,
1226 boundary, expected_boundary_len);
1227 uint8_t *header_end = NULL;
1228 if (header_start != NULL) {
1229 header_end = Bs2bmSearch(header_start, chunks_buffer_len - (header_start - chunks_buffer),
1230 (uint8_t *)"\r\n\r\n", 4);
1231 }
1232 uint8_t *form_end = Bs2bmSearch(chunks_buffer, chunks_buffer_len,
1233 boundary, expected_boundary_end_len);
1234
1235 SCLogDebug("header_start %p, header_end %p, form_end %p", header_start,
1236 header_end, form_end);
1237
1238 /* we currently only handle multipart for ts. When we support it for tc,
1239 * we will need to supply right direction */
1240 tx_progress = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, STREAM_TOSERVER);
1241 /* if we're in the file storage process, deal with that now */
1242 if (htud->tsflags & HTP_FILENAME_SET) {
1243 if (header_start != NULL || form_end != NULL || (tx_progress > HTP_REQUEST_BODY)) {
1244 SCLogDebug("reached the end of the file");
1245
1246 const uint8_t *filedata = chunks_buffer;
1247 uint32_t filedata_len = 0;
1248 uint8_t flags = 0;
1249
1250 if (header_start < form_end || (header_start != NULL && form_end == NULL)) {
1251 filedata_len = header_start - filedata - 2; /* 0d 0a */
1252 } else if (form_end != NULL && form_end < header_start) {
1253 filedata_len = form_end - filedata;
1254 } else if (form_end != NULL && form_end == header_start) {
1255 filedata_len = form_end - filedata - 2; /* 0d 0a */
1256 } else if (tx_progress > HTP_RESPONSE_BODY) {
1257 filedata_len = chunks_buffer_len;
1258 flags = FILE_TRUNCATED;
1259 }
1260
1261 if (filedata_len > chunks_buffer_len) {
1262 HTPSetEvent(hstate, htud,
1263 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
1264 goto end;
1265 }
1266 #ifdef PRINT
1267 printf("FILEDATA (final chunk) START: \n");
1268 PrintRawDataFp(stdout, filedata, filedata_len);
1269 printf("FILEDATA (final chunk) END: \n");
1270 #endif
1271 if (!(htud->tsflags & HTP_DONTSTORE)) {
1272 if (HTPFileClose(hstate, filedata, filedata_len, flags,
1273 STREAM_TOSERVER) == -1)
1274 {
1275 goto end;
1276 }
1277 }
1278
1279 htud->tsflags &=~ HTP_FILENAME_SET;
1280
1281 /* fall through */
1282 } else {
1283 SCLogDebug("not yet at the end of the file");
1284
1285 if (chunks_buffer_len > expected_boundary_end_len) {
1286 const uint8_t *filedata = chunks_buffer;
1287 uint32_t filedata_len = chunks_buffer_len - expected_boundary_len;
1288 #ifdef PRINT
1289 printf("FILEDATA (part) START: \n");
1290 PrintRawDataFp(stdout, filedata, filedata_len);
1291 printf("FILEDATA (part) END: \n");
1292 #endif
1293
1294 if (!(htud->tsflags & HTP_DONTSTORE)) {
1295 result = HTPFileStoreChunk(hstate, filedata,
1296 filedata_len, STREAM_TOSERVER);
1297 if (result == -1) {
1298 goto end;
1299 } else if (result == -2) {
1300 /* we know for sure we're not storing the file */
1301 htud->tsflags |= HTP_DONTSTORE;
1302 }
1303 }
1304
1305 htud->request_body.body_parsed += filedata_len;
1306 } else {
1307 SCLogDebug("chunk too small to already process in part");
1308 }
1309
1310 goto end;
1311 }
1312 }
1313
1314 while (header_start != NULL && header_end != NULL &&
1315 header_end != form_end &&
1316 header_start < (chunks_buffer + chunks_buffer_len) &&
1317 header_end < (chunks_buffer + chunks_buffer_len) &&
1318 header_start < header_end)
1319 {
1320 uint8_t *filename = NULL;
1321 uint16_t filename_len = 0;
1322 uint8_t *filetype = NULL;
1323 uint16_t filetype_len = 0;
1324
1325 uint32_t header_len = header_end - header_start;
1326 SCLogDebug("header_len %u", header_len);
1327 uint8_t *header = header_start;
1328
1329 /* skip empty records */
1330 if (expected_boundary_len == header_len) {
1331 goto next;
1332 } else if ((uint32_t)(expected_boundary_len + 2) <= header_len) {
1333 header_len -= (expected_boundary_len + 2);
1334 header = header_start + (expected_boundary_len + 2); // + for 0d 0a
1335 }
1336
1337 HtpRequestBodyMultipartParseHeader(hstate, htud, header, header_len,
1338 &filename, &filename_len, &filetype, &filetype_len);
1339
1340 if (filename != NULL) {
1341 uint8_t *filedata = NULL;
1342 uint32_t filedata_len = 0;
1343
1344 SCLogDebug("we have a filename");
1345
1346 htud->tsflags |= HTP_FILENAME_SET;
1347 htud->tsflags &= ~HTP_DONTSTORE;
1348
1349 SCLogDebug("header_end %p", header_end);
1350 SCLogDebug("form_end %p", form_end);
1351
1352 /* everything until the final boundary is the file */
1353 if (form_end != NULL) {
1354 filedata = header_end + 4;
1355 if (form_end == filedata) {
1356 HTPSetEvent(hstate, htud,
1357 HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA);
1358 goto end;
1359 } else if (form_end < filedata) {
1360 HTPSetEvent(hstate, htud,
1361 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
1362 goto end;
1363 }
1364
1365 filedata_len = form_end - (header_end + 4 + 2);
1366 SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
1367
1368 /* or is it? */
1369 uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
1370 boundary, expected_boundary_len);
1371 if (header_next != NULL) {
1372 filedata_len -= (form_end - header_next);
1373 }
1374
1375 if (filedata_len > chunks_buffer_len) {
1376 HTPSetEvent(hstate, htud,
1377 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
1378 goto end;
1379 }
1380 SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
1381 #ifdef PRINT
1382 printf("FILEDATA START: \n");
1383 PrintRawDataFp(stdout, filedata, filedata_len);
1384 printf("FILEDATA END: \n");
1385 #endif
1386
1387 result = HTPFileOpen(hstate, filename, filename_len,
1388 filedata, filedata_len, hstate->transaction_cnt,
1389 STREAM_TOSERVER);
1390 if (result == -1) {
1391 goto end;
1392 } else if (result == -2) {
1393 htud->tsflags |= HTP_DONTSTORE;
1394 } else {
1395 if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) {
1396 goto end;
1397 }
1398 }
1399 FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1400
1401 htud->request_body.body_parsed += (header_end - chunks_buffer);
1402 htud->tsflags &= ~HTP_FILENAME_SET;
1403 } else {
1404 SCLogDebug("chunk doesn't contain form end");
1405
1406 filedata = header_end + 4;
1407 filedata_len = chunks_buffer_len - (filedata - chunks_buffer);
1408 SCLogDebug("filedata_len %u (chunks_buffer_len %u)", filedata_len, chunks_buffer_len);
1409
1410 if (filedata_len > chunks_buffer_len) {
1411 HTPSetEvent(hstate, htud,
1412 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR);
1413 goto end;
1414 }
1415
1416 #ifdef PRINT
1417 printf("FILEDATA START: \n");
1418 PrintRawDataFp(stdout, filedata, filedata_len);
1419 printf("FILEDATA END: \n");
1420 #endif
1421 /* form doesn't end in this chunk, but part might. Lets
1422 * see if have another coming up */
1423 uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
1424 boundary, expected_boundary_len);
1425 SCLogDebug("header_next %p", header_next);
1426 if (header_next == NULL) {
1427 /* no, but we'll handle the file data when we see the
1428 * form_end */
1429
1430 SCLogDebug("more file data to come");
1431
1432 uint32_t offset = (header_end + 4) - chunks_buffer;
1433 SCLogDebug("offset %u", offset);
1434 htud->request_body.body_parsed += offset;
1435
1436 result = HTPFileOpen(hstate, filename, filename_len,
1437 NULL, 0, hstate->transaction_cnt,
1438 STREAM_TOSERVER);
1439 if (result == -1) {
1440 goto end;
1441 } else if (result == -2) {
1442 htud->tsflags |= HTP_DONTSTORE;
1443 }
1444 FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1445
1446 } else if (header_next - filedata > 2) {
1447 filedata_len = header_next - filedata - 2;
1448 SCLogDebug("filedata_len %u", filedata_len);
1449
1450 result = HTPFileOpen(hstate, filename, filename_len,
1451 filedata, filedata_len, hstate->transaction_cnt,
1452 STREAM_TOSERVER);
1453 if (result == -1) {
1454 goto end;
1455 } else if (result == -2) {
1456 htud->tsflags |= HTP_DONTSTORE;
1457 } else {
1458 if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) {
1459 goto end;
1460 }
1461 }
1462 FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1463
1464 htud->tsflags &= ~HTP_FILENAME_SET;
1465 htud->request_body.body_parsed += (header_end - chunks_buffer);
1466 }
1467 }
1468 }
1469 next:
1470 SCLogDebug("header_start %p, header_end %p, form_end %p",
1471 header_start, header_end, form_end);
1472
1473 /* Search next boundary entry after the start of body */
1474 uint32_t cursizeread = header_end - chunks_buffer;
1475 header_start = Bs2bmSearch(header_end + 4,
1476 chunks_buffer_len - (cursizeread + 4),
1477 boundary, expected_boundary_len);
1478 if (header_start != NULL) {
1479 header_end = Bs2bmSearch(header_end + 4,
1480 chunks_buffer_len - (cursizeread + 4),
1481 (uint8_t *) "\r\n\r\n", 4);
1482 }
1483 }
1484
1485 /* if we're parsing the multipart and we're not currently processing a
1486 * file, we move the body pointer forward. */
1487 if (form_end == NULL && !(htud->tsflags & HTP_FILENAME_SET) && header_start == NULL) {
1488 if (chunks_buffer_len > expected_boundary_end_len) {
1489 uint32_t move = chunks_buffer_len - expected_boundary_end_len + 1;
1490
1491 htud->request_body.body_parsed += move;
1492 SCLogDebug("form not ready, file not set, parsing non-file "
1493 "record: moved %u", move);
1494 }
1495 }
1496
1497 end:
1498 SCLogDebug("htud->request_body.body_parsed %"PRIu64, htud->request_body.body_parsed);
1499 return 0;
1500 }
1501
1502 /** \brief setup things for put request
1503 * \todo really needed? */
1504 static int HtpRequestBodySetupPUT(htp_tx_data_t *d, HtpTxUserData *htud)
1505 {
1506 // if (d->tx->parsed_uri == NULL || d->tx->parsed_uri->path == NULL) {
1507 // return -1;
1508 // }
1509
1510 /* filename is d->tx->parsed_uri->path */
1511
1512 return 0;
1513 }
1514
1515 /** \internal
1516 * \brief Handle POST, no multipart body data
1517 */
1518 static int HtpRequestBodyHandlePOST(HtpState *hstate, HtpTxUserData *htud,
1519 htp_tx_t *tx, uint8_t *data, uint32_t data_len)
1520 {
1521 int result = 0;
1522
1523 /* see if we need to open the file */
1524 if (!(htud->tsflags & HTP_FILENAME_SET))
1525 {
1526 uint8_t *filename = NULL;
1527 size_t filename_len = 0;
1528
1529 /* get the name */
1530 if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
1531 filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
1532 filename_len = bstr_len(tx->parsed_uri->path);
1533 }
1534
1535 if (filename != NULL) {
1536 result = HTPFileOpen(hstate, filename, (uint32_t)filename_len, data, data_len,
1537 hstate->transaction_cnt, STREAM_TOSERVER);
1538 if (result == -1) {
1539 goto end;
1540 } else if (result == -2) {
1541 htud->tsflags |= HTP_DONTSTORE;
1542 } else {
1543 FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1544 htud->tsflags |= HTP_FILENAME_SET;
1545 htud->tsflags &= ~HTP_DONTSTORE;
1546 }
1547 }
1548 }
1549 else
1550 {
1551 /* otherwise, just store the data */
1552
1553 if (!(htud->tsflags & HTP_DONTSTORE)) {
1554 result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOSERVER);
1555 if (result == -1) {
1556 goto end;
1557 } else if (result == -2) {
1558 /* we know for sure we're not storing the file */
1559 htud->tsflags |= HTP_DONTSTORE;
1560 }
1561 }
1562 }
1563
1564 return 0;
1565 end:
1566 return -1;
1567 }
1568
1569 /** \internal
1570 * \brief Handle PUT body data
1571 */
1572 static int HtpRequestBodyHandlePUT(HtpState *hstate, HtpTxUserData *htud,
1573 htp_tx_t *tx, uint8_t *data, uint32_t data_len)
1574 {
1575 int result = 0;
1576
1577 /* see if we need to open the file */
1578 if (!(htud->tsflags & HTP_FILENAME_SET))
1579 {
1580 uint8_t *filename = NULL;
1581 size_t filename_len = 0;
1582
1583 /* get the name */
1584 if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
1585 filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
1586 filename_len = bstr_len(tx->parsed_uri->path);
1587 }
1588
1589 if (filename != NULL) {
1590 result = HTPFileOpen(hstate, filename, (uint32_t)filename_len, data, data_len,
1591 hstate->transaction_cnt, STREAM_TOSERVER);
1592 if (result == -1) {
1593 goto end;
1594 } else if (result == -2) {
1595 htud->tsflags |= HTP_DONTSTORE;
1596 } else {
1597 FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1598 htud->tsflags |= HTP_FILENAME_SET;
1599 htud->tsflags &= ~HTP_DONTSTORE;
1600 }
1601 }
1602 }
1603 else
1604 {
1605 /* otherwise, just store the data */
1606
1607 if (!(htud->tsflags & HTP_DONTSTORE)) {
1608 result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOSERVER);
1609 if (result == -1) {
1610 goto end;
1611 } else if (result == -2) {
1612 /* we know for sure we're not storing the file */
1613 htud->tsflags |= HTP_DONTSTORE;
1614 }
1615 }
1616 }
1617
1618 return 0;
1619 end:
1620 return -1;
1621 }
1622
1623 static int HtpResponseBodyHandle(HtpState *hstate, HtpTxUserData *htud,
1624 htp_tx_t *tx, uint8_t *data, uint32_t data_len)
1625 {
1626 SCEnter();
1627
1628 int result = 0;
1629
1630 /* see if we need to open the file */
1631 if (!(htud->tcflags & HTP_FILENAME_SET))
1632 {
1633 SCLogDebug("setting up file name");
1634
1635 uint8_t *filename = NULL;
1636 size_t filename_len = 0;
1637
1638 /* try Content-Disposition header first */
1639 htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->response_headers,
1640 "Content-Disposition");
1641 if (h != NULL && bstr_len(h->value) > 0) {
1642 /* parse content-disposition */
1643 (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1644 (uint8_t *) bstr_ptr(h->value), bstr_len(h->value), &filename, &filename_len);
1645 }
1646
1647 /* fall back to name from the uri */
1648 if (filename == NULL) {
1649 /* get the name */
1650 if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
1651 filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
1652 filename_len = bstr_len(tx->parsed_uri->path);
1653 }
1654 }
1655
1656 if (filename != NULL) {
1657 result = HTPFileOpen(hstate, filename, (uint32_t)filename_len,
1658 data, data_len, hstate->transaction_cnt, STREAM_TOCLIENT);
1659 SCLogDebug("result %d", result);
1660 if (result == -1) {
1661 goto end;
1662 } else if (result == -2) {
1663 htud->tcflags |= HTP_DONTSTORE;
1664 } else {
1665 FlagDetectStateNewFile(htud, STREAM_TOCLIENT);
1666 htud->tcflags |= HTP_FILENAME_SET;
1667 htud->tcflags &= ~HTP_DONTSTORE;
1668 }
1669 //set range if present
1670 htp_header_t *h_content_range = htp_table_get_c(tx->response_headers, "content-range");
1671 if (h_content_range != NULL) {
1672 HTPFileSetRange(hstate, h_content_range->value);
1673 }
1674 }
1675 }
1676 else
1677 {
1678 /* otherwise, just store the data */
1679
1680 if (!(htud->tcflags & HTP_DONTSTORE)) {
1681 result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOCLIENT);
1682 SCLogDebug("result %d", result);
1683 if (result == -1) {
1684 goto end;
1685 } else if (result == -2) {
1686 /* we know for sure we're not storing the file */
1687 htud->tcflags |= HTP_DONTSTORE;
1688 }
1689 }
1690 }
1691
1692 htud->response_body.body_parsed += data_len;
1693 return 0;
1694 end:
1695 return -1;
1696 }
1697
1698 /**
1699 * \brief Function callback to append chunks for Requests
1700 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1701 * \retval int HTP_OK if all goes well
1702 */
1703 static int HTPCallbackRequestBodyData(htp_tx_data_t *d)
1704 {
1705 SCEnter();
1706
1707 if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_REQUEST_BODY))
1708 SCReturnInt(HTP_OK);
1709
1710 if (d->data == NULL || d->len == 0)
1711 SCReturnInt(HTP_OK);
1712
1713 #ifdef PRINT
1714 printf("HTPBODY START: \n");
1715 PrintRawDataFp(stdout, (uint8_t *)d->data, d->len);
1716 printf("HTPBODY END: \n");
1717 #endif
1718
1719 HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
1720 if (hstate == NULL) {
1721 SCReturnInt(HTP_ERROR);
1722 }
1723
1724 SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
1725 "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
1726
1727 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
1728 if (tx_ud == NULL) {
1729 tx_ud = HTPMalloc(sizeof(HtpTxUserData));
1730 if (unlikely(tx_ud == NULL)) {
1731 SCReturnInt(HTP_OK);
1732 }
1733 memset(tx_ud, 0, sizeof(HtpTxUserData));
1734
1735 /* Set the user data for handling body chunks on this transaction */
1736 htp_tx_set_user_data(d->tx, tx_ud);
1737 }
1738 if (!tx_ud->response_body_init) {
1739 tx_ud->response_body_init = 1;
1740
1741 if (d->tx->request_method_number == HTP_M_POST) {
1742 SCLogDebug("POST");
1743 int r = HtpRequestBodySetupMultipart(d, tx_ud);
1744 if (r == 1) {
1745 tx_ud->request_body_type = HTP_BODY_REQUEST_MULTIPART;
1746 } else if (r == 0) {
1747 tx_ud->request_body_type = HTP_BODY_REQUEST_POST;
1748 SCLogDebug("not multipart");
1749 }
1750 } else if (d->tx->request_method_number == HTP_M_PUT) {
1751 if (HtpRequestBodySetupPUT(d, tx_ud) == 0) {
1752 tx_ud->request_body_type = HTP_BODY_REQUEST_PUT;
1753 }
1754 }
1755 }
1756
1757 /* see if we can get rid of htp body chunks */
1758 HtpBodyPrune(hstate, &tx_ud->request_body, STREAM_TOSERVER);
1759
1760 SCLogDebug("tx_ud->request_body.content_len_so_far %"PRIu64, tx_ud->request_body.content_len_so_far);
1761 SCLogDebug("hstate->cfg->request.body_limit %u", hstate->cfg->request.body_limit);
1762
1763 /* within limits, add the body chunk to the state. */
1764 if (hstate->cfg->request.body_limit == 0 || tx_ud->request_body.content_len_so_far < hstate->cfg->request.body_limit)
1765 {
1766 uint32_t len = (uint32_t)d->len;
1767
1768 if (hstate->cfg->request.body_limit > 0 &&
1769 (tx_ud->request_body.content_len_so_far + len) > hstate->cfg->request.body_limit)
1770 {
1771 len = hstate->cfg->request.body_limit - tx_ud->request_body.content_len_so_far;
1772 BUG_ON(len > (uint32_t)d->len);
1773 }
1774 SCLogDebug("len %u", len);
1775
1776 HtpBodyAppendChunk(&hstate->cfg->request, &tx_ud->request_body, d->data, len);
1777
1778 const uint8_t *chunks_buffer = NULL;
1779 uint32_t chunks_buffer_len = 0;
1780
1781 if (tx_ud->request_body_type == HTP_BODY_REQUEST_MULTIPART) {
1782 /* multi-part body handling starts here */
1783 if (!(tx_ud->tsflags & HTP_BOUNDARY_SET)) {
1784 goto end;
1785 }
1786
1787 HtpRequestBodyReassemble(tx_ud, &chunks_buffer, &chunks_buffer_len);
1788 if (chunks_buffer == NULL) {
1789 goto end;
1790 }
1791 #ifdef PRINT
1792 printf("REASSCHUNK START: \n");
1793 PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
1794 printf("REASSCHUNK END: \n");
1795 #endif
1796
1797 HtpRequestBodyHandleMultipart(hstate, tx_ud, d->tx, chunks_buffer, chunks_buffer_len);
1798
1799 } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_POST) {
1800 HtpRequestBodyHandlePOST(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
1801 } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_PUT) {
1802 HtpRequestBodyHandlePUT(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
1803 }
1804
1805 } else {
1806 if (tx_ud->tsflags & HTP_FILENAME_SET) {
1807 SCLogDebug("closing file that was being stored");
1808 (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOSERVER);
1809 tx_ud->tsflags &= ~HTP_FILENAME_SET;
1810 }
1811 }
1812
1813 end:
1814 if (hstate->conn != NULL) {
1815 SCLogDebug("checking body size %"PRIu64" against inspect limit %u (cur %"PRIu64", last %"PRIu64")",
1816 tx_ud->request_body.content_len_so_far,
1817 hstate->cfg->request.inspect_min_size,
1818 (uint64_t)hstate->conn->in_data_counter, hstate->last_request_data_stamp);
1819
1820 /* if we reach the inspect_min_size we'll trigger inspection,
1821 * so make sure that raw stream is also inspected. Set the
1822 * data to be used to the amount of raw bytes we've seen to
1823 * get here. */
1824 if (tx_ud->request_body.body_inspected == 0 &&
1825 tx_ud->request_body.content_len_so_far >= hstate->cfg->request.inspect_min_size) {
1826 if ((uint64_t)hstate->conn->in_data_counter > hstate->last_request_data_stamp &&
1827 (uint64_t)hstate->conn->in_data_counter - hstate->last_request_data_stamp < (uint64_t)UINT_MAX)
1828 {
1829 uint32_t x = (uint32_t)((uint64_t)hstate->conn->in_data_counter - hstate->last_request_data_stamp);
1830
1831 /* body still in progress, but due to min inspect size we need to inspect now */
1832 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOSERVER, x);
1833 AppLayerParserTriggerRawStreamReassembly(hstate->f, STREAM_TOSERVER);
1834 }
1835 /* after the start of the body, disable the depth logic */
1836 } else if (tx_ud->request_body.body_inspected > 0) {
1837 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOSERVER, 0);
1838 }
1839 }
1840 SCReturnInt(HTP_OK);
1841 }
1842
1843 /**
1844 * \brief Function callback to append chunks for Responses
1845 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1846 * \retval int HTP_OK if all goes well
1847 */
1848 static int HTPCallbackResponseBodyData(htp_tx_data_t *d)
1849 {
1850 SCEnter();
1851
1852 if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_RESPONSE_BODY))
1853 SCReturnInt(HTP_OK);
1854
1855 if (d->data == NULL || d->len == 0)
1856 SCReturnInt(HTP_OK);
1857
1858 HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
1859 if (hstate == NULL) {
1860 SCReturnInt(HTP_ERROR);
1861 }
1862
1863 SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
1864 "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
1865
1866 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
1867 if (tx_ud == NULL) {
1868 tx_ud = HTPMalloc(sizeof(HtpTxUserData));
1869 if (unlikely(tx_ud == NULL)) {
1870 SCReturnInt(HTP_OK);
1871 }
1872 memset(tx_ud, 0, sizeof(HtpTxUserData));
1873
1874 /* Set the user data for handling body chunks on this transaction */
1875 htp_tx_set_user_data(d->tx, tx_ud);
1876 }
1877 if (!tx_ud->request_body_init) {
1878 tx_ud->request_body_init = 1;
1879 }
1880
1881 /* see if we can get rid of htp body chunks */
1882 HtpBodyPrune(hstate, &tx_ud->response_body, STREAM_TOCLIENT);
1883
1884 SCLogDebug("tx_ud->response_body.content_len_so_far %"PRIu64, tx_ud->response_body.content_len_so_far);
1885 SCLogDebug("hstate->cfg->response.body_limit %u", hstate->cfg->response.body_limit);
1886
1887 /* within limits, add the body chunk to the state. */
1888 if (hstate->cfg->response.body_limit == 0 || tx_ud->response_body.content_len_so_far < hstate->cfg->response.body_limit)
1889 {
1890 uint32_t len = (uint32_t)d->len;
1891
1892 if (hstate->cfg->response.body_limit > 0 &&
1893 (tx_ud->response_body.content_len_so_far + len) > hstate->cfg->response.body_limit)
1894 {
1895 len = hstate->cfg->response.body_limit - tx_ud->response_body.content_len_so_far;
1896 BUG_ON(len > (uint32_t)d->len);
1897 }
1898 SCLogDebug("len %u", len);
1899
1900 HtpBodyAppendChunk(&hstate->cfg->response, &tx_ud->response_body, d->data, len);
1901
1902 HtpResponseBodyHandle(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
1903 } else {
1904 if (tx_ud->tcflags & HTP_FILENAME_SET) {
1905 SCLogDebug("closing file that was being stored");
1906 (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOCLIENT);
1907 tx_ud->tcflags &= ~HTP_FILENAME_SET;
1908 }
1909 }
1910
1911 if (hstate->conn != NULL) {
1912 SCLogDebug("checking body size %"PRIu64" against inspect limit %u (cur %"PRIu64", last %"PRIu64")",
1913 tx_ud->response_body.content_len_so_far,
1914 hstate->cfg->response.inspect_min_size,
1915 (uint64_t)hstate->conn->in_data_counter, hstate->last_response_data_stamp);
1916 /* if we reach the inspect_min_size we'll trigger inspection,
1917 * so make sure that raw stream is also inspected. Set the
1918 * data to be used to the amount of raw bytes we've seen to
1919 * get here. */
1920 if (tx_ud->response_body.body_inspected == 0 &&
1921 tx_ud->response_body.content_len_so_far >= hstate->cfg->response.inspect_min_size) {
1922 if ((uint64_t)hstate->conn->out_data_counter > hstate->last_response_data_stamp &&
1923 (uint64_t)hstate->conn->out_data_counter - hstate->last_response_data_stamp < (uint64_t)UINT_MAX)
1924 {
1925 uint32_t x = (uint32_t)((uint64_t)hstate->conn->out_data_counter - hstate->last_response_data_stamp);
1926
1927 /* body still in progress, but due to min inspect size we need to inspect now */
1928 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOCLIENT, x);
1929 AppLayerParserTriggerRawStreamReassembly(hstate->f, STREAM_TOCLIENT);
1930 }
1931 /* after the start of the body, disable the depth logic */
1932 } else if (tx_ud->response_body.body_inspected > 0) {
1933 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOCLIENT, 0);
1934 }
1935 }
1936 SCReturnInt(HTP_OK);
1937 }
1938
1939 /**
1940 * \brief Print the stats of the HTTP requests
1941 */
1942 void HTPAtExitPrintStats(void)
1943 {
1944 #ifdef DEBUG
1945 SCEnter();
1946 SCMutexLock(&htp_state_mem_lock);
1947 SCLogDebug("http_state_memcnt %"PRIu64", http_state_memuse %"PRIu64"",
1948 htp_state_memcnt, htp_state_memuse);
1949 SCMutexUnlock(&htp_state_mem_lock);
1950 SCReturn;
1951 #endif
1952 }
1953
1954 /** \brief Clears the HTTP server configuration memory used by HTP library */
1955 void HTPFreeConfig(void)
1956 {
1957 SCEnter();
1958
1959 if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
1960 !AppLayerParserConfParserEnabled("tcp", "http"))
1961 {
1962 SCReturn;
1963 }
1964
1965 HTPCfgRec *nextrec = cfglist.next;
1966 SCRadixReleaseRadixTree(cfgtree);
1967 cfgtree = NULL;
1968 htp_config_destroy(cfglist.cfg);
1969 while (nextrec != NULL) {
1970 HTPCfgRec *htprec = nextrec;
1971 nextrec = nextrec->next;
1972
1973 htp_config_destroy(htprec->cfg);
1974 SCFree(htprec);
1975 }
1976 HTPDestroyMemcap();
1977 SCReturn;
1978 }
1979
1980 static int HTPCallbackRequestHasTrailer(htp_tx_t *tx)
1981 {
1982 HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
1983 if (htud != NULL) {
1984 htud->request_has_trailers = 1;
1985 }
1986 return HTP_OK;
1987 }
1988
1989 static int HTPCallbackResponseHasTrailer(htp_tx_t *tx)
1990 {
1991 HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
1992 if (htud != NULL) {
1993 htud->response_has_trailers = 1;
1994 }
1995 return HTP_OK;
1996 }
1997
1998 /**\internal
1999 * \brief called at start of request
2000 * Set min inspect size.
2001 */
2002 static int HTPCallbackRequestStart(htp_tx_t *tx)
2003 {
2004 HtpState *hstate = htp_connp_get_user_data(tx->connp);
2005 if (hstate == NULL) {
2006 SCReturnInt(HTP_ERROR);
2007 }
2008
2009 if (hstate->cfg)
2010 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOSERVER,
2011 hstate->cfg->request.inspect_min_size);
2012 SCReturnInt(HTP_OK);
2013 }
2014
2015 /**\internal
2016 * \brief called at start of response
2017 * Set min inspect size.
2018 */
2019 static int HTPCallbackResponseStart(htp_tx_t *tx)
2020 {
2021 HtpState *hstate = htp_connp_get_user_data(tx->connp);
2022 if (hstate == NULL) {
2023 SCReturnInt(HTP_ERROR);
2024 }
2025
2026 if (hstate->cfg)
2027 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOCLIENT,
2028 hstate->cfg->response.inspect_min_size);
2029 SCReturnInt(HTP_OK);
2030 }
2031
2032
2033 /**
2034 * \brief callback for request to store the recent incoming request
2035 in to the recent_in_tx for the given htp state
2036 * \param connp pointer to the current connection parser which has the htp
2037 * state in it as user data
2038 */
2039 static int HTPCallbackRequest(htp_tx_t *tx)
2040 {
2041 SCEnter();
2042
2043 if (tx == NULL) {
2044 SCReturnInt(HTP_ERROR);
2045 }
2046
2047 HtpState *hstate = htp_connp_get_user_data(tx->connp);
2048 if (hstate == NULL) {
2049 SCReturnInt(HTP_ERROR);
2050 }
2051
2052 SCLogDebug("transaction_cnt %"PRIu64", list_size %"PRIu64,
2053 hstate->transaction_cnt, HTPStateGetTxCnt(hstate));
2054
2055 SCLogDebug("HTTP request completed");
2056
2057 HTPErrorCheckTxRequestFlags(hstate, tx);
2058
2059 HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
2060 if (htud != NULL) {
2061 if (htud->tsflags & HTP_FILENAME_SET) {
2062 SCLogDebug("closing file that was being stored");
2063 (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER);
2064 htud->tsflags &= ~HTP_FILENAME_SET;
2065 StreamTcpReassemblySetMinInspectDepth(hstate->f->protoctx, STREAM_TOSERVER,
2066 (uint32_t)hstate->conn->in_data_counter);
2067 }
2068 }
2069
2070 hstate->last_request_data_stamp = (uint64_t)hstate->conn->in_data_counter;
2071 /* request done, do raw reassembly now to inspect state and stream
2072 * at the same time. */
2073 AppLayerParserTriggerRawStreamReassembly(hstate->f, STREAM_TOSERVER);
2074 SCReturnInt(HTP_OK);
2075 }
2076
2077 /**
2078 * \brief callback for response to remove the recent received requests
2079 from the recent_in_tx for the given htp state
2080 * \param connp pointer to the current connection parser which has the htp
2081 * state in it as user data
2082 */
2083 static int HTPCallbackResponse(htp_tx_t *tx)
2084 {
2085 SCEnter();
2086
2087 HtpState *hstate = htp_connp_get_user_data(tx->connp);
2088 if (hstate == NULL) {
2089 SCReturnInt(HTP_ERROR);
2090 }
2091
2092 /* we have one whole transaction now */
2093 hstate->transaction_cnt++;
2094
2095 HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2096 if (htud != NULL) {
2097 if (htud->tcflags & HTP_FILENAME_SET) {
2098 SCLogDebug("closing file that was being stored");
2099 (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOCLIENT);
2100 htud->tcflags &= ~HTP_FILENAME_SET;
2101 }
2102 }
2103
2104 /* response done, do raw reassembly now to inspect state and stream
2105 * at the same time. */
2106 AppLayerParserTriggerRawStreamReassembly(hstate->f, STREAM_TOCLIENT);
2107
2108 /* handle HTTP CONNECT */
2109 if (tx->request_method_number == HTP_M_CONNECT) {
2110 /* any 2XX status response implies that the connection will become
2111 a tunnel immediately after this packet (RFC 7230, 3.3.3). */
2112 if ((tx->response_status_number >= 200) &&
2113 (tx->response_status_number < 300) &&
2114 (hstate->transaction_cnt == 1)) {
2115 uint16_t dp = 0;
2116 if (tx->request_port_number != -1) {
2117 dp = (uint16_t)tx->request_port_number;
2118 }
2119 // both ALPROTO_HTTP and ALPROTO_TLS are normal options
2120 AppLayerRequestProtocolChange(hstate->f, dp, ALPROTO_UNKNOWN);
2121 tx->request_progress = HTP_REQUEST_COMPLETE;
2122 tx->response_progress = HTP_RESPONSE_COMPLETE;
2123 }
2124 }
2125
2126 hstate->last_response_data_stamp = (uint64_t)hstate->conn->out_data_counter;
2127 SCReturnInt(HTP_OK);
2128 }
2129
2130 static int HTPCallbackRequestLine(htp_tx_t *tx)
2131 {
2132 HtpTxUserData *tx_ud;
2133 bstr *request_uri_normalized;
2134 HtpState *hstate = htp_connp_get_user_data(tx->connp);
2135 const HTPCfgRec *cfg = hstate->cfg;
2136
2137 request_uri_normalized = SCHTPGenerateNormalizedUri(tx, tx->parsed_uri, cfg->uri_include_all);
2138 if (request_uri_normalized == NULL)
2139 return HTP_OK;
2140
2141 tx_ud = htp_tx_get_user_data(tx);
2142 if (likely(tx_ud == NULL)) {
2143 tx_ud = HTPMalloc(sizeof(*tx_ud));
2144 if (unlikely(tx_ud == NULL)) {
2145 bstr_free(request_uri_normalized);
2146 return HTP_OK;
2147 }
2148 memset(tx_ud, 0, sizeof(*tx_ud));
2149 htp_tx_set_user_data(tx, tx_ud);
2150 }
2151 if (unlikely(tx_ud->request_uri_normalized != NULL))
2152 bstr_free(tx_ud->request_uri_normalized);
2153 tx_ud->request_uri_normalized = request_uri_normalized;
2154
2155 if (tx->flags) {
2156 HTPErrorCheckTxRequestFlags(hstate, tx);
2157 }
2158 return HTP_OK;
2159 }
2160
2161 static int HTPCallbackDoubleDecodeQuery(htp_tx_t *tx)
2162 {
2163 if (tx->parsed_uri == NULL || tx->parsed_uri->query == NULL)
2164 return HTP_OK;
2165
2166 uint64_t flags = 0;
2167 htp_urldecode_inplace(tx->cfg, HTP_DECODER_URLENCODED, tx->parsed_uri->query, &flags);
2168
2169 return HTP_OK;
2170 }
2171
2172 static int HTPCallbackDoubleDecodePath(htp_tx_t *tx)
2173 {
2174 if (tx->parsed_uri == NULL || tx->parsed_uri->path == NULL)
2175 return HTP_OK;
2176
2177 uint64_t flags = 0;
2178 htp_urldecode_inplace(tx->cfg, HTP_DECODER_URL_PATH, tx->parsed_uri->path, &flags);
2179
2180 return HTP_OK;
2181 }
2182
2183 static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data)
2184 {
2185 void *ptmp;
2186 if (tx_data->len == 0 || tx_data->tx == NULL)
2187 return HTP_OK;
2188
2189 HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
2190 if (tx_ud == NULL) {
2191 tx_ud = HTPMalloc(sizeof(*tx_ud));
2192 if (unlikely(tx_ud == NULL))
2193 return HTP_OK;
2194 memset(tx_ud, 0, sizeof(*tx_ud));
2195 htp_tx_set_user_data(tx_data->tx, tx_ud);
2196 }
2197 ptmp = HTPRealloc(tx_ud->request_headers_raw,
2198 tx_ud->request_headers_raw_len,
2199 tx_ud->request_headers_raw_len + tx_data->len);
2200 if (ptmp == NULL) {
2201 /* error: we're freeing the entire user data */
2202 HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2203 HtpTxUserDataFree(hstate, tx_ud);
2204 htp_tx_set_user_data(tx_data->tx, NULL);
2205 return HTP_OK;
2206 }
2207 tx_ud->request_headers_raw = ptmp;
2208
2209 memcpy(tx_ud->request_headers_raw + tx_ud->request_headers_raw_len,
2210 tx_data->data, tx_data->len);
2211 tx_ud->request_headers_raw_len += tx_data->len;
2212
2213 if (tx_data->tx && tx_data->tx->flags) {
2214 HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2215 HTPErrorCheckTxRequestFlags(hstate, tx_data->tx);
2216 }
2217 return HTP_OK;
2218 }
2219
2220 static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data)
2221 {
2222 void *ptmp;
2223 if (tx_data->len == 0 || tx_data->tx == NULL)
2224 return HTP_OK;
2225
2226 HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
2227 if (tx_ud == NULL) {
2228 tx_ud = HTPMalloc(sizeof(*tx_ud));
2229 if (unlikely(tx_ud == NULL))
2230 return HTP_OK;
2231 memset(tx_ud, 0, sizeof(*tx_ud));
2232 htp_tx_set_user_data(tx_data->tx, tx_ud);
2233 }
2234 ptmp = HTPRealloc(tx_ud->response_headers_raw,
2235 tx_ud->response_headers_raw_len,
2236 tx_ud->response_headers_raw_len + tx_data->len);
2237 if (ptmp == NULL) {
2238 /* error: we're freeing the entire user data */
2239 HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2240 HtpTxUserDataFree(hstate, tx_ud);
2241 htp_tx_set_user_data(tx_data->tx, NULL);
2242 return HTP_OK;
2243 }
2244 tx_ud->response_headers_raw = ptmp;
2245
2246 memcpy(tx_ud->response_headers_raw + tx_ud->response_headers_raw_len,
2247 tx_data->data, tx_data->len);
2248 tx_ud->response_headers_raw_len += tx_data->len;
2249
2250 return HTP_OK;
2251 }
2252
2253 /*
2254 * We have a similar set function called HTPConfigSetDefaultsPhase1.
2255 */
2256 static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec)
2257 {
2258 cfg_prec->uri_include_all = FALSE;
2259 cfg_prec->request.body_limit = HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT;
2260 cfg_prec->response.body_limit = HTP_CONFIG_DEFAULT_RESPONSE_BODY_LIMIT;
2261 cfg_prec->request.inspect_min_size = HTP_CONFIG_DEFAULT_REQUEST_INSPECT_MIN_SIZE;
2262 cfg_prec->request.inspect_window = HTP_CONFIG_DEFAULT_REQUEST_INSPECT_WINDOW;
2263 cfg_prec->response.inspect_min_size = HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_MIN_SIZE;
2264 cfg_prec->response.inspect_window = HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_WINDOW;
2265
2266 if (!g_disable_randomness) {
2267 cfg_prec->randomize = HTP_CONFIG_DEFAULT_RANDOMIZE;
2268 } else {
2269 cfg_prec->randomize = 0;
2270 }
2271 cfg_prec->randomize_range = HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE;
2272
2273 htp_config_register_request_header_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
2274 htp_config_register_request_trailer_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
2275 htp_config_register_response_header_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
2276 htp_config_register_response_trailer_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
2277
2278 htp_config_register_request_trailer(cfg_prec->cfg, HTPCallbackRequestHasTrailer);
2279 htp_config_register_response_trailer(cfg_prec->cfg, HTPCallbackResponseHasTrailer);
2280
2281 htp_config_register_request_body_data(cfg_prec->cfg, HTPCallbackRequestBodyData);
2282 htp_config_register_response_body_data(cfg_prec->cfg, HTPCallbackResponseBodyData);
2283
2284 htp_config_register_request_start(cfg_prec->cfg, HTPCallbackRequestStart);
2285 htp_config_register_request_complete(cfg_prec->cfg, HTPCallbackRequest);
2286
2287 htp_config_register_response_start(cfg_prec->cfg, HTPCallbackResponseStart);
2288 htp_config_register_response_complete(cfg_prec->cfg, HTPCallbackResponse);
2289
2290 htp_config_set_parse_request_cookies(cfg_prec->cfg, 0);
2291
2292 /* don't convert + to space by default */
2293 htp_config_set_plusspace_decode(cfg_prec->cfg, HTP_DECODER_URLENCODED, 0);
2294
2295 /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set
2296 * only the hard limit. So we set both here to the (current) htp defaults.
2297 * The reason we do this is that if the user sets the hard limit in the
2298 * config, we have to set the soft limit as well. If libhtp starts using
2299 * the soft limit in the future, we at least make sure we control what
2300 * it's value is. */
2301 htp_config_set_field_limits(cfg_prec->cfg,
2302 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT,
2303 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_HARD);
2304 return;
2305 }
2306
2307 /* hack: htp random range code expects random values in range of 0-RAND_MAX,
2308 * but we can get both <0 and >RAND_MAX values from RandomGet
2309 */
2310 static int RandomGetWrap(void)
2311 {
2312 unsigned long r;
2313
2314 do {
2315 r = RandomGet();
2316 } while(r >= ULONG_MAX - (ULONG_MAX % RAND_MAX));
2317
2318 return r % RAND_MAX;
2319 }
2320
2321 /*
2322 * We have this splitup so that in case double decoding has been enabled
2323 * for query and path, they would be called first on the callback queue,
2324 * before the callback set by Phase2() is called. We need this, since
2325 * the callback in Phase2() generates the normalized uri which utilizes
2326 * the query and path. */
2327 static void HTPConfigSetDefaultsPhase2(const char *name, HTPCfgRec *cfg_prec)
2328 {
2329 /* randomize inspection size if needed */
2330 if (cfg_prec->randomize) {
2331 int rdrange = cfg_prec->randomize_range;
2332
2333 long int r = RandomGetWrap();
2334 cfg_prec->request.inspect_min_size +=
2335 (int) (cfg_prec->request.inspect_min_size *
2336 (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2337
2338 r = RandomGetWrap();
2339 cfg_prec->request.inspect_window +=
2340 (int) (cfg_prec->request.inspect_window *
2341 (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2342 SCLogConfig("'%s' server has 'request-body-minimal-inspect-size' set to"
2343 " %d and 'request-body-inspect-window' set to %d after"
2344 " randomization.",
2345 name,
2346 cfg_prec->request.inspect_min_size,
2347 cfg_prec->request.inspect_window);
2348
2349
2350 r = RandomGetWrap();
2351 cfg_prec->response.inspect_min_size +=
2352 (int) (cfg_prec->response.inspect_min_size *
2353 (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2354
2355 r = RandomGetWrap();
2356 cfg_prec->response.inspect_window +=
2357 (int) (cfg_prec->response.inspect_window *
2358 (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2359
2360 SCLogConfig("'%s' server has 'response-body-minimal-inspect-size' set to"
2361 " %d and 'response-body-inspect-window' set to %d after"
2362 " randomization.",
2363 name,
2364 cfg_prec->response.inspect_min_size,
2365 cfg_prec->response.inspect_window);
2366 }
2367
2368 htp_config_register_request_line(cfg_prec->cfg, HTPCallbackRequestLine);
2369
2370 cfg_prec->request.sbcfg.flags = 0;
2371 cfg_prec->request.sbcfg.buf_size = cfg_prec->request.inspect_window ?
2372 cfg_prec->request.inspect_window : 256;
2373 cfg_prec->request.sbcfg.buf_slide = 0;
2374 cfg_prec->request.sbcfg.Malloc = HTPMalloc;
2375 cfg_prec->request.sbcfg.Calloc = HTPCalloc;
2376 cfg_prec->request.sbcfg.Realloc = HTPRealloc;
2377 cfg_prec->request.sbcfg.Free = HTPFree;
2378
2379 cfg_prec->response.sbcfg.flags = 0;
2380 cfg_prec->response.sbcfg.buf_size = cfg_prec->response.inspect_window ?
2381 cfg_prec->response.inspect_window : 256;
2382 cfg_prec->response.sbcfg.buf_slide = 0;
2383 cfg_prec->response.sbcfg.Malloc = HTPMalloc;
2384 cfg_prec->response.sbcfg.Calloc = HTPCalloc;
2385 cfg_prec->response.sbcfg.Realloc = HTPRealloc;
2386 cfg_prec->response.sbcfg.Free = HTPFree;
2387 return;
2388 }
2389
2390 static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s,
2391 SCRadixTree *tree)
2392 {
2393 if (cfg_prec == NULL || s == NULL || tree == NULL)
2394 return;
2395
2396 ConfNode *p = NULL;
2397
2398 /* Default Parameters */
2399 TAILQ_FOREACH(p, &s->head, next) {
2400
2401 if (strcasecmp("address", p->name) == 0) {
2402 ConfNode *pval;
2403 /* Addresses */
2404 TAILQ_FOREACH(pval, &p->head, next) {
2405 SCLogDebug("LIBHTP server %s: %s=%s", s->name, p->name,
2406 pval->val);
2407
2408 /* IPV6 or IPV4? */
2409 if (strchr(pval->val, ':') != NULL) {
2410 SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
2411 s->name, pval->val, cfg_prec->cfg);
2412 if (SCRadixAddKeyIPV6String(pval->val, tree, cfg_prec) == NULL) {
2413 SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed to "
2414 "add ipv6 server %s, ignoring", pval->val);
2415 }
2416 } else {
2417 SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
2418 s->name, pval->val, cfg_prec->cfg);
2419 if (SCRadixAddKeyIPV4String(pval->val, tree, cfg_prec) == NULL) {
2420 SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed "
2421 "to add ipv4 server %s, ignoring",
2422 pval->val);
2423 }
2424 } /* else - if (strchr(pval->val, ':') != NULL) */
2425 } /* TAILQ_FOREACH(pval, &p->head, next) */
2426
2427 } else if (strcasecmp("personality", p->name) == 0) {
2428 /* Personalities */
2429 int personality = HTPLookupPersonality(p->val);
2430 SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
2431 SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
2432
2433 if (personality >= 0) {
2434 SCLogDebug("LIBHTP default: %s=%s (%d)", p->name, p->val,
2435 personality);
2436 if (htp_config_set_server_personality(cfg_prec->cfg, personality) == HTP_ERROR){
2437 SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP Failed adding "
2438 "personality \"%s\", ignoring", p->val);
2439 } else {
2440 SCLogDebug("LIBHTP personality set to %s",
2441 HTPLookupPersonalityString(personality));
2442 }
2443
2444 /* The IDS personality by default converts the path (and due to
2445 * our query string callback also the query string) to lowercase.
2446 * Signatures do not expect this, so override it. */
2447 htp_config_set_convert_lowercase(cfg_prec->cfg, HTP_DECODER_URL_PATH, 0);
2448 } else {
2449 SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Unknown personality "
2450 "\"%s\", ignoring", p->val);
2451 continue;
2452 }
2453
2454 } else if (strcasecmp("request-body-limit", p->name) == 0 ||
2455 strcasecmp("request_body_limit", p->name) == 0) {
2456 if (ParseSizeStringU32(p->val, &cfg_prec->request.body_limit) < 0) {
2457 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-limit "
2458 "from conf file - %s. Killing engine", p->val);
2459 exit(EXIT_FAILURE);
2460 }
2461
2462 } else if (strcasecmp("response-body-limit", p->name) == 0) {
2463 if (ParseSizeStringU32(p->val, &cfg_prec->response.body_limit) < 0) {
2464 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-limit "
2465 "from conf file - %s. Killing engine", p->val);
2466 exit(EXIT_FAILURE);
2467 }
2468
2469 } else if (strcasecmp("request-body-minimal-inspect-size", p->name) == 0) {
2470 if (ParseSizeStringU32(p->val, &cfg_prec->request.inspect_min_size) < 0) {
2471 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-minimal-inspect-size "
2472 "from conf file - %s. Killing engine", p->val);
2473 exit(EXIT_FAILURE);
2474 }
2475
2476 } else if (strcasecmp("request-body-inspect-window", p->name) == 0) {
2477 if (ParseSizeStringU32(p->val, &cfg_prec->request.inspect_window) < 0) {
2478 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-inspect-window "
2479 "from conf file - %s. Killing engine", p->val);
2480 exit(EXIT_FAILURE);
2481 }
2482
2483 } else if (strcasecmp("double-decode-path", p->name) == 0) {
2484 if (ConfValIsTrue(p->val)) {
2485 htp_config_register_request_line(cfg_prec->cfg,
2486 HTPCallbackDoubleDecodeQuery);
2487 }
2488
2489 } else if (strcasecmp("double-decode-query", p->name) == 0) {
2490 if (ConfValIsTrue(p->val)) {
2491 htp_config_register_request_line(cfg_prec->cfg,
2492 HTPCallbackDoubleDecodePath);
2493 }
2494
2495 } else if (strcasecmp("response-body-minimal-inspect-size", p->name) == 0) {
2496 if (ParseSizeStringU32(p->val, &cfg_prec->response.inspect_min_size) < 0) {
2497 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-minimal-inspect-size "
2498 "from conf file - %s. Killing engine", p->val);
2499 exit(EXIT_FAILURE);
2500 }
2501
2502 } else if (strcasecmp("response-body-inspect-window", p->name) == 0) {
2503 if (ParseSizeStringU32(p->val, &cfg_prec->response.inspect_window) < 0) {
2504 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
2505 "from conf file - %s. Killing engine", p->val);
2506 exit(EXIT_FAILURE);
2507 }
2508
2509 } else if (strcasecmp("response-body-decompress-layer-limit", p->name) == 0) {
2510 uint32_t value = 2;
2511 if (ParseSizeStringU32(p->val, &value) < 0) {
2512 SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
2513 "from conf file - %s. Killing engine", p->val);
2514 exit(EXIT_FAILURE);
2515 }
2516 #ifdef HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT
2517 htp_config_set_response_decompression_layer_limit(cfg_prec->cfg, value);
2518 #else
2519 SCLogWarning(SC_WARN_OUTDATED_LIBHTP, "can't set response-body-decompress-layer-limit "
2520 "to %u, libhtp version too old", value);
2521 #endif
2522 } else if (strcasecmp("path-convert-backslash-separators", p->name) == 0) {
2523 htp_config_set_backslash_convert_slashes(cfg_prec->cfg,
2524 HTP_DECODER_URL_PATH,
2525 ConfValIsTrue(p->val));
2526 } else if (strcasecmp("path-bestfit-replacement-char", p->name) == 0) {
2527 if (strlen(p->val) == 1) {
2528 htp_config_set_bestfit_replacement_byte(cfg_prec->cfg,
2529 HTP_DECODER_URL_PATH,
2530 p->val[0]);
2531 } else {
2532 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
2533 "for libhtp param path-bestfit-replacement-char");
2534 }
2535 } else if (strcasecmp("path-convert-lowercase", p->name) == 0) {
2536 htp_config_set_convert_lowercase(cfg_prec->cfg,
2537 HTP_DECODER_URL_PATH,
2538 ConfValIsTrue(p->val));
2539 } else if (strcasecmp("path-nul-encoded-terminates", p->name) == 0) {
2540 htp_config_set_nul_encoded_terminates(cfg_prec->cfg,
2541 HTP_DECODER_URL_PATH,
2542 ConfValIsTrue(p->val));
2543 } else if (strcasecmp("path-nul-raw-terminates", p->name) == 0) {
2544 htp_config_set_nul_raw_terminates(cfg_prec->cfg,
2545 HTP_DECODER_URL_PATH,
2546 ConfValIsTrue(p->val));
2547 } else if (strcasecmp("path-separators-compress", p->name) == 0) {
2548 htp_config_set_path_separators_compress(cfg_prec->cfg,
2549 HTP_DECODER_URL_PATH,
2550 ConfValIsTrue(p->val));
2551 } else if (strcasecmp("path-separators-decode", p->name) == 0) {
2552 htp_config_set_path_separators_decode(cfg_prec->cfg,
2553 HTP_DECODER_URL_PATH,
2554 ConfValIsTrue(p->val));
2555 } else if (strcasecmp("path-u-encoding-decode", p->name) == 0) {
2556 htp_config_set_u_encoding_decode(cfg_prec->cfg,
2557 HTP_DECODER_URL_PATH,
2558 ConfValIsTrue(p->val));
2559 } else if (strcasecmp("path-url-encoding-invalid-handling", p->name) == 0) {
2560 enum htp_url_encoding_handling_t handling;
2561 if (strcasecmp(p->val, "preserve_percent") == 0) {
2562 handling = HTP_URL_DECODE_PRESERVE_PERCENT;
2563 } else if (strcasecmp(p->val, "remove_percent") == 0) {
2564 handling = HTP_URL_DECODE_REMOVE_PERCENT;
2565 } else if (strcasecmp(p->val, "decode_invalid") == 0) {
2566 handling = HTP_URL_DECODE_PROCESS_INVALID;
2567 } else {
2568 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
2569 "for libhtp param path-url-encoding-invalid-handling");
2570 return;
2571 }
2572 htp_config_set_url_encoding_invalid_handling(cfg_prec->cfg,
2573 HTP_DECODER_URL_PATH,
2574 handling);
2575 } else if (strcasecmp("path-utf8-convert-bestfit", p->name) == 0) {
2576 htp_config_set_utf8_convert_bestfit(cfg_prec->cfg,
2577 HTP_DECODER_URL_PATH,
2578 ConfValIsTrue(p->val));
2579 } else if (strcasecmp("uri-include-all", p->name) == 0) {
2580 cfg_prec->uri_include_all = ConfValIsTrue(p->val);
2581 SCLogDebug("uri-include-all %s",
2582 cfg_prec->uri_include_all ? "enabled" : "disabled");
2583 } else if (strcasecmp("query-plusspace-decode", p->name) == 0) {
2584 htp_config_set_plusspace_decode(cfg_prec->cfg,
2585 HTP_DECODER_URLENCODED,
2586 ConfValIsTrue(p->val));
2587 } else if (strcasecmp("meta-field-limit", p->name) == 0) {
2588 uint32_t limit = 0;
2589 if (ParseSizeStringU32(p->val, &limit) < 0) {
2590 SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
2591 "from conf file - %s. Killing engine", p->val);
2592 exit(EXIT_FAILURE);
2593 }
2594 if (limit == 0) {
2595 SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
2596 "from conf file cannot be 0. Killing engine");
2597 exit(EXIT_FAILURE);
2598 }
2599 /* set default soft-limit with our new hard limit */
2600 htp_config_set_field_limits(cfg_prec->cfg,
2601 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT,
2602 (size_t)limit);
2603 } else if (strcasecmp("randomize-inspection-sizes", p->name) == 0) {
2604 if (!g_disable_randomness) {
2605 cfg_prec->randomize = ConfValIsTrue(p->val);
2606 }
2607 } else if (strcasecmp("randomize-inspection-range", p->name) == 0) {
2608 uint32_t range = atoi(p->val);
2609 if (range > 100) {
2610 SCLogError(SC_ERR_SIZE_PARSE, "Invalid value for randomize"
2611 " inspection range setting from conf file - %s."
2612 " It should be inferior to 100."
2613 " Killing engine",
2614 p->val);
2615 exit(EXIT_FAILURE);
2616 }
2617 cfg_prec->randomize_range = range;
2618 } else if (strcasecmp("http-body-inline", p->name) == 0) {
2619 if (ConfValIsTrue(p->val)) {
2620 cfg_prec->http_body_inline = 1;
2621 } else if (ConfValIsFalse(p->val)) {
2622 cfg_prec->http_body_inline = 0;
2623 } else {
2624 if (strcmp("auto", p->val) != 0) {
2625 WarnInvalidConfEntry("http_body_inline", "%s", "auto");
2626 }
2627 if (EngineModeIsIPS()) {
2628 cfg_prec->http_body_inline = 1;
2629 } else {
2630 cfg_prec->http_body_inline = 0;
2631 }
2632 }
2633 } else if (strcasecmp("swf-decompression", p->name) == 0) {
2634 ConfNode *pval;
2635
2636 TAILQ_FOREACH(pval, &p->head, next) {
2637 if (strcasecmp("enabled", pval->name) == 0) {
2638 if (ConfValIsTrue(pval->val)) {
2639 cfg_prec->swf_decompression_enabled = 1;
2640 } else if (ConfValIsFalse(pval->val)) {
2641 cfg_prec->swf_decompression_enabled = 0;
2642 } else {
2643 WarnInvalidConfEntry("swf-decompression.enabled", "%s", "no");
2644 }
2645 } else if (strcasecmp("type", pval->name) == 0) {
2646 if (strcasecmp("no", pval->val) == 0) {
2647 cfg_prec->swf_compression_type = HTTP_SWF_COMPRESSION_NONE;
2648 } else if (strcasecmp("deflate", pval->val) == 0) {
2649 cfg_prec->swf_compression_type = HTTP_SWF_COMPRESSION_ZLIB;
2650 } else if (strcasecmp("lzma", pval->val) == 0) {
2651 cfg_prec->swf_compression_type = HTTP_SWF_COMPRESSION_LZMA;
2652 } else if (strcasecmp("both", pval->val) == 0) {
2653 cfg_prec->swf_compression_type = HTTP_SWF_COMPRESSION_BOTH;
2654 } else {
2655 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
2656 "Invalid entry for "
2657 "swf-decompression.type: %s - "
2658 "Killing engine", pval->val);
2659 exit(EXIT_FAILURE);
2660 }
2661 } else if (strcasecmp("compress-depth", pval->name) == 0) {
2662 if (ParseSizeStringU32(pval->val, &cfg_prec->swf_compress_depth) < 0) {
2663 SCLogError(SC_ERR_SIZE_PARSE,
2664 "Error parsing swf-decompression.compression-depth "
2665 "from conf file - %s. Killing engine", p->val);
2666 exit(EXIT_FAILURE);
2667 }
2668 } else if (strcasecmp("decompress-depth", pval->name) == 0) {
2669 if (ParseSizeStringU32(pval->val, &cfg_prec->swf_decompress_depth) < 0) {
2670 SCLogError(SC_ERR_SIZE_PARSE,
2671 "Error parsing swf-decompression.decompression-depth "
2672 "from conf file - %s. Killing engine", p->val);
2673 exit(EXIT_FAILURE);
2674 }
2675 } else {
2676 SCLogWarning(SC_ERR_UNKNOWN_VALUE, "Ignoring unknown param %s", pval->name);
2677 }
2678 }
2679 } else {
2680 SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Ignoring unknown "
2681 "default config: %s", p->name);
2682 }
2683 } /* TAILQ_FOREACH(p, &default_config->head, next) */
2684
2685 return;
2686 }
2687
2688 void HTPConfigure(void)
2689 {
2690 SCEnter();
2691
2692 cfglist.next = NULL;
2693
2694 cfgtree = SCRadixCreateRadixTree(NULL, NULL);
2695 if (NULL == cfgtree)
2696 exit(EXIT_FAILURE);
2697
2698 /* Default Config */
2699 cfglist.cfg = htp_config_create();
2700 if (NULL == cfglist.cfg) {
2701 SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP default config");
2702 exit(EXIT_FAILURE);
2703 }
2704 SCLogDebug("LIBHTP default config: %p", cfglist.cfg);
2705 HTPConfigSetDefaultsPhase1(&cfglist);
2706 if (ConfGetNode("app-layer.protocols.http.libhtp") == NULL) {
2707 HTPConfigParseParameters(&cfglist, ConfGetNode("libhtp.default-config"),
2708 cfgtree);
2709 } else {
2710 HTPConfigParseParameters(&cfglist, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree);
2711 }
2712 HTPConfigSetDefaultsPhase2("default", &cfglist);
2713
2714 HTPParseMemcap();
2715
2716 /* Read server config and create a parser for each IP in radix tree */
2717 ConfNode *server_config = ConfGetNode("app-layer.protocols.http.libhtp.server-config");
2718 if (server_config == NULL) {
2719 server_config = ConfGetNode("libhtp.server-config");
2720 if (server_config == NULL) {
2721 SCLogDebug("LIBHTP Configuring %p", server_config);
2722 SCReturn;
2723 }
2724 }
2725 SCLogDebug("LIBHTP Configuring %p", server_config);
2726
2727 ConfNode *si;
2728 /* Server Nodes */
2729 TAILQ_FOREACH(si, &server_config->head, next) {
2730 /* Need the named node, not the index */
2731 ConfNode *s = TAILQ_FIRST(&si->head);
2732 if (NULL == s) {
2733 SCLogDebug("LIBHTP s NULL");
2734 continue;
2735 }
2736
2737 SCLogDebug("LIBHTP server %s", s->name);
2738
2739 HTPCfgRec *nextrec = cfglist.next;
2740 HTPCfgRec *htprec = SCMalloc(sizeof(HTPCfgRec));
2741 if (NULL == htprec)
2742 exit(EXIT_FAILURE);
2743 memset(htprec, 0x00, sizeof(*htprec));
2744
2745 cfglist.next = htprec;
2746
2747 cfglist.next->next = nextrec;
2748 cfglist.next->cfg = htp_config_create();
2749 if (NULL == cfglist.next->cfg) {
2750 SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP server config");
2751 exit(EXIT_FAILURE);
2752 }
2753
2754 HTPConfigSetDefaultsPhase1(htprec);
2755 HTPConfigParseParameters(htprec, s, cfgtree);
2756 HTPConfigSetDefaultsPhase2(s->name, htprec);
2757 }
2758
2759 SCReturn;
2760 }
2761
2762 void AppLayerHtpPrintStats(void)
2763 {
2764 #ifdef DEBUG
2765 SCMutexLock(&htp_state_mem_lock);
2766 SCLogPerf("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
2767 SCMutexUnlock(&htp_state_mem_lock);
2768 #endif
2769 }
2770
2771 /** \internal
2772 * \brief get files callback
2773 * \param state state ptr
2774 * \param direction flow direction
2775 * \retval files files ptr
2776 */
2777 static FileContainer *HTPStateGetFiles(void *state, uint8_t direction)
2778 {
2779 if (state == NULL)
2780 return NULL;
2781
2782 HtpState *http_state = (HtpState *)state;
2783
2784 if (direction & STREAM_TOCLIENT) {
2785 SCReturnPtr(http_state->files_tc, "FileContainer");
2786 } else {
2787 SCReturnPtr(http_state->files_ts, "FileContainer");
2788 }
2789 }
2790
2791 static int HTPStateGetAlstateProgress(void *tx, uint8_t direction)
2792 {
2793 if (direction & STREAM_TOSERVER)
2794 return ((htp_tx_t *)tx)->request_progress;
2795 else
2796 return ((htp_tx_t *)tx)->response_progress;
2797 }
2798
2799 static uint64_t HTPStateGetTxCnt(void *alstate)
2800 {
2801 HtpState *http_state = (HtpState *)alstate;
2802
2803 if (http_state != NULL && http_state->conn != NULL) {
2804 const uint64_t size = (uint64_t)htp_list_size(http_state->conn->transactions);
2805 SCLogDebug("size %"PRIu64, size);
2806 return size;
2807 } else {
2808 return 0ULL;
2809 }
2810 }
2811
2812 static void *HTPStateGetTx(void *alstate, uint64_t tx_id)
2813 {
2814 HtpState *http_state = (HtpState *)alstate;
2815
2816 if (http_state != NULL && http_state->conn != NULL)
2817 return htp_list_get(http_state->conn->transactions, tx_id);
2818 else
2819 return NULL;
2820 }
2821
2822 static void HTPStateSetTxLogged(void *alstate, void *vtx, LoggerId bits)
2823 {
2824 htp_tx_t *tx = (htp_tx_t *)vtx;
2825 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2826 if (tx_ud)
2827 tx_ud->logged = bits;
2828 }
2829
2830 static LoggerId HTPStateGetTxLogged(void *alstate, void *vtx)
2831 {
2832 htp_tx_t *tx = (htp_tx_t *)vtx;
2833 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2834 if (tx_ud != NULL)
2835 return tx_ud->logged;
2836
2837 return 0;
2838 }
2839
2840 static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction)
2841 {
2842 return (direction & STREAM_TOSERVER) ? HTP_REQUEST_COMPLETE : HTP_RESPONSE_COMPLETE;
2843 }
2844
2845 static int HTPStateGetEventInfo(const char *event_name,
2846 int *event_id, AppLayerEventType *event_type)
2847 {
2848 *event_id = SCMapEnumNameToValue(event_name, http_decoder_event_table);
2849 if (*event_id == -1) {
2850 SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
2851 "http's enum map table.", event_name);
2852 /* this should be treated as fatal */
2853 return -1;
2854 }
2855
2856 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
2857
2858 return 0;
2859 }
2860
2861 static void HTPStateTruncate(void *state, uint8_t direction)
2862 {
2863 FileContainer *fc = HTPStateGetFiles(state, direction);
2864 if (fc != NULL) {
2865 FileTruncateAllOpenFiles(fc);
2866 }
2867 }
2868
2869 static DetectEngineState *HTPGetTxDetectState(void *vtx)
2870 {
2871 htp_tx_t *tx = (htp_tx_t *)vtx;
2872 HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2873 return tx_ud ? tx_ud->de_state : NULL;
2874 }
2875
2876 static int HTPSetTxDetectState(void *vtx, DetectEngineState *s)
2877 {
2878 htp_tx_t *tx = (htp_tx_t *)vtx;
2879 HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2880 if (tx_ud == NULL) {
2881 tx_ud = HTPMalloc(sizeof(*tx_ud));
2882 if (unlikely(tx_ud == NULL))
2883 return -ENOMEM;
2884 memset(tx_ud, 0, sizeof(*tx_ud));
2885 htp_tx_set_user_data(tx, tx_ud);
2886 }
2887 tx_ud->de_state = s;
2888 return 0;
2889 }
2890
2891 static uint64_t HTPGetTxDetectFlags(void *vtx, uint8_t dir)
2892 {
2893 htp_tx_t *tx = (htp_tx_t *)vtx;
2894 HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2895 if (tx_ud) {
2896 if (dir & STREAM_TOSERVER) {
2897 return tx_ud->detect_flags_ts;
2898 } else {
2899 return tx_ud->detect_flags_tc;
2900 }
2901 }
2902 return 0;
2903 }
2904
2905 static void HTPSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t detect_flags)
2906 {
2907 htp_tx_t *tx = (htp_tx_t *)vtx;
2908 HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2909 if (tx_ud == NULL) {
2910 tx_ud = HTPMalloc(sizeof(*tx_ud));
2911 if (unlikely(tx_ud == NULL))
2912 return;
2913 memset(tx_ud, 0, sizeof(*tx_ud));
2914 htp_tx_set_user_data(tx, tx_ud);
2915 }
2916 if (dir & STREAM_TOSERVER) {
2917 tx_ud->detect_flags_ts = detect_flags;
2918 } else {
2919 tx_ud->detect_flags_tc = detect_flags;
2920 }
2921 return;
2922 }
2923
2924 static int HTPRegisterPatternsForProtocolDetection(void)
2925 {
2926 const char *methods[] = { "GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS",
2927 "CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL",
2928 "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN",
2929 "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE",
2930 "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL", NULL};
2931 const char *spacings[] = { "|20|", "|09|", NULL };
2932 const char *versions[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL };
2933
2934 int methods_pos;
2935 int spacings_pos;
2936 int versions_pos;
2937 int register_result;
2938 char method_buffer[32] = "";
2939
2940 /* Loop through all the methods ands spacings and register the patterns */
2941 for (methods_pos = 0; methods[methods_pos]; methods_pos++) {
2942 for (spacings_pos = 0; spacings[spacings_pos]; spacings_pos++) {
2943
2944 /* Combine the method name and the spacing */
2945 snprintf(method_buffer, sizeof(method_buffer), "%s%s", methods[methods_pos], spacings[spacings_pos]);
2946
2947 /* Register the new method+spacing pattern
2948 * 3 is subtracted from the length since the spacing is hex typed as |xx|
2949 * but the pattern matching should only be one char
2950 */
2951 register_result = AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP,
2952 ALPROTO_HTTP, method_buffer, strlen(method_buffer)-3, 0, STREAM_TOSERVER);
2953 if (register_result < 0) {
2954 return -1;
2955 }
2956 }
2957 }
2958
2959 /* Loop through all the http verions patterns that are TO_CLIENT */
2960 for (versions_pos = 0; versions[versions_pos]; versions_pos++) {
2961 register_result = AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP,
2962 ALPROTO_HTTP, versions[versions_pos], strlen(versions[versions_pos]),
2963 0, STREAM_TOCLIENT);
2964 if (register_result < 0) {
2965 return -1;
2966 }
2967 }
2968
2969 return 0;
2970 }
2971
2972 /**
2973 * \brief Register the HTTP protocol and state handling functions to APP layer
2974 * of the engine.
2975 */
2976 void RegisterHTPParsers(void)
2977 {
2978 SCEnter();
2979
2980 const char *proto_name = "http";
2981
2982 /** HTTP */
2983 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
2984 AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP, proto_name);
2985 if (HTPRegisterPatternsForProtocolDetection() < 0)
2986 return;
2987 } else {
2988 SCLogInfo("Protocol detection and parser disabled for %s protocol",
2989 proto_name);
2990 return;
2991 }
2992
2993 if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
2994 AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
2995 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTransactionFree);
2996 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetFiles);
2997 AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetAlstateProgress);
2998 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTxCnt);
2999 AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTx);
3000 AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTxLogged,
3001 HTPStateSetTxLogged);
3002 AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_HTTP,
3003 HTPStateGetAlstateProgressCompletionStatus);
3004 AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPGetEvents);
3005 AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetEventInfo);
3006
3007 AppLayerParserRegisterTruncateFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTruncate);
3008 AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_HTTP,
3009 HTPGetTxDetectState, HTPSetTxDetectState);
3010 AppLayerParserRegisterDetectFlagsFuncs(IPPROTO_TCP, ALPROTO_HTTP,
3011 HTPGetTxDetectFlags, HTPSetTxDetectFlags);
3012
3013 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_HTTP, STREAM_TOSERVER,
3014 HTPHandleRequestData);
3015 AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_HTTP, STREAM_TOCLIENT,
3016 HTPHandleResponseData);
3017 SC_ATOMIC_INIT(htp_config_flags);
3018 AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP,
3019 ALPROTO_HTTP, STREAM_TOSERVER|STREAM_TOCLIENT);
3020 HTPConfigure();
3021 } else {
3022 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
3023 "still on.", proto_name);
3024 }
3025 #ifdef UNITTESTS
3026 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_HTTP, HTPParserRegisterTests);
3027 #endif
3028
3029 SCReturn;
3030 }
3031
3032 #ifdef UNITTESTS
3033 static HTPCfgRec cfglist_backup;
3034
3035 void HtpConfigCreateBackup(void)
3036 {
3037 cfglist_backup = cfglist;
3038
3039 return;
3040 }
3041
3042 void HtpConfigRestoreBackup(void)
3043 {
3044 cfglist = cfglist_backup;
3045
3046 return;
3047 }
3048
3049 /** \test Test case where chunks are sent in smaller chunks and check the
3050 * response of the parser from HTP library. */
3051 static int HTPParserTest01(void)
3052 {
3053 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3054 " Data is c0oL!";
3055 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3056
3057 TcpSession ssn;
3058 memset(&ssn, 0, sizeof(ssn));
3059
3060 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3061 FAIL_IF_NULL(alp_tctx);
3062
3063 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3064 FAIL_IF_NULL(f);
3065 f->protoctx = &ssn;
3066 f->proto = IPPROTO_TCP;
3067 f->alproto = ALPROTO_HTTP;
3068
3069 StreamTcpInitConfig(TRUE);
3070
3071 uint32_t u;
3072 for (u = 0; u < httplen1; u++) {
3073 uint8_t flags = 0;
3074
3075 if (u == 0)
3076 flags = STREAM_TOSERVER|STREAM_START;
3077 else if (u == (httplen1 - 1))
3078 flags = STREAM_TOSERVER|STREAM_EOF;
3079 else
3080 flags = STREAM_TOSERVER;
3081
3082 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3083 &httpbuf1[u], 1);
3084 FAIL_IF(r != 0);
3085 }
3086
3087 HtpState *htp_state = f->alstate;
3088 FAIL_IF_NULL(htp_state);
3089
3090 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3091 FAIL_IF_NULL(tx);
3092
3093 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3094 FAIL_IF_NULL(h);
3095
3096 FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3097 FAIL_IF(tx->request_method_number != HTP_M_POST);
3098 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3099
3100 AppLayerParserThreadCtxFree(alp_tctx);
3101 StreamTcpFreeConfig(TRUE);
3102 UTHFreeFlow(f);
3103 PASS;
3104 }
3105
3106 /** \test Test folding in 1 read case */
3107 static int HTPParserTest01b(void)
3108 {
3109 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3110 " Data is c0oL!";
3111 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3112
3113 TcpSession ssn;
3114 memset(&ssn, 0, sizeof(ssn));
3115
3116 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3117 FAIL_IF_NULL(alp_tctx);
3118
3119 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3120 FAIL_IF_NULL(f);
3121 f->protoctx = &ssn;
3122 f->proto = IPPROTO_TCP;
3123 f->alproto = ALPROTO_HTTP;
3124
3125 StreamTcpInitConfig(TRUE);
3126
3127 uint8_t flags =STREAM_TOSERVER|STREAM_START|STREAM_EOF;
3128 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3129 httpbuf1, httplen1);
3130 FAIL_IF(r != 0);
3131
3132 HtpState *htp_state = f->alstate;
3133 FAIL_IF_NULL(htp_state);
3134
3135 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3136 FAIL_IF_NULL(tx);
3137
3138 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3139 FAIL_IF_NULL(h);
3140
3141 FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3142 FAIL_IF(tx->request_method_number != HTP_M_POST);
3143 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3144
3145 AppLayerParserThreadCtxFree(alp_tctx);
3146 StreamTcpFreeConfig(TRUE);
3147 UTHFreeFlow(f);
3148 PASS;
3149 }
3150
3151 /** \test Test folding in 1byte per read case */
3152 static int HTPParserTest01c(void)
3153 {
3154 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3155 " Data is c0oL!";
3156 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3157
3158 TcpSession ssn;
3159 memset(&ssn, 0, sizeof(ssn));
3160
3161 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3162 FAIL_IF_NULL(alp_tctx);
3163
3164 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3165 FAIL_IF_NULL(f);
3166 f->protoctx = &ssn;
3167 f->proto = IPPROTO_TCP;
3168 f->alproto = ALPROTO_HTTP;
3169
3170 StreamTcpInitConfig(TRUE);
3171
3172 uint32_t u;
3173 for (u = 0; u < httplen1; u++) {
3174 uint8_t flags = 0;
3175
3176 if (u == 0)
3177 flags = STREAM_TOSERVER|STREAM_START;
3178 else if (u == (httplen1 - 1))
3179 flags = STREAM_TOSERVER|STREAM_EOF;
3180 else
3181 flags = STREAM_TOSERVER;
3182
3183 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3184 &httpbuf1[u], 1);
3185 FAIL_IF(r != 0);
3186 }
3187
3188 HtpState *htp_state = f->alstate;
3189 FAIL_IF_NULL(htp_state);
3190
3191 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3192 FAIL_IF_NULL(tx);
3193
3194 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3195 FAIL_IF_NULL(h);
3196
3197 FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3198 FAIL_IF(tx->request_method_number != HTP_M_POST);
3199 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3200
3201 AppLayerParserThreadCtxFree(alp_tctx);
3202 StreamTcpFreeConfig(TRUE);
3203 UTHFreeFlow(f);
3204 PASS;
3205 }
3206
3207 /** \test Test case where chunks are sent in smaller chunks and check the
3208 * response of the parser from HTP library. */
3209 static int HTPParserTest01a(void)
3210 {
3211 int result = 0;
3212 Flow *f = NULL;
3213 uint8_t httpbuf1[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3214 " Data is c0oL!";
3215 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3216 TcpSession ssn;
3217 HtpState *htp_state = NULL;
3218 int r = 0;
3219 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3220
3221 memset(&ssn, 0, sizeof(ssn));
3222
3223 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3224 if (f == NULL)
3225 goto end;
3226 f->protoctx = &ssn;
3227 f->proto = IPPROTO_TCP;
3228 f->alproto = ALPROTO_HTTP;
3229
3230 StreamTcpInitConfig(TRUE);
3231
3232 uint32_t u;
3233 for (u = 0; u < httplen1; u++) {
3234 uint8_t flags = 0;
3235
3236 if (u == 0)
3237 flags = STREAM_TOSERVER|STREAM_START;
3238 else if (u == (httplen1 - 1))
3239 flags = STREAM_TOSERVER|STREAM_EOF;
3240 else
3241 flags = STREAM_TOSERVER;
3242
3243 FLOWLOCK_WRLOCK(f);
3244 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3245 &httpbuf1[u], 1);
3246 if (r != 0) {
3247 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3248 " 0: ", u, r);
3249 FLOWLOCK_UNLOCK(f);
3250 goto end;
3251 }
3252 FLOWLOCK_UNLOCK(f);
3253 }
3254
3255 htp_state = f->alstate;
3256 if (htp_state == NULL) {
3257 printf("no http state: ");
3258 goto end;
3259 }
3260
3261 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3262 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3263 if (strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0")
3264 || tx->request_method_number != HTP_M_POST ||
3265 tx->request_protocol_number != HTP_PROTOCOL_1_0)
3266 {
3267 printf("expected header value: Victor/1.0 and got %s: and expected"
3268 " method: POST and got %s, expected protocol number HTTP/1.0"
3269 " and got: %s \n", bstr_util_strdup_to_c(h->value),
3270 bstr_util_strdup_to_c(tx->request_method),
3271 bstr_util_strdup_to_c(tx->request_protocol));
3272 goto end;
3273 }
3274 result = 1;
3275 end:
3276 if (alp_tctx != NULL)
3277 AppLayerParserThreadCtxFree(alp_tctx);
3278 StreamTcpFreeConfig(TRUE);
3279 UTHFreeFlow(f);
3280 return result;
3281 }
3282
3283 /** \test See how it deals with an incomplete request. */
3284 static int HTPParserTest02(void)
3285 {
3286 int result = 0;
3287 Flow *f = NULL;
3288 uint8_t httpbuf1[] = "POST";
3289 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3290 TcpSession ssn;
3291 HtpState *http_state = NULL;
3292 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3293
3294 memset(&ssn, 0, sizeof(ssn));
3295
3296 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3297 if (f == NULL)
3298 goto end;
3299 f->protoctx = &ssn;
3300 f->proto = IPPROTO_TCP;
3301 f->alproto = ALPROTO_HTTP;
3302
3303 StreamTcpInitConfig(TRUE);
3304
3305 FLOWLOCK_WRLOCK(f);
3306 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3307 STREAM_TOSERVER | STREAM_START | STREAM_EOF,
3308 httpbuf1,
3309 httplen1);
3310 if (r != 0) {
3311 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
3312 FLOWLOCK_UNLOCK(f);
3313 goto end;
3314 }
3315 FLOWLOCK_UNLOCK(f);
3316
3317 http_state = f->alstate;
3318 if (http_state == NULL) {
3319 printf("no http state: ");
3320 goto end;
3321 }
3322
3323 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3324 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3325 if ((tx->request_method) != NULL || h != NULL)
3326 {
3327 printf("expected method NULL, got %s \n", bstr_util_strdup_to_c(tx->request_method));
3328 goto end;
3329 }
3330 result = 1;
3331 end:
3332 if (alp_tctx != NULL)
3333 AppLayerParserThreadCtxFree(alp_tctx);
3334 StreamTcpFreeConfig(TRUE);
3335 UTHFreeFlow(f);
3336 return result;
3337 }
3338
3339 /** \test Test case where method is invalid and data is sent in smaller chunks
3340 * and check the response of the parser from HTP library. */
3341 static int HTPParserTest03(void)
3342 {
3343 int result = 0;
3344 Flow *f = NULL;
3345 uint8_t httpbuf1[] = "HELLO / HTTP/1.0\r\n";
3346 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3347 TcpSession ssn;
3348 HtpState *htp_state = NULL;
3349 int r = 0;
3350 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3351
3352 memset(&ssn, 0, sizeof(ssn));
3353
3354 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3355 if (f == NULL)
3356 goto end;
3357 f->protoctx = &ssn;
3358 f->proto = IPPROTO_TCP;
3359 f->alproto = ALPROTO_HTTP;
3360
3361 StreamTcpInitConfig(TRUE);
3362
3363 uint32_t u;
3364 for (u = 0; u < httplen1; u++) {
3365 uint8_t flags = 0;
3366
3367 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
3368 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
3369 else flags = STREAM_TOSERVER;
3370
3371 FLOWLOCK_WRLOCK(f);
3372 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3373 &httpbuf1[u], 1);
3374 if (r != 0) {
3375 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3376 " 0: ", u, r);
3377 FLOWLOCK_UNLOCK(f);
3378 goto end;
3379 }
3380 FLOWLOCK_UNLOCK(f);
3381 }
3382 htp_state = f->alstate;
3383 if (htp_state == NULL) {
3384 printf("no http state: ");
3385 goto end;
3386 }
3387
3388 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3389
3390 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3391 if (tx->request_method_number != HTP_M_UNKNOWN ||
3392 h != NULL || tx->request_protocol_number != HTP_PROTOCOL_1_0)
3393 {
3394 printf("expected method M_UNKNOWN and got %s: , expected protocol "
3395 "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
3396 bstr_util_strdup_to_c(tx->request_protocol));
3397 goto end;
3398 }
3399 result = 1;
3400 end:
3401 if (alp_tctx != NULL)
3402 AppLayerParserThreadCtxFree(alp_tctx);
3403 StreamTcpFreeConfig(TRUE);
3404 UTHFreeFlow(f);
3405 return result;
3406 }
3407
3408 /** \test Test case where invalid data is sent and check the response of the
3409 * parser from HTP library. */
3410 static int HTPParserTest04(void)
3411 {
3412 int result = 0;
3413 Flow *f = NULL;
3414 HtpState *htp_state = NULL;
3415 uint8_t httpbuf1[] = "World!\r\n";
3416 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3417 TcpSession ssn;
3418 int r = 0;
3419 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3420
3421 memset(&ssn, 0, sizeof(ssn));
3422
3423 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3424 if (f == NULL)
3425 goto end;
3426 f->protoctx = &ssn;
3427 f->proto = IPPROTO_TCP;
3428 f->alproto = ALPROTO_HTTP;
3429
3430 StreamTcpInitConfig(TRUE);
3431
3432 FLOWLOCK_WRLOCK(f);
3433 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3434 STREAM_TOSERVER | STREAM_START | STREAM_EOF,
3435 httpbuf1,
3436 httplen1);
3437 if (r != 0) {
3438 FLOWLOCK_UNLOCK(f);
3439 goto end;
3440 }
3441 FLOWLOCK_UNLOCK(f);
3442
3443 htp_state = f->alstate;
3444 if (htp_state == NULL) {
3445 printf("no http state: ");
3446 goto end;
3447 }
3448
3449 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3450 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3451 if (tx->request_method_number != HTP_M_UNKNOWN ||
3452 h != NULL || tx->request_protocol_number != HTP_PROTOCOL_0_9)
3453 {
3454 printf("expected method M_UNKNOWN and got %s: , expected protocol "
3455 "NULL and got %s \n", bstr_util_strdup_to_c(tx->request_method),
3456 bstr_util_strdup_to_c(tx->request_protocol));
3457 goto end;
3458 }
3459 result = 1;
3460 end:
3461 if (alp_tctx != NULL)
3462 AppLayerParserThreadCtxFree(alp_tctx);
3463 StreamTcpFreeConfig(TRUE);
3464 UTHFreeFlow(f);
3465 return result;
3466 }
3467
3468 /** \test Test both sides of a http stream mixed up to see if the HTP parser
3469 * properly parsed them and also keeps them separated. */
3470 static int HTPParserTest05(void)
3471 {
3472 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\nContent-Length: 17\r\n\r\n";
3473 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3474 uint8_t httpbuf2[] = "Post D";
3475 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
3476 uint8_t httpbuf3[] = "ata is c0oL!";
3477 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
3478
3479 uint8_t httpbuf4[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
3480 uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
3481 uint8_t httpbuf5[] = "post R";
3482 uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
3483 uint8_t httpbuf6[] = "esults are tha bomb!";
3484 uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
3485
3486 TcpSession ssn;
3487 memset(&ssn, 0, sizeof(ssn));
3488
3489 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3490 FAIL_IF_NULL(alp_tctx);
3491
3492 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3493 FAIL_IF_NULL(f);
3494 f->protoctx = &ssn;
3495 f->proto = IPPROTO_TCP;
3496 f->alproto = ALPROTO_HTTP;
3497
3498 StreamTcpInitConfig(TRUE);
3499
3500 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3501 STREAM_TOSERVER | STREAM_START, httpbuf1,
3502 httplen1);
3503 FAIL_IF(r != 0);
3504
3505 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3506 STREAM_TOCLIENT | STREAM_START, httpbuf4,
3507 httplen4);
3508 FAIL_IF(r != 0);
3509
3510 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT,
3511 httpbuf5, httplen5);
3512 FAIL_IF(r != 0);
3513
3514 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
3515 httpbuf2, httplen2);
3516 FAIL_IF(r != 0);
3517
3518 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3519 STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
3520 FAIL_IF(r != 0);
3521
3522 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3523 STREAM_TOCLIENT | STREAM_EOF, httpbuf6, httplen6);
3524 FAIL_IF(r != 0);
3525
3526 HtpState *http_state = f->alstate;
3527 FAIL_IF_NULL(http_state);
3528
3529 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3530 FAIL_IF_NULL(tx);
3531 FAIL_IF_NOT(tx->request_method_number == HTP_M_POST);
3532 FAIL_IF_NOT(tx->request_protocol_number == HTP_PROTOCOL_1_0);
3533
3534 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3535 FAIL_IF_NULL(h);
3536
3537 FAIL_IF_NOT(tx->response_status_number == 200);
3538
3539 AppLayerParserThreadCtxFree(alp_tctx);
3540 StreamTcpFreeConfig(TRUE);
3541 UTHFreeFlow(f);
3542 PASS;
3543 }
3544
3545 /** \test Test proper chunked encoded response body
3546 */
3547 static int HTPParserTest06(void)
3548 {
3549 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
3550 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
3551 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
3552 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3553 uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
3554 "GMT\r\n"
3555 "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
3556 "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
3557 "FrontPage/5.0.2.2510\r\n"
3558 "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
3559 "chunked\r\n"
3560 "Content-Type: text/html\r\n\r\n"
3561 "580\r\n"
3562 "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
3563 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
3564 "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
3565 "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
3566 "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
3567 "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
3568 "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
3569 "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
3570 "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
3571 "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
3572 "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
3573 "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
3574 "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
3575 "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
3576 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
3577 "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
3578 "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
3579 "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
3580 "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
3581 "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
3582 "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
3583 "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
3584 "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
3585 "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
3586 "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
3587 "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
3588 "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
3589 "aHA=\r\n0\r\n\r\n";
3590 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
3591 TcpSession ssn;
3592
3593 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3594 FAIL_IF_NULL(alp_tctx);
3595
3596 memset(&ssn, 0, sizeof(ssn));
3597
3598 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3599 FAIL_IF_NULL(f);
3600 f->protoctx = &ssn;
3601 f->proto = IPPROTO_TCP;
3602 f->alproto = ALPROTO_HTTP;
3603
3604 StreamTcpInitConfig(TRUE);
3605
3606 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3607 STREAM_TOSERVER | STREAM_START, httpbuf1,
3608 httplen1);
3609 FAIL_IF(r != 0);
3610 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3611 STREAM_TOCLIENT | STREAM_START, httpbuf2,
3612 httplen2);
3613 FAIL_IF(r != 0);
3614
3615 HtpState *http_state = f->alstate;
3616 FAIL_IF_NULL(http_state);
3617
3618 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3619 FAIL_IF_NULL(tx);
3620
3621 FAIL_IF(tx->request_method_number != HTP_M_GET);
3622 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
3623
3624 FAIL_IF(tx->response_status_number != 200);
3625 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
3626
3627 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3628 FAIL_IF_NULL(h);
3629
3630 AppLayerParserThreadCtxFree(alp_tctx);
3631 StreamTcpFreeConfig(TRUE);
3632 UTHFreeFlow(f);
3633 PASS;
3634 }
3635
3636 /** \test
3637 */
3638 static int HTPParserTest07(void)
3639 {
3640 int result = 0;
3641 Flow *f = NULL;
3642 uint8_t httpbuf1[] = "GET /awstats.pl?/migratemigrate%20=%20| HTTP/1.0\r\n\r\n";
3643 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3644 TcpSession ssn;
3645 HtpState *htp_state = NULL;
3646 int r = 0;
3647 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3648
3649 memset(&ssn, 0, sizeof(ssn));
3650
3651 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3652 if (f == NULL)
3653 goto end;
3654 f->protoctx = &ssn;
3655 f->proto = IPPROTO_TCP;
3656 f->alproto = ALPROTO_HTTP;
3657
3658 StreamTcpInitConfig(TRUE);
3659
3660 uint32_t u;
3661 for (u = 0; u < httplen1; u++) {
3662 uint8_t flags = 0;
3663
3664 if (u == 0)
3665 flags = STREAM_TOSERVER|STREAM_START;
3666 else if (u == (httplen1 - 1))
3667 flags = STREAM_TOSERVER|STREAM_EOF;
3668 else
3669 flags = STREAM_TOSERVER;
3670
3671 FLOWLOCK_WRLOCK(f);
3672 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3673 &httpbuf1[u], 1);
3674 if (r != 0) {
3675 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3676 " 0: ", u, r);
3677 FLOWLOCK_UNLOCK(f);
3678 goto end;
3679 }
3680 FLOWLOCK_UNLOCK(f);
3681 }
3682
3683 htp_state = f->alstate;
3684 if (htp_state == NULL) {
3685 printf("no http state: ");
3686 goto end;
3687 }
3688
3689 uint8_t ref[] = "/awstats.pl?/migratemigrate = |";
3690 size_t reflen = sizeof(ref) - 1;
3691
3692 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3693 if (tx == NULL)
3694 goto end;
3695 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3696 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3697 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
3698 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
3699 (uintmax_t)reflen,
3700 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
3701 goto end;
3702 }
3703
3704 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref,
3705 bstr_len(tx_ud->request_uri_normalized)) != 0)
3706 {
3707 printf("normalized uri \"");
3708 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
3709 printf("\" != \"");
3710 PrintRawUriFp(stdout, ref, reflen);
3711 printf("\": ");
3712 goto end;
3713 }
3714 }
3715
3716 result = 1;
3717 end:
3718 if (alp_tctx != NULL)
3719 AppLayerParserThreadCtxFree(alp_tctx);
3720 StreamTcpFreeConfig(TRUE);
3721 UTHFreeFlow(f);
3722 return result;
3723 }
3724
3725 #include "conf-yaml-loader.h"
3726
3727 /** \test Abort
3728 */
3729 static int HTPParserTest08(void)
3730 {
3731 int result = 0;
3732 Flow *f = NULL;
3733 uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3734 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3735 TcpSession ssn;
3736 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3737
3738 char input[] = "\
3739 %YAML 1.1\n\
3740 ---\n\
3741 libhtp:\n\
3742 \n\
3743 default-config:\n\
3744 personality: IDS\n\
3745 ";
3746
3747 ConfCreateContextBackup();
3748 ConfInit();
3749 HtpConfigCreateBackup();
3750
3751 ConfYamlLoadString(input, strlen(input));
3752 HTPConfigure();
3753
3754 HtpState *htp_state = NULL;
3755 int r = 0;
3756 memset(&ssn, 0, sizeof(ssn));
3757
3758 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3759 if (f == NULL)
3760 goto end;
3761 f->protoctx = &ssn;
3762 f->proto = IPPROTO_TCP;
3763 f->alproto = ALPROTO_HTTP;
3764
3765 StreamTcpInitConfig(TRUE);
3766
3767 uint8_t flags = 0;
3768 flags = STREAM_TOSERVER|STREAM_START|STREAM_EOF;
3769
3770 FLOWLOCK_WRLOCK(f);
3771 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1,
3772 httplen1);
3773 if (r != 0) {
3774 printf("toserver chunk returned %" PRId32 ", expected"
3775 " 0: ", r);
3776 result = 0;
3777 FLOWLOCK_UNLOCK(f);
3778 goto end;
3779 }
3780 FLOWLOCK_UNLOCK(f);
3781
3782 htp_state = f->alstate;
3783 if (htp_state == NULL) {
3784 printf("no http state: ");
3785 result = 0;
3786 goto end;
3787 }
3788
3789 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3790 if (tx == NULL)
3791 goto end;
3792 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3793 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3794 //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3795 PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
3796 bstr_len(tx_ud->request_uri_normalized));
3797 }
3798
3799 result = 1;
3800 end:
3801 if (alp_tctx != NULL)
3802 AppLayerParserThreadCtxFree(alp_tctx);
3803 StreamTcpFreeConfig(TRUE);
3804 HTPFreeConfig();
3805 ConfDeInit();
3806 ConfRestoreContextBackup();
3807 HtpConfigRestoreBackup();
3808 UTHFreeFlow(f);
3809 return result;
3810 }
3811
3812 /** \test Abort
3813 */
3814 static int HTPParserTest09(void)
3815 {
3816 int result = 0;
3817 Flow *f = NULL;
3818 uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3819 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3820 TcpSession ssn;
3821 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3822
3823 char input[] = "\
3824 %YAML 1.1\n\
3825 ---\n\
3826 libhtp:\n\
3827 \n\
3828 default-config:\n\
3829 personality: Apache_2_2\n\
3830 ";
3831
3832 ConfCreateContextBackup();
3833 ConfInit();
3834 HtpConfigCreateBackup();
3835
3836 ConfYamlLoadString(input, strlen(input));
3837 HTPConfigure();
3838
3839 HtpState *htp_state = NULL;
3840 int r = 0;
3841
3842 memset(&ssn, 0, sizeof(ssn));
3843
3844 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3845 if (f == NULL)
3846 goto end;
3847 f->protoctx = &ssn;
3848 f->proto = IPPROTO_TCP;
3849 f->alproto = ALPROTO_HTTP;
3850
3851 StreamTcpInitConfig(TRUE);
3852
3853 uint8_t flags = 0;
3854 flags = STREAM_TOSERVER|STREAM_START|STREAM_EOF;
3855
3856 FLOWLOCK_WRLOCK(f);
3857 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1,
3858 httplen1);
3859 if (r != 0) {
3860 printf("toserver chunk returned %" PRId32 ", expected"
3861 " 0: ", r);
3862 FLOWLOCK_UNLOCK(f);
3863 goto end;
3864 }
3865 FLOWLOCK_UNLOCK(f);
3866
3867 htp_state = f->alstate;
3868 if (htp_state == NULL) {
3869 printf("no http state: ");
3870 goto end;
3871 }
3872
3873 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3874 if (tx == NULL)
3875 goto end;
3876 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3877 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3878 //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3879 PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
3880 bstr_len(tx_ud->request_uri_normalized));
3881 }
3882
3883 result = 1;
3884 end:
3885 if (alp_tctx != NULL)
3886 AppLayerParserThreadCtxFree(alp_tctx);
3887 StreamTcpFreeConfig(TRUE);
3888 HTPFreeConfig();
3889 ConfDeInit();
3890 ConfRestoreContextBackup();
3891 HtpConfigRestoreBackup();
3892 UTHFreeFlow(f);
3893 return result;
3894 }
3895
3896 /** \test Host:www.google.com <- missing space between name:value (rfc violation)
3897 */
3898 static int HTPParserTest10(void)
3899 {
3900 int result = 0;
3901 Flow *f = NULL;
3902 uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\r\n\r\n";
3903 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3904 TcpSession ssn;
3905 HtpState *htp_state = NULL;
3906 int r = 0;
3907 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
3908
3909 memset(&ssn, 0, sizeof(ssn));
3910
3911 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3912 if (f == NULL)
3913 goto end;
3914 f->protoctx = &ssn;
3915 f->proto = IPPROTO_TCP;
3916 f->alproto = ALPROTO_HTTP;
3917
3918 StreamTcpInitConfig(TRUE);
3919
3920 uint32_t u;
3921 for (u = 0; u < httplen1; u++) {
3922 uint8_t flags = 0;
3923
3924 if (u == 0)
3925 flags = STREAM_TOSERVER|STREAM_START;
3926 else if (u == (httplen1 - 1))
3927 flags = STREAM_TOSERVER|STREAM_EOF;
3928 else
3929 flags = STREAM_TOSERVER;
3930
3931 FLOWLOCK_WRLOCK(f);
3932 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3933 &httpbuf1[u], 1);
3934 if (r != 0) {
3935 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3936 " 0: ", u, r);
3937 FLOWLOCK_UNLOCK(f);
3938 goto end;
3939 }
3940 FLOWLOCK_UNLOCK(f);
3941 }
3942
3943 htp_state = f->alstate;
3944 if (htp_state == NULL) {
3945 printf("no http state: ");
3946 goto end;
3947 }
3948
3949 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3950 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3951 if (h == NULL) {
3952 goto end;
3953 }
3954
3955 char *name = bstr_util_strdup_to_c(h->name);
3956 if (name == NULL) {
3957 goto end;
3958 }
3959
3960 if (strcmp(name, "Host") != 0) {
3961 printf("header name not \"Host\", instead \"%s\": ", name);
3962 free(name);
3963 goto end;
3964 }
3965 free(name);
3966
3967 char *value = bstr_util_strdup_to_c(h->value);
3968 if (value == NULL) {
3969 goto end;
3970 }
3971
3972 if (strcmp(value, "www.google.com") != 0) {
3973 printf("header value not \"www.google.com\", instead \"%s\": ", value);
3974 free(value);
3975 goto end;
3976 }
3977 free(value);
3978
3979 result = 1;
3980 end:
3981 if (alp_tctx != NULL)
3982 AppLayerParserThreadCtxFree(alp_tctx);
3983 StreamTcpFreeConfig(TRUE);
3984 UTHFreeFlow(f);
3985 return result;
3986 }
3987
3988 /** \test double encoding in path
3989 */
3990 static int HTPParserTest11(void)
3991 {
3992 int result = 0;
3993 Flow *f = NULL;
3994 uint8_t httpbuf1[] = "GET /%2500 HTTP/1.0\r\n\r\n";
3995 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3996 TcpSession ssn;
3997 HtpState *htp_state = NULL;
3998 int r = 0;
3999 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
4000
4001 memset(&ssn, 0, sizeof(ssn));
4002
4003 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4004 if (f == NULL)
4005 goto end;
4006 f->protoctx = &ssn;
4007 f->proto = IPPROTO_TCP;
4008 f->alproto = ALPROTO_HTTP;
4009
4010 StreamTcpInitConfig(TRUE);
4011
4012 uint32_t u;
4013 for (u = 0; u < httplen1; u++) {
4014 uint8_t flags = 0;
4015
4016 if (u == 0)
4017 flags = STREAM_TOSERVER|STREAM_START;
4018 else if (u == (httplen1 - 1))
4019 flags = STREAM_TOSERVER|STREAM_EOF;
4020 else
4021 flags = STREAM_TOSERVER;
4022
4023 FLOWLOCK_WRLOCK(f);
4024 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4025 &httpbuf1[u], 1);
4026 if (r != 0) {
4027 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4028 " 0: ", u, r);
4029 FLOWLOCK_UNLOCK(f);
4030 goto end;
4031 }
4032 FLOWLOCK_UNLOCK(f);
4033 }
4034
4035 htp_state = f->alstate;
4036 if (htp_state == NULL) {
4037 printf("no http state: ");
4038 goto end;
4039 }
4040
4041 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4042 if (tx == NULL)
4043 goto end;
4044 HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4045 if (tx != NULL && tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4046 if (4 != bstr_len(tx_ud->request_uri_normalized)) {
4047 printf("normalized uri len should be 2, is %"PRIuMAX,
4048 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4049 goto end;
4050 }
4051
4052 if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
4053 bstr_ptr(tx_ud->request_uri_normalized)[1] != '%' ||
4054 bstr_ptr(tx_ud->request_uri_normalized)[2] != '0' ||
4055 bstr_ptr(tx_ud->request_uri_normalized)[3] != '0')
4056 {
4057 printf("normalized uri \"");
4058 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4059 printf("\": ");
4060 goto end;
4061 }
4062 }
4063
4064 result = 1;
4065 end:
4066 if (alp_tctx != NULL)
4067 AppLayerParserThreadCtxFree(alp_tctx);
4068 StreamTcpFreeConfig(TRUE);
4069 UTHFreeFlow(f);
4070 return result;
4071 }
4072
4073 /** \test double encoding in query
4074 */
4075 static int HTPParserTest12(void)
4076 {
4077 int result = 0;
4078 Flow *f = NULL;
4079 uint8_t httpbuf1[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
4080 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4081 TcpSession ssn;
4082 HtpState *htp_state = NULL;
4083 int r = 0;
4084 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
4085
4086 memset(&ssn, 0, sizeof(ssn));
4087
4088 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4089 if (f == NULL)
4090 goto end;
4091 f->protoctx = &ssn;
4092 f->proto = IPPROTO_TCP;
4093 f->alproto = ALPROTO_HTTP;
4094
4095 StreamTcpInitConfig(TRUE);
4096
4097 uint32_t u;
4098 for (u = 0; u < httplen1; u++) {
4099 uint8_t flags = 0;
4100
4101 if (u == 0)
4102 flags = STREAM_TOSERVER|STREAM_START;
4103 else if (u == (httplen1 - 1))
4104 flags = STREAM_TOSERVER|STREAM_EOF;
4105 else
4106 flags = STREAM_TOSERVER;
4107
4108 FLOWLOCK_WRLOCK(f);
4109 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4110 &httpbuf1[u], 1);
4111 if (r != 0) {
4112 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4113 " 0: ", u, r);
4114 FLOWLOCK_UNLOCK(f);
4115 goto end;
4116 }
4117 FLOWLOCK_UNLOCK(f);
4118 }
4119
4120 htp_state = f->alstate;
4121 if (htp_state == NULL) {
4122 printf("no http state: ");
4123 goto end;
4124 }
4125
4126 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4127 if (tx == NULL)
4128 goto end;
4129 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4130 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4131 if (7 != bstr_len(tx_ud->request_uri_normalized)) {
4132 printf("normalized uri len should be 5, is %"PRIuMAX,
4133 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4134 goto end;
4135 }
4136
4137 if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
4138 bstr_ptr(tx_ud->request_uri_normalized)[1] != '?' ||
4139 bstr_ptr(tx_ud->request_uri_normalized)[2] != 'a' ||
4140 bstr_ptr(tx_ud->request_uri_normalized)[3] != '=' ||
4141 bstr_ptr(tx_ud->request_uri_normalized)[4] != '%' ||
4142 bstr_ptr(tx_ud->request_uri_normalized)[5] != '0' ||
4143 bstr_ptr(tx_ud->request_uri_normalized)[6] != '0')
4144 {
4145 printf("normalized uri \"");
4146 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4147 printf("\": ");
4148 goto end;
4149 }
4150 }
4151
4152 result = 1;
4153 end:
4154 if (alp_tctx != NULL)
4155 AppLayerParserThreadCtxFree(alp_tctx);
4156 StreamTcpFreeConfig(TRUE);
4157 UTHFreeFlow(f);
4158 return result;
4159 }
4160
4161 /** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
4162 */
4163 static int HTPParserTest13(void)
4164 {
4165 int result = 0;
4166 Flow *f = NULL;
4167 uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\rName: Value\r\n\r\n";
4168 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4169 TcpSession ssn;
4170 HtpState *htp_state = NULL;
4171 int r = 0;
4172 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
4173
4174 memset(&ssn, 0, sizeof(ssn));
4175
4176 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4177 if (f == NULL)
4178 goto end;
4179 f->protoctx = &ssn;
4180 f->proto = IPPROTO_TCP;
4181 f->alproto = ALPROTO_HTTP;
4182
4183 StreamTcpInitConfig(TRUE);
4184
4185 uint32_t u;
4186 for (u = 0; u < httplen1; u++) {
4187 uint8_t flags = 0;
4188
4189 if (u == 0)
4190 flags = STREAM_TOSERVER|STREAM_START;
4191 else if (u == (httplen1 - 1))
4192 flags = STREAM_TOSERVER|STREAM_EOF;
4193 else
4194 flags = STREAM_TOSERVER;
4195
4196 FLOWLOCK_WRLOCK(f);
4197 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4198 &httpbuf1[u], 1);
4199 if (r != 0) {
4200 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4201 " 0: ", u, r);
4202 FLOWLOCK_UNLOCK(f);
4203 goto end;
4204 }
4205 FLOWLOCK_UNLOCK(f);
4206 }
4207
4208 htp_state = f->alstate;
4209 if (htp_state == NULL) {
4210 printf("no http state: ");
4211 goto end;
4212 }
4213
4214 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4215 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
4216 if (h == NULL) {
4217 goto end;
4218 }
4219
4220 char *name = bstr_util_strdup_to_c(h->name);
4221 if (name == NULL) {
4222 goto end;
4223 }
4224
4225 if (strcmp(name, "Host") != 0) {
4226 printf("header name not \"Host\", instead \"%s\": ", name);
4227 free(name);
4228 goto end;
4229 }
4230 free(name);
4231
4232 char *value = bstr_util_strdup_to_c(h->value);
4233 if (value == NULL) {
4234 goto end;
4235 }
4236
4237 if (strcmp(value, "www.google.com\rName: Value") != 0) {
4238 printf("header value not \"www.google.com\", instead \"");
4239 PrintRawUriFp(stdout, (uint8_t *)value, strlen(value));
4240 printf("\": ");
4241 free(value);
4242 goto end;
4243 }
4244 free(value);
4245
4246 result = 1;
4247 end:
4248 if (alp_tctx != NULL)
4249 AppLayerParserThreadCtxFree(alp_tctx);
4250 StreamTcpFreeConfig(TRUE);
4251 UTHFreeFlow(f);
4252 return result;
4253 }
4254
4255 /** \test Test basic config */
4256 static int HTPParserConfigTest01(void)
4257 {
4258 int ret = 0;
4259 char input[] = "\
4260 %YAML 1.1\n\
4261 ---\n\
4262 libhtp:\n\
4263 \n\
4264 default-config:\n\
4265 personality: IDS\n\
4266 \n\
4267 server-config:\n\
4268 \n\
4269 - apache-tomcat:\n\
4270 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4271 personality: Tomcat_6_0\n\
4272 \n\
4273 - iis7:\n\
4274 address: \n\
4275 - 192.168.0.0/24\n\
4276 - 192.168.10.0/24\n\
4277 personality: IIS_7_0\n\
4278 ";
4279
4280 ConfCreateContextBackup();
4281 ConfInit();
4282
4283 ConfYamlLoadString(input, strlen(input));
4284
4285 ConfNode *outputs;
4286 outputs = ConfGetNode("libhtp.default-config.personality");
4287 if (outputs == NULL) {
4288 goto end;
4289 }
4290
4291 outputs = ConfGetNode("libhtp.server-config");
4292 if (outputs == NULL) {
4293 goto end;
4294 }
4295
4296 ConfNode *node = TAILQ_FIRST(&outputs->head);
4297 if (node == NULL) {
4298 goto end;
4299 }
4300 if (strcmp(node->name, "0") != 0) {
4301 goto end;
4302 }
4303 node = TAILQ_FIRST(&node->head);
4304 if (node == NULL) {
4305 goto end;
4306 }
4307 if (strcmp(node->name, "apache-tomcat") != 0) {
4308 goto end;
4309 }
4310
4311 int i = 0;
4312 ConfNode *n;
4313
4314 ConfNode *node2 = ConfNodeLookupChild(node, "personality");
4315 if (node2 == NULL) {
4316 goto end;
4317 }
4318 if (strcmp(node2->val, "Tomcat_6_0") != 0) {
4319 goto end;
4320 }
4321
4322 node = ConfNodeLookupChild(node, "address");
4323 if (node == NULL) {
4324 goto end;
4325 }
4326 TAILQ_FOREACH(n, &node->head, next) {
4327 if (n == NULL) {
4328 goto end;
4329 }
4330
4331 switch(i) {
4332 case 0:
4333 if (strcmp(n->name, "0") != 0) {
4334 goto end;
4335 }
4336 if (strcmp(n->val, "192.168.1.0/24") != 0) {
4337 goto end;
4338 }
4339 break;
4340 case 1:
4341 if (strcmp(n->name, "1") != 0) {
4342 goto end;
4343 }
4344 if (strcmp(n->val, "127.0.0.0/8") != 0) {
4345 goto end;
4346 }
4347 break;
4348 case 2:
4349 if (strcmp(n->name, "2") != 0) {
4350 goto end;
4351 }
4352 if (strcmp(n->val, "::1") != 0) {
4353 goto end;
4354 }
4355 break;
4356 default:
4357 goto end;
4358 }
4359 i++;
4360 }
4361
4362 outputs = ConfGetNode("libhtp.server-config");
4363 if (outputs == NULL) {
4364 goto end;
4365 }
4366
4367 node = TAILQ_FIRST(&outputs->head);
4368 node = TAILQ_NEXT(node, next);
4369 if (node == NULL) {
4370 goto end;
4371 }
4372 if (strcmp(node->name, "1") != 0) {
4373 goto end;
4374 }
4375 node = TAILQ_FIRST(&node->head);
4376 if (node == NULL) {
4377 goto end;
4378 }
4379 if (strcmp(node->name, "iis7") != 0) {
4380 goto end;
4381 }
4382
4383 node2 = ConfNodeLookupChild(node, "personality");
4384 if (node2 == NULL) {
4385 goto end;
4386 }
4387 if (strcmp(node2->val, "IIS_7_0") != 0) {
4388 goto end;
4389 }
4390
4391 node = ConfNodeLookupChild(node, "address");
4392 if (node == NULL) {
4393 goto end;
4394 }
4395
4396 i = 0;
4397 TAILQ_FOREACH(n, &node->head, next) {
4398 if (n == NULL) {
4399 goto end;
4400 }
4401
4402 switch(i) {
4403 case 0:
4404 if (strcmp(n->name, "0") != 0) {
4405 goto end;
4406 }
4407 if (strcmp(n->val, "192.168.0.0/24") != 0) {
4408 goto end;
4409 }
4410 break;
4411 case 1:
4412 if (strcmp(n->name, "1") != 0) {
4413 goto end;
4414 }
4415 if (strcmp(n->val, "192.168.10.0/24") != 0) {
4416 goto end;
4417 }
4418 break;
4419 default:
4420 goto end;
4421 }
4422 i++;
4423 }
4424
4425 ret = 1;
4426
4427 end:
4428 ConfDeInit();
4429 ConfRestoreContextBackup();
4430
4431 return ret;
4432 }
4433
4434 /** \test Test config builds radix correctly */
4435 static int HTPParserConfigTest02(void)
4436 {
4437 int ret = 0;
4438 char input[] = "\
4439 %YAML 1.1\n\
4440 ---\n\
4441 libhtp:\n\
4442 \n\
4443 default-config:\n\
4444 personality: IDS\n\
4445 \n\
4446 server-config:\n\
4447 \n\
4448 - apache-tomcat:\n\
4449 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4450 personality: Tomcat_6_0\n\
4451 \n\
4452 - iis7:\n\
4453 address: \n\
4454 - 192.168.0.0/24\n\
4455 - 192.168.10.0/24\n\
4456 personality: IIS_7_0\n\
4457 ";
4458
4459 ConfCreateContextBackup();
4460 ConfInit();
4461 HtpConfigCreateBackup();
4462
4463 ConfYamlLoadString(input, strlen(input));
4464
4465 HTPConfigure();
4466
4467 if (cfglist.cfg == NULL) {
4468 printf("No default config created.\n");
4469 goto end;
4470 }
4471
4472 if (cfgtree == NULL) {
4473 printf("No config tree created.\n");
4474 goto end;
4475 }
4476
4477 htp_cfg_t *htp = cfglist.cfg;
4478 uint8_t buf[128];
4479 const char *addr;
4480 void *user_data = NULL;
4481
4482 addr = "192.168.10.42";
4483 if (inet_pton(AF_INET, addr, buf) == 1) {
4484 (void)SCRadixFindKeyIPV4BestMatch(buf, cfgtree, &user_data);
4485 if (user_data != NULL) {
4486 HTPCfgRec *htp_cfg_rec = user_data;
4487 htp = htp_cfg_rec->cfg;
4488 SCLogDebug("LIBHTP using config: %p", htp);
4489 }
4490 if (htp == NULL) {
4491 printf("Could not get config for: %s\n", addr);
4492 goto end;
4493 }
4494 }
4495 else {
4496 printf("Failed to parse address: %s\n", addr);
4497 goto end;
4498 }
4499
4500 user_data = NULL;
4501 addr = "::1";
4502 if (inet_pton(AF_INET6, addr, buf) == 1) {
4503 (void)SCRadixFindKeyIPV6BestMatch(buf, cfgtree, &user_data);
4504 if (user_data != NULL) {
4505 HTPCfgRec *htp_cfg_rec = user_data;
4506 htp = htp_cfg_rec->cfg;
4507 SCLogDebug("LIBHTP using config: %p", htp);
4508 }
4509 if (htp == NULL) {
4510 printf("Could not get config for: %s\n", addr);
4511 goto end;
4512 }
4513 }
4514 else {
4515 printf("Failed to parse address: %s\n", addr);
4516 goto end;
4517 }
4518
4519 ret = 1;
4520
4521 end:
4522 HTPFreeConfig();
4523 ConfDeInit();
4524 ConfRestoreContextBackup();
4525 HtpConfigRestoreBackup();
4526
4527 return ret;
4528 }
4529
4530 /** \test Test traffic is handled by the correct htp config */
4531 static int HTPParserConfigTest03(void)
4532 {
4533 int result = 1;
4534 Flow *f = NULL;
4535 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
4536 " Data is c0oL!";
4537 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4538 TcpSession ssn;
4539 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
4540
4541 HtpState *htp_state = NULL;
4542 int r = 0;
4543 char input[] = "\
4544 %YAML 1.1\n\
4545 ---\n\
4546 libhtp:\n\
4547 \n\
4548 default-config:\n\
4549 personality: IDS\n\
4550 \n\
4551 server-config:\n\
4552 \n\
4553 - apache-tomcat:\n\
4554 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4555 personality: Tomcat_6_0\n\
4556 \n\
4557 - iis7:\n\
4558 address: \n\
4559 - 192.168.0.0/24\n\
4560 - 192.168.10.0/24\n\
4561 personality: IIS_7_0\n\
4562 ";
4563
4564 ConfCreateContextBackup();
4565 ConfInit();
4566 HtpConfigCreateBackup();
4567
4568 ConfYamlLoadString(input, strlen(input));
4569
4570 HTPConfigure();
4571
4572 const char *addr = "192.168.10.42";
4573
4574 memset(&ssn, 0, sizeof(ssn));
4575
4576 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4577 if (f == NULL)
4578 goto end;
4579 f->protoctx = &ssn;
4580 f->proto = IPPROTO_TCP;
4581 f->alproto = ALPROTO_HTTP;
4582
4583 htp_cfg_t *htp = cfglist.cfg;
4584
4585 void *user_data = NULL;
4586 (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)f->dst.addr_data32, cfgtree, &user_data);
4587 if (user_data != NULL) {
4588 HTPCfgRec *htp_cfg_rec = user_data;
4589 htp = htp_cfg_rec->cfg;
4590 SCLogDebug("LIBHTP using config: %p", htp);
4591 }
4592 if (htp == NULL) {
4593 printf("Could not get config for: %s\n", addr);
4594 goto end;
4595 }
4596
4597 StreamTcpInitConfig(TRUE);
4598
4599 uint32_t u;
4600 for (u = 0; u < httplen1; u++) {
4601 uint8_t flags = 0;
4602
4603 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4604 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4605 else flags = STREAM_TOSERVER;
4606
4607 FLOWLOCK_WRLOCK(f);
4608 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4609 &httpbuf1[u], 1);
4610 if (r != 0) {
4611 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4612 " 0: ", u, r);
4613 result = 0;
4614 FLOWLOCK_UNLOCK(f);
4615 goto end;
4616 }
4617 FLOWLOCK_UNLOCK(f);
4618 }
4619
4620 htp_state = f->alstate;
4621 if (htp_state == NULL) {
4622 printf("no http state: ");
4623 result = 0;
4624 goto end;
4625 }
4626
4627 if (HTPStateGetTxCnt(htp_state) != 2) {
4628 printf("HTPStateGetTxCnt(htp_state) failure\n");
4629 goto end;
4630 }
4631
4632 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4633 if (tx == NULL)
4634 goto end;
4635 if (tx->cfg != htp) {
4636 printf("wrong HTP config (%p instead of %p - default=%p): ",
4637 tx->cfg, htp, cfglist.cfg);
4638 goto end;
4639 }
4640 tx = HTPStateGetTx(htp_state, 1);
4641 if (tx == NULL)
4642 goto end;
4643 if (tx->cfg != htp) {
4644 printf("wrong HTP config (%p instead of %p - default=%p): ",
4645 tx->cfg, htp, cfglist.cfg);
4646 goto end;
4647 }
4648
4649 end:
4650 if (alp_tctx != NULL)
4651 AppLayerParserThreadCtxFree(alp_tctx);
4652 HTPFreeConfig();
4653 ConfDeInit();
4654 ConfRestoreContextBackup();
4655 HtpConfigRestoreBackup();
4656
4657 StreamTcpFreeConfig(TRUE);
4658 UTHFreeFlow(f);
4659 return result;
4660 }
4661
4662 /* disabled when we upgraded to libhtp 0.5.x */
4663 #if 0
4664 static int HTPParserConfigTest04(void)
4665 {
4666 int result = 0;
4667
4668 char input[] = "\
4669 %YAML 1.1\n\
4670 ---\n\
4671 libhtp:\n\
4672 \n\
4673 default-config:\n\
4674 personality: IDS\n\
4675 path-control-char-handling: status_400\n\
4676 path-convert-utf8: yes\n\
4677 path-invalid-encoding-handling: remove_percent\n\
4678 \n\
4679 server-config:\n\
4680 \n\
4681 - apache-tomcat:\n\
4682 personality: Tomcat_6_0\n\
4683 path-invalid-utf8-handling: none\n\
4684 path-nul-encoded-handling: status_404\n\
4685 path-nul-raw-handling: status_400\n\
4686 \n\
4687 - iis7:\n\
4688 personality: IIS_7_0\n\
4689 path-replacement-char: o\n\
4690 path-unicode-mapping: status_400\n\
4691 ";
4692
4693 ConfCreateContextBackup();
4694 ConfInit();
4695 HtpConfigCreateBackup();
4696
4697 ConfYamlLoadString(input, strlen(input));
4698
4699 HTPConfigure();
4700
4701 HTPCfgRec *cfg_rec = &cfglist;
4702 if (cfg_rec->cfg->path_control_char_handling != STATUS_400 ||
4703 cfg_rec->cfg->path_convert_utf8 != 1 ||
4704 cfg_rec->cfg->path_invalid_encoding_handling != URL_DECODER_REMOVE_PERCENT) {
4705 printf("failed 1\n");
4706 goto end;
4707 }
4708
4709 cfg_rec = cfg_rec->next;
4710 if (cfg_rec->cfg->bestfit_replacement_char != 'o' ||
4711 cfg_rec->cfg->path_unicode_mapping != STATUS_400) {
4712 printf("failed 2\n");
4713 goto end;
4714 }
4715
4716 cfg_rec = cfg_rec->next;
4717 if (cfg_rec->cfg->path_invalid_utf8_handling != NONE ||
4718 cfg_rec->cfg->path_nul_encoded_handling != STATUS_404 ||
4719 cfg_rec->cfg->path_nul_raw_handling != STATUS_400) {
4720 printf("failed 3\n");
4721 goto end;
4722 }
4723
4724 result = 1;
4725
4726 end:
4727 HTPFreeConfig();
4728 ConfDeInit();
4729 ConfRestoreContextBackup();
4730 HtpConfigRestoreBackup();
4731
4732 return result;
4733 }
4734 #endif
4735
4736 /** \test Test %2f decoding in profile Apache_2_2
4737 *
4738 * %2f in path is left untouched
4739 * %2f in query string is normalized to %2F
4740 * %252f in query string is decoded/normalized to %2F
4741 */
4742 static int HTPParserDecodingTest01(void)
4743 {
4744 int result = 0;
4745 Flow *f = NULL;
4746 uint8_t httpbuf1[] =
4747 "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4748 "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4749 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4750 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4751 TcpSession ssn;
4752 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
4753
4754 HtpState *htp_state = NULL;
4755 int r = 0;
4756 char input[] = "\
4757 %YAML 1.1\n\
4758 ---\n\
4759 libhtp:\n\
4760 \n\
4761 default-config:\n\
4762 personality: Apache_2\n\
4763 ";
4764
4765 ConfCreateContextBackup();
4766 ConfInit();
4767 HtpConfigCreateBackup();
4768 ConfYamlLoadString(input, strlen(input));
4769 HTPConfigure();
4770 const char *addr = "4.3.2.1";
4771 memset(&ssn, 0, sizeof(ssn));
4772
4773 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4774 if (f == NULL)
4775 goto end;
4776 f->protoctx = &ssn;
4777 f->proto = IPPROTO_TCP;
4778 f->alproto = ALPROTO_HTTP;
4779
4780 StreamTcpInitConfig(TRUE);
4781
4782 uint32_t u;
4783 for (u = 0; u < httplen1; u++) {
4784 uint8_t flags = 0;
4785
4786 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4787 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4788 else flags = STREAM_TOSERVER;
4789
4790 FLOWLOCK_WRLOCK(f);
4791 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4792 &httpbuf1[u], 1);
4793 if (r != 0) {
4794 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4795 " 0: ", u, r);
4796 FLOWLOCK_UNLOCK(f);
4797 goto end;
4798 }
4799 FLOWLOCK_UNLOCK(f);
4800 }
4801
4802 htp_state = f->alstate;
4803 if (htp_state == NULL) {
4804 printf("no http state: ");
4805 goto end;
4806 }
4807
4808 uint8_t ref1[] = "/abc%2fdef";
4809 size_t reflen = sizeof(ref1) - 1;
4810
4811 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4812 if (tx == NULL)
4813 goto end;
4814 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4815 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4816 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4817 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4818 (uintmax_t)reflen,
4819 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4820 goto end;
4821 }
4822
4823 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
4824 bstr_len(tx_ud->request_uri_normalized)) != 0)
4825 {
4826 printf("normalized uri \"");
4827 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4828 printf("\" != \"");
4829 PrintRawUriFp(stdout, ref1, reflen);
4830 printf("\": ");
4831 goto end;
4832 }
4833 }
4834
4835 uint8_t ref2[] = "/abc/def?ghi/jkl";
4836 reflen = sizeof(ref2) - 1;
4837
4838 tx = HTPStateGetTx(htp_state, 1);
4839 if (tx == NULL)
4840 goto end;
4841 tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4842 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4843 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4844 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4845 (uintmax_t)reflen,
4846 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4847 goto end;
4848 }
4849
4850 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
4851 bstr_len(tx_ud->request_uri_normalized)) != 0)
4852 {
4853 printf("normalized uri \"");
4854 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4855 printf("\" != \"");
4856 PrintRawUriFp(stdout, ref2, reflen);
4857 printf("\": ");
4858 goto end;
4859 }
4860 }
4861
4862 uint8_t ref3[] = "/abc/def?ghi%2fjkl";
4863 reflen = sizeof(ref3) - 1;
4864 tx = HTPStateGetTx(htp_state, 2);
4865 if (tx == NULL)
4866 goto end;
4867 tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4868 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4869 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4870 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4871 (uintmax_t)reflen,
4872 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4873 goto end;
4874 }
4875
4876 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
4877 bstr_len(tx_ud->request_uri_normalized)) != 0)
4878 {
4879 printf("normalized uri \"");
4880 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4881 printf("\" != \"");
4882 PrintRawUriFp(stdout, ref3, reflen);
4883 printf("\": ");
4884 goto end;
4885 }
4886 }
4887
4888 result = 1;
4889
4890 end:
4891 if (alp_tctx != NULL)
4892 AppLayerParserThreadCtxFree(alp_tctx);
4893 HTPFreeConfig();
4894 ConfDeInit();
4895 ConfRestoreContextBackup();
4896 HtpConfigRestoreBackup();
4897
4898 StreamTcpFreeConfig(TRUE);
4899 UTHFreeFlow(f);
4900 return result;
4901 }
4902
4903 /** \test Test %2f decoding in profile IDS
4904 *
4905 * %2f in path decoded to /
4906 * %2f in query string is decoded to /
4907 * %252f in query string is decoded to %2F
4908 */
4909 static int HTPParserDecodingTest02(void)
4910 {
4911 int result = 0;
4912 Flow *f = NULL;
4913 uint8_t httpbuf1[] =
4914 "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4915 "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4916 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4917 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4918 TcpSession ssn;
4919 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
4920
4921 HtpState *htp_state = NULL;
4922 int r = 0;
4923 char input[] = "\
4924 %YAML 1.1\n\
4925 ---\n\
4926 libhtp:\n\
4927 \n\
4928 default-config:\n\
4929 personality: IDS\n\
4930 double-decode-path: no\n\
4931 double-decode-query: no\n\
4932 ";
4933
4934 ConfCreateContextBackup();
4935 ConfInit();
4936 HtpConfigCreateBackup();
4937 ConfYamlLoadString(input, strlen(input));
4938 HTPConfigure();
4939 const char *addr = "4.3.2.1";
4940 memset(&ssn, 0, sizeof(ssn));
4941
4942 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4943 if (f == NULL)
4944 goto end;
4945 f->protoctx = &ssn;
4946 f->proto = IPPROTO_TCP;
4947 f->alproto = ALPROTO_HTTP;
4948
4949 StreamTcpInitConfig(TRUE);
4950
4951 uint32_t u;
4952 for (u = 0; u < httplen1; u++) {
4953 uint8_t flags = 0;
4954
4955 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4956 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4957 else flags = STREAM_TOSERVER;
4958
4959 FLOWLOCK_WRLOCK(f);
4960 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4961 &httpbuf1[u], 1);
4962 if (r != 0) {
4963 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4964 " 0: ", u, r);
4965 FLOWLOCK_UNLOCK(f);
4966 goto end;
4967 }
4968 FLOWLOCK_UNLOCK(f);
4969 }
4970
4971 htp_state = f->alstate;
4972 if (htp_state == NULL) {
4973 printf("no http state: ");
4974 goto end;
4975 }
4976
4977 uint8_t ref1[] = "/abc/def";
4978 size_t reflen = sizeof(ref1) - 1;
4979
4980 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4981 if (tx == NULL)
4982 goto end;
4983 HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4984 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4985 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4986 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4987 (uintmax_t)reflen,
4988 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4989 goto end;
4990 }
4991
4992 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
4993 bstr_len(tx_ud->request_uri_normalized)) != 0)
4994 {
4995 printf("normalized uri \"");
4996 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4997 printf("\" != \"");
4998 PrintRawUriFp(stdout, ref1, reflen);
4999 printf("\": ");
5000 goto end;
5001 }
5002 }
5003
5004 uint8_t ref2[] = "/abc/def?ghi/jkl";
5005 reflen = sizeof(ref2) - 1;
5006
5007 tx = HTPStateGetTx(htp_state, 1);
5008 if (tx == NULL)
5009 goto end;
5010 tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
5011 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5012 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5013 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5014 (uintmax_t)reflen,
5015 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5016 goto end;
5017 }
5018
5019 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
5020 bstr_len(tx_ud->request_uri_normalized)) != 0)
5021 {
5022 printf("normalized uri \"");
5023 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5024 printf("\" != \"");
5025 PrintRawUriFp(stdout, ref2, reflen);
5026 printf("\": ");
5027 goto end;
5028 }
5029 }
5030
5031 uint8_t ref3[] = "/abc/def?ghi%2fjkl";
5032 reflen = sizeof(ref3) - 1;
5033 tx = HTPStateGetTx(htp_state, 2);
5034 if (tx == NULL)
5035 goto end;
5036 tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5037 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5038 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5039 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX" (3): ",
5040 (uintmax_t)reflen,
5041 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5042 goto end;
5043 }
5044
5045 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
5046 bstr_len(tx_ud->request_uri_normalized)) != 0)
5047 {
5048 printf("normalized uri \"");
5049 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5050 printf("\" != \"");
5051 PrintRawUriFp(stdout, ref3, reflen);
5052 printf("\": ");
5053 goto end;
5054 }
5055 }
5056
5057 result = 1;
5058
5059 end:
5060 if (alp_tctx != NULL)
5061 AppLayerParserThreadCtxFree(alp_tctx);
5062 HTPFreeConfig();
5063 ConfDeInit();
5064 ConfRestoreContextBackup();
5065 HtpConfigRestoreBackup();
5066
5067 StreamTcpFreeConfig(TRUE);
5068 UTHFreeFlow(f);
5069 return result;
5070 }
5071
5072 /** \test Test %2f decoding in profile IDS with double-decode-* options
5073 *
5074 * %252f in path decoded to /
5075 * %252f in query string is decoded to /
5076 */
5077 static int HTPParserDecodingTest03(void)
5078 {
5079 int result = 0;
5080 Flow *f = NULL;
5081 uint8_t httpbuf1[] =
5082 "GET /abc%252fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5083 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5084 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5085 TcpSession ssn;
5086 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5087
5088 HtpState *htp_state = NULL;
5089 int r = 0;
5090 char input[] = "\
5091 %YAML 1.1\n\
5092 ---\n\
5093 libhtp:\n\
5094 \n\
5095 default-config:\n\
5096 personality: IDS\n\
5097 double-decode-path: yes\n\
5098 double-decode-query: yes\n\
5099 ";
5100
5101 ConfCreateContextBackup();
5102 ConfInit();
5103 HtpConfigCreateBackup();
5104 ConfYamlLoadString(input, strlen(input));
5105 HTPConfigure();
5106 const char *addr = "4.3.2.1";
5107 memset(&ssn, 0, sizeof(ssn));
5108
5109 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5110 if (f == NULL)
5111 goto end;
5112 f->protoctx = &ssn;
5113 f->proto = IPPROTO_TCP;
5114 f->alproto = ALPROTO_HTTP;
5115
5116 StreamTcpInitConfig(TRUE);
5117
5118 uint32_t u;
5119 for (u = 0; u < httplen1; u++) {
5120 uint8_t flags = 0;
5121
5122 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5123 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5124 else flags = STREAM_TOSERVER;
5125
5126 FLOWLOCK_WRLOCK(f);
5127 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5128 &httpbuf1[u], 1);
5129 if (r != 0) {
5130 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5131 " 0: ", u, r);
5132 FLOWLOCK_UNLOCK(f);
5133 goto end;
5134 }
5135 FLOWLOCK_UNLOCK(f);
5136 }
5137
5138 htp_state = f->alstate;
5139 if (htp_state == NULL) {
5140 printf("no http state: ");
5141 goto end;
5142 }
5143
5144 uint8_t ref1[] = "/abc/def";
5145 size_t reflen = sizeof(ref1) - 1;
5146
5147 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5148 if (tx == NULL)
5149 goto end;
5150 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5151 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5152 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5153 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5154 (uintmax_t)reflen,
5155 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5156 goto end;
5157 }
5158
5159 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5160 bstr_len(tx_ud->request_uri_normalized)) != 0)
5161 {
5162 printf("normalized uri \"");
5163 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5164 printf("\" != \"");
5165 PrintRawUriFp(stdout, ref1, reflen);
5166 printf("\": ");
5167 goto end;
5168 }
5169 }
5170
5171 uint8_t ref2[] = "/abc/def?ghi/jkl";
5172 reflen = sizeof(ref2) - 1;
5173
5174 tx = HTPStateGetTx(htp_state, 1);
5175 if (tx == NULL)
5176 goto end;
5177 tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
5178 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5179 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5180 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5181 (uintmax_t)reflen,
5182 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5183 goto end;
5184 }
5185
5186 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
5187 bstr_len(tx_ud->request_uri_normalized)) != 0)
5188 {
5189 printf("normalized uri \"");
5190 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5191 printf("\" != \"");
5192 PrintRawUriFp(stdout, ref2, reflen);
5193 printf("\": ");
5194 goto end;
5195 }
5196 }
5197
5198 result = 1;
5199
5200 end:
5201 if (alp_tctx != NULL)
5202 AppLayerParserThreadCtxFree(alp_tctx);
5203 HTPFreeConfig();
5204 ConfDeInit();
5205 ConfRestoreContextBackup();
5206 HtpConfigRestoreBackup();
5207
5208 StreamTcpFreeConfig(TRUE);
5209 UTHFreeFlow(f);
5210 return result;
5211 }
5212
5213 /** \test Test http:// in query profile IDS
5214 */
5215 static int HTPParserDecodingTest04(void)
5216 {
5217 int result = 0;
5218 Flow *f = NULL;
5219 uint8_t httpbuf1[] =
5220 "GET /abc/def?a=http://www.abc.com/ HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5221 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5222 TcpSession ssn;
5223 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5224
5225 HtpState *htp_state = NULL;
5226 int r = 0;
5227 char input[] = "\
5228 %YAML 1.1\n\
5229 ---\n\
5230 libhtp:\n\
5231 \n\
5232 default-config:\n\
5233 personality: IDS\n\
5234 double-decode-path: yes\n\
5235 double-decode-query: yes\n\
5236 ";
5237
5238 ConfCreateContextBackup();
5239 ConfInit();
5240 HtpConfigCreateBackup();
5241 ConfYamlLoadString(input, strlen(input));
5242 HTPConfigure();
5243 const char *addr = "4.3.2.1";
5244 memset(&ssn, 0, sizeof(ssn));
5245
5246 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5247 if (f == NULL)
5248 goto end;
5249 f->protoctx = &ssn;
5250 f->proto = IPPROTO_TCP;
5251 f->alproto = ALPROTO_HTTP;
5252
5253 StreamTcpInitConfig(TRUE);
5254
5255 uint32_t u;
5256 for (u = 0; u < httplen1; u++) {
5257 uint8_t flags = 0;
5258
5259 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5260 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5261 else flags = STREAM_TOSERVER;
5262
5263 FLOWLOCK_WRLOCK(f);
5264 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5265 &httpbuf1[u], 1);
5266 if (r != 0) {
5267 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5268 " 0: ", u, r);
5269 FLOWLOCK_UNLOCK(f);
5270 goto end;
5271 }
5272 FLOWLOCK_UNLOCK(f);
5273 }
5274
5275 htp_state = f->alstate;
5276 if (htp_state == NULL) {
5277 printf("no http state: ");
5278 goto end;
5279 }
5280
5281 uint8_t ref1[] = "/abc/def?a=http://www.abc.com/";
5282 size_t reflen = sizeof(ref1) - 1;
5283
5284 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5285 if (tx == NULL)
5286 goto end;
5287 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5288 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5289 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5290 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5291 (uintmax_t)reflen,
5292 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5293 goto end;
5294 }
5295
5296 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5297 bstr_len(tx_ud->request_uri_normalized)) != 0)
5298 {
5299 printf("normalized uri \"");
5300 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5301 printf("\" != \"");
5302 PrintRawUriFp(stdout, ref1, reflen);
5303 printf("\": ");
5304 goto end;
5305 }
5306 }
5307
5308 result = 1;
5309
5310 end:
5311 if (alp_tctx != NULL)
5312 AppLayerParserThreadCtxFree(alp_tctx);
5313 HTPFreeConfig();
5314 ConfDeInit();
5315 ConfRestoreContextBackup();
5316 HtpConfigRestoreBackup();
5317
5318 StreamTcpFreeConfig(TRUE);
5319 UTHFreeFlow(f);
5320 return result;
5321 }
5322
5323 /** \test Test \ char in query profile IDS. Bug 739
5324 */
5325 static int HTPParserDecodingTest05(void)
5326 {
5327 int result = 0;
5328 Flow *f = NULL;
5329 uint8_t httpbuf1[] =
5330 "GET /index?id=\\\"<script>alert(document.cookie)</script> HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5331 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5332 TcpSession ssn;
5333 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5334
5335 HtpState *htp_state = NULL;
5336 int r = 0;
5337 char input[] = "\
5338 %YAML 1.1\n\
5339 ---\n\
5340 libhtp:\n\
5341 \n\
5342 default-config:\n\
5343 personality: IDS\n\
5344 double-decode-path: yes\n\
5345 double-decode-query: yes\n\
5346 ";
5347
5348 ConfCreateContextBackup();
5349 ConfInit();
5350 HtpConfigCreateBackup();
5351 ConfYamlLoadString(input, strlen(input));
5352 HTPConfigure();
5353 const char *addr = "4.3.2.1";
5354 memset(&ssn, 0, sizeof(ssn));
5355
5356 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5357 if (f == NULL)
5358 goto end;
5359 f->protoctx = &ssn;
5360 f->proto = IPPROTO_TCP;
5361 f->alproto = ALPROTO_HTTP;
5362
5363 StreamTcpInitConfig(TRUE);
5364
5365 uint32_t u;
5366 for (u = 0; u < httplen1; u++) {
5367 uint8_t flags = 0;
5368
5369 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5370 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5371 else flags = STREAM_TOSERVER;
5372
5373 FLOWLOCK_WRLOCK(f);
5374 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5375 &httpbuf1[u], 1);
5376 if (r != 0) {
5377 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5378 " 0: ", u, r);
5379 FLOWLOCK_UNLOCK(f);
5380 goto end;
5381 }
5382 FLOWLOCK_UNLOCK(f);
5383 }
5384
5385 htp_state = f->alstate;
5386 if (htp_state == NULL) {
5387 printf("no http state: ");
5388 goto end;
5389 }
5390
5391 uint8_t ref1[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
5392 size_t reflen = sizeof(ref1) - 1;
5393
5394 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5395 if (tx == NULL)
5396 goto end;
5397 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5398 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5399 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5400 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5401 (uintmax_t)reflen,
5402 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5403 goto end;
5404 }
5405
5406 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5407 bstr_len(tx_ud->request_uri_normalized)) != 0)
5408 {
5409 printf("normalized uri \"");
5410 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5411 printf("\" != \"");
5412 PrintRawUriFp(stdout, ref1, reflen);
5413 printf("\": ");
5414 goto end;
5415 }
5416 }
5417
5418 result = 1;
5419
5420 end:
5421 if (alp_tctx != NULL)
5422 AppLayerParserThreadCtxFree(alp_tctx);
5423 HTPFreeConfig();
5424 ConfDeInit();
5425 ConfRestoreContextBackup();
5426 HtpConfigRestoreBackup();
5427
5428 StreamTcpFreeConfig(TRUE);
5429 UTHFreeFlow(f);
5430 return result;
5431 }
5432
5433 /** \test Test + char in query. Bug 1035
5434 */
5435 static int HTPParserDecodingTest06(void)
5436 {
5437 int result = 0;
5438 Flow *f = NULL;
5439 uint8_t httpbuf1[] =
5440 "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5441 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5442 TcpSession ssn;
5443 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5444
5445 HtpState *htp_state = NULL;
5446 int r = 0;
5447 char input[] = "\
5448 %YAML 1.1\n\
5449 ---\n\
5450 libhtp:\n\
5451 \n\
5452 default-config:\n\
5453 personality: IDS\n\
5454 double-decode-path: yes\n\
5455 double-decode-query: yes\n\
5456 ";
5457
5458 ConfCreateContextBackup();
5459 ConfInit();
5460 HtpConfigCreateBackup();
5461 ConfYamlLoadString(input, strlen(input));
5462 HTPConfigure();
5463 const char *addr = "4.3.2.1";
5464 memset(&ssn, 0, sizeof(ssn));
5465
5466 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5467 if (f == NULL)
5468 goto end;
5469 f->protoctx = &ssn;
5470 f->proto = IPPROTO_TCP;
5471 f->alproto = ALPROTO_HTTP;
5472
5473 StreamTcpInitConfig(TRUE);
5474
5475 uint32_t u;
5476 for (u = 0; u < httplen1; u++) {
5477 uint8_t flags = 0;
5478
5479 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5480 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5481 else flags = STREAM_TOSERVER;
5482
5483 FLOWLOCK_WRLOCK(f);
5484 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5485 &httpbuf1[u], 1);
5486 if (r != 0) {
5487 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5488 " 0: ", u, r);
5489 FLOWLOCK_UNLOCK(f);
5490 goto end;
5491 }
5492 FLOWLOCK_UNLOCK(f);
5493 }
5494
5495 htp_state = f->alstate;
5496 if (htp_state == NULL) {
5497 printf("no http state: ");
5498 goto end;
5499 }
5500
5501 uint8_t ref1[] = "/put.php?ip=1.2.3.4&port=+6000";
5502 size_t reflen = sizeof(ref1) - 1;
5503
5504 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5505 if (tx == NULL)
5506 goto end;
5507 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5508 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5509 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5510 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5511 (uintmax_t)reflen,
5512 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5513 goto end;
5514 }
5515
5516 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5517 bstr_len(tx_ud->request_uri_normalized)) != 0)
5518 {
5519 printf("normalized uri \"");
5520 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5521 printf("\" != \"");
5522 PrintRawUriFp(stdout, ref1, reflen);
5523 printf("\": ");
5524 goto end;
5525 }
5526 }
5527
5528 result = 1;
5529
5530 end:
5531 if (alp_tctx != NULL)
5532 AppLayerParserThreadCtxFree(alp_tctx);
5533 HTPFreeConfig();
5534 ConfDeInit();
5535 ConfRestoreContextBackup();
5536 HtpConfigRestoreBackup();
5537
5538 StreamTcpFreeConfig(TRUE);
5539 UTHFreeFlow(f);
5540 return result;
5541 }
5542
5543 /** \test Test + char in query. Bug 1035
5544 */
5545 static int HTPParserDecodingTest07(void)
5546 {
5547 int result = 0;
5548 Flow *f = NULL;
5549 uint8_t httpbuf1[] =
5550 "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5551 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5552 TcpSession ssn;
5553 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5554
5555 HtpState *htp_state = NULL;
5556 int r = 0;
5557 char input[] = "\
5558 %YAML 1.1\n\
5559 ---\n\
5560 libhtp:\n\
5561 \n\
5562 default-config:\n\
5563 personality: IDS\n\
5564 double-decode-path: yes\n\
5565 double-decode-query: yes\n\
5566 query-plusspace-decode: yes\n\
5567 ";
5568
5569 ConfCreateContextBackup();
5570 ConfInit();
5571 HtpConfigCreateBackup();
5572 ConfYamlLoadString(input, strlen(input));
5573 HTPConfigure();
5574 const char *addr = "4.3.2.1";
5575 memset(&ssn, 0, sizeof(ssn));
5576
5577 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5578 if (f == NULL)
5579 goto end;
5580 f->protoctx = &ssn;
5581 f->proto = IPPROTO_TCP;
5582 f->alproto = ALPROTO_HTTP;
5583
5584 StreamTcpInitConfig(TRUE);
5585
5586 uint32_t u;
5587 for (u = 0; u < httplen1; u++) {
5588 uint8_t flags = 0;
5589
5590 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5591 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5592 else flags = STREAM_TOSERVER;
5593
5594 FLOWLOCK_WRLOCK(f);
5595 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5596 &httpbuf1[u], 1);
5597 if (r != 0) {
5598 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5599 " 0: ", u, r);
5600 FLOWLOCK_UNLOCK(f);
5601 goto end;
5602 }
5603 FLOWLOCK_UNLOCK(f);
5604 }
5605
5606 htp_state = f->alstate;
5607 if (htp_state == NULL) {
5608 printf("no http state: ");
5609 goto end;
5610 }
5611
5612 uint8_t ref1[] = "/put.php?ip=1.2.3.4&port= 6000";
5613 size_t reflen = sizeof(ref1) - 1;
5614
5615 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5616 if (tx == NULL)
5617 goto end;
5618 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5619 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5620 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5621 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5622 (uintmax_t)reflen,
5623 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5624 goto end;
5625 }
5626
5627 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5628 bstr_len(tx_ud->request_uri_normalized)) != 0)
5629 {
5630 printf("normalized uri \"");
5631 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5632 printf("\" != \"");
5633 PrintRawUriFp(stdout, ref1, reflen);
5634 printf("\": ");
5635 goto end;
5636 }
5637 }
5638
5639 result = 1;
5640
5641 end:
5642 if (alp_tctx != NULL)
5643 AppLayerParserThreadCtxFree(alp_tctx);
5644 HTPFreeConfig();
5645 ConfDeInit();
5646 ConfRestoreContextBackup();
5647 HtpConfigRestoreBackup();
5648
5649 StreamTcpFreeConfig(TRUE);
5650 UTHFreeFlow(f);
5651 return result;
5652 }
5653
5654 /** \test Test 'proxy' URI normalization. Ticket 1008
5655 */
5656 static int HTPParserDecodingTest08(void)
5657 {
5658 int result = 0;
5659 Flow *f = NULL;
5660 uint8_t httpbuf1[] =
5661 "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5662 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5663 TcpSession ssn;
5664 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5665
5666 HtpState *htp_state = NULL;
5667 int r = 0;
5668 char input[] = "\
5669 %YAML 1.1\n\
5670 ---\n\
5671 libhtp:\n\
5672 \n\
5673 default-config:\n\
5674 personality: IDS\n\
5675 ";
5676
5677 ConfCreateContextBackup();
5678 ConfInit();
5679 HtpConfigCreateBackup();
5680 ConfYamlLoadString(input, strlen(input));
5681 HTPConfigure();
5682 const char *addr = "4.3.2.1";
5683 memset(&ssn, 0, sizeof(ssn));
5684
5685 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5686 if (f == NULL)
5687 goto end;
5688 f->protoctx = &ssn;
5689 f->proto = IPPROTO_TCP;
5690 f->alproto = ALPROTO_HTTP;
5691
5692 StreamTcpInitConfig(TRUE);
5693
5694 uint32_t u;
5695 for (u = 0; u < httplen1; u++) {
5696 uint8_t flags = 0;
5697
5698 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5699 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5700 else flags = STREAM_TOSERVER;
5701
5702 FLOWLOCK_WRLOCK(f);
5703 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5704 &httpbuf1[u], 1);
5705 if (r != 0) {
5706 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5707 " 0: ", u, r);
5708 FLOWLOCK_UNLOCK(f);
5709 goto end;
5710 }
5711 FLOWLOCK_UNLOCK(f);
5712 }
5713
5714 htp_state = f->alstate;
5715 if (htp_state == NULL) {
5716 printf("no http state: ");
5717 goto end;
5718 }
5719
5720 uint8_t ref1[] = "/blah/";
5721 size_t reflen = sizeof(ref1) - 1;
5722
5723 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5724 if (tx == NULL)
5725 goto end;
5726 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5727 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5728 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5729 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5730 (uintmax_t)reflen,
5731 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5732 goto end;
5733 }
5734
5735 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5736 bstr_len(tx_ud->request_uri_normalized)) != 0)
5737 {
5738 printf("normalized uri \"");
5739 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5740 printf("\" != \"");
5741 PrintRawUriFp(stdout, ref1, reflen);
5742 printf("\": ");
5743 goto end;
5744 }
5745 }
5746
5747 result = 1;
5748
5749 end:
5750 if (alp_tctx != NULL)
5751 AppLayerParserThreadCtxFree(alp_tctx);
5752 HTPFreeConfig();
5753 ConfDeInit();
5754 ConfRestoreContextBackup();
5755 HtpConfigRestoreBackup();
5756
5757 StreamTcpFreeConfig(TRUE);
5758 UTHFreeFlow(f);
5759 return result;
5760 }
5761
5762 /** \test Test 'proxy' URI normalization. Ticket 1008
5763 */
5764 static int HTPParserDecodingTest09(void)
5765 {
5766 int result = 0;
5767 Flow *f = NULL;
5768 uint8_t httpbuf1[] =
5769 "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5770 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5771 TcpSession ssn;
5772 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5773
5774 HtpState *htp_state = NULL;
5775 int r = 0;
5776 char input[] = "\
5777 %YAML 1.1\n\
5778 ---\n\
5779 libhtp:\n\
5780 \n\
5781 default-config:\n\
5782 personality: IDS\n\
5783 uri-include-all: true\n\
5784 ";
5785
5786 ConfCreateContextBackup();
5787 ConfInit();
5788 HtpConfigCreateBackup();
5789 ConfYamlLoadString(input, strlen(input));
5790 HTPConfigure();
5791 const char *addr = "4.3.2.1";
5792 memset(&ssn, 0, sizeof(ssn));
5793
5794 f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5795 if (f == NULL)
5796 goto end;
5797 f->protoctx = &ssn;
5798 f->proto = IPPROTO_TCP;
5799 f->alproto = ALPROTO_HTTP;
5800
5801 StreamTcpInitConfig(TRUE);
5802
5803 uint32_t u;
5804 for (u = 0; u < httplen1; u++) {
5805 uint8_t flags = 0;
5806
5807 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5808 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5809 else flags = STREAM_TOSERVER;
5810
5811 FLOWLOCK_WRLOCK(f);
5812 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5813 &httpbuf1[u], 1);
5814 if (r != 0) {
5815 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5816 " 0: ", u, r);
5817 FLOWLOCK_UNLOCK(f);
5818 goto end;
5819 }
5820 FLOWLOCK_UNLOCK(f);
5821 }
5822
5823 htp_state = f->alstate;
5824 if (htp_state == NULL) {
5825 printf("no http state: ");
5826 goto end;
5827 }
5828
5829 uint8_t ref1[] = "http://suricata-ids.org/blah/";
5830 size_t reflen = sizeof(ref1) - 1;
5831
5832 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5833 if (tx == NULL)
5834 goto end;
5835 HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5836 if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5837 if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5838 printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5839 (uintmax_t)reflen,
5840 (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5841 goto end;
5842 }
5843
5844 if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5845 bstr_len(tx_ud->request_uri_normalized)) != 0)
5846 {
5847 printf("normalized uri \"");
5848 PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5849 printf("\" != \"");
5850 PrintRawUriFp(stdout, ref1, reflen);
5851 printf("\": ");
5852 goto end;
5853 }
5854 }
5855
5856 result = 1;
5857
5858 end:
5859 if (alp_tctx != NULL)
5860 AppLayerParserThreadCtxFree(alp_tctx);
5861 HTPFreeConfig();
5862 ConfDeInit();
5863 ConfRestoreContextBackup();
5864 HtpConfigRestoreBackup();
5865
5866 StreamTcpFreeConfig(TRUE);
5867 UTHFreeFlow(f);
5868 return result;
5869 }
5870
5871 /** \test BG box crash -- chunks are messed up. Observed for real. */
5872 static int HTPBodyReassemblyTest01(void)
5873 {
5874 int result = 0;
5875 HtpTxUserData htud;
5876 memset(&htud, 0x00, sizeof(htud));
5877 HtpState hstate;
5878 memset(&hstate, 0x00, sizeof(hstate));
5879 Flow flow;
5880 memset(&flow, 0x00, sizeof(flow));
5881 AppLayerParserState *parser = AppLayerParserStateAlloc();
5882 htp_tx_t tx;
5883 memset(&tx, 0, sizeof(tx));
5884
5885 hstate.f = &flow;
5886 flow.alparser = parser;
5887
5888 uint8_t chunk1[] = "--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
5889 uint8_t chunk2[] = "POST /uri HTTP/1.1\r\nHost: hostname.com\r\nKeep-Alive: 115\r\nAccept-Charset: utf-8\r\nUser-Agent: Mozilla/5.0 (X11; Linux i686; rv:9.0.1) Gecko/20100101 Firefox/9.0.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nConnection: keep-alive\r\nContent-length: 68102\r\nReferer: http://otherhost.com\r\nAccept-Encoding: gzip\r\nContent-Type: multipart/form-data; boundary=e5a320f21416a02493a0a6f561b1c494\r\nCookie: blah\r\nAccept-Language: us\r\n\r\n--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
5890
5891 int r = HtpBodyAppendChunk(NULL, &htud.request_body, chunk1, sizeof(chunk1)-1);
5892 BUG_ON(r != 0);
5893 r = HtpBodyAppendChunk(NULL, &htud.request_body, chunk2, sizeof(chunk2)-1);
5894 BUG_ON(r != 0);
5895
5896 const uint8_t *chunks_buffer = NULL;
5897 uint32_t chunks_buffer_len = 0;
5898
5899 HtpRequestBodyReassemble(&htud, &chunks_buffer, &chunks_buffer_len);
5900 if (chunks_buffer == NULL) {
5901 goto end;
5902 }
5903 #ifdef PRINT
5904 printf("REASSCHUNK START: \n");
5905 PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
5906 printf("REASSCHUNK END: \n");
5907 #endif
5908
5909 HtpRequestBodyHandleMultipart(&hstate, &htud, &tx, chunks_buffer, chunks_buffer_len);
5910
5911 if (htud.request_body.content_len_so_far != 669) {
5912 printf("htud.request_body.content_len_so_far %"PRIu64": ", htud.request_body.content_len_so_far);
5913 goto end;
5914 }
5915
5916 if (hstate.files_ts != NULL)
5917 goto end;
5918
5919 result = 1;
5920 end:
5921 return result;
5922 }
5923
5924 /** \test BG crash */
5925 static int HTPSegvTest01(void)
5926 {
5927 int result = 0;
5928 Flow *f = NULL;
5929 uint8_t httpbuf1[] = "POST /uri HTTP/1.1\r\nHost: hostname.com\r\nKeep-Alive: 115\r\nAccept-Charset: utf-8\r\nUser-Agent: Mozilla/5.0 (X11; Linux i686; rv:9.0.1) Gecko/20100101 Firefox/9.0.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nConnection: keep-alive\r\nContent-length: 68102\r\nReferer: http://otherhost.com\r\nAccept-Encoding: gzip\r\nContent-Type: multipart/form-data; boundary=e5a320f21416a02493a0a6f561b1c494\r\nCookie: blah\r\nAccept-Language: us\r\n\r\n--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
5930 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5931 char input[] = "\
5932 %YAML 1.1\n\
5933 ---\n\
5934 libhtp:\n\
5935 \n\
5936 default-config:\n\
5937 personality: IDS\n\
5938 double-decode-path: no\n\
5939 double-decode-query: no\n\
5940 request-body-limit: 0\n\
5941 response-body-limit: 0\n\
5942 ";
5943
5944 ConfCreateContextBackup();
5945 ConfInit();
5946 HtpConfigCreateBackup();
5947 ConfYamlLoadString(input, strlen(input));
5948 HTPConfigure();
5949
5950 TcpSession ssn;
5951 HtpState *http_state = NULL;
5952 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
5953
5954 memset(&ssn, 0, sizeof(ssn));
5955
5956 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
5957 if (f == NULL)
5958 goto end;
5959 f->protoctx = &ssn;
5960 f->proto = IPPROTO_TCP;
5961 f->alproto = ALPROTO_HTTP;
5962
5963 StreamTcpInitConfig(TRUE);
5964
5965 SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
5966 FLOWLOCK_WRLOCK(f);
5967 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
5968 STREAM_TOSERVER | STREAM_START, httpbuf1,
5969 httplen1);
5970 if (r != 0) {
5971 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
5972 FLOWLOCK_UNLOCK(f);
5973 goto end;
5974 }
5975 FLOWLOCK_UNLOCK(f);
5976 SCLogDebug("\n>>>> processing chunk 1 again <<<<\n");
5977 FLOWLOCK_WRLOCK(f);
5978 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
5979 httpbuf1, httplen1);
5980 if (r != 0) {
5981 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
5982 FLOWLOCK_UNLOCK(f);
5983 goto end;
5984 }
5985 FLOWLOCK_UNLOCK(f);
5986
5987 http_state = f->alstate;
5988 if (http_state == NULL) {
5989 printf("no http state: ");
5990 goto end;
5991 }
5992
5993 FLOWLOCK_WRLOCK(f);
5994 AppLayerDecoderEvents *decoder_events = AppLayerParserGetDecoderEvents(f->alparser);
5995 if (decoder_events != NULL) {
5996 printf("app events: ");
5997 FLOWLOCK_UNLOCK(f);
5998 goto end;
5999 }
6000 FLOWLOCK_UNLOCK(f);
6001 result = 1;
6002 end:
6003 if (alp_tctx != NULL)
6004 AppLayerParserThreadCtxFree(alp_tctx);
6005 HTPFreeConfig();
6006 ConfDeInit();
6007 ConfRestoreContextBackup();
6008 HtpConfigRestoreBackup();
6009 StreamTcpFreeConfig(TRUE);
6010 UTHFreeFlow(f);
6011 return result;
6012 }
6013
6014 /** \test Test really long request, this should result in HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6015 static int HTPParserTest14(void)
6016 {
6017 size_t len = 18887;
6018 TcpSession ssn;
6019 char input[] = "\
6020 %YAML 1.1\n\
6021 ---\n\
6022 libhtp:\n\
6023 \n\
6024 default-config:\n\
6025 personality: IDS\n\
6026 double-decode-path: no\n\
6027 double-decode-query: no\n\
6028 request-body-limit: 0\n\
6029 response-body-limit: 0\n\
6030 ";
6031 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6032 FAIL_IF_NULL(alp_tctx);
6033
6034 memset(&ssn, 0, sizeof(ssn));
6035
6036 ConfCreateContextBackup();
6037 ConfInit();
6038 HtpConfigCreateBackup();
6039 ConfYamlLoadString(input, strlen(input));
6040 HTPConfigure();
6041
6042 char *httpbuf = SCMalloc(len);
6043 FAIL_IF_NULL(httpbuf);
6044 memset(httpbuf, 0x00, len);
6045
6046 /* create the request with a longer than 18k cookie */
6047 strlcpy(httpbuf, "GET /blah/ HTTP/1.1\r\n"
6048 "Host: myhost.lan\r\n"
6049 "Connection: keep-alive\r\n"
6050 "Accept: */*\r\n"
6051 "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36\r\n"
6052 "Referer: http://blah.lan/\r\n"
6053 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6054 "Cookie: ", len);
6055 size_t o = strlen(httpbuf);
6056 for ( ; o < len - 4; o++) {
6057 httpbuf[o] = 'A';
6058 }
6059 httpbuf[len - 4] = '\r';
6060 httpbuf[len - 3] = '\n';
6061 httpbuf[len - 2] = '\r';
6062 httpbuf[len - 1] = '\n';
6063
6064 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6065 FAIL_IF_NULL(f);
6066 f->protoctx = &ssn;
6067 f->alproto = ALPROTO_HTTP;
6068 f->proto = IPPROTO_TCP;
6069
6070 StreamTcpInitConfig(TRUE);
6071
6072 uint32_t u;
6073 for (u = 0; u < len; u++) {
6074 uint8_t flags = 0;
6075
6076 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
6077 else if (u == (len - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
6078 else flags = STREAM_TOSERVER;
6079
6080 (void)AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
6081 (uint8_t *)&httpbuf[u], 1);
6082 }
6083 HtpState *htp_state = f->alstate;
6084 FAIL_IF_NULL(htp_state);
6085
6086 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
6087 FAIL_IF_NULL(tx);
6088 FAIL_IF(tx->request_method_number != HTP_M_GET);
6089 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
6090
6091 AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
6092 FAIL_IF_NULL(decoder_events);
6093
6094 FAIL_IF(decoder_events->events[0] != HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG);
6095
6096 AppLayerParserThreadCtxFree(alp_tctx);
6097 StreamTcpFreeConfig(TRUE);
6098 UTHFreeFlow(f);
6099 SCFree(httpbuf);
6100 HTPFreeConfig();
6101 ConfDeInit();
6102 ConfRestoreContextBackup();
6103 HtpConfigRestoreBackup();
6104 PASS;
6105 }
6106
6107 /** \test Test really long request (same as HTPParserTest14), now with config
6108 * update to allow it */
6109 static int HTPParserTest15(void)
6110 {
6111 int result = 0;
6112 Flow *f = NULL;
6113 char *httpbuf = NULL;
6114 size_t len = 18887;
6115 TcpSession ssn;
6116 HtpState *htp_state = NULL;
6117 int r = 0;
6118 char input[] = "\
6119 %YAML 1.1\n\
6120 ---\n\
6121 libhtp:\n\
6122 \n\
6123 default-config:\n\
6124 personality: IDS\n\
6125 double-decode-path: no\n\
6126 double-decode-query: no\n\
6127 request-body-limit: 0\n\
6128 response-body-limit: 0\n\
6129 meta-field-limit: 20000\n\
6130 ";
6131 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6132
6133 memset(&ssn, 0, sizeof(ssn));
6134
6135 ConfCreateContextBackup();
6136 ConfInit();
6137 HtpConfigCreateBackup();
6138 ConfYamlLoadString(input, strlen(input));
6139 HTPConfigure();
6140
6141 httpbuf = SCMalloc(len);
6142 if (unlikely(httpbuf == NULL))
6143 goto end;
6144 memset(httpbuf, 0x00, len);
6145
6146 /* create the request with a longer than 18k cookie */
6147 strlcpy(httpbuf, "GET /blah/ HTTP/1.1\r\n"
6148 "Host: myhost.lan\r\n"
6149 "Connection: keep-alive\r\n"
6150 "Accept: */*\r\n"
6151 "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36\r\n"
6152 "Referer: http://blah.lan/\r\n"
6153 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6154 "Cookie: ", len);
6155 size_t o = strlen(httpbuf);
6156 for ( ; o < len - 4; o++) {
6157 httpbuf[o] = 'A';
6158 }
6159 httpbuf[len - 4] = '\r';
6160 httpbuf[len - 3] = '\n';
6161 httpbuf[len - 2] = '\r';
6162 httpbuf[len - 1] = '\n';
6163
6164 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6165 if (f == NULL)
6166 goto end;
6167 f->protoctx = &ssn;
6168 f->proto = IPPROTO_TCP;
6169 f->alproto = ALPROTO_HTTP;
6170
6171 StreamTcpInitConfig(TRUE);
6172
6173 uint32_t u;
6174 for (u = 0; u < len; u++) {
6175 uint8_t flags = 0;
6176
6177 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
6178 else if (u == (len - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
6179 else flags = STREAM_TOSERVER;
6180
6181 FLOWLOCK_WRLOCK(f);
6182 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
6183 (uint8_t *)&httpbuf[u], 1);
6184 if (r != 0) {
6185 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
6186 " 0: ", u, r);
6187 FLOWLOCK_UNLOCK(f);
6188 goto end;
6189 }
6190 FLOWLOCK_UNLOCK(f);
6191 }
6192 htp_state = f->alstate;
6193 if (htp_state == NULL) {
6194 printf("no http state: ");
6195 goto end;
6196 }
6197
6198 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
6199 if (tx == NULL || tx->request_method_number != HTP_M_GET || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6200 {
6201 printf("expected method M_GET and got %s: , expected protocol "
6202 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
6203 bstr_util_strdup_to_c(tx->request_protocol));
6204 goto end;
6205 }
6206
6207 FLOWLOCK_WRLOCK(f);
6208 AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
6209 if (decoder_events != NULL) {
6210 printf("app events: ");
6211 FLOWLOCK_UNLOCK(f);
6212 goto end;
6213 }
6214 FLOWLOCK_UNLOCK(f);
6215
6216 result = 1;
6217 end:
6218 if (alp_tctx != NULL)
6219 AppLayerParserThreadCtxFree(alp_tctx);
6220 StreamTcpFreeConfig(TRUE);
6221 UTHFreeFlow(f);
6222 if (httpbuf != NULL)
6223 SCFree(httpbuf);
6224 HTPFreeConfig();
6225 ConfDeInit();
6226 ConfRestoreContextBackup();
6227 HtpConfigRestoreBackup();
6228 return result;
6229 }
6230
6231 /** \test Test unusual delims in request line HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6232 static int HTPParserTest16(void)
6233 {
6234 int result = 0;
6235 Flow *f = NULL;
6236 TcpSession ssn;
6237 HtpState *htp_state = NULL;
6238 int r = 0;
6239 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6240
6241 memset(&ssn, 0, sizeof(ssn));
6242
6243 uint8_t httpbuf[] = "GET\f/blah/\fHTTP/1.1\r\n"
6244 "Host: myhost.lan\r\n"
6245 "Connection: keep-alive\r\n"
6246 "Accept: */*\r\n"
6247 "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36\r\n"
6248 "Referer: http://blah.lan/\r\n"
6249 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6250 "Cookie: blah\r\n\r\n";
6251 size_t len = sizeof(httpbuf) - 1;
6252
6253 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6254 if (f == NULL)
6255 goto end;
6256 f->protoctx = &ssn;
6257 f->proto = IPPROTO_TCP;
6258 f->alproto = ALPROTO_HTTP;
6259
6260 StreamTcpInitConfig(TRUE);
6261
6262 uint8_t flags = STREAM_TOSERVER|STREAM_START|STREAM_EOF;
6263
6264 FLOWLOCK_WRLOCK(f);
6265 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
6266 (uint8_t *)httpbuf, len);
6267 if (r != 0) {
6268 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
6269 FLOWLOCK_UNLOCK(f);
6270 goto end;
6271 }
6272 FLOWLOCK_UNLOCK(f);
6273
6274 htp_state = f->alstate;
6275 if (htp_state == NULL) {
6276 printf("no http state: ");
6277 goto end;
6278 }
6279
6280 htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
6281 if (tx == NULL || tx->request_method_number != HTP_M_GET || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6282 {
6283 printf("expected method M_GET and got %s: , expected protocol "
6284 "HTTP/1.1 and got %s \n", tx ? bstr_util_strdup_to_c(tx->request_method) : "tx null",
6285 tx ? bstr_util_strdup_to_c(tx->request_protocol) : "tx null");
6286 goto end;
6287 }
6288
6289 FLOWLOCK_WRLOCK(f);
6290 AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(IPPROTO_TCP, ALPROTO_HTTP,f->alstate, 0);
6291 if (decoder_events == NULL) {
6292 printf("no app events: ");
6293 FLOWLOCK_UNLOCK(f);
6294 goto end;
6295 }
6296 FLOWLOCK_UNLOCK(f);
6297
6298 if (decoder_events->events[0] != HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT) {
6299 printf("HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT not set: ");
6300 goto end;
6301 }
6302
6303 if (decoder_events->events[1] != HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT) {
6304 printf("HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT not set: ");
6305 goto end;
6306 }
6307
6308 result = 1;
6309 end:
6310 if (alp_tctx != NULL)
6311 AppLayerParserThreadCtxFree(alp_tctx);
6312 StreamTcpFreeConfig(TRUE);
6313 UTHFreeFlow(f);
6314 return result;
6315 }
6316
6317 /** \test CONNECT with plain text HTTP being tunneled */
6318 static int HTPParserTest17(void)
6319 {
6320 int result = 0;
6321 Flow *f = NULL;
6322 HtpState *http_state = NULL;
6323 /* CONNECT setup */
6324 uint8_t httpbuf1[] = "CONNECT abc:443 HTTP/1.1\r\nUser-Agent: Victor/1.0\r\n\r\n";
6325 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6326 uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
6327 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6328 /* plain text HTTP */
6329 uint8_t httpbuf3[] = "GET / HTTP/1.1\r\nUser-Agent: Victor/1.0\r\n\r\n";
6330 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
6331 uint8_t httpbuf4[] = "HTTP/1.1 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
6332 uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
6333 TcpSession ssn;
6334 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6335
6336 memset(&ssn, 0, sizeof(ssn));
6337
6338 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6339 if (f == NULL)
6340 goto end;
6341 f->protoctx = &ssn;
6342 f->proto = IPPROTO_TCP;
6343 f->alproto = ALPROTO_HTTP;
6344
6345 StreamTcpInitConfig(TRUE);
6346
6347 FLOWLOCK_WRLOCK(f);
6348 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6349 STREAM_TOSERVER | STREAM_START, httpbuf1,
6350 httplen1);
6351 if (r != 0) {
6352 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
6353 FLOWLOCK_UNLOCK(f);
6354 goto end;
6355 }
6356
6357 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6358 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6359 httplen2);
6360 if (r != 0) {
6361 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
6362 FLOWLOCK_UNLOCK(f);
6363 goto end;
6364 }
6365 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
6366 httpbuf3, httplen3);
6367 if (r != 0) {
6368 printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
6369 FLOWLOCK_UNLOCK(f);
6370 goto end;
6371 }
6372
6373 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT,
6374 httpbuf4, httplen4);
6375 if (r != 0) {
6376 printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
6377 FLOWLOCK_UNLOCK(f);
6378 goto end;
6379 }
6380
6381 FLOWLOCK_UNLOCK(f);
6382
6383 http_state = f->alstate;
6384 if (http_state == NULL) {
6385 printf("no http state: ");
6386 goto end;
6387 }
6388
6389 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6390 if (tx == NULL)
6391 goto end;
6392 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6393 if (tx->request_method_number != HTP_M_CONNECT ||
6394 h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6395 {
6396 printf("expected method M_POST and got %s: , expected protocol "
6397 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
6398 bstr_util_strdup_to_c(tx->request_protocol));
6399 goto end;
6400 }
6401
6402 if (tx->response_status_number != 200) {
6403 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
6404 "HTTP/1.1 and got %s \n", tx->response_status_number,
6405 bstr_util_strdup_to_c(tx->response_message),
6406 bstr_util_strdup_to_c(tx->response_protocol));
6407 goto end;
6408 }
6409
6410 tx = HTPStateGetTx(http_state, 1);
6411 if (tx == NULL)
6412 goto end;
6413 h = htp_table_get_index(tx->request_headers, 0, NULL);
6414 if (tx->request_method_number != HTP_M_GET ||
6415 h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6416 {
6417 printf("expected method M_GET and got %s: , expected protocol "
6418 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
6419 bstr_util_strdup_to_c(tx->request_protocol));
6420 goto end;
6421 }
6422
6423 if (tx->response_status_number != 200) {
6424 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
6425 "HTTP/1.1 and got %s \n", tx->response_status_number,
6426 bstr_util_strdup_to_c(tx->response_message),
6427 bstr_util_strdup_to_c(tx->response_protocol));
6428 goto end;
6429 }
6430 result = 1;
6431 end:
6432 if (alp_tctx != NULL)
6433 AppLayerParserThreadCtxFree(alp_tctx);
6434 StreamTcpFreeConfig(TRUE);
6435 UTHFreeFlow(f);
6436 return result;
6437 }
6438
6439 /** \test CONNECT with plain text HTTP being tunneled */
6440 static int HTPParserTest18(void)
6441 {
6442 int result = 0;
6443 Flow *f = NULL;
6444 HtpState *http_state = NULL;
6445 /* CONNECT setup */
6446 uint8_t httpbuf1[] = "CONNECT abc:443 HTTP/1.1\r\nUser-Agent: Victor/1.0\r\n\r\n";
6447 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6448 uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
6449 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6450 /* plain text HTTP */
6451 uint8_t httpbuf3[] = "GE";
6452 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
6453 uint8_t httpbuf4[] = "T / HTTP/1.1\r\nUser-Agent: Victor/1.0\r\n\r\n";
6454 uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
6455 uint8_t httpbuf5[] = "HTTP/1.1 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
6456 uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
6457 TcpSession ssn;
6458 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6459
6460 memset(&ssn, 0, sizeof(ssn));
6461
6462 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6463 if (f == NULL)
6464 goto end;
6465 f->protoctx = &ssn;
6466 f->proto = IPPROTO_TCP;
6467 f->alproto = ALPROTO_HTTP;
6468
6469 StreamTcpInitConfig(TRUE);
6470
6471 FLOWLOCK_WRLOCK(f);
6472 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6473 STREAM_TOSERVER | STREAM_START, httpbuf1,
6474 httplen1);
6475 if (r != 0) {
6476 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
6477 FLOWLOCK_UNLOCK(f);
6478 goto end;
6479 }
6480
6481 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6482 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6483 httplen2);
6484 if (r != 0) {
6485 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
6486 FLOWLOCK_UNLOCK(f);
6487 goto end;
6488 }
6489 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
6490 httpbuf3, httplen3);
6491 if (r != 0) {
6492 printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
6493 FLOWLOCK_UNLOCK(f);
6494 goto end;
6495 }
6496 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
6497 httpbuf4, httplen4);
6498 if (r != 0) {
6499 printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
6500 FLOWLOCK_UNLOCK(f);
6501 goto end;
6502 }
6503
6504
6505 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT,
6506 httpbuf5, httplen5);
6507 if (r != 0) {
6508 printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
6509 FLOWLOCK_UNLOCK(f);
6510 goto end;
6511 }
6512
6513 FLOWLOCK_UNLOCK(f);
6514
6515 http_state = f->alstate;
6516 if (http_state == NULL) {
6517 printf("no http state: ");
6518 goto end;
6519 }
6520
6521 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6522 if (tx == NULL)
6523 goto end;
6524 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6525 if (tx->request_method_number != HTP_M_CONNECT ||
6526 h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6527 {
6528 printf("expected method M_POST and got %s: , expected protocol "
6529 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
6530 bstr_util_strdup_to_c(tx->request_protocol));
6531 goto end;
6532 }
6533
6534 if (tx->response_status_number != 200) {
6535 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
6536 "HTTP/1.1 and got %s \n", tx->response_status_number,
6537 bstr_util_strdup_to_c(tx->response_message),
6538 bstr_util_strdup_to_c(tx->response_protocol));
6539 goto end;
6540 }
6541
6542 tx = HTPStateGetTx(http_state, 1);
6543 if (tx == NULL)
6544 goto end;
6545 h = htp_table_get_index(tx->request_headers, 0, NULL);
6546 if (tx->request_method_number != HTP_M_GET ||
6547 h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6548 {
6549 printf("expected method M_GET and got %s: , expected protocol "
6550 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
6551 bstr_util_strdup_to_c(tx->request_protocol));
6552 goto end;
6553 }
6554
6555 if (tx->response_status_number != 200) {
6556 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
6557 "HTTP/1.1 and got %s \n", tx->response_status_number,
6558 bstr_util_strdup_to_c(tx->response_message),
6559 bstr_util_strdup_to_c(tx->response_protocol));
6560 goto end;
6561 }
6562 result = 1;
6563 end:
6564 if (alp_tctx != NULL)
6565 AppLayerParserThreadCtxFree(alp_tctx);
6566 StreamTcpFreeConfig(TRUE);
6567 UTHFreeFlow(f);
6568 return result;
6569 }
6570
6571 /** \test CONNECT with TLS content (start of it at least) */
6572 static int HTPParserTest19(void)
6573 {
6574 int result = 0;
6575 Flow *f = NULL;
6576 HtpState *http_state = NULL;
6577 /* CONNECT setup */
6578 uint8_t httpbuf1[] = "CONNECT abc:443 HTTP/1.1\r\nUser-Agent: Victor/1.0\r\n\r\n";
6579 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6580 uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
6581 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6582 /* start of TLS/SSL */
6583 uint8_t httpbuf3[] = "\x16\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
6584 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
6585 TcpSession ssn;
6586 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6587
6588 memset(&ssn, 0, sizeof(ssn));
6589
6590 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6591 if (f == NULL)
6592 goto end;
6593 f->protoctx = &ssn;
6594 f->proto = IPPROTO_TCP;
6595 f->alproto = ALPROTO_HTTP;
6596
6597 StreamTcpInitConfig(TRUE);
6598
6599 FLOWLOCK_WRLOCK(f);
6600 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6601 STREAM_TOSERVER | STREAM_START, httpbuf1,
6602 httplen1);
6603 if (r != 0) {
6604 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
6605 FLOWLOCK_UNLOCK(f);
6606 goto end;
6607 }
6608
6609 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6610 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6611 httplen2);
6612 if (r != 0) {
6613 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
6614 FLOWLOCK_UNLOCK(f);
6615 goto end;
6616 }
6617 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
6618 httpbuf3, httplen3);
6619 if (r != 0) {
6620 printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
6621 FLOWLOCK_UNLOCK(f);
6622 goto end;
6623 }
6624
6625 FLOWLOCK_UNLOCK(f);
6626
6627 http_state = f->alstate;
6628 if (http_state == NULL) {
6629 printf("no http state: ");
6630 goto end;
6631 }
6632
6633 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6634 if (tx == NULL)
6635 goto end;
6636 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6637 if (tx->request_method_number != HTP_M_CONNECT ||
6638 h == NULL || tx->request_protocol_number != HTP_PROTOCOL_1_1)
6639 {
6640 printf("expected method M_POST and got %s: , expected protocol "
6641 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
6642 bstr_util_strdup_to_c(tx->request_protocol));
6643 goto end;
6644 }
6645
6646 if (tx->response_status_number != 200) {
6647 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
6648 "HTTP/1.1 and got %s \n", tx->response_status_number,
6649 bstr_util_strdup_to_c(tx->response_message),
6650 bstr_util_strdup_to_c(tx->response_protocol));
6651 goto end;
6652 }
6653
6654 /* no new tx should have been set up for the tunneled data */
6655 tx = HTPStateGetTx(http_state, 1);
6656 if (tx != NULL)
6657 goto end;
6658
6659 result = 1;
6660 end:
6661 if (alp_tctx != NULL)
6662 AppLayerParserThreadCtxFree(alp_tctx);
6663 StreamTcpFreeConfig(TRUE);
6664 UTHFreeFlow(f);
6665 return result;
6666 }
6667
6668 /** \test Test response not HTTP
6669 */
6670 static int HTPParserTest20(void)
6671 {
6672 Flow *f = NULL;
6673 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6674 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6675 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6676 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6677 uint8_t httpbuf2[] = "NOTHTTP\r\nSOMEOTHERDATA";
6678 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6679 uint8_t httpbuf3[] = "STILLNOTHTTP\r\nSOMEMOREOTHERDATA";
6680 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
6681 TcpSession ssn;
6682 HtpState *http_state = NULL;
6683 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6684 FAIL_IF_NULL(alp_tctx);
6685
6686 memset(&ssn, 0, sizeof(ssn));
6687
6688 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6689 FAIL_IF_NULL(f);
6690 f->protoctx = &ssn;
6691 f->proto = IPPROTO_TCP;
6692 f->alproto = ALPROTO_HTTP;
6693
6694 StreamTcpInitConfig(TRUE);
6695
6696 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6697 STREAM_TOSERVER | STREAM_START, httpbuf1,
6698 httplen1);
6699 FAIL_IF(r != 0);
6700
6701 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6702 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6703 httplen2);
6704 FAIL_IF(r != 0);
6705
6706 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6707 STREAM_TOCLIENT | STREAM_START, httpbuf3,
6708 httplen3);
6709 FAIL_IF(r != 0);
6710
6711 http_state = f->alstate;
6712 FAIL_IF_NULL(http_state);
6713 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6714 FAIL_IF_NULL(tx);
6715 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6716 FAIL_IF_NULL(h);
6717
6718 FAIL_IF(tx->request_method_number != HTP_M_GET);
6719 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
6720
6721 FAIL_IF(tx->response_status_number != 0);
6722 FAIL_IF(tx->response_protocol_number != -1);
6723
6724 AppLayerParserThreadCtxFree(alp_tctx);
6725 StreamTcpFreeConfig(TRUE);
6726 UTHFreeFlow(f);
6727 PASS;
6728 }
6729
6730 /** \test Test response not HTTP
6731 */
6732 static int HTPParserTest21(void)
6733 {
6734 Flow *f = NULL;
6735 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6736 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6737 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6738 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6739 uint8_t httpbuf2[] = "999 NOTHTTP REALLY\r\nSOMEOTHERDATA\r\n";
6740 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6741 uint8_t httpbuf3[] = "STILLNOTHTTP\r\nSOMEMOREOTHERDATA";
6742 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
6743 TcpSession ssn;
6744 HtpState *http_state = NULL;
6745 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6746 FAIL_IF_NULL(alp_tctx);
6747
6748 memset(&ssn, 0, sizeof(ssn));
6749
6750 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6751 FAIL_IF_NULL(f);
6752 f->protoctx = &ssn;
6753 f->proto = IPPROTO_TCP;
6754 f->alproto = ALPROTO_HTTP;
6755
6756 StreamTcpInitConfig(TRUE);
6757
6758 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6759 STREAM_TOSERVER | STREAM_START, httpbuf1,
6760 httplen1);
6761 FAIL_IF(r != 0);
6762
6763 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6764 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6765 httplen2);
6766 FAIL_IF(r != 0);
6767
6768 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6769 STREAM_TOCLIENT | STREAM_START, httpbuf3,
6770 httplen3);
6771 FAIL_IF(r != 0);
6772
6773 http_state = f->alstate;
6774 FAIL_IF_NULL(http_state);
6775 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6776 FAIL_IF_NULL(tx);
6777 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6778 FAIL_IF_NULL(h);
6779
6780 FAIL_IF(tx->request_method_number != HTP_M_GET);
6781 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
6782
6783 FAIL_IF(tx->response_status_number != 0);
6784 FAIL_IF(tx->response_protocol_number != -1);
6785
6786 AppLayerParserThreadCtxFree(alp_tctx);
6787 StreamTcpFreeConfig(TRUE);
6788 UTHFreeFlow(f);
6789 PASS;
6790 }
6791
6792 /** \test Test response not HTTP
6793 */
6794 static int HTPParserTest22(void)
6795 {
6796 Flow *f = NULL;
6797 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6798 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6799 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6800 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6801 uint8_t httpbuf2[] = "\r\n0000=0000000/ASDF3_31.zip, 456723\r\n"
6802 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6803 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6804 TcpSession ssn;
6805 HtpState *http_state = NULL;
6806 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6807 FAIL_IF_NULL(alp_tctx);
6808
6809 memset(&ssn, 0, sizeof(ssn));
6810
6811 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6812 FAIL_IF_NULL(f);
6813 f->protoctx = &ssn;
6814 f->proto = IPPROTO_TCP;
6815 f->alproto = ALPROTO_HTTP;
6816
6817 StreamTcpInitConfig(TRUE);
6818
6819 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6820 STREAM_TOSERVER | STREAM_START, httpbuf1,
6821 httplen1);
6822 FAIL_IF(r != 0);
6823
6824 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6825 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6826 httplen2);
6827 FAIL_IF(r != 0);
6828
6829 http_state = f->alstate;
6830 FAIL_IF_NULL(http_state);
6831 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6832 FAIL_IF_NULL(tx);
6833 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6834 FAIL_IF_NULL(h);
6835
6836 FAIL_IF(tx->request_method_number != HTP_M_GET);
6837 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
6838
6839 FAIL_IF(tx->response_status_number != -0);
6840 FAIL_IF(tx->response_protocol_number != -1);
6841
6842 AppLayerParserThreadCtxFree(alp_tctx);
6843 StreamTcpFreeConfig(TRUE);
6844 UTHFreeFlow(f);
6845 PASS;
6846 }
6847
6848 /** \test Test response not HTTP
6849 */
6850 static int HTPParserTest23(void)
6851 {
6852 Flow *f = NULL;
6853 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6854 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6855 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6856 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6857 uint8_t httpbuf2[] = "HTTP0000=0000000/ASDF3_31.zip, 456723\r\n"
6858 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6859 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6860 TcpSession ssn;
6861 HtpState *http_state = NULL;
6862 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6863 FAIL_IF_NULL(alp_tctx);
6864
6865 memset(&ssn, 0, sizeof(ssn));
6866
6867 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6868 FAIL_IF_NULL(f);
6869 f->protoctx = &ssn;
6870 f->proto = IPPROTO_TCP;
6871 f->alproto = ALPROTO_HTTP;
6872
6873 StreamTcpInitConfig(TRUE);
6874
6875 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6876 STREAM_TOSERVER | STREAM_START, httpbuf1,
6877 httplen1);
6878 FAIL_IF(r != 0);
6879
6880 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6881 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6882 httplen2);
6883 FAIL_IF(r != 0);
6884
6885 http_state = f->alstate;
6886 FAIL_IF_NULL(http_state);
6887 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6888 FAIL_IF_NULL(tx);
6889 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6890 FAIL_IF_NULL(h);
6891
6892 FAIL_IF(tx->request_method_number != HTP_M_GET);
6893 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
6894
6895 FAIL_IF(tx->response_status_number != -1);
6896 FAIL_IF(tx->response_protocol_number != -2);
6897
6898 AppLayerParserThreadCtxFree(alp_tctx);
6899 StreamTcpFreeConfig(TRUE);
6900 UTHFreeFlow(f);
6901 PASS;
6902 }
6903
6904 /** \test Test response not HTTP
6905 */
6906 static int HTPParserTest24(void)
6907 {
6908 Flow *f = NULL;
6909 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6910 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6911 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6912 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
6913 uint8_t httpbuf2[] = "HTTP/1.0 0000=0000000/ASDF3_31.zip, 456723\r\n"
6914 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6915 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
6916 TcpSession ssn;
6917 HtpState *http_state = NULL;
6918 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6919 FAIL_IF_NULL(alp_tctx);
6920
6921 memset(&ssn, 0, sizeof(ssn));
6922
6923 f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6924 FAIL_IF_NULL(f);
6925 f->protoctx = &ssn;
6926 f->proto = IPPROTO_TCP;
6927 f->alproto = ALPROTO_HTTP;
6928
6929 StreamTcpInitConfig(TRUE);
6930
6931 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6932 STREAM_TOSERVER | STREAM_START, httpbuf1,
6933 httplen1);
6934 FAIL_IF(r != 0);
6935
6936 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6937 STREAM_TOCLIENT | STREAM_START, httpbuf2,
6938 httplen2);
6939 FAIL_IF(r != 0);
6940
6941 http_state = f->alstate;
6942 FAIL_IF_NULL(http_state);
6943 htp_tx_t *tx = HTPStateGetTx(http_state, 0);
6944 FAIL_IF_NULL(tx);
6945 htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
6946 FAIL_IF_NULL(h);
6947
6948 FAIL_IF(tx->request_method_number != HTP_M_GET);
6949 FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
6950
6951 FAIL_IF(tx->response_status_number != -1);
6952 FAIL_IF(tx->response_protocol_number != HTP_PROTOCOL_1_0);
6953
6954 AppLayerParserThreadCtxFree(alp_tctx);
6955 StreamTcpFreeConfig(TRUE);
6956 UTHFreeFlow(f);
6957 PASS;
6958 }
6959
6960 /** \test multi transactions and cleanup */
6961 static int HTPParserTest25(void)
6962 {
6963 AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
6964 FAIL_IF_NULL(alp_tctx);
6965
6966 StreamTcpInitConfig(TRUE);
6967 TcpSession ssn;
6968 memset(&ssn, 0, sizeof(ssn));
6969
6970 Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
6971 FAIL_IF_NULL(f);
6972 f->protoctx = &ssn;
6973 f->proto = IPPROTO_TCP;
6974 f->alproto = ALPROTO_HTTP;
6975
6976 const char *str = "GET / HTTP/1.1\r\nHost: www.google.com\r\nUser-Agent: Suricata/1.0\r\n\r\n";
6977 int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6978 STREAM_TOSERVER | STREAM_START, (uint8_t *)str, strlen(str));
6979 FAIL_IF_NOT(r == 0);
6980 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6981 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
6982 FAIL_IF_NOT(r == 0);
6983 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6984 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
6985 FAIL_IF_NOT(r == 0);
6986 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6987 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
6988 FAIL_IF_NOT(r == 0);
6989 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6990 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
6991 FAIL_IF_NOT(r == 0);
6992 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6993 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
6994 FAIL_IF_NOT(r == 0);
6995 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6996 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
6997 FAIL_IF_NOT(r == 0);
6998 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
6999 STREAM_TOSERVER, (uint8_t *)str, strlen(str));
7000 FAIL_IF_NOT(r == 0);
7001
7002 str = "HTTP 1.1 200 OK\r\nServer: Suricata/1.0\r\nContent-Length: 8\r\n\r\nSuricata";
7003 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7004 STREAM_TOCLIENT | STREAM_START, (uint8_t *)str, strlen(str));
7005 FAIL_IF_NOT(r == 0);
7006 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7007 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7008 FAIL_IF_NOT(r == 0);
7009 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7010 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7011 FAIL_IF_NOT(r == 0);
7012 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7013 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7014 FAIL_IF_NOT(r == 0);
7015 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7016 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7017 FAIL_IF_NOT(r == 0);
7018 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7019 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7020 FAIL_IF_NOT(r == 0);
7021 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7022 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7023 FAIL_IF_NOT(r == 0);
7024 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7025 STREAM_TOCLIENT, (uint8_t *)str, strlen(str));
7026 FAIL_IF_NOT(r == 0);
7027
7028 AppLayerParserTransactionsCleanup(f);
7029
7030 uint64_t ret[4];
7031 UTHAppLayerParserStateGetIds(f->alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
7032 FAIL_IF_NOT(ret[0] == 8); // inspect_id[0]
7033 FAIL_IF_NOT(ret[1] == 8); // inspect_id[1]
7034 FAIL_IF_NOT(ret[2] == 8); // log_id
7035 FAIL_IF_NOT(ret[3] == 8); // min_id
7036
7037 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7038 STREAM_TOSERVER | STREAM_EOF, (uint8_t *)str, strlen(str));
7039 FAIL_IF_NOT(r == 0);
7040 AppLayerParserTransactionsCleanup(f);
7041
7042 UTHAppLayerParserStateGetIds(f->alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
7043 FAIL_IF_NOT(ret[0] == 8); // inspect_id[0] not updated by ..Cleanup() until full tx is done
7044 FAIL_IF_NOT(ret[1] == 8); // inspect_id[1]
7045 FAIL_IF_NOT(ret[2] == 8); // log_id
7046 FAIL_IF_NOT(ret[3] == 8); // min_id
7047
7048 r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
7049 STREAM_TOCLIENT | STREAM_EOF, (uint8_t *)str, strlen(str));
7050 FAIL_IF_NOT(r == 0);
7051 AppLayerParserTransactionsCleanup(f);
7052
7053 UTHAppLayerParserStateGetIds(f->alparser, &ret[0], &ret[1], &ret[2], &ret[3]);
7054 FAIL_IF_NOT(ret[0] == 9); // inspect_id[0]
7055 FAIL_IF_NOT(ret[1] == 9); // inspect_id[1]
7056 FAIL_IF_NOT(ret[2] == 9); // log_id
7057 FAIL_IF_NOT(ret[3] == 9); // min_id
7058
7059 HtpState *http_state = f->alstate;
7060 FAIL_IF_NULL(http_state);
7061
7062 AppLayerParserThreadCtxFree(alp_tctx);
7063 StreamTcpFreeConfig(TRUE);
7064 UTHFreeFlow(f);
7065
7066 PASS;
7067 }
7068
7069 #endif /* UNITTESTS */
7070
7071 /**
7072 * \brief Register the Unit tests for the HTTP protocol
7073 */
7074 void HTPParserRegisterTests(void)
7075 {
7076 #ifdef UNITTESTS
7077 UtRegisterTest("HTPParserTest01", HTPParserTest01);
7078 UtRegisterTest("HTPParserTest01a", HTPParserTest01a);
7079 UtRegisterTest("HTPParserTest01b", HTPParserTest01b);
7080 UtRegisterTest("HTPParserTest01c", HTPParserTest01c);
7081 UtRegisterTest("HTPParserTest02", HTPParserTest02);
7082 UtRegisterTest("HTPParserTest03", HTPParserTest03);
7083 UtRegisterTest("HTPParserTest04", HTPParserTest04);
7084 UtRegisterTest("HTPParserTest05", HTPParserTest05);
7085 UtRegisterTest("HTPParserTest06", HTPParserTest06);
7086 UtRegisterTest("HTPParserTest07", HTPParserTest07);
7087 UtRegisterTest("HTPParserTest08", HTPParserTest08);
7088 UtRegisterTest("HTPParserTest09", HTPParserTest09);
7089 UtRegisterTest("HTPParserTest10", HTPParserTest10);
7090 UtRegisterTest("HTPParserTest11", HTPParserTest11);
7091 UtRegisterTest("HTPParserTest12", HTPParserTest12);
7092 UtRegisterTest("HTPParserTest13", HTPParserTest13);
7093 UtRegisterTest("HTPParserConfigTest01", HTPParserConfigTest01);
7094 UtRegisterTest("HTPParserConfigTest02", HTPParserConfigTest02);
7095 UtRegisterTest("HTPParserConfigTest03", HTPParserConfigTest03);
7096 #if 0 /* disabled when we upgraded to libhtp 0.5.x */
7097 UtRegisterTest("HTPParserConfigTest04", HTPParserConfigTest04, 1);
7098 #endif
7099
7100 UtRegisterTest("HTPParserDecodingTest01", HTPParserDecodingTest01);
7101 UtRegisterTest("HTPParserDecodingTest02", HTPParserDecodingTest02);
7102 UtRegisterTest("HTPParserDecodingTest03", HTPParserDecodingTest03);
7103 UtRegisterTest("HTPParserDecodingTest04", HTPParserDecodingTest04);
7104 UtRegisterTest("HTPParserDecodingTest05", HTPParserDecodingTest05);
7105 UtRegisterTest("HTPParserDecodingTest06", HTPParserDecodingTest06);
7106 UtRegisterTest("HTPParserDecodingTest07", HTPParserDecodingTest07);
7107 UtRegisterTest("HTPParserDecodingTest08", HTPParserDecodingTest08);
7108 UtRegisterTest("HTPParserDecodingTest09", HTPParserDecodingTest09);
7109
7110 UtRegisterTest("HTPBodyReassemblyTest01", HTPBodyReassemblyTest01);
7111
7112 UtRegisterTest("HTPSegvTest01", HTPSegvTest01);
7113
7114 UtRegisterTest("HTPParserTest14", HTPParserTest14);
7115 UtRegisterTest("HTPParserTest15", HTPParserTest15);
7116 UtRegisterTest("HTPParserTest16", HTPParserTest16);
7117 UtRegisterTest("HTPParserTest17", HTPParserTest17);
7118 UtRegisterTest("HTPParserTest18", HTPParserTest18);
7119 UtRegisterTest("HTPParserTest19", HTPParserTest19);
7120 UtRegisterTest("HTPParserTest20", HTPParserTest20);
7121 UtRegisterTest("HTPParserTest21", HTPParserTest21);
7122 UtRegisterTest("HTPParserTest22", HTPParserTest22);
7123 UtRegisterTest("HTPParserTest23", HTPParserTest23);
7124 UtRegisterTest("HTPParserTest24", HTPParserTest24);
7125 UtRegisterTest("HTPParserTest25", HTPParserTest25);
7126
7127 HTPFileParserRegisterTests();
7128 HTPXFFParserRegisterTests();
7129 #endif /* UNITTESTS */
7130 }
7131
7132 /**
7133 * @}
7134 */