1 /* Copyright (C) 2007-2013 Open Information Security Foundation
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
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.
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
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>
33 * This file provides a HTTP protocol support for the engine using HTP library.
37 #include "suricata-common.h"
44 #include "util-print.h"
45 #include "util-pool.h"
46 #include "util-radix-tree.h"
47 #include "util-file.h"
49 #include "stream-tcp-private.h"
50 #include "stream-tcp-reassemble.h"
51 #include "stream-tcp.h"
54 #include "app-layer-protos.h"
55 #include "app-layer-parser.h"
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"
65 #include "util-debug.h"
66 #include "util-time.h"
67 #include "util-misc.h"
69 #include "util-unittest.h"
70 #include "util-unittest-helper.h"
71 #include "flow-util.h"
73 #include "detect-engine.h"
74 #include "detect-engine-state.h"
75 #include "detect-parse.h"
77 #include "decode-events.h"
79 #include "util-memcmp.h"
80 #include "util-random.h"
81 #include "util-validate.h"
85 /** Fast lookup tree (radix) for the various HTP configurations */
86 static SCRadixTree
*cfgtree
;
87 /** List of HTP configurations. */
88 static HTPCfgRec cfglist
;
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;
96 SCEnumCharMap http_decoder_event_table
[ ] = {
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
},
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
},
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
);
196 * \brief Lookup the HTP personality string from the numeric personality.
198 * \todo This needs to be a libhtp function.
200 static const char *HTPLookupPersonalityString(int p
)
202 #define CASE_HTP_PERSONALITY_STRING(p) \
203 case HTP_SERVER_ ## p: return #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
);
225 * \brief Lookup the numeric HTP personality from a string.
227 * \todo This needs to be a libhtp function.
229 static int HTPLookupPersonality(const char *str
)
231 #define IF_HTP_PERSONALITY_NUM(p) \
232 if (strcasecmp(#p, str) == 0) return HTP_SERVER_ ## p
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
);
248 } else if ((strcasecmp("APACHE", str
) == 0) ||
249 (strcasecmp("APACHE_2_2", str
) == 0))
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
;
260 static void HTPSetEvent(HtpState
*s
, HtpTxUserData
*htud
, uint8_t e
)
262 SCLogDebug("setting event %u", e
);
265 AppLayerDecoderEventsSetEventRaw(&htud
->decoder_events
, e
);
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);
274 htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
276 AppLayerDecoderEventsSetEventRaw(&htud
->decoder_events
, e
);
281 SCLogDebug("couldn't set event %u", e
);
284 static AppLayerDecoderEvents
*HTPGetEvents(void *state
, uint64_t tx_id
)
286 SCLogDebug("get HTTP events for TX %"PRIu64
, tx_id
);
288 HtpState
*s
= (HtpState
*)state
;
289 htp_tx_t
*tx
= HTPStateGetTx(s
, tx_id
);
291 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
293 SCLogDebug("has htud, htud->decoder_events %p", htud
->decoder_events
);
294 return htud
->decoder_events
;
300 /** \brief Function to allocates the HTTP state memory and also creates the HTTP
301 * connection parser to be used by the HTP library
303 static void *HTPStateAlloc(void)
307 HtpState
*s
= HTPMalloc(sizeof(HtpState
));
308 if (unlikely(s
== NULL
)) {
309 SCReturnPtr(NULL
, "void");
312 memset(s
, 0x00, sizeof(HtpState
));
315 SCMutexLock(&htp_state_mem_lock
);
317 htp_state_memuse
+= sizeof(HtpState
);
318 SCLogDebug("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
319 SCMutexUnlock(&htp_state_mem_lock
);
322 SCReturnPtr((void *)s
, "void");
325 static void HtpTxUserDataFree(HtpState
*state
, HtpTxUserData
*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
);
337 HTPFree(htud
->boundary
, htud
->boundary_len
);
338 if (htud
->de_state
!= NULL
) {
339 DetectEngineStateFree(htud
->de_state
);
341 HTPFree(htud
, sizeof(HtpTxUserData
));
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
348 void HTPStateFree(void *state
)
352 HtpState
*s
= (HtpState
*)state
;
357 /* free the connection parser memory used by HTP library */
358 if (s
->connp
!= NULL
) {
359 SCLogDebug("freeing HTP state");
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
);
368 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
369 HtpTxUserDataFree(s
, htud
);
370 htp_tx_set_user_data(tx
, NULL
);
374 htp_connp_destroy_all(s
->connp
);
377 FileContainerFree(s
->files_ts
);
378 FileContainerFree(s
->files_tc
);
379 HTPFree(s
, sizeof(HtpState
));
382 SCMutexLock(&htp_state_mem_lock
);
384 htp_state_memuse
-= sizeof(HtpState
);
385 SCLogDebug("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
386 SCMutexUnlock(&htp_state_mem_lock
);
393 * \brief HTP transaction cleanup callback
395 * \warning We cannot actually free the transactions here. It seems that
396 * HTP only accepts freeing of transactions in the response callback.
398 static void HTPStateTransactionFree(void *state
, uint64_t id
)
402 HtpState
*s
= (HtpState
*)state
;
404 SCLogDebug("state %p, id %"PRIu64
, s
, id
);
406 htp_tx_t
*tx
= HTPStateGetTx(s
, id
);
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
);
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. */
418 tx
->request_progress
== HTP_REQUEST_COMPLETE
&&
419 tx
->response_progress
== HTP_RESPONSE_COMPLETE
)))
421 tx
->request_progress
= HTP_REQUEST_COMPLETE
;
422 tx
->response_progress
= HTP_RESPONSE_COMPLETE
;
429 * \brief Sets a flag that informs the HTP app layer that some module in the
430 * engine needs the http request body data.
433 void AppLayerHtpEnableRequestBodyCallback(void)
437 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_BODY
);
442 * \brief Sets a flag that informs the HTP app layer that some module in the
443 * engine needs the http request body data.
446 void AppLayerHtpEnableResponseBodyCallback(void)
450 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_RESPONSE_BODY
);
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.
460 static void AppLayerHtpNeedMultipartHeader(void)
463 AppLayerHtpEnableRequestBodyCallback();
465 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_MULTIPART
);
470 * \brief Sets a flag that informs the HTP app layer that some module in the
471 * engine needs the http request file.
475 void AppLayerHtpNeedFileInspection(void)
478 AppLayerHtpNeedMultipartHeader();
479 AppLayerHtpEnableRequestBodyCallback();
480 AppLayerHtpEnableResponseBodyCallback();
482 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_FILE
);
486 /* below error messages updated up to libhtp 0.5.7 (git 379632278b38b9a792183694a4febb9e0dbd1e7a) */
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
},
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
},
542 #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0]))
543 #define HTP_WARNING_MAX (sizeof(htp_warnings) / sizeof(htp_warnings[0]))
548 * \brief Get the warning id for the warning msg.
550 * \param msg warning message
552 * \retval id the id or 0 in case of not found
554 static int HTPHandleWarningGetId(const char *msg
)
556 SCLogDebug("received warning \"%s\"", msg
);
558 for (idx
= 0; idx
< HTP_WARNING_MAX
; idx
++) {
559 if (strncmp(htp_warnings
[idx
].msg
, msg
,
560 strlen(htp_warnings
[idx
].msg
)) == 0)
562 return htp_warnings
[idx
].de
;
572 * \brief Get the error id for the error msg.
574 * \param msg error message
576 * \retval id the id or 0 in case of not found
578 static int HTPHandleErrorGetId(const char *msg
)
580 SCLogDebug("received error \"%s\"", msg
);
583 for (idx
= 0; idx
< HTP_ERROR_MAX
; idx
++) {
584 if (strncmp(htp_errors
[idx
].msg
, msg
,
585 strlen(htp_errors
[idx
].msg
)) == 0)
587 return htp_errors
[idx
].de
;
597 * \brief Check state for errors, warnings and add any as events
601 static void HTPHandleError(HtpState
*s
)
603 if (s
== NULL
|| s
->conn
== NULL
||
604 s
->conn
->messages
== NULL
) {
608 size_t size
= htp_list_size(s
->conn
->messages
);
611 for (msg
= s
->htp_messages_offset
; msg
< size
; msg
++) {
612 htp_log_t
*log
= htp_list_get(s
->conn
->messages
, msg
);
616 HtpTxUserData
*htud
= NULL
;
617 htp_tx_t
*tx
= log
->tx
; // will be NULL in <=0.5.9
619 htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
621 SCLogDebug("message %s", log
->msg
);
623 int id
= HTPHandleErrorGetId(log
->msg
);
625 id
= HTPHandleWarningGetId(log
->msg
);
627 id
= HTTP_DECODER_EVENT_UNKNOWN_ERROR
;
631 HTPSetEvent(s
, htud
, id
);
634 s
->htp_messages_offset
= (uint16_t)msg
;
635 SCLogDebug("s->htp_messages_offset %u", s
->htp_messages_offset
);
638 static inline void HTPErrorCheckTxRequestFlags(HtpState
*s
, htp_tx_t
*tx
)
641 BUG_ON(s
== NULL
|| tx
== NULL
);
643 if (tx
->flags
& ( HTP_REQUEST_INVALID_T_E
|HTP_REQUEST_INVALID_C_L
|
644 HTP_HOST_MISSING
|HTP_HOST_AMBIGUOUS
|HTP_HOSTU_INVALID
|
647 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
651 if (tx
->flags
& HTP_REQUEST_INVALID_T_E
)
653 HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST
);
654 if (tx
->flags
& HTP_REQUEST_INVALID_C_L
)
656 HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST
);
657 if (tx
->flags
& HTP_HOST_MISSING
)
659 HTTP_DECODER_EVENT_MISSING_HOST_HEADER
);
660 if (tx
->flags
& HTP_HOST_AMBIGUOUS
)
662 HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS
);
663 if (tx
->flags
& HTP_HOSTU_INVALID
)
665 HTTP_DECODER_EVENT_URI_HOST_INVALID
);
666 if (tx
->flags
& HTP_HOSTH_INVALID
)
668 HTTP_DECODER_EVENT_HEADER_HOST_INVALID
);
670 if (tx
->request_auth_type
== HTP_AUTH_UNRECOGNIZED
) {
671 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
675 HTPSetEvent(s
, htud
, HTTP_DECODER_EVENT_AUTH_UNRECOGNIZED
);
679 static int Setup(Flow
*f
, HtpState
*hstate
)
681 /* store flow ref in state so callbacks can access it */
684 HTPCfgRec
*htp_cfg_rec
= &cfglist
;
685 htp_cfg_t
*htp
= cfglist
.cfg
; /* Default to the global HTP config */
686 void *user_data
= NULL
;
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
);
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
);
697 SCLogError(SC_ERR_INVALID_ARGUMENT
, "unknown address family, bug!");
701 if (user_data
!= NULL
) {
702 htp_cfg_rec
= user_data
;
703 htp
= htp_cfg_rec
->cfg
;
704 SCLogDebug("LIBHTP using config: %p", htp
);
706 SCLogDebug("Using default HTP config: %p", htp
);
710 #ifdef DEBUG_VALIDATION
713 /* should never happen if HTPConfigure is properly invoked */
717 hstate
->connp
= htp_connp_create(htp
);
718 if (hstate
->connp
== NULL
) {
722 hstate
->conn
= htp_connp_get_connection(hstate
->connp
);
724 htp_connp_set_user_data(hstate
->connp
, (void *)hstate
);
725 hstate
->cfg
= htp_cfg_rec
;
727 SCLogDebug("New hstate->connp %p", hstate
->connp
);
729 htp_connp_open(hstate
->connp
, NULL
, f
->sp
, NULL
, f
->dp
, &f
->startts
);
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
);
741 * \brief Function to handle the reassembled data from client and feed it to
742 * the HTP library to process it.
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)
751 * \retval On success returns 1 or on failure returns -1.
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
)
760 HtpState
*hstate
= (HtpState
*)htp_state
;
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.
766 if (NULL
== hstate
->conn
) {
767 if (Setup(f
, hstate
) != 0) {
771 DEBUG_VALIDATE_BUG_ON(hstate
->connp
== NULL
);
773 htp_time_t ts
= { f
->lastts
.tv_sec
, f
->lastts
.tv_usec
};
774 /* pass the new data to the htp parser */
776 const int r
= htp_connp_req_data(hstate
->connp
, &ts
, input
, input_len
);
778 case HTP_STREAM_ERROR
:
784 HTPHandleError(hstate
);
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
))
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");
796 SCLogDebug("hstate->connp %p", hstate
->connp
);
804 * \brief Function to handle the reassembled data from server and feed it to
805 * the HTP library to process it.
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)
814 * \retval On success returns 1 or on failure returns -1
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
)
823 HtpState
*hstate
= (HtpState
*)htp_state
;
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.
829 if (NULL
== hstate
->conn
) {
830 if (Setup(f
, hstate
) != 0) {
834 DEBUG_VALIDATE_BUG_ON(hstate
->connp
== NULL
);
836 htp_time_t ts
= { f
->lastts
.tv_sec
, f
->lastts
.tv_usec
};
838 const int r
= htp_connp_res_data(hstate
->connp
, &ts
, input
, input_len
);
840 case HTP_STREAM_ERROR
:
846 HTPHandleError(hstate
);
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
))
853 htp_connp_close(hstate
->connp
, &ts
);
854 hstate
->flags
|= HTP_FLAG_STATE_CLOSED_TC
;
857 SCLogDebug("hstate->connp %p", hstate
->connp
);
864 * \param name /Lowercase/ version of the variable name
866 static int HTTPParseContentDispositionHeader(uint8_t *name
, size_t name_len
,
867 uint8_t *data
, size_t len
, uint8_t **retptr
, size_t *retlen
)
870 printf("DATA START: \n");
871 PrintRawDataFp(stdout
, data
, len
);
872 printf("DATA END: \n");
877 for (x
= 0; x
< len
; x
++) {
878 if (!(isspace(data
[x
])))
885 uint8_t *line
= data
+x
;
886 size_t line_len
= len
-x
;
889 printf("LINE START: \n");
890 PrintRawDataFp(stdout
, line
, line_len
);
891 printf("LINE END: \n");
893 for (x
= 0 ; x
< line_len
; x
++) {
895 if (line
[x
- 1] != '\\' && line
[x
] == '\"') {
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
;
903 if ((x
+ 1) == line_len
) {
909 while (offset
< line_len
&& isspace(line
[offset
])) {
914 printf("TOKEN START: \n");
915 PrintRawDataFp(stdout
, token
, token_len
);
916 printf("TOKEN END: \n");
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
;
923 if (value
[0] == '\"') {
927 if (value
[value_len
-1] == '\"') {
931 printf("VALUE START: \n");
932 PrintRawDataFp(stdout
, value
, value_len
);
933 printf("VALUE END: \n");
948 * \param name /Lowercase/ version of the variable name
950 static int HTTPParseContentTypeHeader(uint8_t *name
, size_t name_len
,
951 uint8_t *data
, size_t len
, uint8_t **retptr
, size_t *retlen
)
955 printf("DATA START: \n");
956 PrintRawDataFp(stdout
, data
, len
);
957 printf("DATA END: \n");
962 for (x
= 0; x
< len
; x
++) {
963 if (!(isspace(data
[x
])))
971 uint8_t *line
= data
+x
;
972 size_t line_len
= len
-x
;
975 printf("LINE START: \n");
976 PrintRawDataFp(stdout
, line
, line_len
);
977 printf("LINE END: \n");
979 for (x
= 0 ; x
< line_len
; x
++) {
981 if (line
[x
- 1] != '\\' && line
[x
] == '\"') {
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
;
989 if ((x
+ 1) == line_len
) {
995 while (offset
< line_len
&& isspace(line
[offset
])) {
1000 printf("TOKEN START: \n");
1001 PrintRawDataFp(stdout
, token
, token_len
);
1002 printf("TOKEN END: \n");
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
;
1009 if (value
[0] == '\"') {
1013 if (value
[value_len
-1] == '\"') {
1017 printf("VALUE START: \n");
1018 PrintRawDataFp(stdout
, value
, value_len
);
1019 printf("VALUE END: \n");
1022 *retlen
= value_len
;
1034 * \brief setup multipart parsing: extract boundary and store it
1036 * \param d HTTP transaction
1037 * \param htud transaction userdata
1039 * \retval 1 ok, multipart set up
1040 * \retval 0 ok, not multipart though
1041 * \retval -1 error: problem with the boundary
1043 * If the request contains a multipart message, this function will
1044 * set the HTP_BOUNDARY_SET in the transaction.
1046 static int HtpRequestBodySetupMultipart(htp_tx_data_t
*d
, HtpTxUserData
*htud
)
1048 htp_header_t
*h
= (htp_header_t
*)htp_table_get_c(d
->tx
->request_headers
,
1050 if (h
!= NULL
&& bstr_len(h
->value
) > 0) {
1051 uint8_t *boundary
= NULL
;
1052 size_t boundary_len
= 0;
1054 int r
= HTTPParseContentTypeHeader((uint8_t *)"boundary=", 9,
1055 (uint8_t *) bstr_ptr(h
->value
), bstr_len(h
->value
),
1056 &boundary
, &boundary_len
);
1059 printf("BOUNDARY START: \n");
1060 PrintRawDataFp(stdout
, boundary
, boundary_len
);
1061 printf("BOUNDARY END: \n");
1063 if (boundary_len
< HTP_BOUNDARY_MAX
) {
1064 htud
->boundary
= HTPMalloc(boundary_len
);
1065 if (htud
->boundary
== NULL
) {
1068 htud
->boundary_len
= (uint8_t)boundary_len
;
1069 memcpy(htud
->boundary
, boundary
, boundary_len
);
1071 htud
->tsflags
|= HTP_BOUNDARY_SET
;
1073 SCLogDebug("invalid boundary");
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
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
)
1100 printf("HEADER START: \n");
1101 PrintRawDataFp(stdout
, header
, header_len
);
1102 printf("HEADER END: \n");
1105 while (header_len
> 0) {
1106 uint8_t *next_line
= Bs2bmSearch(header
, header_len
, (uint8_t *)"\r\n", 2);
1107 uint8_t *line
= header
;
1110 if (next_line
== NULL
) {
1111 line_len
= header_len
;
1113 line_len
= next_line
- header
;
1115 uint8_t *sc
= (uint8_t *)memchr(line
, ':', line_len
);
1117 HTPSetEvent(hstate
, htud
,
1118 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
);
1119 /* if the : we found is the final char, it means we have
1121 } else if (line_len
> 0 && sc
== &line
[line_len
- 1]) {
1122 HTPSetEvent(hstate
, htud
,
1123 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
);
1126 printf("LINE START: \n");
1127 PrintRawDataFp(stdout
, line
, line_len
);
1128 printf("LINE END: \n");
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
;
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
;
1144 (void)HTTPParseContentTypeHeader(NULL
, 0,
1145 value
, value_len
, &ft
, &ft_len
);
1149 if (next_line
== NULL
) {
1150 SCLogDebug("no next_line");
1153 header_len
-= ((next_line
+ 2) - header
);
1154 header
= next_line
+ 2;
1155 } /* while (header_len > 0) */
1157 if (fn_len
> USHRT_MAX
)
1159 if (ft_len
> USHRT_MAX
)
1163 *filename_len
= fn_len
;
1165 *filetype_len
= ft_len
;
1169 * \brief Create a single buffer from the HtpBodyChunks in our list
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
1175 static void HtpRequestBodyReassemble(HtpTxUserData
*htud
,
1176 const uint8_t **chunks_buffer
, uint32_t *chunks_buffer_len
)
1178 StreamingBufferGetDataAtOffset(htud
->request_body
.sb
,
1179 chunks_buffer
, chunks_buffer_len
,
1180 htud
->request_body
.body_parsed
);
1183 static void FlagDetectStateNewFile(HtpTxUserData
*tx
, int dir
)
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
;
1198 * \brief Setup boundary buffers
1200 static void HtpRequestBodySetupBoundary(HtpTxUserData
*htud
,
1201 uint8_t *boundary
, uint32_t boundary_len
)
1203 memset(boundary
, '-', boundary_len
);
1204 memcpy(boundary
+ 2, htud
->boundary
, htud
->boundary_len
);
1207 static int HtpRequestBodyHandleMultipart(HtpState
*hstate
, HtpTxUserData
*htud
, void *tx
,
1208 const uint8_t *chunks_buffer
, uint32_t chunks_buffer_len
)
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;
1217 printf("CHUNK START: \n");
1218 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
1219 printf("CHUNK END: \n");
1222 HtpRequestBodySetupBoundary(htud
, boundary
, htud
->boundary_len
+ 4);
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);
1232 uint8_t *form_end
= Bs2bmSearch(chunks_buffer
, chunks_buffer_len
,
1233 boundary
, expected_boundary_end_len
);
1235 SCLogDebug("header_start %p, header_end %p, form_end %p", header_start
,
1236 header_end
, form_end
);
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");
1246 const uint8_t *filedata
= chunks_buffer
;
1247 uint32_t filedata_len
= 0;
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
;
1261 if (filedata_len
> chunks_buffer_len
) {
1262 HTPSetEvent(hstate
, htud
,
1263 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1267 printf("FILEDATA (final chunk) START: \n");
1268 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1269 printf("FILEDATA (final chunk) END: \n");
1271 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1272 if (HTPFileClose(hstate
, filedata
, filedata_len
, flags
,
1273 STREAM_TOSERVER
) == -1)
1279 htud
->tsflags
&=~ HTP_FILENAME_SET
;
1283 SCLogDebug("not yet at the end of the file");
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
;
1289 printf("FILEDATA (part) START: \n");
1290 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1291 printf("FILEDATA (part) END: \n");
1294 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1295 result
= HTPFileStoreChunk(hstate
, filedata
,
1296 filedata_len
, STREAM_TOSERVER
);
1299 } else if (result
== -2) {
1300 /* we know for sure we're not storing the file */
1301 htud
->tsflags
|= HTP_DONTSTORE
;
1305 htud
->request_body
.body_parsed
+= filedata_len
;
1307 SCLogDebug("chunk too small to already process in part");
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
)
1320 uint8_t *filename
= NULL
;
1321 uint16_t filename_len
= 0;
1322 uint8_t *filetype
= NULL
;
1323 uint16_t filetype_len
= 0;
1325 uint32_t header_len
= header_end
- header_start
;
1326 SCLogDebug("header_len %u", header_len
);
1327 uint8_t *header
= header_start
;
1329 /* skip empty records */
1330 if (expected_boundary_len
== header_len
) {
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
1337 HtpRequestBodyMultipartParseHeader(hstate
, htud
, header
, header_len
,
1338 &filename
, &filename_len
, &filetype
, &filetype_len
);
1340 if (filename
!= NULL
) {
1341 uint8_t *filedata
= NULL
;
1342 uint32_t filedata_len
= 0;
1344 SCLogDebug("we have a filename");
1346 htud
->tsflags
|= HTP_FILENAME_SET
;
1347 htud
->tsflags
&= ~HTP_DONTSTORE
;
1349 SCLogDebug("header_end %p", header_end
);
1350 SCLogDebug("form_end %p", form_end
);
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
);
1359 } else if (form_end
< filedata
) {
1360 HTPSetEvent(hstate
, htud
,
1361 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1365 filedata_len
= form_end
- (header_end
+ 4 + 2);
1366 SCLogDebug("filedata_len %"PRIuMAX
, (uintmax_t)filedata_len
);
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
);
1375 if (filedata_len
> chunks_buffer_len
) {
1376 HTPSetEvent(hstate
, htud
,
1377 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1380 SCLogDebug("filedata_len %"PRIuMAX
, (uintmax_t)filedata_len
);
1382 printf("FILEDATA START: \n");
1383 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1384 printf("FILEDATA END: \n");
1387 result
= HTPFileOpen(hstate
, filename
, filename_len
,
1388 filedata
, filedata_len
, hstate
->transaction_cnt
,
1392 } else if (result
== -2) {
1393 htud
->tsflags
|= HTP_DONTSTORE
;
1395 if (HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
) == -1) {
1399 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1401 htud
->request_body
.body_parsed
+= (header_end
- chunks_buffer
);
1402 htud
->tsflags
&= ~HTP_FILENAME_SET
;
1404 SCLogDebug("chunk doesn't contain form end");
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
);
1410 if (filedata_len
> chunks_buffer_len
) {
1411 HTPSetEvent(hstate
, htud
,
1412 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1417 printf("FILEDATA START: \n");
1418 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1419 printf("FILEDATA END: \n");
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
1430 SCLogDebug("more file data to come");
1432 uint32_t offset
= (header_end
+ 4) - chunks_buffer
;
1433 SCLogDebug("offset %u", offset
);
1434 htud
->request_body
.body_parsed
+= offset
;
1436 result
= HTPFileOpen(hstate
, filename
, filename_len
,
1437 NULL
, 0, hstate
->transaction_cnt
,
1441 } else if (result
== -2) {
1442 htud
->tsflags
|= HTP_DONTSTORE
;
1444 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1446 } else if (header_next
- filedata
> 2) {
1447 filedata_len
= header_next
- filedata
- 2;
1448 SCLogDebug("filedata_len %u", filedata_len
);
1450 result
= HTPFileOpen(hstate
, filename
, filename_len
,
1451 filedata
, filedata_len
, hstate
->transaction_cnt
,
1455 } else if (result
== -2) {
1456 htud
->tsflags
|= HTP_DONTSTORE
;
1458 if (HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
) == -1) {
1462 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1464 htud
->tsflags
&= ~HTP_FILENAME_SET
;
1465 htud
->request_body
.body_parsed
+= (header_end
- chunks_buffer
);
1470 SCLogDebug("header_start %p, header_end %p, form_end %p",
1471 header_start
, header_end
, form_end
);
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);
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;
1491 htud
->request_body
.body_parsed
+= move
;
1492 SCLogDebug("form not ready, file not set, parsing non-file "
1493 "record: moved %u", move
);
1498 SCLogDebug("htud->request_body.body_parsed %"PRIu64
, htud
->request_body
.body_parsed
);
1502 /** \brief setup things for put request
1503 * \todo really needed? */
1504 static int HtpRequestBodySetupPUT(htp_tx_data_t
*d
, HtpTxUserData
*htud
)
1506 // if (d->tx->parsed_uri == NULL || d->tx->parsed_uri->path == NULL) {
1510 /* filename is d->tx->parsed_uri->path */
1516 * \brief Handle POST, no multipart body data
1518 static int HtpRequestBodyHandlePOST(HtpState
*hstate
, HtpTxUserData
*htud
,
1519 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1523 /* see if we need to open the file */
1524 if (!(htud
->tsflags
& HTP_FILENAME_SET
))
1526 uint8_t *filename
= NULL
;
1527 size_t filename_len
= 0;
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
);
1535 if (filename
!= NULL
) {
1536 result
= HTPFileOpen(hstate
, filename
, (uint32_t)filename_len
, data
, data_len
,
1537 hstate
->transaction_cnt
, STREAM_TOSERVER
);
1540 } else if (result
== -2) {
1541 htud
->tsflags
|= HTP_DONTSTORE
;
1543 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1544 htud
->tsflags
|= HTP_FILENAME_SET
;
1545 htud
->tsflags
&= ~HTP_DONTSTORE
;
1551 /* otherwise, just store the data */
1553 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1554 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOSERVER
);
1557 } else if (result
== -2) {
1558 /* we know for sure we're not storing the file */
1559 htud
->tsflags
|= HTP_DONTSTORE
;
1570 * \brief Handle PUT body data
1572 static int HtpRequestBodyHandlePUT(HtpState
*hstate
, HtpTxUserData
*htud
,
1573 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1577 /* see if we need to open the file */
1578 if (!(htud
->tsflags
& HTP_FILENAME_SET
))
1580 uint8_t *filename
= NULL
;
1581 size_t filename_len
= 0;
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
);
1589 if (filename
!= NULL
) {
1590 result
= HTPFileOpen(hstate
, filename
, (uint32_t)filename_len
, data
, data_len
,
1591 hstate
->transaction_cnt
, STREAM_TOSERVER
);
1594 } else if (result
== -2) {
1595 htud
->tsflags
|= HTP_DONTSTORE
;
1597 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1598 htud
->tsflags
|= HTP_FILENAME_SET
;
1599 htud
->tsflags
&= ~HTP_DONTSTORE
;
1605 /* otherwise, just store the data */
1607 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1608 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOSERVER
);
1611 } else if (result
== -2) {
1612 /* we know for sure we're not storing the file */
1613 htud
->tsflags
|= HTP_DONTSTORE
;
1623 static int HtpResponseBodyHandle(HtpState
*hstate
, HtpTxUserData
*htud
,
1624 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1630 /* see if we need to open the file */
1631 if (!(htud
->tcflags
& HTP_FILENAME_SET
))
1633 SCLogDebug("setting up file name");
1635 uint8_t *filename
= NULL
;
1636 size_t filename_len
= 0;
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
);
1647 /* fall back to name from the uri */
1648 if (filename
== NULL
) {
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
);
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
);
1662 } else if (result
== -2) {
1663 htud
->tcflags
|= HTP_DONTSTORE
;
1665 FlagDetectStateNewFile(htud
, STREAM_TOCLIENT
);
1666 htud
->tcflags
|= HTP_FILENAME_SET
;
1667 htud
->tcflags
&= ~HTP_DONTSTORE
;
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
);
1678 /* otherwise, just store the data */
1680 if (!(htud
->tcflags
& HTP_DONTSTORE
)) {
1681 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOCLIENT
);
1682 SCLogDebug("result %d", result
);
1685 } else if (result
== -2) {
1686 /* we know for sure we're not storing the file */
1687 htud
->tcflags
|= HTP_DONTSTORE
;
1692 htud
->response_body
.body_parsed
+= data_len
;
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
1703 static int HTPCallbackRequestBodyData(htp_tx_data_t
*d
)
1707 if (!(SC_ATOMIC_GET(htp_config_flags
) & HTP_REQUIRE_REQUEST_BODY
))
1708 SCReturnInt(HTP_OK
);
1710 if (d
->data
== NULL
|| d
->len
== 0)
1711 SCReturnInt(HTP_OK
);
1714 printf("HTPBODY START: \n");
1715 PrintRawDataFp(stdout
, (uint8_t *)d
->data
, d
->len
);
1716 printf("HTPBODY END: \n");
1719 HtpState
*hstate
= htp_connp_get_user_data(d
->tx
->connp
);
1720 if (hstate
== NULL
) {
1721 SCReturnInt(HTP_ERROR
);
1724 SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
1725 "%"PRIu32
"", hstate
, d
, d
->data
, (uint32_t)d
->len
);
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
);
1733 memset(tx_ud
, 0, sizeof(HtpTxUserData
));
1735 /* Set the user data for handling body chunks on this transaction */
1736 htp_tx_set_user_data(d
->tx
, tx_ud
);
1738 if (!tx_ud
->response_body_init
) {
1739 tx_ud
->response_body_init
= 1;
1741 if (d
->tx
->request_method_number
== HTP_M_POST
) {
1743 int r
= HtpRequestBodySetupMultipart(d
, tx_ud
);
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");
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
;
1757 /* see if we can get rid of htp body chunks */
1758 HtpBodyPrune(hstate
, &tx_ud
->request_body
, STREAM_TOSERVER
);
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
);
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
)
1766 uint32_t len
= (uint32_t)d
->len
;
1768 if (hstate
->cfg
->request
.body_limit
> 0 &&
1769 (tx_ud
->request_body
.content_len_so_far
+ len
) > hstate
->cfg
->request
.body_limit
)
1771 len
= hstate
->cfg
->request
.body_limit
- tx_ud
->request_body
.content_len_so_far
;
1772 BUG_ON(len
> (uint32_t)d
->len
);
1774 SCLogDebug("len %u", len
);
1776 HtpBodyAppendChunk(&hstate
->cfg
->request
, &tx_ud
->request_body
, d
->data
, len
);
1778 const uint8_t *chunks_buffer
= NULL
;
1779 uint32_t chunks_buffer_len
= 0;
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
)) {
1787 HtpRequestBodyReassemble(tx_ud
, &chunks_buffer
, &chunks_buffer_len
);
1788 if (chunks_buffer
== NULL
) {
1792 printf("REASSCHUNK START: \n");
1793 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
1794 printf("REASSCHUNK END: \n");
1797 HtpRequestBodyHandleMultipart(hstate
, tx_ud
, d
->tx
, chunks_buffer
, chunks_buffer_len
);
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
);
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
;
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
);
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
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
)
1829 uint32_t x
= (uint32_t)((uint64_t)hstate
->conn
->in_data_counter
- hstate
->last_request_data_stamp
);
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
);
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);
1840 SCReturnInt(HTP_OK
);
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
1848 static int HTPCallbackResponseBodyData(htp_tx_data_t
*d
)
1852 if (!(SC_ATOMIC_GET(htp_config_flags
) & HTP_REQUIRE_RESPONSE_BODY
))
1853 SCReturnInt(HTP_OK
);
1855 if (d
->data
== NULL
|| d
->len
== 0)
1856 SCReturnInt(HTP_OK
);
1858 HtpState
*hstate
= htp_connp_get_user_data(d
->tx
->connp
);
1859 if (hstate
== NULL
) {
1860 SCReturnInt(HTP_ERROR
);
1863 SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
1864 "%"PRIu32
"", hstate
, d
, d
->data
, (uint32_t)d
->len
);
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
);
1872 memset(tx_ud
, 0, sizeof(HtpTxUserData
));
1874 /* Set the user data for handling body chunks on this transaction */
1875 htp_tx_set_user_data(d
->tx
, tx_ud
);
1877 if (!tx_ud
->request_body_init
) {
1878 tx_ud
->request_body_init
= 1;
1881 /* see if we can get rid of htp body chunks */
1882 HtpBodyPrune(hstate
, &tx_ud
->response_body
, STREAM_TOCLIENT
);
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
);
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
)
1890 uint32_t len
= (uint32_t)d
->len
;
1892 if (hstate
->cfg
->response
.body_limit
> 0 &&
1893 (tx_ud
->response_body
.content_len_so_far
+ len
) > hstate
->cfg
->response
.body_limit
)
1895 len
= hstate
->cfg
->response
.body_limit
- tx_ud
->response_body
.content_len_so_far
;
1896 BUG_ON(len
> (uint32_t)d
->len
);
1898 SCLogDebug("len %u", len
);
1900 HtpBodyAppendChunk(&hstate
->cfg
->response
, &tx_ud
->response_body
, d
->data
, len
);
1902 HtpResponseBodyHandle(hstate
, tx_ud
, d
->tx
, (uint8_t *)d
->data
, (uint32_t)d
->len
);
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
;
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
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
)
1925 uint32_t x
= (uint32_t)((uint64_t)hstate
->conn
->out_data_counter
- hstate
->last_response_data_stamp
);
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
);
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);
1936 SCReturnInt(HTP_OK
);
1940 * \brief Print the stats of the HTTP requests
1942 void HTPAtExitPrintStats(void)
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
);
1954 /** \brief Clears the HTTP server configuration memory used by HTP library */
1955 void HTPFreeConfig(void)
1959 if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
1960 !AppLayerParserConfParserEnabled("tcp", "http"))
1965 HTPCfgRec
*nextrec
= cfglist
.next
;
1966 SCRadixReleaseRadixTree(cfgtree
);
1968 htp_config_destroy(cfglist
.cfg
);
1969 while (nextrec
!= NULL
) {
1970 HTPCfgRec
*htprec
= nextrec
;
1971 nextrec
= nextrec
->next
;
1973 htp_config_destroy(htprec
->cfg
);
1980 static int HTPCallbackRequestHasTrailer(htp_tx_t
*tx
)
1982 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
1984 htud
->request_has_trailers
= 1;
1989 static int HTPCallbackResponseHasTrailer(htp_tx_t
*tx
)
1991 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
1993 htud
->response_has_trailers
= 1;
1999 * \brief called at start of request
2000 * Set min inspect size.
2002 static int HTPCallbackRequestStart(htp_tx_t
*tx
)
2004 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2005 if (hstate
== NULL
) {
2006 SCReturnInt(HTP_ERROR
);
2010 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
,
2011 hstate
->cfg
->request
.inspect_min_size
);
2012 SCReturnInt(HTP_OK
);
2016 * \brief called at start of response
2017 * Set min inspect size.
2019 static int HTPCallbackResponseStart(htp_tx_t
*tx
)
2021 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2022 if (hstate
== NULL
) {
2023 SCReturnInt(HTP_ERROR
);
2027 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
,
2028 hstate
->cfg
->response
.inspect_min_size
);
2029 SCReturnInt(HTP_OK
);
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
2039 static int HTPCallbackRequest(htp_tx_t
*tx
)
2044 SCReturnInt(HTP_ERROR
);
2047 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2048 if (hstate
== NULL
) {
2049 SCReturnInt(HTP_ERROR
);
2052 SCLogDebug("transaction_cnt %"PRIu64
", list_size %"PRIu64
,
2053 hstate
->transaction_cnt
, HTPStateGetTxCnt(hstate
));
2055 SCLogDebug("HTTP request completed");
2057 HTPErrorCheckTxRequestFlags(hstate
, tx
);
2059 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
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
);
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
);
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
2083 static int HTPCallbackResponse(htp_tx_t
*tx
)
2087 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2088 if (hstate
== NULL
) {
2089 SCReturnInt(HTP_ERROR
);
2092 /* we have one whole transaction now */
2093 hstate
->transaction_cnt
++;
2095 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
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
;
2104 /* response done, do raw reassembly now to inspect state and stream
2105 * at the same time. */
2106 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOCLIENT
);
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)) {
2116 if (tx
->request_port_number
!= -1) {
2117 dp
= (uint16_t)tx
->request_port_number
;
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
;
2126 hstate
->last_response_data_stamp
= (uint64_t)hstate
->conn
->out_data_counter
;
2127 SCReturnInt(HTP_OK
);
2130 static int HTPCallbackRequestLine(htp_tx_t
*tx
)
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
;
2137 request_uri_normalized
= SCHTPGenerateNormalizedUri(tx
, tx
->parsed_uri
, cfg
->uri_include_all
);
2138 if (request_uri_normalized
== NULL
)
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
);
2148 memset(tx_ud
, 0, sizeof(*tx_ud
));
2149 htp_tx_set_user_data(tx
, tx_ud
);
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
;
2156 HTPErrorCheckTxRequestFlags(hstate
, tx
);
2161 static int HTPCallbackDoubleDecodeQuery(htp_tx_t
*tx
)
2163 if (tx
->parsed_uri
== NULL
|| tx
->parsed_uri
->query
== NULL
)
2167 htp_urldecode_inplace(tx
->cfg
, HTP_DECODER_URLENCODED
, tx
->parsed_uri
->query
, &flags
);
2172 static int HTPCallbackDoubleDecodePath(htp_tx_t
*tx
)
2174 if (tx
->parsed_uri
== NULL
|| tx
->parsed_uri
->path
== NULL
)
2178 htp_urldecode_inplace(tx
->cfg
, HTP_DECODER_URL_PATH
, tx
->parsed_uri
->path
, &flags
);
2183 static int HTPCallbackRequestHeaderData(htp_tx_data_t
*tx_data
)
2186 if (tx_data
->len
== 0 || tx_data
->tx
== NULL
)
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
))
2194 memset(tx_ud
, 0, sizeof(*tx_ud
));
2195 htp_tx_set_user_data(tx_data
->tx
, tx_ud
);
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
);
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
);
2207 tx_ud
->request_headers_raw
= ptmp
;
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
;
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
);
2220 static int HTPCallbackResponseHeaderData(htp_tx_data_t
*tx_data
)
2223 if (tx_data
->len
== 0 || tx_data
->tx
== NULL
)
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
))
2231 memset(tx_ud
, 0, sizeof(*tx_ud
));
2232 htp_tx_set_user_data(tx_data
->tx
, tx_ud
);
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
);
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
);
2244 tx_ud
->response_headers_raw
= ptmp
;
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
;
2254 * We have a similar set function called HTPConfigSetDefaultsPhase1.
2256 static void HTPConfigSetDefaultsPhase1(HTPCfgRec
*cfg_prec
)
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
;
2266 if (!g_disable_randomness
) {
2267 cfg_prec
->randomize
= HTP_CONFIG_DEFAULT_RANDOMIZE
;
2269 cfg_prec
->randomize
= 0;
2271 cfg_prec
->randomize_range
= HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE
;
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
);
2278 htp_config_register_request_trailer(cfg_prec
->cfg
, HTPCallbackRequestHasTrailer
);
2279 htp_config_register_response_trailer(cfg_prec
->cfg
, HTPCallbackResponseHasTrailer
);
2281 htp_config_register_request_body_data(cfg_prec
->cfg
, HTPCallbackRequestBodyData
);
2282 htp_config_register_response_body_data(cfg_prec
->cfg
, HTPCallbackResponseBodyData
);
2284 htp_config_register_request_start(cfg_prec
->cfg
, HTPCallbackRequestStart
);
2285 htp_config_register_request_complete(cfg_prec
->cfg
, HTPCallbackRequest
);
2287 htp_config_register_response_start(cfg_prec
->cfg
, HTPCallbackResponseStart
);
2288 htp_config_register_response_complete(cfg_prec
->cfg
, HTPCallbackResponse
);
2290 htp_config_set_parse_request_cookies(cfg_prec
->cfg
, 0);
2292 /* don't convert + to space by default */
2293 htp_config_set_plusspace_decode(cfg_prec
->cfg
, HTP_DECODER_URLENCODED
, 0);
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
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
);
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
2310 static int RandomGetWrap(void)
2316 } while(r
>= ULONG_MAX
- (ULONG_MAX
% RAND_MAX
));
2318 return r
% RAND_MAX
;
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
)
2329 /* randomize inspection size if needed */
2330 if (cfg_prec
->randomize
) {
2331 int rdrange
= cfg_prec
->randomize_range
;
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);
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"
2346 cfg_prec
->request
.inspect_min_size
,
2347 cfg_prec
->request
.inspect_window
);
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);
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);
2360 SCLogConfig("'%s' server has 'response-body-minimal-inspect-size' set to"
2361 " %d and 'response-body-inspect-window' set to %d after"
2364 cfg_prec
->response
.inspect_min_size
,
2365 cfg_prec
->response
.inspect_window
);
2368 htp_config_register_request_line(cfg_prec
->cfg
, HTPCallbackRequestLine
);
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
;
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
;
2390 static void HTPConfigParseParameters(HTPCfgRec
*cfg_prec
, ConfNode
*s
,
2393 if (cfg_prec
== NULL
|| s
== NULL
|| tree
== NULL
)
2398 /* Default Parameters */
2399 TAILQ_FOREACH(p
, &s
->head
, next
) {
2401 if (strcasecmp("address", p
->name
) == 0) {
2404 TAILQ_FOREACH(pval
, &p
->head
, next
) {
2405 SCLogDebug("LIBHTP server %s: %s=%s", s
->name
, p
->name
,
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
);
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",
2424 } /* else - if (strchr(pval->val, ':') != NULL) */
2425 } /* TAILQ_FOREACH(pval, &p->head, next) */
2427 } else if (strcasecmp("personality", p
->name
) == 0) {
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
);
2433 if (personality
>= 0) {
2434 SCLogDebug("LIBHTP default: %s=%s (%d)", p
->name
, p
->val
,
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
);
2440 SCLogDebug("LIBHTP personality set to %s",
2441 HTPLookupPersonalityString(personality
));
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);
2449 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "LIBHTP Unknown personality "
2450 "\"%s\", ignoring", p
->val
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
2509 } else if (strcasecmp("response-body-decompress-layer-limit", p
->name
) == 0) {
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
);
2516 #ifdef HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT
2517 htp_config_set_response_decompression_layer_limit(cfg_prec
->cfg
, value
);
2519 SCLogWarning(SC_WARN_OUTDATED_LIBHTP
, "can't set response-body-decompress-layer-limit "
2520 "to %u, libhtp version too old", value
);
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
,
2532 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
, "Invalid entry "
2533 "for libhtp param path-bestfit-replacement-char");
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
;
2568 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
, "Invalid entry "
2569 "for libhtp param path-url-encoding-invalid-handling");
2572 htp_config_set_url_encoding_invalid_handling(cfg_prec
->cfg
,
2573 HTP_DECODER_URL_PATH
,
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) {
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
);
2595 SCLogError(SC_ERR_SIZE_PARSE
, "Error meta-field-limit "
2596 "from conf file cannot be 0. Killing engine");
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
,
2603 } else if (strcasecmp("randomize-inspection-sizes", p
->name
) == 0) {
2604 if (!g_disable_randomness
) {
2605 cfg_prec
->randomize
= ConfValIsTrue(p
->val
);
2607 } else if (strcasecmp("randomize-inspection-range", p
->name
) == 0) {
2608 uint32_t range
= atoi(p
->val
);
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."
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;
2624 if (strcmp("auto", p
->val
) != 0) {
2625 WarnInvalidConfEntry("http_body_inline", "%s", "auto");
2627 if (EngineModeIsIPS()) {
2628 cfg_prec
->http_body_inline
= 1;
2630 cfg_prec
->http_body_inline
= 0;
2633 } else if (strcasecmp("swf-decompression", p
->name
) == 0) {
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;
2643 WarnInvalidConfEntry("swf-decompression.enabled", "%s", "no");
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
;
2655 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
,
2656 "Invalid entry for "
2657 "swf-decompression.type: %s - "
2658 "Killing engine", pval
->val
);
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
);
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
);
2676 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "Ignoring unknown param %s", pval
->name
);
2680 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "LIBHTP Ignoring unknown "
2681 "default config: %s", p
->name
);
2683 } /* TAILQ_FOREACH(p, &default_config->head, next) */
2688 void HTPConfigure(void)
2692 cfglist
.next
= NULL
;
2694 cfgtree
= SCRadixCreateRadixTree(NULL
, NULL
);
2695 if (NULL
== cfgtree
)
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");
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"),
2710 HTPConfigParseParameters(&cfglist
, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree
);
2712 HTPConfigSetDefaultsPhase2("default", &cfglist
);
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
);
2725 SCLogDebug("LIBHTP Configuring %p", server_config
);
2729 TAILQ_FOREACH(si
, &server_config
->head
, next
) {
2730 /* Need the named node, not the index */
2731 ConfNode
*s
= TAILQ_FIRST(&si
->head
);
2733 SCLogDebug("LIBHTP s NULL");
2737 SCLogDebug("LIBHTP server %s", s
->name
);
2739 HTPCfgRec
*nextrec
= cfglist
.next
;
2740 HTPCfgRec
*htprec
= SCMalloc(sizeof(HTPCfgRec
));
2743 memset(htprec
, 0x00, sizeof(*htprec
));
2745 cfglist
.next
= htprec
;
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");
2754 HTPConfigSetDefaultsPhase1(htprec
);
2755 HTPConfigParseParameters(htprec
, s
, cfgtree
);
2756 HTPConfigSetDefaultsPhase2(s
->name
, htprec
);
2762 void AppLayerHtpPrintStats(void)
2765 SCMutexLock(&htp_state_mem_lock
);
2766 SCLogPerf("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
2767 SCMutexUnlock(&htp_state_mem_lock
);
2772 * \brief get files callback
2773 * \param state state ptr
2774 * \param direction flow direction
2775 * \retval files files ptr
2777 static FileContainer
*HTPStateGetFiles(void *state
, uint8_t direction
)
2782 HtpState
*http_state
= (HtpState
*)state
;
2784 if (direction
& STREAM_TOCLIENT
) {
2785 SCReturnPtr(http_state
->files_tc
, "FileContainer");
2787 SCReturnPtr(http_state
->files_ts
, "FileContainer");
2791 static int HTPStateGetAlstateProgress(void *tx
, uint8_t direction
)
2793 if (direction
& STREAM_TOSERVER
)
2794 return ((htp_tx_t
*)tx
)->request_progress
;
2796 return ((htp_tx_t
*)tx
)->response_progress
;
2799 static uint64_t HTPStateGetTxCnt(void *alstate
)
2801 HtpState
*http_state
= (HtpState
*)alstate
;
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
);
2812 static void *HTPStateGetTx(void *alstate
, uint64_t tx_id
)
2814 HtpState
*http_state
= (HtpState
*)alstate
;
2816 if (http_state
!= NULL
&& http_state
->conn
!= NULL
)
2817 return htp_list_get(http_state
->conn
->transactions
, tx_id
);
2822 static void HTPStateSetTxLogged(void *alstate
, void *vtx
, LoggerId bits
)
2824 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
2825 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2827 tx_ud
->logged
= bits
;
2830 static LoggerId
HTPStateGetTxLogged(void *alstate
, void *vtx
)
2832 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
2833 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2835 return tx_ud
->logged
;
2840 static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction
)
2842 return (direction
& STREAM_TOSERVER
) ? HTP_REQUEST_COMPLETE
: HTP_RESPONSE_COMPLETE
;
2845 static int HTPStateGetEventInfo(const char *event_name
,
2846 int *event_id
, AppLayerEventType
*event_type
)
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 */
2856 *event_type
= APP_LAYER_EVENT_TYPE_TRANSACTION
;
2861 static void HTPStateTruncate(void *state
, uint8_t direction
)
2863 FileContainer
*fc
= HTPStateGetFiles(state
, direction
);
2865 FileTruncateAllOpenFiles(fc
);
2869 static DetectEngineState
*HTPGetTxDetectState(void *vtx
)
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
;
2876 static int HTPSetTxDetectState(void *vtx
, DetectEngineState
*s
)
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
))
2884 memset(tx_ud
, 0, sizeof(*tx_ud
));
2885 htp_tx_set_user_data(tx
, tx_ud
);
2887 tx_ud
->de_state
= s
;
2891 static uint64_t HTPGetTxDetectFlags(void *vtx
, uint8_t dir
)
2893 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
2894 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx
);
2896 if (dir
& STREAM_TOSERVER
) {
2897 return tx_ud
->detect_flags_ts
;
2899 return tx_ud
->detect_flags_tc
;
2905 static void HTPSetTxDetectFlags(void *vtx
, uint8_t dir
, uint64_t detect_flags
)
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
))
2913 memset(tx_ud
, 0, sizeof(*tx_ud
));
2914 htp_tx_set_user_data(tx
, tx_ud
);
2916 if (dir
& STREAM_TOSERVER
) {
2917 tx_ud
->detect_flags_ts
= detect_flags
;
2919 tx_ud
->detect_flags_tc
= detect_flags
;
2924 static int HTPRegisterPatternsForProtocolDetection(void)
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
};
2937 int register_result
;
2938 char method_buffer
[32] = "";
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
++) {
2944 /* Combine the method name and the spacing */
2945 snprintf(method_buffer
, sizeof(method_buffer
), "%s%s", methods
[methods_pos
], spacings
[spacings_pos
]);
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
2951 register_result
= AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
,
2952 ALPROTO_HTTP
, method_buffer
, strlen(method_buffer
)-3, 0, STREAM_TOSERVER
);
2953 if (register_result
< 0) {
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) {
2973 * \brief Register the HTTP protocol and state handling functions to APP layer
2976 void RegisterHTPParsers(void)
2980 const char *proto_name
= "http";
2983 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name
)) {
2984 AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP
, proto_name
);
2985 if (HTPRegisterPatternsForProtocolDetection() < 0)
2988 SCLogInfo("Protocol detection and parser disabled for %s protocol",
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
);
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
);
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
);
3022 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
3023 "still on.", proto_name
);
3026 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP
, ALPROTO_HTTP
, HTPParserRegisterTests
);
3033 static HTPCfgRec cfglist_backup
;
3035 void HtpConfigCreateBackup(void)
3037 cfglist_backup
= cfglist
;
3042 void HtpConfigRestoreBackup(void)
3044 cfglist
= cfglist_backup
;
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)
3053 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3055 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3058 memset(&ssn
, 0, sizeof(ssn
));
3060 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3061 FAIL_IF_NULL(alp_tctx
);
3063 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3066 f
->proto
= IPPROTO_TCP
;
3067 f
->alproto
= ALPROTO_HTTP
;
3069 StreamTcpInitConfig(TRUE
);
3072 for (u
= 0; u
< httplen1
; u
++) {
3076 flags
= STREAM_TOSERVER
|STREAM_START
;
3077 else if (u
== (httplen1
- 1))
3078 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3080 flags
= STREAM_TOSERVER
;
3082 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3087 HtpState
*htp_state
= f
->alstate
;
3088 FAIL_IF_NULL(htp_state
);
3090 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3093 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
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
);
3100 AppLayerParserThreadCtxFree(alp_tctx
);
3101 StreamTcpFreeConfig(TRUE
);
3106 /** \test Test folding in 1 read case */
3107 static int HTPParserTest01b(void)
3109 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3111 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3114 memset(&ssn
, 0, sizeof(ssn
));
3116 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3117 FAIL_IF_NULL(alp_tctx
);
3119 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3122 f
->proto
= IPPROTO_TCP
;
3123 f
->alproto
= ALPROTO_HTTP
;
3125 StreamTcpInitConfig(TRUE
);
3127 uint8_t flags
=STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3128 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3129 httpbuf1
, httplen1
);
3132 HtpState
*htp_state
= f
->alstate
;
3133 FAIL_IF_NULL(htp_state
);
3135 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3138 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
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
);
3145 AppLayerParserThreadCtxFree(alp_tctx
);
3146 StreamTcpFreeConfig(TRUE
);
3151 /** \test Test folding in 1byte per read case */
3152 static int HTPParserTest01c(void)
3154 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3156 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3159 memset(&ssn
, 0, sizeof(ssn
));
3161 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3162 FAIL_IF_NULL(alp_tctx
);
3164 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3167 f
->proto
= IPPROTO_TCP
;
3168 f
->alproto
= ALPROTO_HTTP
;
3170 StreamTcpInitConfig(TRUE
);
3173 for (u
= 0; u
< httplen1
; u
++) {
3177 flags
= STREAM_TOSERVER
|STREAM_START
;
3178 else if (u
== (httplen1
- 1))
3179 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3181 flags
= STREAM_TOSERVER
;
3183 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3188 HtpState
*htp_state
= f
->alstate
;
3189 FAIL_IF_NULL(htp_state
);
3191 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3194 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
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
);
3201 AppLayerParserThreadCtxFree(alp_tctx
);
3202 StreamTcpFreeConfig(TRUE
);
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)
3213 uint8_t httpbuf1
[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3215 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3217 HtpState
*htp_state
= NULL
;
3219 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3221 memset(&ssn
, 0, sizeof(ssn
));
3223 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3227 f
->proto
= IPPROTO_TCP
;
3228 f
->alproto
= ALPROTO_HTTP
;
3230 StreamTcpInitConfig(TRUE
);
3233 for (u
= 0; u
< httplen1
; u
++) {
3237 flags
= STREAM_TOSERVER
|STREAM_START
;
3238 else if (u
== (httplen1
- 1))
3239 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3241 flags
= STREAM_TOSERVER
;
3244 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3247 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3255 htp_state
= f
->alstate
;
3256 if (htp_state
== NULL
) {
3257 printf("no http state: ");
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
)
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
));
3276 if (alp_tctx
!= NULL
)
3277 AppLayerParserThreadCtxFree(alp_tctx
);
3278 StreamTcpFreeConfig(TRUE
);
3283 /** \test See how it deals with an incomplete request. */
3284 static int HTPParserTest02(void)
3288 uint8_t httpbuf1
[] = "POST";
3289 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3291 HtpState
*http_state
= NULL
;
3292 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3294 memset(&ssn
, 0, sizeof(ssn
));
3296 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3300 f
->proto
= IPPROTO_TCP
;
3301 f
->alproto
= ALPROTO_HTTP
;
3303 StreamTcpInitConfig(TRUE
);
3306 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3307 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
,
3311 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
3317 http_state
= f
->alstate
;
3318 if (http_state
== NULL
) {
3319 printf("no http state: ");
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
)
3327 printf("expected method NULL, got %s \n", bstr_util_strdup_to_c(tx
->request_method
));
3332 if (alp_tctx
!= NULL
)
3333 AppLayerParserThreadCtxFree(alp_tctx
);
3334 StreamTcpFreeConfig(TRUE
);
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)
3345 uint8_t httpbuf1
[] = "HELLO / HTTP/1.0\r\n";
3346 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3348 HtpState
*htp_state
= NULL
;
3350 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3352 memset(&ssn
, 0, sizeof(ssn
));
3354 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3358 f
->proto
= IPPROTO_TCP
;
3359 f
->alproto
= ALPROTO_HTTP
;
3361 StreamTcpInitConfig(TRUE
);
3364 for (u
= 0; u
< httplen1
; u
++) {
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
;
3372 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3375 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3382 htp_state
= f
->alstate
;
3383 if (htp_state
== NULL
) {
3384 printf("no http state: ");
3388 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
)
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
));
3401 if (alp_tctx
!= NULL
)
3402 AppLayerParserThreadCtxFree(alp_tctx
);
3403 StreamTcpFreeConfig(TRUE
);
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)
3414 HtpState
*htp_state
= NULL
;
3415 uint8_t httpbuf1
[] = "World!\r\n";
3416 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3419 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3421 memset(&ssn
, 0, sizeof(ssn
));
3423 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3427 f
->proto
= IPPROTO_TCP
;
3428 f
->alproto
= ALPROTO_HTTP
;
3430 StreamTcpInitConfig(TRUE
);
3433 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3434 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
,
3443 htp_state
= f
->alstate
;
3444 if (htp_state
== NULL
) {
3445 printf("no http state: ");
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
)
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
));
3461 if (alp_tctx
!= NULL
)
3462 AppLayerParserThreadCtxFree(alp_tctx
);
3463 StreamTcpFreeConfig(TRUE
);
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)
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 */
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 */
3487 memset(&ssn
, 0, sizeof(ssn
));
3489 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3490 FAIL_IF_NULL(alp_tctx
);
3492 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3495 f
->proto
= IPPROTO_TCP
;
3496 f
->alproto
= ALPROTO_HTTP
;
3498 StreamTcpInitConfig(TRUE
);
3500 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3501 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
3505 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3506 STREAM_TOCLIENT
| STREAM_START
, httpbuf4
,
3510 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOCLIENT
,
3511 httpbuf5
, httplen5
);
3514 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOSERVER
,
3515 httpbuf2
, httplen2
);
3518 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3519 STREAM_TOSERVER
| STREAM_EOF
, httpbuf3
, httplen3
);
3522 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3523 STREAM_TOCLIENT
| STREAM_EOF
, httpbuf6
, httplen6
);
3526 HtpState
*http_state
= f
->alstate
;
3527 FAIL_IF_NULL(http_state
);
3529 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3531 FAIL_IF_NOT(tx
->request_method_number
== HTP_M_POST
);
3532 FAIL_IF_NOT(tx
->request_protocol_number
== HTP_PROTOCOL_1_0
);
3534 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3537 FAIL_IF_NOT(tx
->response_status_number
== 200);
3539 AppLayerParserThreadCtxFree(alp_tctx
);
3540 StreamTcpFreeConfig(TRUE
);
3545 /** \test Test proper chunked encoded response body
3547 static int HTPParserTest06(void)
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 "
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: "
3560 "Content-Type: text/html\r\n\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 */
3593 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3594 FAIL_IF_NULL(alp_tctx
);
3596 memset(&ssn
, 0, sizeof(ssn
));
3598 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3601 f
->proto
= IPPROTO_TCP
;
3602 f
->alproto
= ALPROTO_HTTP
;
3604 StreamTcpInitConfig(TRUE
);
3606 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3607 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
3610 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
3611 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
3615 HtpState
*http_state
= f
->alstate
;
3616 FAIL_IF_NULL(http_state
);
3618 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3621 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
3622 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
3624 FAIL_IF(tx
->response_status_number
!= 200);
3625 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
3627 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3630 AppLayerParserThreadCtxFree(alp_tctx
);
3631 StreamTcpFreeConfig(TRUE
);
3638 static int HTPParserTest07(void)
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 */
3645 HtpState
*htp_state
= NULL
;
3647 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3649 memset(&ssn
, 0, sizeof(ssn
));
3651 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3655 f
->proto
= IPPROTO_TCP
;
3656 f
->alproto
= ALPROTO_HTTP
;
3658 StreamTcpInitConfig(TRUE
);
3661 for (u
= 0; u
< httplen1
; u
++) {
3665 flags
= STREAM_TOSERVER
|STREAM_START
;
3666 else if (u
== (httplen1
- 1))
3667 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3669 flags
= STREAM_TOSERVER
;
3672 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3675 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3683 htp_state
= f
->alstate
;
3684 if (htp_state
== NULL
) {
3685 printf("no http state: ");
3689 uint8_t ref
[] = "/awstats.pl?/migratemigrate = |";
3690 size_t reflen
= sizeof(ref
) - 1;
3692 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
3700 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
3704 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref
,
3705 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
3707 printf("normalized uri \"");
3708 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
3710 PrintRawUriFp(stdout
, ref
, reflen
);
3718 if (alp_tctx
!= NULL
)
3719 AppLayerParserThreadCtxFree(alp_tctx
);
3720 StreamTcpFreeConfig(TRUE
);
3725 #include "conf-yaml-loader.h"
3729 static int HTPParserTest08(void)
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 */
3736 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3747 ConfCreateContextBackup();
3749 HtpConfigCreateBackup();
3751 ConfYamlLoadString(input
, strlen(input
));
3754 HtpState
*htp_state
= NULL
;
3756 memset(&ssn
, 0, sizeof(ssn
));
3758 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3762 f
->proto
= IPPROTO_TCP
;
3763 f
->alproto
= ALPROTO_HTTP
;
3765 StreamTcpInitConfig(TRUE
);
3768 flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3771 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
, httpbuf1
,
3774 printf("toserver chunk returned %" PRId32
", expected"
3782 htp_state
= f
->alstate
;
3783 if (htp_state
== NULL
) {
3784 printf("no http state: ");
3789 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
));
3801 if (alp_tctx
!= NULL
)
3802 AppLayerParserThreadCtxFree(alp_tctx
);
3803 StreamTcpFreeConfig(TRUE
);
3806 ConfRestoreContextBackup();
3807 HtpConfigRestoreBackup();
3814 static int HTPParserTest09(void)
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 */
3821 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3829 personality: Apache_2_2\n\
3832 ConfCreateContextBackup();
3834 HtpConfigCreateBackup();
3836 ConfYamlLoadString(input
, strlen(input
));
3839 HtpState
*htp_state
= NULL
;
3842 memset(&ssn
, 0, sizeof(ssn
));
3844 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3848 f
->proto
= IPPROTO_TCP
;
3849 f
->alproto
= ALPROTO_HTTP
;
3851 StreamTcpInitConfig(TRUE
);
3854 flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3857 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
, httpbuf1
,
3860 printf("toserver chunk returned %" PRId32
", expected"
3867 htp_state
= f
->alstate
;
3868 if (htp_state
== NULL
) {
3869 printf("no http state: ");
3873 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
));
3885 if (alp_tctx
!= NULL
)
3886 AppLayerParserThreadCtxFree(alp_tctx
);
3887 StreamTcpFreeConfig(TRUE
);
3890 ConfRestoreContextBackup();
3891 HtpConfigRestoreBackup();
3896 /** \test Host:www.google.com <- missing space between name:value (rfc violation)
3898 static int HTPParserTest10(void)
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 */
3905 HtpState
*htp_state
= NULL
;
3907 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3909 memset(&ssn
, 0, sizeof(ssn
));
3911 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3915 f
->proto
= IPPROTO_TCP
;
3916 f
->alproto
= ALPROTO_HTTP
;
3918 StreamTcpInitConfig(TRUE
);
3921 for (u
= 0; u
< httplen1
; u
++) {
3925 flags
= STREAM_TOSERVER
|STREAM_START
;
3926 else if (u
== (httplen1
- 1))
3927 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3929 flags
= STREAM_TOSERVER
;
3932 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
3935 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3943 htp_state
= f
->alstate
;
3944 if (htp_state
== NULL
) {
3945 printf("no http state: ");
3949 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3950 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3955 char *name
= bstr_util_strdup_to_c(h
->name
);
3960 if (strcmp(name
, "Host") != 0) {
3961 printf("header name not \"Host\", instead \"%s\": ", name
);
3967 char *value
= bstr_util_strdup_to_c(h
->value
);
3968 if (value
== NULL
) {
3972 if (strcmp(value
, "www.google.com") != 0) {
3973 printf("header value not \"www.google.com\", instead \"%s\": ", value
);
3981 if (alp_tctx
!= NULL
)
3982 AppLayerParserThreadCtxFree(alp_tctx
);
3983 StreamTcpFreeConfig(TRUE
);
3988 /** \test double encoding in path
3990 static int HTPParserTest11(void)
3994 uint8_t httpbuf1
[] = "GET /%2500 HTTP/1.0\r\n\r\n";
3995 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3997 HtpState
*htp_state
= NULL
;
3999 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4001 memset(&ssn
, 0, sizeof(ssn
));
4003 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4007 f
->proto
= IPPROTO_TCP
;
4008 f
->alproto
= ALPROTO_HTTP
;
4010 StreamTcpInitConfig(TRUE
);
4013 for (u
= 0; u
< httplen1
; u
++) {
4017 flags
= STREAM_TOSERVER
|STREAM_START
;
4018 else if (u
== (httplen1
- 1))
4019 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4021 flags
= STREAM_TOSERVER
;
4024 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
4027 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4035 htp_state
= f
->alstate
;
4036 if (htp_state
== NULL
) {
4037 printf("no http state: ");
4041 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
));
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')
4057 printf("normalized uri \"");
4058 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4066 if (alp_tctx
!= NULL
)
4067 AppLayerParserThreadCtxFree(alp_tctx
);
4068 StreamTcpFreeConfig(TRUE
);
4073 /** \test double encoding in query
4075 static int HTPParserTest12(void)
4079 uint8_t httpbuf1
[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
4080 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4082 HtpState
*htp_state
= NULL
;
4084 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4086 memset(&ssn
, 0, sizeof(ssn
));
4088 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4092 f
->proto
= IPPROTO_TCP
;
4093 f
->alproto
= ALPROTO_HTTP
;
4095 StreamTcpInitConfig(TRUE
);
4098 for (u
= 0; u
< httplen1
; u
++) {
4102 flags
= STREAM_TOSERVER
|STREAM_START
;
4103 else if (u
== (httplen1
- 1))
4104 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4106 flags
= STREAM_TOSERVER
;
4109 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
4112 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4120 htp_state
= f
->alstate
;
4121 if (htp_state
== NULL
) {
4122 printf("no http state: ");
4126 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
));
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')
4145 printf("normalized uri \"");
4146 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4154 if (alp_tctx
!= NULL
)
4155 AppLayerParserThreadCtxFree(alp_tctx
);
4156 StreamTcpFreeConfig(TRUE
);
4161 /** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
4163 static int HTPParserTest13(void)
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 */
4170 HtpState
*htp_state
= NULL
;
4172 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4174 memset(&ssn
, 0, sizeof(ssn
));
4176 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4180 f
->proto
= IPPROTO_TCP
;
4181 f
->alproto
= ALPROTO_HTTP
;
4183 StreamTcpInitConfig(TRUE
);
4186 for (u
= 0; u
< httplen1
; u
++) {
4190 flags
= STREAM_TOSERVER
|STREAM_START
;
4191 else if (u
== (httplen1
- 1))
4192 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4194 flags
= STREAM_TOSERVER
;
4197 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
4200 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4208 htp_state
= f
->alstate
;
4209 if (htp_state
== NULL
) {
4210 printf("no http state: ");
4214 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4215 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
4220 char *name
= bstr_util_strdup_to_c(h
->name
);
4225 if (strcmp(name
, "Host") != 0) {
4226 printf("header name not \"Host\", instead \"%s\": ", name
);
4232 char *value
= bstr_util_strdup_to_c(h
->value
);
4233 if (value
== NULL
) {
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
));
4248 if (alp_tctx
!= NULL
)
4249 AppLayerParserThreadCtxFree(alp_tctx
);
4250 StreamTcpFreeConfig(TRUE
);
4255 /** \test Test basic config */
4256 static int HTPParserConfigTest01(void)
4270 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4271 personality: Tomcat_6_0\n\
4276 - 192.168.10.0/24\n\
4277 personality: IIS_7_0\n\
4280 ConfCreateContextBackup();
4283 ConfYamlLoadString(input
, strlen(input
));
4286 outputs
= ConfGetNode("libhtp.default-config.personality");
4287 if (outputs
== NULL
) {
4291 outputs
= ConfGetNode("libhtp.server-config");
4292 if (outputs
== NULL
) {
4296 ConfNode
*node
= TAILQ_FIRST(&outputs
->head
);
4300 if (strcmp(node
->name
, "0") != 0) {
4303 node
= TAILQ_FIRST(&node
->head
);
4307 if (strcmp(node
->name
, "apache-tomcat") != 0) {
4314 ConfNode
*node2
= ConfNodeLookupChild(node
, "personality");
4315 if (node2
== NULL
) {
4318 if (strcmp(node2
->val
, "Tomcat_6_0") != 0) {
4322 node
= ConfNodeLookupChild(node
, "address");
4326 TAILQ_FOREACH(n
, &node
->head
, next
) {
4333 if (strcmp(n
->name
, "0") != 0) {
4336 if (strcmp(n
->val
, "192.168.1.0/24") != 0) {
4341 if (strcmp(n
->name
, "1") != 0) {
4344 if (strcmp(n
->val
, "127.0.0.0/8") != 0) {
4349 if (strcmp(n
->name
, "2") != 0) {
4352 if (strcmp(n
->val
, "::1") != 0) {
4362 outputs
= ConfGetNode("libhtp.server-config");
4363 if (outputs
== NULL
) {
4367 node
= TAILQ_FIRST(&outputs
->head
);
4368 node
= TAILQ_NEXT(node
, next
);
4372 if (strcmp(node
->name
, "1") != 0) {
4375 node
= TAILQ_FIRST(&node
->head
);
4379 if (strcmp(node
->name
, "iis7") != 0) {
4383 node2
= ConfNodeLookupChild(node
, "personality");
4384 if (node2
== NULL
) {
4387 if (strcmp(node2
->val
, "IIS_7_0") != 0) {
4391 node
= ConfNodeLookupChild(node
, "address");
4397 TAILQ_FOREACH(n
, &node
->head
, next
) {
4404 if (strcmp(n
->name
, "0") != 0) {
4407 if (strcmp(n
->val
, "192.168.0.0/24") != 0) {
4412 if (strcmp(n
->name
, "1") != 0) {
4415 if (strcmp(n
->val
, "192.168.10.0/24") != 0) {
4429 ConfRestoreContextBackup();
4434 /** \test Test config builds radix correctly */
4435 static int HTPParserConfigTest02(void)
4449 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4450 personality: Tomcat_6_0\n\
4455 - 192.168.10.0/24\n\
4456 personality: IIS_7_0\n\
4459 ConfCreateContextBackup();
4461 HtpConfigCreateBackup();
4463 ConfYamlLoadString(input
, strlen(input
));
4467 if (cfglist
.cfg
== NULL
) {
4468 printf("No default config created.\n");
4472 if (cfgtree
== NULL
) {
4473 printf("No config tree created.\n");
4477 htp_cfg_t
*htp
= cfglist
.cfg
;
4480 void *user_data
= NULL
;
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
);
4491 printf("Could not get config for: %s\n", addr
);
4496 printf("Failed to parse address: %s\n", addr
);
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
);
4510 printf("Could not get config for: %s\n", addr
);
4515 printf("Failed to parse address: %s\n", addr
);
4524 ConfRestoreContextBackup();
4525 HtpConfigRestoreBackup();
4530 /** \test Test traffic is handled by the correct htp config */
4531 static int HTPParserConfigTest03(void)
4535 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
4537 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4539 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4541 HtpState
*htp_state
= NULL
;
4554 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4555 personality: Tomcat_6_0\n\
4560 - 192.168.10.0/24\n\
4561 personality: IIS_7_0\n\
4564 ConfCreateContextBackup();
4566 HtpConfigCreateBackup();
4568 ConfYamlLoadString(input
, strlen(input
));
4572 const char *addr
= "192.168.10.42";
4574 memset(&ssn
, 0, sizeof(ssn
));
4576 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4580 f
->proto
= IPPROTO_TCP
;
4581 f
->alproto
= ALPROTO_HTTP
;
4583 htp_cfg_t
*htp
= cfglist
.cfg
;
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
);
4593 printf("Could not get config for: %s\n", addr
);
4597 StreamTcpInitConfig(TRUE
);
4600 for (u
= 0; u
< httplen1
; u
++) {
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
;
4608 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
4611 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4620 htp_state
= f
->alstate
;
4621 if (htp_state
== NULL
) {
4622 printf("no http state: ");
4627 if (HTPStateGetTxCnt(htp_state
) != 2) {
4628 printf("HTPStateGetTxCnt(htp_state) failure\n");
4632 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4635 if (tx
->cfg
!= htp
) {
4636 printf("wrong HTP config (%p instead of %p - default=%p): ",
4637 tx
->cfg
, htp
, cfglist
.cfg
);
4640 tx
= HTPStateGetTx(htp_state
, 1);
4643 if (tx
->cfg
!= htp
) {
4644 printf("wrong HTP config (%p instead of %p - default=%p): ",
4645 tx
->cfg
, htp
, cfglist
.cfg
);
4650 if (alp_tctx
!= NULL
)
4651 AppLayerParserThreadCtxFree(alp_tctx
);
4654 ConfRestoreContextBackup();
4655 HtpConfigRestoreBackup();
4657 StreamTcpFreeConfig(TRUE
);
4662 /* disabled when we upgraded to libhtp 0.5.x */
4664 static int HTPParserConfigTest04(void)
4675 path-control-char-handling: status_400\n\
4676 path-convert-utf8: yes\n\
4677 path-invalid-encoding-handling: remove_percent\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\
4688 personality: IIS_7_0\n\
4689 path-replacement-char: o\n\
4690 path-unicode-mapping: status_400\n\
4693 ConfCreateContextBackup();
4695 HtpConfigCreateBackup();
4697 ConfYamlLoadString(input
, strlen(input
));
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");
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");
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");
4729 ConfRestoreContextBackup();
4730 HtpConfigRestoreBackup();
4736 /** \test Test %2f decoding in profile Apache_2_2
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
4742 static int HTPParserDecodingTest01(void)
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 */
4752 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4754 HtpState
*htp_state
= NULL
;
4762 personality: Apache_2\n\
4765 ConfCreateContextBackup();
4767 HtpConfigCreateBackup();
4768 ConfYamlLoadString(input
, strlen(input
));
4770 const char *addr
= "4.3.2.1";
4771 memset(&ssn
, 0, sizeof(ssn
));
4773 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4777 f
->proto
= IPPROTO_TCP
;
4778 f
->alproto
= ALPROTO_HTTP
;
4780 StreamTcpInitConfig(TRUE
);
4783 for (u
= 0; u
< httplen1
; u
++) {
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
;
4791 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
4794 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4802 htp_state
= f
->alstate
;
4803 if (htp_state
== NULL
) {
4804 printf("no http state: ");
4808 uint8_t ref1
[] = "/abc%2fdef";
4809 size_t reflen
= sizeof(ref1
) - 1;
4811 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
4819 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4823 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
4824 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4826 printf("normalized uri \"");
4827 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4829 PrintRawUriFp(stdout
, ref1
, reflen
);
4835 uint8_t ref2
[] = "/abc/def?ghi/jkl";
4836 reflen
= sizeof(ref2
) - 1;
4838 tx
= HTPStateGetTx(htp_state
, 1);
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
,
4846 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4850 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
4851 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4853 printf("normalized uri \"");
4854 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4856 PrintRawUriFp(stdout
, ref2
, reflen
);
4862 uint8_t ref3
[] = "/abc/def?ghi%2fjkl";
4863 reflen
= sizeof(ref3
) - 1;
4864 tx
= HTPStateGetTx(htp_state
, 2);
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
,
4872 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4876 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref3
,
4877 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4879 printf("normalized uri \"");
4880 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4882 PrintRawUriFp(stdout
, ref3
, reflen
);
4891 if (alp_tctx
!= NULL
)
4892 AppLayerParserThreadCtxFree(alp_tctx
);
4895 ConfRestoreContextBackup();
4896 HtpConfigRestoreBackup();
4898 StreamTcpFreeConfig(TRUE
);
4903 /** \test Test %2f decoding in profile IDS
4905 * %2f in path decoded to /
4906 * %2f in query string is decoded to /
4907 * %252f in query string is decoded to %2F
4909 static int HTPParserDecodingTest02(void)
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 */
4919 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4921 HtpState
*htp_state
= NULL
;
4930 double-decode-path: no\n\
4931 double-decode-query: no\n\
4934 ConfCreateContextBackup();
4936 HtpConfigCreateBackup();
4937 ConfYamlLoadString(input
, strlen(input
));
4939 const char *addr
= "4.3.2.1";
4940 memset(&ssn
, 0, sizeof(ssn
));
4942 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4946 f
->proto
= IPPROTO_TCP
;
4947 f
->alproto
= ALPROTO_HTTP
;
4949 StreamTcpInitConfig(TRUE
);
4952 for (u
= 0; u
< httplen1
; u
++) {
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
;
4960 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
4963 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4971 htp_state
= f
->alstate
;
4972 if (htp_state
== NULL
) {
4973 printf("no http state: ");
4977 uint8_t ref1
[] = "/abc/def";
4978 size_t reflen
= sizeof(ref1
) - 1;
4980 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
4988 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4992 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
4993 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4995 printf("normalized uri \"");
4996 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4998 PrintRawUriFp(stdout
, ref1
, reflen
);
5004 uint8_t ref2
[] = "/abc/def?ghi/jkl";
5005 reflen
= sizeof(ref2
) - 1;
5007 tx
= HTPStateGetTx(htp_state
, 1);
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
,
5015 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5019 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
5020 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5022 printf("normalized uri \"");
5023 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5025 PrintRawUriFp(stdout
, ref2
, reflen
);
5031 uint8_t ref3
[] = "/abc/def?ghi%2fjkl";
5032 reflen
= sizeof(ref3
) - 1;
5033 tx
= HTPStateGetTx(htp_state
, 2);
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): ",
5041 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5045 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref3
,
5046 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5048 printf("normalized uri \"");
5049 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5051 PrintRawUriFp(stdout
, ref3
, reflen
);
5060 if (alp_tctx
!= NULL
)
5061 AppLayerParserThreadCtxFree(alp_tctx
);
5064 ConfRestoreContextBackup();
5065 HtpConfigRestoreBackup();
5067 StreamTcpFreeConfig(TRUE
);
5072 /** \test Test %2f decoding in profile IDS with double-decode-* options
5074 * %252f in path decoded to /
5075 * %252f in query string is decoded to /
5077 static int HTPParserDecodingTest03(void)
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 */
5086 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5088 HtpState
*htp_state
= NULL
;
5097 double-decode-path: yes\n\
5098 double-decode-query: yes\n\
5101 ConfCreateContextBackup();
5103 HtpConfigCreateBackup();
5104 ConfYamlLoadString(input
, strlen(input
));
5106 const char *addr
= "4.3.2.1";
5107 memset(&ssn
, 0, sizeof(ssn
));
5109 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5113 f
->proto
= IPPROTO_TCP
;
5114 f
->alproto
= ALPROTO_HTTP
;
5116 StreamTcpInitConfig(TRUE
);
5119 for (u
= 0; u
< httplen1
; u
++) {
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
;
5127 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5130 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5138 htp_state
= f
->alstate
;
5139 if (htp_state
== NULL
) {
5140 printf("no http state: ");
5144 uint8_t ref1
[] = "/abc/def";
5145 size_t reflen
= sizeof(ref1
) - 1;
5147 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5155 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5159 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5160 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5162 printf("normalized uri \"");
5163 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5165 PrintRawUriFp(stdout
, ref1
, reflen
);
5171 uint8_t ref2
[] = "/abc/def?ghi/jkl";
5172 reflen
= sizeof(ref2
) - 1;
5174 tx
= HTPStateGetTx(htp_state
, 1);
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
,
5182 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5186 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
5187 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5189 printf("normalized uri \"");
5190 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5192 PrintRawUriFp(stdout
, ref2
, reflen
);
5201 if (alp_tctx
!= NULL
)
5202 AppLayerParserThreadCtxFree(alp_tctx
);
5205 ConfRestoreContextBackup();
5206 HtpConfigRestoreBackup();
5208 StreamTcpFreeConfig(TRUE
);
5213 /** \test Test http:// in query profile IDS
5215 static int HTPParserDecodingTest04(void)
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 */
5223 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5225 HtpState
*htp_state
= NULL
;
5234 double-decode-path: yes\n\
5235 double-decode-query: yes\n\
5238 ConfCreateContextBackup();
5240 HtpConfigCreateBackup();
5241 ConfYamlLoadString(input
, strlen(input
));
5243 const char *addr
= "4.3.2.1";
5244 memset(&ssn
, 0, sizeof(ssn
));
5246 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5250 f
->proto
= IPPROTO_TCP
;
5251 f
->alproto
= ALPROTO_HTTP
;
5253 StreamTcpInitConfig(TRUE
);
5256 for (u
= 0; u
< httplen1
; u
++) {
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
;
5264 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5267 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5275 htp_state
= f
->alstate
;
5276 if (htp_state
== NULL
) {
5277 printf("no http state: ");
5281 uint8_t ref1
[] = "/abc/def?a=http://www.abc.com/";
5282 size_t reflen
= sizeof(ref1
) - 1;
5284 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5292 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5296 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5297 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5299 printf("normalized uri \"");
5300 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5302 PrintRawUriFp(stdout
, ref1
, reflen
);
5311 if (alp_tctx
!= NULL
)
5312 AppLayerParserThreadCtxFree(alp_tctx
);
5315 ConfRestoreContextBackup();
5316 HtpConfigRestoreBackup();
5318 StreamTcpFreeConfig(TRUE
);
5323 /** \test Test \ char in query profile IDS. Bug 739
5325 static int HTPParserDecodingTest05(void)
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 */
5333 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5335 HtpState
*htp_state
= NULL
;
5344 double-decode-path: yes\n\
5345 double-decode-query: yes\n\
5348 ConfCreateContextBackup();
5350 HtpConfigCreateBackup();
5351 ConfYamlLoadString(input
, strlen(input
));
5353 const char *addr
= "4.3.2.1";
5354 memset(&ssn
, 0, sizeof(ssn
));
5356 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5360 f
->proto
= IPPROTO_TCP
;
5361 f
->alproto
= ALPROTO_HTTP
;
5363 StreamTcpInitConfig(TRUE
);
5366 for (u
= 0; u
< httplen1
; u
++) {
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
;
5374 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5377 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5385 htp_state
= f
->alstate
;
5386 if (htp_state
== NULL
) {
5387 printf("no http state: ");
5391 uint8_t ref1
[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
5392 size_t reflen
= sizeof(ref1
) - 1;
5394 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5402 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5406 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5407 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5409 printf("normalized uri \"");
5410 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5412 PrintRawUriFp(stdout
, ref1
, reflen
);
5421 if (alp_tctx
!= NULL
)
5422 AppLayerParserThreadCtxFree(alp_tctx
);
5425 ConfRestoreContextBackup();
5426 HtpConfigRestoreBackup();
5428 StreamTcpFreeConfig(TRUE
);
5433 /** \test Test + char in query. Bug 1035
5435 static int HTPParserDecodingTest06(void)
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 */
5443 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5445 HtpState
*htp_state
= NULL
;
5454 double-decode-path: yes\n\
5455 double-decode-query: yes\n\
5458 ConfCreateContextBackup();
5460 HtpConfigCreateBackup();
5461 ConfYamlLoadString(input
, strlen(input
));
5463 const char *addr
= "4.3.2.1";
5464 memset(&ssn
, 0, sizeof(ssn
));
5466 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5470 f
->proto
= IPPROTO_TCP
;
5471 f
->alproto
= ALPROTO_HTTP
;
5473 StreamTcpInitConfig(TRUE
);
5476 for (u
= 0; u
< httplen1
; u
++) {
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
;
5484 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5487 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5495 htp_state
= f
->alstate
;
5496 if (htp_state
== NULL
) {
5497 printf("no http state: ");
5501 uint8_t ref1
[] = "/put.php?ip=1.2.3.4&port=+6000";
5502 size_t reflen
= sizeof(ref1
) - 1;
5504 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5512 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5516 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5517 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5519 printf("normalized uri \"");
5520 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5522 PrintRawUriFp(stdout
, ref1
, reflen
);
5531 if (alp_tctx
!= NULL
)
5532 AppLayerParserThreadCtxFree(alp_tctx
);
5535 ConfRestoreContextBackup();
5536 HtpConfigRestoreBackup();
5538 StreamTcpFreeConfig(TRUE
);
5543 /** \test Test + char in query. Bug 1035
5545 static int HTPParserDecodingTest07(void)
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 */
5553 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5555 HtpState
*htp_state
= NULL
;
5564 double-decode-path: yes\n\
5565 double-decode-query: yes\n\
5566 query-plusspace-decode: yes\n\
5569 ConfCreateContextBackup();
5571 HtpConfigCreateBackup();
5572 ConfYamlLoadString(input
, strlen(input
));
5574 const char *addr
= "4.3.2.1";
5575 memset(&ssn
, 0, sizeof(ssn
));
5577 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5581 f
->proto
= IPPROTO_TCP
;
5582 f
->alproto
= ALPROTO_HTTP
;
5584 StreamTcpInitConfig(TRUE
);
5587 for (u
= 0; u
< httplen1
; u
++) {
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
;
5595 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5598 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5606 htp_state
= f
->alstate
;
5607 if (htp_state
== NULL
) {
5608 printf("no http state: ");
5612 uint8_t ref1
[] = "/put.php?ip=1.2.3.4&port= 6000";
5613 size_t reflen
= sizeof(ref1
) - 1;
5615 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5623 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5627 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5628 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5630 printf("normalized uri \"");
5631 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5633 PrintRawUriFp(stdout
, ref1
, reflen
);
5642 if (alp_tctx
!= NULL
)
5643 AppLayerParserThreadCtxFree(alp_tctx
);
5646 ConfRestoreContextBackup();
5647 HtpConfigRestoreBackup();
5649 StreamTcpFreeConfig(TRUE
);
5654 /** \test Test 'proxy' URI normalization. Ticket 1008
5656 static int HTPParserDecodingTest08(void)
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 */
5664 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5666 HtpState
*htp_state
= NULL
;
5677 ConfCreateContextBackup();
5679 HtpConfigCreateBackup();
5680 ConfYamlLoadString(input
, strlen(input
));
5682 const char *addr
= "4.3.2.1";
5683 memset(&ssn
, 0, sizeof(ssn
));
5685 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5689 f
->proto
= IPPROTO_TCP
;
5690 f
->alproto
= ALPROTO_HTTP
;
5692 StreamTcpInitConfig(TRUE
);
5695 for (u
= 0; u
< httplen1
; u
++) {
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
;
5703 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5706 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5714 htp_state
= f
->alstate
;
5715 if (htp_state
== NULL
) {
5716 printf("no http state: ");
5720 uint8_t ref1
[] = "/blah/";
5721 size_t reflen
= sizeof(ref1
) - 1;
5723 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5731 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5735 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5736 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5738 printf("normalized uri \"");
5739 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5741 PrintRawUriFp(stdout
, ref1
, reflen
);
5750 if (alp_tctx
!= NULL
)
5751 AppLayerParserThreadCtxFree(alp_tctx
);
5754 ConfRestoreContextBackup();
5755 HtpConfigRestoreBackup();
5757 StreamTcpFreeConfig(TRUE
);
5762 /** \test Test 'proxy' URI normalization. Ticket 1008
5764 static int HTPParserDecodingTest09(void)
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 */
5772 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5774 HtpState
*htp_state
= NULL
;
5783 uri-include-all: true\n\
5786 ConfCreateContextBackup();
5788 HtpConfigCreateBackup();
5789 ConfYamlLoadString(input
, strlen(input
));
5791 const char *addr
= "4.3.2.1";
5792 memset(&ssn
, 0, sizeof(ssn
));
5794 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5798 f
->proto
= IPPROTO_TCP
;
5799 f
->alproto
= ALPROTO_HTTP
;
5801 StreamTcpInitConfig(TRUE
);
5804 for (u
= 0; u
< httplen1
; u
++) {
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
;
5812 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
5815 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5823 htp_state
= f
->alstate
;
5824 if (htp_state
== NULL
) {
5825 printf("no http state: ");
5829 uint8_t ref1
[] = "http://suricata-ids.org/blah/";
5830 size_t reflen
= sizeof(ref1
) - 1;
5832 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
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
,
5840 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5844 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5845 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5847 printf("normalized uri \"");
5848 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5850 PrintRawUriFp(stdout
, ref1
, reflen
);
5859 if (alp_tctx
!= NULL
)
5860 AppLayerParserThreadCtxFree(alp_tctx
);
5863 ConfRestoreContextBackup();
5864 HtpConfigRestoreBackup();
5866 StreamTcpFreeConfig(TRUE
);
5871 /** \test BG box crash -- chunks are messed up. Observed for real. */
5872 static int HTPBodyReassemblyTest01(void)
5876 memset(&htud
, 0x00, sizeof(htud
));
5878 memset(&hstate
, 0x00, sizeof(hstate
));
5880 memset(&flow
, 0x00, sizeof(flow
));
5881 AppLayerParserState
*parser
= AppLayerParserStateAlloc();
5883 memset(&tx
, 0, sizeof(tx
));
5886 flow
.alparser
= parser
;
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";
5891 int r
= HtpBodyAppendChunk(NULL
, &htud
.request_body
, chunk1
, sizeof(chunk1
)-1);
5893 r
= HtpBodyAppendChunk(NULL
, &htud
.request_body
, chunk2
, sizeof(chunk2
)-1);
5896 const uint8_t *chunks_buffer
= NULL
;
5897 uint32_t chunks_buffer_len
= 0;
5899 HtpRequestBodyReassemble(&htud
, &chunks_buffer
, &chunks_buffer_len
);
5900 if (chunks_buffer
== NULL
) {
5904 printf("REASSCHUNK START: \n");
5905 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
5906 printf("REASSCHUNK END: \n");
5909 HtpRequestBodyHandleMultipart(&hstate
, &htud
, &tx
, chunks_buffer
, chunks_buffer_len
);
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
);
5916 if (hstate
.files_ts
!= NULL
)
5924 /** \test BG crash */
5925 static int HTPSegvTest01(void)
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 */
5938 double-decode-path: no\n\
5939 double-decode-query: no\n\
5940 request-body-limit: 0\n\
5941 response-body-limit: 0\n\
5944 ConfCreateContextBackup();
5946 HtpConfigCreateBackup();
5947 ConfYamlLoadString(input
, strlen(input
));
5951 HtpState
*http_state
= NULL
;
5952 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5954 memset(&ssn
, 0, sizeof(ssn
));
5956 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
5960 f
->proto
= IPPROTO_TCP
;
5961 f
->alproto
= ALPROTO_HTTP
;
5963 StreamTcpInitConfig(TRUE
);
5965 SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
5967 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
5968 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
5971 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
5976 SCLogDebug("\n>>>> processing chunk 1 again <<<<\n");
5978 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOSERVER
,
5979 httpbuf1
, httplen1
);
5981 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
5987 http_state
= f
->alstate
;
5988 if (http_state
== NULL
) {
5989 printf("no http state: ");
5994 AppLayerDecoderEvents
*decoder_events
= AppLayerParserGetDecoderEvents(f
->alparser
);
5995 if (decoder_events
!= NULL
) {
5996 printf("app events: ");
6003 if (alp_tctx
!= NULL
)
6004 AppLayerParserThreadCtxFree(alp_tctx
);
6007 ConfRestoreContextBackup();
6008 HtpConfigRestoreBackup();
6009 StreamTcpFreeConfig(TRUE
);
6014 /** \test Test really long request, this should result in HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6015 static int HTPParserTest14(void)
6026 double-decode-path: no\n\
6027 double-decode-query: no\n\
6028 request-body-limit: 0\n\
6029 response-body-limit: 0\n\
6031 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6032 FAIL_IF_NULL(alp_tctx
);
6034 memset(&ssn
, 0, sizeof(ssn
));
6036 ConfCreateContextBackup();
6038 HtpConfigCreateBackup();
6039 ConfYamlLoadString(input
, strlen(input
));
6042 char *httpbuf
= SCMalloc(len
);
6043 FAIL_IF_NULL(httpbuf
);
6044 memset(httpbuf
, 0x00, len
);
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"
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"
6055 size_t o
= strlen(httpbuf
);
6056 for ( ; o
< len
- 4; o
++) {
6059 httpbuf
[len
- 4] = '\r';
6060 httpbuf
[len
- 3] = '\n';
6061 httpbuf
[len
- 2] = '\r';
6062 httpbuf
[len
- 1] = '\n';
6064 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6067 f
->alproto
= ALPROTO_HTTP
;
6068 f
->proto
= IPPROTO_TCP
;
6070 StreamTcpInitConfig(TRUE
);
6073 for (u
= 0; u
< len
; u
++) {
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
;
6080 (void)AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
6081 (uint8_t *)&httpbuf
[u
], 1);
6083 HtpState
*htp_state
= f
->alstate
;
6084 FAIL_IF_NULL(htp_state
);
6086 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6088 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6089 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6091 AppLayerDecoderEvents
*decoder_events
= AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP
,f
->alstate
, 0);
6092 FAIL_IF_NULL(decoder_events
);
6094 FAIL_IF(decoder_events
->events
[0] != HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG
);
6096 AppLayerParserThreadCtxFree(alp_tctx
);
6097 StreamTcpFreeConfig(TRUE
);
6102 ConfRestoreContextBackup();
6103 HtpConfigRestoreBackup();
6107 /** \test Test really long request (same as HTPParserTest14), now with config
6108 * update to allow it */
6109 static int HTPParserTest15(void)
6113 char *httpbuf
= NULL
;
6116 HtpState
*htp_state
= NULL
;
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\
6131 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6133 memset(&ssn
, 0, sizeof(ssn
));
6135 ConfCreateContextBackup();
6137 HtpConfigCreateBackup();
6138 ConfYamlLoadString(input
, strlen(input
));
6141 httpbuf
= SCMalloc(len
);
6142 if (unlikely(httpbuf
== NULL
))
6144 memset(httpbuf
, 0x00, len
);
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"
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"
6155 size_t o
= strlen(httpbuf
);
6156 for ( ; o
< len
- 4; o
++) {
6159 httpbuf
[len
- 4] = '\r';
6160 httpbuf
[len
- 3] = '\n';
6161 httpbuf
[len
- 2] = '\r';
6162 httpbuf
[len
- 1] = '\n';
6164 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6168 f
->proto
= IPPROTO_TCP
;
6169 f
->alproto
= ALPROTO_HTTP
;
6171 StreamTcpInitConfig(TRUE
);
6174 for (u
= 0; u
< len
; u
++) {
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
;
6182 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
6183 (uint8_t *)&httpbuf
[u
], 1);
6185 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
6192 htp_state
= f
->alstate
;
6193 if (htp_state
== NULL
) {
6194 printf("no http state: ");
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
)
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
));
6208 AppLayerDecoderEvents
*decoder_events
= AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP
,f
->alstate
, 0);
6209 if (decoder_events
!= NULL
) {
6210 printf("app events: ");
6218 if (alp_tctx
!= NULL
)
6219 AppLayerParserThreadCtxFree(alp_tctx
);
6220 StreamTcpFreeConfig(TRUE
);
6222 if (httpbuf
!= NULL
)
6226 ConfRestoreContextBackup();
6227 HtpConfigRestoreBackup();
6231 /** \test Test unusual delims in request line HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6232 static int HTPParserTest16(void)
6237 HtpState
*htp_state
= NULL
;
6239 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6241 memset(&ssn
, 0, sizeof(ssn
));
6243 uint8_t httpbuf
[] = "GET\f/blah/\fHTTP/1.1\r\n"
6244 "Host: myhost.lan\r\n"
6245 "Connection: keep-alive\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;
6253 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6257 f
->proto
= IPPROTO_TCP
;
6258 f
->alproto
= ALPROTO_HTTP
;
6260 StreamTcpInitConfig(TRUE
);
6262 uint8_t flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
6265 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, flags
,
6266 (uint8_t *)httpbuf
, len
);
6268 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6274 htp_state
= f
->alstate
;
6275 if (htp_state
== NULL
) {
6276 printf("no http state: ");
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
)
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");
6290 AppLayerDecoderEvents
*decoder_events
= AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP
,f
->alstate
, 0);
6291 if (decoder_events
== NULL
) {
6292 printf("no app events: ");
6298 if (decoder_events
->events
[0] != HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT
) {
6299 printf("HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT not set: ");
6303 if (decoder_events
->events
[1] != HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT
) {
6304 printf("HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT not set: ");
6310 if (alp_tctx
!= NULL
)
6311 AppLayerParserThreadCtxFree(alp_tctx
);
6312 StreamTcpFreeConfig(TRUE
);
6317 /** \test CONNECT with plain text HTTP being tunneled */
6318 static int HTPParserTest17(void)
6322 HtpState
*http_state
= NULL
;
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 */
6334 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6336 memset(&ssn
, 0, sizeof(ssn
));
6338 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6342 f
->proto
= IPPROTO_TCP
;
6343 f
->alproto
= ALPROTO_HTTP
;
6345 StreamTcpInitConfig(TRUE
);
6348 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6349 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6352 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6357 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6358 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6361 printf("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
6365 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOSERVER
,
6366 httpbuf3
, httplen3
);
6368 printf("toserver chunk 3 returned %" PRId32
", expected 0: ", r
);
6373 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOCLIENT
,
6374 httpbuf4
, httplen4
);
6376 printf("toserver chunk 4 returned %" PRId32
", expected 0: ", r
);
6383 http_state
= f
->alstate
;
6384 if (http_state
== NULL
) {
6385 printf("no http state: ");
6389 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
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
)
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
));
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
));
6410 tx
= HTPStateGetTx(http_state
, 1);
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
)
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
));
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
));
6432 if (alp_tctx
!= NULL
)
6433 AppLayerParserThreadCtxFree(alp_tctx
);
6434 StreamTcpFreeConfig(TRUE
);
6439 /** \test CONNECT with plain text HTTP being tunneled */
6440 static int HTPParserTest18(void)
6444 HtpState
*http_state
= NULL
;
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 */
6458 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6460 memset(&ssn
, 0, sizeof(ssn
));
6462 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6466 f
->proto
= IPPROTO_TCP
;
6467 f
->alproto
= ALPROTO_HTTP
;
6469 StreamTcpInitConfig(TRUE
);
6472 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6473 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6476 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6481 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6482 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6485 printf("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
6489 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOSERVER
,
6490 httpbuf3
, httplen3
);
6492 printf("toserver chunk 3 returned %" PRId32
", expected 0: ", r
);
6496 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOSERVER
,
6497 httpbuf4
, httplen4
);
6499 printf("toserver chunk 4 returned %" PRId32
", expected 0: ", r
);
6505 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOCLIENT
,
6506 httpbuf5
, httplen5
);
6508 printf("toserver chunk 5 returned %" PRId32
", expected 0: ", r
);
6515 http_state
= f
->alstate
;
6516 if (http_state
== NULL
) {
6517 printf("no http state: ");
6521 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
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
)
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
));
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
));
6542 tx
= HTPStateGetTx(http_state
, 1);
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
)
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
));
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
));
6564 if (alp_tctx
!= NULL
)
6565 AppLayerParserThreadCtxFree(alp_tctx
);
6566 StreamTcpFreeConfig(TRUE
);
6571 /** \test CONNECT with TLS content (start of it at least) */
6572 static int HTPParserTest19(void)
6576 HtpState
*http_state
= NULL
;
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 */
6586 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6588 memset(&ssn
, 0, sizeof(ssn
));
6590 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6594 f
->proto
= IPPROTO_TCP
;
6595 f
->alproto
= ALPROTO_HTTP
;
6597 StreamTcpInitConfig(TRUE
);
6600 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6601 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6604 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6609 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6610 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6613 printf("toserver chunk 2 returned %" PRId32
", expected 0: ", r
);
6617 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
, STREAM_TOSERVER
,
6618 httpbuf3
, httplen3
);
6620 printf("toserver chunk 3 returned %" PRId32
", expected 0: ", r
);
6627 http_state
= f
->alstate
;
6628 if (http_state
== NULL
) {
6629 printf("no http state: ");
6633 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
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
)
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
));
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
));
6654 /* no new tx should have been set up for the tunneled data */
6655 tx
= HTPStateGetTx(http_state
, 1);
6661 if (alp_tctx
!= NULL
)
6662 AppLayerParserThreadCtxFree(alp_tctx
);
6663 StreamTcpFreeConfig(TRUE
);
6668 /** \test Test response not HTTP
6670 static int HTPParserTest20(void)
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 */
6682 HtpState
*http_state
= NULL
;
6683 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6684 FAIL_IF_NULL(alp_tctx
);
6686 memset(&ssn
, 0, sizeof(ssn
));
6688 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6691 f
->proto
= IPPROTO_TCP
;
6692 f
->alproto
= ALPROTO_HTTP
;
6694 StreamTcpInitConfig(TRUE
);
6696 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6697 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6701 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6702 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6706 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6707 STREAM_TOCLIENT
| STREAM_START
, httpbuf3
,
6711 http_state
= f
->alstate
;
6712 FAIL_IF_NULL(http_state
);
6713 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6715 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6718 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6719 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6721 FAIL_IF(tx
->response_status_number
!= 0);
6722 FAIL_IF(tx
->response_protocol_number
!= -1);
6724 AppLayerParserThreadCtxFree(alp_tctx
);
6725 StreamTcpFreeConfig(TRUE
);
6730 /** \test Test response not HTTP
6732 static int HTPParserTest21(void)
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 */
6744 HtpState
*http_state
= NULL
;
6745 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6746 FAIL_IF_NULL(alp_tctx
);
6748 memset(&ssn
, 0, sizeof(ssn
));
6750 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6753 f
->proto
= IPPROTO_TCP
;
6754 f
->alproto
= ALPROTO_HTTP
;
6756 StreamTcpInitConfig(TRUE
);
6758 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6759 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6763 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6764 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6768 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6769 STREAM_TOCLIENT
| STREAM_START
, httpbuf3
,
6773 http_state
= f
->alstate
;
6774 FAIL_IF_NULL(http_state
);
6775 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6777 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6780 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6781 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6783 FAIL_IF(tx
->response_status_number
!= 0);
6784 FAIL_IF(tx
->response_protocol_number
!= -1);
6786 AppLayerParserThreadCtxFree(alp_tctx
);
6787 StreamTcpFreeConfig(TRUE
);
6792 /** \test Test response not HTTP
6794 static int HTPParserTest22(void)
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 */
6805 HtpState
*http_state
= NULL
;
6806 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6807 FAIL_IF_NULL(alp_tctx
);
6809 memset(&ssn
, 0, sizeof(ssn
));
6811 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6814 f
->proto
= IPPROTO_TCP
;
6815 f
->alproto
= ALPROTO_HTTP
;
6817 StreamTcpInitConfig(TRUE
);
6819 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6820 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6824 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6825 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6829 http_state
= f
->alstate
;
6830 FAIL_IF_NULL(http_state
);
6831 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6833 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6836 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6837 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6839 FAIL_IF(tx
->response_status_number
!= -0);
6840 FAIL_IF(tx
->response_protocol_number
!= -1);
6842 AppLayerParserThreadCtxFree(alp_tctx
);
6843 StreamTcpFreeConfig(TRUE
);
6848 /** \test Test response not HTTP
6850 static int HTPParserTest23(void)
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 */
6861 HtpState
*http_state
= NULL
;
6862 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6863 FAIL_IF_NULL(alp_tctx
);
6865 memset(&ssn
, 0, sizeof(ssn
));
6867 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6870 f
->proto
= IPPROTO_TCP
;
6871 f
->alproto
= ALPROTO_HTTP
;
6873 StreamTcpInitConfig(TRUE
);
6875 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6876 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6880 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6881 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6885 http_state
= f
->alstate
;
6886 FAIL_IF_NULL(http_state
);
6887 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6889 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6892 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6893 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6895 FAIL_IF(tx
->response_status_number
!= -1);
6896 FAIL_IF(tx
->response_protocol_number
!= -2);
6898 AppLayerParserThreadCtxFree(alp_tctx
);
6899 StreamTcpFreeConfig(TRUE
);
6904 /** \test Test response not HTTP
6906 static int HTPParserTest24(void)
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 */
6917 HtpState
*http_state
= NULL
;
6918 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6919 FAIL_IF_NULL(alp_tctx
);
6921 memset(&ssn
, 0, sizeof(ssn
));
6923 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6926 f
->proto
= IPPROTO_TCP
;
6927 f
->alproto
= ALPROTO_HTTP
;
6929 StreamTcpInitConfig(TRUE
);
6931 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6932 STREAM_TOSERVER
| STREAM_START
, httpbuf1
,
6936 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP
,
6937 STREAM_TOCLIENT
| STREAM_START
, httpbuf2
,
6941 http_state
= f
->alstate
;
6942 FAIL_IF_NULL(http_state
);
6943 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6945 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6948 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6949 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6951 FAIL_IF(tx
->response_status_number
!= -1);
6952 FAIL_IF(tx
->response_protocol_number
!= HTP_PROTOCOL_1_0
);
6954 AppLayerParserThreadCtxFree(alp_tctx
);
6955 StreamTcpFreeConfig(TRUE
);
6960 /** \test multi transactions and cleanup */
6961 static int HTPParserTest25(void)
6963 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6964 FAIL_IF_NULL(alp_tctx
);
6966 StreamTcpInitConfig(TRUE
);
6968 memset(&ssn
, 0, sizeof(ssn
));
6970 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6973 f
->proto
= IPPROTO_TCP
;
6974 f
->alproto
= ALPROTO_HTTP
;
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);
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);
7028 AppLayerParserTransactionsCleanup(f
);
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
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
);
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
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
);
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
7059 HtpState
*http_state
= f
->alstate
;
7060 FAIL_IF_NULL(http_state
);
7062 AppLayerParserThreadCtxFree(alp_tctx
);
7063 StreamTcpFreeConfig(TRUE
);
7069 #endif /* UNITTESTS */
7072 * \brief Register the Unit tests for the HTTP protocol
7074 void HTPParserRegisterTests(void)
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);
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
);
7110 UtRegisterTest("HTPBodyReassemblyTest01", HTPBodyReassemblyTest01
);
7112 UtRegisterTest("HTPSegvTest01", HTPSegvTest01
);
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
);
7127 HTPFileParserRegisterTests();
7128 HTPXFFParserRegisterTests();
7129 #endif /* UNITTESTS */