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
->decoder_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
->decoder_events
, e
);
284 SCLogDebug("couldn't set event %u", e
);
287 static AppLayerDecoderEvents
*HTPGetEvents(void *tx
)
289 SCLogDebug("get HTTP events for TX %p", tx
);
291 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
293 SCLogDebug("has htud, htud->decoder_events %p", htud
->decoder_events
);
294 return htud
->decoder_events
;
300 /** \brief Function to allocates the HTTP state memory and also creates the HTTP
301 * connection parser to be used by the HTP library
303 static void *HTPStateAlloc(void *orig_state
, AppProto proto_orig
)
307 HtpState
*s
= HTPMalloc(sizeof(HtpState
));
308 if (unlikely(s
== NULL
)) {
309 SCReturnPtr(NULL
, "void");
312 memset(s
, 0x00, sizeof(HtpState
));
315 SCMutexLock(&htp_state_mem_lock
);
317 htp_state_memuse
+= sizeof(HtpState
);
318 SCLogDebug("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
319 SCMutexUnlock(&htp_state_mem_lock
);
322 SCReturnPtr((void *)s
, "void");
325 static void HtpTxUserDataFree(HtpState
*state
, HtpTxUserData
*htud
)
328 HtpBodyFree(&htud
->request_body
);
329 HtpBodyFree(&htud
->response_body
);
330 bstr_free(htud
->request_uri_normalized
);
331 if (htud
->request_headers_raw
)
332 HTPFree(htud
->request_headers_raw
, htud
->request_headers_raw_len
);
333 if (htud
->response_headers_raw
)
334 HTPFree(htud
->response_headers_raw
, htud
->response_headers_raw_len
);
335 AppLayerDecoderEventsFreeEvents(&htud
->decoder_events
);
337 HTPFree(htud
->boundary
, htud
->boundary_len
);
338 if (htud
->de_state
!= NULL
) {
339 DetectEngineStateFree(htud
->de_state
);
341 HTPFree(htud
, sizeof(HtpTxUserData
));
345 /** \brief Function to frees the HTTP state memory and also frees the HTTP
346 * connection parser memory which was used by the HTP library
348 void HTPStateFree(void *state
)
352 HtpState
*s
= (HtpState
*)state
;
357 /* free the connection parser memory used by HTP library */
358 if (s
->connp
!= NULL
) {
359 SCLogDebug("freeing HTP state");
362 uint64_t total_txs
= HTPStateGetTxCnt(state
);
363 /* free the list of body chunks */
364 if (s
->conn
!= NULL
) {
365 for (tx_id
= 0; tx_id
< total_txs
; tx_id
++) {
366 htp_tx_t
*tx
= HTPStateGetTx(s
, tx_id
);
368 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
369 HtpTxUserDataFree(s
, htud
);
370 htp_tx_set_user_data(tx
, NULL
);
374 htp_connp_destroy_all(s
->connp
);
378 HTPFileCloseHandleRange(s
->files_tc
, 0, s
->file_range
, NULL
, 0);
379 HttpRangeFreeBlock(s
->file_range
);
382 FileContainerFree(s
->files_ts
);
383 FileContainerFree(s
->files_tc
);
384 HTPFree(s
, sizeof(HtpState
));
387 SCMutexLock(&htp_state_mem_lock
);
389 htp_state_memuse
-= sizeof(HtpState
);
390 SCLogDebug("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
391 SCMutexUnlock(&htp_state_mem_lock
);
398 * \brief HTP transaction cleanup callback
400 * \warning We cannot actually free the transactions here. It seems that
401 * HTP only accepts freeing of transactions in the response callback.
403 static void HTPStateTransactionFree(void *state
, uint64_t id
)
407 HtpState
*s
= (HtpState
*)state
;
409 SCLogDebug("state %p, id %"PRIu64
, s
, id
);
411 htp_tx_t
*tx
= HTPStateGetTx(s
, id
);
413 /* This will remove obsolete body chunks */
414 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
415 HtpTxUserDataFree(s
, htud
);
416 htp_tx_set_user_data(tx
, NULL
);
418 /* hack: even if libhtp considers the tx incomplete, we want to
419 * free it here. htp_tx_destroy however, will refuse to do this.
420 * As htp_tx_destroy_incomplete isn't available in the public API,
421 * we hack around it here. */
423 tx
->request_progress
== HTP_REQUEST_COMPLETE
&&
424 tx
->response_progress
== HTP_RESPONSE_COMPLETE
)))
426 tx
->request_progress
= HTP_REQUEST_COMPLETE
;
427 tx
->response_progress
= HTP_RESPONSE_COMPLETE
;
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 AppLayerHtpEnableRequestBodyCallback(void)
442 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_BODY
);
447 * \brief Sets a flag that informs the HTP app layer that some module in the
448 * engine needs the http request body data.
451 void AppLayerHtpEnableResponseBodyCallback(void)
455 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_RESPONSE_BODY
);
460 * \brief Sets a flag that informs the HTP app layer that some module in the
461 * engine needs the http request multi part header.
465 static void AppLayerHtpNeedMultipartHeader(void)
468 AppLayerHtpEnableRequestBodyCallback();
470 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_MULTIPART
);
475 * \brief Sets a flag that informs the HTP app layer that some module in the
476 * engine needs the http request file.
480 void AppLayerHtpNeedFileInspection(void)
483 AppLayerHtpNeedMultipartHeader();
484 AppLayerHtpEnableRequestBodyCallback();
485 AppLayerHtpEnableResponseBodyCallback();
487 SC_ATOMIC_OR(htp_config_flags
, HTP_REQUIRE_REQUEST_FILE
);
491 static void AppLayerHtpSetStreamDepthFlag(void *tx
, uint8_t flags
)
493 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data((htp_tx_t
*)tx
);
495 if (flags
& STREAM_TOCLIENT
) {
496 tx_ud
->tcflags
|= HTP_STREAM_DEPTH_SET
;
498 tx_ud
->tsflags
|= HTP_STREAM_DEPTH_SET
;
503 static bool AppLayerHtpCheckDepth(const HTPCfgDir
*cfg
, HtpBody
*body
, uint8_t flags
)
505 if (flags
& HTP_STREAM_DEPTH_SET
) {
506 uint32_t stream_depth
= FileReassemblyDepth();
507 if (body
->content_len_so_far
< (uint64_t)stream_depth
|| stream_depth
== 0) {
511 if (cfg
->body_limit
== 0 || body
->content_len_so_far
< cfg
->body_limit
) {
518 static uint32_t AppLayerHtpComputeChunkLength(uint64_t content_len_so_far
, uint32_t body_limit
,
519 uint32_t stream_depth
, uint8_t flags
, uint32_t data_len
)
521 uint32_t chunk_len
= 0;
522 if (!(flags
& HTP_STREAM_DEPTH_SET
) && body_limit
> 0 &&
523 (content_len_so_far
< (uint64_t)body_limit
) &&
524 (content_len_so_far
+ (uint64_t)data_len
) > body_limit
)
526 chunk_len
= body_limit
- content_len_so_far
;
527 } else if ((flags
& HTP_STREAM_DEPTH_SET
) && stream_depth
> 0 &&
528 (content_len_so_far
< (uint64_t)stream_depth
) &&
529 (content_len_so_far
+ (uint64_t)data_len
) > stream_depth
)
531 chunk_len
= stream_depth
- content_len_so_far
;
533 SCLogDebug("len %u", chunk_len
);
534 return (chunk_len
== 0 ? data_len
: chunk_len
);
537 /* below error messages updated up to libhtp 0.5.7 (git 379632278b38b9a792183694a4febb9e0dbd1e7a) */
542 { "GZip decompressor: inflateInit2 failed", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED
},
543 { "Request field invalid: colon missing", HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON
},
544 { "Response field invalid: missing colon", HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON
},
545 { "Request chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN
},
546 { "Response chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN
},
547 /* { "Invalid T-E value in request", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_T_E
548 { "Invalid T-E value in response", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE}, <- nothing to replace it */
549 /* { "Invalid C-L field in request", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_C_L */
550 { "Invalid C-L field in response", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE
},
551 { "Already seen 100-Continue", HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN
},
552 { "Unable to match response to request", HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST
},
553 { "Invalid server port information in request", HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST
},
554 /* { "Invalid authority port", HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT}, htp no longer returns this error */
555 { "Request buffer over", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG
},
556 { "Response buffer over", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG
},
557 { "C-T multipart/byteranges in responses not supported", HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES
},
558 { "Compression bomb:", HTTP_DECODER_EVENT_COMPRESSION_BOMB
},
565 { "GZip decompressor:", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED
},
566 { "Request field invalid", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID
},
567 { "Response field invalid", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID
},
568 { "Request header name is not a token", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID
},
569 { "Response header name is not a token", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID
},
570 /* { "Host information in request headers required by HTTP/1.1", HTTP_DECODER_EVENT_MISSING_HOST_HEADER}, <- tx flag HTP_HOST_MISSING
571 { "Host information ambiguous", HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS}, <- tx flag HTP_HOST_AMBIGUOUS */
572 { "Invalid request field folding", HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING
},
573 { "Invalid response field folding", HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING
},
574 /* 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);
575 * luckily, "Request server port=" is unique */
576 /* { "Request server port number differs from the actual TCP port", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH}, */
577 { "Request server port=", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH
},
578 { "Request line: URI contains non-compliant delimiter", HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT
},
579 { "Request line: non-compliant delimiter between Method and URI", HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT
},
580 { "Request line: leading whitespace", HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE
},
581 { "Too many response content encoding layers", HTTP_DECODER_EVENT_TOO_MANY_ENCODING_LAYERS
},
582 { "C-E gzip has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
583 { "C-E deflate has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
584 { "C-E unknown setting", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER
},
585 { "Excessive request header repetitions", HTTP_DECODER_EVENT_REQUEST_HEADER_REPETITION
},
586 { "Excessive response header repetitions", HTTP_DECODER_EVENT_RESPONSE_HEADER_REPETITION
},
587 { "Transfer-encoding has abnormal chunked value", HTTP_DECODER_EVENT_RESPONSE_ABNORMAL_TRANSFER_ENCODING
},
588 { "Chunked transfer-encoding on HTTP/0.9 or HTTP/1.0", HTTP_DECODER_EVENT_RESPONSE_CHUNKED_OLD_PROTO
},
589 { "Invalid response line: invalid protocol", HTTP_DECODER_EVENT_RESPONSE_INVALID_PROTOCOL
},
590 { "Invalid response line: invalid response status", HTTP_DECODER_EVENT_RESPONSE_INVALID_STATUS
},
591 { "Request line incomplete", HTTP_DECODER_EVENT_REQUEST_LINE_INCOMPLETE
},
592 { "Unexpected request body", HTTP_DECODER_EVENT_REQUEST_BODY_UNEXPECTED
},
593 { "LZMA decompressor: memory limit reached", HTTP_DECODER_EVENT_LZMA_MEMLIMIT_REACHED
},
594 { "Ambiguous request C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST
},
595 { "Ambiguous response C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE
},
598 #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0]))
599 #define HTP_WARNING_MAX (sizeof(htp_warnings) / sizeof(htp_warnings[0]))
604 * \brief Get the warning id for the warning msg.
606 * \param msg warning message
608 * \retval id the id or 0 in case of not found
610 static int HTPHandleWarningGetId(const char *msg
)
612 SCLogDebug("received warning \"%s\"", msg
);
614 for (idx
= 0; idx
< HTP_WARNING_MAX
; idx
++) {
615 if (strncmp(htp_warnings
[idx
].msg
, msg
,
616 strlen(htp_warnings
[idx
].msg
)) == 0)
618 return htp_warnings
[idx
].de
;
628 * \brief Get the error id for the error msg.
630 * \param msg error message
632 * \retval id the id or 0 in case of not found
634 static int HTPHandleErrorGetId(const char *msg
)
636 SCLogDebug("received error \"%s\"", msg
);
639 for (idx
= 0; idx
< HTP_ERROR_MAX
; idx
++) {
640 if (strncmp(htp_errors
[idx
].msg
, msg
,
641 strlen(htp_errors
[idx
].msg
)) == 0)
643 return htp_errors
[idx
].de
;
653 * \brief Check state for errors, warnings and add any as events
656 * \param dir direction: STREAM_TOSERVER or STREAM_TOCLIENT
658 static void HTPHandleError(HtpState
*s
, const uint8_t dir
)
660 if (s
== NULL
|| s
->conn
== NULL
||
661 s
->conn
->messages
== NULL
) {
665 size_t size
= htp_list_size(s
->conn
->messages
);
667 if(size
>= HTP_MAX_MESSAGES
) {
668 if (s
->htp_messages_offset
< HTP_MAX_MESSAGES
) {
669 //only once per HtpState
670 HTPSetEvent(s
, NULL
, dir
, HTTP_DECODER_EVENT_TOO_MANY_WARNINGS
);
671 s
->htp_messages_offset
= HTP_MAX_MESSAGES
;
672 //too noisy in fuzzing
673 //DEBUG_VALIDATE_BUG_ON("Too many libhtp messages");
675 // ignore further messages
679 for (msg
= s
->htp_messages_offset
; msg
< size
; msg
++) {
680 htp_log_t
*log
= htp_list_get(s
->conn
->messages
, msg
);
684 HtpTxUserData
*htud
= NULL
;
685 htp_tx_t
*tx
= log
->tx
; // will be NULL in <=0.5.9
687 htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
689 SCLogDebug("message %s", log
->msg
);
691 int id
= HTPHandleErrorGetId(log
->msg
);
693 id
= HTPHandleWarningGetId(log
->msg
);
695 id
= HTTP_DECODER_EVENT_UNKNOWN_ERROR
;
699 HTPSetEvent(s
, htud
, dir
, id
);
702 s
->htp_messages_offset
= (uint16_t)msg
;
703 SCLogDebug("s->htp_messages_offset %u", s
->htp_messages_offset
);
706 static inline void HTPErrorCheckTxRequestFlags(HtpState
*s
, htp_tx_t
*tx
)
709 BUG_ON(s
== NULL
|| tx
== NULL
);
711 if (tx
->flags
& ( HTP_REQUEST_INVALID_T_E
|HTP_REQUEST_INVALID_C_L
|
712 HTP_HOST_MISSING
|HTP_HOST_AMBIGUOUS
|HTP_HOSTU_INVALID
|
715 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
719 if (tx
->flags
& HTP_REQUEST_INVALID_T_E
)
720 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
721 HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST
);
722 if (tx
->flags
& HTP_REQUEST_INVALID_C_L
)
723 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
724 HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST
);
725 if (tx
->flags
& HTP_HOST_MISSING
)
726 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
727 HTTP_DECODER_EVENT_MISSING_HOST_HEADER
);
728 if (tx
->flags
& HTP_HOST_AMBIGUOUS
)
729 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
730 HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS
);
731 if (tx
->flags
& HTP_HOSTU_INVALID
)
732 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
733 HTTP_DECODER_EVENT_URI_HOST_INVALID
);
734 if (tx
->flags
& HTP_HOSTH_INVALID
)
735 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
736 HTTP_DECODER_EVENT_HEADER_HOST_INVALID
);
738 if (tx
->request_auth_type
== HTP_AUTH_UNRECOGNIZED
) {
739 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
742 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
743 HTTP_DECODER_EVENT_AUTH_UNRECOGNIZED
);
745 if (tx
->is_protocol_0_9
&& tx
->request_method_number
== HTP_M_UNKNOWN
&&
746 (tx
->request_protocol_number
== HTP_PROTOCOL_INVALID
||
747 tx
->request_protocol_number
== HTP_PROTOCOL_UNKNOWN
)) {
748 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
751 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
752 HTTP_DECODER_EVENT_REQUEST_LINE_INVALID
);
756 static int Setup(Flow
*f
, HtpState
*hstate
)
758 /* store flow ref in state so callbacks can access it */
761 HTPCfgRec
*htp_cfg_rec
= &cfglist
;
762 htp_cfg_t
*htp
= cfglist
.cfg
; /* Default to the global HTP config */
763 void *user_data
= NULL
;
765 if (FLOW_IS_IPV4(f
)) {
766 SCLogDebug("Looking up HTP config for ipv4 %08x", *GET_IPV4_DST_ADDR_PTR(f
));
767 (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(f
), cfgtree
, &user_data
);
769 else if (FLOW_IS_IPV6(f
)) {
770 SCLogDebug("Looking up HTP config for ipv6");
771 (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)GET_IPV6_DST_ADDR(f
), cfgtree
, &user_data
);
774 SCLogError(SC_ERR_INVALID_ARGUMENT
, "unknown address family, bug!");
778 if (user_data
!= NULL
) {
779 htp_cfg_rec
= user_data
;
780 htp
= htp_cfg_rec
->cfg
;
781 SCLogDebug("LIBHTP using config: %p", htp
);
783 SCLogDebug("Using default HTP config: %p", htp
);
787 #ifdef DEBUG_VALIDATION
790 /* should never happen if HTPConfigure is properly invoked */
794 hstate
->connp
= htp_connp_create(htp
);
795 if (hstate
->connp
== NULL
) {
799 hstate
->conn
= htp_connp_get_connection(hstate
->connp
);
801 htp_connp_set_user_data(hstate
->connp
, (void *)hstate
);
802 hstate
->cfg
= htp_cfg_rec
;
804 SCLogDebug("New hstate->connp %p", hstate
->connp
);
806 htp_connp_open(hstate
->connp
, NULL
, f
->sp
, NULL
, f
->dp
, &f
->startts
);
808 StreamTcpReassemblySetMinInspectDepth(f
->protoctx
, STREAM_TOSERVER
,
809 htp_cfg_rec
->request
.inspect_min_size
);
810 StreamTcpReassemblySetMinInspectDepth(f
->protoctx
, STREAM_TOCLIENT
,
811 htp_cfg_rec
->response
.inspect_min_size
);
818 * \brief Function to handle the reassembled data from client and feed it to
819 * the HTP library to process it.
821 * \param flow Pointer to the flow the data belong to
822 * \param htp_state Pointer the state in which the parsed value to be stored
823 * \param pstate Application layer parser state for this session
824 * \param input Pointer the received HTTP client data
825 * \param input_len Length in bytes of the received data
826 * \param output Pointer to the output (not used in this function)
828 * \retval On success returns 1 or on failure returns -1.
830 static AppLayerResult
HTPHandleRequestData(Flow
*f
, void *htp_state
,
831 AppLayerParserState
*pstate
,
832 const uint8_t *input
, uint32_t input_len
,
833 void *local_data
, const uint8_t flags
)
837 HtpState
*hstate
= (HtpState
*)htp_state
;
839 /* On the first invocation, create the connection parser structure to
840 * be used by HTP library. This is looked up via IP in the radix
841 * tree. Failing that, the default HTP config is used.
843 if (NULL
== hstate
->conn
) {
844 if (Setup(f
, hstate
) != 0) {
845 SCReturnStruct(APP_LAYER_ERROR
);
848 DEBUG_VALIDATE_BUG_ON(hstate
->connp
== NULL
);
850 htp_time_t ts
= { f
->lastts
.tv_sec
, f
->lastts
.tv_usec
};
851 /* pass the new data to the htp parser */
853 const int r
= htp_connp_req_data(hstate
->connp
, &ts
, input
, input_len
);
855 case HTP_STREAM_ERROR
:
861 HTPHandleError(hstate
, STREAM_TOSERVER
);
864 /* if the TCP connection is closed, then close the HTTP connection */
865 if (AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF_TS
) &&
866 !(hstate
->flags
& HTP_FLAG_STATE_CLOSED_TS
))
868 htp_connp_req_close(hstate
->connp
, &ts
);
869 hstate
->flags
|= HTP_FLAG_STATE_CLOSED_TS
;
870 SCLogDebug("stream eof encountered, closing htp handle for ts");
873 SCLogDebug("hstate->connp %p", hstate
->connp
);
876 SCReturnStruct(APP_LAYER_ERROR
);
878 SCReturnStruct(APP_LAYER_OK
);
882 * \brief Function to handle the reassembled data from server and feed it to
883 * the HTP library to process it.
885 * \param flow Pointer to the flow the data belong to
886 * \param htp_state Pointer the state in which the parsed value to be stored
887 * \param pstate Application layer parser state for this session
888 * \param input Pointer the received HTTP server data
889 * \param input_len Length in bytes of the received data
890 * \param output Pointer to the output (not used in this function)
892 * \retval On success returns 1 or on failure returns -1
894 static AppLayerResult
HTPHandleResponseData(Flow
*f
, void *htp_state
,
895 AppLayerParserState
*pstate
,
896 const uint8_t *input
, uint32_t input_len
,
897 void *local_data
, const uint8_t flags
)
901 HtpState
*hstate
= (HtpState
*)htp_state
;
903 /* On the first invocation, create the connection parser structure to
904 * be used by HTP library. This is looked up via IP in the radix
905 * tree. Failing that, the default HTP config is used.
907 if (NULL
== hstate
->conn
) {
908 if (Setup(f
, hstate
) != 0) {
909 SCReturnStruct(APP_LAYER_ERROR
);
912 DEBUG_VALIDATE_BUG_ON(hstate
->connp
== NULL
);
914 htp_time_t ts
= { f
->lastts
.tv_sec
, f
->lastts
.tv_usec
};
918 const int r
= htp_connp_res_data(hstate
->connp
, &ts
, input
, input_len
);
920 case HTP_STREAM_ERROR
:
923 case HTP_STREAM_TUNNEL
:
924 tx
= htp_connp_get_out_tx(hstate
->connp
);
925 if (tx
!= NULL
&& tx
->response_status_number
== 101) {
927 (htp_header_t
*)htp_table_get_c(tx
->response_headers
, "Upgrade");
929 if (bstr_cmp_c(h
->value
, "h2c") == 0) {
931 if (tx
->request_port_number
!= -1) {
932 dp
= (uint16_t)tx
->request_port_number
;
934 consumed
= htp_connp_res_data_consumed(hstate
->connp
);
935 AppLayerRequestProtocolChange(hstate
->f
, dp
, ALPROTO_HTTP2
);
936 // During HTTP2 upgrade, we may consume the HTTP1 part of the data
937 // and we need to parser the remaining part with HTTP2
938 if (consumed
> 0 && consumed
< input_len
) {
940 APP_LAYER_INCOMPLETE(consumed
, input_len
- consumed
));
942 SCReturnStruct(APP_LAYER_OK
);
950 HTPHandleError(hstate
, STREAM_TOCLIENT
);
953 /* if we the TCP connection is closed, then close the HTTP connection */
954 if (AppLayerParserStateIssetFlag(pstate
, APP_LAYER_PARSER_EOF_TC
) &&
955 !(hstate
->flags
& HTP_FLAG_STATE_CLOSED_TC
))
957 htp_connp_close(hstate
->connp
, &ts
);
958 hstate
->flags
|= HTP_FLAG_STATE_CLOSED_TC
;
961 SCLogDebug("hstate->connp %p", hstate
->connp
);
964 SCReturnStruct(APP_LAYER_ERROR
);
966 SCReturnStruct(APP_LAYER_OK
);
970 * \param name /Lowercase/ version of the variable name
972 static int HTTPParseContentDispositionHeader(uint8_t *name
, size_t name_len
,
973 uint8_t *data
, size_t len
, uint8_t **retptr
, size_t *retlen
)
976 printf("DATA START: \n");
977 PrintRawDataFp(stdout
, data
, len
);
978 printf("DATA END: \n");
983 for (x
= 0; x
< len
; x
++) {
984 if (!(isspace(data
[x
])))
991 uint8_t *line
= data
+x
;
992 size_t line_len
= len
-x
;
995 printf("LINE START: \n");
996 PrintRawDataFp(stdout
, line
, line_len
);
997 printf("LINE END: \n");
999 for (x
= 0 ; x
< line_len
; x
++) {
1001 if (line
[x
- 1] != '\\' && line
[x
] == '\"') {
1005 if (((line
[x
- 1] != '\\' && line
[x
] == ';') || ((x
+ 1) == line_len
)) && (quote
== 0 || quote
% 2 == 0)) {
1006 uint8_t *token
= line
+ offset
;
1007 size_t token_len
= x
- offset
;
1009 if ((x
+ 1) == line_len
) {
1015 while (offset
< line_len
&& isspace(line
[offset
])) {
1020 printf("TOKEN START: \n");
1021 PrintRawDataFp(stdout
, token
, token_len
);
1022 printf("TOKEN END: \n");
1024 if (token_len
> name_len
) {
1025 if (name
== NULL
|| SCMemcmpLowercase(name
, token
, name_len
) == 0) {
1026 uint8_t *value
= token
+ name_len
;
1027 size_t value_len
= token_len
- name_len
;
1029 if (value
[0] == '\"') {
1033 if (value
[value_len
-1] == '\"') {
1037 printf("VALUE START: \n");
1038 PrintRawDataFp(stdout
, value
, value_len
);
1039 printf("VALUE END: \n");
1042 *retlen
= value_len
;
1054 * \param name /Lowercase/ version of the variable name
1056 static int HTTPParseContentTypeHeader(uint8_t *name
, size_t name_len
,
1057 uint8_t *data
, size_t len
, uint8_t **retptr
, size_t *retlen
)
1061 printf("DATA START: \n");
1062 PrintRawDataFp(stdout
, data
, len
);
1063 printf("DATA END: \n");
1068 for (x
= 0; x
< len
; x
++) {
1069 if (!(isspace(data
[x
])))
1077 uint8_t *line
= data
+x
;
1078 size_t line_len
= len
-x
;
1081 printf("LINE START: \n");
1082 PrintRawDataFp(stdout
, line
, line_len
);
1083 printf("LINE END: \n");
1085 for (x
= 0 ; x
< line_len
; x
++) {
1087 if (line
[x
- 1] != '\\' && line
[x
] == '\"') {
1091 if (((line
[x
- 1] != '\\' && line
[x
] == ';') || ((x
+ 1) == line_len
)) && (quote
== 0 || quote
% 2 == 0)) {
1092 uint8_t *token
= line
+ offset
;
1093 size_t token_len
= x
- offset
;
1095 if ((x
+ 1) == line_len
) {
1101 while (offset
< line_len
&& isspace(line
[offset
])) {
1106 printf("TOKEN START: \n");
1107 PrintRawDataFp(stdout
, token
, token_len
);
1108 printf("TOKEN END: \n");
1110 if (token_len
> name_len
) {
1111 if (name
== NULL
|| SCMemcmpLowercase(name
, token
, name_len
) == 0) {
1112 uint8_t *value
= token
+ name_len
;
1113 size_t value_len
= token_len
- name_len
;
1115 if (value
[0] == '\"') {
1119 if (value
[value_len
-1] == '\"') {
1123 printf("VALUE START: \n");
1124 PrintRawDataFp(stdout
, value
, value_len
);
1125 printf("VALUE END: \n");
1128 *retlen
= value_len
;
1140 * \brief setup multipart parsing: extract boundary and store it
1142 * \param d HTTP transaction
1143 * \param htud transaction userdata
1145 * \retval 1 ok, multipart set up
1146 * \retval 0 ok, not multipart though
1147 * \retval -1 error: problem with the boundary
1149 * If the request contains a multipart message, this function will
1150 * set the HTP_BOUNDARY_SET in the transaction.
1152 static int HtpRequestBodySetupMultipart(htp_tx_t
*tx
, HtpTxUserData
*htud
)
1154 htp_header_t
*h
= (htp_header_t
*)htp_table_get_c(tx
->request_headers
,
1156 if (h
!= NULL
&& bstr_len(h
->value
) > 0) {
1157 uint8_t *boundary
= NULL
;
1158 size_t boundary_len
= 0;
1160 int r
= HTTPParseContentTypeHeader((uint8_t *)"boundary=", 9,
1161 (uint8_t *) bstr_ptr(h
->value
), bstr_len(h
->value
),
1162 &boundary
, &boundary_len
);
1165 printf("BOUNDARY START: \n");
1166 PrintRawDataFp(stdout
, boundary
, boundary_len
);
1167 printf("BOUNDARY END: \n");
1169 if (boundary_len
< HTP_BOUNDARY_MAX
) {
1170 htud
->boundary
= HTPMalloc(boundary_len
);
1171 if (htud
->boundary
== NULL
) {
1174 htud
->boundary_len
= (uint8_t)boundary_len
;
1175 memcpy(htud
->boundary
, boundary
, boundary_len
);
1177 htud
->tsflags
|= HTP_BOUNDARY_SET
;
1179 SCLogDebug("invalid boundary");
1189 #define C_D_HDR "content-disposition:"
1190 #define C_D_HDR_LEN 20
1191 #define C_T_HDR "content-type:"
1192 #define C_T_HDR_LEN 13
1194 static void HtpRequestBodyMultipartParseHeader(HtpState
*hstate
,
1195 HtpTxUserData
*htud
,
1196 uint8_t *header
, uint32_t header_len
,
1197 uint8_t **filename
, uint16_t *filename_len
,
1198 uint8_t **filetype
, uint16_t *filetype_len
)
1206 printf("HEADER START: \n");
1207 PrintRawDataFp(stdout
, header
, header_len
);
1208 printf("HEADER END: \n");
1211 while (header_len
> 0) {
1212 uint8_t *next_line
= Bs2bmSearch(header
, header_len
, (uint8_t *)"\r\n", 2);
1213 uint8_t *line
= header
;
1216 if (next_line
== NULL
) {
1217 line_len
= header_len
;
1219 line_len
= next_line
- header
;
1221 uint8_t *sc
= (uint8_t *)memchr(line
, ':', line_len
);
1223 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1224 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
);
1225 /* if the : we found is the final char, it means we have
1227 } else if (line_len
> 0 && sc
== &line
[line_len
- 1]) {
1228 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1229 HTTP_DECODER_EVENT_MULTIPART_INVALID_HEADER
);
1232 printf("LINE START: \n");
1233 PrintRawDataFp(stdout
, line
, line_len
);
1234 printf("LINE END: \n");
1236 if (line_len
>= C_D_HDR_LEN
&&
1237 SCMemcmpLowercase(C_D_HDR
, line
, C_D_HDR_LEN
) == 0) {
1238 uint8_t *value
= line
+ C_D_HDR_LEN
;
1239 uint32_t value_len
= line_len
- C_D_HDR_LEN
;
1241 /* parse content-disposition */
1242 (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1243 value
, value_len
, &fn
, &fn_len
);
1244 } else if (line_len
>= C_T_HDR_LEN
&&
1245 SCMemcmpLowercase(C_T_HDR
, line
, C_T_HDR_LEN
) == 0) {
1246 SCLogDebug("content-type line");
1247 uint8_t *value
= line
+ C_T_HDR_LEN
;
1248 uint32_t value_len
= line_len
- C_T_HDR_LEN
;
1250 (void)HTTPParseContentTypeHeader(NULL
, 0,
1251 value
, value_len
, &ft
, &ft_len
);
1255 if (next_line
== NULL
) {
1256 SCLogDebug("no next_line");
1259 header_len
-= ((next_line
+ 2) - header
);
1260 header
= next_line
+ 2;
1261 } /* while (header_len > 0) */
1263 if (fn_len
> USHRT_MAX
)
1265 if (ft_len
> USHRT_MAX
)
1269 *filename_len
= fn_len
;
1271 *filetype_len
= ft_len
;
1275 * \brief Create a single buffer from the HtpBodyChunks in our list
1277 * \param htud transaction user data
1278 * \param chunks_buffers pointer to pass back the buffer to the caller
1279 * \param chunks_buffer_len pointer to pass back the buffer length to the caller
1281 static void HtpRequestBodyReassemble(HtpTxUserData
*htud
,
1282 const uint8_t **chunks_buffer
, uint32_t *chunks_buffer_len
)
1284 StreamingBufferGetDataAtOffset(htud
->request_body
.sb
,
1285 chunks_buffer
, chunks_buffer_len
,
1286 htud
->request_body
.body_parsed
);
1289 static void FlagDetectStateNewFile(HtpTxUserData
*tx
, int dir
)
1292 if (tx
&& tx
->de_state
) {
1293 if (dir
== STREAM_TOSERVER
) {
1294 SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1295 tx
->de_state
->dir_state
[0].flags
|= DETECT_ENGINE_STATE_FLAG_FILE_NEW
;
1296 } else if (STREAM_TOCLIENT
) {
1297 SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1298 tx
->de_state
->dir_state
[1].flags
|= DETECT_ENGINE_STATE_FLAG_FILE_NEW
;
1304 * \brief Setup boundary buffers
1306 static void HtpRequestBodySetupBoundary(HtpTxUserData
*htud
,
1307 uint8_t *boundary
, uint32_t boundary_len
)
1309 memset(boundary
, '-', boundary_len
);
1310 memcpy(boundary
+ 2, htud
->boundary
, htud
->boundary_len
);
1313 static int HtpRequestBodyHandleMultipart(HtpState
*hstate
, HtpTxUserData
*htud
, void *tx
,
1314 const uint8_t *chunks_buffer
, uint32_t chunks_buffer_len
)
1317 uint8_t boundary
[htud
->boundary_len
+ 4]; /**< size limited to HTP_BOUNDARY_MAX + 4 */
1318 uint32_t expected_boundary_len
= htud
->boundary_len
+ 2;
1319 uint32_t expected_boundary_end_len
= htud
->boundary_len
+ 4;
1320 int tx_progress
= 0;
1323 printf("CHUNK START: \n");
1324 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
1325 printf("CHUNK END: \n");
1328 HtpRequestBodySetupBoundary(htud
, boundary
, htud
->boundary_len
+ 4);
1330 /* search for the header start, header end and form end */
1331 const uint8_t *header_start
= Bs2bmSearch(chunks_buffer
, chunks_buffer_len
,
1332 boundary
, expected_boundary_len
);
1333 /* end of the multipart form */
1334 const uint8_t *form_end
= NULL
;
1335 /* end marker belonging to header_start */
1336 const uint8_t *header_end
= NULL
;
1337 if (header_start
!= NULL
) {
1338 header_end
= Bs2bmSearch(header_start
, chunks_buffer_len
- (header_start
- chunks_buffer
),
1339 (uint8_t *)"\r\n\r\n", 4);
1340 form_end
= Bs2bmSearch(header_start
, chunks_buffer_len
- (header_start
- chunks_buffer
),
1341 boundary
, expected_boundary_end_len
);
1344 SCLogDebug("header_start %p, header_end %p, form_end %p", header_start
,
1345 header_end
, form_end
);
1347 /* we currently only handle multipart for ts. When we support it for tc,
1348 * we will need to supply right direction */
1349 tx_progress
= AppLayerParserGetStateProgress(IPPROTO_TCP
, ALPROTO_HTTP1
, tx
, STREAM_TOSERVER
);
1350 /* if we're in the file storage process, deal with that now */
1351 if (htud
->tsflags
& HTP_FILENAME_SET
) {
1352 if (header_start
!= NULL
|| (tx_progress
> HTP_REQUEST_BODY
)) {
1353 SCLogDebug("reached the end of the file");
1355 const uint8_t *filedata
= chunks_buffer
;
1356 uint32_t filedata_len
= 0;
1359 if (header_start
!= NULL
) {
1360 if (header_start
== filedata
+ 2) {
1361 /* last chunk had all data, but not the boundary */
1362 SCLogDebug("last chunk had all data, but not the boundary");
1364 } else if (header_start
> filedata
+ 2) {
1365 SCLogDebug("some data from last file before the boundary");
1366 /* some data from last file before the boundary */
1367 filedata_len
= header_start
- filedata
- 2;
1370 /* body parsing done, we did not get our form end. Use all data
1371 * we still have and signal to files API we have an issue. */
1372 if (tx_progress
> HTP_REQUEST_BODY
) {
1373 filedata_len
= chunks_buffer_len
;
1374 flags
= FILE_TRUNCATED
;
1377 if (filedata_len
> chunks_buffer_len
) {
1378 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1379 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1383 printf("FILEDATA (final chunk) START: \n");
1384 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1385 printf("FILEDATA (final chunk) END: \n");
1387 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1388 if (HTPFileClose(hstate
, filedata
, filedata_len
, flags
,
1389 STREAM_TOSERVER
) == -1)
1395 htud
->tsflags
&=~ HTP_FILENAME_SET
;
1399 SCLogDebug("not yet at the end of the file");
1401 if (chunks_buffer_len
> expected_boundary_end_len
) {
1402 const uint8_t *filedata
= chunks_buffer
;
1403 uint32_t filedata_len
= chunks_buffer_len
- expected_boundary_len
;
1405 printf("FILEDATA (part) START: \n");
1406 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1407 printf("FILEDATA (part) END: \n");
1410 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1411 result
= HTPFileStoreChunk(hstate
, filedata
,
1412 filedata_len
, STREAM_TOSERVER
);
1415 } else if (result
== -2) {
1416 /* we know for sure we're not storing the file */
1417 htud
->tsflags
|= HTP_DONTSTORE
;
1421 htud
->request_body
.body_parsed
+= filedata_len
;
1423 SCLogDebug("chunk too small to already process in part");
1430 while (header_start
!= NULL
&& header_end
!= NULL
&&
1431 header_end
!= form_end
&&
1432 header_start
< (chunks_buffer
+ chunks_buffer_len
) &&
1433 header_end
< (chunks_buffer
+ chunks_buffer_len
) &&
1434 header_start
< header_end
)
1436 uint8_t *filename
= NULL
;
1437 uint16_t filename_len
= 0;
1438 uint8_t *filetype
= NULL
;
1439 uint16_t filetype_len
= 0;
1441 uint32_t header_len
= header_end
- header_start
;
1442 SCLogDebug("header_len %u", header_len
);
1443 uint8_t *header
= (uint8_t *)header_start
;
1445 /* skip empty records */
1446 if (expected_boundary_len
== header_len
) {
1448 } else if ((expected_boundary_len
+ 2) <= header_len
) {
1449 header_len
-= (expected_boundary_len
+ 2);
1450 header
= (uint8_t *)header_start
+ (expected_boundary_len
+ 2); // + for 0d 0a
1453 HtpRequestBodyMultipartParseHeader(hstate
, htud
, header
, header_len
,
1454 &filename
, &filename_len
, &filetype
, &filetype_len
);
1456 if (filename
!= NULL
) {
1457 const uint8_t *filedata
= NULL
;
1458 uint32_t filedata_len
= 0;
1460 SCLogDebug("we have a filename");
1462 htud
->tsflags
|= HTP_FILENAME_SET
;
1463 htud
->tsflags
&= ~HTP_DONTSTORE
;
1465 SCLogDebug("header_end %p", header_end
);
1466 SCLogDebug("form_end %p", form_end
);
1468 /* everything until the final boundary is the file */
1469 if (form_end
!= NULL
) {
1470 SCLogDebug("have form_end");
1472 filedata
= header_end
+ 4;
1473 if (form_end
== filedata
) {
1474 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1475 HTTP_DECODER_EVENT_MULTIPART_NO_FILEDATA
);
1477 } else if (form_end
< filedata
) {
1478 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1479 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1483 filedata_len
= form_end
- (header_end
+ 4 + 2);
1484 SCLogDebug("filedata_len %"PRIuMAX
, (uintmax_t)filedata_len
);
1487 uint8_t *header_next
= Bs2bmSearch(filedata
, filedata_len
,
1488 boundary
, expected_boundary_len
);
1489 if (header_next
!= NULL
) {
1490 filedata_len
-= (form_end
- header_next
);
1493 if (filedata_len
> chunks_buffer_len
) {
1494 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1495 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1498 SCLogDebug("filedata_len %"PRIuMAX
, (uintmax_t)filedata_len
);
1500 printf("FILEDATA START: \n");
1501 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1502 printf("FILEDATA END: \n");
1505 result
= HTPFileOpen(hstate
, htud
, filename
, filename_len
, filedata
, filedata_len
,
1506 HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1509 } else if (result
== -2) {
1510 htud
->tsflags
|= HTP_DONTSTORE
;
1512 if (HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
) == -1) {
1516 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1518 htud
->request_body
.body_parsed
+= (header_end
- chunks_buffer
);
1519 htud
->tsflags
&= ~HTP_FILENAME_SET
;
1521 SCLogDebug("chunk doesn't contain form end");
1523 filedata
= header_end
+ 4;
1524 filedata_len
= chunks_buffer_len
- (filedata
- chunks_buffer
);
1525 SCLogDebug("filedata_len %u (chunks_buffer_len %u)", filedata_len
, chunks_buffer_len
);
1527 if (filedata_len
> chunks_buffer_len
) {
1528 HTPSetEvent(hstate
, htud
, STREAM_TOSERVER
,
1529 HTTP_DECODER_EVENT_MULTIPART_GENERIC_ERROR
);
1534 printf("FILEDATA START: \n");
1535 PrintRawDataFp(stdout
, filedata
, filedata_len
);
1536 printf("FILEDATA END: \n");
1538 /* form doesn't end in this chunk, but the part might. Lets
1539 * see if have another coming up */
1540 uint8_t *header_next
= Bs2bmSearch(filedata
, filedata_len
,
1541 boundary
, expected_boundary_len
);
1542 SCLogDebug("header_next %p", header_next
);
1543 if (header_next
== NULL
) {
1544 SCLogDebug("more file data to come");
1546 uint32_t offset
= (header_end
+ 4) - chunks_buffer
;
1547 SCLogDebug("offset %u", offset
);
1548 htud
->request_body
.body_parsed
+= offset
;
1550 if (filedata_len
>= (expected_boundary_len
+ 2)) {
1551 filedata_len
-= (expected_boundary_len
+ 2 - 1);
1552 SCLogDebug("opening file with partial data");
1557 result
= HTPFileOpen(hstate
, htud
, filename
, filename_len
, filedata
,
1558 filedata_len
, HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1561 } else if (result
== -2) {
1562 htud
->tsflags
|= HTP_DONTSTORE
;
1564 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1565 htud
->request_body
.body_parsed
+= filedata_len
;
1566 SCLogDebug("htud->request_body.body_parsed %"PRIu64
, htud
->request_body
.body_parsed
);
1568 } else if (header_next
- filedata
> 2) {
1569 filedata_len
= header_next
- filedata
- 2;
1570 SCLogDebug("filedata_len %u", filedata_len
);
1572 result
= HTPFileOpen(hstate
, htud
, filename
, filename_len
, filedata
,
1573 filedata_len
, HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1576 } else if (result
== -2) {
1577 htud
->tsflags
|= HTP_DONTSTORE
;
1579 if (HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
) == -1) {
1583 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1585 htud
->tsflags
&= ~HTP_FILENAME_SET
;
1586 htud
->request_body
.body_parsed
+= (header_end
- chunks_buffer
);
1591 SCLogDebug("header_start %p, header_end %p, form_end %p",
1592 header_start
, header_end
, form_end
);
1594 /* Search next boundary entry after the start of body */
1595 uint32_t cursizeread
= header_end
- chunks_buffer
;
1596 header_start
= Bs2bmSearch(header_end
+ 4,
1597 chunks_buffer_len
- (cursizeread
+ 4),
1598 boundary
, expected_boundary_len
);
1599 if (header_start
!= NULL
) {
1600 header_end
= Bs2bmSearch(header_end
+ 4,
1601 chunks_buffer_len
- (cursizeread
+ 4),
1602 (uint8_t *) "\r\n\r\n", 4);
1606 /* if we're parsing the multipart and we're not currently processing a
1607 * file, we move the body pointer forward. */
1608 if (form_end
== NULL
&& !(htud
->tsflags
& HTP_FILENAME_SET
) && header_start
== NULL
) {
1609 if (chunks_buffer_len
> expected_boundary_end_len
) {
1610 uint32_t move
= chunks_buffer_len
- expected_boundary_end_len
+ 1;
1612 htud
->request_body
.body_parsed
+= move
;
1613 SCLogDebug("form not ready, file not set, parsing non-file "
1614 "record: moved %u", move
);
1619 SCLogDebug("htud->request_body.body_parsed %"PRIu64
, htud
->request_body
.body_parsed
);
1624 * \brief Handle POST or PUT, no multipart body data
1626 static int HtpRequestBodyHandlePOSTorPUT(HtpState
*hstate
, HtpTxUserData
*htud
,
1627 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1631 /* see if we need to open the file */
1632 if (!(htud
->tsflags
& HTP_FILENAME_SET
))
1634 uint8_t *filename
= NULL
;
1635 size_t filename_len
= 0;
1638 if (tx
->parsed_uri
!= NULL
&& tx
->parsed_uri
->path
!= NULL
) {
1639 filename
= (uint8_t *)bstr_ptr(tx
->parsed_uri
->path
);
1640 filename_len
= bstr_len(tx
->parsed_uri
->path
);
1643 if (filename
!= NULL
) {
1644 result
= HTPFileOpen(hstate
, htud
, filename
, (uint32_t)filename_len
, data
, data_len
,
1645 HtpGetActiveRequestTxID(hstate
), STREAM_TOSERVER
);
1648 } else if (result
== -2) {
1649 htud
->tsflags
|= HTP_DONTSTORE
;
1651 FlagDetectStateNewFile(htud
, STREAM_TOSERVER
);
1652 htud
->tsflags
|= HTP_FILENAME_SET
;
1653 htud
->tsflags
&= ~HTP_DONTSTORE
;
1659 /* otherwise, just store the data */
1661 if (!(htud
->tsflags
& HTP_DONTSTORE
)) {
1662 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOSERVER
);
1665 } else if (result
== -2) {
1666 /* we know for sure we're not storing the file */
1667 htud
->tsflags
|= HTP_DONTSTORE
;
1677 static int HtpResponseBodyHandle(HtpState
*hstate
, HtpTxUserData
*htud
,
1678 htp_tx_t
*tx
, uint8_t *data
, uint32_t data_len
)
1684 /* see if we need to open the file
1685 * we check for tx->response_line in case of junk
1686 * interpreted as body before response line
1688 if (!(htud
->tcflags
& HTP_FILENAME_SET
) &&
1689 (tx
->response_line
!= NULL
|| tx
->is_protocol_0_9
))
1691 SCLogDebug("setting up file name");
1693 uint8_t *filename
= NULL
;
1694 size_t filename_len
= 0;
1696 /* try Content-Disposition header first */
1697 htp_header_t
*h
= (htp_header_t
*)htp_table_get_c(tx
->response_headers
,
1698 "Content-Disposition");
1699 if (h
!= NULL
&& bstr_len(h
->value
) > 0) {
1700 /* parse content-disposition */
1701 (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1702 (uint8_t *) bstr_ptr(h
->value
), bstr_len(h
->value
), &filename
, &filename_len
);
1705 /* fall back to name from the uri */
1706 if (filename
== NULL
) {
1708 if (tx
->parsed_uri
!= NULL
&& tx
->parsed_uri
->path
!= NULL
) {
1709 filename
= (uint8_t *)bstr_ptr(tx
->parsed_uri
->path
);
1710 filename_len
= bstr_len(tx
->parsed_uri
->path
);
1714 if (filename
!= NULL
) {
1715 // set range if present
1716 htp_header_t
*h_content_range
= htp_table_get_c(tx
->response_headers
, "content-range");
1717 if (h_content_range
!= NULL
) {
1718 result
= HTPFileOpenWithRange(hstate
, htud
, filename
, (uint32_t)filename_len
, data
,
1719 data_len
, HtpGetActiveResponseTxID(hstate
), h_content_range
->value
, htud
);
1721 result
= HTPFileOpen(hstate
, htud
, filename
, (uint32_t)filename_len
, data
, data_len
,
1722 HtpGetActiveResponseTxID(hstate
), STREAM_TOCLIENT
);
1724 SCLogDebug("result %d", result
);
1727 } else if (result
== -2) {
1728 htud
->tcflags
|= HTP_DONTSTORE
;
1730 FlagDetectStateNewFile(htud
, STREAM_TOCLIENT
);
1731 htud
->tcflags
|= HTP_FILENAME_SET
;
1732 htud
->tcflags
&= ~HTP_DONTSTORE
;
1736 else if (tx
->response_line
!= NULL
|| tx
->is_protocol_0_9
)
1738 /* otherwise, just store the data */
1740 if (!(htud
->tcflags
& HTP_DONTSTORE
)) {
1741 result
= HTPFileStoreChunk(hstate
, data
, data_len
, STREAM_TOCLIENT
);
1742 SCLogDebug("result %d", result
);
1745 } else if (result
== -2) {
1746 /* we know for sure we're not storing the file */
1747 htud
->tcflags
|= HTP_DONTSTORE
;
1752 htud
->response_body
.body_parsed
+= data_len
;
1759 * \brief Function callback to append chunks for Requests
1760 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1761 * \retval int HTP_OK if all goes well
1763 static int HTPCallbackRequestBodyData(htp_tx_data_t
*d
)
1767 if (!(SC_ATOMIC_GET(htp_config_flags
) & HTP_REQUIRE_REQUEST_BODY
))
1768 SCReturnInt(HTP_OK
);
1771 SCReturnInt(HTP_OK
);
1774 printf("HTPBODY START: \n");
1775 PrintRawDataFp(stdout
, (uint8_t *)d
->data
, d
->len
);
1776 printf("HTPBODY END: \n");
1779 HtpState
*hstate
= htp_connp_get_user_data(d
->tx
->connp
);
1780 if (hstate
== NULL
) {
1781 SCReturnInt(HTP_ERROR
);
1784 SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
1785 "%"PRIu32
"", hstate
, d
, d
->data
, (uint32_t)d
->len
);
1787 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(d
->tx
);
1788 if (tx_ud
== NULL
) {
1789 SCReturnInt(HTP_OK
);
1791 if (!tx_ud
->response_body_init
) {
1792 tx_ud
->response_body_init
= 1;
1794 if (d
->tx
->request_method_number
== HTP_M_POST
) {
1796 int r
= HtpRequestBodySetupMultipart(d
->tx
, tx_ud
);
1798 tx_ud
->request_body_type
= HTP_BODY_REQUEST_MULTIPART
;
1799 } else if (r
== 0) {
1800 tx_ud
->request_body_type
= HTP_BODY_REQUEST_POST
;
1801 SCLogDebug("not multipart");
1803 } else if (d
->tx
->request_method_number
== HTP_M_PUT
) {
1804 tx_ud
->request_body_type
= HTP_BODY_REQUEST_PUT
;
1808 /* see if we can get rid of htp body chunks */
1809 HtpBodyPrune(hstate
, &tx_ud
->request_body
, STREAM_TOSERVER
);
1811 SCLogDebug("tx_ud->request_body.content_len_so_far %"PRIu64
, tx_ud
->request_body
.content_len_so_far
);
1812 SCLogDebug("hstate->cfg->request.body_limit %u", hstate
->cfg
->request
.body_limit
);
1814 /* within limits, add the body chunk to the state. */
1815 if (AppLayerHtpCheckDepth(&hstate
->cfg
->request
, &tx_ud
->request_body
, tx_ud
->tsflags
)) {
1816 uint32_t stream_depth
= FileReassemblyDepth();
1817 uint32_t len
= AppLayerHtpComputeChunkLength(tx_ud
->request_body
.content_len_so_far
,
1818 hstate
->cfg
->request
.body_limit
,
1822 BUG_ON(len
> (uint32_t)d
->len
);
1824 HtpBodyAppendChunk(&hstate
->cfg
->request
, &tx_ud
->request_body
, d
->data
, len
);
1826 const uint8_t *chunks_buffer
= NULL
;
1827 uint32_t chunks_buffer_len
= 0;
1829 if (tx_ud
->request_body_type
== HTP_BODY_REQUEST_MULTIPART
) {
1830 /* multi-part body handling starts here */
1831 if (!(tx_ud
->tsflags
& HTP_BOUNDARY_SET
)) {
1835 HtpRequestBodyReassemble(tx_ud
, &chunks_buffer
, &chunks_buffer_len
);
1836 if (chunks_buffer
== NULL
) {
1840 printf("REASSCHUNK START: \n");
1841 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
1842 printf("REASSCHUNK END: \n");
1845 HtpRequestBodyHandleMultipart(hstate
, tx_ud
, d
->tx
, chunks_buffer
, chunks_buffer_len
);
1847 } else if (tx_ud
->request_body_type
== HTP_BODY_REQUEST_POST
||
1848 tx_ud
->request_body_type
== HTP_BODY_REQUEST_PUT
) {
1849 HtpRequestBodyHandlePOSTorPUT(hstate
, tx_ud
, d
->tx
, (uint8_t *)d
->data
, len
);
1853 if (tx_ud
->tsflags
& HTP_FILENAME_SET
) {
1854 SCLogDebug("closing file that was being stored");
1855 (void)HTPFileClose(hstate
, NULL
, 0, FILE_TRUNCATED
, STREAM_TOSERVER
);
1856 tx_ud
->tsflags
&= ~HTP_FILENAME_SET
;
1861 if (hstate
->conn
!= NULL
) {
1862 SCLogDebug("checking body size %"PRIu64
" against inspect limit %u (cur %"PRIu64
", last %"PRIu64
")",
1863 tx_ud
->request_body
.content_len_so_far
,
1864 hstate
->cfg
->request
.inspect_min_size
,
1865 (uint64_t)hstate
->conn
->in_data_counter
, hstate
->last_request_data_stamp
);
1867 /* if we reach the inspect_min_size we'll trigger inspection,
1868 * so make sure that raw stream is also inspected. Set the
1869 * data to be used to the amount of raw bytes we've seen to
1871 if (tx_ud
->request_body
.body_inspected
== 0 &&
1872 tx_ud
->request_body
.content_len_so_far
>= hstate
->cfg
->request
.inspect_min_size
) {
1873 if ((uint64_t)hstate
->conn
->in_data_counter
> hstate
->last_request_data_stamp
&&
1874 (uint64_t)hstate
->conn
->in_data_counter
- hstate
->last_request_data_stamp
< (uint64_t)UINT_MAX
)
1876 uint32_t x
= (uint32_t)((uint64_t)hstate
->conn
->in_data_counter
- hstate
->last_request_data_stamp
);
1878 /* body still in progress, but due to min inspect size we need to inspect now */
1879 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
, x
);
1880 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOSERVER
);
1882 /* after the start of the body, disable the depth logic */
1883 } else if (tx_ud
->request_body
.body_inspected
> 0) {
1884 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
, 0);
1887 SCReturnInt(HTP_OK
);
1891 * \brief Function callback to append chunks for Responses
1892 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1893 * \retval int HTP_OK if all goes well
1895 static int HTPCallbackResponseBodyData(htp_tx_data_t
*d
)
1899 if (!(SC_ATOMIC_GET(htp_config_flags
) & HTP_REQUIRE_RESPONSE_BODY
))
1900 SCReturnInt(HTP_OK
);
1903 SCReturnInt(HTP_OK
);
1905 HtpState
*hstate
= htp_connp_get_user_data(d
->tx
->connp
);
1906 if (hstate
== NULL
) {
1907 SCReturnInt(HTP_ERROR
);
1910 SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
1911 "%"PRIu32
"", hstate
, d
, d
->data
, (uint32_t)d
->len
);
1913 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(d
->tx
);
1914 if (tx_ud
== NULL
) {
1915 SCReturnInt(HTP_OK
);
1917 if (!tx_ud
->request_body_init
) {
1918 tx_ud
->request_body_init
= 1;
1921 /* see if we can get rid of htp body chunks */
1922 HtpBodyPrune(hstate
, &tx_ud
->response_body
, STREAM_TOCLIENT
);
1924 SCLogDebug("tx_ud->response_body.content_len_so_far %"PRIu64
, tx_ud
->response_body
.content_len_so_far
);
1925 SCLogDebug("hstate->cfg->response.body_limit %u", hstate
->cfg
->response
.body_limit
);
1927 /* within limits, add the body chunk to the state. */
1928 if (AppLayerHtpCheckDepth(&hstate
->cfg
->response
, &tx_ud
->response_body
, tx_ud
->tcflags
)) {
1929 uint32_t stream_depth
= FileReassemblyDepth();
1930 uint32_t len
= AppLayerHtpComputeChunkLength(tx_ud
->response_body
.content_len_so_far
,
1931 hstate
->cfg
->response
.body_limit
,
1935 BUG_ON(len
> (uint32_t)d
->len
);
1937 HtpBodyAppendChunk(&hstate
->cfg
->response
, &tx_ud
->response_body
, d
->data
, len
);
1939 HtpResponseBodyHandle(hstate
, tx_ud
, d
->tx
, (uint8_t *)d
->data
, len
);
1941 if (tx_ud
->tcflags
& HTP_FILENAME_SET
) {
1942 SCLogDebug("closing file that was being stored");
1943 (void)HTPFileClose(hstate
, NULL
, 0, FILE_TRUNCATED
, STREAM_TOCLIENT
);
1944 tx_ud
->tcflags
&= ~HTP_FILENAME_SET
;
1948 if (hstate
->conn
!= NULL
) {
1949 SCLogDebug("checking body size %"PRIu64
" against inspect limit %u (cur %"PRIu64
", last %"PRIu64
")",
1950 tx_ud
->response_body
.content_len_so_far
,
1951 hstate
->cfg
->response
.inspect_min_size
,
1952 (uint64_t)hstate
->conn
->in_data_counter
, hstate
->last_response_data_stamp
);
1953 /* if we reach the inspect_min_size we'll trigger inspection,
1954 * so make sure that raw stream is also inspected. Set the
1955 * data to be used to the amount of raw bytes we've seen to
1957 if (tx_ud
->response_body
.body_inspected
== 0 &&
1958 tx_ud
->response_body
.content_len_so_far
>= hstate
->cfg
->response
.inspect_min_size
) {
1959 if ((uint64_t)hstate
->conn
->out_data_counter
> hstate
->last_response_data_stamp
&&
1960 (uint64_t)hstate
->conn
->out_data_counter
- hstate
->last_response_data_stamp
< (uint64_t)UINT_MAX
)
1962 uint32_t x
= (uint32_t)((uint64_t)hstate
->conn
->out_data_counter
- hstate
->last_response_data_stamp
);
1964 /* body still in progress, but due to min inspect size we need to inspect now */
1965 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
, x
);
1966 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOCLIENT
);
1968 /* after the start of the body, disable the depth logic */
1969 } else if (tx_ud
->response_body
.body_inspected
> 0) {
1970 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
, 0);
1973 SCReturnInt(HTP_OK
);
1977 * \brief Print the stats of the HTTP requests
1979 void HTPAtExitPrintStats(void)
1983 SCMutexLock(&htp_state_mem_lock
);
1984 SCLogDebug("http_state_memcnt %"PRIu64
", http_state_memuse %"PRIu64
"",
1985 htp_state_memcnt
, htp_state_memuse
);
1986 SCMutexUnlock(&htp_state_mem_lock
);
1991 /** \brief Clears the HTTP server configuration memory used by HTP library */
1992 void HTPFreeConfig(void)
1996 if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
1997 !AppLayerParserConfParserEnabled("tcp", "http"))
2002 HTPCfgRec
*nextrec
= cfglist
.next
;
2003 SCRadixReleaseRadixTree(cfgtree
);
2005 htp_config_destroy(cfglist
.cfg
);
2006 while (nextrec
!= NULL
) {
2007 HTPCfgRec
*htprec
= nextrec
;
2008 nextrec
= nextrec
->next
;
2010 htp_config_destroy(htprec
->cfg
);
2016 static int HTPCallbackRequestHasTrailer(htp_tx_t
*tx
)
2018 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
2020 htud
->request_has_trailers
= 1;
2025 static int HTPCallbackResponseHasTrailer(htp_tx_t
*tx
)
2027 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
2029 htud
->response_has_trailers
= 1;
2035 * \brief called at start of request
2036 * Set min inspect size.
2038 static int HTPCallbackRequestStart(htp_tx_t
*tx
)
2040 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2041 if (hstate
== NULL
) {
2042 SCReturnInt(HTP_ERROR
);
2046 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
,
2047 hstate
->cfg
->request
.inspect_min_size
);
2049 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2050 if (tx_ud
== NULL
) {
2051 tx_ud
= HTPCalloc(1, sizeof(HtpTxUserData
));
2052 if (unlikely(tx_ud
== NULL
)) {
2053 SCReturnInt(HTP_OK
);
2055 htp_tx_set_user_data(tx
, tx_ud
);
2057 SCReturnInt(HTP_OK
);
2061 * \brief called at start of response
2062 * Set min inspect size.
2064 static int HTPCallbackResponseStart(htp_tx_t
*tx
)
2066 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2067 if (hstate
== NULL
) {
2068 SCReturnInt(HTP_ERROR
);
2072 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOCLIENT
,
2073 hstate
->cfg
->response
.inspect_min_size
);
2075 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2076 if (tx_ud
== NULL
) {
2077 tx_ud
= HTPCalloc(1, sizeof(HtpTxUserData
));
2078 if (unlikely(tx_ud
== NULL
)) {
2079 SCReturnInt(HTP_OK
);
2081 htp_tx_set_user_data(tx
, tx_ud
);
2083 SCReturnInt(HTP_OK
);
2088 * \brief callback for request to store the recent incoming request
2089 in to the recent_in_tx for the given htp state
2090 * \param connp pointer to the current connection parser which has the htp
2091 * state in it as user data
2093 static int HTPCallbackRequest(htp_tx_t
*tx
)
2098 SCReturnInt(HTP_ERROR
);
2101 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2102 if (hstate
== NULL
) {
2103 SCReturnInt(HTP_ERROR
);
2106 SCLogDebug("transaction_cnt %"PRIu64
", list_size %"PRIu64
,
2107 hstate
->transaction_cnt
, HTPStateGetTxCnt(hstate
));
2109 SCLogDebug("HTTP request completed");
2111 HTPErrorCheckTxRequestFlags(hstate
, tx
);
2113 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
2115 if (htud
->tsflags
& HTP_FILENAME_SET
) {
2116 SCLogDebug("closing file that was being stored");
2117 (void)HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOSERVER
);
2118 htud
->tsflags
&= ~HTP_FILENAME_SET
;
2119 StreamTcpReassemblySetMinInspectDepth(hstate
->f
->protoctx
, STREAM_TOSERVER
,
2120 (uint32_t)hstate
->conn
->in_data_counter
);
2124 hstate
->last_request_data_stamp
= (uint64_t)hstate
->conn
->in_data_counter
;
2125 /* request done, do raw reassembly now to inspect state and stream
2126 * at the same time. */
2127 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOSERVER
);
2128 SCReturnInt(HTP_OK
);
2132 * \brief callback for response to remove the recent received requests
2133 from the recent_in_tx for the given htp state
2134 * \param connp pointer to the current connection parser which has the htp
2135 * state in it as user data
2137 static int HTPCallbackResponse(htp_tx_t
*tx
)
2141 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2142 if (hstate
== NULL
) {
2143 SCReturnInt(HTP_ERROR
);
2146 /* we have one whole transaction now */
2147 hstate
->transaction_cnt
++;
2149 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2151 if (htud
->tcflags
& HTP_FILENAME_SET
) {
2152 SCLogDebug("closing file that was being stored");
2153 (void)HTPFileClose(hstate
, NULL
, 0, 0, STREAM_TOCLIENT
);
2154 htud
->tcflags
&= ~HTP_FILENAME_SET
;
2158 /* response done, do raw reassembly now to inspect state and stream
2159 * at the same time. */
2160 AppLayerParserTriggerRawStreamReassembly(hstate
->f
, STREAM_TOCLIENT
);
2162 /* handle HTTP CONNECT */
2163 if (tx
->request_method_number
== HTP_M_CONNECT
) {
2164 /* any 2XX status response implies that the connection will become
2165 a tunnel immediately after this packet (RFC 7230, 3.3.3). */
2166 if ((tx
->response_status_number
>= 200) &&
2167 (tx
->response_status_number
< 300) &&
2168 (hstate
->transaction_cnt
== 1)) {
2170 if (tx
->request_port_number
!= -1) {
2171 dp
= (uint16_t)tx
->request_port_number
;
2173 // both ALPROTO_HTTP1 and ALPROTO_TLS are normal options
2174 AppLayerRequestProtocolChange(hstate
->f
, dp
, ALPROTO_UNKNOWN
);
2175 tx
->request_progress
= HTP_REQUEST_COMPLETE
;
2176 tx
->response_progress
= HTP_RESPONSE_COMPLETE
;
2180 hstate
->last_response_data_stamp
= (uint64_t)hstate
->conn
->out_data_counter
;
2181 SCReturnInt(HTP_OK
);
2184 static int HTPCallbackRequestLine(htp_tx_t
*tx
)
2186 HtpTxUserData
*tx_ud
;
2187 bstr
*request_uri_normalized
;
2188 HtpState
*hstate
= htp_connp_get_user_data(tx
->connp
);
2189 const HTPCfgRec
*cfg
= hstate
->cfg
;
2191 request_uri_normalized
= SCHTPGenerateNormalizedUri(tx
, tx
->parsed_uri
, cfg
->uri_include_all
);
2192 if (request_uri_normalized
== NULL
)
2195 tx_ud
= htp_tx_get_user_data(tx
);
2196 if (likely(tx_ud
== NULL
)) {
2197 tx_ud
= HTPMalloc(sizeof(*tx_ud
));
2198 if (unlikely(tx_ud
== NULL
)) {
2199 bstr_free(request_uri_normalized
);
2202 memset(tx_ud
, 0, sizeof(*tx_ud
));
2203 htp_tx_set_user_data(tx
, tx_ud
);
2205 if (unlikely(tx_ud
->request_uri_normalized
!= NULL
))
2206 bstr_free(tx_ud
->request_uri_normalized
);
2207 tx_ud
->request_uri_normalized
= request_uri_normalized
;
2210 HTPErrorCheckTxRequestFlags(hstate
, tx
);
2215 static int HTPCallbackDoubleDecodeUriPart(htp_tx_t
*tx
, bstr
*part
)
2221 size_t prevlen
= bstr_len(part
);
2222 htp_status_t res
= htp_urldecode_inplace(tx
->cfg
, HTP_DECODER_URLENCODED
, part
, &flags
);
2223 // shorter string means that uri was encoded
2224 if (res
== HTP_OK
&& prevlen
> bstr_len(part
)) {
2225 HtpTxUserData
*htud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
2228 HtpState
*s
= htp_connp_get_user_data(tx
->connp
);
2231 HTPSetEvent(s
, htud
, STREAM_TOSERVER
,
2232 HTTP_DECODER_EVENT_DOUBLE_ENCODED_URI
);
2238 static int HTPCallbackDoubleDecodeQuery(htp_tx_t
*tx
)
2240 if (tx
->parsed_uri
== NULL
)
2243 return HTPCallbackDoubleDecodeUriPart(tx
, tx
->parsed_uri
->query
);
2246 static int HTPCallbackDoubleDecodePath(htp_tx_t
*tx
)
2248 if (tx
->parsed_uri
== NULL
)
2251 return HTPCallbackDoubleDecodeUriPart(tx
, tx
->parsed_uri
->path
);
2254 static int HTPCallbackRequestHeaderData(htp_tx_data_t
*tx_data
)
2257 if (tx_data
->len
== 0 || tx_data
->tx
== NULL
)
2260 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx_data
->tx
);
2261 if (tx_ud
== NULL
) {
2264 ptmp
= HTPRealloc(tx_ud
->request_headers_raw
,
2265 tx_ud
->request_headers_raw_len
,
2266 tx_ud
->request_headers_raw_len
+ tx_data
->len
);
2270 tx_ud
->request_headers_raw
= ptmp
;
2272 memcpy(tx_ud
->request_headers_raw
+ tx_ud
->request_headers_raw_len
,
2273 tx_data
->data
, tx_data
->len
);
2274 tx_ud
->request_headers_raw_len
+= tx_data
->len
;
2276 if (tx_data
->tx
&& tx_data
->tx
->flags
) {
2277 HtpState
*hstate
= htp_connp_get_user_data(tx_data
->tx
->connp
);
2278 HTPErrorCheckTxRequestFlags(hstate
, tx_data
->tx
);
2283 static int HTPCallbackResponseHeaderData(htp_tx_data_t
*tx_data
)
2286 if (tx_data
->len
== 0 || tx_data
->tx
== NULL
)
2289 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx_data
->tx
);
2290 if (tx_ud
== NULL
) {
2293 ptmp
= HTPRealloc(tx_ud
->response_headers_raw
,
2294 tx_ud
->response_headers_raw_len
,
2295 tx_ud
->response_headers_raw_len
+ tx_data
->len
);
2299 tx_ud
->response_headers_raw
= ptmp
;
2301 memcpy(tx_ud
->response_headers_raw
+ tx_ud
->response_headers_raw_len
,
2302 tx_data
->data
, tx_data
->len
);
2303 tx_ud
->response_headers_raw_len
+= tx_data
->len
;
2309 * We have a similar set function called HTPConfigSetDefaultsPhase1.
2311 static void HTPConfigSetDefaultsPhase1(HTPCfgRec
*cfg_prec
)
2313 cfg_prec
->uri_include_all
= FALSE
;
2314 cfg_prec
->request
.body_limit
= HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT
;
2315 cfg_prec
->response
.body_limit
= HTP_CONFIG_DEFAULT_RESPONSE_BODY_LIMIT
;
2316 cfg_prec
->request
.inspect_min_size
= HTP_CONFIG_DEFAULT_REQUEST_INSPECT_MIN_SIZE
;
2317 cfg_prec
->request
.inspect_window
= HTP_CONFIG_DEFAULT_REQUEST_INSPECT_WINDOW
;
2318 cfg_prec
->response
.inspect_min_size
= HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_MIN_SIZE
;
2319 cfg_prec
->response
.inspect_window
= HTP_CONFIG_DEFAULT_RESPONSE_INSPECT_WINDOW
;
2321 if (!g_disable_randomness
) {
2322 cfg_prec
->randomize
= HTP_CONFIG_DEFAULT_RANDOMIZE
;
2324 cfg_prec
->randomize
= 0;
2326 cfg_prec
->randomize_range
= HTP_CONFIG_DEFAULT_RANDOMIZE_RANGE
;
2328 htp_config_register_request_header_data(cfg_prec
->cfg
, HTPCallbackRequestHeaderData
);
2329 htp_config_register_request_trailer_data(cfg_prec
->cfg
, HTPCallbackRequestHeaderData
);
2330 htp_config_register_response_header_data(cfg_prec
->cfg
, HTPCallbackResponseHeaderData
);
2331 htp_config_register_response_trailer_data(cfg_prec
->cfg
, HTPCallbackResponseHeaderData
);
2333 htp_config_register_request_trailer(cfg_prec
->cfg
, HTPCallbackRequestHasTrailer
);
2334 htp_config_register_response_trailer(cfg_prec
->cfg
, HTPCallbackResponseHasTrailer
);
2336 htp_config_register_request_body_data(cfg_prec
->cfg
, HTPCallbackRequestBodyData
);
2337 htp_config_register_response_body_data(cfg_prec
->cfg
, HTPCallbackResponseBodyData
);
2339 htp_config_register_request_start(cfg_prec
->cfg
, HTPCallbackRequestStart
);
2340 htp_config_register_request_complete(cfg_prec
->cfg
, HTPCallbackRequest
);
2342 htp_config_register_response_start(cfg_prec
->cfg
, HTPCallbackResponseStart
);
2343 htp_config_register_response_complete(cfg_prec
->cfg
, HTPCallbackResponse
);
2345 htp_config_set_parse_request_cookies(cfg_prec
->cfg
, 0);
2347 /* don't convert + to space by default */
2348 htp_config_set_plusspace_decode(cfg_prec
->cfg
, HTP_DECODER_URLENCODED
, 0);
2349 // enables request decompression
2350 htp_config_set_request_decompression(cfg_prec
->cfg
, 1);
2351 #ifdef HAVE_HTP_CONFIG_SET_LZMA_LAYERS
2352 // disable by default
2353 htp_config_set_lzma_layers(cfg_prec
->cfg
, HTP_CONFIG_DEFAULT_LZMA_LAYERS
);
2355 #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT
2356 htp_config_set_lzma_memlimit(cfg_prec
->cfg
,
2357 HTP_CONFIG_DEFAULT_LZMA_MEMLIMIT
);
2359 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT
2360 htp_config_set_compression_bomb_limit(cfg_prec
->cfg
,
2361 HTP_CONFIG_DEFAULT_COMPRESSION_BOMB_LIMIT
);
2363 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT
2364 htp_config_set_compression_time_limit(cfg_prec
->cfg
, HTP_CONFIG_DEFAULT_COMPRESSION_TIME_LIMIT
);
2366 /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set
2367 * only the hard limit. So we set both here to the (current) htp defaults.
2368 * The reason we do this is that if the user sets the hard limit in the
2369 * config, we have to set the soft limit as well. If libhtp starts using
2370 * the soft limit in the future, we at least make sure we control what
2372 htp_config_set_field_limits(cfg_prec
->cfg
,
2373 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT
,
2374 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_HARD
);
2378 /* hack: htp random range code expects random values in range of 0-RAND_MAX,
2379 * but we can get both <0 and >RAND_MAX values from RandomGet
2381 static int RandomGetWrap(void)
2387 } while(r
>= ULONG_MAX
- (ULONG_MAX
% RAND_MAX
));
2389 return r
% RAND_MAX
;
2393 * We have this splitup so that in case double decoding has been enabled
2394 * for query and path, they would be called first on the callback queue,
2395 * before the callback set by Phase2() is called. We need this, since
2396 * the callback in Phase2() generates the normalized uri which utilizes
2397 * the query and path. */
2398 static void HTPConfigSetDefaultsPhase2(const char *name
, HTPCfgRec
*cfg_prec
)
2400 /* randomize inspection size if needed */
2401 if (cfg_prec
->randomize
) {
2402 int rdrange
= cfg_prec
->randomize_range
;
2404 long int r
= RandomGetWrap();
2405 cfg_prec
->request
.inspect_min_size
+=
2406 (int) (cfg_prec
->request
.inspect_min_size
*
2407 (r
* 1.0 / RAND_MAX
- 0.5) * rdrange
/ 100);
2409 r
= RandomGetWrap();
2410 cfg_prec
->request
.inspect_window
+=
2411 (int) (cfg_prec
->request
.inspect_window
*
2412 (r
* 1.0 / RAND_MAX
- 0.5) * rdrange
/ 100);
2413 SCLogConfig("'%s' server has 'request-body-minimal-inspect-size' set to"
2414 " %d and 'request-body-inspect-window' set to %d after"
2417 cfg_prec
->request
.inspect_min_size
,
2418 cfg_prec
->request
.inspect_window
);
2421 r
= RandomGetWrap();
2422 cfg_prec
->response
.inspect_min_size
+=
2423 (int) (cfg_prec
->response
.inspect_min_size
*
2424 (r
* 1.0 / RAND_MAX
- 0.5) * rdrange
/ 100);
2426 r
= RandomGetWrap();
2427 cfg_prec
->response
.inspect_window
+=
2428 (int) (cfg_prec
->response
.inspect_window
*
2429 (r
* 1.0 / RAND_MAX
- 0.5) * rdrange
/ 100);
2431 SCLogConfig("'%s' server has 'response-body-minimal-inspect-size' set to"
2432 " %d and 'response-body-inspect-window' set to %d after"
2435 cfg_prec
->response
.inspect_min_size
,
2436 cfg_prec
->response
.inspect_window
);
2439 htp_config_register_request_line(cfg_prec
->cfg
, HTPCallbackRequestLine
);
2441 cfg_prec
->request
.sbcfg
.flags
= 0;
2442 cfg_prec
->request
.sbcfg
.buf_size
= cfg_prec
->request
.inspect_window
?
2443 cfg_prec
->request
.inspect_window
: 256;
2444 cfg_prec
->request
.sbcfg
.buf_slide
= 0;
2445 cfg_prec
->request
.sbcfg
.Malloc
= HTPMalloc
;
2446 cfg_prec
->request
.sbcfg
.Calloc
= HTPCalloc
;
2447 cfg_prec
->request
.sbcfg
.Realloc
= HTPRealloc
;
2448 cfg_prec
->request
.sbcfg
.Free
= HTPFree
;
2450 cfg_prec
->response
.sbcfg
.flags
= 0;
2451 cfg_prec
->response
.sbcfg
.buf_size
= cfg_prec
->response
.inspect_window
?
2452 cfg_prec
->response
.inspect_window
: 256;
2453 cfg_prec
->response
.sbcfg
.buf_slide
= 0;
2454 cfg_prec
->response
.sbcfg
.Malloc
= HTPMalloc
;
2455 cfg_prec
->response
.sbcfg
.Calloc
= HTPCalloc
;
2456 cfg_prec
->response
.sbcfg
.Realloc
= HTPRealloc
;
2457 cfg_prec
->response
.sbcfg
.Free
= HTPFree
;
2461 static void HTPConfigParseParameters(HTPCfgRec
*cfg_prec
, ConfNode
*s
,
2464 if (cfg_prec
== NULL
|| s
== NULL
|| tree
== NULL
)
2469 /* Default Parameters */
2470 TAILQ_FOREACH(p
, &s
->head
, next
) {
2472 if (strcasecmp("address", p
->name
) == 0) {
2475 TAILQ_FOREACH(pval
, &p
->head
, next
) {
2476 SCLogDebug("LIBHTP server %s: %s=%s", s
->name
, p
->name
,
2480 if (strchr(pval
->val
, ':') != NULL
) {
2481 SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
2482 s
->name
, pval
->val
, cfg_prec
->cfg
);
2483 if (SCRadixAddKeyIPV6String(pval
->val
, tree
, cfg_prec
) == NULL
) {
2484 SCLogWarning(SC_ERR_INVALID_VALUE
, "LIBHTP failed to "
2485 "add ipv6 server %s, ignoring", pval
->val
);
2488 SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
2489 s
->name
, pval
->val
, cfg_prec
->cfg
);
2490 if (SCRadixAddKeyIPV4String(pval
->val
, tree
, cfg_prec
) == NULL
) {
2491 SCLogWarning(SC_ERR_INVALID_VALUE
, "LIBHTP failed "
2492 "to add ipv4 server %s, ignoring",
2495 } /* else - if (strchr(pval->val, ':') != NULL) */
2496 } /* TAILQ_FOREACH(pval, &p->head, next) */
2498 } else if (strcasecmp("personality", p
->name
) == 0) {
2500 int personality
= HTPLookupPersonality(p
->val
);
2501 SCLogDebug("LIBHTP default: %s = %s", p
->name
, p
->val
);
2502 SCLogDebug("LIBHTP default: %s = %s", p
->name
, p
->val
);
2504 if (personality
>= 0) {
2505 SCLogDebug("LIBHTP default: %s=%s (%d)", p
->name
, p
->val
,
2507 if (htp_config_set_server_personality(cfg_prec
->cfg
, personality
) == HTP_ERROR
){
2508 SCLogWarning(SC_ERR_INVALID_VALUE
, "LIBHTP Failed adding "
2509 "personality \"%s\", ignoring", p
->val
);
2511 SCLogDebug("LIBHTP personality set to %s",
2512 HTPLookupPersonalityString(personality
));
2515 /* The IDS personality by default converts the path (and due to
2516 * our query string callback also the query string) to lowercase.
2517 * Signatures do not expect this, so override it. */
2518 htp_config_set_convert_lowercase(cfg_prec
->cfg
, HTP_DECODER_URL_PATH
, 0);
2520 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "LIBHTP Unknown personality "
2521 "\"%s\", ignoring", p
->val
);
2525 } else if (strcasecmp("request-body-limit", p
->name
) == 0 ||
2526 strcasecmp("request_body_limit", p
->name
) == 0) {
2527 if (ParseSizeStringU32(p
->val
, &cfg_prec
->request
.body_limit
) < 0) {
2528 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing request-body-limit "
2529 "from conf file - %s. Killing engine", p
->val
);
2533 } else if (strcasecmp("response-body-limit", p
->name
) == 0) {
2534 if (ParseSizeStringU32(p
->val
, &cfg_prec
->response
.body_limit
) < 0) {
2535 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-limit "
2536 "from conf file - %s. Killing engine", p
->val
);
2540 } else if (strcasecmp("request-body-minimal-inspect-size", p
->name
) == 0) {
2541 if (ParseSizeStringU32(p
->val
, &cfg_prec
->request
.inspect_min_size
) < 0) {
2542 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing request-body-minimal-inspect-size "
2543 "from conf file - %s. Killing engine", p
->val
);
2547 } else if (strcasecmp("request-body-inspect-window", p
->name
) == 0) {
2548 if (ParseSizeStringU32(p
->val
, &cfg_prec
->request
.inspect_window
) < 0) {
2549 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing request-body-inspect-window "
2550 "from conf file - %s. Killing engine", p
->val
);
2554 } else if (strcasecmp("double-decode-query", p
->name
) == 0) {
2555 if (ConfValIsTrue(p
->val
)) {
2556 htp_config_register_request_line(cfg_prec
->cfg
,
2557 HTPCallbackDoubleDecodeQuery
);
2560 } else if (strcasecmp("double-decode-path", p
->name
) == 0) {
2561 if (ConfValIsTrue(p
->val
)) {
2562 htp_config_register_request_line(cfg_prec
->cfg
,
2563 HTPCallbackDoubleDecodePath
);
2566 } else if (strcasecmp("response-body-minimal-inspect-size", p
->name
) == 0) {
2567 if (ParseSizeStringU32(p
->val
, &cfg_prec
->response
.inspect_min_size
) < 0) {
2568 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-minimal-inspect-size "
2569 "from conf file - %s. Killing engine", p
->val
);
2573 } else if (strcasecmp("response-body-inspect-window", p
->name
) == 0) {
2574 if (ParseSizeStringU32(p
->val
, &cfg_prec
->response
.inspect_window
) < 0) {
2575 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-inspect-window "
2576 "from conf file - %s. Killing engine", p
->val
);
2580 } else if (strcasecmp("response-body-decompress-layer-limit", p
->name
) == 0) {
2582 if (ParseSizeStringU32(p
->val
, &value
) < 0) {
2583 SCLogError(SC_ERR_SIZE_PARSE
, "Error parsing response-body-inspect-window "
2584 "from conf file - %s. Killing engine", p
->val
);
2587 #ifdef HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT
2588 htp_config_set_response_decompression_layer_limit(cfg_prec
->cfg
, value
);
2590 SCLogWarning(SC_WARN_OUTDATED_LIBHTP
, "can't set response-body-decompress-layer-limit "
2591 "to %u, libhtp version too old", value
);
2593 } else if (strcasecmp("path-convert-backslash-separators", p
->name
) == 0) {
2594 htp_config_set_backslash_convert_slashes(cfg_prec
->cfg
,
2595 HTP_DECODER_URL_PATH
,
2596 ConfValIsTrue(p
->val
));
2597 } else if (strcasecmp("path-bestfit-replacement-char", p
->name
) == 0) {
2598 if (strlen(p
->val
) == 1) {
2599 htp_config_set_bestfit_replacement_byte(cfg_prec
->cfg
,
2600 HTP_DECODER_URL_PATH
,
2603 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
, "Invalid entry "
2604 "for libhtp param path-bestfit-replacement-char");
2606 } else if (strcasecmp("path-convert-lowercase", p
->name
) == 0) {
2607 htp_config_set_convert_lowercase(cfg_prec
->cfg
,
2608 HTP_DECODER_URL_PATH
,
2609 ConfValIsTrue(p
->val
));
2610 } else if (strcasecmp("path-nul-encoded-terminates", p
->name
) == 0) {
2611 htp_config_set_nul_encoded_terminates(cfg_prec
->cfg
,
2612 HTP_DECODER_URL_PATH
,
2613 ConfValIsTrue(p
->val
));
2614 } else if (strcasecmp("path-nul-raw-terminates", p
->name
) == 0) {
2615 htp_config_set_nul_raw_terminates(cfg_prec
->cfg
,
2616 HTP_DECODER_URL_PATH
,
2617 ConfValIsTrue(p
->val
));
2618 } else if (strcasecmp("path-separators-compress", p
->name
) == 0) {
2619 htp_config_set_path_separators_compress(cfg_prec
->cfg
,
2620 HTP_DECODER_URL_PATH
,
2621 ConfValIsTrue(p
->val
));
2622 } else if (strcasecmp("path-separators-decode", p
->name
) == 0) {
2623 htp_config_set_path_separators_decode(cfg_prec
->cfg
,
2624 HTP_DECODER_URL_PATH
,
2625 ConfValIsTrue(p
->val
));
2626 } else if (strcasecmp("path-u-encoding-decode", p
->name
) == 0) {
2627 htp_config_set_u_encoding_decode(cfg_prec
->cfg
,
2628 HTP_DECODER_URL_PATH
,
2629 ConfValIsTrue(p
->val
));
2630 } else if (strcasecmp("path-url-encoding-invalid-handling", p
->name
) == 0) {
2631 enum htp_url_encoding_handling_t handling
;
2632 if (strcasecmp(p
->val
, "preserve_percent") == 0) {
2633 handling
= HTP_URL_DECODE_PRESERVE_PERCENT
;
2634 } else if (strcasecmp(p
->val
, "remove_percent") == 0) {
2635 handling
= HTP_URL_DECODE_REMOVE_PERCENT
;
2636 } else if (strcasecmp(p
->val
, "decode_invalid") == 0) {
2637 handling
= HTP_URL_DECODE_PROCESS_INVALID
;
2639 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
, "Invalid entry "
2640 "for libhtp param path-url-encoding-invalid-handling");
2643 htp_config_set_url_encoding_invalid_handling(cfg_prec
->cfg
,
2644 HTP_DECODER_URL_PATH
,
2646 } else if (strcasecmp("path-utf8-convert-bestfit", p
->name
) == 0) {
2647 htp_config_set_utf8_convert_bestfit(cfg_prec
->cfg
,
2648 HTP_DECODER_URL_PATH
,
2649 ConfValIsTrue(p
->val
));
2650 } else if (strcasecmp("uri-include-all", p
->name
) == 0) {
2651 cfg_prec
->uri_include_all
= ConfValIsTrue(p
->val
);
2652 SCLogDebug("uri-include-all %s",
2653 cfg_prec
->uri_include_all
? "enabled" : "disabled");
2654 } else if (strcasecmp("query-plusspace-decode", p
->name
) == 0) {
2655 htp_config_set_plusspace_decode(cfg_prec
->cfg
,
2656 HTP_DECODER_URLENCODED
,
2657 ConfValIsTrue(p
->val
));
2658 } else if (strcasecmp("meta-field-limit", p
->name
) == 0) {
2660 if (ParseSizeStringU32(p
->val
, &limit
) < 0) {
2661 SCLogError(SC_ERR_SIZE_PARSE
, "Error meta-field-limit "
2662 "from conf file - %s. Killing engine", p
->val
);
2666 FatalError(SC_ERR_FATAL
, "Error meta-field-limit "
2667 "from conf file cannot be 0. Killing engine");
2669 /* set default soft-limit with our new hard limit */
2670 htp_config_set_field_limits(cfg_prec
->cfg
,
2671 (size_t)HTP_CONFIG_DEFAULT_FIELD_LIMIT_SOFT
,
2673 #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT
2674 } else if (strcasecmp("lzma-memlimit", p
->name
) == 0) {
2676 if (ParseSizeStringU32(p
->val
, &limit
) < 0) {
2677 FatalError(SC_ERR_SIZE_PARSE
, "failed to parse 'lzma-memlimit' "
2678 "from conf file - %s.", p
->val
);
2681 FatalError(SC_ERR_SIZE_PARSE
, "'lzma-memlimit' "
2682 "from conf file cannot be 0.");
2684 /* set default soft-limit with our new hard limit */
2685 SCLogConfig("Setting HTTP LZMA memory limit to %"PRIu32
" bytes", limit
);
2686 htp_config_set_lzma_memlimit(cfg_prec
->cfg
, (size_t)limit
);
2688 #ifdef HAVE_HTP_CONFIG_SET_LZMA_LAYERS
2689 } else if (strcasecmp("lzma-enabled", p
->name
) == 0) {
2690 if (ConfValIsTrue(p
->val
)) {
2691 htp_config_set_lzma_layers(cfg_prec
->cfg
, 1);
2692 } else if (!ConfValIsFalse(p
->val
)) {
2694 if (StringParseInt8(&limit
, 10, 0, (const char *)p
->val
) < 0) {
2695 FatalError(SC_ERR_SIZE_PARSE
,
2696 "failed to parse 'lzma-enabled' "
2697 "from conf file - %s.",
2700 SCLogConfig("Setting HTTP LZMA decompression layers to %" PRIu32
"", (int)limit
);
2701 htp_config_set_lzma_layers(cfg_prec
->cfg
, limit
);
2704 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT
2705 } else if (strcasecmp("compression-bomb-limit", p
->name
) == 0) {
2707 if (ParseSizeStringU32(p
->val
, &limit
) < 0) {
2708 FatalError(SC_ERR_SIZE_PARSE
, "failed to parse 'compression-bomb-limit' "
2709 "from conf file - %s.", p
->val
);
2712 FatalError(SC_ERR_SIZE_PARSE
, "'compression-bomb-limit' "
2713 "from conf file cannot be 0.");
2715 /* set default soft-limit with our new hard limit */
2716 SCLogConfig("Setting HTTP compression bomb limit to %"PRIu32
" bytes", limit
);
2717 htp_config_set_compression_bomb_limit(cfg_prec
->cfg
, (size_t)limit
);
2719 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_TIME_LIMIT
2720 } else if (strcasecmp("decompression-time-limit", p
->name
) == 0) {
2722 // between 1 usec and 1 second
2723 if (StringParseU32RangeCheck(&limit
, 10, 0, p
->val
, 1, 1000000) < 0) {
2724 FatalError(SC_ERR_SIZE_PARSE
,
2725 "failed to parse 'decompression-time-limit' "
2726 "from conf file - %s.",
2729 SCLogConfig("Setting HTTP decompression time limit to %" PRIu32
" usec", limit
);
2730 htp_config_set_compression_time_limit(cfg_prec
->cfg
, (size_t)limit
);
2732 } else if (strcasecmp("randomize-inspection-sizes", p
->name
) == 0) {
2733 if (!g_disable_randomness
) {
2734 cfg_prec
->randomize
= ConfValIsTrue(p
->val
);
2736 } else if (strcasecmp("randomize-inspection-range", p
->name
) == 0) {
2738 if (StringParseU32RangeCheck(&range
, 10, 0,
2739 (const char *)p
->val
, 0, 100) < 0) {
2740 SCLogError(SC_ERR_INVALID_VALUE
, "Invalid value for randomize"
2741 "-inspection-range setting from conf file - \"%s\"."
2742 " It should be a valid integer less than or equal to 100."
2747 cfg_prec
->randomize_range
= range
;
2748 } else if (strcasecmp("http-body-inline", p
->name
) == 0) {
2749 if (ConfValIsTrue(p
->val
)) {
2750 cfg_prec
->http_body_inline
= 1;
2751 } else if (ConfValIsFalse(p
->val
)) {
2752 cfg_prec
->http_body_inline
= 0;
2754 if (strcmp("auto", p
->val
) != 0) {
2755 WarnInvalidConfEntry("http_body_inline", "%s", "auto");
2757 if (EngineModeIsIPS()) {
2758 cfg_prec
->http_body_inline
= 1;
2760 cfg_prec
->http_body_inline
= 0;
2763 } else if (strcasecmp("swf-decompression", p
->name
) == 0) {
2766 TAILQ_FOREACH(pval
, &p
->head
, next
) {
2767 if (strcasecmp("enabled", pval
->name
) == 0) {
2768 if (ConfValIsTrue(pval
->val
)) {
2769 cfg_prec
->swf_decompression_enabled
= 1;
2770 } else if (ConfValIsFalse(pval
->val
)) {
2771 cfg_prec
->swf_decompression_enabled
= 0;
2773 WarnInvalidConfEntry("swf-decompression.enabled", "%s", "no");
2775 } else if (strcasecmp("type", pval
->name
) == 0) {
2776 if (strcasecmp("no", pval
->val
) == 0) {
2777 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_NONE
;
2778 } else if (strcasecmp("deflate", pval
->val
) == 0) {
2779 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_ZLIB
;
2780 } else if (strcasecmp("lzma", pval
->val
) == 0) {
2781 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_LZMA
;
2782 } else if (strcasecmp("both", pval
->val
) == 0) {
2783 cfg_prec
->swf_compression_type
= HTTP_SWF_COMPRESSION_BOTH
;
2785 SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY
,
2786 "Invalid entry for "
2787 "swf-decompression.type: %s - "
2788 "Killing engine", pval
->val
);
2791 } else if (strcasecmp("compress-depth", pval
->name
) == 0) {
2792 if (ParseSizeStringU32(pval
->val
, &cfg_prec
->swf_compress_depth
) < 0) {
2793 SCLogError(SC_ERR_SIZE_PARSE
,
2794 "Error parsing swf-decompression.compression-depth "
2795 "from conf file - %s. Killing engine", p
->val
);
2798 } else if (strcasecmp("decompress-depth", pval
->name
) == 0) {
2799 if (ParseSizeStringU32(pval
->val
, &cfg_prec
->swf_decompress_depth
) < 0) {
2800 SCLogError(SC_ERR_SIZE_PARSE
,
2801 "Error parsing swf-decompression.decompression-depth "
2802 "from conf file - %s. Killing engine", p
->val
);
2806 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "Ignoring unknown param %s", pval
->name
);
2810 SCLogWarning(SC_ERR_UNKNOWN_VALUE
, "LIBHTP Ignoring unknown "
2811 "default config: %s", p
->name
);
2813 } /* TAILQ_FOREACH(p, &default_config->head, next) */
2818 void HTPConfigure(void)
2822 cfglist
.next
= NULL
;
2824 cfgtree
= SCRadixCreateRadixTree(NULL
, NULL
);
2825 if (NULL
== cfgtree
)
2828 /* Default Config */
2829 cfglist
.cfg
= htp_config_create();
2830 if (NULL
== cfglist
.cfg
) {
2831 FatalError(SC_ERR_FATAL
, "Failed to create HTP default config");
2833 SCLogDebug("LIBHTP default config: %p", cfglist
.cfg
);
2834 HTPConfigSetDefaultsPhase1(&cfglist
);
2835 if (ConfGetNode("app-layer.protocols.http.libhtp") == NULL
) {
2836 HTPConfigParseParameters(&cfglist
, ConfGetNode("libhtp.default-config"),
2839 HTPConfigParseParameters(&cfglist
, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree
);
2841 HTPConfigSetDefaultsPhase2("default", &cfglist
);
2845 /* Read server config and create a parser for each IP in radix tree */
2846 ConfNode
*server_config
= ConfGetNode("app-layer.protocols.http.libhtp.server-config");
2847 if (server_config
== NULL
) {
2848 server_config
= ConfGetNode("libhtp.server-config");
2849 if (server_config
== NULL
) {
2850 SCLogDebug("LIBHTP Configuring %p", server_config
);
2854 SCLogDebug("LIBHTP Configuring %p", server_config
);
2858 TAILQ_FOREACH(si
, &server_config
->head
, next
) {
2859 /* Need the named node, not the index */
2860 ConfNode
*s
= TAILQ_FIRST(&si
->head
);
2862 SCLogDebug("LIBHTP s NULL");
2866 SCLogDebug("LIBHTP server %s", s
->name
);
2868 HTPCfgRec
*nextrec
= cfglist
.next
;
2869 HTPCfgRec
*htprec
= SCMalloc(sizeof(HTPCfgRec
));
2872 memset(htprec
, 0x00, sizeof(*htprec
));
2874 cfglist
.next
= htprec
;
2876 cfglist
.next
->next
= nextrec
;
2877 cfglist
.next
->cfg
= htp_config_create();
2878 if (NULL
== cfglist
.next
->cfg
) {
2879 FatalError(SC_ERR_FATAL
, "Failed to create HTP server config");
2882 HTPConfigSetDefaultsPhase1(htprec
);
2883 HTPConfigParseParameters(htprec
, s
, cfgtree
);
2884 HTPConfigSetDefaultsPhase2(s
->name
, htprec
);
2890 void AppLayerHtpPrintStats(void)
2893 SCMutexLock(&htp_state_mem_lock
);
2894 SCLogPerf("htp memory %"PRIu64
" (%"PRIu64
")", htp_state_memuse
, htp_state_memcnt
);
2895 SCMutexUnlock(&htp_state_mem_lock
);
2900 * \brief get files callback
2901 * \param state state ptr
2902 * \param direction flow direction
2903 * \retval files files ptr
2905 static FileContainer
*HTPStateGetFiles(void *state
, uint8_t direction
)
2910 HtpState
*http_state
= (HtpState
*)state
;
2912 if (direction
& STREAM_TOCLIENT
) {
2913 SCReturnPtr(http_state
->files_tc
, "FileContainer");
2915 SCReturnPtr(http_state
->files_ts
, "FileContainer");
2919 static int HTPStateGetAlstateProgress(void *tx
, uint8_t direction
)
2921 if (direction
& STREAM_TOSERVER
)
2922 return ((htp_tx_t
*)tx
)->request_progress
;
2924 return ((htp_tx_t
*)tx
)->response_progress
;
2927 static uint64_t HTPStateGetTxCnt(void *alstate
)
2929 HtpState
*http_state
= (HtpState
*)alstate
;
2931 if (http_state
!= NULL
&& http_state
->conn
!= NULL
) {
2932 const int64_t size
= (int64_t)htp_list_size(http_state
->conn
->transactions
);
2935 SCLogDebug("size %"PRIu64
, size
);
2936 return (uint64_t)size
;
2942 static void *HTPStateGetTx(void *alstate
, uint64_t tx_id
)
2944 HtpState
*http_state
= (HtpState
*)alstate
;
2946 if (http_state
!= NULL
&& http_state
->conn
!= NULL
)
2947 return htp_list_get(http_state
->conn
->transactions
, tx_id
);
2952 void *HtpGetTxForH2(void *alstate
)
2954 // gets last transaction
2955 HtpState
*http_state
= (HtpState
*)alstate
;
2956 if (http_state
!= NULL
&& http_state
->conn
!= NULL
) {
2957 size_t txid
= htp_list_array_size(http_state
->conn
->transactions
);
2959 return htp_list_get(http_state
->conn
->transactions
, txid
- 1);
2965 static int HTPStateGetEventInfo(const char *event_name
,
2966 int *event_id
, AppLayerEventType
*event_type
)
2968 *event_id
= SCMapEnumNameToValue(event_name
, http_decoder_event_table
);
2969 if (*event_id
== -1) {
2970 SCLogError(SC_ERR_INVALID_ENUM_MAP
, "event \"%s\" not present in "
2971 "http's enum map table.", event_name
);
2972 /* this should be treated as fatal */
2976 *event_type
= APP_LAYER_EVENT_TYPE_TRANSACTION
;
2981 static int HTPStateGetEventInfoById(int event_id
, const char **event_name
,
2982 AppLayerEventType
*event_type
)
2984 *event_name
= SCMapEnumValueToName(event_id
, http_decoder_event_table
);
2985 if (*event_name
== NULL
) {
2986 SCLogError(SC_ERR_INVALID_ENUM_MAP
, "event \"%d\" not present in "
2987 "http's enum map table.", event_id
);
2988 /* this should be treated as fatal */
2992 *event_type
= APP_LAYER_EVENT_TYPE_TRANSACTION
;
2997 static void HTPStateTruncate(void *state
, uint8_t direction
)
2999 FileContainer
*fc
= HTPStateGetFiles(state
, direction
);
3001 FileTruncateAllOpenFiles(fc
);
3005 static DetectEngineState
*HTPGetTxDetectState(void *vtx
)
3007 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
3008 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx
);
3009 return tx_ud
? tx_ud
->de_state
: NULL
;
3012 static int HTPSetTxDetectState(void *vtx
, DetectEngineState
*s
)
3014 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
3015 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx
);
3016 if (tx_ud
== NULL
) {
3019 tx_ud
->de_state
= s
;
3023 static AppLayerTxData
*HTPGetTxData(void *vtx
)
3025 htp_tx_t
*tx
= (htp_tx_t
*)vtx
;
3026 HtpTxUserData
*tx_ud
= htp_tx_get_user_data(tx
);
3028 return &tx_ud
->tx_data
;
3033 static int HTPRegisterPatternsForProtocolDetection(void)
3035 const char *methods
[] = { "GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS",
3036 "CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL",
3037 "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN",
3038 "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE",
3039 "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL", NULL
};
3040 const char *spacings
[] = { "|20|", "|09|", NULL
};
3041 const char *versions
[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL
};
3046 int register_result
;
3047 char method_buffer
[32] = "";
3049 /* Loop through all the methods ands spacings and register the patterns */
3050 for (methods_pos
= 0; methods
[methods_pos
]; methods_pos
++) {
3051 for (spacings_pos
= 0; spacings
[spacings_pos
]; spacings_pos
++) {
3053 /* Combine the method name and the spacing */
3054 snprintf(method_buffer
, sizeof(method_buffer
), "%s%s", methods
[methods_pos
], spacings
[spacings_pos
]);
3056 /* Register the new method+spacing pattern
3057 * 3 is subtracted from the length since the spacing is hex typed as |xx|
3058 * but the pattern matching should only be one char
3060 register_result
= AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_HTTP1
,
3061 method_buffer
, strlen(method_buffer
) - 3, 0, STREAM_TOSERVER
);
3062 if (register_result
< 0) {
3068 /* Loop through all the http verions patterns that are TO_CLIENT */
3069 for (versions_pos
= 0; versions
[versions_pos
]; versions_pos
++) {
3070 register_result
= AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP
, ALPROTO_HTTP1
,
3071 versions
[versions_pos
], strlen(versions
[versions_pos
]), 0, STREAM_TOCLIENT
);
3072 if (register_result
< 0) {
3081 * \brief Register the HTTP protocol and state handling functions to APP layer
3084 void RegisterHTPParsers(void)
3088 const char *proto_name
= "http";
3091 if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name
)) {
3092 AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP1
, proto_name
);
3093 if (HTPRegisterPatternsForProtocolDetection() < 0)
3096 SCLogInfo("Protocol detection and parser disabled for %s protocol",
3101 if (AppLayerParserConfParserEnabled("tcp", proto_name
)) {
3102 AppLayerParserRegisterStateFuncs(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateAlloc
, HTPStateFree
);
3103 AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateTransactionFree
);
3104 AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetFiles
);
3105 AppLayerParserRegisterGetStateProgressFunc(
3106 IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetAlstateProgress
);
3107 AppLayerParserRegisterGetTxCnt(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetTxCnt
);
3108 AppLayerParserRegisterGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetTx
);
3110 AppLayerParserRegisterStateProgressCompletionStatus(
3111 ALPROTO_HTTP1
, HTP_REQUEST_COMPLETE
, HTP_RESPONSE_COMPLETE
);
3112 AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPGetEvents
);
3113 AppLayerParserRegisterGetEventInfo(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetEventInfo
);
3114 AppLayerParserRegisterGetEventInfoById(
3115 IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateGetEventInfoById
);
3117 AppLayerParserRegisterTruncateFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPStateTruncate
);
3118 AppLayerParserRegisterDetectStateFuncs(
3119 IPPROTO_TCP
, ALPROTO_HTTP1
, HTPGetTxDetectState
, HTPSetTxDetectState
);
3120 AppLayerParserRegisterTxDataFunc(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPGetTxData
);
3122 AppLayerParserRegisterSetStreamDepthFlag(
3123 IPPROTO_TCP
, ALPROTO_HTTP1
, AppLayerHtpSetStreamDepthFlag
);
3125 AppLayerParserRegisterParser(
3126 IPPROTO_TCP
, ALPROTO_HTTP1
, STREAM_TOSERVER
, HTPHandleRequestData
);
3127 AppLayerParserRegisterParser(
3128 IPPROTO_TCP
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, HTPHandleResponseData
);
3129 SC_ATOMIC_INIT(htp_config_flags
);
3130 /* This parser accepts gaps. */
3131 AppLayerParserRegisterOptionFlags(
3132 IPPROTO_TCP
, ALPROTO_HTTP1
, APP_LAYER_PARSER_OPT_ACCEPT_GAPS
);
3133 AppLayerParserRegisterParserAcceptableDataDirection(
3134 IPPROTO_TCP
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_TOCLIENT
);
3137 SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
3138 "still on.", proto_name
);
3141 AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP
, ALPROTO_HTTP1
, HTPParserRegisterTests
);
3148 static HTPCfgRec cfglist_backup
;
3150 void HtpConfigCreateBackup(void)
3152 cfglist_backup
= cfglist
;
3157 void HtpConfigRestoreBackup(void)
3159 cfglist
= cfglist_backup
;
3164 /** \test Test case where chunks are sent in smaller chunks and check the
3165 * response of the parser from HTP library. */
3166 static int HTPParserTest01(void)
3168 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3170 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3173 memset(&ssn
, 0, sizeof(ssn
));
3175 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3176 FAIL_IF_NULL(alp_tctx
);
3178 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3181 f
->proto
= IPPROTO_TCP
;
3182 f
->alproto
= ALPROTO_HTTP1
;
3184 StreamTcpInitConfig(true);
3187 for (u
= 0; u
< httplen1
; u
++) {
3191 flags
= STREAM_TOSERVER
|STREAM_START
;
3192 else if (u
== (httplen1
- 1))
3193 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3195 flags
= STREAM_TOSERVER
;
3197 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3201 HtpState
*htp_state
= f
->alstate
;
3202 FAIL_IF_NULL(htp_state
);
3204 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3207 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3210 FAIL_IF(strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0"));
3211 FAIL_IF(tx
->request_method_number
!= HTP_M_POST
);
3212 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
);
3214 AppLayerParserThreadCtxFree(alp_tctx
);
3215 StreamTcpFreeConfig(true);
3220 /** \test Test folding in 1 read case */
3221 static int HTPParserTest01b(void)
3223 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3225 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3228 memset(&ssn
, 0, sizeof(ssn
));
3230 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3231 FAIL_IF_NULL(alp_tctx
);
3233 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3236 f
->proto
= IPPROTO_TCP
;
3237 f
->alproto
= ALPROTO_HTTP1
;
3239 StreamTcpInitConfig(true);
3241 uint8_t flags
=STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3242 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, httpbuf1
, httplen1
);
3245 HtpState
*htp_state
= f
->alstate
;
3246 FAIL_IF_NULL(htp_state
);
3248 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3251 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3254 FAIL_IF(strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0"));
3255 FAIL_IF(tx
->request_method_number
!= HTP_M_POST
);
3256 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
);
3258 AppLayerParserThreadCtxFree(alp_tctx
);
3259 StreamTcpFreeConfig(true);
3264 /** \test Test folding in 1byte per read case */
3265 static int HTPParserTest01c(void)
3267 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3269 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3272 memset(&ssn
, 0, sizeof(ssn
));
3274 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3275 FAIL_IF_NULL(alp_tctx
);
3277 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3280 f
->proto
= IPPROTO_TCP
;
3281 f
->alproto
= ALPROTO_HTTP1
;
3283 StreamTcpInitConfig(true);
3286 for (u
= 0; u
< httplen1
; u
++) {
3290 flags
= STREAM_TOSERVER
|STREAM_START
;
3291 else if (u
== (httplen1
- 1))
3292 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3294 flags
= STREAM_TOSERVER
;
3296 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3300 HtpState
*htp_state
= f
->alstate
;
3301 FAIL_IF_NULL(htp_state
);
3303 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3306 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3309 FAIL_IF(strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0"));
3310 FAIL_IF(tx
->request_method_number
!= HTP_M_POST
);
3311 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
);
3313 AppLayerParserThreadCtxFree(alp_tctx
);
3314 StreamTcpFreeConfig(true);
3319 /** \test Test case where chunks are sent in smaller chunks and check the
3320 * response of the parser from HTP library. */
3321 static int HTPParserTest01a(void)
3325 uint8_t httpbuf1
[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3327 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3329 HtpState
*htp_state
= NULL
;
3331 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3333 memset(&ssn
, 0, sizeof(ssn
));
3335 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3339 f
->proto
= IPPROTO_TCP
;
3340 f
->alproto
= ALPROTO_HTTP1
;
3342 StreamTcpInitConfig(true);
3345 for (u
= 0; u
< httplen1
; u
++) {
3349 flags
= STREAM_TOSERVER
|STREAM_START
;
3350 else if (u
== (httplen1
- 1))
3351 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3353 flags
= STREAM_TOSERVER
;
3356 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3358 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3366 htp_state
= f
->alstate
;
3367 if (htp_state
== NULL
) {
3368 printf("no http state: ");
3372 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3373 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3374 if (strcmp(bstr_util_strdup_to_c(h
->value
), "Victor/1.0")
3375 || tx
->request_method_number
!= HTP_M_POST
||
3376 tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
)
3378 printf("expected header value: Victor/1.0 and got %s: and expected"
3379 " method: POST and got %s, expected protocol number HTTP/1.0"
3380 " and got: %s \n", bstr_util_strdup_to_c(h
->value
),
3381 bstr_util_strdup_to_c(tx
->request_method
),
3382 bstr_util_strdup_to_c(tx
->request_protocol
));
3387 if (alp_tctx
!= NULL
)
3388 AppLayerParserThreadCtxFree(alp_tctx
);
3389 StreamTcpFreeConfig(true);
3394 /** \test See how it deals with an incomplete request. */
3395 static int HTPParserTest02(void)
3399 uint8_t httpbuf1
[] = "POST";
3400 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3402 HtpState
*http_state
= NULL
;
3403 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3405 memset(&ssn
, 0, sizeof(ssn
));
3407 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3411 f
->proto
= IPPROTO_TCP
;
3412 f
->alproto
= ALPROTO_HTTP1
;
3414 StreamTcpInitConfig(true);
3417 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
,
3418 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
, httpbuf1
, httplen1
);
3420 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
3426 http_state
= f
->alstate
;
3427 if (http_state
== NULL
) {
3428 printf("no http state: ");
3432 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3434 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3435 FAIL_IF_NOT_NULL(h
);
3437 FAIL_IF_NULL(tx
->request_method
);
3438 char *method
= bstr_util_strdup_to_c(tx
->request_method
);
3439 FAIL_IF_NULL(method
);
3441 FAIL_IF(strcmp(method
, "POST") != 0);
3446 if (alp_tctx
!= NULL
)
3447 AppLayerParserThreadCtxFree(alp_tctx
);
3448 StreamTcpFreeConfig(true);
3453 /** \test Test case where method is invalid and data is sent in smaller chunks
3454 * and check the response of the parser from HTP library. */
3455 static int HTPParserTest03(void)
3459 uint8_t httpbuf1
[] = "HELLO / HTTP/1.0\r\n";
3460 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3462 HtpState
*htp_state
= NULL
;
3464 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3466 memset(&ssn
, 0, sizeof(ssn
));
3468 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3472 f
->proto
= IPPROTO_TCP
;
3473 f
->alproto
= ALPROTO_HTTP1
;
3475 StreamTcpInitConfig(true);
3478 for (u
= 0; u
< httplen1
; u
++) {
3481 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
3482 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
3483 else flags
= STREAM_TOSERVER
;
3486 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3488 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3495 htp_state
= f
->alstate
;
3496 if (htp_state
== NULL
) {
3497 printf("no http state: ");
3501 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3503 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3504 if (tx
->request_method_number
!= HTP_M_UNKNOWN
||
3505 h
!= NULL
|| tx
->request_protocol_number
!= HTP_PROTOCOL_1_0
)
3507 printf("expected method M_UNKNOWN and got %s: , expected protocol "
3508 "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx
->request_method
),
3509 bstr_util_strdup_to_c(tx
->request_protocol
));
3514 if (alp_tctx
!= NULL
)
3515 AppLayerParserThreadCtxFree(alp_tctx
);
3516 StreamTcpFreeConfig(true);
3521 /** \test Test case where invalid data is sent and check the response of the
3522 * parser from HTP library. */
3523 static int HTPParserTest04(void)
3527 HtpState
*htp_state
= NULL
;
3528 uint8_t httpbuf1
[] = "World!\r\n";
3529 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3532 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3534 memset(&ssn
, 0, sizeof(ssn
));
3536 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3540 f
->proto
= IPPROTO_TCP
;
3541 f
->alproto
= ALPROTO_HTTP1
;
3543 StreamTcpInitConfig(true);
3546 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
,
3547 STREAM_TOSERVER
| STREAM_START
| STREAM_EOF
, httpbuf1
, httplen1
);
3554 htp_state
= f
->alstate
;
3555 if (htp_state
== NULL
) {
3556 printf("no http state: ");
3560 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3561 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3562 if (tx
->request_method_number
!= HTP_M_UNKNOWN
||
3563 h
!= NULL
|| tx
->request_protocol_number
!= HTP_PROTOCOL_0_9
)
3565 printf("expected method M_UNKNOWN and got %s: , expected protocol "
3566 "NULL and got %s \n", bstr_util_strdup_to_c(tx
->request_method
),
3567 bstr_util_strdup_to_c(tx
->request_protocol
));
3572 if (alp_tctx
!= NULL
)
3573 AppLayerParserThreadCtxFree(alp_tctx
);
3574 StreamTcpFreeConfig(true);
3579 /** \test Test both sides of a http stream mixed up to see if the HTP parser
3580 * properly parsed them and also keeps them separated. */
3581 static int HTPParserTest05(void)
3583 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\nContent-Length: 17\r\n\r\n";
3584 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3585 uint8_t httpbuf2
[] = "Post D";
3586 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
3587 uint8_t httpbuf3
[] = "ata is c0oL!";
3588 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
3590 uint8_t httpbuf4
[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
3591 uint32_t httplen4
= sizeof(httpbuf4
) - 1; /* minus the \0 */
3592 uint8_t httpbuf5
[] = "post R";
3593 uint32_t httplen5
= sizeof(httpbuf5
) - 1; /* minus the \0 */
3594 uint8_t httpbuf6
[] = "esults are tha bomb!";
3595 uint32_t httplen6
= sizeof(httpbuf6
) - 1; /* minus the \0 */
3598 memset(&ssn
, 0, sizeof(ssn
));
3600 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3601 FAIL_IF_NULL(alp_tctx
);
3603 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3606 f
->proto
= IPPROTO_TCP
;
3607 f
->alproto
= ALPROTO_HTTP1
;
3609 StreamTcpInitConfig(true);
3611 int r
= AppLayerParserParse(
3612 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
3615 r
= AppLayerParserParse(
3616 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf4
, httplen4
);
3619 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, httpbuf5
, httplen5
);
3622 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, httpbuf2
, httplen2
);
3625 r
= AppLayerParserParse(
3626 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_EOF
, httpbuf3
, httplen3
);
3629 r
= AppLayerParserParse(
3630 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_EOF
, httpbuf6
, httplen6
);
3633 HtpState
*http_state
= f
->alstate
;
3634 FAIL_IF_NULL(http_state
);
3636 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3638 FAIL_IF_NOT(tx
->request_method_number
== HTP_M_POST
);
3639 FAIL_IF_NOT(tx
->request_protocol_number
== HTP_PROTOCOL_1_0
);
3641 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3644 FAIL_IF_NOT(tx
->response_status_number
== 200);
3646 AppLayerParserThreadCtxFree(alp_tctx
);
3647 StreamTcpFreeConfig(true);
3652 /** \test Test proper chunked encoded response body
3654 static int HTPParserTest06(void)
3656 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
3657 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
3658 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
3659 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3660 uint8_t httpbuf2
[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
3662 "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
3663 "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
3664 "FrontPage/5.0.2.2510\r\n"
3665 "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
3667 "Content-Type: text/html\r\n\r\n"
3669 "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
3670 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
3671 "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
3672 "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
3673 "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
3674 "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
3675 "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
3676 "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
3677 "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
3678 "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
3679 "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
3680 "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
3681 "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
3682 "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
3683 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
3684 "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
3685 "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
3686 "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
3687 "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
3688 "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
3689 "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
3690 "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
3691 "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
3692 "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
3693 "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
3694 "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
3695 "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
3696 "aHA=\r\n0\r\n\r\n";
3697 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
3700 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3701 FAIL_IF_NULL(alp_tctx
);
3703 memset(&ssn
, 0, sizeof(ssn
));
3705 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3708 f
->proto
= IPPROTO_TCP
;
3709 f
->alproto
= ALPROTO_HTTP1
;
3711 StreamTcpInitConfig(true);
3713 int r
= AppLayerParserParse(
3714 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
3716 r
= AppLayerParserParse(
3717 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
3720 HtpState
*http_state
= f
->alstate
;
3721 FAIL_IF_NULL(http_state
);
3723 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
3726 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
3727 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
3729 FAIL_IF(tx
->response_status_number
!= 200);
3730 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
3732 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
3735 AppLayerParserThreadCtxFree(alp_tctx
);
3736 StreamTcpFreeConfig(true);
3743 static int HTPParserTest07(void)
3747 uint8_t httpbuf1
[] = "GET /awstats.pl?/migratemigrate%20=%20| HTTP/1.0\r\n\r\n";
3748 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3750 HtpState
*htp_state
= NULL
;
3752 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3754 memset(&ssn
, 0, sizeof(ssn
));
3756 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3760 f
->proto
= IPPROTO_TCP
;
3761 f
->alproto
= ALPROTO_HTTP1
;
3763 StreamTcpInitConfig(true);
3766 for (u
= 0; u
< httplen1
; u
++) {
3770 flags
= STREAM_TOSERVER
|STREAM_START
;
3771 else if (u
== (httplen1
- 1))
3772 flags
= STREAM_TOSERVER
|STREAM_EOF
;
3774 flags
= STREAM_TOSERVER
;
3777 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
3779 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
3787 htp_state
= f
->alstate
;
3788 if (htp_state
== NULL
) {
3789 printf("no http state: ");
3793 uint8_t ref
[] = "/awstats.pl?/migratemigrate = |";
3794 size_t reflen
= sizeof(ref
) - 1;
3796 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3799 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
3800 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
3801 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
3802 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
3804 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
3808 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref
,
3809 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
3811 printf("normalized uri \"");
3812 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
3814 PrintRawUriFp(stdout
, ref
, reflen
);
3822 if (alp_tctx
!= NULL
)
3823 AppLayerParserThreadCtxFree(alp_tctx
);
3824 StreamTcpFreeConfig(true);
3829 #include "conf-yaml-loader.h"
3833 static int HTPParserTest08(void)
3837 uint8_t httpbuf1
[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3838 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3840 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3851 ConfCreateContextBackup();
3853 HtpConfigCreateBackup();
3855 ConfYamlLoadString(input
, strlen(input
));
3858 HtpState
*htp_state
= NULL
;
3860 memset(&ssn
, 0, sizeof(ssn
));
3862 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3866 f
->proto
= IPPROTO_TCP
;
3867 f
->alproto
= ALPROTO_HTTP1
;
3869 StreamTcpInitConfig(true);
3872 flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3875 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, httpbuf1
, httplen1
);
3877 printf("toserver chunk returned %" PRId32
", expected"
3885 htp_state
= f
->alstate
;
3886 if (htp_state
== NULL
) {
3887 printf("no http state: ");
3892 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3895 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
3896 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
3897 //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3898 PrintRawDataFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
),
3899 bstr_len(tx_ud
->request_uri_normalized
));
3904 if (alp_tctx
!= NULL
)
3905 AppLayerParserThreadCtxFree(alp_tctx
);
3906 StreamTcpFreeConfig(true);
3909 ConfRestoreContextBackup();
3910 HtpConfigRestoreBackup();
3917 static int HTPParserTest09(void)
3921 uint8_t httpbuf1
[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3922 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
3924 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
3932 personality: Apache_2_2\n\
3935 ConfCreateContextBackup();
3937 HtpConfigCreateBackup();
3939 ConfYamlLoadString(input
, strlen(input
));
3942 HtpState
*htp_state
= NULL
;
3945 memset(&ssn
, 0, sizeof(ssn
));
3947 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
3951 f
->proto
= IPPROTO_TCP
;
3952 f
->alproto
= ALPROTO_HTTP1
;
3954 StreamTcpInitConfig(true);
3957 flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
3960 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, httpbuf1
, httplen1
);
3962 printf("toserver chunk returned %" PRId32
", expected"
3969 htp_state
= f
->alstate
;
3970 if (htp_state
== NULL
) {
3971 printf("no http state: ");
3975 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
3978 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
3979 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
3980 //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3981 PrintRawDataFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
),
3982 bstr_len(tx_ud
->request_uri_normalized
));
3987 if (alp_tctx
!= NULL
)
3988 AppLayerParserThreadCtxFree(alp_tctx
);
3989 StreamTcpFreeConfig(true);
3992 ConfRestoreContextBackup();
3993 HtpConfigRestoreBackup();
3998 /** \test Host:www.google.com <- missing space between name:value (rfc violation)
4000 static int HTPParserTest10(void)
4004 uint8_t httpbuf1
[] = "GET / HTTP/1.0\r\nHost:www.google.com\r\n\r\n";
4005 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4007 HtpState
*htp_state
= NULL
;
4009 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4011 memset(&ssn
, 0, sizeof(ssn
));
4013 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4017 f
->proto
= IPPROTO_TCP
;
4018 f
->alproto
= ALPROTO_HTTP1
;
4020 StreamTcpInitConfig(true);
4023 for (u
= 0; u
< httplen1
; u
++) {
4027 flags
= STREAM_TOSERVER
|STREAM_START
;
4028 else if (u
== (httplen1
- 1))
4029 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4031 flags
= STREAM_TOSERVER
;
4034 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4036 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4044 htp_state
= f
->alstate
;
4045 if (htp_state
== NULL
) {
4046 printf("no http state: ");
4050 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4051 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
4056 char *name
= bstr_util_strdup_to_c(h
->name
);
4061 if (strcmp(name
, "Host") != 0) {
4062 printf("header name not \"Host\", instead \"%s\": ", name
);
4068 char *value
= bstr_util_strdup_to_c(h
->value
);
4069 if (value
== NULL
) {
4073 if (strcmp(value
, "www.google.com") != 0) {
4074 printf("header value not \"www.google.com\", instead \"%s\": ", value
);
4082 if (alp_tctx
!= NULL
)
4083 AppLayerParserThreadCtxFree(alp_tctx
);
4084 StreamTcpFreeConfig(true);
4089 /** \test double encoding in path
4091 static int HTPParserTest11(void)
4095 uint8_t httpbuf1
[] = "GET /%2500 HTTP/1.0\r\n\r\n";
4096 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4098 HtpState
*htp_state
= NULL
;
4100 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4102 memset(&ssn
, 0, sizeof(ssn
));
4104 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4108 f
->proto
= IPPROTO_TCP
;
4109 f
->alproto
= ALPROTO_HTTP1
;
4111 StreamTcpInitConfig(true);
4114 for (u
= 0; u
< httplen1
; u
++) {
4118 flags
= STREAM_TOSERVER
|STREAM_START
;
4119 else if (u
== (httplen1
- 1))
4120 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4122 flags
= STREAM_TOSERVER
;
4125 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4127 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4135 htp_state
= f
->alstate
;
4136 if (htp_state
== NULL
) {
4137 printf("no http state: ");
4141 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4144 HtpTxUserData
*tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
4145 if (tx
!= NULL
&& tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4146 if (4 != bstr_len(tx_ud
->request_uri_normalized
)) {
4147 printf("normalized uri len should be 2, is %"PRIuMAX
,
4148 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4152 if (bstr_ptr(tx_ud
->request_uri_normalized
)[0] != '/' ||
4153 bstr_ptr(tx_ud
->request_uri_normalized
)[1] != '%' ||
4154 bstr_ptr(tx_ud
->request_uri_normalized
)[2] != '0' ||
4155 bstr_ptr(tx_ud
->request_uri_normalized
)[3] != '0')
4157 printf("normalized uri \"");
4158 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4166 if (alp_tctx
!= NULL
)
4167 AppLayerParserThreadCtxFree(alp_tctx
);
4168 StreamTcpFreeConfig(true);
4173 /** \test double encoding in query
4175 static int HTPParserTest12(void)
4179 uint8_t httpbuf1
[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
4180 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4182 HtpState
*htp_state
= NULL
;
4184 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4186 memset(&ssn
, 0, sizeof(ssn
));
4188 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4192 f
->proto
= IPPROTO_TCP
;
4193 f
->alproto
= ALPROTO_HTTP1
;
4195 StreamTcpInitConfig(true);
4198 for (u
= 0; u
< httplen1
; u
++) {
4202 flags
= STREAM_TOSERVER
|STREAM_START
;
4203 else if (u
== (httplen1
- 1))
4204 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4206 flags
= STREAM_TOSERVER
;
4209 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4211 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4219 htp_state
= f
->alstate
;
4220 if (htp_state
== NULL
) {
4221 printf("no http state: ");
4225 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4228 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
4229 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4230 if (7 != bstr_len(tx_ud
->request_uri_normalized
)) {
4231 printf("normalized uri len should be 5, is %"PRIuMAX
,
4232 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4236 if (bstr_ptr(tx_ud
->request_uri_normalized
)[0] != '/' ||
4237 bstr_ptr(tx_ud
->request_uri_normalized
)[1] != '?' ||
4238 bstr_ptr(tx_ud
->request_uri_normalized
)[2] != 'a' ||
4239 bstr_ptr(tx_ud
->request_uri_normalized
)[3] != '=' ||
4240 bstr_ptr(tx_ud
->request_uri_normalized
)[4] != '%' ||
4241 bstr_ptr(tx_ud
->request_uri_normalized
)[5] != '0' ||
4242 bstr_ptr(tx_ud
->request_uri_normalized
)[6] != '0')
4244 printf("normalized uri \"");
4245 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4253 if (alp_tctx
!= NULL
)
4254 AppLayerParserThreadCtxFree(alp_tctx
);
4255 StreamTcpFreeConfig(true);
4260 /** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
4262 static int HTPParserTest13(void)
4266 uint8_t httpbuf1
[] = "GET / HTTP/1.0\r\nHost:www.google.com\rName: Value\r\n\r\n";
4267 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4269 HtpState
*htp_state
= NULL
;
4271 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4273 memset(&ssn
, 0, sizeof(ssn
));
4275 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
4279 f
->proto
= IPPROTO_TCP
;
4280 f
->alproto
= ALPROTO_HTTP1
;
4282 StreamTcpInitConfig(true);
4285 for (u
= 0; u
< httplen1
; u
++) {
4289 flags
= STREAM_TOSERVER
|STREAM_START
;
4290 else if (u
== (httplen1
- 1))
4291 flags
= STREAM_TOSERVER
|STREAM_EOF
;
4293 flags
= STREAM_TOSERVER
;
4296 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4298 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4306 htp_state
= f
->alstate
;
4307 if (htp_state
== NULL
) {
4308 printf("no http state: ");
4312 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4313 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
4318 char *name
= bstr_util_strdup_to_c(h
->name
);
4323 if (strcmp(name
, "Host") != 0) {
4324 printf("header name not \"Host\", instead \"%s\": ", name
);
4330 char *value
= bstr_util_strdup_to_c(h
->value
);
4331 if (value
== NULL
) {
4335 if (strcmp(value
, "www.google.com\rName: Value") != 0) {
4336 printf("header value not \"www.google.com\", instead \"");
4337 PrintRawUriFp(stdout
, (uint8_t *)value
, strlen(value
));
4346 if (alp_tctx
!= NULL
)
4347 AppLayerParserThreadCtxFree(alp_tctx
);
4348 StreamTcpFreeConfig(true);
4353 /** \test Test basic config */
4354 static int HTPParserConfigTest01(void)
4368 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4369 personality: Tomcat_6_0\n\
4374 - 192.168.10.0/24\n\
4375 personality: IIS_7_0\n\
4378 ConfCreateContextBackup();
4381 ConfYamlLoadString(input
, strlen(input
));
4384 outputs
= ConfGetNode("libhtp.default-config.personality");
4385 if (outputs
== NULL
) {
4389 outputs
= ConfGetNode("libhtp.server-config");
4390 if (outputs
== NULL
) {
4394 ConfNode
*node
= TAILQ_FIRST(&outputs
->head
);
4398 if (strcmp(node
->name
, "0") != 0) {
4401 node
= TAILQ_FIRST(&node
->head
);
4405 if (strcmp(node
->name
, "apache-tomcat") != 0) {
4412 ConfNode
*node2
= ConfNodeLookupChild(node
, "personality");
4413 if (node2
== NULL
) {
4416 if (strcmp(node2
->val
, "Tomcat_6_0") != 0) {
4420 node
= ConfNodeLookupChild(node
, "address");
4424 TAILQ_FOREACH(n
, &node
->head
, next
) {
4431 if (strcmp(n
->name
, "0") != 0) {
4434 if (strcmp(n
->val
, "192.168.1.0/24") != 0) {
4439 if (strcmp(n
->name
, "1") != 0) {
4442 if (strcmp(n
->val
, "127.0.0.0/8") != 0) {
4447 if (strcmp(n
->name
, "2") != 0) {
4450 if (strcmp(n
->val
, "::1") != 0) {
4460 outputs
= ConfGetNode("libhtp.server-config");
4461 if (outputs
== NULL
) {
4465 node
= TAILQ_FIRST(&outputs
->head
);
4466 node
= TAILQ_NEXT(node
, next
);
4470 if (strcmp(node
->name
, "1") != 0) {
4473 node
= TAILQ_FIRST(&node
->head
);
4477 if (strcmp(node
->name
, "iis7") != 0) {
4481 node2
= ConfNodeLookupChild(node
, "personality");
4482 if (node2
== NULL
) {
4485 if (strcmp(node2
->val
, "IIS_7_0") != 0) {
4489 node
= ConfNodeLookupChild(node
, "address");
4495 TAILQ_FOREACH(n
, &node
->head
, next
) {
4502 if (strcmp(n
->name
, "0") != 0) {
4505 if (strcmp(n
->val
, "192.168.0.0/24") != 0) {
4510 if (strcmp(n
->name
, "1") != 0) {
4513 if (strcmp(n
->val
, "192.168.10.0/24") != 0) {
4527 ConfRestoreContextBackup();
4532 /** \test Test config builds radix correctly */
4533 static int HTPParserConfigTest02(void)
4547 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4548 personality: Tomcat_6_0\n\
4553 - 192.168.10.0/24\n\
4554 personality: IIS_7_0\n\
4557 ConfCreateContextBackup();
4559 HtpConfigCreateBackup();
4561 ConfYamlLoadString(input
, strlen(input
));
4565 if (cfglist
.cfg
== NULL
) {
4566 printf("No default config created.\n");
4570 if (cfgtree
== NULL
) {
4571 printf("No config tree created.\n");
4575 htp_cfg_t
*htp
= cfglist
.cfg
;
4578 void *user_data
= NULL
;
4580 addr
= "192.168.10.42";
4581 if (inet_pton(AF_INET
, addr
, buf
) == 1) {
4582 (void)SCRadixFindKeyIPV4BestMatch(buf
, cfgtree
, &user_data
);
4583 if (user_data
!= NULL
) {
4584 HTPCfgRec
*htp_cfg_rec
= user_data
;
4585 htp
= htp_cfg_rec
->cfg
;
4586 SCLogDebug("LIBHTP using config: %p", htp
);
4589 printf("Could not get config for: %s\n", addr
);
4594 printf("Failed to parse address: %s\n", addr
);
4600 if (inet_pton(AF_INET6
, addr
, buf
) == 1) {
4601 (void)SCRadixFindKeyIPV6BestMatch(buf
, cfgtree
, &user_data
);
4602 if (user_data
!= NULL
) {
4603 HTPCfgRec
*htp_cfg_rec
= user_data
;
4604 htp
= htp_cfg_rec
->cfg
;
4605 SCLogDebug("LIBHTP using config: %p", htp
);
4608 printf("Could not get config for: %s\n", addr
);
4613 printf("Failed to parse address: %s\n", addr
);
4622 ConfRestoreContextBackup();
4623 HtpConfigRestoreBackup();
4628 /** \test Test traffic is handled by the correct htp config */
4629 static int HTPParserConfigTest03(void)
4633 uint8_t httpbuf1
[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
4635 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4637 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4639 HtpState
*htp_state
= NULL
;
4652 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4653 personality: Tomcat_6_0\n\
4658 - 192.168.10.0/24\n\
4659 personality: IIS_7_0\n\
4662 ConfCreateContextBackup();
4664 HtpConfigCreateBackup();
4666 ConfYamlLoadString(input
, strlen(input
));
4670 const char *addr
= "192.168.10.42";
4672 memset(&ssn
, 0, sizeof(ssn
));
4674 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4678 f
->proto
= IPPROTO_TCP
;
4679 f
->alproto
= ALPROTO_HTTP1
;
4681 htp_cfg_t
*htp
= cfglist
.cfg
;
4683 void *user_data
= NULL
;
4684 (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)f
->dst
.addr_data32
, cfgtree
, &user_data
);
4685 if (user_data
!= NULL
) {
4686 HTPCfgRec
*htp_cfg_rec
= user_data
;
4687 htp
= htp_cfg_rec
->cfg
;
4688 SCLogDebug("LIBHTP using config: %p", htp
);
4691 printf("Could not get config for: %s\n", addr
);
4695 StreamTcpInitConfig(true);
4698 for (u
= 0; u
< httplen1
; u
++) {
4701 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
4702 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
4703 else flags
= STREAM_TOSERVER
;
4706 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4708 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4717 htp_state
= f
->alstate
;
4718 if (htp_state
== NULL
) {
4719 printf("no http state: ");
4724 if (HTPStateGetTxCnt(htp_state
) != 2) {
4725 printf("HTPStateGetTxCnt(htp_state) failure\n");
4729 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4732 if (tx
->cfg
!= htp
) {
4733 printf("wrong HTP config (%p instead of %p - default=%p): ",
4734 tx
->cfg
, htp
, cfglist
.cfg
);
4737 tx
= HTPStateGetTx(htp_state
, 1);
4740 if (tx
->cfg
!= htp
) {
4741 printf("wrong HTP config (%p instead of %p - default=%p): ",
4742 tx
->cfg
, htp
, cfglist
.cfg
);
4747 if (alp_tctx
!= NULL
)
4748 AppLayerParserThreadCtxFree(alp_tctx
);
4751 ConfRestoreContextBackup();
4752 HtpConfigRestoreBackup();
4754 StreamTcpFreeConfig(true);
4759 /* disabled when we upgraded to libhtp 0.5.x */
4761 static int HTPParserConfigTest04(void)
4772 path-control-char-handling: status_400\n\
4773 path-convert-utf8: yes\n\
4774 path-invalid-encoding-handling: remove_percent\n\
4779 personality: Tomcat_6_0\n\
4780 path-invalid-utf8-handling: none\n\
4781 path-nul-encoded-handling: status_404\n\
4782 path-nul-raw-handling: status_400\n\
4785 personality: IIS_7_0\n\
4786 path-replacement-char: o\n\
4787 path-unicode-mapping: status_400\n\
4790 ConfCreateContextBackup();
4792 HtpConfigCreateBackup();
4794 ConfYamlLoadString(input
, strlen(input
));
4798 HTPCfgRec
*cfg_rec
= &cfglist
;
4799 if (cfg_rec
->cfg
->path_control_char_handling
!= STATUS_400
||
4800 cfg_rec
->cfg
->path_convert_utf8
!= 1 ||
4801 cfg_rec
->cfg
->path_invalid_encoding_handling
!= URL_DECODER_REMOVE_PERCENT
) {
4802 printf("failed 1\n");
4806 cfg_rec
= cfg_rec
->next
;
4807 if (cfg_rec
->cfg
->bestfit_replacement_char
!= 'o' ||
4808 cfg_rec
->cfg
->path_unicode_mapping
!= STATUS_400
) {
4809 printf("failed 2\n");
4813 cfg_rec
= cfg_rec
->next
;
4814 if (cfg_rec
->cfg
->path_invalid_utf8_handling
!= NONE
||
4815 cfg_rec
->cfg
->path_nul_encoded_handling
!= STATUS_404
||
4816 cfg_rec
->cfg
->path_nul_raw_handling
!= STATUS_400
) {
4817 printf("failed 3\n");
4826 ConfRestoreContextBackup();
4827 HtpConfigRestoreBackup();
4833 /** \test Test %2f decoding in profile Apache_2_2
4835 * %2f in path is left untouched
4836 * %2f in query string is normalized to %2F
4837 * %252f in query string is decoded/normalized to %2F
4839 static int HTPParserDecodingTest01(void)
4843 uint8_t httpbuf1
[] =
4844 "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4845 "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4846 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4847 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
4849 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
4851 HtpState
*htp_state
= NULL
;
4859 personality: Apache_2\n\
4862 ConfCreateContextBackup();
4864 HtpConfigCreateBackup();
4865 ConfYamlLoadString(input
, strlen(input
));
4867 const char *addr
= "4.3.2.1";
4868 memset(&ssn
, 0, sizeof(ssn
));
4870 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
4874 f
->proto
= IPPROTO_TCP
;
4875 f
->alproto
= ALPROTO_HTTP1
;
4877 StreamTcpInitConfig(true);
4880 for (u
= 0; u
< httplen1
; u
++) {
4883 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
4884 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
4885 else flags
= STREAM_TOSERVER
;
4888 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
4890 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
4898 htp_state
= f
->alstate
;
4899 if (htp_state
== NULL
) {
4900 printf("no http state: ");
4904 uint8_t ref1
[] = "/abc%2fdef";
4905 size_t reflen
= sizeof(ref1
) - 1;
4907 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
4910 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
4911 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4912 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
4913 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
4915 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4919 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
4920 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4922 printf("normalized uri \"");
4923 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4925 PrintRawUriFp(stdout
, ref1
, reflen
);
4931 uint8_t ref2
[] = "/abc/def?ghi/jkl";
4932 reflen
= sizeof(ref2
) - 1;
4934 tx
= HTPStateGetTx(htp_state
, 1);
4937 tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
4938 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4939 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
4940 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
4942 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4946 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
4947 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4949 printf("normalized uri \"");
4950 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4952 PrintRawUriFp(stdout
, ref2
, reflen
);
4958 uint8_t ref3
[] = "/abc/def?ghi%2fjkl";
4959 reflen
= sizeof(ref3
) - 1;
4960 tx
= HTPStateGetTx(htp_state
, 2);
4963 tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
4964 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
4965 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
4966 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
4968 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
4972 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref3
,
4973 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
4975 printf("normalized uri \"");
4976 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
4978 PrintRawUriFp(stdout
, ref3
, reflen
);
4987 if (alp_tctx
!= NULL
)
4988 AppLayerParserThreadCtxFree(alp_tctx
);
4991 ConfRestoreContextBackup();
4992 HtpConfigRestoreBackup();
4994 StreamTcpFreeConfig(true);
4999 /** \test Test %2f decoding in profile IDS
5001 * %2f in path decoded to /
5002 * %2f in query string is decoded to /
5003 * %252f in query string is decoded to %2F
5005 static int HTPParserDecodingTest02(void)
5009 uint8_t httpbuf1
[] =
5010 "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5011 "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5012 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5013 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5015 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5017 HtpState
*htp_state
= NULL
;
5026 double-decode-path: no\n\
5027 double-decode-query: no\n\
5030 ConfCreateContextBackup();
5032 HtpConfigCreateBackup();
5033 ConfYamlLoadString(input
, strlen(input
));
5035 const char *addr
= "4.3.2.1";
5036 memset(&ssn
, 0, sizeof(ssn
));
5038 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5042 f
->proto
= IPPROTO_TCP
;
5043 f
->alproto
= ALPROTO_HTTP1
;
5045 StreamTcpInitConfig(true);
5048 for (u
= 0; u
< httplen1
; u
++) {
5051 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5052 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5053 else flags
= STREAM_TOSERVER
;
5056 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5058 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5066 htp_state
= f
->alstate
;
5067 if (htp_state
== NULL
) {
5068 printf("no http state: ");
5072 uint8_t ref1
[] = "/abc/def";
5073 size_t reflen
= sizeof(ref1
) - 1;
5075 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5078 HtpTxUserData
*tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
5079 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5080 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5081 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5083 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5087 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5088 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5090 printf("normalized uri \"");
5091 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5093 PrintRawUriFp(stdout
, ref1
, reflen
);
5099 uint8_t ref2
[] = "/abc/def?ghi/jkl";
5100 reflen
= sizeof(ref2
) - 1;
5102 tx
= HTPStateGetTx(htp_state
, 1);
5105 tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
5106 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5107 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5108 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5110 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5114 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
5115 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5117 printf("normalized uri \"");
5118 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5120 PrintRawUriFp(stdout
, ref2
, reflen
);
5126 uint8_t ref3
[] = "/abc/def?ghi%2fjkl";
5127 reflen
= sizeof(ref3
) - 1;
5128 tx
= HTPStateGetTx(htp_state
, 2);
5131 tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5132 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5133 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5134 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
" (3): ",
5136 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5140 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref3
,
5141 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5143 printf("normalized uri \"");
5144 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5146 PrintRawUriFp(stdout
, ref3
, reflen
);
5155 if (alp_tctx
!= NULL
)
5156 AppLayerParserThreadCtxFree(alp_tctx
);
5159 ConfRestoreContextBackup();
5160 HtpConfigRestoreBackup();
5162 StreamTcpFreeConfig(true);
5167 /** \test Test %2f decoding in profile IDS with double-decode-* options
5169 * %252f in path decoded to /
5170 * %252f in query string is decoded to /
5172 static int HTPParserDecodingTest03(void)
5176 uint8_t httpbuf1
[] =
5177 "GET /abc%252fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5178 "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5179 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5181 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5183 HtpState
*htp_state
= NULL
;
5192 double-decode-path: yes\n\
5193 double-decode-query: yes\n\
5196 ConfCreateContextBackup();
5198 HtpConfigCreateBackup();
5199 ConfYamlLoadString(input
, strlen(input
));
5201 const char *addr
= "4.3.2.1";
5202 memset(&ssn
, 0, sizeof(ssn
));
5204 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5208 f
->proto
= IPPROTO_TCP
;
5209 f
->alproto
= ALPROTO_HTTP1
;
5211 StreamTcpInitConfig(true);
5214 for (u
= 0; u
< httplen1
; u
++) {
5217 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5218 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5219 else flags
= STREAM_TOSERVER
;
5222 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5224 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5232 htp_state
= f
->alstate
;
5233 if (htp_state
== NULL
) {
5234 printf("no http state: ");
5238 uint8_t ref1
[] = "/abc/def";
5239 size_t reflen
= sizeof(ref1
) - 1;
5241 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5244 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5245 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5246 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5247 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5249 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5253 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5254 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5256 printf("normalized uri \"");
5257 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5259 PrintRawUriFp(stdout
, ref1
, reflen
);
5265 uint8_t ref2
[] = "/abc/def?ghi/jkl";
5266 reflen
= sizeof(ref2
) - 1;
5268 tx
= HTPStateGetTx(htp_state
, 1);
5271 tx_ud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
5272 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5273 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5274 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5276 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5280 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref2
,
5281 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5283 printf("normalized uri \"");
5284 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5286 PrintRawUriFp(stdout
, ref2
, reflen
);
5295 if (alp_tctx
!= NULL
)
5296 AppLayerParserThreadCtxFree(alp_tctx
);
5299 ConfRestoreContextBackup();
5300 HtpConfigRestoreBackup();
5302 StreamTcpFreeConfig(true);
5307 /** \test Test http:// in query profile IDS
5309 static int HTPParserDecodingTest04(void)
5313 uint8_t httpbuf1
[] =
5314 "GET /abc/def?a=http://www.abc.com/ HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5315 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5317 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5319 HtpState
*htp_state
= NULL
;
5328 double-decode-path: yes\n\
5329 double-decode-query: yes\n\
5332 ConfCreateContextBackup();
5334 HtpConfigCreateBackup();
5335 ConfYamlLoadString(input
, strlen(input
));
5337 const char *addr
= "4.3.2.1";
5338 memset(&ssn
, 0, sizeof(ssn
));
5340 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5344 f
->proto
= IPPROTO_TCP
;
5345 f
->alproto
= ALPROTO_HTTP1
;
5347 StreamTcpInitConfig(true);
5350 for (u
= 0; u
< httplen1
; u
++) {
5353 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5354 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5355 else flags
= STREAM_TOSERVER
;
5358 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5360 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5368 htp_state
= f
->alstate
;
5369 if (htp_state
== NULL
) {
5370 printf("no http state: ");
5374 uint8_t ref1
[] = "/abc/def?a=http://www.abc.com/";
5375 size_t reflen
= sizeof(ref1
) - 1;
5377 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5380 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5381 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5382 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5383 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5385 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5389 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5390 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5392 printf("normalized uri \"");
5393 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5395 PrintRawUriFp(stdout
, ref1
, reflen
);
5404 if (alp_tctx
!= NULL
)
5405 AppLayerParserThreadCtxFree(alp_tctx
);
5408 ConfRestoreContextBackup();
5409 HtpConfigRestoreBackup();
5411 StreamTcpFreeConfig(true);
5416 /** \test Test \ char in query profile IDS. Bug 739
5418 static int HTPParserDecodingTest05(void)
5422 uint8_t httpbuf1
[] =
5423 "GET /index?id=\\\"<script>alert(document.cookie)</script> HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5424 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5426 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5428 HtpState
*htp_state
= NULL
;
5437 double-decode-path: yes\n\
5438 double-decode-query: yes\n\
5441 ConfCreateContextBackup();
5443 HtpConfigCreateBackup();
5444 ConfYamlLoadString(input
, strlen(input
));
5446 const char *addr
= "4.3.2.1";
5447 memset(&ssn
, 0, sizeof(ssn
));
5449 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5453 f
->proto
= IPPROTO_TCP
;
5454 f
->alproto
= ALPROTO_HTTP1
;
5456 StreamTcpInitConfig(true);
5459 for (u
= 0; u
< httplen1
; u
++) {
5462 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5463 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5464 else flags
= STREAM_TOSERVER
;
5467 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5469 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5477 htp_state
= f
->alstate
;
5478 if (htp_state
== NULL
) {
5479 printf("no http state: ");
5483 uint8_t ref1
[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
5484 size_t reflen
= sizeof(ref1
) - 1;
5486 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5489 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5490 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5491 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5492 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5494 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5498 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5499 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5501 printf("normalized uri \"");
5502 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5504 PrintRawUriFp(stdout
, ref1
, reflen
);
5513 if (alp_tctx
!= NULL
)
5514 AppLayerParserThreadCtxFree(alp_tctx
);
5517 ConfRestoreContextBackup();
5518 HtpConfigRestoreBackup();
5520 StreamTcpFreeConfig(true);
5525 /** \test Test + char in query. Bug 1035
5527 static int HTPParserDecodingTest06(void)
5531 uint8_t httpbuf1
[] =
5532 "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5533 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5535 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5537 HtpState
*htp_state
= NULL
;
5546 double-decode-path: yes\n\
5547 double-decode-query: yes\n\
5550 ConfCreateContextBackup();
5552 HtpConfigCreateBackup();
5553 ConfYamlLoadString(input
, strlen(input
));
5555 const char *addr
= "4.3.2.1";
5556 memset(&ssn
, 0, sizeof(ssn
));
5558 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5562 f
->proto
= IPPROTO_TCP
;
5563 f
->alproto
= ALPROTO_HTTP1
;
5565 StreamTcpInitConfig(true);
5568 for (u
= 0; u
< httplen1
; u
++) {
5571 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5572 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5573 else flags
= STREAM_TOSERVER
;
5576 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5578 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5586 htp_state
= f
->alstate
;
5587 if (htp_state
== NULL
) {
5588 printf("no http state: ");
5592 uint8_t ref1
[] = "/put.php?ip=1.2.3.4&port=+6000";
5593 size_t reflen
= sizeof(ref1
) - 1;
5595 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5598 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5599 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5600 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5601 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5603 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5607 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5608 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5610 printf("normalized uri \"");
5611 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5613 PrintRawUriFp(stdout
, ref1
, reflen
);
5622 if (alp_tctx
!= NULL
)
5623 AppLayerParserThreadCtxFree(alp_tctx
);
5626 ConfRestoreContextBackup();
5627 HtpConfigRestoreBackup();
5629 StreamTcpFreeConfig(true);
5634 /** \test Test + char in query. Bug 1035
5636 static int HTPParserDecodingTest07(void)
5640 uint8_t httpbuf1
[] =
5641 "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5642 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5644 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5646 HtpState
*htp_state
= NULL
;
5655 double-decode-path: yes\n\
5656 double-decode-query: yes\n\
5657 query-plusspace-decode: yes\n\
5660 ConfCreateContextBackup();
5662 HtpConfigCreateBackup();
5663 ConfYamlLoadString(input
, strlen(input
));
5665 const char *addr
= "4.3.2.1";
5666 memset(&ssn
, 0, sizeof(ssn
));
5668 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5672 f
->proto
= IPPROTO_TCP
;
5673 f
->alproto
= ALPROTO_HTTP1
;
5675 StreamTcpInitConfig(true);
5678 for (u
= 0; u
< httplen1
; u
++) {
5681 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5682 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5683 else flags
= STREAM_TOSERVER
;
5686 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5688 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5696 htp_state
= f
->alstate
;
5697 if (htp_state
== NULL
) {
5698 printf("no http state: ");
5702 uint8_t ref1
[] = "/put.php?ip=1.2.3.4&port= 6000";
5703 size_t reflen
= sizeof(ref1
) - 1;
5705 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5708 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5709 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5710 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5711 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5713 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5717 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5718 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5720 printf("normalized uri \"");
5721 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5723 PrintRawUriFp(stdout
, ref1
, reflen
);
5732 if (alp_tctx
!= NULL
)
5733 AppLayerParserThreadCtxFree(alp_tctx
);
5736 ConfRestoreContextBackup();
5737 HtpConfigRestoreBackup();
5739 StreamTcpFreeConfig(true);
5744 /** \test Test 'proxy' URI normalization. Ticket 1008
5746 static int HTPParserDecodingTest08(void)
5750 uint8_t httpbuf1
[] =
5751 "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5752 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5754 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5756 HtpState
*htp_state
= NULL
;
5767 ConfCreateContextBackup();
5769 HtpConfigCreateBackup();
5770 ConfYamlLoadString(input
, strlen(input
));
5772 const char *addr
= "4.3.2.1";
5773 memset(&ssn
, 0, sizeof(ssn
));
5775 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5779 f
->proto
= IPPROTO_TCP
;
5780 f
->alproto
= ALPROTO_HTTP1
;
5782 StreamTcpInitConfig(true);
5785 for (u
= 0; u
< httplen1
; u
++) {
5788 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5789 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5790 else flags
= STREAM_TOSERVER
;
5793 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5795 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5803 htp_state
= f
->alstate
;
5804 if (htp_state
== NULL
) {
5805 printf("no http state: ");
5809 uint8_t ref1
[] = "/blah/";
5810 size_t reflen
= sizeof(ref1
) - 1;
5812 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5815 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5816 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5817 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5818 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5820 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5824 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5825 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5827 printf("normalized uri \"");
5828 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5830 PrintRawUriFp(stdout
, ref1
, reflen
);
5839 if (alp_tctx
!= NULL
)
5840 AppLayerParserThreadCtxFree(alp_tctx
);
5843 ConfRestoreContextBackup();
5844 HtpConfigRestoreBackup();
5846 StreamTcpFreeConfig(true);
5851 /** \test Test 'proxy' URI normalization. Ticket 1008
5853 static int HTPParserDecodingTest09(void)
5857 uint8_t httpbuf1
[] =
5858 "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5859 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
5861 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
5863 HtpState
*htp_state
= NULL
;
5872 uri-include-all: true\n\
5875 ConfCreateContextBackup();
5877 HtpConfigCreateBackup();
5878 ConfYamlLoadString(input
, strlen(input
));
5880 const char *addr
= "4.3.2.1";
5881 memset(&ssn
, 0, sizeof(ssn
));
5883 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", addr
, 1024, 80);
5887 f
->proto
= IPPROTO_TCP
;
5888 f
->alproto
= ALPROTO_HTTP1
;
5890 StreamTcpInitConfig(true);
5893 for (u
= 0; u
< httplen1
; u
++) {
5896 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
5897 else if (u
== (httplen1
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
5898 else flags
= STREAM_TOSERVER
;
5901 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, &httpbuf1
[u
], 1);
5903 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
5911 htp_state
= f
->alstate
;
5912 if (htp_state
== NULL
) {
5913 printf("no http state: ");
5917 uint8_t ref1
[] = "http://suricata-ids.org/blah/";
5918 size_t reflen
= sizeof(ref1
) - 1;
5920 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
5923 HtpTxUserData
*tx_ud
= (HtpTxUserData
*) htp_tx_get_user_data(tx
);
5924 if (tx_ud
!= NULL
&& tx_ud
->request_uri_normalized
!= NULL
) {
5925 if (reflen
!= bstr_len(tx_ud
->request_uri_normalized
)) {
5926 printf("normalized uri len should be %"PRIuMAX
", is %"PRIuMAX
,
5928 (uintmax_t)bstr_len(tx_ud
->request_uri_normalized
));
5932 if (memcmp(bstr_ptr(tx_ud
->request_uri_normalized
), ref1
,
5933 bstr_len(tx_ud
->request_uri_normalized
)) != 0)
5935 printf("normalized uri \"");
5936 PrintRawUriFp(stdout
, bstr_ptr(tx_ud
->request_uri_normalized
), bstr_len(tx_ud
->request_uri_normalized
));
5938 PrintRawUriFp(stdout
, ref1
, reflen
);
5947 if (alp_tctx
!= NULL
)
5948 AppLayerParserThreadCtxFree(alp_tctx
);
5951 ConfRestoreContextBackup();
5952 HtpConfigRestoreBackup();
5954 StreamTcpFreeConfig(true);
5959 /** \test BG box crash -- chunks are messed up. Observed for real. */
5960 static int HTPBodyReassemblyTest01(void)
5964 memset(&htud
, 0x00, sizeof(htud
));
5966 memset(&hstate
, 0x00, sizeof(hstate
));
5968 memset(&flow
, 0x00, sizeof(flow
));
5969 AppLayerParserState
*parser
= AppLayerParserStateAlloc();
5971 memset(&tx
, 0, sizeof(tx
));
5974 flow
.alparser
= parser
;
5976 uint8_t chunk1
[] = "--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
5977 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";
5979 int r
= HtpBodyAppendChunk(NULL
, &htud
.request_body
, chunk1
, sizeof(chunk1
)-1);
5981 r
= HtpBodyAppendChunk(NULL
, &htud
.request_body
, chunk2
, sizeof(chunk2
)-1);
5984 const uint8_t *chunks_buffer
= NULL
;
5985 uint32_t chunks_buffer_len
= 0;
5987 HtpRequestBodyReassemble(&htud
, &chunks_buffer
, &chunks_buffer_len
);
5988 if (chunks_buffer
== NULL
) {
5992 printf("REASSCHUNK START: \n");
5993 PrintRawDataFp(stdout
, chunks_buffer
, chunks_buffer_len
);
5994 printf("REASSCHUNK END: \n");
5997 HtpRequestBodyHandleMultipart(&hstate
, &htud
, &tx
, chunks_buffer
, chunks_buffer_len
);
5999 if (htud
.request_body
.content_len_so_far
!= 669) {
6000 printf("htud.request_body.content_len_so_far %"PRIu64
": ", htud
.request_body
.content_len_so_far
);
6004 if (hstate
.files_ts
!= NULL
)
6012 /** \test BG crash */
6013 static int HTPSegvTest01(void)
6017 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";
6018 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6026 double-decode-path: no\n\
6027 double-decode-query: no\n\
6028 request-body-limit: 0\n\
6029 response-body-limit: 0\n\
6032 ConfCreateContextBackup();
6034 HtpConfigCreateBackup();
6035 ConfYamlLoadString(input
, strlen(input
));
6039 HtpState
*http_state
= NULL
;
6040 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6042 memset(&ssn
, 0, sizeof(ssn
));
6044 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6048 f
->proto
= IPPROTO_TCP
;
6049 f
->alproto
= ALPROTO_HTTP1
;
6051 StreamTcpInitConfig(true);
6053 SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
6055 int r
= AppLayerParserParse(
6056 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6058 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6063 SCLogDebug("\n>>>> processing chunk 1 again <<<<\n");
6065 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, httpbuf1
, httplen1
);
6067 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6073 http_state
= f
->alstate
;
6074 if (http_state
== NULL
) {
6075 printf("no http state: ");
6080 AppLayerDecoderEvents
*decoder_events
= AppLayerParserGetDecoderEvents(f
->alparser
);
6081 if (decoder_events
!= NULL
) {
6082 printf("app events: ");
6089 if (alp_tctx
!= NULL
)
6090 AppLayerParserThreadCtxFree(alp_tctx
);
6093 ConfRestoreContextBackup();
6094 HtpConfigRestoreBackup();
6095 StreamTcpFreeConfig(true);
6100 /** \test Test really long request, this should result in HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6101 static int HTPParserTest14(void)
6112 double-decode-path: no\n\
6113 double-decode-query: no\n\
6114 request-body-limit: 0\n\
6115 response-body-limit: 0\n\
6117 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6118 FAIL_IF_NULL(alp_tctx
);
6120 memset(&ssn
, 0, sizeof(ssn
));
6122 ConfCreateContextBackup();
6124 HtpConfigCreateBackup();
6125 ConfYamlLoadString(input
, strlen(input
));
6128 char *httpbuf
= SCMalloc(len
);
6129 FAIL_IF_NULL(httpbuf
);
6130 memset(httpbuf
, 0x00, len
);
6132 /* create the request with a longer than 18k cookie */
6133 strlcpy(httpbuf
, "GET /blah/ HTTP/1.1\r\n"
6134 "Host: myhost.lan\r\n"
6135 "Connection: keep-alive\r\n"
6137 "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"
6138 "Referer: http://blah.lan/\r\n"
6139 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6141 size_t o
= strlen(httpbuf
);
6142 for ( ; o
< len
- 4; o
++) {
6145 httpbuf
[len
- 4] = '\r';
6146 httpbuf
[len
- 3] = '\n';
6147 httpbuf
[len
- 2] = '\r';
6148 httpbuf
[len
- 1] = '\n';
6150 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6153 f
->alproto
= ALPROTO_HTTP1
;
6154 f
->proto
= IPPROTO_TCP
;
6156 StreamTcpInitConfig(true);
6159 for (u
= 0; u
< len
; u
++) {
6162 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
6163 else if (u
== (len
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
6164 else flags
= STREAM_TOSERVER
;
6166 (void)AppLayerParserParse(
6167 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, (uint8_t *)&httpbuf
[u
], 1);
6169 HtpState
*htp_state
= f
->alstate
;
6170 FAIL_IF_NULL(htp_state
);
6172 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6174 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6175 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6177 void *txtmp
= AppLayerParserGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, f
->alstate
, 0);
6178 AppLayerDecoderEvents
*decoder_events
=
6179 AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP1
, txtmp
);
6180 FAIL_IF_NULL(decoder_events
);
6182 FAIL_IF(decoder_events
->events
[0] != HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG
);
6184 AppLayerParserThreadCtxFree(alp_tctx
);
6185 StreamTcpFreeConfig(true);
6190 ConfRestoreContextBackup();
6191 HtpConfigRestoreBackup();
6195 /** \test Test really long request (same as HTPParserTest14), now with config
6196 * update to allow it */
6197 static int HTPParserTest15(void)
6201 char *httpbuf
= NULL
;
6204 HtpState
*htp_state
= NULL
;
6213 double-decode-path: no\n\
6214 double-decode-query: no\n\
6215 request-body-limit: 0\n\
6216 response-body-limit: 0\n\
6217 meta-field-limit: 20000\n\
6219 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6221 memset(&ssn
, 0, sizeof(ssn
));
6223 ConfCreateContextBackup();
6225 HtpConfigCreateBackup();
6226 ConfYamlLoadString(input
, strlen(input
));
6229 httpbuf
= SCMalloc(len
);
6230 if (unlikely(httpbuf
== NULL
))
6232 memset(httpbuf
, 0x00, len
);
6234 /* create the request with a longer than 18k cookie */
6235 strlcpy(httpbuf
, "GET /blah/ HTTP/1.1\r\n"
6236 "Host: myhost.lan\r\n"
6237 "Connection: keep-alive\r\n"
6239 "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"
6240 "Referer: http://blah.lan/\r\n"
6241 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6243 size_t o
= strlen(httpbuf
);
6244 for ( ; o
< len
- 4; o
++) {
6247 httpbuf
[len
- 4] = '\r';
6248 httpbuf
[len
- 3] = '\n';
6249 httpbuf
[len
- 2] = '\r';
6250 httpbuf
[len
- 1] = '\n';
6252 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6256 f
->proto
= IPPROTO_TCP
;
6257 f
->alproto
= ALPROTO_HTTP1
;
6259 StreamTcpInitConfig(true);
6262 for (u
= 0; u
< len
; u
++) {
6265 if (u
== 0) flags
= STREAM_TOSERVER
|STREAM_START
;
6266 else if (u
== (len
- 1)) flags
= STREAM_TOSERVER
|STREAM_EOF
;
6267 else flags
= STREAM_TOSERVER
;
6270 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, (uint8_t *)&httpbuf
[u
], 1);
6272 printf("toserver chunk %" PRIu32
" returned %" PRId32
", expected"
6279 htp_state
= f
->alstate
;
6280 if (htp_state
== NULL
) {
6281 printf("no http state: ");
6285 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6286 if (tx
== NULL
|| tx
->request_method_number
!= HTP_M_GET
|| tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
)
6288 printf("expected method M_GET and got %s: , expected protocol "
6289 "HTTP/1.1 and got %s \n", bstr_util_strdup_to_c(tx
->request_method
),
6290 bstr_util_strdup_to_c(tx
->request_protocol
));
6295 void *txtmp
= AppLayerParserGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, f
->alstate
, 0);
6296 AppLayerDecoderEvents
*decoder_events
=
6297 AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP1
, txtmp
);
6298 if (decoder_events
!= NULL
) {
6299 printf("app events: ");
6307 if (alp_tctx
!= NULL
)
6308 AppLayerParserThreadCtxFree(alp_tctx
);
6309 StreamTcpFreeConfig(true);
6311 if (httpbuf
!= NULL
)
6315 ConfRestoreContextBackup();
6316 HtpConfigRestoreBackup();
6320 /** \test Test unusual delims in request line HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG */
6321 static int HTPParserTest16(void)
6326 HtpState
*htp_state
= NULL
;
6328 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6330 memset(&ssn
, 0, sizeof(ssn
));
6332 uint8_t httpbuf
[] = "GET\f/blah/\fHTTP/1.1\r\n"
6333 "Host: myhost.lan\r\n"
6334 "Connection: keep-alive\r\n"
6336 "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"
6337 "Referer: http://blah.lan/\r\n"
6338 "Accept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\n"
6339 "Cookie: blah\r\n\r\n";
6340 size_t len
= sizeof(httpbuf
) - 1;
6342 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6346 f
->proto
= IPPROTO_TCP
;
6347 f
->alproto
= ALPROTO_HTTP1
;
6349 StreamTcpInitConfig(true);
6351 uint8_t flags
= STREAM_TOSERVER
|STREAM_START
|STREAM_EOF
;
6354 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, flags
, (uint8_t *)httpbuf
, len
);
6356 printf("toserver chunk 1 returned %" PRId32
", expected 0: ", r
);
6362 htp_state
= f
->alstate
;
6363 if (htp_state
== NULL
) {
6364 printf("no http state: ");
6368 htp_tx_t
*tx
= HTPStateGetTx(htp_state
, 0);
6369 if (tx
== NULL
|| tx
->request_method_number
!= HTP_M_GET
|| tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
)
6371 printf("expected method M_GET and got %s: , expected protocol "
6372 "HTTP/1.1 and got %s \n", tx
? bstr_util_strdup_to_c(tx
->request_method
) : "tx null",
6373 tx
? bstr_util_strdup_to_c(tx
->request_protocol
) : "tx null");
6377 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
6378 //these events are disabled during fuzzing as they are too noisy and consume much resource
6380 void *txtmp
= AppLayerParserGetTx(IPPROTO_TCP
, ALPROTO_HTTP1
, f
->alstate
, 0);
6381 AppLayerDecoderEvents
*decoder_events
=
6382 AppLayerParserGetEventsByTx(IPPROTO_TCP
, ALPROTO_HTTP1
, txtmp
);
6383 if (decoder_events
== NULL
) {
6384 printf("no app events: ");
6390 if (decoder_events
->events
[0] != HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT
) {
6391 printf("HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT not set: ");
6395 if (decoder_events
->events
[1] != HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT
) {
6396 printf("HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT not set: ");
6403 if (alp_tctx
!= NULL
)
6404 AppLayerParserThreadCtxFree(alp_tctx
);
6405 StreamTcpFreeConfig(true);
6410 /** \test Test response not HTTP
6412 static int HTPParserTest20(void)
6415 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6416 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6417 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6418 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6419 uint8_t httpbuf2
[] = "NOTHTTP\r\nSOMEOTHERDATA";
6420 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6421 uint8_t httpbuf3
[] = "STILLNOTHTTP\r\nSOMEMOREOTHERDATA";
6422 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
6424 HtpState
*http_state
= NULL
;
6425 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6426 FAIL_IF_NULL(alp_tctx
);
6428 memset(&ssn
, 0, sizeof(ssn
));
6430 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6433 f
->proto
= IPPROTO_TCP
;
6434 f
->alproto
= ALPROTO_HTTP1
;
6436 StreamTcpInitConfig(true);
6438 int r
= AppLayerParserParse(
6439 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6442 r
= AppLayerParserParse(
6443 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6446 r
= AppLayerParserParse(
6447 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf3
, httplen3
);
6450 http_state
= f
->alstate
;
6451 FAIL_IF_NULL(http_state
);
6452 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6454 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6457 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6458 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6460 FAIL_IF(tx
->response_status_number
!= 0);
6461 FAIL_IF(tx
->response_protocol_number
!= -1);
6463 AppLayerParserThreadCtxFree(alp_tctx
);
6464 StreamTcpFreeConfig(true);
6469 /** \test Test response not HTTP
6471 static int HTPParserTest21(void)
6474 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6475 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6476 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6477 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6478 uint8_t httpbuf2
[] = "999 NOTHTTP REALLY\r\nSOMEOTHERDATA\r\n";
6479 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6480 uint8_t httpbuf3
[] = "STILLNOTHTTP\r\nSOMEMOREOTHERDATA";
6481 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
6483 HtpState
*http_state
= NULL
;
6484 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6485 FAIL_IF_NULL(alp_tctx
);
6487 memset(&ssn
, 0, sizeof(ssn
));
6489 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6492 f
->proto
= IPPROTO_TCP
;
6493 f
->alproto
= ALPROTO_HTTP1
;
6495 StreamTcpInitConfig(true);
6497 int r
= AppLayerParserParse(
6498 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6501 r
= AppLayerParserParse(
6502 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6505 r
= AppLayerParserParse(
6506 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf3
, httplen3
);
6509 http_state
= f
->alstate
;
6510 FAIL_IF_NULL(http_state
);
6511 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6513 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6516 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6517 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6519 FAIL_IF(tx
->response_status_number
!= 0);
6520 FAIL_IF(tx
->response_protocol_number
!= -1);
6522 AppLayerParserThreadCtxFree(alp_tctx
);
6523 StreamTcpFreeConfig(true);
6528 /** \test Test response not HTTP
6530 static int HTPParserTest22(void)
6533 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6534 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6535 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6536 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6537 uint8_t httpbuf2
[] = "\r\n0000=0000000/ASDF3_31.zip, 456723\r\n"
6538 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6539 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6541 HtpState
*http_state
= NULL
;
6542 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6543 FAIL_IF_NULL(alp_tctx
);
6545 memset(&ssn
, 0, sizeof(ssn
));
6547 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6550 f
->proto
= IPPROTO_TCP
;
6551 f
->alproto
= ALPROTO_HTTP1
;
6553 StreamTcpInitConfig(true);
6555 int r
= AppLayerParserParse(
6556 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6559 r
= AppLayerParserParse(
6560 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6563 http_state
= f
->alstate
;
6564 FAIL_IF_NULL(http_state
);
6565 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6567 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6570 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6571 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6573 FAIL_IF(tx
->response_status_number
!= -0);
6574 FAIL_IF(tx
->response_protocol_number
!= -1);
6576 AppLayerParserThreadCtxFree(alp_tctx
);
6577 StreamTcpFreeConfig(true);
6582 /** \test Test response not HTTP
6584 static int HTPParserTest23(void)
6587 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6588 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6589 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6590 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6591 uint8_t httpbuf2
[] = "HTTP0000=0000000/ASDF3_31.zip, 456723\r\n"
6592 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6593 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6595 HtpState
*http_state
= NULL
;
6596 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6597 FAIL_IF_NULL(alp_tctx
);
6599 memset(&ssn
, 0, sizeof(ssn
));
6601 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6604 f
->proto
= IPPROTO_TCP
;
6605 f
->alproto
= ALPROTO_HTTP1
;
6607 StreamTcpInitConfig(true);
6609 int r
= AppLayerParserParse(
6610 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6613 r
= AppLayerParserParse(
6614 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6617 http_state
= f
->alstate
;
6618 FAIL_IF_NULL(http_state
);
6619 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6621 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6624 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6625 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6627 FAIL_IF(tx
->response_status_number
!= -1);
6628 FAIL_IF(tx
->response_protocol_number
!= -2);
6630 AppLayerParserThreadCtxFree(alp_tctx
);
6631 StreamTcpFreeConfig(true);
6636 /** \test Test response not HTTP
6638 static int HTPParserTest24(void)
6641 uint8_t httpbuf1
[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
6642 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
6643 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
6644 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6645 uint8_t httpbuf2
[] = "HTTP/1.0 0000=0000000/ASDF3_31.zip, 456723\r\n"
6646 "AAAAAA_0000=0000000/AAAAAAAA.zip,46725\r\n";
6647 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6649 HtpState
*http_state
= NULL
;
6650 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6651 FAIL_IF_NULL(alp_tctx
);
6653 memset(&ssn
, 0, sizeof(ssn
));
6655 f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6658 f
->proto
= IPPROTO_TCP
;
6659 f
->alproto
= ALPROTO_HTTP1
;
6661 StreamTcpInitConfig(true);
6663 int r
= AppLayerParserParse(
6664 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
, httpbuf1
, httplen1
);
6667 r
= AppLayerParserParse(
6668 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
, httpbuf2
, httplen2
);
6671 http_state
= f
->alstate
;
6672 FAIL_IF_NULL(http_state
);
6673 htp_tx_t
*tx
= HTPStateGetTx(http_state
, 0);
6675 htp_header_t
*h
= htp_table_get_index(tx
->request_headers
, 0, NULL
);
6678 FAIL_IF(tx
->request_method_number
!= HTP_M_GET
);
6679 FAIL_IF(tx
->request_protocol_number
!= HTP_PROTOCOL_1_1
);
6681 FAIL_IF(tx
->response_status_number
!= -1);
6682 FAIL_IF(tx
->response_protocol_number
!= HTP_PROTOCOL_1_0
);
6684 AppLayerParserThreadCtxFree(alp_tctx
);
6685 StreamTcpFreeConfig(true);
6690 /** \test multi transactions and cleanup */
6691 static int HTPParserTest25(void)
6693 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6694 FAIL_IF_NULL(alp_tctx
);
6696 StreamTcpInitConfig(true);
6698 memset(&ssn
, 0, sizeof(ssn
));
6700 Flow
*f
= UTHBuildFlow(AF_INET
, "1.2.3.4", "1.2.3.5", 1024, 80);
6703 f
->proto
= IPPROTO_TCP
;
6704 f
->alproto
= ALPROTO_HTTP1
;
6706 const char *str
= "GET / HTTP/1.1\r\nHost: www.google.com\r\nUser-Agent: Suricata/1.0\r\n\r\n";
6707 int r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_START
,
6708 (uint8_t *)str
, strlen(str
));
6709 FAIL_IF_NOT(r
== 0);
6710 r
= AppLayerParserParse(
6711 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6712 FAIL_IF_NOT(r
== 0);
6713 r
= AppLayerParserParse(
6714 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6715 FAIL_IF_NOT(r
== 0);
6716 r
= AppLayerParserParse(
6717 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6718 FAIL_IF_NOT(r
== 0);
6719 r
= AppLayerParserParse(
6720 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6721 FAIL_IF_NOT(r
== 0);
6722 r
= AppLayerParserParse(
6723 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6724 FAIL_IF_NOT(r
== 0);
6725 r
= AppLayerParserParse(
6726 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6727 FAIL_IF_NOT(r
== 0);
6728 r
= AppLayerParserParse(
6729 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, (uint8_t *)str
, strlen(str
));
6730 FAIL_IF_NOT(r
== 0);
6732 str
= "HTTP 1.1 200 OK\r\nServer: Suricata/1.0\r\nContent-Length: 8\r\n\r\nSuricata";
6733 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_START
,
6734 (uint8_t *)str
, strlen(str
));
6735 FAIL_IF_NOT(r
== 0);
6736 r
= AppLayerParserParse(
6737 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6738 FAIL_IF_NOT(r
== 0);
6739 r
= AppLayerParserParse(
6740 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6741 FAIL_IF_NOT(r
== 0);
6742 r
= AppLayerParserParse(
6743 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6744 FAIL_IF_NOT(r
== 0);
6745 r
= AppLayerParserParse(
6746 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6747 FAIL_IF_NOT(r
== 0);
6748 r
= AppLayerParserParse(
6749 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6750 FAIL_IF_NOT(r
== 0);
6751 r
= AppLayerParserParse(
6752 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6753 FAIL_IF_NOT(r
== 0);
6754 r
= AppLayerParserParse(
6755 NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, (uint8_t *)str
, strlen(str
));
6756 FAIL_IF_NOT(r
== 0);
6758 AppLayerParserTransactionsCleanup(f
);
6761 UTHAppLayerParserStateGetIds(f
->alparser
, &ret
[0], &ret
[1], &ret
[2], &ret
[3]);
6762 FAIL_IF_NOT(ret
[0] == 8); // inspect_id[0]
6763 FAIL_IF_NOT(ret
[1] == 8); // inspect_id[1]
6764 FAIL_IF_NOT(ret
[2] == 8); // log_id
6765 FAIL_IF_NOT(ret
[3] == 8); // min_id
6767 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOSERVER
| STREAM_EOF
,
6768 (uint8_t *)str
, strlen(str
));
6769 FAIL_IF_NOT(r
== 0);
6770 AppLayerParserTransactionsCleanup(f
);
6772 UTHAppLayerParserStateGetIds(f
->alparser
, &ret
[0], &ret
[1], &ret
[2], &ret
[3]);
6773 FAIL_IF_NOT(ret
[0] == 8); // inspect_id[0] not updated by ..Cleanup() until full tx is done
6774 FAIL_IF_NOT(ret
[1] == 8); // inspect_id[1]
6775 FAIL_IF_NOT(ret
[2] == 8); // log_id
6776 FAIL_IF_NOT(ret
[3] == 8); // min_id
6778 r
= AppLayerParserParse(NULL
, alp_tctx
, f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
| STREAM_EOF
,
6779 (uint8_t *)str
, strlen(str
));
6780 FAIL_IF_NOT(r
== 0);
6781 AppLayerParserTransactionsCleanup(f
);
6783 UTHAppLayerParserStateGetIds(f
->alparser
, &ret
[0], &ret
[1], &ret
[2], &ret
[3]);
6784 FAIL_IF_NOT(ret
[0] == 9); // inspect_id[0]
6785 FAIL_IF_NOT(ret
[1] == 9); // inspect_id[1]
6786 FAIL_IF_NOT(ret
[2] == 9); // log_id
6787 FAIL_IF_NOT(ret
[3] == 9); // min_id
6789 HtpState
*http_state
= f
->alstate
;
6790 FAIL_IF_NULL(http_state
);
6792 AppLayerParserThreadCtxFree(alp_tctx
);
6793 StreamTcpFreeConfig(true);
6799 static int HTPParserTest26(void)
6808 request-body-limit: 1\n\
6809 response-body-limit: 1\n\
6811 ConfCreateContextBackup();
6813 HtpConfigCreateBackup();
6814 ConfYamlLoadString(input
, strlen(input
));
6820 DetectEngineCtx
*de_ctx
= NULL
;
6821 DetectEngineThreadCtx
*det_ctx
= NULL
;
6823 uint8_t httpbuf1
[] = "GET /alice.txt HTTP/1.1\r\n\r\n";
6824 uint32_t httplen1
= sizeof(httpbuf1
) - 1; /* minus the \0 */
6825 uint8_t httpbuf2
[] = "HTTP/1.1 200 OK\r\n"
6826 "Content-Type: text/plain\r\n"
6827 "Content-Length: 228\r\n\r\n"
6828 "Alice was beginning to get very tired of sitting by her sister on the bank."
6829 "Alice was beginning to get very tired of sitting by her sister on the bank.";
6830 uint32_t httplen2
= sizeof(httpbuf2
) - 1; /* minus the \0 */
6831 uint8_t httpbuf3
[] = "Alice was beginning to get very tired of sitting by her sister on the bank.\r\n\r\n";
6832 uint32_t httplen3
= sizeof(httpbuf3
) - 1; /* minus the \0 */
6834 HtpState
*http_state
= NULL
;
6835 AppLayerParserThreadCtx
*alp_tctx
= AppLayerParserThreadCtxAlloc();
6836 FAIL_IF_NULL(alp_tctx
);
6838 memset(&th_v
, 0, sizeof(th_v
));
6839 memset(&f
, 0, sizeof(f
));
6840 memset(&ssn
, 0, sizeof(ssn
));
6842 p1
= UTHBuildPacket(NULL
, 0, IPPROTO_TCP
);
6843 p2
= UTHBuildPacket(NULL
, 0, IPPROTO_TCP
);
6845 FLOW_INITIALIZE(&f
);
6846 f
.protoctx
= (void *)&ssn
;
6847 f
.proto
= IPPROTO_TCP
;
6848 f
.flags
|= FLOW_IPV4
;
6851 p1
->flowflags
|= FLOW_PKT_TOSERVER
;
6852 p1
->flowflags
|= FLOW_PKT_ESTABLISHED
;
6853 p1
->flags
|= PKT_HAS_FLOW
|PKT_STREAM_EST
;
6855 p2
->flowflags
|= FLOW_PKT_TOCLIENT
;
6856 p2
->flowflags
|= FLOW_PKT_ESTABLISHED
;
6857 p2
->flags
|= PKT_HAS_FLOW
|PKT_STREAM_EST
;
6858 f
.alproto
= ALPROTO_HTTP1
;
6860 StreamTcpInitConfig(true);
6862 de_ctx
= DetectEngineCtxInit();
6863 FAIL_IF_NULL(de_ctx
);
6865 de_ctx
->flags
|= DE_QUIET
;
6867 de_ctx
->sig_list
= SigInit(de_ctx
,"alert http any any -> any any "
6868 "(filestore; sid:1; rev:1;)");
6869 FAIL_IF_NULL(de_ctx
->sig_list
);
6871 SigGroupBuild(de_ctx
);
6872 DetectEngineThreadCtxInit(&th_v
, (void *)de_ctx
, (void *)&det_ctx
);
6874 int r
= AppLayerParserParse(
6875 &th_v
, alp_tctx
, &f
, ALPROTO_HTTP1
, STREAM_TOSERVER
, httpbuf1
, httplen1
);
6878 http_state
= f
.alstate
;
6879 FAIL_IF_NULL(http_state
);
6882 SigMatchSignatures(&th_v
, de_ctx
, det_ctx
, p1
);
6884 FAIL_IF((PacketAlertCheck(p1
, 1)));
6887 SigMatchSignatures(&th_v
, de_ctx
, det_ctx
, p1
);
6889 FAIL_IF((PacketAlertCheck(p1
, 1)));
6891 r
= AppLayerParserParse(
6892 &th_v
, alp_tctx
, &f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, httpbuf2
, httplen2
);
6895 http_state
= f
.alstate
;
6896 FAIL_IF_NULL(http_state
);
6899 SigMatchSignatures(&th_v
, de_ctx
, det_ctx
, p2
);
6901 FAIL_IF(!(PacketAlertCheck(p2
, 1)));
6903 r
= AppLayerParserParse(
6904 &th_v
, alp_tctx
, &f
, ALPROTO_HTTP1
, STREAM_TOCLIENT
, httpbuf3
, httplen3
);
6907 http_state
= f
.alstate
;
6908 FAIL_IF_NULL(http_state
);
6910 FileContainer
*ffc
= HTPStateGetFiles(http_state
, STREAM_TOCLIENT
);
6913 File
*ptr
= ffc
->head
;
6914 FAIL_IF(ptr
->state
!= FILE_STATE_CLOSED
);
6916 AppLayerParserThreadCtxFree(alp_tctx
);
6917 DetectEngineThreadCtxDeinit(&th_v
, (void *)det_ctx
);
6918 DetectEngineCtxFree(de_ctx
);
6919 StreamTcpFreeConfig(true);
6923 UTHFreePackets(&p1
, 1);
6924 UTHFreePackets(&p2
, 1);
6926 ConfRestoreContextBackup();
6927 HtpConfigRestoreBackup();
6931 static int HTPParserTest27(void)
6934 memset(&cfg
, 0, sizeof(cfg
));
6935 cfg
.body_limit
= 1500;
6936 FileReassemblyDepthEnable(2000);
6938 uint32_t len
= 1000;
6940 HtpTxUserData
*tx_ud
= SCMalloc(sizeof(HtpTxUserData
));
6941 FAIL_IF_NULL(tx_ud
);
6943 tx_ud
->tsflags
|= HTP_STREAM_DEPTH_SET
;
6944 tx_ud
->request_body
.content_len_so_far
= 2500;
6946 FAIL_IF(AppLayerHtpCheckDepth(&cfg
, &tx_ud
->request_body
, tx_ud
->tsflags
));
6948 len
= AppLayerHtpComputeChunkLength(tx_ud
->request_body
.content_len_so_far
,
6950 FileReassemblyDepth(),
6953 FAIL_IF(len
!= 1000);
6961 * \brief Register the Unit tests for the HTTP protocol
6963 static void HTPParserRegisterTests(void)
6965 UtRegisterTest("HTPParserTest01", HTPParserTest01
);
6966 UtRegisterTest("HTPParserTest01a", HTPParserTest01a
);
6967 UtRegisterTest("HTPParserTest01b", HTPParserTest01b
);
6968 UtRegisterTest("HTPParserTest01c", HTPParserTest01c
);
6969 UtRegisterTest("HTPParserTest02", HTPParserTest02
);
6970 UtRegisterTest("HTPParserTest03", HTPParserTest03
);
6971 UtRegisterTest("HTPParserTest04", HTPParserTest04
);
6972 UtRegisterTest("HTPParserTest05", HTPParserTest05
);
6973 UtRegisterTest("HTPParserTest06", HTPParserTest06
);
6974 UtRegisterTest("HTPParserTest07", HTPParserTest07
);
6975 UtRegisterTest("HTPParserTest08", HTPParserTest08
);
6976 UtRegisterTest("HTPParserTest09", HTPParserTest09
);
6977 UtRegisterTest("HTPParserTest10", HTPParserTest10
);
6978 UtRegisterTest("HTPParserTest11", HTPParserTest11
);
6979 UtRegisterTest("HTPParserTest12", HTPParserTest12
);
6980 UtRegisterTest("HTPParserTest13", HTPParserTest13
);
6981 UtRegisterTest("HTPParserConfigTest01", HTPParserConfigTest01
);
6982 UtRegisterTest("HTPParserConfigTest02", HTPParserConfigTest02
);
6983 UtRegisterTest("HTPParserConfigTest03", HTPParserConfigTest03
);
6984 #if 0 /* disabled when we upgraded to libhtp 0.5.x */
6985 UtRegisterTest("HTPParserConfigTest04", HTPParserConfigTest04
, 1);
6988 UtRegisterTest("HTPParserDecodingTest01", HTPParserDecodingTest01
);
6989 UtRegisterTest("HTPParserDecodingTest02", HTPParserDecodingTest02
);
6990 UtRegisterTest("HTPParserDecodingTest03", HTPParserDecodingTest03
);
6991 UtRegisterTest("HTPParserDecodingTest04", HTPParserDecodingTest04
);
6992 UtRegisterTest("HTPParserDecodingTest05", HTPParserDecodingTest05
);
6993 UtRegisterTest("HTPParserDecodingTest06", HTPParserDecodingTest06
);
6994 UtRegisterTest("HTPParserDecodingTest07", HTPParserDecodingTest07
);
6995 UtRegisterTest("HTPParserDecodingTest08", HTPParserDecodingTest08
);
6996 UtRegisterTest("HTPParserDecodingTest09", HTPParserDecodingTest09
);
6998 UtRegisterTest("HTPBodyReassemblyTest01", HTPBodyReassemblyTest01
);
7000 UtRegisterTest("HTPSegvTest01", HTPSegvTest01
);
7002 UtRegisterTest("HTPParserTest14", HTPParserTest14
);
7003 UtRegisterTest("HTPParserTest15", HTPParserTest15
);
7004 UtRegisterTest("HTPParserTest16", HTPParserTest16
);
7005 UtRegisterTest("HTPParserTest20", HTPParserTest20
);
7006 UtRegisterTest("HTPParserTest21", HTPParserTest21
);
7007 UtRegisterTest("HTPParserTest22", HTPParserTest22
);
7008 UtRegisterTest("HTPParserTest23", HTPParserTest23
);
7009 UtRegisterTest("HTPParserTest24", HTPParserTest24
);
7010 UtRegisterTest("HTPParserTest25", HTPParserTest25
);
7011 UtRegisterTest("HTPParserTest26", HTPParserTest26
);
7012 UtRegisterTest("HTPParserTest27", HTPParserTest27
);
7014 HTPFileParserRegisterTests();
7015 HTPXFFParserRegisterTests();
7017 #endif /* UNITTESTS */