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