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