1 /* Copyright (C) 2007-2020 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"
48 #include "util-byte.h"
50 #include "stream-tcp-private.h"
51 #include "stream-tcp-reassemble.h"
52 #include "stream-tcp.h"
55 #include "app-layer-protos.h"
56 #include "app-layer-parser.h"
58 #include "app-layer.h"
59 #include "app-layer-htp.h"
60 #include "app-layer-htp-body.h"
61 #include "app-layer-htp-file.h"
62 #include "app-layer-htp-libhtp.h"
63 #include "app-layer-htp-xff.h"
66 #include "util-debug.h"
67 #include "util-time.h"
68 #include "util-misc.h"
70 #include "util-unittest.h"
71 #include "util-unittest-helper.h"
72 #include "flow-util.h"
74 #include "detect-engine.h"
75 #include "detect-engine-state.h"
76 #include "detect-parse.h"
78 #include "decode-events.h"
80 #include "util-memcmp.h"
81 #include "util-random.h"
82 #include "util-validate.h"
86 /** Fast lookup tree (radix) for the various HTP configurations */
87 static SCRadixTree
*cfgtree
;
88 /** List of HTP configurations. */
89 static HTPCfgRec cfglist
;
91 /** Limit to the number of libhtp messages that can be handled */
92 #define HTP_MAX_MESSAGES 512
94 SC_ATOMIC_DECLARE(uint32_t, htp_config_flags
);
97 static SCMutex htp_state_mem_lock
= SCMUTEX_INITIALIZER
;
98 static uint64_t htp_state_memuse
= 0;
99 static uint64_t htp_state_memcnt
= 0;
102 SCEnumCharMap http_decoder_event_table
[] = {
103 { "UNKNOWN_ERROR", HTTP_DECODER_EVENT_UNKNOWN_ERROR
},
104 { "GZIP_DECOMPRESSION_FAILED", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED
},
105 { "REQUEST_FIELD_MISSING_COLON", HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON
},
106 { "RESPONSE_FIELD_MISSING_COLON", HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON
},
107 { "INVALID_REQUEST_CHUNK_LEN", HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN
},
108 { "INVALID_RESPONSE_CHUNK_LEN", 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 { "DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST",
118 HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST
},
119 { "DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE",
120 HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE
},
121 { "100_CONTINUE_ALREADY_SEEN", HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN
},
122 { "UNABLE_TO_MATCH_RESPONSE_TO_REQUEST",
123 HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST
},
124 { "INVALID_SERVER_PORT_IN_REQUEST", HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST
},
125 { "INVALID_AUTHORITY_PORT", HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT
},
126 { "REQUEST_HEADER_INVALID", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID
},
127 { "RESPONSE_HEADER_INVALID", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID
},
128 { "MISSING_HOST_HEADER", HTTP_DECODER_EVENT_MISSING_HOST_HEADER
},
129 { "HOST_HEADER_AMBIGUOUS", HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS
},
130 { "INVALID_REQUEST_FIELD_FOLDING", HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING
},
131 { "INVALID_RESPONSE_FIELD_FOLDING", HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING
},
132 { "REQUEST_FIELD_TOO_LONG", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG
},
133 { "RESPONSE_FIELD_TOO_LONG", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG
},
134 { "REQUEST_LINE_INVALID", HTTP_DECODER_EVENT_REQUEST_LINE_INVALID
},
135 { "REQUEST_BODY_UNEXPECTED", HTTP_DECODER_EVENT_REQUEST_BODY_UNEXPECTED
},
136 { "REQUEST_SERVER_PORT_TCP_PORT_MISMATCH",
137 HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH
},
138 { "REQUEST_URI_HOST_INVALID", HTTP_DECODER_EVENT_URI_HOST_INVALID
},
139 { "REQUEST_HEADER_HOST_INVALID", HTTP_DECODER_EVENT_HEADER_HOST_INVALID
},
140 { "REQUEST_AUTH_UNRECOGNIZED", HTTP_DECODER_EVENT_AUTH_UNRECOGNIZED
},
141 { "REQUEST_HEADER_REPETITION", HTTP_DECODER_EVENT_REQUEST_HEADER_REPETITION
},
142 { "RESPONSE_HEADER_REPETITION", HTTP_DECODER_EVENT_RESPONSE_HEADER_REPETITION
},
143 { "DOUBLE_ENCODED_URI", HTTP_DECODER_EVENT_DOUBLE_ENCODED_URI
},
144 { "URI_DELIM_NON_COMPLIANT", HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT
},
145 { "METHOD_DELIM_NON_COMPLIANT", HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT
},
146 { "REQUEST_LINE_LEADING_WHITESPACE", HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE
},
147 { "TOO_MANY_ENCODING_LAYERS", HTTP_DECODER_EVENT_TOO_MANY_ENCODING_LAYERS
},
148 { "ABNORMAL_CE_HEADER", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
149 { "RESPONSE_MULTIPART_BYTERANGES", HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES
},
150 { "RESPONSE_ABNORMAL_TRANSFER_ENCODING",
151 HTTP_DECODER_EVENT_RESPONSE_ABNORMAL_TRANSFER_ENCODING
},
152 { "RESPONSE_CHUNKED_OLD_PROTO", HTTP_DECODER_EVENT_RESPONSE_CHUNKED_OLD_PROTO
},
153 { "RESPONSE_INVALID_PROTOCOL", HTTP_DECODER_EVENT_RESPONSE_INVALID_PROTOCOL
},
154 { "RESPONSE_INVALID_STATUS", HTTP_DECODER_EVENT_RESPONSE_INVALID_STATUS
},
155 { "REQUEST_LINE_INCOMPLETE", HTTP_DECODER_EVENT_REQUEST_LINE_INCOMPLETE
},
157 { "LZMA_MEMLIMIT_REACHED", HTTP_DECODER_EVENT_LZMA_MEMLIMIT_REACHED
},
158 { "COMPRESSION_BOMB", HTTP_DECODER_EVENT_COMPRESSION_BOMB
},
160 { "RANGE_INVALID", HTTP_DECODER_EVENT_RANGE_INVALID
},
162 /* suricata warnings/errors */
163 { "MULTIPART_GENERIC_ERROR", HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
},
164 { "MULTIPART_NO_FILEDATA", HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA
},
165 { "MULTIPART_INVALID_HEADER", HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
},
167 { "TOO_MANY_WARNINGS", HTTP_DECODER_EVENT_TOO_MANY_WARNINGS
},
172 static void *HTPStateGetTx(void *alstate
, uint64_t tx_id
);
173 static int HTPStateGetAlstateProgress(void *tx
, uint8_t direction
);
174 static uint64_t HTPStateGetTxCnt(void *alstate
);
176 static void HTPParserRegisterTests(void);
179 static inline uint64_t HtpGetActiveRequestTxID(HtpState
*s
)
181 uint64_t id
= HTPStateGetTxCnt(s
);
186 static inline uint64_t HtpGetActiveResponseTxID(HtpState
*s
)
188 return s
->transaction_cnt
;
195 * \brief Lookup the HTP personality string from the numeric personality.
197 * \todo This needs to be a libhtp function.
199 static const char *HTPLookupPersonalityString(int p
)
201 #define CASE_HTP_PERSONALITY_STRING(p) \
202 case HTP_SERVER_ ## p: return #p
205 CASE_HTP_PERSONALITY_STRING(MINIMAL
);
206 CASE_HTP_PERSONALITY_STRING(GENERIC
);
207 CASE_HTP_PERSONALITY_STRING(IDS
);
208 CASE_HTP_PERSONALITY_STRING(IIS_4_0
);
209 CASE_HTP_PERSONALITY_STRING(IIS_5_0
);
210 CASE_HTP_PERSONALITY_STRING(IIS_5_1
);
211 CASE_HTP_PERSONALITY_STRING(IIS_6_0
);
212 CASE_HTP_PERSONALITY_STRING(IIS_7_0
);
213 CASE_HTP_PERSONALITY_STRING(IIS_7_5
);
214 CASE_HTP_PERSONALITY_STRING(APACHE_2
);
224 * \brief Lookup the numeric HTP personality from a string.
226 * \todo This needs to be a libhtp function.
228 static int HTPLookupPersonality(const char *str
)
230 #define IF_HTP_PERSONALITY_NUM(p) \
231 if (strcasecmp(#p, str) == 0) return HTP_SERVER_ ## p
233 IF_HTP_PERSONALITY_NUM(MINIMAL
);
234 IF_HTP_PERSONALITY_NUM(GENERIC
);
235 IF_HTP_PERSONALITY_NUM(IDS
);
236 IF_HTP_PERSONALITY_NUM(IIS_4_0
);
237 IF_HTP_PERSONALITY_NUM(IIS_5_0
);
238 IF_HTP_PERSONALITY_NUM(IIS_5_1
);
239 IF_HTP_PERSONALITY_NUM(IIS_6_0
);
240 IF_HTP_PERSONALITY_NUM(IIS_7_0
);
241 IF_HTP_PERSONALITY_NUM(IIS_7_5
);
242 IF_HTP_PERSONALITY_NUM(APACHE_2
);
243 if (strcasecmp("TOMCAT_6_0", str
) == 0) {
244 SCLogError(SC_WARN_OPTION_OBSOLETE
, "Personality %s no "
245 "longer supported by libhtp.", str
);
247 } else if ((strcasecmp("APACHE", str
) == 0) ||
248 (strcasecmp("APACHE_2_2", str
) == 0))
250 SCLogWarning(SC_WARN_OPTION_OBSOLETE
, "Personality %s no "
251 "longer supported by libhtp, failing back to "
252 "Apache2 personality.", str
);
253 return HTP_SERVER_APACHE_2
;
259 static void HTPSetEvent(HtpState
*s
, HtpTxUserData
*htud
,
260 const uint8_t dir
, const uint8_t e
)
262 SCLogDebug("setting event %u", e
);
265 AppLayerDecoderEventsSetEventRaw(&htud
->tx_data
.events
, e
);
270 const uint64_t tx_id
= (dir
== STREAM_TOSERVER
) ?
271 HtpGetActiveRequestTxID(s
) : HtpGetActiveResponseTxID(s
);
273 htp_tx_t
*tx
= HTPStateGetTx(s
, tx_id
);
274 if (tx
== NULL
&& tx_id
> 0)
275 tx
= HTPStateGetTx(s
, tx_id
- 1);
277 htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
279 AppLayerDecoderEventsSetEventRaw(&htud
->tx_data
.events
, e
);
284 SCLogDebug("couldn't set event %u", e
);
287 /** \brief Function to allocates the HTTP state memory and also creates the HTTP
288 * connection parser to be used by the HTP library
290 static void *HTPStateAlloc(void *orig_state
, AppProto proto_orig
)
294 HtpState
*s
= HTPMalloc(sizeof(HtpState
));
295 if (unlikely(s
== NULL
)) {
296 SCReturnPtr(NULL
, "void");
299 memset(s
, 0x00, sizeof(HtpState
));
302 SCMutexLock(&htp_state_mem_lock
);
304 htp_state_memuse
+= sizeof(HtpState
);
305 SCLogDebug("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
306 SCMutexUnlock(&htp_state_mem_lock
);
309 SCReturnPtr((void *)s
, "void");
312 static void HtpTxUserDataFree(HtpState
*state
, HtpTxUserData
*htud
)
315 HtpBodyFree(&htud
->request_body
);
316 HtpBodyFree(&htud
->response_body
);
317 bstr_free(htud
->request_uri_normalized
);
318 if (htud
->request_headers_raw
)
319 HTPFree(htud
->request_headers_raw
, htud
->request_headers_raw_len
);
320 if (htud
->response_headers_raw
)
321 HTPFree(htud
->response_headers_raw
, htud
->response_headers_raw_len
);
322 AppLayerDecoderEventsFreeEvents(&htud
->tx_data
.events
);
324 HTPFree(htud
->boundary
, htud
->boundary_len
);
325 if (htud
->tx_data
.de_state
!= NULL
) {
326 DetectEngineStateFree(htud
->tx_data
.de_state
);
328 HTPFree(htud
, sizeof(HtpTxUserData
));
332 /** \brief Function to frees the HTTP state memory and also frees the HTTP
333 * connection parser memory which was used by the HTP library
335 void HTPStateFree(void *state
)
339 HtpState
*s
= (HtpState
*)state
;
344 /* free the connection parser memory used by HTP library */
345 if (s
->connp
!= NULL
) {
346 SCLogDebug("freeing HTP state");
349 uint64_t total_txs
= HTPStateGetTxCnt(state
);
350 /* free the list of body chunks */
351 if (s
->conn
!= NULL
) {
352 for (tx_id
= 0; tx_id
< total_txs
; tx_id
++) {
353 htp_tx_t
*tx
= HTPStateGetTx(s
, tx_id
);
355 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
356 HtpTxUserDataFree(s
, htud
);
357 htp_tx_set_user_data(tx
, NULL
);
361 htp_connp_destroy_all(s
->connp
);
365 HTPFileCloseHandleRange(s
->files_tc
, 0, s
->file_range
, NULL
, 0);
366 HttpRangeFreeBlock(s
->file_range
);
369 FileContainerFree(s
->files_ts
);
370 FileContainerFree(s
->files_tc
);
371 HTPFree(s
, sizeof(HtpState
));
374 SCMutexLock(&htp_state_mem_lock
);
376 htp_state_memuse
-= sizeof(HtpState
);
377 SCLogDebug("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
378 SCMutexUnlock(&htp_state_mem_lock
);
385 * \brief HTP transaction cleanup callback
387 * \warning We cannot actually free the transactions here. It seems that
388 * HTP only accepts freeing of transactions in the response callback.
390 static void HTPStateTransactionFree(void *state
, uint64_t id
)
394 HtpState
*s
= (HtpState
*)state
;
396 SCLogDebug("state %p, id %"PRIu64
, s
, id
);
398 htp_tx_t
*tx
= HTPStateGetTx(s
, id
);
400 /* This will remove obsolete body chunks */
401 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
402 HtpTxUserDataFree(s
, htud
);
403 htp_tx_set_user_data(tx
, NULL
);
405 /* hack: even if libhtp considers the tx incomplete, we want to
406 * free it here. htp_tx_destroy however, will refuse to do this.
407 * As htp_tx_destroy_incomplete isn't available in the public API,
408 * we hack around it here. */
410 tx
->request_progress
== HTP_REQUEST_COMPLETE
&&
411 tx
->response_progress
== HTP_RESPONSE_COMPLETE
)))
413 tx
->request_progress
= HTP_REQUEST_COMPLETE
;
414 tx
->response_progress
= HTP_RESPONSE_COMPLETE
;
421 * \brief Sets a flag that informs the HTP app layer that some module in the
422 * engine needs the http request body data.
425 void AppLayerHtpEnableRequestBodyCallback(void)
429 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_BODY
);
434 * \brief Sets a flag that informs the HTP app layer that some module in the
435 * engine needs the http request body data.
438 void AppLayerHtpEnableResponseBodyCallback(void)
442 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_RESPONSE_BODY
);
447 * \brief Sets a flag that informs the HTP app layer that some module in the
448 * engine needs the http request multi part header.
452 static void AppLayerHtpNeedMultipartHeader(void)
455 AppLayerHtpEnableRequestBodyCallback();
457 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_MULTIPART
);
462 * \brief Sets a flag that informs the HTP app layer that some module in the
463 * engine needs the http request file.
467 void AppLayerHtpNeedFileInspection(void)
470 AppLayerHtpNeedMultipartHeader();
471 AppLayerHtpEnableRequestBodyCallback();
472 AppLayerHtpEnableResponseBodyCallback();
474 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_FILE
);
478 static void AppLayerHtpSetStreamDepthFlag(void *tx
, uint8_t flags
)
480 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data((htp_tx_t
*)tx
);
482 if (flags
& STREAM_TOCLIENT
) {
483 tx_ud
->tcflags
|= HTP_STREAM_DEPTH_SET
;
485 tx_ud
->tsflags
|= HTP_STREAM_DEPTH_SET
;
490 static bool AppLayerHtpCheckDepth(const HTPCfgDir
*cfg
, HtpBody
*body
, uint8_t flags
)
492 if (flags
& HTP_STREAM_DEPTH_SET
) {
493 uint32_t stream_depth
= FileReassemblyDepth();
494 if (body
->content_len_so_far
< (uint64_t)stream_depth
|| stream_depth
== 0) {
498 if (cfg
->body_limit
== 0 || body
->content_len_so_far
< cfg
->body_limit
) {
505 static uint32_t AppLayerHtpComputeChunkLength(uint64_t content_len_so_far
, uint32_t body_limit
,
506 uint32_t stream_depth
, uint8_t flags
, uint32_t data_len
)
508 uint32_t chunk_len
= 0;
509 if (!(flags
& HTP_STREAM_DEPTH_SET
) && body_limit
> 0 &&
510 (content_len_so_far
< (uint64_t)body_limit
) &&
511 (content_len_so_far
+ (uint64_t)data_len
) > body_limit
)
513 chunk_len
= body_limit
- content_len_so_far
;
514 } else if ((flags
& HTP_STREAM_DEPTH_SET
) && stream_depth
> 0 &&
515 (content_len_so_far
< (uint64_t)stream_depth
) &&
516 (content_len_so_far
+ (uint64_t)data_len
) > stream_depth
)
518 chunk_len
= stream_depth
- content_len_so_far
;
520 SCLogDebug("len %u", chunk_len
);
521 return (chunk_len
== 0 ? data_len
: chunk_len
);
524 /* below error messages updated up to libhtp 0.5.7 (git 379632278b38b9a792183694a4febb9e0dbd1e7a) */
529 { "GZip decompressor: inflateInit2 failed", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED
},
530 { "Request field invalid: colon missing", HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON
},
531 { "Response field invalid: missing colon", HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON
},
532 { "Request chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN
},
533 { "Response chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN
},
534 /* { "Invalid T-E value in request", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_T_E
535 { "Invalid T-E value in response", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE}, <- nothing to replace it */
536 /* { "Invalid C-L field in request", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_C_L */
537 { "Invalid C-L field in response", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE
},
538 { "Already seen 100-Continue", HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN
},
539 { "Unable to match response to request", HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST
},
540 { "Invalid server port information in request", HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST
},
541 /* { "Invalid authority port", HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT}, htp no longer returns this error */
542 { "Request buffer over", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG
},
543 { "Response buffer over", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG
},
544 { "C-T multipart/byteranges in responses not supported", HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES
},
545 { "Compression bomb:", HTTP_DECODER_EVENT_COMPRESSION_BOMB
},
552 { "GZip decompressor:", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED
},
553 { "Request field invalid", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID
},
554 { "Response field invalid", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID
},
555 { "Request header name is not a token", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID
},
556 { "Response header name is not a token", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID
},
557 /* { "Host information in request headers required by HTTP/1.1", HTTP_DECODER_EVENT_MISSING_HOST_HEADER}, <- tx flag HTP_HOST_MISSING
558 { "Host information ambiguous", HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS}, <- tx flag HTP_HOST_AMBIGUOUS */
559 { "Invalid request field folding", HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING
},
560 { "Invalid response field folding", HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING
},
561 /* 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);
562 * luckily, "Request server port=" is unique */
563 /* { "Request server port number differs from the actual TCP port", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH}, */
564 { "Request server port=", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH
},
565 { "Request line: URI contains non-compliant delimiter", HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT
},
566 { "Request line: non-compliant delimiter between Method and URI", HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT
},
567 { "Request line: leading whitespace", HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE
},
568 { "Too many response content encoding layers", HTTP_DECODER_EVENT_TOO_MANY_ENCODING_LAYERS
},
569 { "C-E gzip has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
570 { "C-E deflate has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
571 { "C-E unknown setting", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
572 { "Excessive request header repetitions", HTTP_DECODER_EVENT_REQUEST_HEADER_REPETITION
},
573 { "Excessive response header repetitions", HTTP_DECODER_EVENT_RESPONSE_HEADER_REPETITION
},
574 { "Transfer-encoding has abnormal chunked value", HTTP_DECODER_EVENT_RESPONSE_ABNORMAL_TRANSFER_ENCODING
},
575 { "Chunked transfer-encoding on HTTP/0.9 or HTTP/1.0", HTTP_DECODER_EVENT_RESPONSE_CHUNKED_OLD_PROTO
},
576 { "Invalid response line: invalid protocol", HTTP_DECODER_EVENT_RESPONSE_INVALID_PROTOCOL
},
577 { "Invalid response line: invalid response status", HTTP_DECODER_EVENT_RESPONSE_INVALID_STATUS
},
578 { "Request line incomplete", HTTP_DECODER_EVENT_REQUEST_LINE_INCOMPLETE
},
579 { "Unexpected request body", HTTP_DECODER_EVENT_REQUEST_BODY_UNEXPECTED
},
580 { "LZMA decompressor: memory limit reached", HTTP_DECODER_EVENT_LZMA_MEMLIMIT_REACHED
},
581 { "Ambiguous request C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST
},
582 { "Ambiguous response C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE
},
585 #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0]))
586 #define HTP_WARNING_MAX (sizeof(htp_warnings) / sizeof(htp_warnings[0]))
591 * \brief Get the warning id for the warning msg.
593 * \param msg warning message
595 * \retval id the id or 0 in case of not found
597 static int HTPHandleWarningGetId(const char *msg
)
599 SCLogDebug("received warning \"%s\"", msg
);
601 for (idx
= 0; idx
< HTP_WARNING_MAX
; idx
++) {
602 if (strncmp(htp_warnings
[idx
].msg
, msg
,
603 strlen(htp_warnings
[idx
].msg
)) == 0)
605 return htp_warnings
[idx
].de
;
615 * \brief Get the error id for the error msg.
617 * \param msg error message
619 * \retval id the id or 0 in case of not found
621 static int HTPHandleErrorGetId(const char *msg
)
623 SCLogDebug("received error \"%s\"", msg
);
626 for (idx
= 0; idx
< HTP_ERROR_MAX
; idx
++) {
627 if (strncmp(htp_errors
[idx
].msg
, msg
,
628 strlen(htp_errors
[idx
].msg
)) == 0)
630 return htp_errors
[idx
].de
;
640 * \brief Check state for errors, warnings and add any as events
643 * \param dir direction: STREAM_TOSERVER or STREAM_TOCLIENT
645 static void HTPHandleError(HtpState
*s
, const uint8_t dir
)
647 if (s
== NULL
|| s
->conn
== NULL
||
648 s
->conn
->messages
== NULL
) {
652 size_t size
= htp_list_size(s
->conn
->messages
);
654 if(size
>= HTP_MAX_MESSAGES
) {
655 if (s
->htp_messages_offset
< HTP_MAX_MESSAGES
) {
656 //only once per HtpState
657 HTPSetEvent(s
, NULL
, dir
, HTTP_DECODER_EVENT_TOO_MANY_WARNINGS
);
658 s
->htp_messages_offset
= HTP_MAX_MESSAGES
;
659 //too noisy in fuzzing
660 //DEBUG_VALIDATE_BUG_ON("Too many libhtp messages");
662 // ignore further messages
666 for (msg
= s
->htp_messages_offset
; msg
< size
; msg
++) {
667 htp_log_t
*log
= htp_list_get(s
->conn
->messages
, msg
);
671 HtpTxUserData
*htud
= NULL
;
672 htp_tx_t
*tx
= log
->tx
; // will be NULL in <=0.5.9
674 htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
676 SCLogDebug("message %s", log
->msg
);
678 int id
= HTPHandleErrorGetId(log
->msg
);
680 id
= HTPHandleWarningGetId(log
->msg
);
682 id
= HTTP_DECODER_EVENT_UNKNOWN_ERROR
;
686 HTPSetEvent(s
, htud
, dir
, id
);
689 s
->htp_messages_offset
= (uint16_t)msg
;
690 SCLogDebug("s->htp_messages_offset %u", s
->htp_messages_offset
);
693 static inline void HTPErrorCheckTxRequestFlags(HtpState
*s
, htp_tx_t
*tx
)
696 BUG_ON(s
== NULL
|| tx
== NULL
);
698 if (tx
->flags
& ( HTP_REQUEST_INVALID_T_E
|HTP_REQUEST_INVALID_C_L
|
699 HTP_HOST_MISSING
|HTP_HOST_AMBIGUOUS
|HTP_HOSTU_INVALID
|
702 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
706 if (tx
->flags
& HTP_REQUEST_INVALID_T_E
)
707 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
708 HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST
);
709 if (tx
->flags
& HTP_REQUEST_INVALID_C_L
)
710 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
711 HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST
);
712 if (tx
->flags
& HTP_HOST_MISSING
)
713 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
714 HTTP_DECODER_EVENT_MISSING_HOST_HEADER
);
715 if (tx
->flags
& HTP_HOST_AMBIGUOUS
)
716 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
717 HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS
);
718 if (tx
->flags
& HTP_HOSTU_INVALID
)
719 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
720 HTTP_DECODER_EVENT_URI_HOST_INVALID
);
721 if (tx
->flags
& HTP_HOSTH_INVALID
)
722 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
723 HTTP_DECODER_EVENT_HEADER_HOST_INVALID
);
725 if (tx
->request_auth_type
== HTP_AUTH_UNRECOGNIZED
) {
726 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
729 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
730 HTTP_DECODER_EVENT_AUTH_UNRECOGNIZED
);
732 if (tx
->is_protocol_0_9
&& tx
->request_method_number
== HTP_M_UNKNOWN
&&
733 (tx
->request_protocol_number
== HTP_PROTOCOL_INVALID
||
734 tx
->request_protocol_number
== HTP_PROTOCOL_UNKNOWN
)) {
735 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
738 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
739 HTTP_DECODER_EVENT_REQUEST_LINE_INVALID
);
743 static int Setup(Flow
*f
, HtpState
*hstate
)
745 /* store flow ref in state so callbacks can access it */
748 HTPCfgRec
*htp_cfg_rec
= &cfglist
;
749 htp_cfg_t
*htp
= cfglist
.cfg
; /* Default to the global HTP config */
750 void *user_data
= NULL
;
752 if (FLOW_IS_IPV4(f
)) {
753 SCLogDebug("Looking up HTP config for ipv4 %08x", *GET_IPV4_DST_ADDR_PTR(f
));
754 (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(f
), cfgtree
, &user_data
);
756 else if (FLOW_IS_IPV6(f
)) {
757 SCLogDebug("Looking up HTP config for ipv6");
758 (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)GET_IPV6_DST_ADDR(f
), cfgtree
, &user_data
);
761 SCLogError(SC_ERR_INVALID_ARGUMENT
, "unknown address family, bug!");
765 if (user_data
!= NULL
) {
766 htp_cfg_rec
= user_data
;
767 htp
= htp_cfg_rec
->cfg
;
768 SCLogDebug("LIBHTP using config: %p", htp
);
770 SCLogDebug("Using default HTP config: %p", htp
);
774 #ifdef DEBUG_VALIDATION
777 /* should never happen if HTPConfigure is properly invoked */
781 hstate
->connp
= htp_connp_create(htp
);
782 if (hstate
->connp
== NULL
) {
786 hstate
->conn
= htp_connp_get_connection(hstate
->connp
);
788 htp_connp_set_user_data(hstate
->connp
, (void *)hstate
);
789 hstate
->cfg
= htp_cfg_rec
;
791 SCLogDebug("New hstate->connp %p", hstate
->connp
);
793 htp_connp_open(hstate
->connp
, NULL
, f
->sp
, NULL
, f
->dp
, &f
->startts
);
795 StreamTcpReassemblySetMinInspectDepth(f
->protoctx
, STREAM_TOSERVER
,
796 htp_cfg_rec
->request
.inspect_min_size
);
797 StreamTcpReassemblySetMinInspectDepth(f
->protoctx
, STREAM_TOCLIENT
,
798 htp_cfg_rec
->response
.inspect_min_size
);
805 * \brief Function to handle the reassembled data from client and feed it to
806 * the HTP library to process it.
808 * \param flow Pointer to the flow the data belong to
809 * \param htp_state Pointer the state in which the parsed value to be stored
810 * \param pstate Application layer parser state for this session
811 * \param input Pointer the received HTTP client data
812 * \param input_len Length in bytes of the received data
813 * \param output Pointer to the output (not used in this function)
815 * \retval On success returns 1 or on failure returns -1.
817 static AppLayerResult
HTPHandleRequestData(Flow
*f
, void *htp_state
,
818 AppLayerParserState
*pstate
,
819 const uint8_t *input
, uint32_t input_len
,
820 void *local_data
, const uint8_t flags
)
824 HtpState
*hstate
= (HtpState
*)htp_state
;
826 /* On the first invocation, create the connection parser structure to
827 * be used by HTP library. This is looked up via IP in the radix
828 * tree. Failing that, the default HTP config is used.
830 if (NULL
== hstate
->conn
) {
831 if (Setup(f
, hstate
) != 0) {
832 SCReturnStruct(APP_LAYER_ERROR
);
835 DEBUG_VALIDATE_BUG_ON(hstate
->connp
== NULL
);
837 htp_time_t ts
= { f
->lastts
.tv_sec
, f
->lastts
.tv_usec
};
838 /* pass the new data to the htp parser */
840 const int r
= htp_connp_req_data(hstate
->connp
, &ts
, input
, input_len
);
842 case HTP_STREAM_ERROR
:
848 HTPHandleError(hstate
, STREAM_TOSERVER
);
851 /* if the TCP connection is closed, then close the HTTP connection */
852 if (AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF_TS
) &&
853 !(hstate
->flags
& HTP_FLAG_STATE_CLOSED_TS
))
855 htp_connp_req_close(hstate
->connp
, &ts
);
856 hstate
->flags
|= HTP_FLAG_STATE_CLOSED_TS
;
857 SCLogDebug("stream eof encountered, closing htp handle for ts");
860 SCLogDebug("hstate->connp %p", hstate
->connp
);
863 SCReturnStruct(APP_LAYER_ERROR
);
865 SCReturnStruct(APP_LAYER_OK
);
869 * \brief Function to handle the reassembled data from server and feed it to
870 * the HTP library to process it.
872 * \param flow Pointer to the flow the data belong to
873 * \param htp_state Pointer the state in which the parsed value to be stored
874 * \param pstate Application layer parser state for this session
875 * \param input Pointer the received HTTP server data
876 * \param input_len Length in bytes of the received data
877 * \param output Pointer to the output (not used in this function)
879 * \retval On success returns 1 or on failure returns -1
881 static AppLayerResult
HTPHandleResponseData(Flow
*f
, void *htp_state
,
882 AppLayerParserState
*pstate
,
883 const uint8_t *input
, uint32_t input_len
,
884 void *local_data
, const uint8_t flags
)
888 HtpState
*hstate
= (HtpState
*)htp_state
;
890 /* On the first invocation, create the connection parser structure to
891 * be used by HTP library. This is looked up via IP in the radix
892 * tree. Failing that, the default HTP config is used.
894 if (NULL
== hstate
->conn
) {
895 if (Setup(f
, hstate
) != 0) {
896 SCReturnStruct(APP_LAYER_ERROR
);
899 DEBUG_VALIDATE_BUG_ON(hstate
->connp
== NULL
);
901 htp_time_t ts
= { f
->lastts
.tv_sec
, f
->lastts
.tv_usec
};
905 const int r
= htp_connp_res_data(hstate
->connp
, &ts
, input
, input_len
);
907 case HTP_STREAM_ERROR
:
910 case HTP_STREAM_TUNNEL
:
911 tx
= htp_connp_get_out_tx(hstate
->connp
);
912 if (tx
!= NULL
&& tx
->response_status_number
== 101) {
914 (htp_header_t
*)htp_table_get_c(tx
->response_headers
, "Upgrade");
915 if (h
== NULL
|| bstr_cmp_c(h
->value
, "h2c") != 0) {
918 if (AppLayerProtoDetectGetProtoName(ALPROTO_HTTP2
) == NULL
) {
919 // if HTTP2 is disabled, keep the HTP_STREAM_TUNNEL mode
923 if (tx
->request_port_number
!= -1) {
924 dp
= (uint16_t)tx
->request_port_number
;
926 consumed
= htp_connp_res_data_consumed(hstate
->connp
);
927 AppLayerRequestProtocolChange(hstate
->f
, dp
, ALPROTO_HTTP2
);
928 // During HTTP2 upgrade, we may consume the HTTP1 part of the data
929 // and we need to parser the remaining part with HTTP2
930 if (consumed
> 0 && consumed
< input_len
) {
931 SCReturnStruct(APP_LAYER_INCOMPLETE(consumed
, input_len
- consumed
));
933 SCReturnStruct(APP_LAYER_OK
);
939 HTPHandleError(hstate
, STREAM_TOCLIENT
);
942 /* if we the TCP connection is closed, then close the HTTP connection */
943 if (AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF_TC
) &&
944 !(hstate
->flags
& HTP_FLAG_STATE_CLOSED_TC
))
946 htp_connp_close(hstate
->connp
, &ts
);
947 hstate
->flags
|= HTP_FLAG_STATE_CLOSED_TC
;
950 SCLogDebug("hstate->connp %p", hstate
->connp
);
953 SCReturnStruct(APP_LAYER_ERROR
);
955 SCReturnStruct(APP_LAYER_OK
);
959 * \param name /Lowercase/ version of the variable name
961 static int HTTPParseContentDispositionHeader(uint8_t *name
, size_t name_len
,
962 uint8_t *data
, size_t len
, uint8_t **retptr
, size_t *retlen
)
965 printf("DATA START: \n");
966 PrintRawDataFp(stdout
, data
, len
);
967 printf("DATA END: \n");
972 for (x
= 0; x
< len
; x
++) {
973 if (!(isspace(data
[x
])))
980 uint8_t *line
= data
+x
;
981 size_t line_len
= len
-x
;
984 printf("LINE START: \n");
985 PrintRawDataFp(stdout
, line
, line_len
);
986 printf("LINE END: \n");
988 for (x
= 0 ; x
< line_len
; x
++) {
990 if (line
[x
- 1] != '\\' && line
[x
] == '\"') {
994 if (((line
[x
- 1] != '\\' && line
[x
] == ';') || ((x
+ 1) == line_len
)) && (quote
== 0 || quote
% 2 == 0)) {
995 uint8_t *token
= line
+ offset
;
996 size_t token_len
= x
- offset
;
998 if ((x
+ 1) == line_len
) {
1004 while (offset
< line_len
&& isspace(line
[offset
])) {
1009 printf("TOKEN START: \n");
1010 PrintRawDataFp(stdout
, token
, token_len
);
1011 printf("TOKEN END: \n");
1013 if (token_len
> name_len
) {
1014 if (name
== NULL
|| SCMemcmpLowercase(name
, token
, name_len
) == 0) {
1015 uint8_t *value
= token
+ name_len
;
1016 size_t value_len
= token_len
- name_len
;
1018 if (value
[0] == '\"') {
1022 if (value
[value_len
-1] == '\"') {
1026 printf("VALUE START: \n");
1027 PrintRawDataFp(stdout
, value
, value_len
);
1028 printf("VALUE END: \n");
1031 *retlen
= value_len
;
1043 * \param name /Lowercase/ version of the variable name
1045 static int HTTPParseContentTypeHeader(uint8_t *name
, size_t name_len
,
1046 uint8_t *data
, size_t len
, uint8_t **retptr
, size_t *retlen
)
1050 printf("DATA START: \n");
1051 PrintRawDataFp(stdout
, data
, len
);
1052 printf("DATA END: \n");
1057 for (x
= 0; x
< len
; x
++) {
1058 if (!(isspace(data
[x
])))
1066 uint8_t *line
= data
+x
;
1067 size_t line_len
= len
-x
;
1070 printf("LINE START: \n");
1071 PrintRawDataFp(stdout
, line
, line_len
);
1072 printf("LINE END: \n");
1074 for (x
= 0 ; x
< line_len
; x
++) {
1076 if (line
[x
- 1] != '\\' && line
[x
] == '\"') {
1080 if (((line
[x
- 1] != '\\' && line
[x
] == ';') || ((x
+ 1) == line_len
)) && (quote
== 0 || quote
% 2 == 0)) {
1081 uint8_t *token
= line
+ offset
;
1082 size_t token_len
= x
- offset
;
1084 if ((x
+ 1) == line_len
) {
1090 while (offset
< line_len
&& isspace(line
[offset
])) {
1095 printf("TOKEN START: \n");
1096 PrintRawDataFp(stdout
, token
, token_len
);
1097 printf("TOKEN END: \n");
1099 if (token_len
> name_len
) {
1100 if (name
== NULL
|| SCMemcmpLowercase(name
, token
, name_len
) == 0) {
1101 uint8_t *value
= token
+ name_len
;
1102 size_t value_len
= token_len
- name_len
;
1104 if (value
[0] == '\"') {
1108 if (value
[value_len
-1] == '\"') {
1112 printf("VALUE START: \n");
1113 PrintRawDataFp(stdout
, value
, value_len
);
1114 printf("VALUE END: \n");
1117 *retlen
= value_len
;
1129 * \brief setup multipart parsing: extract boundary and store it
1131 * \param d HTTP transaction
1132 * \param htud transaction userdata
1134 * \retval 1 ok, multipart set up
1135 * \retval 0 ok, not multipart though
1136 * \retval -1 error: problem with the boundary
1138 * If the request contains a multipart message, this function will
1139 * set the HTP_BOUNDARY_SET in the transaction.
1141 static int HtpRequestBodySetupMultipart(htp_tx_t
*tx
, HtpTxUserData
*htud
)
1143 htp_header_t
*h
= (htp_header_t
*)htp_table_get_c(tx
->request_headers
,
1145 if (h
!= NULL
&& bstr_len(h
->value
) > 0) {
1146 uint8_t *boundary
= NULL
;
1147 size_t boundary_len
= 0;
1149 int r
= HTTPParseContentTypeHeader((uint8_t *)"boundary=", 9,
1150 (uint8_t *) bstr_ptr(h
->value
), bstr_len(h
->value
),
1151 &boundary
, &boundary_len
);
1154 printf("BOUNDARY START: \n");
1155 PrintRawDataFp(stdout
, boundary
, boundary_len
);
1156 printf("BOUNDARY END: \n");
1158 if (boundary_len
< HTP_BOUNDARY_MAX
) {
1159 htud
->boundary
= HTPMalloc(boundary_len
);
1160 if (htud
->boundary
== NULL
) {
1163 htud
->boundary_len
= (uint8_t)boundary_len
;
1164 memcpy(htud
->boundary
, boundary
, boundary_len
);
1166 htud
->tsflags
|= HTP_BOUNDARY_SET
;
1168 SCLogDebug("invalid boundary");
1178 #define C_D_HDR "content-disposition:"
1179 #define C_D_HDR_LEN 20
1180 #define C_T_HDR "content-type:"
1181 #define C_T_HDR_LEN 13
1183 static void HtpRequestBodyMultipartParseHeader(HtpState
*hstate
,
1184 HtpTxUserData
*htud
,
1185 uint8_t *header
, uint32_t header_len
,
1186 uint8_t **filename
, uint16_t *filename_len
,
1187 uint8_t **filetype
, uint16_t *filetype_len
)
1195 printf("HEADER START: \n");
1196 PrintRawDataFp(stdout
, header
, header_len
);
1197 printf("HEADER END: \n");
1200 while (header_len
> 0) {
1201 uint8_t *next_line
= Bs2bmSearch(header
, header_len
, (uint8_t *)"\r\n", 2);
1202 uint8_t *line
= header
;
1205 if (next_line
== NULL
) {
1206 line_len
= header_len
;
1208 line_len
= next_line
- header
;
1210 uint8_t *sc
= (uint8_t *)memchr(line
, ':', line_len
);
1212 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1213 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
);
1214 /* if the : we found is the final char, it means we have
1216 } else if (line_len
> 0 && sc
== &line
[line_len
- 1]) {
1217 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1218 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
);
1221 printf("LINE START: \n");
1222 PrintRawDataFp(stdout
, line
, line_len
);
1223 printf("LINE END: \n");
1225 if (line_len
>= C_D_HDR_LEN
&&
1226 SCMemcmpLowercase(C_D_HDR
, line
, C_D_HDR_LEN
) == 0) {
1227 uint8_t *value
= line
+ C_D_HDR_LEN
;
1228 uint32_t value_len
= line_len
- C_D_HDR_LEN
;
1230 /* parse content-disposition */
1231 (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1232 value
, value_len
, &fn
, &fn_len
);
1233 } else if (line_len
>= C_T_HDR_LEN
&&
1234 SCMemcmpLowercase(C_T_HDR
, line
, C_T_HDR_LEN
) == 0) {
1235 SCLogDebug("content-type line");
1236 uint8_t *value
= line
+ C_T_HDR_LEN
;
1237 uint32_t value_len
= line_len
- C_T_HDR_LEN
;
1239 (void)HTTPParseContentTypeHeader(NULL
, 0,
1240 value
, value_len
, &ft
, &ft_len
);
1244 if (next_line
== NULL
) {
1245 SCLogDebug("no next_line");
1248 header_len
-= ((next_line
+ 2) - header
);
1249 header
= next_line
+ 2;
1250 } /* while (header_len > 0) */
1252 if (fn_len
> USHRT_MAX
)
1254 if (ft_len
> USHRT_MAX
)
1258 *filename_len
= fn_len
;
1260 *filetype_len
= ft_len
;
1264 * \brief Create a single buffer from the HtpBodyChunks in our list
1266 * \param htud transaction user data
1267 * \param chunks_buffers pointer to pass back the buffer to the caller
1268 * \param chunks_buffer_len pointer to pass back the buffer length to the caller
1270 static void HtpRequestBodyReassemble(HtpTxUserData
*htud
,
1271 const uint8_t **chunks_buffer
, uint32_t *chunks_buffer_len
)
1273 StreamingBufferGetDataAtOffset(htud
->request_body
.sb
,
1274 chunks_buffer
, chunks_buffer_len
,
1275 htud
->request_body
.body_parsed
);
1278 static void FlagDetectStateNewFile(HtpTxUserData
*tx
, int dir
)
1281 if (tx
&& tx
->tx_data
.de_state
) {
1282 if (dir
== STREAM_TOSERVER
) {
1283 SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1284 tx
->tx_data
.de_state
->dir_state
[0].flags
|= DETECT_ENGINE_STATE_FLAG_FILE_NEW
;
1285 } else if (STREAM_TOCLIENT
) {
1286 SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1287 tx
->tx_data
.de_state
->dir_state
[1].flags
|= DETECT_ENGINE_STATE_FLAG_FILE_NEW
;
1293 * \brief Setup boundary buffers
1295 static void HtpRequestBodySetupBoundary(HtpTxUserData
*htud
,
1296 uint8_t *boundary
, uint32_t boundary_len
)
1298 memset(boundary
, '-', boundary_len
);
1299 memcpy(boundary
+ 2, htud
->boundary
, htud
->boundary_len
);
1302 static int HtpRequestBodyHandleMultipart(HtpState
*hstate
, HtpTxUserData
*htud
, void *tx
,
1303 const uint8_t *chunks_buffer
, uint32_t chunks_buffer_len
)
1306 uint8_t boundary
[htud
->boundary_len
+ 4]; /**< size limited to HTP_BOUNDARY_MAX + 4 */
1307 uint32_t expected_boundary_len
= htud
->boundary_len
+ 2;
1308 uint32_t expected_boundary_end_len
= htud
->boundary_len
+ 4;
1309 int tx_progress
= 0;
1312 printf("CHUNK START: \n");
1313 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
1314 printf("CHUNK END: \n");
1317 HtpRequestBodySetupBoundary(htud
, boundary
, htud
->boundary_len
+ 4);
1319 /* search for the header start, header end and form end */
1320 const uint8_t *header_start
= Bs2bmSearch(chunks_buffer
, chunks_buffer_len
,
1321 boundary
, expected_boundary_len
);
1322 /* end of the multipart form */
1323 const uint8_t *form_end
= NULL
;
1324 /* end marker belonging to header_start */
1325 const uint8_t *header_end
= NULL
;
1326 if (header_start
!= NULL
) {
1327 header_end
= Bs2bmSearch(header_start
, chunks_buffer_len
- (header_start
- chunks_buffer
),
1328 (uint8_t *)"\r\n\r\n", 4);
1329 form_end
= Bs2bmSearch(header_start
, chunks_buffer_len
- (header_start
- chunks_buffer
),
1330 boundary
, expected_boundary_end_len
);
1333 SCLogDebug("header_start %p, header_end %p, form_end %p", header_start
,
1334 header_end
, form_end
);
1336 /* we currently only handle multipart for ts. When we support it for tc,
1337 * we will need to supply right direction */
1338 tx_progress
= AppLayerParserGetStateProgress(IPPROTO_TCP
, ALPROTO_HTTP1
, tx
, STREAM_TOSERVER
);
1339 /* if we're in the file storage process, deal with that now */
1340 if (htud
->tsflags
& HTP_FILENAME_SET
) {
1341 if (header_start
!= NULL
|| (tx_progress
> HTP_REQUEST_BODY
)) {
1342 SCLogDebug("reached the end of the file");
1344 const uint8_t *filedata
= chunks_buffer
;
1345 uint32_t filedata_len
= 0;
1348 if (header_start
!= NULL
) {
1349 if (header_start
== filedata
+ 2) {
1350 /* last chunk had all data, but not the boundary */
1351 SCLogDebug("last chunk had all data, but not the boundary");
1353 } else if (header_start
> filedata
+ 2) {
1354 SCLogDebug("some data from last file before the boundary");
1355 /* some data from last file before the boundary */
1356 filedata_len
= header_start
- filedata
- 2;
1359 /* body parsing done, we did not get our form end. Use all data
1360 * we still have and signal to files API we have an issue. */
1361 if (tx_progress
> HTP_REQUEST_BODY
) {
1362 filedata_len
= chunks_buffer_len
;
1363 flags
= FILE_TRUNCATED
;
1366 if (filedata_len
> chunks_buffer_len
) {
1367 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1368 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1372 printf("FILEDATA (final chunk) START: \n");
1373 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1374 printf("FILEDATA (final chunk) END: \n");
1376 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1377 if (HTPFileClose(hstate
, filedata
, filedata_len
, flags
,
1378 STREAM_TOSERVER
) == -1)
1384 htud
->tsflags
&=~ HTP_FILENAME_SET
;
1388 SCLogDebug("not yet at the end of the file");
1390 if (chunks_buffer_len
> expected_boundary_end_len
) {
1391 const uint8_t *filedata
= chunks_buffer
;
1392 uint32_t filedata_len
= chunks_buffer_len
- expected_boundary_len
;
1394 printf("FILEDATA (part) START: \n");
1395 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1396 printf("FILEDATA (part) END: \n");
1399 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1400 result
= HTPFileStoreChunk(hstate
, filedata
,
1401 filedata_len
, STREAM_TOSERVER
);
1404 } else if (result
== -2) {
1405 /* we know for sure we're not storing the file */
1406 htud
->tsflags
|= HTP_DONTSTORE
;
1410 htud
->request_body
.body_parsed
+= filedata_len
;
1412 SCLogDebug("chunk too small to already process in part");
1419 while (header_start
!= NULL
&& header_end
!= NULL
&&
1420 header_end
!= form_end
&&
1421 header_start
< (chunks_buffer
+ chunks_buffer_len
) &&
1422 header_end
< (chunks_buffer
+ chunks_buffer_len
) &&
1423 header_start
< header_end
)
1425 uint8_t *filename
= NULL
;
1426 uint16_t filename_len
= 0;
1427 uint8_t *filetype
= NULL
;
1428 uint16_t filetype_len
= 0;
1430 uint32_t header_len
= header_end
- header_start
;
1431 SCLogDebug("header_len %u", header_len
);
1432 uint8_t *header
= (uint8_t *)header_start
;
1434 /* skip empty records */
1435 if (expected_boundary_len
== header_len
) {
1437 } else if ((expected_boundary_len
+ 2) <= header_len
) {
1438 header_len
-= (expected_boundary_len
+ 2);
1439 header
= (uint8_t *)header_start
+ (expected_boundary_len
+ 2); // + for 0d 0a
1442 HtpRequestBodyMultipartParseHeader(hstate
, htud
, header
, header_len
,
1443 &filename
, &filename_len
, &filetype
, &filetype_len
);
1445 if (filename
!= NULL
) {
1446 const uint8_t *filedata
= NULL
;
1447 uint32_t filedata_len
= 0;
1449 SCLogDebug("we have a filename");
1451 htud
->tsflags
|= HTP_FILENAME_SET
;
1452 htud
->tsflags
&= ~HTP_DONTSTORE
;
1454 SCLogDebug("header_end %p", header_end
);
1455 SCLogDebug("form_end %p", form_end
);
1457 /* everything until the final boundary is the file */
1458 if (form_end
!= NULL
) {
1459 SCLogDebug("have form_end");
1461 filedata
= header_end
+ 4;
1462 if (form_end
== filedata
) {
1463 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1464 HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA
);
1466 } else if (form_end
< filedata
) {
1467 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1468 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1472 filedata_len
= form_end
- (header_end
+ 4 + 2);
1473 SCLogDebug("filedata_len %"PRIuMAX
, (uintmax_t)filedata_len
);
1476 uint8_t *header_next
= Bs2bmSearch(filedata
, filedata_len
,
1477 boundary
, expected_boundary_len
);
1478 if (header_next
!= NULL
) {
1479 filedata_len
-= (form_end
- header_next
);
1482 if (filedata_len
> chunks_buffer_len
) {
1483 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1484 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1487 SCLogDebug("filedata_len %"PRIuMAX
, (uintmax_t)filedata_len
);
1489 printf("FILEDATA START: \n");
1490 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1491 printf("FILEDATA END: \n");
1494 result
= HTPFileOpen(hstate
, htud
, filename
, filename_len
, filedata
, filedata_len
,
1495 HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1498 } else if (result
== -2) {
1499 htud
->tsflags
|= HTP_DONTSTORE
;
1501 if (HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
) == -1) {
1505 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1507 htud
->request_body
.body_parsed
+= (header_end
- chunks_buffer
);
1508 htud
->tsflags
&= ~HTP_FILENAME_SET
;
1510 SCLogDebug("chunk doesn't contain form end");
1512 filedata
= header_end
+ 4;
1513 filedata_len
= chunks_buffer_len
- (filedata
- chunks_buffer
);
1514 SCLogDebug("filedata_len %u (chunks_buffer_len %u)", filedata_len
, chunks_buffer_len
);
1516 if (filedata_len
> chunks_buffer_len
) {
1517 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1518 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1523 printf("FILEDATA START: \n");
1524 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1525 printf("FILEDATA END: \n");
1527 /* form doesn't end in this chunk, but the part might. Lets
1528 * see if have another coming up */
1529 uint8_t *header_next
= Bs2bmSearch(filedata
, filedata_len
,
1530 boundary
, expected_boundary_len
);
1531 SCLogDebug("header_next %p", header_next
);
1532 if (header_next
== NULL
) {
1533 SCLogDebug("more file data to come");
1535 uint32_t offset
= (header_end
+ 4) - chunks_buffer
;
1536 SCLogDebug("offset %u", offset
);
1537 htud
->request_body
.body_parsed
+= offset
;
1539 if (filedata_len
>= (expected_boundary_len
+ 2)) {
1540 filedata_len
-= (expected_boundary_len
+ 2 - 1);
1541 SCLogDebug("opening file with partial data");
1546 result
= HTPFileOpen(hstate
, htud
, filename
, filename_len
, filedata
,
1547 filedata_len
, HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1550 } else if (result
== -2) {
1551 htud
->tsflags
|= HTP_DONTSTORE
;
1553 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1554 htud
->request_body
.body_parsed
+= filedata_len
;
1555 SCLogDebug("htud->request_body.body_parsed %"PRIu64
, htud
->request_body
.body_parsed
);
1557 } else if (header_next
- filedata
> 2) {
1558 filedata_len
= header_next
- filedata
- 2;
1559 SCLogDebug("filedata_len %u", filedata_len
);
1561 result
= HTPFileOpen(hstate
, htud
, filename
, filename_len
, filedata
,
1562 filedata_len
, HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1565 } else if (result
== -2) {
1566 htud
->tsflags
|= HTP_DONTSTORE
;
1568 if (HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
) == -1) {
1572 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1574 htud
->tsflags
&= ~HTP_FILENAME_SET
;
1575 htud
->request_body
.body_parsed
+= (header_end
- chunks_buffer
);
1580 SCLogDebug("header_start %p, header_end %p, form_end %p",
1581 header_start
, header_end
, form_end
);
1583 /* Search next boundary entry after the start of body */
1584 uint32_t cursizeread
= header_end
- chunks_buffer
;
1585 header_start
= Bs2bmSearch(header_end
+ 4,
1586 chunks_buffer_len
- (cursizeread
+ 4),
1587 boundary
, expected_boundary_len
);
1588 if (header_start
!= NULL
) {
1589 header_end
= Bs2bmSearch(header_end
+ 4,
1590 chunks_buffer_len
- (cursizeread
+ 4),
1591 (uint8_t *) "\r\n\r\n", 4);
1595 /* if we're parsing the multipart and we're not currently processing a
1596 * file, we move the body pointer forward. */
1597 if (form_end
== NULL
&& !(htud
->tsflags
& HTP_FILENAME_SET
) && header_start
== NULL
) {
1598 if (chunks_buffer_len
> expected_boundary_end_len
) {
1599 uint32_t move
= chunks_buffer_len
- expected_boundary_end_len
+ 1;
1601 htud
->request_body
.body_parsed
+= move
;
1602 SCLogDebug("form not ready, file not set, parsing non-file "
1603 "record: moved %u", move
);
1608 SCLogDebug("htud->request_body.body_parsed %"PRIu64
, htud
->request_body
.body_parsed
);
1613 * \brief Handle POST or PUT, no multipart body data
1615 static int HtpRequestBodyHandlePOSTorPUT(HtpState
*hstate
, HtpTxUserData
*htud
,
1616 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1620 /* see if we need to open the file */
1621 if (!(htud
->tsflags
& HTP_FILENAME_SET
))
1623 uint8_t *filename
= NULL
;
1624 size_t filename_len
= 0;
1627 if (tx
->parsed_uri
!= NULL
&& tx
->parsed_uri
->path
!= NULL
) {
1628 filename
= (uint8_t *)bstr_ptr(tx
->parsed_uri
->path
);
1629 filename_len
= bstr_len(tx
->parsed_uri
->path
);
1632 if (filename
!= NULL
) {
1633 result
= HTPFileOpen(hstate
, htud
, filename
, (uint32_t)filename_len
, data
, data_len
,
1634 HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1637 } else if (result
== -2) {
1638 htud
->tsflags
|= HTP_DONTSTORE
;
1640 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1641 htud
->tsflags
|= HTP_FILENAME_SET
;
1642 htud
->tsflags
&= ~HTP_DONTSTORE
;
1648 /* otherwise, just store the data */
1650 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1651 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOSERVER
);
1654 } else if (result
== -2) {
1655 /* we know for sure we're not storing the file */
1656 htud
->tsflags
|= HTP_DONTSTORE
;
1666 static int HtpResponseBodyHandle(HtpState
*hstate
, HtpTxUserData
*htud
,
1667 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1673 /* see if we need to open the file
1674 * we check for tx->response_line in case of junk
1675 * interpreted as body before response line
1677 if (!(htud
->tcflags
& HTP_FILENAME_SET
) &&
1678 (tx
->response_line
!= NULL
|| tx
->is_protocol_0_9
))
1680 SCLogDebug("setting up file name");
1682 uint8_t *filename
= NULL
;
1683 size_t filename_len
= 0;
1685 /* try Content-Disposition header first */
1686 htp_header_t
*h
= (htp_header_t
*)htp_table_get_c(tx
->response_headers
,
1687 "Content-Disposition");
1688 if (h
!= NULL
&& bstr_len(h
->value
) > 0) {
1689 /* parse content-disposition */
1690 (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1691 (uint8_t *) bstr_ptr(h
->value
), bstr_len(h
->value
), &filename
, &filename_len
);
1694 /* fall back to name from the uri */
1695 if (filename
== NULL
) {
1697 if (tx
->parsed_uri
!= NULL
&& tx
->parsed_uri
->path
!= NULL
) {
1698 filename
= (uint8_t *)bstr_ptr(tx
->parsed_uri
->path
);
1699 filename_len
= bstr_len(tx
->parsed_uri
->path
);
1703 if (filename
!= NULL
) {
1704 // set range if present
1705 htp_header_t
*h_content_range
= htp_table_get_c(tx
->response_headers
, "content-range");
1706 if (h_content_range
!= NULL
) {
1707 result
= HTPFileOpenWithRange(hstate
, htud
, filename
, (uint32_t)filename_len
, data
,
1708 data_len
, HtpGetActiveResponseTxID(hstate
), h_content_range
->value
, htud
);
1710 result
= HTPFileOpen(hstate
, htud
, filename
, (uint32_t)filename_len
, data
, data_len
,
1711 HtpGetActiveResponseTxID(hstate
), STREAM_TOCLIENT
);
1713 SCLogDebug("result %d", result
);
1716 } else if (result
== -2) {
1717 htud
->tcflags
|= HTP_DONTSTORE
;
1719 FlagDetectStateNewFile(htud
, STREAM_TOCLIENT
);
1720 htud
->tcflags
|= HTP_FILENAME_SET
;
1721 htud
->tcflags
&= ~HTP_DONTSTORE
;
1725 else if (tx
->response_line
!= NULL
|| tx
->is_protocol_0_9
)
1727 /* otherwise, just store the data */
1729 if (!(htud
->tcflags
& HTP_DONTSTORE
)) {
1730 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOCLIENT
);
1731 SCLogDebug("result %d", result
);
1734 } else if (result
== -2) {
1735 /* we know for sure we're not storing the file */
1736 htud
->tcflags
|= HTP_DONTSTORE
;
1741 htud
->response_body
.body_parsed
+= data_len
;
1748 * \brief Function callback to append chunks for Requests
1749 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1750 * \retval int HTP_OK if all goes well
1752 static int HTPCallbackRequestBodyData(htp_tx_data_t
*d
)
1756 if (!(SC_ATOMIC_GET(htp_config_flags
) & HTP_REQUIRE_REQUEST_BODY
))
1757 SCReturnInt(HTP_OK
);
1760 SCReturnInt(HTP_OK
);
1763 printf("HTPBODY START: \n");
1764 PrintRawDataFp(stdout
, (uint8_t *)d
->data
, d
->len
);
1765 printf("HTPBODY END: \n");
1768 HtpState
*hstate
= htp_connp_get_user_data(d
->tx
->connp
);
1769 if (hstate
== NULL
) {
1770 SCReturnInt(HTP_ERROR
);
1773 SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
1774 "%"PRIu32
"", hstate
, d
, d
->data
, (uint32_t)d
->len
);
1776 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(d
->tx
);
1777 if (tx_ud
== NULL
) {
1778 SCReturnInt(HTP_OK
);
1780 if (!tx_ud
->response_body_init
) {
1781 tx_ud
->response_body_init
= 1;
1783 if (d
->tx
->request_method_number
== HTP_M_POST
) {
1785 int r
= HtpRequestBodySetupMultipart(d
->tx
, tx_ud
);
1787 tx_ud
->request_body_type
= HTP_BODY_REQUEST_MULTIPART
;
1788 } else if (r
== 0) {
1789 tx_ud
->request_body_type
= HTP_BODY_REQUEST_POST
;
1790 SCLogDebug("not multipart");
1792 } else if (d
->tx
->request_method_number
== HTP_M_PUT
) {
1793 tx_ud
->request_body_type
= HTP_BODY_REQUEST_PUT
;
1797 /* see if we can get rid of htp body chunks */
1798 HtpBodyPrune(hstate
, &tx_ud
->request_body
, STREAM_TOSERVER
);
1800 SCLogDebug("tx_ud->request_body.content_len_so_far %"PRIu64
, tx_ud
->request_body
.content_len_so_far
);
1801 SCLogDebug("hstate->cfg->request.body_limit %u", hstate
->cfg
->request
.body_limit
);
1803 /* within limits, add the body chunk to the state. */
1804 if (AppLayerHtpCheckDepth(&hstate
->cfg
->request
, &tx_ud
->request_body
, tx_ud
->tsflags
)) {
1805 uint32_t stream_depth
= FileReassemblyDepth();
1806 uint32_t len
= AppLayerHtpComputeChunkLength(tx_ud
->request_body
.content_len_so_far
,
1807 hstate
->cfg
->request
.body_limit
,
1811 BUG_ON(len
> (uint32_t)d
->len
);
1813 HtpBodyAppendChunk(&hstate
->cfg
->request
, &tx_ud
->request_body
, d
->data
, len
);
1815 const uint8_t *chunks_buffer
= NULL
;
1816 uint32_t chunks_buffer_len
= 0;
1818 if (tx_ud
->request_body_type
== HTP_BODY_REQUEST_MULTIPART
) {
1819 /* multi-part body handling starts here */
1820 if (!(tx_ud
->tsflags
& HTP_BOUNDARY_SET
)) {
1824 HtpRequestBodyReassemble(tx_ud
, &chunks_buffer
, &chunks_buffer_len
);
1825 if (chunks_buffer
== NULL
) {
1829 printf("REASSCHUNK START: \n");
1830 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
1831 printf("REASSCHUNK END: \n");
1834 HtpRequestBodyHandleMultipart(hstate
, tx_ud
, d
->tx
, chunks_buffer
, chunks_buffer_len
);
1836 } else if (tx_ud
->request_body_type
== HTP_BODY_REQUEST_POST
||
1837 tx_ud
->request_body_type
== HTP_BODY_REQUEST_PUT
) {
1838 HtpRequestBodyHandlePOSTorPUT(hstate
, tx_ud
, d
->tx
, (uint8_t *)d
->data
, len
);
1842 if (tx_ud
->tsflags
& HTP_FILENAME_SET
) {
1843 SCLogDebug("closing file that was being stored");
1844 (void)HTPFileClose(hstate
, NULL
, 0, FILE_TRUNCATED
, STREAM_TOSERVER
);
1845 tx_ud
->tsflags
&= ~HTP_FILENAME_SET
;
1850 if (hstate
->conn
!= NULL
) {
1851 SCLogDebug("checking body size %"PRIu64
" against inspect limit %u (cur %"PRIu64
", last %"PRIu64
")",
1852 tx_ud
->request_body
.content_len_so_far
,
1853 hstate
->cfg
->request
.inspect_min_size
,
1854 (uint64_t)hstate
->conn
->in_data_counter
, hstate
->last_request_data_stamp
);
1856 /* if we reach the inspect_min_size we'll trigger inspection,
1857 * so make sure that raw stream is also inspected. Set the
1858 * data to be used to the amount of raw bytes we've seen to
1860 if (tx_ud
->request_body
.body_inspected
== 0 &&
1861 tx_ud
->request_body
.content_len_so_far
>= hstate
->cfg
->request
.inspect_min_size
) {
1862 if ((uint64_t)hstate
->conn
->in_data_counter
> hstate
->last_request_data_stamp
&&
1863 (uint64_t)hstate
->conn
->in_data_counter
- hstate
->last_request_data_stamp
< (uint64_t)UINT_MAX
)
1865 uint32_t x
= (uint32_t)((uint64_t)hstate
->conn
->in_data_counter
- hstate
->last_request_data_stamp
);
1867 /* body still in progress, but due to min inspect size we need to inspect now */
1868 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
, x
);
1869 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOSERVER
);
1871 /* after the start of the body, disable the depth logic */
1872 } else if (tx_ud
->request_body
.body_inspected
> 0) {
1873 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
, 0);
1876 SCReturnInt(HTP_OK
);
1880 * \brief Function callback to append chunks for Responses
1881 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1882 * \retval int HTP_OK if all goes well
1884 static int HTPCallbackResponseBodyData(htp_tx_data_t
*d
)
1888 if (!(SC_ATOMIC_GET(htp_config_flags
) & HTP_REQUIRE_RESPONSE_BODY
))
1889 SCReturnInt(HTP_OK
);
1892 SCReturnInt(HTP_OK
);
1894 HtpState
*hstate
= htp_connp_get_user_data(d
->tx
->connp
);
1895 if (hstate
== NULL
) {
1896 SCReturnInt(HTP_ERROR
);
1899 SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
1900 "%"PRIu32
"", hstate
, d
, d
->data
, (uint32_t)d
->len
);
1902 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(d
->tx
);
1903 if (tx_ud
== NULL
) {
1904 SCReturnInt(HTP_OK
);
1906 if (!tx_ud
->request_body_init
) {
1907 tx_ud
->request_body_init
= 1;
1910 /* see if we can get rid of htp body chunks */
1911 HtpBodyPrune(hstate
, &tx_ud
->response_body
, STREAM_TOCLIENT
);
1913 SCLogDebug("tx_ud->response_body.content_len_so_far %"PRIu64
, tx_ud
->response_body
.content_len_so_far
);
1914 SCLogDebug("hstate->cfg->response.body_limit %u", hstate
->cfg
->response
.body_limit
);
1916 /* within limits, add the body chunk to the state. */
1917 if (AppLayerHtpCheckDepth(&hstate
->cfg
->response
, &tx_ud
->response_body
, tx_ud
->tcflags
)) {
1918 uint32_t stream_depth
= FileReassemblyDepth();
1919 uint32_t len
= AppLayerHtpComputeChunkLength(tx_ud
->response_body
.content_len_so_far
,
1920 hstate
->cfg
->response
.body_limit
,
1924 BUG_ON(len
> (uint32_t)d
->len
);
1926 HtpBodyAppendChunk(&hstate
->cfg
->response
, &tx_ud
->response_body
, d
->data
, len
);
1928 HtpResponseBodyHandle(hstate
, tx_ud
, d
->tx
, (uint8_t *)d
->data
, len
);
1930 if (tx_ud
->tcflags
& HTP_FILENAME_SET
) {
1931 SCLogDebug("closing file that was being stored");
1932 (void)HTPFileClose(hstate
, NULL
, 0, FILE_TRUNCATED
, STREAM_TOCLIENT
);
1933 tx_ud
->tcflags
&= ~HTP_FILENAME_SET
;
1937 if (hstate
->conn
!= NULL
) {
1938 SCLogDebug("checking body size %"PRIu64
" against inspect limit %u (cur %"PRIu64
", last %"PRIu64
")",
1939 tx_ud
->response_body
.content_len_so_far
,
1940 hstate
->cfg
->response
.inspect_min_size
,
1941 (uint64_t)hstate
->conn
->in_data_counter
, hstate
->last_response_data_stamp
);
1942 /* if we reach the inspect_min_size we'll trigger inspection,
1943 * so make sure that raw stream is also inspected. Set the
1944 * data to be used to the amount of raw bytes we've seen to
1946 if (tx_ud
->response_body
.body_inspected
== 0 &&
1947 tx_ud
->response_body
.content_len_so_far
>= hstate
->cfg
->response
.inspect_min_size
) {
1948 if ((uint64_t)hstate
->conn
->out_data_counter
> hstate
->last_response_data_stamp
&&
1949 (uint64_t)hstate
->conn
->out_data_counter
- hstate
->last_response_data_stamp
< (uint64_t)UINT_MAX
)
1951 uint32_t x
= (uint32_t)((uint64_t)hstate
->conn
->out_data_counter
- hstate
->last_response_data_stamp
);
1953 /* body still in progress, but due to min inspect size we need to inspect now */
1954 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
, x
);
1955 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOCLIENT
);
1957 /* after the start of the body, disable the depth logic */
1958 } else if (tx_ud
->response_body
.body_inspected
> 0) {
1959 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
, 0);
1962 SCReturnInt(HTP_OK
);
1966 * \brief Print the stats of the HTTP requests
1968 void HTPAtExitPrintStats(void)
1972 SCMutexLock(&htp_state_mem_lock
);
1973 SCLogDebug("http_state_memcnt %"PRIu64
", http_state_memuse %"PRIu64
"",
1974 htp_state_memcnt
, htp_state_memuse
);
1975 SCMutexUnlock(&htp_state_mem_lock
);
1980 /** \brief Clears the HTTP server configuration memory used by HTP library */
1981 void HTPFreeConfig(void)
1985 if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
1986 !AppLayerParserConfParserEnabled("tcp", "http"))
1991 HTPCfgRec
*nextrec
= cfglist
.next
;
1992 SCRadixReleaseRadixTree(cfgtree
);
1994 htp_config_destroy(cfglist
.cfg
);
1995 while (nextrec
!= NULL
) {
1996 HTPCfgRec
*htprec
= nextrec
;
1997 nextrec
= nextrec
->next
;
1999 htp_config_destroy(htprec
->cfg
);
2005 static int HTPCallbackRequestHasTrailer(htp_tx_t
*tx
)
2007 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
2009 htud
->request_has_trailers
= 1;
2014 static int HTPCallbackResponseHasTrailer(htp_tx_t
*tx
)
2016 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
2018 htud
->response_has_trailers
= 1;
2024 * \brief called at start of request
2025 * Set min inspect size.
2027 static int HTPCallbackRequestStart(htp_tx_t
*tx
)
2029 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2030 if (hstate
== NULL
) {
2031 SCReturnInt(HTP_ERROR
);
2035 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
,
2036 hstate
->cfg
->request
.inspect_min_size
);
2038 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2039 if (tx_ud
== NULL
) {
2040 tx_ud
= HTPCalloc(1, sizeof(HtpTxUserData
));
2041 if (unlikely(tx_ud
== NULL
)) {
2042 SCReturnInt(HTP_OK
);
2044 htp_tx_set_user_data(tx
, tx_ud
);
2046 SCReturnInt(HTP_OK
);
2050 * \brief called at start of response
2051 * Set min inspect size.
2053 static int HTPCallbackResponseStart(htp_tx_t
*tx
)
2055 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2056 if (hstate
== NULL
) {
2057 SCReturnInt(HTP_ERROR
);
2061 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
,
2062 hstate
->cfg
->response
.inspect_min_size
);
2064 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2065 if (tx_ud
== NULL
) {
2066 tx_ud
= HTPCalloc(1, sizeof(HtpTxUserData
));
2067 if (unlikely(tx_ud
== NULL
)) {
2068 SCReturnInt(HTP_OK
);
2070 htp_tx_set_user_data(tx
, tx_ud
);
2072 SCReturnInt(HTP_OK
);
2077 * \brief callback for request to store the recent incoming request
2078 in to the recent_in_tx for the given htp state
2079 * \param connp pointer to the current connection parser which has the htp
2080 * state in it as user data
2082 static int HTPCallbackRequest(htp_tx_t
*tx
)
2087 SCReturnInt(HTP_ERROR
);
2090 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2091 if (hstate
== NULL
) {
2092 SCReturnInt(HTP_ERROR
);
2095 SCLogDebug("transaction_cnt %"PRIu64
", list_size %"PRIu64
,
2096 hstate
->transaction_cnt
, HTPStateGetTxCnt(hstate
));
2098 SCLogDebug("HTTP request completed");
2100 HTPErrorCheckTxRequestFlags(hstate
, tx
);
2102 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
2104 if (htud
->tsflags
& HTP_FILENAME_SET
) {
2105 SCLogDebug("closing file that was being stored");
2106 (void)HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
);
2107 htud
->tsflags
&= ~HTP_FILENAME_SET
;
2108 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
,
2109 (uint32_t)hstate
->conn
->in_data_counter
);
2113 hstate
->last_request_data_stamp
= (uint64_t)hstate
->conn
->in_data_counter
;
2114 /* request done, do raw reassembly now to inspect state and stream
2115 * at the same time. */
2116 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOSERVER
);
2117 SCReturnInt(HTP_OK
);
2121 * \brief callback for response to remove the recent received requests
2122 from the recent_in_tx for the given htp state
2123 * \param connp pointer to the current connection parser which has the htp
2124 * state in it as user data
2126 static int HTPCallbackResponse(htp_tx_t
*tx
)
2130 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2131 if (hstate
== NULL
) {
2132 SCReturnInt(HTP_ERROR
);
2135 /* we have one whole transaction now */
2136 hstate
->transaction_cnt
++;
2138 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2140 if (htud
->tcflags
& HTP_FILENAME_SET
) {
2141 SCLogDebug("closing file that was being stored");
2142 (void)HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOCLIENT
);
2143 htud
->tcflags
&= ~HTP_FILENAME_SET
;
2147 /* response done, do raw reassembly now to inspect state and stream
2148 * at the same time. */
2149 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOCLIENT
);
2151 /* handle HTTP CONNECT */
2152 if (tx
->request_method_number
== HTP_M_CONNECT
) {
2153 /* any 2XX status response implies that the connection will become
2154 a tunnel immediately after this packet (RFC 7230, 3.3.3). */
2155 if ((tx
->response_status_number
>= 200) &&
2156 (tx
->response_status_number
< 300) &&
2157 (hstate
->transaction_cnt
== 1)) {
2159 if (tx
->request_port_number
!= -1) {
2160 dp
= (uint16_t)tx
->request_port_number
;
2162 // both ALPROTO_HTTP1 and ALPROTO_TLS are normal options
2163 AppLayerRequestProtocolChange(hstate
->f
, dp
, ALPROTO_UNKNOWN
);
2164 tx
->request_progress
= HTP_REQUEST_COMPLETE
;
2165 tx
->response_progress
= HTP_RESPONSE_COMPLETE
;
2169 hstate
->last_response_data_stamp
= (uint64_t)hstate
->conn
->out_data_counter
;
2170 SCReturnInt(HTP_OK
);
2173 static int HTPCallbackRequestLine(htp_tx_t
*tx
)
2175 HtpTxUserData
*tx_ud
;
2176 bstr
*request_uri_normalized
;
2177 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2178 const HTPCfgRec
*cfg
= hstate
->cfg
;
2180 request_uri_normalized
= SCHTPGenerateNormalizedUri(tx
, tx
->parsed_uri
, cfg
->uri_include_all
);
2181 if (request_uri_normalized
== NULL
)
2184 tx_ud
= htp_tx_get_user_data(tx
);
2185 if (likely(tx_ud
== NULL
)) {
2186 tx_ud
= HTPMalloc(sizeof(*tx_ud
));
2187 if (unlikely(tx_ud
== NULL
)) {
2188 bstr_free(request_uri_normalized
);
2191 memset(tx_ud
, 0, sizeof(*tx_ud
));
2192 htp_tx_set_user_data(tx
, tx_ud
);
2194 if (unlikely(tx_ud
->request_uri_normalized
!= NULL
))
2195 bstr_free(tx_ud
->request_uri_normalized
);
2196 tx_ud
->request_uri_normalized
= request_uri_normalized
;
2199 HTPErrorCheckTxRequestFlags(hstate
, tx
);
2204 static int HTPCallbackDoubleDecodeUriPart(htp_tx_t
*tx
, bstr
*part
)
2210 size_t prevlen
= bstr_len(part
);
2211 htp_status_t res
= htp_urldecode_inplace(tx
->cfg
, HTP_DECODER_URLENCODED
, part
, &flags
);
2212 // shorter string means that uri was encoded
2213 if (res
== HTP_OK
&& prevlen
> bstr_len(part
)) {
2214 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2217 HtpState
*s
= htp_connp_get_user_data(tx
->connp
);
2220 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
2221 HTTP_DECODER_EVENT_DOUBLE_ENCODED_URI
);
2227 static int HTPCallbackDoubleDecodeQuery(htp_tx_t
*tx
)
2229 if (tx
->parsed_uri
== NULL
)
2232 return HTPCallbackDoubleDecodeUriPart(tx
, tx
->parsed_uri
->query
);
2235 static int HTPCallbackDoubleDecodePath(htp_tx_t
*tx
)
2237 if (tx
->parsed_uri
== NULL
)
2240 return HTPCallbackDoubleDecodeUriPart(tx
, tx
->parsed_uri
->path
);
2243 static int HTPCallbackRequestHeaderData(htp_tx_data_t
*tx_data
)
2246 if (tx_data
->len
== 0 || tx_data
->tx
== NULL
)
2249 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx_data
->tx
);
2250 if (tx_ud
== NULL
) {
2253 ptmp
= HTPRealloc(tx_ud
->request_headers_raw
,
2254 tx_ud
->request_headers_raw_len
,
2255 tx_ud
->request_headers_raw_len
+ tx_data
->len
);
2259 tx_ud
->request_headers_raw
= ptmp
;
2261 memcpy(tx_ud
->request_headers_raw
+ tx_ud
->request_headers_raw_len
,
2262 tx_data
->data
, tx_data
->len
);
2263 tx_ud
->request_headers_raw_len
+= tx_data
->len
;
2265 if (tx_data
->tx
&& tx_data
->tx
->flags
) {
2266 HtpState
*hstate
= htp_connp_get_user_data(tx_data
->tx
->connp
);
2267 HTPErrorCheckTxRequestFlags(hstate
, tx_data
->tx
);
2272 static int HTPCallbackResponseHeaderData(htp_tx_data_t
*tx_data
)
2275 if (tx_data
->len
== 0 || tx_data
->tx
== NULL
)
2278 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx_data
->tx
);
2279 if (tx_ud
== NULL
) {
2282 ptmp
= HTPRealloc(tx_ud
->response_headers_raw
,
2283 tx_ud
->response_headers_raw_len
,
2284 tx_ud
->response_headers_raw_len
+ tx_data
->len
);
2288 tx_ud
->response_headers_raw
= ptmp
;
2290 memcpy(tx_ud
->response_headers_raw
+ tx_ud
->response_headers_raw_len
,
2291 tx_data
->data
, tx_data
->len
);
2292 tx_ud
->response_headers_raw_len
+= tx_data
->len
;
2298 * We have a similar set function called HTPConfigSetDefaultsPhase1.
2300 static void HTPConfigSetDefaultsPhase1(HTPCfgRec
*cfg_prec
)
2302 cfg_prec
->uri_include_all
= FALSE
;
2303 cfg_prec
->request
.body_limit
= HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT
;
2304 cfg_prec
->response
.body_limit
= HTP_CONFIG_DEFAULT_RESPONSE_BODY_LIMIT
;
2305 cfg_prec
->request
.inspect_min_size
= HTP_CONFIG_DEFAULT_REQUEST_INSPECT_MIN_SIZE
;
2306 cfg_prec
->request
.inspect_window
= HTP_CONFIG_DEFAULT_REQUEST_INSPECT_WINDOW
;
2307 cfg_prec
->response
.inspect_min_size
= HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_MIN_SIZE
;
2308 cfg_prec
->response
.inspect_window
= HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_WINDOW
;
2310 if (!g_disable_randomness
) {
2311 cfg_prec
->randomize
= HTP_CONFIG_DEFAULT_RANDOMIZE
;
2313 cfg_prec
->randomize
= 0;
2315 cfg_prec
->randomize_range
= HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE
;
2317 htp_config_register_request_header_data(cfg_prec
->cfg
, HTPCallbackRequestHeaderData
);
2318 htp_config_register_request_trailer_data(cfg_prec
->cfg
, HTPCallbackRequestHeaderData
);
2319 htp_config_register_response_header_data(cfg_prec
->cfg
, HTPCallbackResponseHeaderData
);
2320 htp_config_register_response_trailer_data(cfg_prec
->cfg
, HTPCallbackResponseHeaderData
);
2322 htp_config_register_request_trailer(cfg_prec
->cfg
, HTPCallbackRequestHasTrailer
);
2323 htp_config_register_response_trailer(cfg_prec
->cfg
, HTPCallbackResponseHasTrailer
);
2325 htp_config_register_request_body_data(cfg_prec
->cfg
, HTPCallbackRequestBodyData
);
2326 htp_config_register_response_body_data(cfg_prec
->cfg
, HTPCallbackResponseBodyData
);
2328 htp_config_register_request_start(cfg_prec
->cfg
, HTPCallbackRequestStart
);
2329 htp_config_register_request_complete(cfg_prec
->cfg
, HTPCallbackRequest
);
2331 htp_config_register_response_start(cfg_prec
->cfg
, HTPCallbackResponseStart
);
2332 htp_config_register_response_complete(cfg_prec
->cfg
, HTPCallbackResponse
);
2334 htp_config_set_parse_request_cookies(cfg_prec
->cfg
, 0);
2336 /* don't convert + to space by default */
2337 htp_config_set_plusspace_decode(cfg_prec
->cfg
, HTP_DECODER_URLENCODED
, 0);
2338 // enables request decompression
2339 htp_config_set_request_decompression(cfg_prec
->cfg
, 1);
2340 #ifdef HAVE_HTP_CONFIG_SET_LZMA_LAYERS
2341 // disable by default
2342 htp_config_set_lzma_layers(cfg_prec
->cfg
, HTP_CONFIG_DEFAULT_LZMA_LAYERS
);
2344 #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT
2345 htp_config_set_lzma_memlimit(cfg_prec
->cfg
,
2346 HTP_CONFIG_DEFAULT_LZMA_MEMLIMIT
);
2348 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT
2349 htp_config_set_compression_bomb_limit(cfg_prec
->cfg
,
2350 HTP_CONFIG_DEFAULT_COMPRESSION_BOMB_LIMIT
);
2352 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT
2353 htp_config_set_compression_time_limit(cfg_prec
->cfg
, HTP_CONFIG_DEFAULT_COMPRESSION_TIME_LIMIT
);
2355 /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set
2356 * only the hard limit. So we set both here to the (current) htp defaults.
2357 * The reason we do this is that if the user sets the hard limit in the
2358 * config, we have to set the soft limit as well. If libhtp starts using
2359 * the soft limit in the future, we at least make sure we control what
2361 htp_config_set_field_limits(cfg_prec
->cfg
,
2362 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT
,
2363 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_HARD
);
2367 /* hack: htp random range code expects random values in range of 0-RAND_MAX,
2368 * but we can get both <0 and >RAND_MAX values from RandomGet
2370 static int RandomGetWrap(void)
2376 } while(r
>= ULONG_MAX
- (ULONG_MAX
% RAND_MAX
));
2378 return r
% RAND_MAX
;
2382 * We have this splitup so that in case double decoding has been enabled
2383 * for query and path, they would be called first on the callback queue,
2384 * before the callback set by Phase2() is called. We need this, since
2385 * the callback in Phase2() generates the normalized uri which utilizes
2386 * the query and path. */
2387 static void HTPConfigSetDefaultsPhase2(const char *name
, HTPCfgRec
*cfg_prec
)
2389 /* randomize inspection size if needed */
2390 if (cfg_prec
->randomize
) {
2391 int rdrange
= cfg_prec
->randomize_range
;
2393 long int r
= RandomGetWrap();
2394 cfg_prec
->request
.inspect_min_size
+= (int)(cfg_prec
->request
.inspect_min_size
*
2395 ((double)r
/ RAND_MAX
- 0.5) * rdrange
/ 100);
2397 r
= RandomGetWrap();
2398 cfg_prec
->request
.inspect_window
+= (int)(cfg_prec
->request
.inspect_window
*
2399 ((double)r
/ RAND_MAX
- 0.5) * rdrange
/ 100);
2400 SCLogConfig("'%s' server has 'request-body-minimal-inspect-size' set to"
2401 " %d and 'request-body-inspect-window' set to %d after"
2404 cfg_prec
->request
.inspect_min_size
,
2405 cfg_prec
->request
.inspect_window
);
2408 r
= RandomGetWrap();
2409 cfg_prec
->response
.inspect_min_size
+= (int)(cfg_prec
->response
.inspect_min_size
*
2410 ((double)r
/ RAND_MAX
- 0.5) * rdrange
/ 100);
2412 r
= RandomGetWrap();
2413 cfg_prec
->response
.inspect_window
+= (int)(cfg_prec
->response
.inspect_window
*
2414 ((double)r
/ RAND_MAX
- 0.5) * rdrange
/ 100);
2416 SCLogConfig("'%s' server has 'response-body-minimal-inspect-size' set to"
2417 " %d and 'response-body-inspect-window' set to %d after"
2420 cfg_prec
->response
.inspect_min_size
,
2421 cfg_prec
->response
.inspect_window
);
2424 htp_config_register_request_line(cfg_prec
->cfg
, HTPCallbackRequestLine
);
2426 cfg_prec
->request
.sbcfg
.flags
= 0;
2427 cfg_prec
->request
.sbcfg
.buf_size
= cfg_prec
->request
.inspect_window
?
2428 cfg_prec
->request
.inspect_window
: 256;
2429 cfg_prec
->request
.sbcfg
.buf_slide
= 0;
2430 cfg_prec
->request
.sbcfg
.Malloc
= HTPMalloc
;
2431 cfg_prec
->request
.sbcfg
.Calloc
= HTPCalloc
;
2432 cfg_prec
->request
.sbcfg
.Realloc
= HTPRealloc
;
2433 cfg_prec
->request
.sbcfg
.Free
= HTPFree
;
2435 cfg_prec
->response
.sbcfg
.flags
= 0;
2436 cfg_prec
->response
.sbcfg
.buf_size
= cfg_prec
->response
.inspect_window
?
2437 cfg_prec
->response
.inspect_window
: 256;
2438 cfg_prec
->response
.sbcfg
.buf_slide
= 0;
2439 cfg_prec
->response
.sbcfg
.Malloc
= HTPMalloc
;
2440 cfg_prec
->response
.sbcfg
.Calloc
= HTPCalloc
;
2441 cfg_prec
->response
.sbcfg
.Realloc
= HTPRealloc
;
2442 cfg_prec
->response
.sbcfg
.Free
= HTPFree
;
2446 static void HTPConfigParseParameters(HTPCfgRec
*cfg_prec
, ConfNode
*s
,
2449 if (cfg_prec
== NULL
|| s
== NULL
|| tree
== NULL
)
2454 /* Default Parameters */
2455 TAILQ_FOREACH(p
, &s
->head
, next
) {
2457 if (strcasecmp("address", p
->name
) == 0) {
2460 TAILQ_FOREACH(pval
, &p
->head
, next
) {
2461 SCLogDebug("LIBHTP server %s: %s=%s", s
->name
, p
->name
,
2465 if (strchr(pval
->val
, ':') != NULL
) {
2466 SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
2467 s
->name
, pval
->val
, cfg_prec
->cfg
);
2468 if (SCRadixAddKeyIPV6String(pval
->val
, tree
, cfg_prec
) == NULL
) {
2469 SCLogWarning(SC_ERR_INVALID_VALUE
, "LIBHTP failed to "
2470 "add ipv6 server %s, ignoring", pval
->val
);
2473 SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
2474 s
->name
, pval
->val
, cfg_prec
->cfg
);
2475 if (SCRadixAddKeyIPV4String(pval
->val
, tree
, cfg_prec
) == NULL
) {
2476 SCLogWarning(SC_ERR_INVALID_VALUE
, "LIBHTP failed "
2477 "to add ipv4 server %s, ignoring",
2480 } /* else - if (strchr(pval->val, ':') != NULL) */
2481 } /* TAILQ_FOREACH(pval, &p->head, next) */
2483 } else if (strcasecmp("personality", p
->name
) == 0) {
2485 int personality
= HTPLookupPersonality(p
->val
);
2486 SCLogDebug("LIBHTP default: %s = %s", p
->name
, p
->val
);
2487 SCLogDebug("LIBHTP default: %s = %s", p
->name
, p
->val
);
2489 if (personality
>= 0) {
2490 SCLogDebug("LIBHTP default: %s=%s (%d)", p
->name
, p
->val
,
2492 if (htp_config_set_server_personality(cfg_prec
->cfg
, personality
) == HTP_ERROR
){
2493 SCLogWarning(SC_ERR_INVALID_VALUE
, "LIBHTP Failed adding "
2494 "personality \"%s\", ignoring", p
->val
);
2496 SCLogDebug("LIBHTP personality set to %s",
2497 HTPLookupPersonalityString(personality
));
2500 /* The IDS personality by default converts the path (and due to
2501 * our query string callback also the query string) to lowercase.
2502 * Signatures do not expect this, so override it. */
2503 htp_config_set_convert_lowercase(cfg_prec
->cfg
, HTP_DECODER_URL_PATH
, 0);
2505 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "LIBHTP Unknown personality "
2506 "\"%s\", ignoring", p
->val
);
2510 } else if (strcasecmp("request-body-limit", p
->name
) == 0 ||
2511 strcasecmp("request_body_limit", p
->name
) == 0) {
2512 if (ParseSizeStringU32(p
->val
, &cfg_prec
->request
.body_limit
) < 0) {
2513 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing request-body-limit "
2514 "from conf file - %s. Killing engine", p
->val
);
2518 } else if (strcasecmp("response-body-limit", p
->name
) == 0) {
2519 if (ParseSizeStringU32(p
->val
, &cfg_prec
->response
.body_limit
) < 0) {
2520 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-limit "
2521 "from conf file - %s. Killing engine", p
->val
);
2525 } else if (strcasecmp("request-body-minimal-inspect-size", p
->name
) == 0) {
2526 if (ParseSizeStringU32(p
->val
, &cfg_prec
->request
.inspect_min_size
) < 0) {
2527 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing request-body-minimal-inspect-size "
2528 "from conf file - %s. Killing engine", p
->val
);
2532 } else if (strcasecmp("request-body-inspect-window", p
->name
) == 0) {
2533 if (ParseSizeStringU32(p
->val
, &cfg_prec
->request
.inspect_window
) < 0) {
2534 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing request-body-inspect-window "
2535 "from conf file - %s. Killing engine", p
->val
);
2539 } else if (strcasecmp("double-decode-query", p
->name
) == 0) {
2540 if (ConfValIsTrue(p
->val
)) {
2541 htp_config_register_request_line(cfg_prec
->cfg
,
2542 HTPCallbackDoubleDecodeQuery
);
2545 } else if (strcasecmp("double-decode-path", p
->name
) == 0) {
2546 if (ConfValIsTrue(p
->val
)) {
2547 htp_config_register_request_line(cfg_prec
->cfg
,
2548 HTPCallbackDoubleDecodePath
);
2551 } else if (strcasecmp("response-body-minimal-inspect-size", p
->name
) == 0) {
2552 if (ParseSizeStringU32(p
->val
, &cfg_prec
->response
.inspect_min_size
) < 0) {
2553 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-minimal-inspect-size "
2554 "from conf file - %s. Killing engine", p
->val
);
2558 } else if (strcasecmp("response-body-inspect-window", p
->name
) == 0) {
2559 if (ParseSizeStringU32(p
->val
, &cfg_prec
->response
.inspect_window
) < 0) {
2560 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-inspect-window "
2561 "from conf file - %s. Killing engine", p
->val
);
2565 } else if (strcasecmp("response-body-decompress-layer-limit", p
->name
) == 0) {
2567 if (ParseSizeStringU32(p
->val
, &value
) < 0) {
2568 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-inspect-window "
2569 "from conf file - %s. Killing engine", p
->val
);
2572 #ifdef HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT
2573 htp_config_set_response_decompression_layer_limit(cfg_prec
->cfg
, value
);
2575 SCLogWarning(SC_WARN_OUTDATED_LIBHTP
, "can't set response-body-decompress-layer-limit "
2576 "to %u, libhtp version too old", value
);
2578 } else if (strcasecmp("path-convert-backslash-separators", p
->name
) == 0) {
2579 htp_config_set_backslash_convert_slashes(cfg_prec
->cfg
,
2580 HTP_DECODER_URL_PATH
,
2581 ConfValIsTrue(p
->val
));
2582 } else if (strcasecmp("path-bestfit-replacement-char", p
->name
) == 0) {
2583 if (strlen(p
->val
) == 1) {
2584 htp_config_set_bestfit_replacement_byte(cfg_prec
->cfg
,
2585 HTP_DECODER_URL_PATH
,
2588 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
, "Invalid entry "
2589 "for libhtp param path-bestfit-replacement-char");
2591 } else if (strcasecmp("path-convert-lowercase", p
->name
) == 0) {
2592 htp_config_set_convert_lowercase(cfg_prec
->cfg
,
2593 HTP_DECODER_URL_PATH
,
2594 ConfValIsTrue(p
->val
));
2595 } else if (strcasecmp("path-nul-encoded-terminates", p
->name
) == 0) {
2596 htp_config_set_nul_encoded_terminates(cfg_prec
->cfg
,
2597 HTP_DECODER_URL_PATH
,
2598 ConfValIsTrue(p
->val
));
2599 } else if (strcasecmp("path-nul-raw-terminates", p
->name
) == 0) {
2600 htp_config_set_nul_raw_terminates(cfg_prec
->cfg
,
2601 HTP_DECODER_URL_PATH
,
2602 ConfValIsTrue(p
->val
));
2603 } else if (strcasecmp("path-separators-compress", p
->name
) == 0) {
2604 htp_config_set_path_separators_compress(cfg_prec
->cfg
,
2605 HTP_DECODER_URL_PATH
,
2606 ConfValIsTrue(p
->val
));
2607 } else if (strcasecmp("path-separators-decode", p
->name
) == 0) {
2608 htp_config_set_path_separators_decode(cfg_prec
->cfg
,
2609 HTP_DECODER_URL_PATH
,
2610 ConfValIsTrue(p
->val
));
2611 } else if (strcasecmp("path-u-encoding-decode", p
->name
) == 0) {
2612 htp_config_set_u_encoding_decode(cfg_prec
->cfg
,
2613 HTP_DECODER_URL_PATH
,
2614 ConfValIsTrue(p
->val
));
2615 } else if (strcasecmp("path-url-encoding-invalid-handling", p
->name
) == 0) {
2616 enum htp_url_encoding_handling_t handling
;
2617 if (strcasecmp(p
->val
, "preserve_percent") == 0) {
2618 handling
= HTP_URL_DECODE_PRESERVE_PERCENT
;
2619 } else if (strcasecmp(p
->val
, "remove_percent") == 0) {
2620 handling
= HTP_URL_DECODE_REMOVE_PERCENT
;
2621 } else if (strcasecmp(p
->val
, "decode_invalid") == 0) {
2622 handling
= HTP_URL_DECODE_PROCESS_INVALID
;
2624 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
, "Invalid entry "
2625 "for libhtp param path-url-encoding-invalid-handling");
2628 htp_config_set_url_encoding_invalid_handling(cfg_prec
->cfg
,
2629 HTP_DECODER_URL_PATH
,
2631 } else if (strcasecmp("path-utf8-convert-bestfit", p
->name
) == 0) {
2632 htp_config_set_utf8_convert_bestfit(cfg_prec
->cfg
,
2633 HTP_DECODER_URL_PATH
,
2634 ConfValIsTrue(p
->val
));
2635 } else if (strcasecmp("uri-include-all", p
->name
) == 0) {
2636 cfg_prec
->uri_include_all
= ConfValIsTrue(p
->val
);
2637 SCLogDebug("uri-include-all %s",
2638 cfg_prec
->uri_include_all
? "enabled" : "disabled");
2639 } else if (strcasecmp("query-plusspace-decode", p
->name
) == 0) {
2640 htp_config_set_plusspace_decode(cfg_prec
->cfg
,
2641 HTP_DECODER_URLENCODED
,
2642 ConfValIsTrue(p
->val
));
2643 } else if (strcasecmp("meta-field-limit", p
->name
) == 0) {
2645 if (ParseSizeStringU32(p
->val
, &limit
) < 0) {
2646 SCLogError(SC_ERR_SIZE_PARSE
, "Error meta-field-limit "
2647 "from conf file - %s. Killing engine", p
->val
);
2651 FatalError(SC_ERR_FATAL
, "Error meta-field-limit "
2652 "from conf file cannot be 0. Killing engine");
2654 /* set default soft-limit with our new hard limit */
2655 htp_config_set_field_limits(cfg_prec
->cfg
,
2656 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT
,
2658 #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT
2659 } else if (strcasecmp("lzma-memlimit", p
->name
) == 0) {
2661 if (ParseSizeStringU32(p
->val
, &limit
) < 0) {
2662 FatalError(SC_ERR_SIZE_PARSE
, "failed to parse 'lzma-memlimit' "
2663 "from conf file - %s.", p
->val
);
2666 FatalError(SC_ERR_SIZE_PARSE
, "'lzma-memlimit' "
2667 "from conf file cannot be 0.");
2669 /* set default soft-limit with our new hard limit */
2670 SCLogConfig("Setting HTTP LZMA memory limit to %"PRIu32
" bytes", limit
);
2671 htp_config_set_lzma_memlimit(cfg_prec
->cfg
, (size_t)limit
);
2673 #ifdef HAVE_HTP_CONFIG_SET_LZMA_LAYERS
2674 } else if (strcasecmp("lzma-enabled", p
->name
) == 0) {
2675 if (ConfValIsTrue(p
->val
)) {
2676 htp_config_set_lzma_layers(cfg_prec
->cfg
, 1);
2677 } else if (!ConfValIsFalse(p
->val
)) {
2679 if (StringParseInt8(&limit
, 10, 0, (const char *)p
->val
) < 0) {
2680 FatalError(SC_ERR_SIZE_PARSE
,
2681 "failed to parse 'lzma-enabled' "
2682 "from conf file - %s.",
2685 SCLogConfig("Setting HTTP LZMA decompression layers to %" PRIu32
"", (int)limit
);
2686 htp_config_set_lzma_layers(cfg_prec
->cfg
, limit
);
2689 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT
2690 } else if (strcasecmp("compression-bomb-limit", p
->name
) == 0) {
2692 if (ParseSizeStringU32(p
->val
, &limit
) < 0) {
2693 FatalError(SC_ERR_SIZE_PARSE
, "failed to parse 'compression-bomb-limit' "
2694 "from conf file - %s.", p
->val
);
2697 FatalError(SC_ERR_SIZE_PARSE
, "'compression-bomb-limit' "
2698 "from conf file cannot be 0.");
2700 /* set default soft-limit with our new hard limit */
2701 SCLogConfig("Setting HTTP compression bomb limit to %"PRIu32
" bytes", limit
);
2702 htp_config_set_compression_bomb_limit(cfg_prec
->cfg
, (size_t)limit
);
2704 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT
2705 } else if (strcasecmp("decompression-time-limit", p
->name
) == 0) {
2707 // between 1 usec and 1 second
2708 if (StringParseU32RangeCheck(&limit
, 10, 0, p
->val
, 1, 1000000) < 0) {
2709 FatalError(SC_ERR_SIZE_PARSE
,
2710 "failed to parse 'decompression-time-limit' "
2711 "from conf file - %s.",
2714 SCLogConfig("Setting HTTP decompression time limit to %" PRIu32
" usec", limit
);
2715 htp_config_set_compression_time_limit(cfg_prec
->cfg
, (size_t)limit
);
2717 } else if (strcasecmp("randomize-inspection-sizes", p
->name
) == 0) {
2718 if (!g_disable_randomness
) {
2719 cfg_prec
->randomize
= ConfValIsTrue(p
->val
);
2721 } else if (strcasecmp("randomize-inspection-range", p
->name
) == 0) {
2723 if (StringParseU32RangeCheck(&range
, 10, 0,
2724 (const char *)p
->val
, 0, 100) < 0) {
2725 SCLogError(SC_ERR_INVALID_VALUE
, "Invalid value for randomize"
2726 "-inspection-range setting from conf file - \"%s\"."
2727 " It should be a valid integer less than or equal to 100."
2732 cfg_prec
->randomize_range
= range
;
2733 } else if (strcasecmp("http-body-inline", p
->name
) == 0) {
2734 if (ConfValIsTrue(p
->val
)) {
2735 cfg_prec
->http_body_inline
= 1;
2736 } else if (ConfValIsFalse(p
->val
)) {
2737 cfg_prec
->http_body_inline
= 0;
2739 if (strcmp("auto", p
->val
) != 0) {
2740 WarnInvalidConfEntry("http_body_inline", "%s", "auto");
2742 if (EngineModeIsIPS()) {
2743 cfg_prec
->http_body_inline
= 1;
2745 cfg_prec
->http_body_inline
= 0;
2748 } else if (strcasecmp("swf-decompression", p
->name
) == 0) {
2751 TAILQ_FOREACH(pval
, &p
->head
, next
) {
2752 if (strcasecmp("enabled", pval
->name
) == 0) {
2753 if (ConfValIsTrue(pval
->val
)) {
2754 cfg_prec
->swf_decompression_enabled
= 1;
2755 } else if (ConfValIsFalse(pval
->val
)) {
2756 cfg_prec
->swf_decompression_enabled
= 0;
2758 WarnInvalidConfEntry("swf-decompression.enabled", "%s", "no");
2760 } else if (strcasecmp("type", pval
->name
) == 0) {
2761 if (strcasecmp("no", pval
->val
) == 0) {
2762 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_NONE
;
2763 } else if (strcasecmp("deflate", pval
->val
) == 0) {
2764 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_ZLIB
;
2765 } else if (strcasecmp("lzma", pval
->val
) == 0) {
2766 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_LZMA
;
2767 } else if (strcasecmp("both", pval
->val
) == 0) {
2768 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_BOTH
;
2770 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
,
2771 "Invalid entry for "
2772 "swf-decompression.type: %s - "
2773 "Killing engine", pval
->val
);
2776 } else if (strcasecmp("compress-depth", pval
->name
) == 0) {
2777 if (ParseSizeStringU32(pval
->val
, &cfg_prec
->swf_compress_depth
) < 0) {
2778 SCLogError(SC_ERR_SIZE_PARSE
,
2779 "Error parsing swf-decompression.compression-depth "
2780 "from conf file - %s. Killing engine", p
->val
);
2783 } else if (strcasecmp("decompress-depth", pval
->name
) == 0) {
2784 if (ParseSizeStringU32(pval
->val
, &cfg_prec
->swf_decompress_depth
) < 0) {
2785 SCLogError(SC_ERR_SIZE_PARSE
,
2786 "Error parsing swf-decompression.decompression-depth "
2787 "from conf file - %s. Killing engine", p
->val
);
2791 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "Ignoring unknown param %s", pval
->name
);
2795 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "LIBHTP Ignoring unknown "
2796 "default config: %s", p
->name
);
2798 } /* TAILQ_FOREACH(p, &default_config->head, next) */
2803 void HTPConfigure(void)
2807 cfglist
.next
= NULL
;
2809 cfgtree
= SCRadixCreateRadixTree(NULL
, NULL
);
2810 if (NULL
== cfgtree
)
2813 /* Default Config */
2814 cfglist
.cfg
= htp_config_create();
2815 if (NULL
== cfglist
.cfg
) {
2816 FatalError(SC_ERR_FATAL
, "Failed to create HTP default config");
2818 SCLogDebug("LIBHTP default config: %p", cfglist
.cfg
);
2819 HTPConfigSetDefaultsPhase1(&cfglist
);
2820 if (ConfGetNode("app-layer.protocols.http.libhtp") == NULL
) {
2821 HTPConfigParseParameters(&cfglist
, ConfGetNode("libhtp.default-config"),
2824 HTPConfigParseParameters(&cfglist
, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree
);
2826 HTPConfigSetDefaultsPhase2("default", &cfglist
);
2830 /* Read server config and create a parser for each IP in radix tree */
2831 ConfNode
*server_config
= ConfGetNode("app-layer.protocols.http.libhtp.server-config");
2832 if (server_config
== NULL
) {
2833 server_config
= ConfGetNode("libhtp.server-config");
2834 if (server_config
== NULL
) {
2835 SCLogDebug("LIBHTP Configuring %p", server_config
);
2839 SCLogDebug("LIBHTP Configuring %p", server_config
);
2843 TAILQ_FOREACH(si
, &server_config
->head
, next
) {
2844 /* Need the named node, not the index */
2845 ConfNode
*s
= TAILQ_FIRST(&si
->head
);
2847 SCLogDebug("LIBHTP s NULL");
2851 SCLogDebug("LIBHTP server %s", s
->name
);
2853 HTPCfgRec
*nextrec
= cfglist
.next
;
2854 HTPCfgRec
*htprec
= SCMalloc(sizeof(HTPCfgRec
));
2857 memset(htprec
, 0x00, sizeof(*htprec
));
2859 cfglist
.next
= htprec
;
2861 cfglist
.next
->next
= nextrec
;
2862 cfglist
.next
->cfg
= htp_config_create();
2863 if (NULL
== cfglist
.next
->cfg
) {
2864 FatalError(SC_ERR_FATAL
, "Failed to create HTP server config");
2867 HTPConfigSetDefaultsPhase1(htprec
);
2868 HTPConfigParseParameters(htprec
, s
, cfgtree
);
2869 HTPConfigSetDefaultsPhase2(s
->name
, htprec
);
2875 void AppLayerHtpPrintStats(void)
2878 SCMutexLock(&htp_state_mem_lock
);
2879 SCLogPerf("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
2880 SCMutexUnlock(&htp_state_mem_lock
);
2885 * \brief get files callback
2886 * \param state state ptr
2887 * \param direction flow direction
2888 * \retval files files ptr
2890 static FileContainer
*HTPStateGetFiles(void *state
, uint8_t direction
)
2895 HtpState
*http_state
= (HtpState
*)state
;
2897 if (direction
& STREAM_TOCLIENT
) {
2898 SCReturnPtr(http_state
->files_tc
, "FileContainer");
2900 SCReturnPtr(http_state
->files_ts
, "FileContainer");
2904 static int HTPStateGetAlstateProgress(void *tx
, uint8_t direction
)
2906 if (direction
& STREAM_TOSERVER
)
2907 return ((htp_tx_t
*)tx
)->request_progress
;
2909 return ((htp_tx_t
*)tx
)->response_progress
;
2912 static uint64_t HTPStateGetTxCnt(void *alstate
)
2914 HtpState
*http_state
= (HtpState
*)alstate
;
2916 if (http_state
!= NULL
&& http_state
->conn
!= NULL
) {
2917 const int64_t size
= (int64_t)htp_list_size(http_state
->conn
->transactions
);
2920 SCLogDebug("size %"PRIu64
, size
);
2921 return (uint64_t)size
;
2927 static void *HTPStateGetTx(void *alstate
, uint64_t tx_id
)
2929 HtpState
*http_state
= (HtpState
*)alstate
;
2931 if (http_state
!= NULL
&& http_state
->conn
!= NULL
)
2932 return htp_list_get(http_state
->conn
->transactions
, tx_id
);
2937 void *HtpGetTxForH2(void *alstate
)
2939 // gets last transaction
2940 HtpState
*http_state
= (HtpState
*)alstate
;
2941 if (http_state
!= NULL
&& http_state
->conn
!= NULL
) {
2942 size_t txid
= htp_list_array_size(http_state
->conn
->transactions
);
2944 return htp_list_get(http_state
->conn
->transactions
, txid
- 1);
2950 static int HTPStateGetEventInfo(const char *event_name
,
2951 int *event_id
, AppLayerEventType
*event_type
)
2953 *event_id
= SCMapEnumNameToValue(event_name
, http_decoder_event_table
);
2954 if (*event_id
== -1) {
2955 SCLogError(SC_ERR_INVALID_ENUM_MAP
, "event \"%s\" not present in "
2956 "http's enum map table.", event_name
);
2957 /* this should be treated as fatal */
2961 *event_type
= APP_LAYER_EVENT_TYPE_TRANSACTION
;
2966 static int HTPStateGetEventInfoById(int event_id
, const char **event_name
,
2967 AppLayerEventType
*event_type
)
2969 *event_name
= SCMapEnumValueToName(event_id
, http_decoder_event_table
);
2970 if (*event_name
== NULL
) {
2971 SCLogError(SC_ERR_INVALID_ENUM_MAP
, "event \"%d\" not present in "
2972 "http's enum map table.", event_id
);
2973 /* this should be treated as fatal */
2977 *event_type
= APP_LAYER_EVENT_TYPE_TRANSACTION
;
2982 static void HTPStateTruncate(void *state
, uint8_t direction
)
2984 FileContainer
*fc
= HTPStateGetFiles(state
, direction
);
2986 FileTruncateAllOpenFiles(fc
);
2990 static AppLayerTxData
*HTPGetTxData(void *vtx
)
2992 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
2993 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx
);
2995 return &tx_ud
->tx_data
;
3000 static int HTPRegisterPatternsForProtocolDetection(void)
3002 const char *methods
[] = { "GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS",
3003 "CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL",
3004 "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN",
3005 "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE",
3006 "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL", NULL
};
3007 const char *spacings
[] = { "|20|", "|09|", NULL
};
3008 const char *versions
[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL
};
3013 int register_result
;
3014 char method_buffer
[32] = "";
3016 /* Loop through all the methods ands spacings and register the patterns */
3017 for (methods_pos
= 0; methods
[methods_pos
]; methods_pos
++) {
3018 for (spacings_pos
= 0; spacings
[spacings_pos
]; spacings_pos
++) {
3020 /* Combine the method name and the spacing */
3021 snprintf(method_buffer
, sizeof(method_buffer
), "%s%s", methods
[methods_pos
], spacings
[spacings_pos
]);
3023 /* Register the new method+spacing pattern
3024 * 3 is subtracted from the length since the spacing is hex typed as |xx|
3025 * but the pattern matching should only be one char
3027 register_result
= AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_HTTP1
,
3028 method_buffer
, strlen(method_buffer
) - 3, 0, STREAM_TOSERVER
);
3029 if (register_result
< 0) {
3035 /* Loop through all the http verions patterns that are TO_CLIENT */
3036 for (versions_pos
= 0; versions
[versions_pos
]; versions_pos
++) {
3037 register_result
= AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_HTTP1
,
3038 versions
[versions_pos
], strlen(versions
[versions_pos
]), 0, STREAM_TOCLIENT
);
3039 if (register_result
< 0) {
3048 * \brief Register the HTTP protocol and state handling functions to APP layer
3051 void RegisterHTPParsers(void)
3055 const char *proto_name
= "http";
3058 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name
)) {
3059 AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP1
, proto_name
);
3060 if (HTPRegisterPatternsForProtocolDetection() < 0)
3063 SCLogInfo("Protocol detection and parser disabled for %s protocol",
3068 if (AppLayerParserConfParserEnabled("tcp", proto_name
)) {
3069 AppLayerParserRegisterStateFuncs(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateAlloc
, HTPStateFree
);
3070 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateTransactionFree
);
3071 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetFiles
);
3072 AppLayerParserRegisterGetStateProgressFunc(
3073 IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetAlstateProgress
);
3074 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetTxCnt
);
3075 AppLayerParserRegisterGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetTx
);
3077 AppLayerParserRegisterStateProgressCompletionStatus(
3078 ALPROTO_HTTP1
, HTP_REQUEST_COMPLETE
, HTP_RESPONSE_COMPLETE
);
3079 AppLayerParserRegisterGetEventInfo(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetEventInfo
);
3080 AppLayerParserRegisterGetEventInfoById(
3081 IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetEventInfoById
);
3083 AppLayerParserRegisterTruncateFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateTruncate
);
3084 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPGetTxData
);
3086 AppLayerParserRegisterSetStreamDepthFlag(
3087 IPPROTO_TCP
, ALPROTO_HTTP1
, AppLayerHtpSetStreamDepthFlag
);
3089 AppLayerParserRegisterParser(
3090 IPPROTO_TCP
, ALPROTO_HTTP1
, STREAM_TOSERVER
, HTPHandleRequestData
);
3091 AppLayerParserRegisterParser(
3092 IPPROTO_TCP
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, HTPHandleResponseData
);
3093 SC_ATOMIC_INIT(htp_config_flags
);
3094 /* This parser accepts gaps. */
3095 AppLayerParserRegisterOptionFlags(
3096 IPPROTO_TCP
, ALPROTO_HTTP1
, APP_LAYER_PARSER_OPT_ACCEPT_GAPS
);
3097 AppLayerParserRegisterParserAcceptableDataDirection(
3098 IPPROTO_TCP
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_TOCLIENT
);
3101 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
3102 "still on.", proto_name
);
3105 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPParserRegisterTests
);
3112 static HTPCfgRec cfglist_backup
;
3114 void HtpConfigCreateBackup(void)
3116 cfglist_backup
= cfglist
;
3121 void HtpConfigRestoreBackup(void)
3123 cfglist
= cfglist_backup
;
3128 /** \test Test case where chunks are sent in smaller chunks and check the
3129 * response of the parser from HTP library. */
3130 static int HTPParserTest01(void)
3132 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3134 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3137 memset(&ssn
, 0, sizeof(ssn
));
3139 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3140 FAIL_IF_NULL(alp_tctx
);
3142 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3145 f
->proto
= IPPROTO_TCP
;
3146 f
->alproto
= ALPROTO_HTTP1
;
3148 StreamTcpInitConfig(true);
3151 for (u
= 0; u
< httplen1
; u
++) {
3155 flags
= STREAM_TOSERVER
|STREAM_START
;
3156 else if (u
== (httplen1
- 1))
3157 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3159 flags
= STREAM_TOSERVER
;
3161 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3165 HtpState
*htp_state
= f
->alstate
;
3166 FAIL_IF_NULL(htp_state
);
3168 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3171 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3174 FAIL_IF(strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0"));
3175 FAIL_IF(tx
->request_method_number
!= HTP_M_POST
);
3176 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
);
3178 AppLayerParserThreadCtxFree(alp_tctx
);
3179 StreamTcpFreeConfig(true);
3184 /** \test Test folding in 1 read case */
3185 static int HTPParserTest01b(void)
3187 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3189 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3192 memset(&ssn
, 0, sizeof(ssn
));
3194 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3195 FAIL_IF_NULL(alp_tctx
);
3197 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3200 f
->proto
= IPPROTO_TCP
;
3201 f
->alproto
= ALPROTO_HTTP1
;
3203 StreamTcpInitConfig(true);
3205 uint8_t flags
=STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3206 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, httpbuf1
, httplen1
);
3209 HtpState
*htp_state
= f
->alstate
;
3210 FAIL_IF_NULL(htp_state
);
3212 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3215 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3218 FAIL_IF(strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0"));
3219 FAIL_IF(tx
->request_method_number
!= HTP_M_POST
);
3220 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
);
3222 AppLayerParserThreadCtxFree(alp_tctx
);
3223 StreamTcpFreeConfig(true);
3228 /** \test Test folding in 1byte per read case */
3229 static int HTPParserTest01c(void)
3231 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3233 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3236 memset(&ssn
, 0, sizeof(ssn
));
3238 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3239 FAIL_IF_NULL(alp_tctx
);
3241 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3244 f
->proto
= IPPROTO_TCP
;
3245 f
->alproto
= ALPROTO_HTTP1
;
3247 StreamTcpInitConfig(true);
3250 for (u
= 0; u
< httplen1
; u
++) {
3254 flags
= STREAM_TOSERVER
|STREAM_START
;
3255 else if (u
== (httplen1
- 1))
3256 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3258 flags
= STREAM_TOSERVER
;
3260 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3264 HtpState
*htp_state
= f
->alstate
;
3265 FAIL_IF_NULL(htp_state
);
3267 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3270 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3273 FAIL_IF(strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0"));
3274 FAIL_IF(tx
->request_method_number
!= HTP_M_POST
);
3275 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
);
3277 AppLayerParserThreadCtxFree(alp_tctx
);
3278 StreamTcpFreeConfig(true);
3283 /** \test Test case where chunks are sent in smaller chunks and check the
3284 * response of the parser from HTP library. */
3285 static int HTPParserTest01a(void)
3289 uint8_t httpbuf1
[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3291 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3293 HtpState
*htp_state
= NULL
;
3295 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3297 memset(&ssn
, 0, sizeof(ssn
));
3299 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3303 f
->proto
= IPPROTO_TCP
;
3304 f
->alproto
= ALPROTO_HTTP1
;
3306 StreamTcpInitConfig(true);
3309 for (u
= 0; u
< httplen1
; u
++) {
3313 flags
= STREAM_TOSERVER
|STREAM_START
;
3314 else if (u
== (httplen1
- 1))
3315 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3317 flags
= STREAM_TOSERVER
;
3320 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3322 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3330 htp_state
= f
->alstate
;
3331 if (htp_state
== NULL
) {
3332 printf("no http state: ");
3336 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3337 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3338 if (strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0")
3339 || tx
->request_method_number
!= HTP_M_POST
||
3340 tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
)
3342 printf("expected header value: Victor/1.0 and got %s: and expected"
3343 " method: POST and got %s, expected protocol number HTTP/1.0"
3344 " and got: %s \n", bstr_util_strdup_to_c(h
->value
),
3345 bstr_util_strdup_to_c(tx
->request_method
),
3346 bstr_util_strdup_to_c(tx
->request_protocol
));
3351 if (alp_tctx
!= NULL
)
3352 AppLayerParserThreadCtxFree(alp_tctx
);
3353 StreamTcpFreeConfig(true);
3358 /** \test See how it deals with an incomplete request. */
3359 static int HTPParserTest02(void)
3363 uint8_t httpbuf1
[] = "POST";
3364 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3366 HtpState
*http_state
= NULL
;
3367 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3369 memset(&ssn
, 0, sizeof(ssn
));
3371 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3375 f
->proto
= IPPROTO_TCP
;
3376 f
->alproto
= ALPROTO_HTTP1
;
3378 StreamTcpInitConfig(true);
3381 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
,
3382 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
, httpbuf1
, httplen1
);
3384 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
3390 http_state
= f
->alstate
;
3391 if (http_state
== NULL
) {
3392 printf("no http state: ");
3396 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3398 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3399 FAIL_IF_NOT_NULL(h
);
3401 FAIL_IF_NULL(tx
->request_method
);
3402 char *method
= bstr_util_strdup_to_c(tx
->request_method
);
3403 FAIL_IF_NULL(method
);
3405 FAIL_IF(strcmp(method
, "POST") != 0);
3410 if (alp_tctx
!= NULL
)
3411 AppLayerParserThreadCtxFree(alp_tctx
);
3412 StreamTcpFreeConfig(true);
3417 /** \test Test case where method is invalid and data is sent in smaller chunks
3418 * and check the response of the parser from HTP library. */
3419 static int HTPParserTest03(void)
3423 uint8_t httpbuf1
[] = "HELLO / HTTP/1.0\r\n";
3424 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3426 HtpState
*htp_state
= NULL
;
3428 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3430 memset(&ssn
, 0, sizeof(ssn
));
3432 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3436 f
->proto
= IPPROTO_TCP
;
3437 f
->alproto
= ALPROTO_HTTP1
;
3439 StreamTcpInitConfig(true);
3442 for (u
= 0; u
< httplen1
; u
++) {
3445 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
3446 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
3447 else flags
= STREAM_TOSERVER
;
3450 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3452 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3459 htp_state
= f
->alstate
;
3460 if (htp_state
== NULL
) {
3461 printf("no http state: ");
3465 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3467 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3468 if (tx
->request_method_number
!= HTP_M_UNKNOWN
||
3469 h
!= NULL
|| tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
)
3471 printf("expected method M_UNKNOWN and got %s: , expected protocol "
3472 "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx
->request_method
),
3473 bstr_util_strdup_to_c(tx
->request_protocol
));
3478 if (alp_tctx
!= NULL
)
3479 AppLayerParserThreadCtxFree(alp_tctx
);
3480 StreamTcpFreeConfig(true);
3485 /** \test Test case where invalid data is sent and check the response of the
3486 * parser from HTP library. */
3487 static int HTPParserTest04(void)
3491 HtpState
*htp_state
= NULL
;
3492 uint8_t httpbuf1
[] = "World!\r\n";
3493 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3496 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3498 memset(&ssn
, 0, sizeof(ssn
));
3500 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3504 f
->proto
= IPPROTO_TCP
;
3505 f
->alproto
= ALPROTO_HTTP1
;
3507 StreamTcpInitConfig(true);
3510 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
,
3511 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
, httpbuf1
, httplen1
);
3518 htp_state
= f
->alstate
;
3519 if (htp_state
== NULL
) {
3520 printf("no http state: ");
3524 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3525 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3526 if (tx
->request_method_number
!= HTP_M_UNKNOWN
||
3527 h
!= NULL
|| tx
->request_protocol_number
!= HTP_PROTOCOL_0_9
)
3529 printf("expected method M_UNKNOWN and got %s: , expected protocol "
3530 "NULL and got %s \n", bstr_util_strdup_to_c(tx
->request_method
),
3531 bstr_util_strdup_to_c(tx
->request_protocol
));
3536 if (alp_tctx
!= NULL
)
3537 AppLayerParserThreadCtxFree(alp_tctx
);
3538 StreamTcpFreeConfig(true);
3543 /** \test Test both sides of a http stream mixed up to see if the HTP parser
3544 * properly parsed them and also keeps them separated. */
3545 static int HTPParserTest05(void)
3547 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\nContent-Length: 17\r\n\r\n";
3548 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3549 uint8_t httpbuf2
[] = "Post D";
3550 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
3551 uint8_t httpbuf3
[] = "ata is c0oL!";
3552 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
3554 uint8_t httpbuf4
[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
3555 uint32_t httplen4
= sizeof(httpbuf4
) - 1; /* minus the \0 */
3556 uint8_t httpbuf5
[] = "post R";
3557 uint32_t httplen5
= sizeof(httpbuf5
) - 1; /* minus the \0 */
3558 uint8_t httpbuf6
[] = "esults are tha bomb!";
3559 uint32_t httplen6
= sizeof(httpbuf6
) - 1; /* minus the \0 */
3562 memset(&ssn
, 0, sizeof(ssn
));
3564 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3565 FAIL_IF_NULL(alp_tctx
);
3567 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3570 f
->proto
= IPPROTO_TCP
;
3571 f
->alproto
= ALPROTO_HTTP1
;
3573 StreamTcpInitConfig(true);
3575 int r
= AppLayerParserParse(
3576 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
3579 r
= AppLayerParserParse(
3580 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf4
, httplen4
);
3583 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, httpbuf5
, httplen5
);
3586 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, httpbuf2
, httplen2
);
3589 r
= AppLayerParserParse(
3590 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_EOF
, httpbuf3
, httplen3
);
3593 r
= AppLayerParserParse(
3594 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_EOF
, httpbuf6
, httplen6
);
3597 HtpState
*http_state
= f
->alstate
;
3598 FAIL_IF_NULL(http_state
);
3600 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3602 FAIL_IF_NOT(tx
->request_method_number
== HTP_M_POST
);
3603 FAIL_IF_NOT(tx
->request_protocol_number
== HTP_PROTOCOL_1_0
);
3605 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3608 FAIL_IF_NOT(tx
->response_status_number
== 200);
3610 AppLayerParserThreadCtxFree(alp_tctx
);
3611 StreamTcpFreeConfig(true);
3616 /** \test Test proper chunked encoded response body
3618 static int HTPParserTest06(void)
3620 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
3621 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
3622 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
3623 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3624 uint8_t httpbuf2
[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
3626 "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
3627 "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
3628 "FrontPage/5.0.2.2510\r\n"
3629 "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
3631 "Content-Type: text/html\r\n\r\n"
3633 "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
3634 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
3635 "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
3636 "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
3637 "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
3638 "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
3639 "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
3640 "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
3641 "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
3642 "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
3643 "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
3644 "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
3645 "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
3646 "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
3647 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
3648 "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
3649 "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
3650 "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
3651 "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
3652 "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
3653 "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
3654 "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
3655 "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
3656 "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
3657 "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
3658 "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
3659 "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
3660 "aHA=\r\n0\r\n\r\n";
3661 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
3664 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3665 FAIL_IF_NULL(alp_tctx
);
3667 memset(&ssn
, 0, sizeof(ssn
));
3669 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3672 f
->proto
= IPPROTO_TCP
;
3673 f
->alproto
= ALPROTO_HTTP1
;
3675 StreamTcpInitConfig(true);
3677 int r
= AppLayerParserParse(
3678 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
3680 r
= AppLayerParserParse(
3681 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
3684 HtpState
*http_state
= f
->alstate
;
3685 FAIL_IF_NULL(http_state
);
3687 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3690 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
3691 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
3693 FAIL_IF(tx
->response_status_number
!= 200);
3694 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
3696 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3699 AppLayerParserThreadCtxFree(alp_tctx
);
3700 StreamTcpFreeConfig(true);
3707 static int HTPParserTest07(void)
3711 uint8_t httpbuf1
[] = "GET /awstats.pl?/migratemigrate%20=%20| HTTP/1.0\r\n\r\n";
3712 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3714 HtpState
*htp_state
= NULL
;
3716 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3718 memset(&ssn
, 0, sizeof(ssn
));
3720 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3724 f
->proto
= IPPROTO_TCP
;
3725 f
->alproto
= ALPROTO_HTTP1
;
3727 StreamTcpInitConfig(true);
3730 for (u
= 0; u
< httplen1
; u
++) {
3734 flags
= STREAM_TOSERVER
|STREAM_START
;
3735 else if (u
== (httplen1
- 1))
3736 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3738 flags
= STREAM_TOSERVER
;
3741 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3743 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3751 htp_state
= f
->alstate
;
3752 if (htp_state
== NULL
) {
3753 printf("no http state: ");
3757 uint8_t ref
[] = "/awstats.pl?/migratemigrate = |";
3758 size_t reflen
= sizeof(ref
) - 1;
3760 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3763 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
3764 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
3765 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
3766 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
3768 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
3772 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref
,
3773 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
3775 printf("normalized uri \"");
3776 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
3778 PrintRawUriFp(stdout
, ref
, reflen
);
3786 if (alp_tctx
!= NULL
)
3787 AppLayerParserThreadCtxFree(alp_tctx
);
3788 StreamTcpFreeConfig(true);
3793 #include "conf-yaml-loader.h"
3797 static int HTPParserTest08(void)
3801 uint8_t httpbuf1
[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3802 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3804 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3815 ConfCreateContextBackup();
3817 HtpConfigCreateBackup();
3819 ConfYamlLoadString(input
, strlen(input
));
3822 HtpState
*htp_state
= NULL
;
3824 memset(&ssn
, 0, sizeof(ssn
));
3826 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3830 f
->proto
= IPPROTO_TCP
;
3831 f
->alproto
= ALPROTO_HTTP1
;
3833 StreamTcpInitConfig(true);
3836 flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3839 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, httpbuf1
, httplen1
);
3841 printf("toserver chunk returned %" PRId32
", expected"
3849 htp_state
= f
->alstate
;
3850 if (htp_state
== NULL
) {
3851 printf("no http state: ");
3856 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3859 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
3860 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
3861 //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3862 PrintRawDataFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
),
3863 bstr_len(tx_ud
->request_uri_normalized
));
3868 if (alp_tctx
!= NULL
)
3869 AppLayerParserThreadCtxFree(alp_tctx
);
3870 StreamTcpFreeConfig(true);
3873 ConfRestoreContextBackup();
3874 HtpConfigRestoreBackup();
3881 static int HTPParserTest09(void)
3885 uint8_t httpbuf1
[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3886 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3888 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3896 personality: Apache_2_2\n\
3899 ConfCreateContextBackup();
3901 HtpConfigCreateBackup();
3903 ConfYamlLoadString(input
, strlen(input
));
3906 HtpState
*htp_state
= NULL
;
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_HTTP1
;
3918 StreamTcpInitConfig(true);
3921 flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3924 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, httpbuf1
, httplen1
);
3926 printf("toserver chunk returned %" PRId32
", expected"
3933 htp_state
= f
->alstate
;
3934 if (htp_state
== NULL
) {
3935 printf("no http state: ");
3939 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3942 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
3943 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
3944 //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3945 PrintRawDataFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
),
3946 bstr_len(tx_ud
->request_uri_normalized
));
3951 if (alp_tctx
!= NULL
)
3952 AppLayerParserThreadCtxFree(alp_tctx
);
3953 StreamTcpFreeConfig(true);
3956 ConfRestoreContextBackup();
3957 HtpConfigRestoreBackup();
3962 /** \test Host:www.google.com <- missing space between name:value (rfc violation)
3964 static int HTPParserTest10(void)
3968 uint8_t httpbuf1
[] = "GET / HTTP/1.0\r\nHost:www.google.com\r\n\r\n";
3969 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3971 HtpState
*htp_state
= NULL
;
3973 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3975 memset(&ssn
, 0, sizeof(ssn
));
3977 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3981 f
->proto
= IPPROTO_TCP
;
3982 f
->alproto
= ALPROTO_HTTP1
;
3984 StreamTcpInitConfig(true);
3987 for (u
= 0; u
< httplen1
; u
++) {
3991 flags
= STREAM_TOSERVER
|STREAM_START
;
3992 else if (u
== (httplen1
- 1))
3993 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3995 flags
= STREAM_TOSERVER
;
3998 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4000 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4008 htp_state
= f
->alstate
;
4009 if (htp_state
== NULL
) {
4010 printf("no http state: ");
4014 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4015 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
4020 char *name
= bstr_util_strdup_to_c(h
->name
);
4025 if (strcmp(name
, "Host") != 0) {
4026 printf("header name not \"Host\", instead \"%s\": ", name
);
4032 char *value
= bstr_util_strdup_to_c(h
->value
);
4033 if (value
== NULL
) {
4037 if (strcmp(value
, "www.google.com") != 0) {
4038 printf("header value not \"www.google.com\", instead \"%s\": ", value
);
4046 if (alp_tctx
!= NULL
)
4047 AppLayerParserThreadCtxFree(alp_tctx
);
4048 StreamTcpFreeConfig(true);
4053 /** \test double encoding in path
4055 static int HTPParserTest11(void)
4059 uint8_t httpbuf1
[] = "GET /%2500 HTTP/1.0\r\n\r\n";
4060 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4062 HtpState
*htp_state
= NULL
;
4064 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4066 memset(&ssn
, 0, sizeof(ssn
));
4068 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4072 f
->proto
= IPPROTO_TCP
;
4073 f
->alproto
= ALPROTO_HTTP1
;
4075 StreamTcpInitConfig(true);
4078 for (u
= 0; u
< httplen1
; u
++) {
4082 flags
= STREAM_TOSERVER
|STREAM_START
;
4083 else if (u
== (httplen1
- 1))
4084 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4086 flags
= STREAM_TOSERVER
;
4089 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4091 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4099 htp_state
= f
->alstate
;
4100 if (htp_state
== NULL
) {
4101 printf("no http state: ");
4105 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4108 HtpTxUserData
*tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
4109 if (tx
!= NULL
&& tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4110 if (4 != bstr_len(tx_ud
->request_uri_normalized
)) {
4111 printf("normalized uri len should be 2, is %"PRIuMAX
,
4112 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4116 if (bstr_ptr(tx_ud
->request_uri_normalized
)[0] != '/' ||
4117 bstr_ptr(tx_ud
->request_uri_normalized
)[1] != '%' ||
4118 bstr_ptr(tx_ud
->request_uri_normalized
)[2] != '0' ||
4119 bstr_ptr(tx_ud
->request_uri_normalized
)[3] != '0')
4121 printf("normalized uri \"");
4122 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4130 if (alp_tctx
!= NULL
)
4131 AppLayerParserThreadCtxFree(alp_tctx
);
4132 StreamTcpFreeConfig(true);
4137 /** \test double encoding in query
4139 static int HTPParserTest12(void)
4143 uint8_t httpbuf1
[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
4144 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4146 HtpState
*htp_state
= NULL
;
4148 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4150 memset(&ssn
, 0, sizeof(ssn
));
4152 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4156 f
->proto
= IPPROTO_TCP
;
4157 f
->alproto
= ALPROTO_HTTP1
;
4159 StreamTcpInitConfig(true);
4162 for (u
= 0; u
< httplen1
; u
++) {
4166 flags
= STREAM_TOSERVER
|STREAM_START
;
4167 else if (u
== (httplen1
- 1))
4168 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4170 flags
= STREAM_TOSERVER
;
4173 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4175 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4183 htp_state
= f
->alstate
;
4184 if (htp_state
== NULL
) {
4185 printf("no http state: ");
4189 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4192 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
4193 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4194 if (7 != bstr_len(tx_ud
->request_uri_normalized
)) {
4195 printf("normalized uri len should be 5, is %"PRIuMAX
,
4196 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4200 if (bstr_ptr(tx_ud
->request_uri_normalized
)[0] != '/' ||
4201 bstr_ptr(tx_ud
->request_uri_normalized
)[1] != '?' ||
4202 bstr_ptr(tx_ud
->request_uri_normalized
)[2] != 'a' ||
4203 bstr_ptr(tx_ud
->request_uri_normalized
)[3] != '=' ||
4204 bstr_ptr(tx_ud
->request_uri_normalized
)[4] != '%' ||
4205 bstr_ptr(tx_ud
->request_uri_normalized
)[5] != '0' ||
4206 bstr_ptr(tx_ud
->request_uri_normalized
)[6] != '0')
4208 printf("normalized uri \"");
4209 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4217 if (alp_tctx
!= NULL
)
4218 AppLayerParserThreadCtxFree(alp_tctx
);
4219 StreamTcpFreeConfig(true);
4224 /** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
4226 static int HTPParserTest13(void)
4230 uint8_t httpbuf1
[] = "GET / HTTP/1.0\r\nHost:www.google.com\rName: Value\r\n\r\n";
4231 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4233 HtpState
*htp_state
= NULL
;
4235 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4237 memset(&ssn
, 0, sizeof(ssn
));
4239 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4243 f
->proto
= IPPROTO_TCP
;
4244 f
->alproto
= ALPROTO_HTTP1
;
4246 StreamTcpInitConfig(true);
4249 for (u
= 0; u
< httplen1
; u
++) {
4253 flags
= STREAM_TOSERVER
|STREAM_START
;
4254 else if (u
== (httplen1
- 1))
4255 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4257 flags
= STREAM_TOSERVER
;
4260 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4262 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4270 htp_state
= f
->alstate
;
4271 if (htp_state
== NULL
) {
4272 printf("no http state: ");
4276 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4277 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
4282 char *name
= bstr_util_strdup_to_c(h
->name
);
4287 if (strcmp(name
, "Host") != 0) {
4288 printf("header name not \"Host\", instead \"%s\": ", name
);
4294 char *value
= bstr_util_strdup_to_c(h
->value
);
4295 if (value
== NULL
) {
4299 if (strcmp(value
, "www.google.com\rName: Value") != 0) {
4300 printf("header value not \"www.google.com\", instead \"");
4301 PrintRawUriFp(stdout
, (uint8_t *)value
, strlen(value
));
4310 if (alp_tctx
!= NULL
)
4311 AppLayerParserThreadCtxFree(alp_tctx
);
4312 StreamTcpFreeConfig(true);
4317 /** \test Test basic config */
4318 static int HTPParserConfigTest01(void)
4332 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4333 personality: Tomcat_6_0\n\
4338 - 192.168.10.0/24\n\
4339 personality: IIS_7_0\n\
4342 ConfCreateContextBackup();
4345 ConfYamlLoadString(input
, strlen(input
));
4348 outputs
= ConfGetNode("libhtp.default-config.personality");
4349 if (outputs
== NULL
) {
4353 outputs
= ConfGetNode("libhtp.server-config");
4354 if (outputs
== NULL
) {
4358 ConfNode
*node
= TAILQ_FIRST(&outputs
->head
);
4362 if (strcmp(node
->name
, "0") != 0) {
4365 node
= TAILQ_FIRST(&node
->head
);
4369 if (strcmp(node
->name
, "apache-tomcat") != 0) {
4376 ConfNode
*node2
= ConfNodeLookupChild(node
, "personality");
4377 if (node2
== NULL
) {
4380 if (strcmp(node2
->val
, "Tomcat_6_0") != 0) {
4384 node
= ConfNodeLookupChild(node
, "address");
4388 TAILQ_FOREACH(n
, &node
->head
, next
) {
4395 if (strcmp(n
->name
, "0") != 0) {
4398 if (strcmp(n
->val
, "192.168.1.0/24") != 0) {
4403 if (strcmp(n
->name
, "1") != 0) {
4406 if (strcmp(n
->val
, "127.0.0.0/8") != 0) {
4411 if (strcmp(n
->name
, "2") != 0) {
4414 if (strcmp(n
->val
, "::1") != 0) {
4424 outputs
= ConfGetNode("libhtp.server-config");
4425 if (outputs
== NULL
) {
4429 node
= TAILQ_FIRST(&outputs
->head
);
4430 node
= TAILQ_NEXT(node
, next
);
4434 if (strcmp(node
->name
, "1") != 0) {
4437 node
= TAILQ_FIRST(&node
->head
);
4441 if (strcmp(node
->name
, "iis7") != 0) {
4445 node2
= ConfNodeLookupChild(node
, "personality");
4446 if (node2
== NULL
) {
4449 if (strcmp(node2
->val
, "IIS_7_0") != 0) {
4453 node
= ConfNodeLookupChild(node
, "address");
4459 TAILQ_FOREACH(n
, &node
->head
, next
) {
4466 if (strcmp(n
->name
, "0") != 0) {
4469 if (strcmp(n
->val
, "192.168.0.0/24") != 0) {
4474 if (strcmp(n
->name
, "1") != 0) {
4477 if (strcmp(n
->val
, "192.168.10.0/24") != 0) {
4491 ConfRestoreContextBackup();
4496 /** \test Test config builds radix correctly */
4497 static int HTPParserConfigTest02(void)
4511 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4512 personality: Tomcat_6_0\n\
4517 - 192.168.10.0/24\n\
4518 personality: IIS_7_0\n\
4521 ConfCreateContextBackup();
4523 HtpConfigCreateBackup();
4525 ConfYamlLoadString(input
, strlen(input
));
4529 if (cfglist
.cfg
== NULL
) {
4530 printf("No default config created.\n");
4534 if (cfgtree
== NULL
) {
4535 printf("No config tree created.\n");
4539 htp_cfg_t
*htp
= cfglist
.cfg
;
4542 void *user_data
= NULL
;
4544 addr
= "192.168.10.42";
4545 if (inet_pton(AF_INET
, addr
, buf
) == 1) {
4546 (void)SCRadixFindKeyIPV4BestMatch(buf
, cfgtree
, &user_data
);
4547 if (user_data
!= NULL
) {
4548 HTPCfgRec
*htp_cfg_rec
= user_data
;
4549 htp
= htp_cfg_rec
->cfg
;
4550 SCLogDebug("LIBHTP using config: %p", htp
);
4553 printf("Could not get config for: %s\n", addr
);
4558 printf("Failed to parse address: %s\n", addr
);
4564 if (inet_pton(AF_INET6
, addr
, buf
) == 1) {
4565 (void)SCRadixFindKeyIPV6BestMatch(buf
, cfgtree
, &user_data
);
4566 if (user_data
!= NULL
) {
4567 HTPCfgRec
*htp_cfg_rec
= user_data
;
4568 htp
= htp_cfg_rec
->cfg
;
4569 SCLogDebug("LIBHTP using config: %p", htp
);
4572 printf("Could not get config for: %s\n", addr
);
4577 printf("Failed to parse address: %s\n", addr
);
4586 ConfRestoreContextBackup();
4587 HtpConfigRestoreBackup();
4592 /** \test Test traffic is handled by the correct htp config */
4593 static int HTPParserConfigTest03(void)
4597 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
4599 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4601 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4603 HtpState
*htp_state
= NULL
;
4616 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4617 personality: Tomcat_6_0\n\
4622 - 192.168.10.0/24\n\
4623 personality: IIS_7_0\n\
4626 ConfCreateContextBackup();
4628 HtpConfigCreateBackup();
4630 ConfYamlLoadString(input
, strlen(input
));
4634 const char *addr
= "192.168.10.42";
4636 memset(&ssn
, 0, sizeof(ssn
));
4638 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4642 f
->proto
= IPPROTO_TCP
;
4643 f
->alproto
= ALPROTO_HTTP1
;
4645 htp_cfg_t
*htp
= cfglist
.cfg
;
4647 void *user_data
= NULL
;
4648 (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)f
->dst
.addr_data32
, cfgtree
, &user_data
);
4649 if (user_data
!= NULL
) {
4650 HTPCfgRec
*htp_cfg_rec
= user_data
;
4651 htp
= htp_cfg_rec
->cfg
;
4652 SCLogDebug("LIBHTP using config: %p", htp
);
4655 printf("Could not get config for: %s\n", addr
);
4659 StreamTcpInitConfig(true);
4662 for (u
= 0; u
< httplen1
; u
++) {
4665 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
4666 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
4667 else flags
= STREAM_TOSERVER
;
4670 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4672 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4681 htp_state
= f
->alstate
;
4682 if (htp_state
== NULL
) {
4683 printf("no http state: ");
4688 if (HTPStateGetTxCnt(htp_state
) != 2) {
4689 printf("HTPStateGetTxCnt(htp_state) failure\n");
4693 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4696 if (tx
->cfg
!= htp
) {
4697 printf("wrong HTP config (%p instead of %p - default=%p): ",
4698 tx
->cfg
, htp
, cfglist
.cfg
);
4701 tx
= HTPStateGetTx(htp_state
, 1);
4704 if (tx
->cfg
!= htp
) {
4705 printf("wrong HTP config (%p instead of %p - default=%p): ",
4706 tx
->cfg
, htp
, cfglist
.cfg
);
4711 if (alp_tctx
!= NULL
)
4712 AppLayerParserThreadCtxFree(alp_tctx
);
4715 ConfRestoreContextBackup();
4716 HtpConfigRestoreBackup();
4718 StreamTcpFreeConfig(true);
4723 /* disabled when we upgraded to libhtp 0.5.x */
4725 static int HTPParserConfigTest04(void)
4736 path-control-char-handling: status_400\n\
4737 path-convert-utf8: yes\n\
4738 path-invalid-encoding-handling: remove_percent\n\
4743 personality: Tomcat_6_0\n\
4744 path-invalid-utf8-handling: none\n\
4745 path-nul-encoded-handling: status_404\n\
4746 path-nul-raw-handling: status_400\n\
4749 personality: IIS_7_0\n\
4750 path-replacement-char: o\n\
4751 path-unicode-mapping: status_400\n\
4754 ConfCreateContextBackup();
4756 HtpConfigCreateBackup();
4758 ConfYamlLoadString(input
, strlen(input
));
4762 HTPCfgRec
*cfg_rec
= &cfglist
;
4763 if (cfg_rec
->cfg
->path_control_char_handling
!= STATUS_400
||
4764 cfg_rec
->cfg
->path_convert_utf8
!= 1 ||
4765 cfg_rec
->cfg
->path_invalid_encoding_handling
!= URL_DECODER_REMOVE_PERCENT
) {
4766 printf("failed 1\n");
4770 cfg_rec
= cfg_rec
->next
;
4771 if (cfg_rec
->cfg
->bestfit_replacement_char
!= 'o' ||
4772 cfg_rec
->cfg
->path_unicode_mapping
!= STATUS_400
) {
4773 printf("failed 2\n");
4777 cfg_rec
= cfg_rec
->next
;
4778 if (cfg_rec
->cfg
->path_invalid_utf8_handling
!= NONE
||
4779 cfg_rec
->cfg
->path_nul_encoded_handling
!= STATUS_404
||
4780 cfg_rec
->cfg
->path_nul_raw_handling
!= STATUS_400
) {
4781 printf("failed 3\n");
4790 ConfRestoreContextBackup();
4791 HtpConfigRestoreBackup();
4797 /** \test Test %2f decoding in profile Apache_2_2
4799 * %2f in path is left untouched
4800 * %2f in query string is normalized to %2F
4801 * %252f in query string is decoded/normalized to %2F
4803 static int HTPParserDecodingTest01(void)
4807 uint8_t httpbuf1
[] =
4808 "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4809 "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4810 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4811 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4813 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4815 HtpState
*htp_state
= NULL
;
4823 personality: Apache_2\n\
4826 ConfCreateContextBackup();
4828 HtpConfigCreateBackup();
4829 ConfYamlLoadString(input
, strlen(input
));
4831 const char *addr
= "4.3.2.1";
4832 memset(&ssn
, 0, sizeof(ssn
));
4834 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4838 f
->proto
= IPPROTO_TCP
;
4839 f
->alproto
= ALPROTO_HTTP1
;
4841 StreamTcpInitConfig(true);
4844 for (u
= 0; u
< httplen1
; u
++) {
4847 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
4848 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
4849 else flags
= STREAM_TOSERVER
;
4852 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4854 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4862 htp_state
= f
->alstate
;
4863 if (htp_state
== NULL
) {
4864 printf("no http state: ");
4868 uint8_t ref1
[] = "/abc%2fdef";
4869 size_t reflen
= sizeof(ref1
) - 1;
4871 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4874 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
4875 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4876 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
4877 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
4879 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4883 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
4884 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4886 printf("normalized uri \"");
4887 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4889 PrintRawUriFp(stdout
, ref1
, reflen
);
4895 uint8_t ref2
[] = "/abc/def?ghi/jkl";
4896 reflen
= sizeof(ref2
) - 1;
4898 tx
= HTPStateGetTx(htp_state
, 1);
4901 tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
4902 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4903 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
4904 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
4906 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4910 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
4911 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4913 printf("normalized uri \"");
4914 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4916 PrintRawUriFp(stdout
, ref2
, reflen
);
4922 uint8_t ref3
[] = "/abc/def?ghi%2fjkl";
4923 reflen
= sizeof(ref3
) - 1;
4924 tx
= HTPStateGetTx(htp_state
, 2);
4927 tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
4928 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4929 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
4930 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
4932 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4936 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref3
,
4937 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4939 printf("normalized uri \"");
4940 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4942 PrintRawUriFp(stdout
, ref3
, reflen
);
4951 if (alp_tctx
!= NULL
)
4952 AppLayerParserThreadCtxFree(alp_tctx
);
4955 ConfRestoreContextBackup();
4956 HtpConfigRestoreBackup();
4958 StreamTcpFreeConfig(true);
4963 /** \test Test %2f decoding in profile IDS
4965 * %2f in path decoded to /
4966 * %2f in query string is decoded to /
4967 * %252f in query string is decoded to %2F
4969 static int HTPParserDecodingTest02(void)
4973 uint8_t httpbuf1
[] =
4974 "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4975 "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4976 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4977 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4979 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4981 HtpState
*htp_state
= NULL
;
4990 double-decode-path: no\n\
4991 double-decode-query: no\n\
4994 ConfCreateContextBackup();
4996 HtpConfigCreateBackup();
4997 ConfYamlLoadString(input
, strlen(input
));
4999 const char *addr
= "4.3.2.1";
5000 memset(&ssn
, 0, sizeof(ssn
));
5002 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5006 f
->proto
= IPPROTO_TCP
;
5007 f
->alproto
= ALPROTO_HTTP1
;
5009 StreamTcpInitConfig(true);
5012 for (u
= 0; u
< httplen1
; u
++) {
5015 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5016 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5017 else flags
= STREAM_TOSERVER
;
5020 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5022 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5030 htp_state
= f
->alstate
;
5031 if (htp_state
== NULL
) {
5032 printf("no http state: ");
5036 uint8_t ref1
[] = "/abc/def";
5037 size_t reflen
= sizeof(ref1
) - 1;
5039 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5042 HtpTxUserData
*tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
5043 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5044 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5045 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5047 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5051 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5052 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5054 printf("normalized uri \"");
5055 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5057 PrintRawUriFp(stdout
, ref1
, reflen
);
5063 uint8_t ref2
[] = "/abc/def?ghi/jkl";
5064 reflen
= sizeof(ref2
) - 1;
5066 tx
= HTPStateGetTx(htp_state
, 1);
5069 tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
5070 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5071 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5072 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5074 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5078 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
5079 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5081 printf("normalized uri \"");
5082 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5084 PrintRawUriFp(stdout
, ref2
, reflen
);
5090 uint8_t ref3
[] = "/abc/def?ghi%2fjkl";
5091 reflen
= sizeof(ref3
) - 1;
5092 tx
= HTPStateGetTx(htp_state
, 2);
5095 tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5096 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5097 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5098 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
" (3): ",
5100 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5104 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref3
,
5105 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5107 printf("normalized uri \"");
5108 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5110 PrintRawUriFp(stdout
, ref3
, reflen
);
5119 if (alp_tctx
!= NULL
)
5120 AppLayerParserThreadCtxFree(alp_tctx
);
5123 ConfRestoreContextBackup();
5124 HtpConfigRestoreBackup();
5126 StreamTcpFreeConfig(true);
5131 /** \test Test %2f decoding in profile IDS with double-decode-* options
5133 * %252f in path decoded to /
5134 * %252f in query string is decoded to /
5136 static int HTPParserDecodingTest03(void)
5140 uint8_t httpbuf1
[] =
5141 "GET /abc%252fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5142 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5143 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5145 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5147 HtpState
*htp_state
= NULL
;
5156 double-decode-path: yes\n\
5157 double-decode-query: yes\n\
5160 ConfCreateContextBackup();
5162 HtpConfigCreateBackup();
5163 ConfYamlLoadString(input
, strlen(input
));
5165 const char *addr
= "4.3.2.1";
5166 memset(&ssn
, 0, sizeof(ssn
));
5168 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5172 f
->proto
= IPPROTO_TCP
;
5173 f
->alproto
= ALPROTO_HTTP1
;
5175 StreamTcpInitConfig(true);
5178 for (u
= 0; u
< httplen1
; u
++) {
5181 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5182 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5183 else flags
= STREAM_TOSERVER
;
5186 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5188 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5196 htp_state
= f
->alstate
;
5197 if (htp_state
== NULL
) {
5198 printf("no http state: ");
5202 uint8_t ref1
[] = "/abc/def";
5203 size_t reflen
= sizeof(ref1
) - 1;
5205 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5208 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5209 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5210 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5211 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5213 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5217 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5218 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5220 printf("normalized uri \"");
5221 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5223 PrintRawUriFp(stdout
, ref1
, reflen
);
5229 uint8_t ref2
[] = "/abc/def?ghi/jkl";
5230 reflen
= sizeof(ref2
) - 1;
5232 tx
= HTPStateGetTx(htp_state
, 1);
5235 tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
5236 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5237 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5238 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5240 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5244 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
5245 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5247 printf("normalized uri \"");
5248 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5250 PrintRawUriFp(stdout
, ref2
, reflen
);
5259 if (alp_tctx
!= NULL
)
5260 AppLayerParserThreadCtxFree(alp_tctx
);
5263 ConfRestoreContextBackup();
5264 HtpConfigRestoreBackup();
5266 StreamTcpFreeConfig(true);
5271 /** \test Test http:// in query profile IDS
5273 static int HTPParserDecodingTest04(void)
5277 uint8_t httpbuf1
[] =
5278 "GET /abc/def?a=http://www.abc.com/ HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5279 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5281 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5283 HtpState
*htp_state
= NULL
;
5292 double-decode-path: yes\n\
5293 double-decode-query: yes\n\
5296 ConfCreateContextBackup();
5298 HtpConfigCreateBackup();
5299 ConfYamlLoadString(input
, strlen(input
));
5301 const char *addr
= "4.3.2.1";
5302 memset(&ssn
, 0, sizeof(ssn
));
5304 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5308 f
->proto
= IPPROTO_TCP
;
5309 f
->alproto
= ALPROTO_HTTP1
;
5311 StreamTcpInitConfig(true);
5314 for (u
= 0; u
< httplen1
; u
++) {
5317 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5318 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5319 else flags
= STREAM_TOSERVER
;
5322 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5324 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5332 htp_state
= f
->alstate
;
5333 if (htp_state
== NULL
) {
5334 printf("no http state: ");
5338 uint8_t ref1
[] = "/abc/def?a=http://www.abc.com/";
5339 size_t reflen
= sizeof(ref1
) - 1;
5341 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5344 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5345 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5346 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5347 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5349 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5353 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5354 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5356 printf("normalized uri \"");
5357 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5359 PrintRawUriFp(stdout
, ref1
, reflen
);
5368 if (alp_tctx
!= NULL
)
5369 AppLayerParserThreadCtxFree(alp_tctx
);
5372 ConfRestoreContextBackup();
5373 HtpConfigRestoreBackup();
5375 StreamTcpFreeConfig(true);
5380 /** \test Test \ char in query profile IDS. Bug 739
5382 static int HTPParserDecodingTest05(void)
5386 uint8_t httpbuf1
[] =
5387 "GET /index?id=\\\"<script>alert(document.cookie)</script> HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5388 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5390 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5392 HtpState
*htp_state
= NULL
;
5401 double-decode-path: yes\n\
5402 double-decode-query: yes\n\
5405 ConfCreateContextBackup();
5407 HtpConfigCreateBackup();
5408 ConfYamlLoadString(input
, strlen(input
));
5410 const char *addr
= "4.3.2.1";
5411 memset(&ssn
, 0, sizeof(ssn
));
5413 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5417 f
->proto
= IPPROTO_TCP
;
5418 f
->alproto
= ALPROTO_HTTP1
;
5420 StreamTcpInitConfig(true);
5423 for (u
= 0; u
< httplen1
; u
++) {
5426 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5427 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5428 else flags
= STREAM_TOSERVER
;
5431 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5433 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5441 htp_state
= f
->alstate
;
5442 if (htp_state
== NULL
) {
5443 printf("no http state: ");
5447 uint8_t ref1
[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
5448 size_t reflen
= sizeof(ref1
) - 1;
5450 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5453 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5454 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5455 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5456 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5458 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5462 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5463 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5465 printf("normalized uri \"");
5466 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5468 PrintRawUriFp(stdout
, ref1
, reflen
);
5477 if (alp_tctx
!= NULL
)
5478 AppLayerParserThreadCtxFree(alp_tctx
);
5481 ConfRestoreContextBackup();
5482 HtpConfigRestoreBackup();
5484 StreamTcpFreeConfig(true);
5489 /** \test Test + char in query. Bug 1035
5491 static int HTPParserDecodingTest06(void)
5495 uint8_t httpbuf1
[] =
5496 "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5497 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5499 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5501 HtpState
*htp_state
= NULL
;
5510 double-decode-path: yes\n\
5511 double-decode-query: yes\n\
5514 ConfCreateContextBackup();
5516 HtpConfigCreateBackup();
5517 ConfYamlLoadString(input
, strlen(input
));
5519 const char *addr
= "4.3.2.1";
5520 memset(&ssn
, 0, sizeof(ssn
));
5522 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5526 f
->proto
= IPPROTO_TCP
;
5527 f
->alproto
= ALPROTO_HTTP1
;
5529 StreamTcpInitConfig(true);
5532 for (u
= 0; u
< httplen1
; u
++) {
5535 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5536 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5537 else flags
= STREAM_TOSERVER
;
5540 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5542 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5550 htp_state
= f
->alstate
;
5551 if (htp_state
== NULL
) {
5552 printf("no http state: ");
5556 uint8_t ref1
[] = "/put.php?ip=1.2.3.4&port=+6000";
5557 size_t reflen
= sizeof(ref1
) - 1;
5559 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5562 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5563 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5564 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5565 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5567 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5571 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5572 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5574 printf("normalized uri \"");
5575 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5577 PrintRawUriFp(stdout
, ref1
, reflen
);
5586 if (alp_tctx
!= NULL
)
5587 AppLayerParserThreadCtxFree(alp_tctx
);
5590 ConfRestoreContextBackup();
5591 HtpConfigRestoreBackup();
5593 StreamTcpFreeConfig(true);
5598 /** \test Test + char in query. Bug 1035
5600 static int HTPParserDecodingTest07(void)
5604 uint8_t httpbuf1
[] =
5605 "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5606 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5608 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5610 HtpState
*htp_state
= NULL
;
5619 double-decode-path: yes\n\
5620 double-decode-query: yes\n\
5621 query-plusspace-decode: yes\n\
5624 ConfCreateContextBackup();
5626 HtpConfigCreateBackup();
5627 ConfYamlLoadString(input
, strlen(input
));
5629 const char *addr
= "4.3.2.1";
5630 memset(&ssn
, 0, sizeof(ssn
));
5632 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5636 f
->proto
= IPPROTO_TCP
;
5637 f
->alproto
= ALPROTO_HTTP1
;
5639 StreamTcpInitConfig(true);
5642 for (u
= 0; u
< httplen1
; u
++) {
5645 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5646 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5647 else flags
= STREAM_TOSERVER
;
5650 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5652 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5660 htp_state
= f
->alstate
;
5661 if (htp_state
== NULL
) {
5662 printf("no http state: ");
5666 uint8_t ref1
[] = "/put.php?ip=1.2.3.4&port= 6000";
5667 size_t reflen
= sizeof(ref1
) - 1;
5669 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5672 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5673 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5674 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5675 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5677 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5681 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5682 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5684 printf("normalized uri \"");
5685 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5687 PrintRawUriFp(stdout
, ref1
, reflen
);
5696 if (alp_tctx
!= NULL
)
5697 AppLayerParserThreadCtxFree(alp_tctx
);
5700 ConfRestoreContextBackup();
5701 HtpConfigRestoreBackup();
5703 StreamTcpFreeConfig(true);
5708 /** \test Test 'proxy' URI normalization. Ticket 1008
5710 static int HTPParserDecodingTest08(void)
5714 uint8_t httpbuf1
[] =
5715 "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5716 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5718 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5720 HtpState
*htp_state
= NULL
;
5731 ConfCreateContextBackup();
5733 HtpConfigCreateBackup();
5734 ConfYamlLoadString(input
, strlen(input
));
5736 const char *addr
= "4.3.2.1";
5737 memset(&ssn
, 0, sizeof(ssn
));
5739 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5743 f
->proto
= IPPROTO_TCP
;
5744 f
->alproto
= ALPROTO_HTTP1
;
5746 StreamTcpInitConfig(true);
5749 for (u
= 0; u
< httplen1
; u
++) {
5752 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5753 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5754 else flags
= STREAM_TOSERVER
;
5757 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5759 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5767 htp_state
= f
->alstate
;
5768 if (htp_state
== NULL
) {
5769 printf("no http state: ");
5773 uint8_t ref1
[] = "/blah/";
5774 size_t reflen
= sizeof(ref1
) - 1;
5776 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5779 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5780 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5781 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5782 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5784 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5788 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5789 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5791 printf("normalized uri \"");
5792 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5794 PrintRawUriFp(stdout
, ref1
, reflen
);
5803 if (alp_tctx
!= NULL
)
5804 AppLayerParserThreadCtxFree(alp_tctx
);
5807 ConfRestoreContextBackup();
5808 HtpConfigRestoreBackup();
5810 StreamTcpFreeConfig(true);
5815 /** \test Test 'proxy' URI normalization. Ticket 1008
5817 static int HTPParserDecodingTest09(void)
5821 uint8_t httpbuf1
[] =
5822 "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5823 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5825 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5827 HtpState
*htp_state
= NULL
;
5836 uri-include-all: true\n\
5839 ConfCreateContextBackup();
5841 HtpConfigCreateBackup();
5842 ConfYamlLoadString(input
, strlen(input
));
5844 const char *addr
= "4.3.2.1";
5845 memset(&ssn
, 0, sizeof(ssn
));
5847 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5851 f
->proto
= IPPROTO_TCP
;
5852 f
->alproto
= ALPROTO_HTTP1
;
5854 StreamTcpInitConfig(true);
5857 for (u
= 0; u
< httplen1
; u
++) {
5860 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5861 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5862 else flags
= STREAM_TOSERVER
;
5865 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5867 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5875 htp_state
= f
->alstate
;
5876 if (htp_state
== NULL
) {
5877 printf("no http state: ");
5881 uint8_t ref1
[] = "http://suricata-ids.org/blah/";
5882 size_t reflen
= sizeof(ref1
) - 1;
5884 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5887 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5888 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5889 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5890 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5892 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5896 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5897 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5899 printf("normalized uri \"");
5900 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5902 PrintRawUriFp(stdout
, ref1
, reflen
);
5911 if (alp_tctx
!= NULL
)
5912 AppLayerParserThreadCtxFree(alp_tctx
);
5915 ConfRestoreContextBackup();
5916 HtpConfigRestoreBackup();
5918 StreamTcpFreeConfig(true);
5923 /** \test BG box crash -- chunks are messed up. Observed for real. */
5924 static int HTPBodyReassemblyTest01(void)
5928 memset(&htud
, 0x00, sizeof(htud
));
5930 memset(&hstate
, 0x00, sizeof(hstate
));
5932 memset(&flow
, 0x00, sizeof(flow
));
5933 AppLayerParserState
*parser
= AppLayerParserStateAlloc();
5935 memset(&tx
, 0, sizeof(tx
));
5938 flow
.alparser
= parser
;
5940 uint8_t chunk1
[] = "--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
5941 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";
5943 int r
= HtpBodyAppendChunk(NULL
, &htud
.request_body
, chunk1
, sizeof(chunk1
)-1);
5945 r
= HtpBodyAppendChunk(NULL
, &htud
.request_body
, chunk2
, sizeof(chunk2
)-1);
5948 const uint8_t *chunks_buffer
= NULL
;
5949 uint32_t chunks_buffer_len
= 0;
5951 HtpRequestBodyReassemble(&htud
, &chunks_buffer
, &chunks_buffer_len
);
5952 if (chunks_buffer
== NULL
) {
5956 printf("REASSCHUNK START: \n");
5957 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
5958 printf("REASSCHUNK END: \n");
5961 HtpRequestBodyHandleMultipart(&hstate
, &htud
, &tx
, chunks_buffer
, chunks_buffer_len
);
5963 if (htud
.request_body
.content_len_so_far
!= 669) {
5964 printf("htud.request_body.content_len_so_far %"PRIu64
": ", htud
.request_body
.content_len_so_far
);
5968 if (hstate
.files_ts
!= NULL
)
5976 /** \test BG crash */
5977 static int HTPSegvTest01(void)
5981 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";
5982 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5990 double-decode-path: no\n\
5991 double-decode-query: no\n\
5992 request-body-limit: 0\n\
5993 response-body-limit: 0\n\
5996 ConfCreateContextBackup();
5998 HtpConfigCreateBackup();
5999 ConfYamlLoadString(input
, strlen(input
));
6003 HtpState
*http_state
= NULL
;
6004 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6006 memset(&ssn
, 0, sizeof(ssn
));
6008 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6012 f
->proto
= IPPROTO_TCP
;
6013 f
->alproto
= ALPROTO_HTTP1
;
6015 StreamTcpInitConfig(true);
6017 SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
6019 int r
= AppLayerParserParse(
6020 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6022 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6027 SCLogDebug("\n>>>> processing chunk 1 again <<<<\n");
6029 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, httpbuf1
, httplen1
);
6031 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6037 http_state
= f
->alstate
;
6038 if (http_state
== NULL
) {
6039 printf("no http state: ");
6044 AppLayerDecoderEvents
*decoder_events
= AppLayerParserGetDecoderEvents(f
->alparser
);
6045 if (decoder_events
!= NULL
) {
6046 printf("app events: ");
6053 if (alp_tctx
!= NULL
)
6054 AppLayerParserThreadCtxFree(alp_tctx
);
6057 ConfRestoreContextBackup();
6058 HtpConfigRestoreBackup();
6059 StreamTcpFreeConfig(true);
6064 /** \test Test really long request, this should result in HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6065 static int HTPParserTest14(void)
6076 double-decode-path: no\n\
6077 double-decode-query: no\n\
6078 request-body-limit: 0\n\
6079 response-body-limit: 0\n\
6081 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6082 FAIL_IF_NULL(alp_tctx
);
6084 memset(&ssn
, 0, sizeof(ssn
));
6086 ConfCreateContextBackup();
6088 HtpConfigCreateBackup();
6089 ConfYamlLoadString(input
, strlen(input
));
6092 char *httpbuf
= SCMalloc(len
);
6093 FAIL_IF_NULL(httpbuf
);
6094 memset(httpbuf
, 0x00, len
);
6096 /* create the request with a longer than 18k cookie */
6097 strlcpy(httpbuf
, "GET /blah/ HTTP/1.1\r\n"
6098 "Host: myhost.lan\r\n"
6099 "Connection: keep-alive\r\n"
6101 "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"
6102 "Referer: http://blah.lan/\r\n"
6103 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6105 size_t o
= strlen(httpbuf
);
6106 for ( ; o
< len
- 4; o
++) {
6109 httpbuf
[len
- 4] = '\r';
6110 httpbuf
[len
- 3] = '\n';
6111 httpbuf
[len
- 2] = '\r';
6112 httpbuf
[len
- 1] = '\n';
6114 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6117 f
->alproto
= ALPROTO_HTTP1
;
6118 f
->proto
= IPPROTO_TCP
;
6120 StreamTcpInitConfig(true);
6123 for (u
= 0; u
< len
; u
++) {
6126 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
6127 else if (u
== (len
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
6128 else flags
= STREAM_TOSERVER
;
6130 (void)AppLayerParserParse(
6131 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, (uint8_t *)&httpbuf
[u
], 1);
6133 HtpState
*htp_state
= f
->alstate
;
6134 FAIL_IF_NULL(htp_state
);
6136 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6138 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6139 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6141 void *txtmp
= AppLayerParserGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, f
->alstate
, 0);
6142 AppLayerDecoderEvents
*decoder_events
=
6143 AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP1
, txtmp
);
6144 FAIL_IF_NULL(decoder_events
);
6146 FAIL_IF(decoder_events
->events
[0] != HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG
);
6148 AppLayerParserThreadCtxFree(alp_tctx
);
6149 StreamTcpFreeConfig(true);
6154 ConfRestoreContextBackup();
6155 HtpConfigRestoreBackup();
6159 /** \test Test really long request (same as HTPParserTest14), now with config
6160 * update to allow it */
6161 static int HTPParserTest15(void)
6165 char *httpbuf
= NULL
;
6168 HtpState
*htp_state
= NULL
;
6177 double-decode-path: no\n\
6178 double-decode-query: no\n\
6179 request-body-limit: 0\n\
6180 response-body-limit: 0\n\
6181 meta-field-limit: 20000\n\
6183 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6185 memset(&ssn
, 0, sizeof(ssn
));
6187 ConfCreateContextBackup();
6189 HtpConfigCreateBackup();
6190 ConfYamlLoadString(input
, strlen(input
));
6193 httpbuf
= SCMalloc(len
);
6194 if (unlikely(httpbuf
== NULL
))
6196 memset(httpbuf
, 0x00, len
);
6198 /* create the request with a longer than 18k cookie */
6199 strlcpy(httpbuf
, "GET /blah/ HTTP/1.1\r\n"
6200 "Host: myhost.lan\r\n"
6201 "Connection: keep-alive\r\n"
6203 "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"
6204 "Referer: http://blah.lan/\r\n"
6205 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6207 size_t o
= strlen(httpbuf
);
6208 for ( ; o
< len
- 4; o
++) {
6211 httpbuf
[len
- 4] = '\r';
6212 httpbuf
[len
- 3] = '\n';
6213 httpbuf
[len
- 2] = '\r';
6214 httpbuf
[len
- 1] = '\n';
6216 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6220 f
->proto
= IPPROTO_TCP
;
6221 f
->alproto
= ALPROTO_HTTP1
;
6223 StreamTcpInitConfig(true);
6226 for (u
= 0; u
< len
; u
++) {
6229 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
6230 else if (u
== (len
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
6231 else flags
= STREAM_TOSERVER
;
6234 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, (uint8_t *)&httpbuf
[u
], 1);
6236 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
6243 htp_state
= f
->alstate
;
6244 if (htp_state
== NULL
) {
6245 printf("no http state: ");
6249 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6250 if (tx
== NULL
|| tx
->request_method_number
!= HTP_M_GET
|| tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
)
6252 printf("expected method M_GET and got %s: , expected protocol "
6253 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx
->request_method
),
6254 bstr_util_strdup_to_c(tx
->request_protocol
));
6259 void *txtmp
= AppLayerParserGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, f
->alstate
, 0);
6260 AppLayerDecoderEvents
*decoder_events
=
6261 AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP1
, txtmp
);
6262 if (decoder_events
!= NULL
) {
6263 printf("app events: ");
6271 if (alp_tctx
!= NULL
)
6272 AppLayerParserThreadCtxFree(alp_tctx
);
6273 StreamTcpFreeConfig(true);
6275 if (httpbuf
!= NULL
)
6279 ConfRestoreContextBackup();
6280 HtpConfigRestoreBackup();
6284 /** \test Test unusual delims in request line HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6285 static int HTPParserTest16(void)
6290 HtpState
*htp_state
= NULL
;
6292 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6294 memset(&ssn
, 0, sizeof(ssn
));
6296 uint8_t httpbuf
[] = "GET\f/blah/\fHTTP/1.1\r\n"
6297 "Host: myhost.lan\r\n"
6298 "Connection: keep-alive\r\n"
6300 "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"
6301 "Referer: http://blah.lan/\r\n"
6302 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6303 "Cookie: blah\r\n\r\n";
6304 size_t len
= sizeof(httpbuf
) - 1;
6306 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6310 f
->proto
= IPPROTO_TCP
;
6311 f
->alproto
= ALPROTO_HTTP1
;
6313 StreamTcpInitConfig(true);
6315 uint8_t flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
6318 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, (uint8_t *)httpbuf
, len
);
6320 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6326 htp_state
= f
->alstate
;
6327 if (htp_state
== NULL
) {
6328 printf("no http state: ");
6332 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6333 if (tx
== NULL
|| tx
->request_method_number
!= HTP_M_GET
|| tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
)
6335 printf("expected method M_GET and got %s: , expected protocol "
6336 "HTTP/1.1 and got %s \n", tx
? bstr_util_strdup_to_c(tx
->request_method
) : "tx null",
6337 tx
? bstr_util_strdup_to_c(tx
->request_protocol
) : "tx null");
6341 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
6342 //these events are disabled during fuzzing as they are too noisy and consume much resource
6344 void *txtmp
= AppLayerParserGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, f
->alstate
, 0);
6345 AppLayerDecoderEvents
*decoder_events
=
6346 AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP1
, txtmp
);
6347 if (decoder_events
== NULL
) {
6348 printf("no app events: ");
6354 if (decoder_events
->events
[0] != HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT
) {
6355 printf("HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT not set: ");
6359 if (decoder_events
->events
[1] != HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT
) {
6360 printf("HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT not set: ");
6367 if (alp_tctx
!= NULL
)
6368 AppLayerParserThreadCtxFree(alp_tctx
);
6369 StreamTcpFreeConfig(true);
6374 /** \test Test response not HTTP
6376 static int HTPParserTest20(void)
6379 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6380 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6381 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6382 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6383 uint8_t httpbuf2
[] = "NOTHTTP\r\nSOMEOTHERDATA";
6384 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6385 uint8_t httpbuf3
[] = "STILLNOTHTTP\r\nSOMEMOREOTHERDATA";
6386 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
6388 HtpState
*http_state
= NULL
;
6389 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6390 FAIL_IF_NULL(alp_tctx
);
6392 memset(&ssn
, 0, sizeof(ssn
));
6394 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6397 f
->proto
= IPPROTO_TCP
;
6398 f
->alproto
= ALPROTO_HTTP1
;
6400 StreamTcpInitConfig(true);
6402 int r
= AppLayerParserParse(
6403 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6406 r
= AppLayerParserParse(
6407 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6410 r
= AppLayerParserParse(
6411 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf3
, httplen3
);
6414 http_state
= f
->alstate
;
6415 FAIL_IF_NULL(http_state
);
6416 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6418 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6421 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6422 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6424 FAIL_IF(tx
->response_status_number
!= 0);
6425 FAIL_IF(tx
->response_protocol_number
!= -1);
6427 AppLayerParserThreadCtxFree(alp_tctx
);
6428 StreamTcpFreeConfig(true);
6433 /** \test Test response not HTTP
6435 static int HTPParserTest21(void)
6438 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6439 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6440 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6441 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6442 uint8_t httpbuf2
[] = "999 NOTHTTP REALLY\r\nSOMEOTHERDATA\r\n";
6443 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6444 uint8_t httpbuf3
[] = "STILLNOTHTTP\r\nSOMEMOREOTHERDATA";
6445 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
6447 HtpState
*http_state
= NULL
;
6448 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6449 FAIL_IF_NULL(alp_tctx
);
6451 memset(&ssn
, 0, sizeof(ssn
));
6453 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6456 f
->proto
= IPPROTO_TCP
;
6457 f
->alproto
= ALPROTO_HTTP1
;
6459 StreamTcpInitConfig(true);
6461 int r
= AppLayerParserParse(
6462 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6465 r
= AppLayerParserParse(
6466 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6469 r
= AppLayerParserParse(
6470 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf3
, httplen3
);
6473 http_state
= f
->alstate
;
6474 FAIL_IF_NULL(http_state
);
6475 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6477 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6480 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6481 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6483 FAIL_IF(tx
->response_status_number
!= 0);
6484 FAIL_IF(tx
->response_protocol_number
!= -1);
6486 AppLayerParserThreadCtxFree(alp_tctx
);
6487 StreamTcpFreeConfig(true);
6492 /** \test Test response not HTTP
6494 static int HTPParserTest22(void)
6497 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6498 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6499 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6500 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6501 uint8_t httpbuf2
[] = "\r\n0000=0000000/ASDF3_31.zip, 456723\r\n"
6502 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6503 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6505 HtpState
*http_state
= NULL
;
6506 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6507 FAIL_IF_NULL(alp_tctx
);
6509 memset(&ssn
, 0, sizeof(ssn
));
6511 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6514 f
->proto
= IPPROTO_TCP
;
6515 f
->alproto
= ALPROTO_HTTP1
;
6517 StreamTcpInitConfig(true);
6519 int r
= AppLayerParserParse(
6520 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6523 r
= AppLayerParserParse(
6524 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6527 http_state
= f
->alstate
;
6528 FAIL_IF_NULL(http_state
);
6529 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6531 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6534 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6535 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6537 FAIL_IF(tx
->response_status_number
!= -0);
6538 FAIL_IF(tx
->response_protocol_number
!= -1);
6540 AppLayerParserThreadCtxFree(alp_tctx
);
6541 StreamTcpFreeConfig(true);
6546 /** \test Test response not HTTP
6548 static int HTPParserTest23(void)
6551 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6552 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6553 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6554 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6555 uint8_t httpbuf2
[] = "HTTP0000=0000000/ASDF3_31.zip, 456723\r\n"
6556 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6557 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6559 HtpState
*http_state
= NULL
;
6560 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6561 FAIL_IF_NULL(alp_tctx
);
6563 memset(&ssn
, 0, sizeof(ssn
));
6565 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6568 f
->proto
= IPPROTO_TCP
;
6569 f
->alproto
= ALPROTO_HTTP1
;
6571 StreamTcpInitConfig(true);
6573 int r
= AppLayerParserParse(
6574 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6577 r
= AppLayerParserParse(
6578 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6581 http_state
= f
->alstate
;
6582 FAIL_IF_NULL(http_state
);
6583 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6585 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6588 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6589 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6591 FAIL_IF(tx
->response_status_number
!= -1);
6592 FAIL_IF(tx
->response_protocol_number
!= -2);
6594 AppLayerParserThreadCtxFree(alp_tctx
);
6595 StreamTcpFreeConfig(true);
6600 /** \test Test response not HTTP
6602 static int HTPParserTest24(void)
6605 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6606 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6607 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6608 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6609 uint8_t httpbuf2
[] = "HTTP/1.0 0000=0000000/ASDF3_31.zip, 456723\r\n"
6610 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6611 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6613 HtpState
*http_state
= NULL
;
6614 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6615 FAIL_IF_NULL(alp_tctx
);
6617 memset(&ssn
, 0, sizeof(ssn
));
6619 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6622 f
->proto
= IPPROTO_TCP
;
6623 f
->alproto
= ALPROTO_HTTP1
;
6625 StreamTcpInitConfig(true);
6627 int r
= AppLayerParserParse(
6628 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6631 r
= AppLayerParserParse(
6632 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6635 http_state
= f
->alstate
;
6636 FAIL_IF_NULL(http_state
);
6637 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6639 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6642 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6643 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6645 FAIL_IF(tx
->response_status_number
!= -1);
6646 FAIL_IF(tx
->response_protocol_number
!= HTP_PROTOCOL_1_0
);
6648 AppLayerParserThreadCtxFree(alp_tctx
);
6649 StreamTcpFreeConfig(true);
6654 /** \test multi transactions and cleanup */
6655 static int HTPParserTest25(void)
6657 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6658 FAIL_IF_NULL(alp_tctx
);
6660 StreamTcpInitConfig(true);
6662 memset(&ssn
, 0, sizeof(ssn
));
6664 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6667 f
->proto
= IPPROTO_TCP
;
6668 f
->alproto
= ALPROTO_HTTP1
;
6670 const char *str
= "GET / HTTP/1.1\r\nHost: www.google.com\r\nUser-Agent: Suricata/1.0\r\n\r\n";
6671 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
,
6672 (uint8_t *)str
, strlen(str
));
6673 FAIL_IF_NOT(r
== 0);
6674 r
= AppLayerParserParse(
6675 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6676 FAIL_IF_NOT(r
== 0);
6677 r
= AppLayerParserParse(
6678 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6679 FAIL_IF_NOT(r
== 0);
6680 r
= AppLayerParserParse(
6681 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6682 FAIL_IF_NOT(r
== 0);
6683 r
= AppLayerParserParse(
6684 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6685 FAIL_IF_NOT(r
== 0);
6686 r
= AppLayerParserParse(
6687 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6688 FAIL_IF_NOT(r
== 0);
6689 r
= AppLayerParserParse(
6690 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6691 FAIL_IF_NOT(r
== 0);
6692 r
= AppLayerParserParse(
6693 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6694 FAIL_IF_NOT(r
== 0);
6696 str
= "HTTP 1.1 200 OK\r\nServer: Suricata/1.0\r\nContent-Length: 8\r\n\r\nSuricata";
6697 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
,
6698 (uint8_t *)str
, strlen(str
));
6699 FAIL_IF_NOT(r
== 0);
6700 r
= AppLayerParserParse(
6701 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6702 FAIL_IF_NOT(r
== 0);
6703 r
= AppLayerParserParse(
6704 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6705 FAIL_IF_NOT(r
== 0);
6706 r
= AppLayerParserParse(
6707 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6708 FAIL_IF_NOT(r
== 0);
6709 r
= AppLayerParserParse(
6710 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6711 FAIL_IF_NOT(r
== 0);
6712 r
= AppLayerParserParse(
6713 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6714 FAIL_IF_NOT(r
== 0);
6715 r
= AppLayerParserParse(
6716 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6717 FAIL_IF_NOT(r
== 0);
6718 r
= AppLayerParserParse(
6719 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6720 FAIL_IF_NOT(r
== 0);
6722 AppLayerParserTransactionsCleanup(f
);
6725 UTHAppLayerParserStateGetIds(f
->alparser
, &ret
[0], &ret
[1], &ret
[2], &ret
[3]);
6726 FAIL_IF_NOT(ret
[0] == 8); // inspect_id[0]
6727 FAIL_IF_NOT(ret
[1] == 8); // inspect_id[1]
6728 FAIL_IF_NOT(ret
[2] == 8); // log_id
6729 FAIL_IF_NOT(ret
[3] == 8); // min_id
6731 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_EOF
,
6732 (uint8_t *)str
, strlen(str
));
6733 FAIL_IF_NOT(r
== 0);
6734 AppLayerParserTransactionsCleanup(f
);
6736 UTHAppLayerParserStateGetIds(f
->alparser
, &ret
[0], &ret
[1], &ret
[2], &ret
[3]);
6737 FAIL_IF_NOT(ret
[0] == 8); // inspect_id[0] not updated by ..Cleanup() until full tx is done
6738 FAIL_IF_NOT(ret
[1] == 8); // inspect_id[1]
6739 FAIL_IF_NOT(ret
[2] == 8); // log_id
6740 FAIL_IF_NOT(ret
[3] == 8); // min_id
6742 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_EOF
,
6743 (uint8_t *)str
, strlen(str
));
6744 FAIL_IF_NOT(r
== 0);
6745 AppLayerParserTransactionsCleanup(f
);
6747 UTHAppLayerParserStateGetIds(f
->alparser
, &ret
[0], &ret
[1], &ret
[2], &ret
[3]);
6748 FAIL_IF_NOT(ret
[0] == 9); // inspect_id[0]
6749 FAIL_IF_NOT(ret
[1] == 9); // inspect_id[1]
6750 FAIL_IF_NOT(ret
[2] == 9); // log_id
6751 FAIL_IF_NOT(ret
[3] == 9); // min_id
6753 HtpState
*http_state
= f
->alstate
;
6754 FAIL_IF_NULL(http_state
);
6756 AppLayerParserThreadCtxFree(alp_tctx
);
6757 StreamTcpFreeConfig(true);
6763 static int HTPParserTest26(void)
6772 request-body-limit: 1\n\
6773 response-body-limit: 1\n\
6775 ConfCreateContextBackup();
6777 HtpConfigCreateBackup();
6778 ConfYamlLoadString(input
, strlen(input
));
6784 DetectEngineCtx
*de_ctx
= NULL
;
6785 DetectEngineThreadCtx
*det_ctx
= NULL
;
6787 uint8_t httpbuf1
[] = "GET /alice.txt HTTP/1.1\r\n\r\n";
6788 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6789 uint8_t httpbuf2
[] = "HTTP/1.1 200 OK\r\n"
6790 "Content-Type: text/plain\r\n"
6791 "Content-Length: 228\r\n\r\n"
6792 "Alice was beginning to get very tired of sitting by her sister on the bank."
6793 "Alice was beginning to get very tired of sitting by her sister on the bank.";
6794 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6795 uint8_t httpbuf3
[] = "Alice was beginning to get very tired of sitting by her sister on the bank.\r\n\r\n";
6796 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
6798 HtpState
*http_state
= NULL
;
6799 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6800 FAIL_IF_NULL(alp_tctx
);
6802 memset(&th_v
, 0, sizeof(th_v
));
6803 memset(&f
, 0, sizeof(f
));
6804 memset(&ssn
, 0, sizeof(ssn
));
6806 p1
= UTHBuildPacket(NULL
, 0, IPPROTO_TCP
);
6807 p2
= UTHBuildPacket(NULL
, 0, IPPROTO_TCP
);
6809 FLOW_INITIALIZE(&f
);
6810 f
.protoctx
= (void *)&ssn
;
6811 f
.proto
= IPPROTO_TCP
;
6812 f
.flags
|= FLOW_IPV4
;
6815 p1
->flowflags
|= FLOW_PKT_TOSERVER
;
6816 p1
->flowflags
|= FLOW_PKT_ESTABLISHED
;
6817 p1
->flags
|= PKT_HAS_FLOW
|PKT_STREAM_EST
;
6819 p2
->flowflags
|= FLOW_PKT_TOCLIENT
;
6820 p2
->flowflags
|= FLOW_PKT_ESTABLISHED
;
6821 p2
->flags
|= PKT_HAS_FLOW
|PKT_STREAM_EST
;
6822 f
.alproto
= ALPROTO_HTTP1
;
6824 StreamTcpInitConfig(true);
6826 de_ctx
= DetectEngineCtxInit();
6827 FAIL_IF_NULL(de_ctx
);
6829 de_ctx
->flags
|= DE_QUIET
;
6831 de_ctx
->sig_list
= SigInit(de_ctx
,"alert http any any -> any any "
6832 "(filestore; sid:1; rev:1;)");
6833 FAIL_IF_NULL(de_ctx
->sig_list
);
6835 SigGroupBuild(de_ctx
);
6836 DetectEngineThreadCtxInit(&th_v
, (void *)de_ctx
, (void *)&det_ctx
);
6838 int r
= AppLayerParserParse(
6839 &th_v
, alp_tctx
, &f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, httpbuf1
, httplen1
);
6842 http_state
= f
.alstate
;
6843 FAIL_IF_NULL(http_state
);
6846 SigMatchSignatures(&th_v
, de_ctx
, det_ctx
, p1
);
6848 FAIL_IF((PacketAlertCheck(p1
, 1)));
6851 SigMatchSignatures(&th_v
, de_ctx
, det_ctx
, p1
);
6853 FAIL_IF((PacketAlertCheck(p1
, 1)));
6855 r
= AppLayerParserParse(
6856 &th_v
, alp_tctx
, &f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, httpbuf2
, httplen2
);
6859 http_state
= f
.alstate
;
6860 FAIL_IF_NULL(http_state
);
6863 SigMatchSignatures(&th_v
, de_ctx
, det_ctx
, p2
);
6865 FAIL_IF(!(PacketAlertCheck(p2
, 1)));
6867 r
= AppLayerParserParse(
6868 &th_v
, alp_tctx
, &f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, httpbuf3
, httplen3
);
6871 http_state
= f
.alstate
;
6872 FAIL_IF_NULL(http_state
);
6874 FileContainer
*ffc
= HTPStateGetFiles(http_state
, STREAM_TOCLIENT
);
6877 File
*ptr
= ffc
->head
;
6878 FAIL_IF(ptr
->state
!= FILE_STATE_CLOSED
);
6880 AppLayerParserThreadCtxFree(alp_tctx
);
6881 DetectEngineThreadCtxDeinit(&th_v
, (void *)det_ctx
);
6882 DetectEngineCtxFree(de_ctx
);
6883 StreamTcpFreeConfig(true);
6887 UTHFreePackets(&p1
, 1);
6888 UTHFreePackets(&p2
, 1);
6890 ConfRestoreContextBackup();
6891 HtpConfigRestoreBackup();
6895 static int HTPParserTest27(void)
6898 memset(&cfg
, 0, sizeof(cfg
));
6899 cfg
.body_limit
= 1500;
6900 FileReassemblyDepthEnable(2000);
6902 uint32_t len
= 1000;
6904 HtpTxUserData
*tx_ud
= SCMalloc(sizeof(HtpTxUserData
));
6905 FAIL_IF_NULL(tx_ud
);
6907 tx_ud
->tsflags
|= HTP_STREAM_DEPTH_SET
;
6908 tx_ud
->request_body
.content_len_so_far
= 2500;
6910 FAIL_IF(AppLayerHtpCheckDepth(&cfg
, &tx_ud
->request_body
, tx_ud
->tsflags
));
6912 len
= AppLayerHtpComputeChunkLength(tx_ud
->request_body
.content_len_so_far
,
6914 FileReassemblyDepth(),
6917 FAIL_IF(len
!= 1000);
6925 * \brief Register the Unit tests for the HTTP protocol
6927 static void HTPParserRegisterTests(void)
6929 UtRegisterTest("HTPParserTest01", HTPParserTest01
);
6930 UtRegisterTest("HTPParserTest01a", HTPParserTest01a
);
6931 UtRegisterTest("HTPParserTest01b", HTPParserTest01b
);
6932 UtRegisterTest("HTPParserTest01c", HTPParserTest01c
);
6933 UtRegisterTest("HTPParserTest02", HTPParserTest02
);
6934 UtRegisterTest("HTPParserTest03", HTPParserTest03
);
6935 UtRegisterTest("HTPParserTest04", HTPParserTest04
);
6936 UtRegisterTest("HTPParserTest05", HTPParserTest05
);
6937 UtRegisterTest("HTPParserTest06", HTPParserTest06
);
6938 UtRegisterTest("HTPParserTest07", HTPParserTest07
);
6939 UtRegisterTest("HTPParserTest08", HTPParserTest08
);
6940 UtRegisterTest("HTPParserTest09", HTPParserTest09
);
6941 UtRegisterTest("HTPParserTest10", HTPParserTest10
);
6942 UtRegisterTest("HTPParserTest11", HTPParserTest11
);
6943 UtRegisterTest("HTPParserTest12", HTPParserTest12
);
6944 UtRegisterTest("HTPParserTest13", HTPParserTest13
);
6945 UtRegisterTest("HTPParserConfigTest01", HTPParserConfigTest01
);
6946 UtRegisterTest("HTPParserConfigTest02", HTPParserConfigTest02
);
6947 UtRegisterTest("HTPParserConfigTest03", HTPParserConfigTest03
);
6948 #if 0 /* disabled when we upgraded to libhtp 0.5.x */
6949 UtRegisterTest("HTPParserConfigTest04", HTPParserConfigTest04
, 1);
6952 UtRegisterTest("HTPParserDecodingTest01", HTPParserDecodingTest01
);
6953 UtRegisterTest("HTPParserDecodingTest02", HTPParserDecodingTest02
);
6954 UtRegisterTest("HTPParserDecodingTest03", HTPParserDecodingTest03
);
6955 UtRegisterTest("HTPParserDecodingTest04", HTPParserDecodingTest04
);
6956 UtRegisterTest("HTPParserDecodingTest05", HTPParserDecodingTest05
);
6957 UtRegisterTest("HTPParserDecodingTest06", HTPParserDecodingTest06
);
6958 UtRegisterTest("HTPParserDecodingTest07", HTPParserDecodingTest07
);
6959 UtRegisterTest("HTPParserDecodingTest08", HTPParserDecodingTest08
);
6960 UtRegisterTest("HTPParserDecodingTest09", HTPParserDecodingTest09
);
6962 UtRegisterTest("HTPBodyReassemblyTest01", HTPBodyReassemblyTest01
);
6964 UtRegisterTest("HTPSegvTest01", HTPSegvTest01
);
6966 UtRegisterTest("HTPParserTest14", HTPParserTest14
);
6967 UtRegisterTest("HTPParserTest15", HTPParserTest15
);
6968 UtRegisterTest("HTPParserTest16", HTPParserTest16
);
6969 UtRegisterTest("HTPParserTest20", HTPParserTest20
);
6970 UtRegisterTest("HTPParserTest21", HTPParserTest21
);
6971 UtRegisterTest("HTPParserTest22", HTPParserTest22
);
6972 UtRegisterTest("HTPParserTest23", HTPParserTest23
);
6973 UtRegisterTest("HTPParserTest24", HTPParserTest24
);
6974 UtRegisterTest("HTPParserTest25", HTPParserTest25
);
6975 UtRegisterTest("HTPParserTest26", HTPParserTest26
);
6976 UtRegisterTest("HTPParserTest27", HTPParserTest27
);
6978 HTPFileParserRegisterTests();
6979 HTPXFFParserRegisterTests();
6981 #endif /* UNITTESTS */