suricata
app-layer-htp.c
Go to the documentation of this file.
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 
103  { "UNKNOWN_ERROR",
105  { "GZIP_DECOMPRESSION_FAILED",
107  { "REQUEST_FIELD_MISSING_COLON",
109  { "RESPONSE_FIELD_MISSING_COLON",
111  { "INVALID_REQUEST_CHUNK_LEN",
113  { "INVALID_RESPONSE_CHUNK_LEN",
115  { "INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST",
117  { "INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE",
119  { "INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST",
121  { "INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE",
123  { "DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST",
125  { "DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE",
127  { "100_CONTINUE_ALREADY_SEEN",
129  { "UNABLE_TO_MATCH_RESPONSE_TO_REQUEST",
131  { "INVALID_SERVER_PORT_IN_REQUEST",
133  { "INVALID_AUTHORITY_PORT",
135  { "REQUEST_HEADER_INVALID",
137  { "RESPONSE_HEADER_INVALID",
139  { "MISSING_HOST_HEADER",
141  { "HOST_HEADER_AMBIGUOUS",
143  { "INVALID_REQUEST_FIELD_FOLDING",
145  { "INVALID_RESPONSE_FIELD_FOLDING",
147  { "REQUEST_FIELD_TOO_LONG",
149  { "RESPONSE_FIELD_TOO_LONG",
151  { "REQUEST_LINE_INVALID",
153  { "REQUEST_BODY_UNEXPECTED",
155  { "REQUEST_SERVER_PORT_TCP_PORT_MISMATCH",
157  { "REQUEST_URI_HOST_INVALID",
159  { "REQUEST_HEADER_HOST_INVALID",
161  { "REQUEST_AUTH_UNRECOGNIZED",
163  { "REQUEST_HEADER_REPETITION",
165  { "RESPONSE_HEADER_REPETITION",
167  { "DOUBLE_ENCODED_URI",
169  { "URI_DELIM_NON_COMPLIANT",
171  { "METHOD_DELIM_NON_COMPLIANT",
173  { "REQUEST_LINE_LEADING_WHITESPACE",
175  { "TOO_MANY_ENCODING_LAYERS",
177  { "ABNORMAL_CE_HEADER",
179  { "RESPONSE_MULTIPART_BYTERANGES",
181  { "RESPONSE_ABNORMAL_TRANSFER_ENCODING",
183  { "RESPONSE_CHUNKED_OLD_PROTO",
185  { "RESPONSE_INVALID_PROTOCOL",
187  { "RESPONSE_INVALID_STATUS",
189  { "REQUEST_LINE_INCOMPLETE",
191 
192  { "LZMA_MEMLIMIT_REACHED",
194  { "COMPRESSION_BOMB",
196 
197  /* suricata warnings/errors */
198  { "MULTIPART_GENERIC_ERROR",
200  { "MULTIPART_NO_FILEDATA",
202  { "MULTIPART_INVALID_HEADER",
204 
205  { "TOO_MANY_WARNINGS",
207 
208  { NULL, -1 },
209 };
210 
211 static void *HTPStateGetTx(void *alstate, uint64_t tx_id);
212 static int HTPStateGetAlstateProgress(void *tx, uint8_t direction);
213 static uint64_t HTPStateGetTxCnt(void *alstate);
214 #ifdef UNITTESTS
215 static void HTPParserRegisterTests(void);
216 #endif
217 
218 static inline uint64_t HtpGetActiveRequestTxID(HtpState *s)
219 {
220  uint64_t id = HTPStateGetTxCnt(s);
221  BUG_ON(id == 0);
222  return id - 1;
223 }
224 
225 static inline uint64_t HtpGetActiveResponseTxID(HtpState *s)
226 {
227  return s->transaction_cnt;
228 }
229 
230 #ifdef DEBUG
231 /**
232  * \internal
233  *
234  * \brief Lookup the HTP personality string from the numeric personality.
235  *
236  * \todo This needs to be a libhtp function.
237  */
238 static const char *HTPLookupPersonalityString(int p)
239 {
240 #define CASE_HTP_PERSONALITY_STRING(p) \
241  case HTP_SERVER_ ## p: return #p
242 
243  switch (p) {
244  CASE_HTP_PERSONALITY_STRING(MINIMAL);
245  CASE_HTP_PERSONALITY_STRING(GENERIC);
246  CASE_HTP_PERSONALITY_STRING(IDS);
247  CASE_HTP_PERSONALITY_STRING(IIS_4_0);
248  CASE_HTP_PERSONALITY_STRING(IIS_5_0);
249  CASE_HTP_PERSONALITY_STRING(IIS_5_1);
250  CASE_HTP_PERSONALITY_STRING(IIS_6_0);
251  CASE_HTP_PERSONALITY_STRING(IIS_7_0);
252  CASE_HTP_PERSONALITY_STRING(IIS_7_5);
253  CASE_HTP_PERSONALITY_STRING(APACHE_2);
254  }
255 
256  return NULL;
257 }
258 #endif /* DEBUG */
259 
260 /**
261  * \internal
262  *
263  * \brief Lookup the numeric HTP personality from a string.
264  *
265  * \todo This needs to be a libhtp function.
266  */
267 static int HTPLookupPersonality(const char *str)
268 {
269 #define IF_HTP_PERSONALITY_NUM(p) \
270  if (strcasecmp(#p, str) == 0) return HTP_SERVER_ ## p
271 
272  IF_HTP_PERSONALITY_NUM(MINIMAL);
273  IF_HTP_PERSONALITY_NUM(GENERIC);
275  IF_HTP_PERSONALITY_NUM(IIS_4_0);
276  IF_HTP_PERSONALITY_NUM(IIS_5_0);
277  IF_HTP_PERSONALITY_NUM(IIS_5_1);
278  IF_HTP_PERSONALITY_NUM(IIS_6_0);
279  IF_HTP_PERSONALITY_NUM(IIS_7_0);
280  IF_HTP_PERSONALITY_NUM(IIS_7_5);
281  IF_HTP_PERSONALITY_NUM(APACHE_2);
282  if (strcasecmp("TOMCAT_6_0", str) == 0) {
283  SCLogError(SC_WARN_OPTION_OBSOLETE, "Personality %s no "
284  "longer supported by libhtp.", str);
285  return -1;
286  } else if ((strcasecmp("APACHE", str) == 0) ||
287  (strcasecmp("APACHE_2_2", str) == 0))
288  {
289  SCLogWarning(SC_WARN_OPTION_OBSOLETE, "Personality %s no "
290  "longer supported by libhtp, failing back to "
291  "Apache2 personality.", str);
292  return HTP_SERVER_APACHE_2;
293  }
294 
295  return -1;
296 }
297 
298 static void HTPSetEvent(HtpState *s, HtpTxUserData *htud,
299  const uint8_t dir, const uint8_t e)
300 {
301  SCLogDebug("setting event %u", e);
302 
303  if (htud) {
305  s->events++;
306  return;
307  }
308 
309  const uint64_t tx_id = (dir == STREAM_TOSERVER) ?
310  HtpGetActiveRequestTxID(s) : HtpGetActiveResponseTxID(s);
311 
312  htp_tx_t *tx = HTPStateGetTx(s, tx_id);
313  if (tx == NULL && tx_id > 0)
314  tx = HTPStateGetTx(s, tx_id - 1);
315  if (tx != NULL) {
316  htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
317  if (htud != NULL) {
319  s->events++;
320  return;
321  }
322  }
323  SCLogDebug("couldn't set event %u", e);
324 }
325 
326 static AppLayerDecoderEvents *HTPGetEvents(void *tx)
327 {
328  SCLogDebug("get HTTP events for TX %p", tx);
329 
330  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
331  if (htud != NULL) {
332  SCLogDebug("has htud, htud->decoder_events %p", htud->decoder_events);
333  return htud->decoder_events;
334  }
335 
336  return NULL;
337 }
338 
339 /** \brief Function to allocates the HTTP state memory and also creates the HTTP
340  * connection parser to be used by the HTP library
341  */
342 static void *HTPStateAlloc(void *orig_state, AppProto proto_orig)
343 {
344  SCEnter();
345 
346  HtpState *s = HTPMalloc(sizeof(HtpState));
347  if (unlikely(s == NULL)) {
348  SCReturnPtr(NULL, "void");
349  }
350 
351  memset(s, 0x00, sizeof(HtpState));
352 
353 #ifdef DEBUG
354  SCMutexLock(&htp_state_mem_lock);
355  htp_state_memcnt++;
356  htp_state_memuse += sizeof(HtpState);
357  SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
358  SCMutexUnlock(&htp_state_mem_lock);
359 #endif
360 
361  SCReturnPtr((void *)s, "void");
362 }
363 
364 static void HtpTxUserDataFree(HtpState *state, HtpTxUserData *htud)
365 {
366  if (likely(htud)) {
367  HtpBodyFree(&htud->request_body);
368  HtpBodyFree(&htud->response_body);
369  bstr_free(htud->request_uri_normalized);
370  if (htud->request_headers_raw)
372  if (htud->response_headers_raw)
375  if (htud->boundary)
376  HTPFree(htud->boundary, htud->boundary_len);
377  if (htud->de_state != NULL) {
379  }
380  HTPFree(htud, sizeof(HtpTxUserData));
381  }
382 }
383 
384 /** \brief Function to frees the HTTP state memory and also frees the HTTP
385  * connection parser memory which was used by the HTP library
386  */
387 void HTPStateFree(void *state)
388 {
389  SCEnter();
390 
391  HtpState *s = (HtpState *)state;
392  if (s == NULL) {
393  SCReturn;
394  }
395 
396  /* free the connection parser memory used by HTP library */
397  if (s->connp != NULL) {
398  SCLogDebug("freeing HTP state");
399 
400  uint64_t tx_id;
401  uint64_t total_txs = HTPStateGetTxCnt(state);
402  /* free the list of body chunks */
403  if (s->conn != NULL) {
404  for (tx_id = 0; tx_id < total_txs; tx_id++) {
405  htp_tx_t *tx = HTPStateGetTx(s, tx_id);
406  if (tx != NULL) {
407  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
408  HtpTxUserDataFree(s, htud);
409  htp_tx_set_user_data(tx, NULL);
410  }
411  }
412  }
413  htp_connp_destroy_all(s->connp);
414  }
415 
418  HTPFree(s, sizeof(HtpState));
419 
420 #ifdef DEBUG
421  SCMutexLock(&htp_state_mem_lock);
422  htp_state_memcnt--;
423  htp_state_memuse -= sizeof(HtpState);
424  SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
425  SCMutexUnlock(&htp_state_mem_lock);
426 #endif
427 
428  SCReturn;
429 }
430 
431 /**
432  * \brief HTP transaction cleanup callback
433  *
434  * \warning We cannot actually free the transactions here. It seems that
435  * HTP only accepts freeing of transactions in the response callback.
436  */
437 static void HTPStateTransactionFree(void *state, uint64_t id)
438 {
439  SCEnter();
440 
441  HtpState *s = (HtpState *)state;
442 
443  SCLogDebug("state %p, id %"PRIu64, s, id);
444 
445  htp_tx_t *tx = HTPStateGetTx(s, id);
446  if (tx != NULL) {
447  /* This will remove obsolete body chunks */
448  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
449  HtpTxUserDataFree(s, htud);
450  htp_tx_set_user_data(tx, NULL);
451 
452  /* hack: even if libhtp considers the tx incomplete, we want to
453  * free it here. htp_tx_destroy however, will refuse to do this.
454  * As htp_tx_destroy_incomplete isn't available in the public API,
455  * we hack around it here. */
456  if (unlikely(!(
457  tx->request_progress == HTP_REQUEST_COMPLETE &&
458  tx->response_progress == HTP_RESPONSE_COMPLETE)))
459  {
460  tx->request_progress = HTP_REQUEST_COMPLETE;
461  tx->response_progress = HTP_RESPONSE_COMPLETE;
462  }
463  htp_tx_destroy(tx);
464  }
465 }
466 
467 /**
468  * \brief Sets a flag that informs the HTP app layer that some module in the
469  * engine needs the http request body data.
470  * \initonly
471  */
473 {
474  SCEnter();
475 
476  SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_BODY);
477  SCReturn;
478 }
479 
480 /**
481  * \brief Sets a flag that informs the HTP app layer that some module in the
482  * engine needs the http request body data.
483  * \initonly
484  */
486 {
487  SCEnter();
488 
489  SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_RESPONSE_BODY);
490  SCReturn;
491 }
492 
493 /**
494  * \brief Sets a flag that informs the HTP app layer that some module in the
495  * engine needs the http request multi part header.
496  *
497  * \initonly
498  */
499 static void AppLayerHtpNeedMultipartHeader(void)
500 {
501  SCEnter();
503 
504  SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_MULTIPART);
505  SCReturn;
506 }
507 
508 /**
509  * \brief Sets a flag that informs the HTP app layer that some module in the
510  * engine needs the http request file.
511  *
512  * \initonly
513  */
515 {
516  SCEnter();
517  AppLayerHtpNeedMultipartHeader();
520 
521  SC_ATOMIC_OR(htp_config_flags, HTP_REQUIRE_REQUEST_FILE);
522  SCReturn;
523 }
524 
525 static void AppLayerHtpSetStreamDepthFlag(void *tx, uint8_t flags)
526 {
527  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data((htp_tx_t *)tx);
528  if (tx_ud) {
529  if (flags & STREAM_TOCLIENT) {
530  tx_ud->tcflags |= HTP_STREAM_DEPTH_SET;
531  } else {
532  tx_ud->tsflags |= HTP_STREAM_DEPTH_SET;
533  }
534  }
535 }
536 
537 static bool AppLayerHtpCheckDepth(const HTPCfgDir *cfg, HtpBody *body, uint8_t flags)
538 {
539  if (flags & HTP_STREAM_DEPTH_SET) {
540  uint32_t stream_depth = FileReassemblyDepth();
541  if (body->content_len_so_far < (uint64_t)stream_depth || stream_depth == 0) {
542  return true;
543  }
544  } else {
545  if (cfg->body_limit == 0 || body->content_len_so_far < cfg->body_limit) {
546  return true;
547  }
548  }
549  return false;
550 }
551 
552 static uint32_t AppLayerHtpComputeChunkLength(uint64_t content_len_so_far, uint32_t body_limit,
553  uint32_t stream_depth, uint8_t flags, uint32_t data_len)
554 {
555  uint32_t chunk_len = 0;
556  if (!(flags & HTP_STREAM_DEPTH_SET) && body_limit > 0 &&
557  (content_len_so_far < (uint64_t)body_limit) &&
558  (content_len_so_far + (uint64_t)data_len) > body_limit)
559  {
560  chunk_len = body_limit - content_len_so_far;
561  } else if ((flags & HTP_STREAM_DEPTH_SET) && stream_depth > 0 &&
562  (content_len_so_far < (uint64_t)stream_depth) &&
563  (content_len_so_far + (uint64_t)data_len) > stream_depth)
564  {
565  chunk_len = stream_depth - content_len_so_far;
566  }
567  SCLogDebug("len %u", chunk_len);
568  return (chunk_len == 0 ? data_len : chunk_len);
569 }
570 
571 /* below error messages updated up to libhtp 0.5.7 (git 379632278b38b9a792183694a4febb9e0dbd1e7a) */
572 struct {
573  const char *msg;
574  int de;
575 } htp_errors[] = {
576  { "GZip decompressor: inflateInit2 failed", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
577  { "Request field invalid: colon missing", HTTP_DECODER_EVENT_REQUEST_FIELD_MISSING_COLON},
578  { "Response field invalid: missing colon", HTTP_DECODER_EVENT_RESPONSE_FIELD_MISSING_COLON},
579  { "Request chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_REQUEST_CHUNK_LEN},
580  { "Response chunk encoding: Invalid chunk length", HTTP_DECODER_EVENT_INVALID_RESPONSE_CHUNK_LEN},
581 /* { "Invalid T-E value in request", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_T_E
582  { "Invalid T-E value in response", HTTP_DECODER_EVENT_INVALID_TRANSFER_ENCODING_VALUE_IN_RESPONSE}, <- nothing to replace it */
583 /* { "Invalid C-L field in request", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_REQUEST}, <- tx flag HTP_REQUEST_INVALID_C_L */
584  { "Invalid C-L field in response", HTTP_DECODER_EVENT_INVALID_CONTENT_LENGTH_FIELD_IN_RESPONSE},
585  { "Already seen 100-Continue", HTTP_DECODER_EVENT_100_CONTINUE_ALREADY_SEEN},
586  { "Unable to match response to request", HTTP_DECODER_EVENT_UNABLE_TO_MATCH_RESPONSE_TO_REQUEST},
587  { "Invalid server port information in request", HTTP_DECODER_EVENT_INVALID_SERVER_PORT_IN_REQUEST},
588 /* { "Invalid authority port", HTTP_DECODER_EVENT_INVALID_AUTHORITY_PORT}, htp no longer returns this error */
589  { "Request buffer over", HTTP_DECODER_EVENT_REQUEST_FIELD_TOO_LONG},
590  { "Response buffer over", HTTP_DECODER_EVENT_RESPONSE_FIELD_TOO_LONG},
591  { "C-T multipart/byteranges in responses not supported", HTTP_DECODER_EVENT_RESPONSE_MULTIPART_BYTERANGES},
592  { "Compression bomb:", HTTP_DECODER_EVENT_COMPRESSION_BOMB},
593 };
594 
595 struct {
596  const char *msg;
597  int de;
598 } htp_warnings[] = {
599  { "GZip decompressor:", HTTP_DECODER_EVENT_GZIP_DECOMPRESSION_FAILED},
600  { "Request field invalid", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
601  { "Response field invalid", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
602  { "Request header name is not a token", HTTP_DECODER_EVENT_REQUEST_HEADER_INVALID},
603  { "Response header name is not a token", HTTP_DECODER_EVENT_RESPONSE_HEADER_INVALID},
604 /* { "Host information in request headers required by HTTP/1.1", HTTP_DECODER_EVENT_MISSING_HOST_HEADER}, <- tx flag HTP_HOST_MISSING
605  { "Host information ambiguous", HTTP_DECODER_EVENT_HOST_HEADER_AMBIGUOUS}, <- tx flag HTP_HOST_AMBIGUOUS */
606  { "Invalid request field folding", HTTP_DECODER_EVENT_INVALID_REQUEST_FIELD_FOLDING},
607  { "Invalid response field folding", HTTP_DECODER_EVENT_INVALID_RESPONSE_FIELD_FOLDING},
608  /* 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);
609  * luckily, "Request server port=" is unique */
610 /* { "Request server port number differs from the actual TCP port", HTTP_DECODER_EVENT_REQUEST_SERVER_PORT_TCP_PORT_MISMATCH}, */
612  { "Request line: URI contains non-compliant delimiter", HTTP_DECODER_EVENT_URI_DELIM_NON_COMPLIANT},
613  { "Request line: non-compliant delimiter between Method and URI", HTTP_DECODER_EVENT_METHOD_DELIM_NON_COMPLIANT},
614  { "Request line: leading whitespace", HTTP_DECODER_EVENT_REQUEST_LINE_LEADING_WHITESPACE},
615  { "Too many response content encoding layers", HTTP_DECODER_EVENT_TOO_MANY_ENCODING_LAYERS},
616  { "C-E gzip has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
617  { "C-E deflate has abnormal value", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
618  { "C-E unknown setting", HTTP_DECODER_EVENT_ABNORMAL_CE_HEADER},
619  { "Excessive request header repetitions", HTTP_DECODER_EVENT_REQUEST_HEADER_REPETITION},
620  { "Excessive response header repetitions", HTTP_DECODER_EVENT_RESPONSE_HEADER_REPETITION},
621  { "Transfer-encoding has abnormal chunked value", HTTP_DECODER_EVENT_RESPONSE_ABNORMAL_TRANSFER_ENCODING},
622  { "Chunked transfer-encoding on HTTP/0.9 or HTTP/1.0", HTTP_DECODER_EVENT_RESPONSE_CHUNKED_OLD_PROTO},
623  { "Invalid response line: invalid protocol", HTTP_DECODER_EVENT_RESPONSE_INVALID_PROTOCOL},
624  { "Invalid response line: invalid response status", HTTP_DECODER_EVENT_RESPONSE_INVALID_STATUS},
625  { "Request line incomplete", HTTP_DECODER_EVENT_REQUEST_LINE_INCOMPLETE},
626  { "Unexpected request body", HTTP_DECODER_EVENT_REQUEST_BODY_UNEXPECTED},
627  { "LZMA decompressor: memory limit reached", HTTP_DECODER_EVENT_LZMA_MEMLIMIT_REACHED},
628  { "Ambiguous request C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_REQUEST},
629  { "Ambiguous response C-L value", HTTP_DECODER_EVENT_DUPLICATE_CONTENT_LENGTH_FIELD_IN_RESPONSE},
630 };
631 
632 #define HTP_ERROR_MAX (sizeof(htp_errors) / sizeof(htp_errors[0]))
633 #define HTP_WARNING_MAX (sizeof(htp_warnings) / sizeof(htp_warnings[0]))
634 
635 /**
636  * \internal
637  *
638  * \brief Get the warning id for the warning msg.
639  *
640  * \param msg warning message
641  *
642  * \retval id the id or 0 in case of not found
643  */
644 static int HTPHandleWarningGetId(const char *msg)
645 {
646  SCLogDebug("received warning \"%s\"", msg);
647  size_t idx;
648  for (idx = 0; idx < HTP_WARNING_MAX; idx++) {
649  if (strncmp(htp_warnings[idx].msg, msg,
650  strlen(htp_warnings[idx].msg)) == 0)
651  {
652  return htp_warnings[idx].de;
653  }
654  }
655 
656  return 0;
657 }
658 
659 /**
660  * \internal
661  *
662  * \brief Get the error id for the error msg.
663  *
664  * \param msg error message
665  *
666  * \retval id the id or 0 in case of not found
667  */
668 static int HTPHandleErrorGetId(const char *msg)
669 {
670  SCLogDebug("received error \"%s\"", msg);
671 
672  size_t idx;
673  for (idx = 0; idx < HTP_ERROR_MAX; idx++) {
674  if (strncmp(htp_errors[idx].msg, msg,
675  strlen(htp_errors[idx].msg)) == 0)
676  {
677  return htp_errors[idx].de;
678  }
679  }
680 
681  return 0;
682 }
683 
684 /**
685  * \internal
686  *
687  * \brief Check state for errors, warnings and add any as events
688  *
689  * \param s state
690  * \param dir direction: STREAM_TOSERVER or STREAM_TOCLIENT
691  */
692 static void HTPHandleError(HtpState *s, const uint8_t dir)
693 {
694  if (s == NULL || s->conn == NULL ||
695  s->conn->messages == NULL) {
696  return;
697  }
698 
699  size_t size = htp_list_size(s->conn->messages);
700  size_t msg;
701  if(size >= HTP_MAX_MESSAGES) {
703  //only once per HtpState
704  HTPSetEvent(s, NULL, dir, HTTP_DECODER_EVENT_TOO_MANY_WARNINGS);
706  //too noisy in fuzzing
707  //DEBUG_VALIDATE_BUG_ON("Too many libhtp messages");
708  }
709  // ignore further messages
710  return;
711  }
712 
713  for (msg = s->htp_messages_offset; msg < size; msg++) {
714  htp_log_t *log = htp_list_get(s->conn->messages, msg);
715  if (log == NULL)
716  continue;
717 
718  HtpTxUserData *htud = NULL;
719  htp_tx_t *tx = log->tx; // will be NULL in <=0.5.9
720  if (tx != NULL)
721  htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
722 
723  SCLogDebug("message %s", log->msg);
724 
725  int id = HTPHandleErrorGetId(log->msg);
726  if (id == 0) {
727  id = HTPHandleWarningGetId(log->msg);
728  if (id == 0)
730  }
731 
732  if (id > 0) {
733  HTPSetEvent(s, htud, dir, id);
734  }
735  }
736  s->htp_messages_offset = (uint16_t)msg;
737  SCLogDebug("s->htp_messages_offset %u", s->htp_messages_offset);
738 }
739 
740 static inline void HTPErrorCheckTxRequestFlags(HtpState *s, htp_tx_t *tx)
741 {
742 #ifdef DEBUG
743  BUG_ON(s == NULL || tx == NULL);
744 #endif
745  if (tx->flags & ( HTP_REQUEST_INVALID_T_E|HTP_REQUEST_INVALID_C_L|
746  HTP_HOST_MISSING|HTP_HOST_AMBIGUOUS|HTP_HOSTU_INVALID|
747  HTP_HOSTH_INVALID))
748  {
749  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
750  if (htud == NULL)
751  return;
752 
753  if (tx->flags & HTP_REQUEST_INVALID_T_E)
754  HTPSetEvent(s, htud, STREAM_TOSERVER,
756  if (tx->flags & HTP_REQUEST_INVALID_C_L)
757  HTPSetEvent(s, htud, STREAM_TOSERVER,
759  if (tx->flags & HTP_HOST_MISSING)
760  HTPSetEvent(s, htud, STREAM_TOSERVER,
762  if (tx->flags & HTP_HOST_AMBIGUOUS)
763  HTPSetEvent(s, htud, STREAM_TOSERVER,
765  if (tx->flags & HTP_HOSTU_INVALID)
766  HTPSetEvent(s, htud, STREAM_TOSERVER,
768  if (tx->flags & HTP_HOSTH_INVALID)
769  HTPSetEvent(s, htud, STREAM_TOSERVER,
771  }
772  if (tx->request_auth_type == HTP_AUTH_UNRECOGNIZED) {
773  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
774  if (htud == NULL)
775  return;
776  HTPSetEvent(s, htud, STREAM_TOSERVER,
778  }
779  if (tx->is_protocol_0_9 && tx->request_method_number == HTP_M_UNKNOWN &&
780  (tx->request_protocol_number == HTP_PROTOCOL_INVALID ||
781  tx->request_protocol_number == HTP_PROTOCOL_UNKNOWN)) {
782  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
783  if (htud == NULL)
784  return;
785  HTPSetEvent(s, htud, STREAM_TOSERVER,
787  }
788 }
789 
790 static int Setup(Flow *f, HtpState *hstate)
791 {
792  /* store flow ref in state so callbacks can access it */
793  hstate->f = f;
794 
795  HTPCfgRec *htp_cfg_rec = &cfglist;
796  htp_cfg_t *htp = cfglist.cfg; /* Default to the global HTP config */
797  void *user_data = NULL;
798 
799  if (FLOW_IS_IPV4(f)) {
800  SCLogDebug("Looking up HTP config for ipv4 %08x", *GET_IPV4_DST_ADDR_PTR(f));
801  (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(f), cfgtree, &user_data);
802  }
803  else if (FLOW_IS_IPV6(f)) {
804  SCLogDebug("Looking up HTP config for ipv6");
805  (void)SCRadixFindKeyIPV6BestMatch((uint8_t *)GET_IPV6_DST_ADDR(f), cfgtree, &user_data);
806  }
807  else {
808  SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown address family, bug!");
809  goto error;
810  }
811 
812  if (user_data != NULL) {
813  htp_cfg_rec = user_data;
814  htp = htp_cfg_rec->cfg;
815  SCLogDebug("LIBHTP using config: %p", htp);
816  } else {
817  SCLogDebug("Using default HTP config: %p", htp);
818  }
819 
820  if (NULL == htp) {
821 #ifdef DEBUG_VALIDATION
822  BUG_ON(htp == NULL);
823 #endif
824  /* should never happen if HTPConfigure is properly invoked */
825  goto error;
826  }
827 
828  hstate->connp = htp_connp_create(htp);
829  if (hstate->connp == NULL) {
830  goto error;
831  }
832 
833  hstate->conn = htp_connp_get_connection(hstate->connp);
834 
835  htp_connp_set_user_data(hstate->connp, (void *)hstate);
836  hstate->cfg = htp_cfg_rec;
837 
838  SCLogDebug("New hstate->connp %p", hstate->connp);
839 
840  htp_connp_open(hstate->connp, NULL, f->sp, NULL, f->dp, &f->startts);
841 
843  htp_cfg_rec->request.inspect_min_size);
845  htp_cfg_rec->response.inspect_min_size);
846  return 0;
847 error:
848  return -1;
849 }
850 
851 /**
852  * \brief Function to handle the reassembled data from client and feed it to
853  * the HTP library to process it.
854  *
855  * \param flow Pointer to the flow the data belong to
856  * \param htp_state Pointer the state in which the parsed value to be stored
857  * \param pstate Application layer parser state for this session
858  * \param input Pointer the received HTTP client data
859  * \param input_len Length in bytes of the received data
860  * \param output Pointer to the output (not used in this function)
861  *
862  * \retval On success returns 1 or on failure returns -1.
863  */
864 static AppLayerResult HTPHandleRequestData(Flow *f, void *htp_state,
865  AppLayerParserState *pstate,
866  const uint8_t *input, uint32_t input_len,
867  void *local_data, const uint8_t flags)
868 {
869  SCEnter();
870  int ret = 0;
871  HtpState *hstate = (HtpState *)htp_state;
872 
873  /* On the first invocation, create the connection parser structure to
874  * be used by HTP library. This is looked up via IP in the radix
875  * tree. Failing that, the default HTP config is used.
876  */
877  if (NULL == hstate->conn) {
878  if (Setup(f, hstate) != 0) {
880  }
881  }
882  DEBUG_VALIDATE_BUG_ON(hstate->connp == NULL);
883 
884  htp_time_t ts = { f->lastts.tv_sec, f->lastts.tv_usec };
885  /* pass the new data to the htp parser */
886  if (input_len > 0) {
887  const int r = htp_connp_req_data(hstate->connp, &ts, input, input_len);
888  switch (r) {
889  case HTP_STREAM_ERROR:
890  ret = -1;
891  break;
892  default:
893  break;
894  }
895  HTPHandleError(hstate, STREAM_TOSERVER);
896  }
897 
898  /* if the TCP connection is closed, then close the HTTP connection */
900  !(hstate->flags & HTP_FLAG_STATE_CLOSED_TS))
901  {
902  htp_connp_req_close(hstate->connp, &ts);
903  hstate->flags |= HTP_FLAG_STATE_CLOSED_TS;
904  SCLogDebug("stream eof encountered, closing htp handle for ts");
905  }
906 
907  SCLogDebug("hstate->connp %p", hstate->connp);
908 
909  if (ret < 0) {
911  }
913 }
914 
915 /**
916  * \brief Function to handle the reassembled data from server and feed it to
917  * the HTP library to process it.
918  *
919  * \param flow Pointer to the flow the data belong to
920  * \param htp_state Pointer the state in which the parsed value to be stored
921  * \param pstate Application layer parser state for this session
922  * \param input Pointer the received HTTP server data
923  * \param input_len Length in bytes of the received data
924  * \param output Pointer to the output (not used in this function)
925  *
926  * \retval On success returns 1 or on failure returns -1
927  */
928 static AppLayerResult HTPHandleResponseData(Flow *f, void *htp_state,
929  AppLayerParserState *pstate,
930  const uint8_t *input, uint32_t input_len,
931  void *local_data, const uint8_t flags)
932 {
933  SCEnter();
934  int ret = 0;
935  HtpState *hstate = (HtpState *)htp_state;
936 
937  /* On the first invocation, create the connection parser structure to
938  * be used by HTP library. This is looked up via IP in the radix
939  * tree. Failing that, the default HTP config is used.
940  */
941  if (NULL == hstate->conn) {
942  if (Setup(f, hstate) != 0) {
944  }
945  }
946  DEBUG_VALIDATE_BUG_ON(hstate->connp == NULL);
947 
948  htp_time_t ts = { f->lastts.tv_sec, f->lastts.tv_usec };
949  htp_tx_t *tx = NULL;
950  size_t consumed = 0;
951  if (input_len > 0) {
952  const int r = htp_connp_res_data(hstate->connp, &ts, input, input_len);
953  switch (r) {
954  case HTP_STREAM_ERROR:
955  ret = -1;
956  break;
957  case HTP_STREAM_TUNNEL:
958  tx = htp_connp_get_out_tx(hstate->connp);
959  if (tx != NULL && tx->response_status_number == 101) {
960  htp_header_t *h =
961  (htp_header_t *)htp_table_get_c(tx->response_headers, "Upgrade");
962  if (h != NULL) {
963  if (bstr_cmp_c(h->value, "h2c") == 0) {
964  uint16_t dp = 0;
965  if (tx->request_port_number != -1) {
966  dp = (uint16_t)tx->request_port_number;
967  }
968  consumed = htp_connp_res_data_consumed(hstate->connp);
970  // During HTTP2 upgrade, we may consume the HTTP1 part of the data
971  // and we need to parser the remaining part with HTTP2
972  if (consumed > 0 && consumed < input_len) {
974  APP_LAYER_INCOMPLETE(consumed, input_len - consumed));
975  }
977  }
978  }
979  }
980  break;
981  default:
982  break;
983  }
984  HTPHandleError(hstate, STREAM_TOCLIENT);
985  }
986 
987  /* if we the TCP connection is closed, then close the HTTP connection */
989  !(hstate->flags & HTP_FLAG_STATE_CLOSED_TC))
990  {
991  htp_connp_close(hstate->connp, &ts);
992  hstate->flags |= HTP_FLAG_STATE_CLOSED_TC;
993  }
994 
995  SCLogDebug("hstate->connp %p", hstate->connp);
996 
997  if (ret < 0) {
999  }
1001 }
1002 
1003 /**
1004  * \param name /Lowercase/ version of the variable name
1005  */
1006 static int HTTPParseContentDispositionHeader(uint8_t *name, size_t name_len,
1007  uint8_t *data, size_t len, uint8_t **retptr, size_t *retlen)
1008 {
1009 #ifdef PRINT
1010  printf("DATA START: \n");
1011  PrintRawDataFp(stdout, data, len);
1012  printf("DATA END: \n");
1013 #endif
1014  size_t x;
1015  int quote = 0;
1016 
1017  for (x = 0; x < len; x++) {
1018  if (!(isspace(data[x])))
1019  break;
1020  }
1021 
1022  if (x >= len)
1023  return 0;
1024 
1025  uint8_t *line = data+x;
1026  size_t line_len = len-x;
1027  size_t offset = 0;
1028 #ifdef PRINT
1029  printf("LINE START: \n");
1030  PrintRawDataFp(stdout, line, line_len);
1031  printf("LINE END: \n");
1032 #endif
1033  for (x = 0 ; x < line_len; x++) {
1034  if (x > 0) {
1035  if (line[x - 1] != '\\' && line[x] == '\"') {
1036  quote++;
1037  }
1038 
1039  if (((line[x - 1] != '\\' && line[x] == ';') || ((x + 1) == line_len)) && (quote == 0 || quote % 2 == 0)) {
1040  uint8_t *token = line + offset;
1041  size_t token_len = x - offset;
1042 
1043  if ((x + 1) == line_len) {
1044  token_len++;
1045  }
1046 
1047  offset = x + 1;
1048 
1049  while (offset < line_len && isspace(line[offset])) {
1050  x++;
1051  offset++;
1052  }
1053 #ifdef PRINT
1054  printf("TOKEN START: \n");
1055  PrintRawDataFp(stdout, token, token_len);
1056  printf("TOKEN END: \n");
1057 #endif
1058  if (token_len > name_len) {
1059  if (name == NULL || SCMemcmpLowercase(name, token, name_len) == 0) {
1060  uint8_t *value = token + name_len;
1061  size_t value_len = token_len - name_len;
1062 
1063  if (value[0] == '\"') {
1064  value++;
1065  value_len--;
1066  }
1067  if (value[value_len-1] == '\"') {
1068  value_len--;
1069  }
1070 #ifdef PRINT
1071  printf("VALUE START: \n");
1072  PrintRawDataFp(stdout, value, value_len);
1073  printf("VALUE END: \n");
1074 #endif
1075  *retptr = value;
1076  *retlen = value_len;
1077  return 1;
1078  }
1079  }
1080  }
1081  }
1082  }
1083 
1084  return 0;
1085 }
1086 
1087 /**
1088  * \param name /Lowercase/ version of the variable name
1089  */
1090 static int HTTPParseContentTypeHeader(uint8_t *name, size_t name_len,
1091  uint8_t *data, size_t len, uint8_t **retptr, size_t *retlen)
1092 {
1093  SCEnter();
1094 #ifdef PRINT
1095  printf("DATA START: \n");
1096  PrintRawDataFp(stdout, data, len);
1097  printf("DATA END: \n");
1098 #endif
1099  size_t x;
1100  int quote = 0;
1101 
1102  for (x = 0; x < len; x++) {
1103  if (!(isspace(data[x])))
1104  break;
1105  }
1106 
1107  if (x >= len) {
1108  SCReturnInt(0);
1109  }
1110 
1111  uint8_t *line = data+x;
1112  size_t line_len = len-x;
1113  size_t offset = 0;
1114 #ifdef PRINT
1115  printf("LINE START: \n");
1116  PrintRawDataFp(stdout, line, line_len);
1117  printf("LINE END: \n");
1118 #endif
1119  for (x = 0 ; x < line_len; x++) {
1120  if (x > 0) {
1121  if (line[x - 1] != '\\' && line[x] == '\"') {
1122  quote++;
1123  }
1124 
1125  if (((line[x - 1] != '\\' && line[x] == ';') || ((x + 1) == line_len)) && (quote == 0 || quote % 2 == 0)) {
1126  uint8_t *token = line + offset;
1127  size_t token_len = x - offset;
1128 
1129  if ((x + 1) == line_len) {
1130  token_len++;
1131  }
1132 
1133  offset = x + 1;
1134 
1135  while (offset < line_len && isspace(line[offset])) {
1136  x++;
1137  offset++;
1138  }
1139 #ifdef PRINT
1140  printf("TOKEN START: \n");
1141  PrintRawDataFp(stdout, token, token_len);
1142  printf("TOKEN END: \n");
1143 #endif
1144  if (token_len > name_len) {
1145  if (name == NULL || SCMemcmpLowercase(name, token, name_len) == 0) {
1146  uint8_t *value = token + name_len;
1147  size_t value_len = token_len - name_len;
1148 
1149  if (value[0] == '\"') {
1150  value++;
1151  value_len--;
1152  }
1153  if (value[value_len-1] == '\"') {
1154  value_len--;
1155  }
1156 #ifdef PRINT
1157  printf("VALUE START: \n");
1158  PrintRawDataFp(stdout, value, value_len);
1159  printf("VALUE END: \n");
1160 #endif
1161  *retptr = value;
1162  *retlen = value_len;
1163  SCReturnInt(1);
1164  }
1165  }
1166  }
1167  }
1168  }
1169 
1170  SCReturnInt(0);
1171 }
1172 
1173 /**
1174  * \brief setup multipart parsing: extract boundary and store it
1175  *
1176  * \param d HTTP transaction
1177  * \param htud transaction userdata
1178  *
1179  * \retval 1 ok, multipart set up
1180  * \retval 0 ok, not multipart though
1181  * \retval -1 error: problem with the boundary
1182  *
1183  * If the request contains a multipart message, this function will
1184  * set the HTP_BOUNDARY_SET in the transaction.
1185  */
1186 static int HtpRequestBodySetupMultipart(htp_tx_t *tx, HtpTxUserData *htud)
1187 {
1188  htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->request_headers,
1189  "Content-Type");
1190  if (h != NULL && bstr_len(h->value) > 0) {
1191  uint8_t *boundary = NULL;
1192  size_t boundary_len = 0;
1193 
1194  int r = HTTPParseContentTypeHeader((uint8_t *)"boundary=", 9,
1195  (uint8_t *) bstr_ptr(h->value), bstr_len(h->value),
1196  &boundary, &boundary_len);
1197  if (r == 1) {
1198 #ifdef PRINT
1199  printf("BOUNDARY START: \n");
1200  PrintRawDataFp(stdout, boundary, boundary_len);
1201  printf("BOUNDARY END: \n");
1202 #endif
1203  if (boundary_len < HTP_BOUNDARY_MAX) {
1204  htud->boundary = HTPMalloc(boundary_len);
1205  if (htud->boundary == NULL) {
1206  return -1;
1207  }
1208  htud->boundary_len = (uint8_t)boundary_len;
1209  memcpy(htud->boundary, boundary, boundary_len);
1210 
1211  htud->tsflags |= HTP_BOUNDARY_SET;
1212  } else {
1213  SCLogDebug("invalid boundary");
1214  return -1;
1215  }
1216  SCReturnInt(1);
1217  }
1218  //SCReturnInt(1);
1219  }
1220  SCReturnInt(0);
1221 }
1222 
1223 #define C_D_HDR "content-disposition:"
1224 #define C_D_HDR_LEN 20
1225 #define C_T_HDR "content-type:"
1226 #define C_T_HDR_LEN 13
1228 static void HtpRequestBodyMultipartParseHeader(HtpState *hstate,
1229  HtpTxUserData *htud,
1230  uint8_t *header, uint32_t header_len,
1231  uint8_t **filename, uint16_t *filename_len,
1232  uint8_t **filetype, uint16_t *filetype_len)
1233 {
1234  uint8_t *fn = NULL;
1235  size_t fn_len = 0;
1236  uint8_t *ft = NULL;
1237  size_t ft_len = 0;
1238 
1239 #ifdef PRINT
1240  printf("HEADER START: \n");
1241  PrintRawDataFp(stdout, header, header_len);
1242  printf("HEADER END: \n");
1243 #endif
1244 
1245  while (header_len > 0) {
1246  uint8_t *next_line = Bs2bmSearch(header, header_len, (uint8_t *)"\r\n", 2);
1247  uint8_t *line = header;
1248  uint32_t line_len;
1249 
1250  if (next_line == NULL) {
1251  line_len = header_len;
1252  } else {
1253  line_len = next_line - header;
1254  }
1255  uint8_t *sc = (uint8_t *)memchr(line, ':', line_len);
1256  if (sc == NULL) {
1257  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1259  /* if the : we found is the final char, it means we have
1260  * no value */
1261  } else if (line_len > 0 && sc == &line[line_len - 1]) {
1262  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1264  } else {
1265 #ifdef PRINT
1266  printf("LINE START: \n");
1267  PrintRawDataFp(stdout, line, line_len);
1268  printf("LINE END: \n");
1269 #endif
1270  if (line_len >= C_D_HDR_LEN &&
1271  SCMemcmpLowercase(C_D_HDR, line, C_D_HDR_LEN) == 0) {
1272  uint8_t *value = line + C_D_HDR_LEN;
1273  uint32_t value_len = line_len - C_D_HDR_LEN;
1274 
1275  /* parse content-disposition */
1276  (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1277  value, value_len, &fn, &fn_len);
1278  } else if (line_len >= C_T_HDR_LEN &&
1279  SCMemcmpLowercase(C_T_HDR, line, C_T_HDR_LEN) == 0) {
1280  SCLogDebug("content-type line");
1281  uint8_t *value = line + C_T_HDR_LEN;
1282  uint32_t value_len = line_len - C_T_HDR_LEN;
1283 
1284  (void)HTTPParseContentTypeHeader(NULL, 0,
1285  value, value_len, &ft, &ft_len);
1286  }
1287  }
1288 
1289  if (next_line == NULL) {
1290  SCLogDebug("no next_line");
1291  break;
1292  }
1293  header_len -= ((next_line + 2) - header);
1294  header = next_line + 2;
1295  } /* while (header_len > 0) */
1296 
1297  if (fn_len > USHRT_MAX)
1298  fn_len = USHRT_MAX;
1299  if (ft_len > USHRT_MAX)
1300  ft_len = USHRT_MAX;
1301 
1302  *filename = fn;
1303  *filename_len = fn_len;
1304  *filetype = ft;
1305  *filetype_len = ft_len;
1306 }
1307 
1308 /**
1309  * \brief Create a single buffer from the HtpBodyChunks in our list
1310  *
1311  * \param htud transaction user data
1312  * \param chunks_buffers pointer to pass back the buffer to the caller
1313  * \param chunks_buffer_len pointer to pass back the buffer length to the caller
1314  */
1315 static void HtpRequestBodyReassemble(HtpTxUserData *htud,
1316  const uint8_t **chunks_buffer, uint32_t *chunks_buffer_len)
1317 {
1319  chunks_buffer, chunks_buffer_len,
1320  htud->request_body.body_parsed);
1321 }
1322 
1323 static void FlagDetectStateNewFile(HtpTxUserData *tx, int dir)
1324 {
1325  SCEnter();
1326  if (tx && tx->de_state) {
1327  if (dir == STREAM_TOSERVER) {
1328  SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1330  } else if (STREAM_TOCLIENT) {
1331  SCLogDebug("DETECT_ENGINE_STATE_FLAG_FILE_NEW set");
1333  }
1334  }
1335 }
1336 
1337 /**
1338  * \brief Setup boundary buffers
1339  */
1340 static void HtpRequestBodySetupBoundary(HtpTxUserData *htud,
1341  uint8_t *boundary, uint32_t boundary_len)
1342 {
1343  memset(boundary, '-', boundary_len);
1344  memcpy(boundary + 2, htud->boundary, htud->boundary_len);
1345 }
1346 
1347 static int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud, void *tx,
1348  const uint8_t *chunks_buffer, uint32_t chunks_buffer_len)
1349 {
1350  int result = 0;
1351  uint8_t boundary[htud->boundary_len + 4]; /**< size limited to HTP_BOUNDARY_MAX + 4 */
1352  uint32_t expected_boundary_len = htud->boundary_len + 2;
1353  uint32_t expected_boundary_end_len = htud->boundary_len + 4;
1354  int tx_progress = 0;
1355 
1356 #ifdef PRINT
1357  printf("CHUNK START: \n");
1358  PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
1359  printf("CHUNK END: \n");
1360 #endif
1361 
1362  HtpRequestBodySetupBoundary(htud, boundary, htud->boundary_len + 4);
1363 
1364  /* search for the header start, header end and form end */
1365  const uint8_t *header_start = Bs2bmSearch(chunks_buffer, chunks_buffer_len,
1366  boundary, expected_boundary_len);
1367  /* end of the multipart form */
1368  const uint8_t *form_end = NULL;
1369  /* end marker belonging to header_start */
1370  const uint8_t *header_end = NULL;
1371  if (header_start != NULL) {
1372  header_end = Bs2bmSearch(header_start, chunks_buffer_len - (header_start - chunks_buffer),
1373  (uint8_t *)"\r\n\r\n", 4);
1374  form_end = Bs2bmSearch(header_start, chunks_buffer_len - (header_start - chunks_buffer),
1375  boundary, expected_boundary_end_len);
1376  }
1377 
1378  SCLogDebug("header_start %p, header_end %p, form_end %p", header_start,
1379  header_end, form_end);
1380 
1381  /* we currently only handle multipart for ts. When we support it for tc,
1382  * we will need to supply right direction */
1383  tx_progress = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, STREAM_TOSERVER);
1384  /* if we're in the file storage process, deal with that now */
1385  if (htud->tsflags & HTP_FILENAME_SET) {
1386  if (header_start != NULL || (tx_progress > HTP_REQUEST_BODY)) {
1387  SCLogDebug("reached the end of the file");
1388 
1389  const uint8_t *filedata = chunks_buffer;
1390  uint32_t filedata_len = 0;
1391  uint8_t flags = 0;
1392 
1393  if (header_start != NULL) {
1394  if (header_start == filedata + 2) {
1395  /* last chunk had all data, but not the boundary */
1396  SCLogDebug("last chunk had all data, but not the boundary");
1397  filedata_len = 0;
1398  } else if (header_start > filedata + 2) {
1399  SCLogDebug("some data from last file before the boundary");
1400  /* some data from last file before the boundary */
1401  filedata_len = header_start - filedata - 2;
1402  }
1403  }
1404  /* body parsing done, we did not get our form end. Use all data
1405  * we still have and signal to files API we have an issue. */
1406  if (tx_progress > HTP_REQUEST_BODY) {
1407  filedata_len = chunks_buffer_len;
1409  }
1410 
1411  if (filedata_len > chunks_buffer_len) {
1412  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1414  goto end;
1415  }
1416 #ifdef PRINT
1417  printf("FILEDATA (final chunk) START: \n");
1418  PrintRawDataFp(stdout, filedata, filedata_len);
1419  printf("FILEDATA (final chunk) END: \n");
1420 #endif
1421  if (!(htud->tsflags & HTP_DONTSTORE)) {
1422  if (HTPFileClose(hstate, filedata, filedata_len, flags,
1423  STREAM_TOSERVER) == -1)
1424  {
1425  goto end;
1426  }
1427  }
1428 
1429  htud->tsflags &=~ HTP_FILENAME_SET;
1430 
1431  /* fall through */
1432  } else {
1433  SCLogDebug("not yet at the end of the file");
1434 
1435  if (chunks_buffer_len > expected_boundary_end_len) {
1436  const uint8_t *filedata = chunks_buffer;
1437  uint32_t filedata_len = chunks_buffer_len - expected_boundary_len;
1438 #ifdef PRINT
1439  printf("FILEDATA (part) START: \n");
1440  PrintRawDataFp(stdout, filedata, filedata_len);
1441  printf("FILEDATA (part) END: \n");
1442 #endif
1443 
1444  if (!(htud->tsflags & HTP_DONTSTORE)) {
1445  result = HTPFileStoreChunk(hstate, filedata,
1446  filedata_len, STREAM_TOSERVER);
1447  if (result == -1) {
1448  goto end;
1449  } else if (result == -2) {
1450  /* we know for sure we're not storing the file */
1451  htud->tsflags |= HTP_DONTSTORE;
1452  }
1453  }
1454 
1455  htud->request_body.body_parsed += filedata_len;
1456  } else {
1457  SCLogDebug("chunk too small to already process in part");
1458  }
1459 
1460  goto end;
1461  }
1462  }
1463 
1464  while (header_start != NULL && header_end != NULL &&
1465  header_end != form_end &&
1466  header_start < (chunks_buffer + chunks_buffer_len) &&
1467  header_end < (chunks_buffer + chunks_buffer_len) &&
1468  header_start < header_end)
1469  {
1470  uint8_t *filename = NULL;
1471  uint16_t filename_len = 0;
1472  uint8_t *filetype = NULL;
1473  uint16_t filetype_len = 0;
1474 
1475  uint32_t header_len = header_end - header_start;
1476  SCLogDebug("header_len %u", header_len);
1477  uint8_t *header = (uint8_t *)header_start;
1478 
1479  /* skip empty records */
1480  if (expected_boundary_len == header_len) {
1481  goto next;
1482  } else if ((expected_boundary_len + 2) <= header_len) {
1483  header_len -= (expected_boundary_len + 2);
1484  header = (uint8_t *)header_start + (expected_boundary_len + 2); // + for 0d 0a
1485  }
1486 
1487  HtpRequestBodyMultipartParseHeader(hstate, htud, header, header_len,
1488  &filename, &filename_len, &filetype, &filetype_len);
1489 
1490  if (filename != NULL) {
1491  const uint8_t *filedata = NULL;
1492  uint32_t filedata_len = 0;
1493 
1494  SCLogDebug("we have a filename");
1495 
1496  htud->tsflags |= HTP_FILENAME_SET;
1497  htud->tsflags &= ~HTP_DONTSTORE;
1498 
1499  SCLogDebug("header_end %p", header_end);
1500  SCLogDebug("form_end %p", form_end);
1501 
1502  /* everything until the final boundary is the file */
1503  if (form_end != NULL) {
1504  SCLogDebug("have form_end");
1505 
1506  filedata = header_end + 4;
1507  if (form_end == filedata) {
1508  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1510  goto end;
1511  } else if (form_end < filedata) {
1512  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1514  goto end;
1515  }
1516 
1517  filedata_len = form_end - (header_end + 4 + 2);
1518  SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
1519 
1520  /* or is it? */
1521  uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
1522  boundary, expected_boundary_len);
1523  if (header_next != NULL) {
1524  filedata_len -= (form_end - header_next);
1525  }
1526 
1527  if (filedata_len > chunks_buffer_len) {
1528  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1530  goto end;
1531  }
1532  SCLogDebug("filedata_len %"PRIuMAX, (uintmax_t)filedata_len);
1533 #ifdef PRINT
1534  printf("FILEDATA START: \n");
1535  PrintRawDataFp(stdout, filedata, filedata_len);
1536  printf("FILEDATA END: \n");
1537 #endif
1538 
1539  result = HTPFileOpen(hstate, filename, filename_len,
1540  filedata, filedata_len, HtpGetActiveRequestTxID(hstate),
1541  STREAM_TOSERVER);
1542  if (result == -1) {
1543  goto end;
1544  } else if (result == -2) {
1545  htud->tsflags |= HTP_DONTSTORE;
1546  } else {
1547  if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) {
1548  goto end;
1549  }
1550  }
1551  FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1552 
1553  htud->request_body.body_parsed += (header_end - chunks_buffer);
1554  htud->tsflags &= ~HTP_FILENAME_SET;
1555  } else {
1556  SCLogDebug("chunk doesn't contain form end");
1557 
1558  filedata = header_end + 4;
1559  filedata_len = chunks_buffer_len - (filedata - chunks_buffer);
1560  SCLogDebug("filedata_len %u (chunks_buffer_len %u)", filedata_len, chunks_buffer_len);
1561 
1562  if (filedata_len > chunks_buffer_len) {
1563  HTPSetEvent(hstate, htud, STREAM_TOSERVER,
1565  goto end;
1566  }
1567 
1568 #ifdef PRINT
1569  printf("FILEDATA START: \n");
1570  PrintRawDataFp(stdout, filedata, filedata_len);
1571  printf("FILEDATA END: \n");
1572 #endif
1573  /* form doesn't end in this chunk, but the part might. Lets
1574  * see if have another coming up */
1575  uint8_t *header_next = Bs2bmSearch(filedata, filedata_len,
1576  boundary, expected_boundary_len);
1577  SCLogDebug("header_next %p", header_next);
1578  if (header_next == NULL) {
1579  SCLogDebug("more file data to come");
1580 
1581  uint32_t offset = (header_end + 4) - chunks_buffer;
1582  SCLogDebug("offset %u", offset);
1583  htud->request_body.body_parsed += offset;
1584 
1585  if (filedata_len >= (expected_boundary_len + 2)) {
1586  filedata_len -= (expected_boundary_len + 2 - 1);
1587  SCLogDebug("opening file with partial data");
1588  } else {
1589  filedata = NULL;
1590  filedata_len = 0;
1591  }
1592  result = HTPFileOpen(hstate, filename, filename_len,
1593  filedata, filedata_len, HtpGetActiveRequestTxID(hstate),
1594  STREAM_TOSERVER);
1595  if (result == -1) {
1596  goto end;
1597  } else if (result == -2) {
1598  htud->tsflags |= HTP_DONTSTORE;
1599  }
1600  FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1601  htud->request_body.body_parsed += filedata_len;
1602  SCLogDebug("htud->request_body.body_parsed %"PRIu64, htud->request_body.body_parsed);
1603 
1604  } else if (header_next - filedata > 2) {
1605  filedata_len = header_next - filedata - 2;
1606  SCLogDebug("filedata_len %u", filedata_len);
1607 
1608  result = HTPFileOpen(hstate, filename, filename_len,
1609  filedata, filedata_len, HtpGetActiveRequestTxID(hstate),
1610  STREAM_TOSERVER);
1611  if (result == -1) {
1612  goto end;
1613  } else if (result == -2) {
1614  htud->tsflags |= HTP_DONTSTORE;
1615  } else {
1616  if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) {
1617  goto end;
1618  }
1619  }
1620  FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1621 
1622  htud->tsflags &= ~HTP_FILENAME_SET;
1623  htud->request_body.body_parsed += (header_end - chunks_buffer);
1624  }
1625  }
1626  }
1627 next:
1628  SCLogDebug("header_start %p, header_end %p, form_end %p",
1629  header_start, header_end, form_end);
1630 
1631  /* Search next boundary entry after the start of body */
1632  uint32_t cursizeread = header_end - chunks_buffer;
1633  header_start = Bs2bmSearch(header_end + 4,
1634  chunks_buffer_len - (cursizeread + 4),
1635  boundary, expected_boundary_len);
1636  if (header_start != NULL) {
1637  header_end = Bs2bmSearch(header_end + 4,
1638  chunks_buffer_len - (cursizeread + 4),
1639  (uint8_t *) "\r\n\r\n", 4);
1640  }
1641  }
1642 
1643  /* if we're parsing the multipart and we're not currently processing a
1644  * file, we move the body pointer forward. */
1645  if (form_end == NULL && !(htud->tsflags & HTP_FILENAME_SET) && header_start == NULL) {
1646  if (chunks_buffer_len > expected_boundary_end_len) {
1647  uint32_t move = chunks_buffer_len - expected_boundary_end_len + 1;
1648 
1649  htud->request_body.body_parsed += move;
1650  SCLogDebug("form not ready, file not set, parsing non-file "
1651  "record: moved %u", move);
1652  }
1653  }
1654 
1655 end:
1656  SCLogDebug("htud->request_body.body_parsed %"PRIu64, htud->request_body.body_parsed);
1657  return 0;
1658 }
1659 
1660 /** \internal
1661  * \brief Handle POST or PUT, no multipart body data
1662  */
1663 static int HtpRequestBodyHandlePOSTorPUT(HtpState *hstate, HtpTxUserData *htud,
1664  htp_tx_t *tx, uint8_t *data, uint32_t data_len)
1665 {
1666  int result = 0;
1667 
1668  /* see if we need to open the file */
1669  if (!(htud->tsflags & HTP_FILENAME_SET))
1670  {
1671  uint8_t *filename = NULL;
1672  size_t filename_len = 0;
1673 
1674  /* get the name */
1675  if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
1676  filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
1677  filename_len = bstr_len(tx->parsed_uri->path);
1678  }
1679 
1680  if (filename != NULL) {
1681  result = HTPFileOpen(hstate, filename, (uint32_t)filename_len, data, data_len,
1682  HtpGetActiveRequestTxID(hstate), STREAM_TOSERVER);
1683  if (result == -1) {
1684  goto end;
1685  } else if (result == -2) {
1686  htud->tsflags |= HTP_DONTSTORE;
1687  } else {
1688  FlagDetectStateNewFile(htud, STREAM_TOSERVER);
1689  htud->tsflags |= HTP_FILENAME_SET;
1690  htud->tsflags &= ~HTP_DONTSTORE;
1691  }
1692  }
1693  }
1694  else
1695  {
1696  /* otherwise, just store the data */
1697 
1698  if (!(htud->tsflags & HTP_DONTSTORE)) {
1699  result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOSERVER);
1700  if (result == -1) {
1701  goto end;
1702  } else if (result == -2) {
1703  /* we know for sure we're not storing the file */
1704  htud->tsflags |= HTP_DONTSTORE;
1705  }
1706  }
1707  }
1708 
1709  return 0;
1710 end:
1711  return -1;
1712 }
1713 
1714 static int HtpResponseBodyHandle(HtpState *hstate, HtpTxUserData *htud,
1715  htp_tx_t *tx, uint8_t *data, uint32_t data_len)
1716 {
1717  SCEnter();
1718 
1719  int result = 0;
1720 
1721  /* see if we need to open the file
1722  * we check for tx->response_line in case of junk
1723  * interpreted as body before response line
1724  */
1725  if (!(htud->tcflags & HTP_FILENAME_SET) &&
1726  (tx->response_line != NULL || tx->is_protocol_0_9))
1727  {
1728  SCLogDebug("setting up file name");
1729 
1730  uint8_t *filename = NULL;
1731  size_t filename_len = 0;
1732 
1733  /* try Content-Disposition header first */
1734  htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->response_headers,
1735  "Content-Disposition");
1736  if (h != NULL && bstr_len(h->value) > 0) {
1737  /* parse content-disposition */
1738  (void)HTTPParseContentDispositionHeader((uint8_t *)"filename=", 9,
1739  (uint8_t *) bstr_ptr(h->value), bstr_len(h->value), &filename, &filename_len);
1740  }
1741 
1742  /* fall back to name from the uri */
1743  if (filename == NULL) {
1744  /* get the name */
1745  if (tx->parsed_uri != NULL && tx->parsed_uri->path != NULL) {
1746  filename = (uint8_t *)bstr_ptr(tx->parsed_uri->path);
1747  filename_len = bstr_len(tx->parsed_uri->path);
1748  }
1749  }
1750 
1751  if (filename != NULL) {
1752  result = HTPFileOpen(hstate, filename, (uint32_t)filename_len,
1753  data, data_len, HtpGetActiveResponseTxID(hstate), STREAM_TOCLIENT);
1754  SCLogDebug("result %d", result);
1755  if (result == -1) {
1756  goto end;
1757  } else if (result == -2) {
1758  htud->tcflags |= HTP_DONTSTORE;
1759  } else {
1760  FlagDetectStateNewFile(htud, STREAM_TOCLIENT);
1761  htud->tcflags |= HTP_FILENAME_SET;
1762  htud->tcflags &= ~HTP_DONTSTORE;
1763  }
1764  //set range if present
1765  htp_header_t *h_content_range = htp_table_get_c(tx->response_headers, "content-range");
1766  if (h_content_range != NULL) {
1767  HTPFileSetRange(hstate, h_content_range->value);
1768  }
1769  }
1770  }
1771  else if (tx->response_line != NULL || tx->is_protocol_0_9)
1772  {
1773  /* otherwise, just store the data */
1774 
1775  if (!(htud->tcflags & HTP_DONTSTORE)) {
1776  result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOCLIENT);
1777  SCLogDebug("result %d", result);
1778  if (result == -1) {
1779  goto end;
1780  } else if (result == -2) {
1781  /* we know for sure we're not storing the file */
1782  htud->tcflags |= HTP_DONTSTORE;
1783  }
1784  }
1785  }
1786 
1787  htud->response_body.body_parsed += data_len;
1788  return 0;
1789 end:
1790  return -1;
1791 }
1792 
1793 /**
1794  * \brief Function callback to append chunks for Requests
1795  * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1796  * \retval int HTP_OK if all goes well
1797  */
1798 static int HTPCallbackRequestBodyData(htp_tx_data_t *d)
1799 {
1800  SCEnter();
1801 
1802  if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_REQUEST_BODY))
1803  SCReturnInt(HTP_OK);
1804 
1805  if (d->len == 0)
1806  SCReturnInt(HTP_OK);
1807 
1808 #ifdef PRINT
1809  printf("HTPBODY START: \n");
1810  PrintRawDataFp(stdout, (uint8_t *)d->data, d->len);
1811  printf("HTPBODY END: \n");
1812 #endif
1813 
1814  HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
1815  if (hstate == NULL) {
1816  SCReturnInt(HTP_ERROR);
1817  }
1818 
1819  SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
1820  "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
1821 
1822  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
1823  if (tx_ud == NULL) {
1824  SCReturnInt(HTP_OK);
1825  }
1826  if (!tx_ud->response_body_init) {
1827  tx_ud->response_body_init = 1;
1828 
1829  if (d->tx->request_method_number == HTP_M_POST) {
1830  SCLogDebug("POST");
1831  int r = HtpRequestBodySetupMultipart(d->tx, tx_ud);
1832  if (r == 1) {
1834  } else if (r == 0) {
1836  SCLogDebug("not multipart");
1837  }
1838  } else if (d->tx->request_method_number == HTP_M_PUT) {
1840  }
1841  }
1842 
1843  /* see if we can get rid of htp body chunks */
1844  HtpBodyPrune(hstate, &tx_ud->request_body, STREAM_TOSERVER);
1845 
1846  SCLogDebug("tx_ud->request_body.content_len_so_far %"PRIu64, tx_ud->request_body.content_len_so_far);
1847  SCLogDebug("hstate->cfg->request.body_limit %u", hstate->cfg->request.body_limit);
1848 
1849  /* within limits, add the body chunk to the state. */
1850  if (AppLayerHtpCheckDepth(&hstate->cfg->request, &tx_ud->request_body, tx_ud->tsflags)) {
1851  uint32_t stream_depth = FileReassemblyDepth();
1852  uint32_t len = AppLayerHtpComputeChunkLength(tx_ud->request_body.content_len_so_far,
1853  hstate->cfg->request.body_limit,
1854  stream_depth,
1855  tx_ud->tsflags,
1856  (uint32_t)d->len);
1857  BUG_ON(len > (uint32_t)d->len);
1858 
1859  HtpBodyAppendChunk(&hstate->cfg->request, &tx_ud->request_body, d->data, len);
1860 
1861  const uint8_t *chunks_buffer = NULL;
1862  uint32_t chunks_buffer_len = 0;
1863 
1865  /* multi-part body handling starts here */
1866  if (!(tx_ud->tsflags & HTP_BOUNDARY_SET)) {
1867  goto end;
1868  }
1869 
1870  HtpRequestBodyReassemble(tx_ud, &chunks_buffer, &chunks_buffer_len);
1871  if (chunks_buffer == NULL) {
1872  goto end;
1873  }
1874 #ifdef PRINT
1875  printf("REASSCHUNK START: \n");
1876  PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
1877  printf("REASSCHUNK END: \n");
1878 #endif
1879 
1880  HtpRequestBodyHandleMultipart(hstate, tx_ud, d->tx, chunks_buffer, chunks_buffer_len);
1881 
1882  } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_POST ||
1884  HtpRequestBodyHandlePOSTorPUT(hstate, tx_ud, d->tx, (uint8_t *)d->data, len);
1885  }
1886 
1887  } else {
1888  if (tx_ud->tsflags & HTP_FILENAME_SET) {
1889  SCLogDebug("closing file that was being stored");
1890  (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOSERVER);
1891  tx_ud->tsflags &= ~HTP_FILENAME_SET;
1892  }
1893  }
1894 
1895 end:
1896  if (hstate->conn != NULL) {
1897  SCLogDebug("checking body size %"PRIu64" against inspect limit %u (cur %"PRIu64", last %"PRIu64")",
1899  hstate->cfg->request.inspect_min_size,
1900  (uint64_t)hstate->conn->in_data_counter, hstate->last_request_data_stamp);
1901 
1902  /* if we reach the inspect_min_size we'll trigger inspection,
1903  * so make sure that raw stream is also inspected. Set the
1904  * data to be used to the amount of raw bytes we've seen to
1905  * get here. */
1906  if (tx_ud->request_body.body_inspected == 0 &&
1908  if ((uint64_t)hstate->conn->in_data_counter > hstate->last_request_data_stamp &&
1909  (uint64_t)hstate->conn->in_data_counter - hstate->last_request_data_stamp < (uint64_t)UINT_MAX)
1910  {
1911  uint32_t x = (uint32_t)((uint64_t)hstate->conn->in_data_counter - hstate->last_request_data_stamp);
1912 
1913  /* body still in progress, but due to min inspect size we need to inspect now */
1916  }
1917  /* after the start of the body, disable the depth logic */
1918  } else if (tx_ud->request_body.body_inspected > 0) {
1920  }
1921  }
1922  SCReturnInt(HTP_OK);
1923 }
1924 
1925 /**
1926  * \brief Function callback to append chunks for Responses
1927  * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1928  * \retval int HTP_OK if all goes well
1929  */
1930 static int HTPCallbackResponseBodyData(htp_tx_data_t *d)
1931 {
1932  SCEnter();
1933 
1934  if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_RESPONSE_BODY))
1935  SCReturnInt(HTP_OK);
1936 
1937  if (d->len == 0)
1938  SCReturnInt(HTP_OK);
1939 
1940  HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
1941  if (hstate == NULL) {
1942  SCReturnInt(HTP_ERROR);
1943  }
1944 
1945  SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
1946  "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
1947 
1948  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
1949  if (tx_ud == NULL) {
1950  SCReturnInt(HTP_OK);
1951  }
1952  if (!tx_ud->request_body_init) {
1953  tx_ud->request_body_init = 1;
1954  }
1955 
1956  /* see if we can get rid of htp body chunks */
1957  HtpBodyPrune(hstate, &tx_ud->response_body, STREAM_TOCLIENT);
1958 
1959  SCLogDebug("tx_ud->response_body.content_len_so_far %"PRIu64, tx_ud->response_body.content_len_so_far);
1960  SCLogDebug("hstate->cfg->response.body_limit %u", hstate->cfg->response.body_limit);
1961 
1962  /* within limits, add the body chunk to the state. */
1963  if (AppLayerHtpCheckDepth(&hstate->cfg->response, &tx_ud->response_body, tx_ud->tcflags)) {
1964  uint32_t stream_depth = FileReassemblyDepth();
1965  uint32_t len = AppLayerHtpComputeChunkLength(tx_ud->response_body.content_len_so_far,
1966  hstate->cfg->response.body_limit,
1967  stream_depth,
1968  tx_ud->tcflags,
1969  (uint32_t)d->len);
1970  BUG_ON(len > (uint32_t)d->len);
1971 
1972  HtpBodyAppendChunk(&hstate->cfg->response, &tx_ud->response_body, d->data, len);
1973 
1974  HtpResponseBodyHandle(hstate, tx_ud, d->tx, (uint8_t *)d->data, len);
1975  } else {
1976  if (tx_ud->tcflags & HTP_FILENAME_SET) {
1977  SCLogDebug("closing file that was being stored");
1978  (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOCLIENT);
1979  tx_ud->tcflags &= ~HTP_FILENAME_SET;
1980  }
1981  }
1982 
1983  if (hstate->conn != NULL) {
1984  SCLogDebug("checking body size %"PRIu64" against inspect limit %u (cur %"PRIu64", last %"PRIu64")",
1986  hstate->cfg->response.inspect_min_size,
1987  (uint64_t)hstate->conn->in_data_counter, hstate->last_response_data_stamp);
1988  /* if we reach the inspect_min_size we'll trigger inspection,
1989  * so make sure that raw stream is also inspected. Set the
1990  * data to be used to the amount of raw bytes we've seen to
1991  * get here. */
1992  if (tx_ud->response_body.body_inspected == 0 &&
1994  if ((uint64_t)hstate->conn->out_data_counter > hstate->last_response_data_stamp &&
1995  (uint64_t)hstate->conn->out_data_counter - hstate->last_response_data_stamp < (uint64_t)UINT_MAX)
1996  {
1997  uint32_t x = (uint32_t)((uint64_t)hstate->conn->out_data_counter - hstate->last_response_data_stamp);
1998 
1999  /* body still in progress, but due to min inspect size we need to inspect now */
2002  }
2003  /* after the start of the body, disable the depth logic */
2004  } else if (tx_ud->response_body.body_inspected > 0) {
2006  }
2007  }
2008  SCReturnInt(HTP_OK);
2009 }
2010 
2011 /**
2012  * \brief Print the stats of the HTTP requests
2013  */
2015 {
2016 #ifdef DEBUG
2017  SCEnter();
2018  SCMutexLock(&htp_state_mem_lock);
2019  SCLogDebug("http_state_memcnt %"PRIu64", http_state_memuse %"PRIu64"",
2020  htp_state_memcnt, htp_state_memuse);
2021  SCMutexUnlock(&htp_state_mem_lock);
2022  SCReturn;
2023 #endif
2024 }
2025 
2026 /** \brief Clears the HTTP server configuration memory used by HTP library */
2027 void HTPFreeConfig(void)
2028 {
2029  SCEnter();
2030 
2031  if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
2032  !AppLayerParserConfParserEnabled("tcp", "http"))
2033  {
2034  SCReturn;
2035  }
2036 
2037  HTPCfgRec *nextrec = cfglist.next;
2038  SCRadixReleaseRadixTree(cfgtree);
2039  cfgtree = NULL;
2040  htp_config_destroy(cfglist.cfg);
2041  while (nextrec != NULL) {
2042  HTPCfgRec *htprec = nextrec;
2043  nextrec = nextrec->next;
2044 
2045  htp_config_destroy(htprec->cfg);
2046  SCFree(htprec);
2047  }
2048  SCReturn;
2049 }
2050 
2051 static int HTPCallbackRequestHasTrailer(htp_tx_t *tx)
2052 {
2053  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
2054  if (htud != NULL) {
2055  htud->request_has_trailers = 1;
2056  }
2057  return HTP_OK;
2058 }
2059 
2060 static int HTPCallbackResponseHasTrailer(htp_tx_t *tx)
2061 {
2062  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
2063  if (htud != NULL) {
2064  htud->response_has_trailers = 1;
2065  }
2066  return HTP_OK;
2067 }
2068 
2069 /**\internal
2070  * \brief called at start of request
2071  * Set min inspect size.
2072  */
2073 static int HTPCallbackRequestStart(htp_tx_t *tx)
2074 {
2075  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2076  if (hstate == NULL) {
2077  SCReturnInt(HTP_ERROR);
2078  }
2079 
2080  if (hstate->cfg)
2082  hstate->cfg->request.inspect_min_size);
2083 
2084  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2085  if (tx_ud == NULL) {
2086  tx_ud = HTPCalloc(1, sizeof(HtpTxUserData));
2087  if (unlikely(tx_ud == NULL)) {
2088  SCReturnInt(HTP_OK);
2089  }
2090  htp_tx_set_user_data(tx, tx_ud);
2091  }
2092  SCReturnInt(HTP_OK);
2093 }
2094 
2095 /**\internal
2096  * \brief called at start of response
2097  * Set min inspect size.
2098  */
2099 static int HTPCallbackResponseStart(htp_tx_t *tx)
2100 {
2101  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2102  if (hstate == NULL) {
2103  SCReturnInt(HTP_ERROR);
2104  }
2105 
2106  if (hstate->cfg)
2108  hstate->cfg->response.inspect_min_size);
2109 
2110  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2111  if (tx_ud == NULL) {
2112  tx_ud = HTPCalloc(1, sizeof(HtpTxUserData));
2113  if (unlikely(tx_ud == NULL)) {
2114  SCReturnInt(HTP_OK);
2115  }
2116  htp_tx_set_user_data(tx, tx_ud);
2117  }
2118  SCReturnInt(HTP_OK);
2119 }
2120 
2121 
2122 /**
2123  * \brief callback for request to store the recent incoming request
2124  in to the recent_in_tx for the given htp state
2125  * \param connp pointer to the current connection parser which has the htp
2126  * state in it as user data
2127  */
2128 static int HTPCallbackRequest(htp_tx_t *tx)
2129 {
2130  SCEnter();
2131 
2132  if (tx == NULL) {
2133  SCReturnInt(HTP_ERROR);
2134  }
2135 
2136  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2137  if (hstate == NULL) {
2138  SCReturnInt(HTP_ERROR);
2139  }
2140 
2141  SCLogDebug("transaction_cnt %"PRIu64", list_size %"PRIu64,
2142  hstate->transaction_cnt, HTPStateGetTxCnt(hstate));
2143 
2144  SCLogDebug("HTTP request completed");
2145 
2146  HTPErrorCheckTxRequestFlags(hstate, tx);
2147 
2148  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
2149  if (htud != NULL) {
2150  if (htud->tsflags & HTP_FILENAME_SET) {
2151  SCLogDebug("closing file that was being stored");
2152  (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER);
2153  htud->tsflags &= ~HTP_FILENAME_SET;
2155  (uint32_t)hstate->conn->in_data_counter);
2156  }
2157  }
2158 
2159  hstate->last_request_data_stamp = (uint64_t)hstate->conn->in_data_counter;
2160  /* request done, do raw reassembly now to inspect state and stream
2161  * at the same time. */
2163  SCReturnInt(HTP_OK);
2164 }
2165 
2166 /**
2167  * \brief callback for response to remove the recent received requests
2168  from the recent_in_tx for the given htp state
2169  * \param connp pointer to the current connection parser which has the htp
2170  * state in it as user data
2171  */
2172 static int HTPCallbackResponse(htp_tx_t *tx)
2173 {
2174  SCEnter();
2175 
2176  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2177  if (hstate == NULL) {
2178  SCReturnInt(HTP_ERROR);
2179  }
2180 
2181  /* we have one whole transaction now */
2182  hstate->transaction_cnt++;
2183 
2184  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2185  if (htud != NULL) {
2186  if (htud->tcflags & HTP_FILENAME_SET) {
2187  SCLogDebug("closing file that was being stored");
2188  (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOCLIENT);
2189  htud->tcflags &= ~HTP_FILENAME_SET;
2190  }
2191  }
2192 
2193  /* response done, do raw reassembly now to inspect state and stream
2194  * at the same time. */
2196 
2197  /* handle HTTP CONNECT */
2198  if (tx->request_method_number == HTP_M_CONNECT) {
2199  /* any 2XX status response implies that the connection will become
2200  a tunnel immediately after this packet (RFC 7230, 3.3.3). */
2201  if ((tx->response_status_number >= 200) &&
2202  (tx->response_status_number < 300) &&
2203  (hstate->transaction_cnt == 1)) {
2204  uint16_t dp = 0;
2205  if (tx->request_port_number != -1) {
2206  dp = (uint16_t)tx->request_port_number;
2207  }
2208  // both ALPROTO_HTTP and ALPROTO_TLS are normal options
2210  tx->request_progress = HTP_REQUEST_COMPLETE;
2211  tx->response_progress = HTP_RESPONSE_COMPLETE;
2212  }
2213  }
2214 
2215  hstate->last_response_data_stamp = (uint64_t)hstate->conn->out_data_counter;
2216  SCReturnInt(HTP_OK);
2217 }
2218 
2219 static int HTPCallbackRequestLine(htp_tx_t *tx)
2220 {
2221  HtpTxUserData *tx_ud;
2222  bstr *request_uri_normalized;
2223  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2224  const HTPCfgRec *cfg = hstate->cfg;
2225 
2226  request_uri_normalized = SCHTPGenerateNormalizedUri(tx, tx->parsed_uri, cfg->uri_include_all);
2227  if (request_uri_normalized == NULL)
2228  return HTP_OK;
2229 
2230  tx_ud = htp_tx_get_user_data(tx);
2231  if (likely(tx_ud == NULL)) {
2232  tx_ud = HTPMalloc(sizeof(*tx_ud));
2233  if (unlikely(tx_ud == NULL)) {
2234  bstr_free(request_uri_normalized);
2235  return HTP_OK;
2236  }
2237  memset(tx_ud, 0, sizeof(*tx_ud));
2238  htp_tx_set_user_data(tx, tx_ud);
2239  }
2240  if (unlikely(tx_ud->request_uri_normalized != NULL))
2241  bstr_free(tx_ud->request_uri_normalized);
2242  tx_ud->request_uri_normalized = request_uri_normalized;
2243 
2244  if (tx->flags) {
2245  HTPErrorCheckTxRequestFlags(hstate, tx);
2246  }
2247  return HTP_OK;
2248 }
2249 
2250 static int HTPCallbackDoubleDecodeUriPart(htp_tx_t *tx, bstr *part)
2251 {
2252  if (part == NULL)
2253  return HTP_OK;
2254 
2255  uint64_t flags = 0;
2256  size_t prevlen = bstr_len(part);
2257  htp_status_t res = htp_urldecode_inplace(tx->cfg, HTP_DECODER_URLENCODED, part, &flags);
2258  // shorter string means that uri was encoded
2259  if (res == HTP_OK && prevlen > bstr_len(part)) {
2260  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2261  if (htud == NULL)
2262  return HTP_OK;
2263  HtpState *s = htp_connp_get_user_data(tx->connp);
2264  if (s == NULL)
2265  return HTP_OK;
2266  HTPSetEvent(s, htud, STREAM_TOSERVER,
2268  }
2269 
2270  return HTP_OK;
2271 }
2272 
2273 static int HTPCallbackDoubleDecodeQuery(htp_tx_t *tx)
2274 {
2275  if (tx->parsed_uri == NULL)
2276  return HTP_OK;
2277 
2278  return HTPCallbackDoubleDecodeUriPart(tx, tx->parsed_uri->query);
2279 }
2280 
2281 static int HTPCallbackDoubleDecodePath(htp_tx_t *tx)
2282 {
2283  if (tx->parsed_uri == NULL)
2284  return HTP_OK;
2285 
2286  return HTPCallbackDoubleDecodeUriPart(tx, tx->parsed_uri->path);
2287 }
2288 
2289 static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data)
2290 {
2291  void *ptmp;
2292  if (tx_data->len == 0 || tx_data->tx == NULL)
2293  return HTP_OK;
2294 
2295  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
2296  if (tx_ud == NULL) {
2297  return HTP_OK;
2298  }
2299  ptmp = HTPRealloc(tx_ud->request_headers_raw,
2300  tx_ud->request_headers_raw_len,
2301  tx_ud->request_headers_raw_len + tx_data->len);
2302  if (ptmp == NULL) {
2303  return HTP_OK;
2304  }
2305  tx_ud->request_headers_raw = ptmp;
2306 
2307  memcpy(tx_ud->request_headers_raw + tx_ud->request_headers_raw_len,
2308  tx_data->data, tx_data->len);
2309  tx_ud->request_headers_raw_len += tx_data->len;
2310 
2311  if (tx_data->tx && tx_data->tx->flags) {
2312  HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2313  HTPErrorCheckTxRequestFlags(hstate, tx_data->tx);
2314  }
2315  return HTP_OK;
2316 }
2317 
2318 static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data)
2319 {
2320  void *ptmp;
2321  if (tx_data->len == 0 || tx_data->tx == NULL)
2322  return HTP_OK;
2323 
2324  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
2325  if (tx_ud == NULL) {
2326  return HTP_OK;
2327  }
2328  ptmp = HTPRealloc(tx_ud->response_headers_raw,
2329  tx_ud->response_headers_raw_len,
2330  tx_ud->response_headers_raw_len + tx_data->len);
2331  if (ptmp == NULL) {
2332  return HTP_OK;
2333  }
2334  tx_ud->response_headers_raw = ptmp;
2335 
2336  memcpy(tx_ud->response_headers_raw + tx_ud->response_headers_raw_len,
2337  tx_data->data, tx_data->len);
2338  tx_ud->response_headers_raw_len += tx_data->len;
2339 
2340  return HTP_OK;
2341 }
2342 
2343 /*
2344  * We have a similar set function called HTPConfigSetDefaultsPhase1.
2345  */
2346 static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec)
2347 {
2348  cfg_prec->uri_include_all = FALSE;
2355 
2356  if (!g_disable_randomness) {
2358  } else {
2359  cfg_prec->randomize = 0;
2360  }
2362 
2363  htp_config_register_request_header_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
2364  htp_config_register_request_trailer_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
2365  htp_config_register_response_header_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
2366  htp_config_register_response_trailer_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
2367 
2368  htp_config_register_request_trailer(cfg_prec->cfg, HTPCallbackRequestHasTrailer);
2369  htp_config_register_response_trailer(cfg_prec->cfg, HTPCallbackResponseHasTrailer);
2370 
2371  htp_config_register_request_body_data(cfg_prec->cfg, HTPCallbackRequestBodyData);
2372  htp_config_register_response_body_data(cfg_prec->cfg, HTPCallbackResponseBodyData);
2373 
2374  htp_config_register_request_start(cfg_prec->cfg, HTPCallbackRequestStart);
2375  htp_config_register_request_complete(cfg_prec->cfg, HTPCallbackRequest);
2376 
2377  htp_config_register_response_start(cfg_prec->cfg, HTPCallbackResponseStart);
2378  htp_config_register_response_complete(cfg_prec->cfg, HTPCallbackResponse);
2379 
2380  htp_config_set_parse_request_cookies(cfg_prec->cfg, 0);
2381 
2382  /* don't convert + to space by default */
2383  htp_config_set_plusspace_decode(cfg_prec->cfg, HTP_DECODER_URLENCODED, 0);
2384 #ifdef HAVE_HTP_CONFIG_SET_LZMA_LAYERS
2385  // disable by default
2386  htp_config_set_lzma_layers(cfg_prec->cfg, HTP_CONFIG_DEFAULT_LZMA_LAYERS);
2387 #endif
2388 #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT
2389  htp_config_set_lzma_memlimit(cfg_prec->cfg,
2391 #endif
2392 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT
2393  htp_config_set_compression_bomb_limit(cfg_prec->cfg,
2395 #endif
2396  /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set
2397  * only the hard limit. So we set both here to the (current) htp defaults.
2398  * The reason we do this is that if the user sets the hard limit in the
2399  * config, we have to set the soft limit as well. If libhtp starts using
2400  * the soft limit in the future, we at least make sure we control what
2401  * it's value is. */
2402  htp_config_set_field_limits(cfg_prec->cfg,
2405  return;
2406 }
2407 
2408 /* hack: htp random range code expects random values in range of 0-RAND_MAX,
2409  * but we can get both <0 and >RAND_MAX values from RandomGet
2410  */
2411 static int RandomGetWrap(void)
2412 {
2413  unsigned long r;
2414 
2415  do {
2416  r = RandomGet();
2417  } while(r >= ULONG_MAX - (ULONG_MAX % RAND_MAX));
2418 
2419  return r % RAND_MAX;
2420 }
2421 
2422 /*
2423  * We have this splitup so that in case double decoding has been enabled
2424  * for query and path, they would be called first on the callback queue,
2425  * before the callback set by Phase2() is called. We need this, since
2426  * the callback in Phase2() generates the normalized uri which utilizes
2427  * the query and path. */
2428 static void HTPConfigSetDefaultsPhase2(const char *name, HTPCfgRec *cfg_prec)
2429 {
2430  /* randomize inspection size if needed */
2431  if (cfg_prec->randomize) {
2432  int rdrange = cfg_prec->randomize_range;
2433 
2434  long int r = RandomGetWrap();
2435  cfg_prec->request.inspect_min_size +=
2436  (int) (cfg_prec->request.inspect_min_size *
2437  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2438 
2439  r = RandomGetWrap();
2440  cfg_prec->request.inspect_window +=
2441  (int) (cfg_prec->request.inspect_window *
2442  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2443  SCLogConfig("'%s' server has 'request-body-minimal-inspect-size' set to"
2444  " %d and 'request-body-inspect-window' set to %d after"
2445  " randomization.",
2446  name,
2447  cfg_prec->request.inspect_min_size,
2448  cfg_prec->request.inspect_window);
2449 
2450 
2451  r = RandomGetWrap();
2452  cfg_prec->response.inspect_min_size +=
2453  (int) (cfg_prec->response.inspect_min_size *
2454  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2455 
2456  r = RandomGetWrap();
2457  cfg_prec->response.inspect_window +=
2458  (int) (cfg_prec->response.inspect_window *
2459  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2460 
2461  SCLogConfig("'%s' server has 'response-body-minimal-inspect-size' set to"
2462  " %d and 'response-body-inspect-window' set to %d after"
2463  " randomization.",
2464  name,
2465  cfg_prec->response.inspect_min_size,
2466  cfg_prec->response.inspect_window);
2467  }
2468 
2469  htp_config_register_request_line(cfg_prec->cfg, HTPCallbackRequestLine);
2470 
2471  cfg_prec->request.sbcfg.flags = 0;
2472  cfg_prec->request.sbcfg.buf_size = cfg_prec->request.inspect_window ?
2473  cfg_prec->request.inspect_window : 256;
2474  cfg_prec->request.sbcfg.buf_slide = 0;
2475  cfg_prec->request.sbcfg.Malloc = HTPMalloc;
2476  cfg_prec->request.sbcfg.Calloc = HTPCalloc;
2477  cfg_prec->request.sbcfg.Realloc = HTPRealloc;
2478  cfg_prec->request.sbcfg.Free = HTPFree;
2479 
2480  cfg_prec->response.sbcfg.flags = 0;
2481  cfg_prec->response.sbcfg.buf_size = cfg_prec->response.inspect_window ?
2482  cfg_prec->response.inspect_window : 256;
2483  cfg_prec->response.sbcfg.buf_slide = 0;
2484  cfg_prec->response.sbcfg.Malloc = HTPMalloc;
2485  cfg_prec->response.sbcfg.Calloc = HTPCalloc;
2486  cfg_prec->response.sbcfg.Realloc = HTPRealloc;
2487  cfg_prec->response.sbcfg.Free = HTPFree;
2488  return;
2489 }
2490 
2491 static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s,
2492  SCRadixTree *tree)
2493 {
2494  if (cfg_prec == NULL || s == NULL || tree == NULL)
2495  return;
2496 
2497  ConfNode *p = NULL;
2498 
2499  /* Default Parameters */
2500  TAILQ_FOREACH(p, &s->head, next) {
2501 
2502  if (strcasecmp("address", p->name) == 0) {
2503  ConfNode *pval;
2504  /* Addresses */
2505  TAILQ_FOREACH(pval, &p->head, next) {
2506  SCLogDebug("LIBHTP server %s: %s=%s", s->name, p->name,
2507  pval->val);
2508 
2509  /* IPV6 or IPV4? */
2510  if (strchr(pval->val, ':') != NULL) {
2511  SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
2512  s->name, pval->val, cfg_prec->cfg);
2513  if (SCRadixAddKeyIPV6String(pval->val, tree, cfg_prec) == NULL) {
2514  SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed to "
2515  "add ipv6 server %s, ignoring", pval->val);
2516  }
2517  } else {
2518  SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
2519  s->name, pval->val, cfg_prec->cfg);
2520  if (SCRadixAddKeyIPV4String(pval->val, tree, cfg_prec) == NULL) {
2521  SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed "
2522  "to add ipv4 server %s, ignoring",
2523  pval->val);
2524  }
2525  } /* else - if (strchr(pval->val, ':') != NULL) */
2526  } /* TAILQ_FOREACH(pval, &p->head, next) */
2527 
2528  } else if (strcasecmp("personality", p->name) == 0) {
2529  /* Personalities */
2530  int personality = HTPLookupPersonality(p->val);
2531  SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
2532  SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
2533 
2534  if (personality >= 0) {
2535  SCLogDebug("LIBHTP default: %s=%s (%d)", p->name, p->val,
2536  personality);
2537  if (htp_config_set_server_personality(cfg_prec->cfg, personality) == HTP_ERROR){
2538  SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP Failed adding "
2539  "personality \"%s\", ignoring", p->val);
2540  } else {
2541  SCLogDebug("LIBHTP personality set to %s",
2542  HTPLookupPersonalityString(personality));
2543  }
2544 
2545  /* The IDS personality by default converts the path (and due to
2546  * our query string callback also the query string) to lowercase.
2547  * Signatures do not expect this, so override it. */
2548  htp_config_set_convert_lowercase(cfg_prec->cfg, HTP_DECODER_URL_PATH, 0);
2549  } else {
2550  SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Unknown personality "
2551  "\"%s\", ignoring", p->val);
2552  continue;
2553  }
2554 
2555  } else if (strcasecmp("request-body-limit", p->name) == 0 ||
2556  strcasecmp("request_body_limit", p->name) == 0) {
2557  if (ParseSizeStringU32(p->val, &cfg_prec->request.body_limit) < 0) {
2558  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-limit "
2559  "from conf file - %s. Killing engine", p->val);
2560  exit(EXIT_FAILURE);
2561  }
2562 
2563  } else if (strcasecmp("response-body-limit", p->name) == 0) {
2564  if (ParseSizeStringU32(p->val, &cfg_prec->response.body_limit) < 0) {
2565  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-limit "
2566  "from conf file - %s. Killing engine", p->val);
2567  exit(EXIT_FAILURE);
2568  }
2569 
2570  } else if (strcasecmp("request-body-minimal-inspect-size", p->name) == 0) {
2571  if (ParseSizeStringU32(p->val, &cfg_prec->request.inspect_min_size) < 0) {
2572  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-minimal-inspect-size "
2573  "from conf file - %s. Killing engine", p->val);
2574  exit(EXIT_FAILURE);
2575  }
2576 
2577  } else if (strcasecmp("request-body-inspect-window", p->name) == 0) {
2578  if (ParseSizeStringU32(p->val, &cfg_prec->request.inspect_window) < 0) {
2579  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-inspect-window "
2580  "from conf file - %s. Killing engine", p->val);
2581  exit(EXIT_FAILURE);
2582  }
2583 
2584  } else if (strcasecmp("double-decode-query", p->name) == 0) {
2585  if (ConfValIsTrue(p->val)) {
2586  htp_config_register_request_line(cfg_prec->cfg,
2587  HTPCallbackDoubleDecodeQuery);
2588  }
2589 
2590  } else if (strcasecmp("double-decode-path", p->name) == 0) {
2591  if (ConfValIsTrue(p->val)) {
2592  htp_config_register_request_line(cfg_prec->cfg,
2593  HTPCallbackDoubleDecodePath);
2594  }
2595 
2596  } else if (strcasecmp("response-body-minimal-inspect-size", p->name) == 0) {
2597  if (ParseSizeStringU32(p->val, &cfg_prec->response.inspect_min_size) < 0) {
2598  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-minimal-inspect-size "
2599  "from conf file - %s. Killing engine", p->val);
2600  exit(EXIT_FAILURE);
2601  }
2602 
2603  } else if (strcasecmp("response-body-inspect-window", p->name) == 0) {
2604  if (ParseSizeStringU32(p->val, &cfg_prec->response.inspect_window) < 0) {
2605  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
2606  "from conf file - %s. Killing engine", p->val);
2607  exit(EXIT_FAILURE);
2608  }
2609 
2610  } else if (strcasecmp("response-body-decompress-layer-limit", p->name) == 0) {
2611  uint32_t value = 2;
2612  if (ParseSizeStringU32(p->val, &value) < 0) {
2613  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
2614  "from conf file - %s. Killing engine", p->val);
2615  exit(EXIT_FAILURE);
2616  }
2617 #ifdef HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT
2618  htp_config_set_response_decompression_layer_limit(cfg_prec->cfg, value);
2619 #else
2620  SCLogWarning(SC_WARN_OUTDATED_LIBHTP, "can't set response-body-decompress-layer-limit "
2621  "to %u, libhtp version too old", value);
2622 #endif
2623  } else if (strcasecmp("path-convert-backslash-separators", p->name) == 0) {
2624  htp_config_set_backslash_convert_slashes(cfg_prec->cfg,
2625  HTP_DECODER_URL_PATH,
2626  ConfValIsTrue(p->val));
2627  } else if (strcasecmp("path-bestfit-replacement-char", p->name) == 0) {
2628  if (strlen(p->val) == 1) {
2629  htp_config_set_bestfit_replacement_byte(cfg_prec->cfg,
2630  HTP_DECODER_URL_PATH,
2631  p->val[0]);
2632  } else {
2633  SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
2634  "for libhtp param path-bestfit-replacement-char");
2635  }
2636  } else if (strcasecmp("path-convert-lowercase", p->name) == 0) {
2637  htp_config_set_convert_lowercase(cfg_prec->cfg,
2638  HTP_DECODER_URL_PATH,
2639  ConfValIsTrue(p->val));
2640  } else if (strcasecmp("path-nul-encoded-terminates", p->name) == 0) {
2641  htp_config_set_nul_encoded_terminates(cfg_prec->cfg,
2642  HTP_DECODER_URL_PATH,
2643  ConfValIsTrue(p->val));
2644  } else if (strcasecmp("path-nul-raw-terminates", p->name) == 0) {
2645  htp_config_set_nul_raw_terminates(cfg_prec->cfg,
2646  HTP_DECODER_URL_PATH,
2647  ConfValIsTrue(p->val));
2648  } else if (strcasecmp("path-separators-compress", p->name) == 0) {
2649  htp_config_set_path_separators_compress(cfg_prec->cfg,
2650  HTP_DECODER_URL_PATH,
2651  ConfValIsTrue(p->val));
2652  } else if (strcasecmp("path-separators-decode", p->name) == 0) {
2653  htp_config_set_path_separators_decode(cfg_prec->cfg,
2654  HTP_DECODER_URL_PATH,
2655  ConfValIsTrue(p->val));
2656  } else if (strcasecmp("path-u-encoding-decode", p->name) == 0) {
2657  htp_config_set_u_encoding_decode(cfg_prec->cfg,
2658  HTP_DECODER_URL_PATH,
2659  ConfValIsTrue(p->val));
2660  } else if (strcasecmp("path-url-encoding-invalid-handling", p->name) == 0) {
2661  enum htp_url_encoding_handling_t handling;
2662  if (strcasecmp(p->val, "preserve_percent") == 0) {
2663  handling = HTP_URL_DECODE_PRESERVE_PERCENT;
2664  } else if (strcasecmp(p->val, "remove_percent") == 0) {
2665  handling = HTP_URL_DECODE_REMOVE_PERCENT;
2666  } else if (strcasecmp(p->val, "decode_invalid") == 0) {
2667  handling = HTP_URL_DECODE_PROCESS_INVALID;
2668  } else {
2669  SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
2670  "for libhtp param path-url-encoding-invalid-handling");
2671  return;
2672  }
2673  htp_config_set_url_encoding_invalid_handling(cfg_prec->cfg,
2674  HTP_DECODER_URL_PATH,
2675  handling);
2676  } else if (strcasecmp("path-utf8-convert-bestfit", p->name) == 0) {
2677  htp_config_set_utf8_convert_bestfit(cfg_prec->cfg,
2678  HTP_DECODER_URL_PATH,
2679  ConfValIsTrue(p->val));
2680  } else if (strcasecmp("uri-include-all", p->name) == 0) {
2681  cfg_prec->uri_include_all = ConfValIsTrue(p->val);
2682  SCLogDebug("uri-include-all %s",
2683  cfg_prec->uri_include_all ? "enabled" : "disabled");
2684  } else if (strcasecmp("query-plusspace-decode", p->name) == 0) {
2685  htp_config_set_plusspace_decode(cfg_prec->cfg,
2686  HTP_DECODER_URLENCODED,
2687  ConfValIsTrue(p->val));
2688  } else if (strcasecmp("meta-field-limit", p->name) == 0) {
2689  uint32_t limit = 0;
2690  if (ParseSizeStringU32(p->val, &limit) < 0) {
2691  SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
2692  "from conf file - %s. Killing engine", p->val);
2693  exit(EXIT_FAILURE);
2694  }
2695  if (limit == 0) {
2696  FatalError(SC_ERR_FATAL, "Error meta-field-limit "
2697  "from conf file cannot be 0. Killing engine");
2698  }
2699  /* set default soft-limit with our new hard limit */
2700  htp_config_set_field_limits(cfg_prec->cfg,
2702  (size_t)limit);
2703 #ifdef HAVE_HTP_CONFIG_SET_LZMA_MEMLIMIT
2704  } else if (strcasecmp("lzma-memlimit", p->name) == 0) {
2705  uint32_t limit = 0;
2706  if (ParseSizeStringU32(p->val, &limit) < 0) {
2707  FatalError(SC_ERR_SIZE_PARSE, "failed to parse 'lzma-memlimit' "
2708  "from conf file - %s.", p->val);
2709  }
2710  if (limit == 0) {
2711  FatalError(SC_ERR_SIZE_PARSE, "'lzma-memlimit' "
2712  "from conf file cannot be 0.");
2713  }
2714  /* set default soft-limit with our new hard limit */
2715  SCLogConfig("Setting HTTP LZMA memory limit to %"PRIu32" bytes", limit);
2716  htp_config_set_lzma_memlimit(cfg_prec->cfg, (size_t)limit);
2717 #endif
2718 #ifdef HAVE_HTP_CONFIG_SET_LZMA_LAYERS
2719  } else if (strcasecmp("lzma-enabled", p->name) == 0) {
2720  if (ConfValIsTrue(p->val)) {
2721  htp_config_set_lzma_layers(cfg_prec->cfg, 1);
2722  } else if (!ConfValIsFalse(p->val)) {
2723  int8_t limit;
2724  if (StringParseInt8(&limit, 10, 0, (const char *)p->val) < 0) {
2726  "failed to parse 'lzma-enabled' "
2727  "from conf file - %s.",
2728  p->val);
2729  }
2730  SCLogConfig("Setting HTTP LZMA decompression layers to %" PRIu32 "", (int)limit);
2731  htp_config_set_lzma_layers(cfg_prec->cfg, limit);
2732  }
2733 #endif
2734 #ifdef HAVE_HTP_CONFIG_SET_COMPRESSION_BOMB_LIMIT
2735  } else if (strcasecmp("compression-bomb-limit", p->name) == 0) {
2736  uint32_t limit = 0;
2737  if (ParseSizeStringU32(p->val, &limit) < 0) {
2738  FatalError(SC_ERR_SIZE_PARSE, "failed to parse 'compression-bomb-limit' "
2739  "from conf file - %s.", p->val);
2740  }
2741  if (limit == 0) {
2742  FatalError(SC_ERR_SIZE_PARSE, "'compression-bomb-limit' "
2743  "from conf file cannot be 0.");
2744  }
2745  /* set default soft-limit with our new hard limit */
2746  SCLogConfig("Setting HTTP compression bomb limit to %"PRIu32" bytes", limit);
2747  htp_config_set_compression_bomb_limit(cfg_prec->cfg, (size_t)limit);
2748 #endif
2749  } else if (strcasecmp("randomize-inspection-sizes", p->name) == 0) {
2750  if (!g_disable_randomness) {
2751  cfg_prec->randomize = ConfValIsTrue(p->val);
2752  }
2753  } else if (strcasecmp("randomize-inspection-range", p->name) == 0) {
2754  uint32_t range;
2755  if (StringParseU32RangeCheck(&range, 10, 0,
2756  (const char *)p->val, 0, 100) < 0) {
2757  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for randomize"
2758  "-inspection-range setting from conf file - \"%s\"."
2759  " It should be a valid integer less than or equal to 100."
2760  " Killing engine",
2761  p->val);
2762  exit(EXIT_FAILURE);
2763  }
2764  cfg_prec->randomize_range = range;
2765  } else if (strcasecmp("http-body-inline", p->name) == 0) {
2766  if (ConfValIsTrue(p->val)) {
2767  cfg_prec->http_body_inline = 1;
2768  } else if (ConfValIsFalse(p->val)) {
2769  cfg_prec->http_body_inline = 0;
2770  } else {
2771  if (strcmp("auto", p->val) != 0) {
2772  WarnInvalidConfEntry("http_body_inline", "%s", "auto");
2773  }
2774  if (EngineModeIsIPS()) {
2775  cfg_prec->http_body_inline = 1;
2776  } else {
2777  cfg_prec->http_body_inline = 0;
2778  }
2779  }
2780  } else if (strcasecmp("swf-decompression", p->name) == 0) {
2781  ConfNode *pval;
2782 
2783  TAILQ_FOREACH(pval, &p->head, next) {
2784  if (strcasecmp("enabled", pval->name) == 0) {
2785  if (ConfValIsTrue(pval->val)) {
2786  cfg_prec->swf_decompression_enabled = 1;
2787  } else if (ConfValIsFalse(pval->val)) {
2788  cfg_prec->swf_decompression_enabled = 0;
2789  } else {
2790  WarnInvalidConfEntry("swf-decompression.enabled", "%s", "no");
2791  }
2792  } else if (strcasecmp("type", pval->name) == 0) {
2793  if (strcasecmp("no", pval->val) == 0) {
2795  } else if (strcasecmp("deflate", pval->val) == 0) {
2797  } else if (strcasecmp("lzma", pval->val) == 0) {
2799  } else if (strcasecmp("both", pval->val) == 0) {
2801  } else {
2803  "Invalid entry for "
2804  "swf-decompression.type: %s - "
2805  "Killing engine", pval->val);
2806  exit(EXIT_FAILURE);
2807  }
2808  } else if (strcasecmp("compress-depth", pval->name) == 0) {
2809  if (ParseSizeStringU32(pval->val, &cfg_prec->swf_compress_depth) < 0) {
2811  "Error parsing swf-decompression.compression-depth "
2812  "from conf file - %s. Killing engine", p->val);
2813  exit(EXIT_FAILURE);
2814  }
2815  } else if (strcasecmp("decompress-depth", pval->name) == 0) {
2816  if (ParseSizeStringU32(pval->val, &cfg_prec->swf_decompress_depth) < 0) {
2818  "Error parsing swf-decompression.decompression-depth "
2819  "from conf file - %s. Killing engine", p->val);
2820  exit(EXIT_FAILURE);
2821  }
2822  } else {
2823  SCLogWarning(SC_ERR_UNKNOWN_VALUE, "Ignoring unknown param %s", pval->name);
2824  }
2825  }
2826  } else {
2827  SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Ignoring unknown "
2828  "default config: %s", p->name);
2829  }
2830  } /* TAILQ_FOREACH(p, &default_config->head, next) */
2831 
2832  return;
2833 }
2834 
2835 void HTPConfigure(void)
2836 {
2837  SCEnter();
2838 
2839  cfglist.next = NULL;
2840 
2841  cfgtree = SCRadixCreateRadixTree(NULL, NULL);
2842  if (NULL == cfgtree)
2843  exit(EXIT_FAILURE);
2844 
2845  /* Default Config */
2846  cfglist.cfg = htp_config_create();
2847  if (NULL == cfglist.cfg) {
2848  FatalError(SC_ERR_FATAL, "Failed to create HTP default config");
2849  }
2850  SCLogDebug("LIBHTP default config: %p", cfglist.cfg);
2851  HTPConfigSetDefaultsPhase1(&cfglist);
2852  if (ConfGetNode("app-layer.protocols.http.libhtp") == NULL) {
2853  HTPConfigParseParameters(&cfglist, ConfGetNode("libhtp.default-config"),
2854  cfgtree);
2855  } else {
2856  HTPConfigParseParameters(&cfglist, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree);
2857  }
2858  HTPConfigSetDefaultsPhase2("default", &cfglist);
2859 
2860  HTPParseMemcap();
2861 
2862  /* Read server config and create a parser for each IP in radix tree */
2863  ConfNode *server_config = ConfGetNode("app-layer.protocols.http.libhtp.server-config");
2864  if (server_config == NULL) {
2865  server_config = ConfGetNode("libhtp.server-config");
2866  if (server_config == NULL) {
2867  SCLogDebug("LIBHTP Configuring %p", server_config);
2868  SCReturn;
2869  }
2870  }
2871  SCLogDebug("LIBHTP Configuring %p", server_config);
2872 
2873  ConfNode *si;
2874  /* Server Nodes */
2875  TAILQ_FOREACH(si, &server_config->head, next) {
2876  /* Need the named node, not the index */
2877  ConfNode *s = TAILQ_FIRST(&si->head);
2878  if (NULL == s) {
2879  SCLogDebug("LIBHTP s NULL");
2880  continue;
2881  }
2882 
2883  SCLogDebug("LIBHTP server %s", s->name);
2884 
2885  HTPCfgRec *nextrec = cfglist.next;
2886  HTPCfgRec *htprec = SCMalloc(sizeof(HTPCfgRec));
2887  if (NULL == htprec)
2888  exit(EXIT_FAILURE);
2889  memset(htprec, 0x00, sizeof(*htprec));
2890 
2891  cfglist.next = htprec;
2892 
2893  cfglist.next->next = nextrec;
2894  cfglist.next->cfg = htp_config_create();
2895  if (NULL == cfglist.next->cfg) {
2896  FatalError(SC_ERR_FATAL, "Failed to create HTP server config");
2897  }
2898 
2899  HTPConfigSetDefaultsPhase1(htprec);
2900  HTPConfigParseParameters(htprec, s, cfgtree);
2901  HTPConfigSetDefaultsPhase2(s->name, htprec);
2902  }
2903 
2904  SCReturn;
2905 }
2906 
2908 {
2909 #ifdef DEBUG
2910  SCMutexLock(&htp_state_mem_lock);
2911  SCLogPerf("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
2912  SCMutexUnlock(&htp_state_mem_lock);
2913 #endif
2914 }
2915 
2916 /** \internal
2917  * \brief get files callback
2918  * \param state state ptr
2919  * \param direction flow direction
2920  * \retval files files ptr
2921  */
2922 static FileContainer *HTPStateGetFiles(void *state, uint8_t direction)
2923 {
2924  if (state == NULL)
2925  return NULL;
2926 
2927  HtpState *http_state = (HtpState *)state;
2928 
2929  if (direction & STREAM_TOCLIENT) {
2930  SCReturnPtr(http_state->files_tc, "FileContainer");
2931  } else {
2932  SCReturnPtr(http_state->files_ts, "FileContainer");
2933  }
2934 }
2935 
2936 static int HTPStateGetAlstateProgress(void *tx, uint8_t direction)
2937 {
2938  if (direction & STREAM_TOSERVER)
2939  return ((htp_tx_t *)tx)->request_progress;
2940  else
2941  return ((htp_tx_t *)tx)->response_progress;
2942 }
2943 
2944 static uint64_t HTPStateGetTxCnt(void *alstate)
2945 {
2946  HtpState *http_state = (HtpState *)alstate;
2947 
2948  if (http_state != NULL && http_state->conn != NULL) {
2949  const int64_t size = (int64_t)htp_list_size(http_state->conn->transactions);
2950  if (size < 0)
2951  return 0ULL;
2952  SCLogDebug("size %"PRIu64, size);
2953  return (uint64_t)size;
2954  } else {
2955  return 0ULL;
2956  }
2957 }
2958 
2959 static void *HTPStateGetTx(void *alstate, uint64_t tx_id)
2960 {
2961  HtpState *http_state = (HtpState *)alstate;
2962 
2963  if (http_state != NULL && http_state->conn != NULL)
2964  return htp_list_get(http_state->conn->transactions, tx_id);
2965  else
2966  return NULL;
2967 }
2968 
2969 void *HtpGetTxForH2(void *alstate)
2970 {
2971  // gets last transaction
2972  HtpState *http_state = (HtpState *)alstate;
2973  if (http_state != NULL && http_state->conn != NULL) {
2974  size_t txid = htp_list_array_size(http_state->conn->transactions);
2975  if (txid > 0) {
2976  return htp_list_get(http_state->conn->transactions, txid - 1);
2977  }
2978  }
2979  return NULL;
2980 }
2981 
2982 static int HTPStateGetEventInfo(const char *event_name,
2983  int *event_id, AppLayerEventType *event_type)
2984 {
2985  *event_id = SCMapEnumNameToValue(event_name, http_decoder_event_table);
2986  if (*event_id == -1) {
2987  SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
2988  "http's enum map table.", event_name);
2989  /* this should be treated as fatal */
2990  return -1;
2991  }
2992 
2993  *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
2994 
2995  return 0;
2996 }
2997 
2998 static int HTPStateGetEventInfoById(int event_id, const char **event_name,
2999  AppLayerEventType *event_type)
3000 {
3001  *event_name = SCMapEnumValueToName(event_id, http_decoder_event_table);
3002  if (*event_name == NULL) {
3003  SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%d\" not present in "
3004  "http's enum map table.", event_id);
3005  /* this should be treated as fatal */
3006  return -1;
3007  }
3008 
3009  *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
3010 
3011  return 0;
3012 }
3013 
3014 static void HTPStateTruncate(void *state, uint8_t direction)
3015 {
3016  FileContainer *fc = HTPStateGetFiles(state, direction);
3017  if (fc != NULL) {
3019  }
3020 }
3021 
3022 static DetectEngineState *HTPGetTxDetectState(void *vtx)
3023 {
3024  htp_tx_t *tx = (htp_tx_t *)vtx;
3025  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
3026  return tx_ud ? tx_ud->de_state : NULL;
3027 }
3028 
3029 static int HTPSetTxDetectState(void *vtx, DetectEngineState *s)
3030 {
3031  htp_tx_t *tx = (htp_tx_t *)vtx;
3032  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
3033  if (tx_ud == NULL) {
3034  return -ENOMEM;
3035  }
3036  tx_ud->de_state = s;
3037  return 0;
3038 }
3039 
3040 static AppLayerTxData *HTPGetTxData(void *vtx)
3041 {
3042  htp_tx_t *tx = (htp_tx_t *)vtx;
3043  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
3044  if (tx_ud) {
3045  return &tx_ud->tx_data;
3046  }
3047  return NULL;
3048 }
3049 
3050 static int HTPRegisterPatternsForProtocolDetection(void)
3051 {
3052  const char *methods[] = { "GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS",
3053  "CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL",
3054  "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN",
3055  "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE",
3056  "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL", NULL};
3057  const char *spacings[] = { "|20|", "|09|", NULL };
3058  const char *versions[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL };
3059 
3060  int methods_pos;
3061  int spacings_pos;
3062  int versions_pos;
3063  int register_result;
3064  char method_buffer[32] = "";
3065 
3066  /* Loop through all the methods ands spacings and register the patterns */
3067  for (methods_pos = 0; methods[methods_pos]; methods_pos++) {
3068  for (spacings_pos = 0; spacings[spacings_pos]; spacings_pos++) {
3069 
3070  /* Combine the method name and the spacing */
3071  snprintf(method_buffer, sizeof(method_buffer), "%s%s", methods[methods_pos], spacings[spacings_pos]);
3072 
3073  /* Register the new method+spacing pattern
3074  * 3 is subtracted from the length since the spacing is hex typed as |xx|
3075  * but the pattern matching should only be one char
3076  */
3077  register_result = AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP,
3078  ALPROTO_HTTP, method_buffer, strlen(method_buffer)-3, 0, STREAM_TOSERVER);
3079  if (register_result < 0) {
3080  return -1;
3081  }
3082  }
3083  }
3084 
3085  /* Loop through all the http verions patterns that are TO_CLIENT */
3086  for (versions_pos = 0; versions[versions_pos]; versions_pos++) {
3087  register_result = AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP,
3088  ALPROTO_HTTP, versions[versions_pos], strlen(versions[versions_pos]),
3089  0, STREAM_TOCLIENT);
3090  if (register_result < 0) {
3091  return -1;
3092  }
3093  }
3094 
3095  return 0;
3096 }
3097 
3098 /**
3099  * \brief Register the HTTP protocol and state handling functions to APP layer
3100  * of the engine.
3101  */
3103 {
3104  SCEnter();
3105 
3106  const char *proto_name = "http";
3107 
3108  /** HTTP */
3109  if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
3111  if (HTPRegisterPatternsForProtocolDetection() < 0)
3112  return;
3113  } else {
3114  SCLogInfo("Protocol detection and parser disabled for %s protocol",
3115  proto_name);
3116  return;
3117  }
3118 
3119  if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
3120  AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
3121  AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTransactionFree);
3122  AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetFiles);
3123  AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetAlstateProgress);
3124  AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTxCnt);
3125  AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTx);
3126 
3128  ALPROTO_HTTP, HTP_REQUEST_COMPLETE, HTP_RESPONSE_COMPLETE);
3129  AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPGetEvents);
3130  AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetEventInfo);
3131  AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetEventInfoById);
3132 
3133  AppLayerParserRegisterTruncateFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTruncate);
3135  HTPGetTxDetectState, HTPSetTxDetectState);
3136  AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPGetTxData);
3137 
3139  AppLayerHtpSetStreamDepthFlag);
3140 
3142  HTPHandleRequestData);
3144  HTPHandleResponseData);
3145  SC_ATOMIC_INIT(htp_config_flags);
3146  /* This parser accepts gaps. */
3151  HTPConfigure();
3152  } else {
3153  SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
3154  "still on.", proto_name);
3155  }
3156 #ifdef UNITTESTS
3157  AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_HTTP, HTPParserRegisterTests);
3158 #endif
3159 
3160  SCReturn;
3161 }
3162 
3163 #ifdef UNITTESTS
3164 static HTPCfgRec cfglist_backup;
3165 
3167 {
3168  cfglist_backup = cfglist;
3169 
3170  return;
3171 }
3172 
3174 {
3175  cfglist = cfglist_backup;
3176 
3177  return;
3178 }
3179 
3180 /** \test Test case where chunks are sent in smaller chunks and check the
3181  * response of the parser from HTP library. */
3182 static int HTPParserTest01(void)
3183 {
3184  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3185  " Data is c0oL!";
3186  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3187 
3188  TcpSession ssn;
3189  memset(&ssn, 0, sizeof(ssn));
3190 
3193 
3194  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3195  FAIL_IF_NULL(f);
3196  f->protoctx = &ssn;
3197  f->proto = IPPROTO_TCP;
3198  f->alproto = ALPROTO_HTTP;
3199 
3201 
3202  uint32_t u;
3203  for (u = 0; u < httplen1; u++) {
3204  uint8_t flags = 0;
3205 
3206  if (u == 0)
3208  else if (u == (httplen1 - 1))
3210  else
3212 
3213  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3214  &httpbuf1[u], 1);
3215  FAIL_IF(r != 0);
3216  }
3217 
3218  HtpState *htp_state = f->alstate;
3219  FAIL_IF_NULL(htp_state);
3220 
3221  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3222  FAIL_IF_NULL(tx);
3223 
3224  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3225  FAIL_IF_NULL(h);
3226 
3227  FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3228  FAIL_IF(tx->request_method_number != HTP_M_POST);
3229  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3230 
3233  UTHFreeFlow(f);
3234  PASS;
3235 }
3236 
3237 /** \test Test folding in 1 read case */
3238 static int HTPParserTest01b(void)
3239 {
3240  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3241  " Data is c0oL!";
3242  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3243 
3244  TcpSession ssn;
3245  memset(&ssn, 0, sizeof(ssn));
3246 
3249 
3250  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3251  FAIL_IF_NULL(f);
3252  f->protoctx = &ssn;
3253  f->proto = IPPROTO_TCP;
3254  f->alproto = ALPROTO_HTTP;
3255 
3257 
3259  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3260  httpbuf1, httplen1);
3261  FAIL_IF(r != 0);
3262 
3263  HtpState *htp_state = f->alstate;
3264  FAIL_IF_NULL(htp_state);
3265 
3266  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3267  FAIL_IF_NULL(tx);
3268 
3269  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3270  FAIL_IF_NULL(h);
3271 
3272  FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3273  FAIL_IF(tx->request_method_number != HTP_M_POST);
3274  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3275 
3278  UTHFreeFlow(f);
3279  PASS;
3280 }
3281 
3282 /** \test Test folding in 1byte per read case */
3283 static int HTPParserTest01c(void)
3284 {
3285  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3286  " Data is c0oL!";
3287  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3288 
3289  TcpSession ssn;
3290  memset(&ssn, 0, sizeof(ssn));
3291 
3294 
3295  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3296  FAIL_IF_NULL(f);
3297  f->protoctx = &ssn;
3298  f->proto = IPPROTO_TCP;
3299  f->alproto = ALPROTO_HTTP;
3300 
3302 
3303  uint32_t u;
3304  for (u = 0; u < httplen1; u++) {
3305  uint8_t flags = 0;
3306 
3307  if (u == 0)
3309  else if (u == (httplen1 - 1))
3311  else
3313 
3314  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3315  &httpbuf1[u], 1);
3316  FAIL_IF(r != 0);
3317  }
3318 
3319  HtpState *htp_state = f->alstate;
3320  FAIL_IF_NULL(htp_state);
3321 
3322  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3323  FAIL_IF_NULL(tx);
3324 
3325  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3326  FAIL_IF_NULL(h);
3327 
3328  FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3329  FAIL_IF(tx->request_method_number != HTP_M_POST);
3330  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3331 
3334  UTHFreeFlow(f);
3335  PASS;
3336 }
3337 
3338 /** \test Test case where chunks are sent in smaller chunks and check the
3339  * response of the parser from HTP library. */
3340 static int HTPParserTest01a(void)
3341 {
3342  int result = 0;
3343  Flow *f = NULL;
3344  uint8_t httpbuf1[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3345  " Data is c0oL!";
3346  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3347  TcpSession ssn;
3348  HtpState *htp_state = NULL;
3349  int r = 0;
3351 
3352  memset(&ssn, 0, sizeof(ssn));
3353 
3354  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3355  if (f == NULL)
3356  goto end;
3357  f->protoctx = &ssn;
3358  f->proto = IPPROTO_TCP;
3359  f->alproto = ALPROTO_HTTP;
3360 
3362 
3363  uint32_t u;
3364  for (u = 0; u < httplen1; u++) {
3365  uint8_t flags = 0;
3366 
3367  if (u == 0)
3369  else if (u == (httplen1 - 1))
3371  else
3373 
3374  FLOWLOCK_WRLOCK(f);
3376  &httpbuf1[u], 1);
3377  if (r != 0) {
3378  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3379  " 0: ", u, r);
3380  FLOWLOCK_UNLOCK(f);
3381  goto end;
3382  }
3383  FLOWLOCK_UNLOCK(f);
3384  }
3385 
3386  htp_state = f->alstate;
3387  if (htp_state == NULL) {
3388  printf("no http state: ");
3389  goto end;
3390  }
3391 
3392  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3393  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3394  if (strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0")
3395  || tx->request_method_number != HTP_M_POST ||
3396  tx->request_protocol_number != HTP_PROTOCOL_1_0)
3397  {
3398  printf("expected header value: Victor/1.0 and got %s: and expected"
3399  " method: POST and got %s, expected protocol number HTTP/1.0"
3400  " and got: %s \n", bstr_util_strdup_to_c(h->value),
3401  bstr_util_strdup_to_c(tx->request_method),
3402  bstr_util_strdup_to_c(tx->request_protocol));
3403  goto end;
3404  }
3405  result = 1;
3406 end:
3407  if (alp_tctx != NULL)
3410  UTHFreeFlow(f);
3411  return result;
3412 }
3413 
3414 /** \test See how it deals with an incomplete request. */
3415 static int HTPParserTest02(void)
3416 {
3417  int result = 0;
3418  Flow *f = NULL;
3419  uint8_t httpbuf1[] = "POST";
3420  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3421  TcpSession ssn;
3422  HtpState *http_state = NULL;
3424 
3425  memset(&ssn, 0, sizeof(ssn));
3426 
3427  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3428  if (f == NULL)
3429  goto end;
3430  f->protoctx = &ssn;
3431  f->proto = IPPROTO_TCP;
3432  f->alproto = ALPROTO_HTTP;
3433 
3435 
3436  FLOWLOCK_WRLOCK(f);
3437  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3439  httpbuf1,
3440  httplen1);
3441  if (r != 0) {
3442  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
3443  FLOWLOCK_UNLOCK(f);
3444  goto end;
3445  }
3446  FLOWLOCK_UNLOCK(f);
3447 
3448  http_state = f->alstate;
3449  if (http_state == NULL) {
3450  printf("no http state: ");
3451  goto end;
3452  }
3453 
3454  htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3455  FAIL_IF_NULL(tx);
3456  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3457  FAIL_IF_NOT_NULL(h);
3458 
3459  FAIL_IF_NULL(tx->request_method);
3460  char *method = bstr_util_strdup_to_c(tx->request_method);
3461  FAIL_IF_NULL(method);
3462 
3463  FAIL_IF(strcmp(method, "POST") != 0);
3464  SCFree(method);
3465 
3466  result = 1;
3467 end:
3468  if (alp_tctx != NULL)
3471  UTHFreeFlow(f);
3472  return result;
3473 }
3474 
3475 /** \test Test case where method is invalid and data is sent in smaller chunks
3476  * and check the response of the parser from HTP library. */
3477 static int HTPParserTest03(void)
3478 {
3479  int result = 0;
3480  Flow *f = NULL;
3481  uint8_t httpbuf1[] = "HELLO / HTTP/1.0\r\n";
3482  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3483  TcpSession ssn;
3484  HtpState *htp_state = NULL;
3485  int r = 0;
3487 
3488  memset(&ssn, 0, sizeof(ssn));
3489 
3490  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3491  if (f == NULL)
3492  goto end;
3493  f->protoctx = &ssn;
3494  f->proto = IPPROTO_TCP;
3495  f->alproto = ALPROTO_HTTP;
3496 
3498 
3499  uint32_t u;
3500  for (u = 0; u < httplen1; u++) {
3501  uint8_t flags = 0;
3502 
3503  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
3504  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
3505  else flags = STREAM_TOSERVER;
3506 
3507  FLOWLOCK_WRLOCK(f);
3509  &httpbuf1[u], 1);
3510  if (r != 0) {
3511  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3512  " 0: ", u, r);
3513  FLOWLOCK_UNLOCK(f);
3514  goto end;
3515  }
3516  FLOWLOCK_UNLOCK(f);
3517  }
3518  htp_state = f->alstate;
3519  if (htp_state == NULL) {
3520  printf("no http state: ");
3521  goto end;
3522  }
3523 
3524  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3525 
3526  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3527  if (tx->request_method_number != HTP_M_UNKNOWN ||
3528  h != NULL || tx->request_protocol_number != HTP_PROTOCOL_1_0)
3529  {
3530  printf("expected method M_UNKNOWN and got %s: , expected protocol "
3531  "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
3532  bstr_util_strdup_to_c(tx->request_protocol));
3533  goto end;
3534  }
3535  result = 1;
3536 end:
3537  if (alp_tctx != NULL)
3540  UTHFreeFlow(f);
3541  return result;
3542 }
3543 
3544 /** \test Test case where invalid data is sent and check the response of the
3545  * parser from HTP library. */
3546 static int HTPParserTest04(void)
3547 {
3548  int result = 0;
3549  Flow *f = NULL;
3550  HtpState *htp_state = NULL;
3551  uint8_t httpbuf1[] = "World!\r\n";
3552  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3553  TcpSession ssn;
3554  int r = 0;
3556 
3557  memset(&ssn, 0, sizeof(ssn));
3558 
3559  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3560  if (f == NULL)
3561  goto end;
3562  f->protoctx = &ssn;
3563  f->proto = IPPROTO_TCP;
3564  f->alproto = ALPROTO_HTTP;
3565 
3567 
3568  FLOWLOCK_WRLOCK(f);
3571  httpbuf1,
3572  httplen1);
3573  if (r != 0) {
3574  FLOWLOCK_UNLOCK(f);
3575  goto end;
3576  }
3577  FLOWLOCK_UNLOCK(f);
3578 
3579  htp_state = f->alstate;
3580  if (htp_state == NULL) {
3581  printf("no http state: ");
3582  goto end;
3583  }
3584 
3585  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3586  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3587  if (tx->request_method_number != HTP_M_UNKNOWN ||
3588  h != NULL || tx->request_protocol_number != HTP_PROTOCOL_0_9)
3589  {
3590  printf("expected method M_UNKNOWN and got %s: , expected protocol "
3591  "NULL and got %s \n", bstr_util_strdup_to_c(tx->request_method),
3592  bstr_util_strdup_to_c(tx->request_protocol));
3593  goto end;
3594  }
3595  result = 1;
3596 end:
3597  if (alp_tctx != NULL)
3600  UTHFreeFlow(f);
3601  return result;
3602 }
3603 
3604 /** \test Test both sides of a http stream mixed up to see if the HTP parser
3605  * properly parsed them and also keeps them separated. */
3606 static int HTPParserTest05(void)
3607 {
3608  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\nContent-Length: 17\r\n\r\n";
3609  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3610  uint8_t httpbuf2[] = "Post D";
3611  uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
3612  uint8_t httpbuf3[] = "ata is c0oL!";
3613  uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
3614 
3615  uint8_t httpbuf4[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
3616  uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
3617  uint8_t httpbuf5[] = "post R";
3618  uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
3619  uint8_t httpbuf6[] = "esults are tha bomb!";
3620  uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
3621 
3622  TcpSession ssn;
3623  memset(&ssn, 0, sizeof(ssn));
3624 
3627 
3628  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3629  FAIL_IF_NULL(f);
3630  f->protoctx = &ssn;
3631  f->proto = IPPROTO_TCP;
3632  f->alproto = ALPROTO_HTTP;
3633 
3635 
3636  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3637  STREAM_TOSERVER | STREAM_START, httpbuf1,
3638  httplen1);
3639  FAIL_IF(r != 0);
3640 
3642  STREAM_TOCLIENT | STREAM_START, httpbuf4,
3643  httplen4);
3644  FAIL_IF(r != 0);
3645 
3647  httpbuf5, httplen5);
3648  FAIL_IF(r != 0);
3649 
3651  httpbuf2, httplen2);
3652  FAIL_IF(r != 0);
3653 
3655  STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
3656  FAIL_IF(r != 0);
3657 
3659  STREAM_TOCLIENT | STREAM_EOF, httpbuf6, httplen6);
3660  FAIL_IF(r != 0);
3661 
3662  HtpState *http_state = f->alstate;
3663  FAIL_IF_NULL(http_state);
3664 
3665  htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3666  FAIL_IF_NULL(tx);
3667  FAIL_IF_NOT(tx->request_method_number == HTP_M_POST);
3668  FAIL_IF_NOT(tx->request_protocol_number == HTP_PROTOCOL_1_0);
3669 
3670  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3671  FAIL_IF_NULL(h);
3672 
3673  FAIL_IF_NOT(tx->response_status_number == 200);
3674 
3677  UTHFreeFlow(f);
3678  PASS;
3679 }
3680 
3681 /** \test Test proper chunked encoded response body
3682  */
3683 static int HTPParserTest06(void)
3684 {
3685  uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
3686  "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
3687  "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
3688  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3689  uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
3690  "GMT\r\n"
3691  "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
3692  "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
3693  "FrontPage/5.0.2.2510\r\n"
3694  "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
3695  "chunked\r\n"
3696  "Content-Type: text/html\r\n\r\n"
3697  "580\r\n"
3698  "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
3699  "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
3700  "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
3701  "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
3702  "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
3703  "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
3704  "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
3705  "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
3706  "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
3707  "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
3708  "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
3709  "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
3710  "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
3711  "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
3712  "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
3713  "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
3714  "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
3715  "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
3716  "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
3717  "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
3718  "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
3719  "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
3720  "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
3721  "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
3722  "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
3723  "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
3724  "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
3725  "aHA=\r\n0\r\n\r\n";
3726  uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
3727  TcpSession ssn;
3728 
3731 
3732  memset(&ssn, 0, sizeof(ssn));
3733 
3734  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3735  FAIL_IF_NULL(f);
3736  f->protoctx = &ssn;
3737  f->proto = IPPROTO_TCP;
3738  f->alproto = ALPROTO_HTTP;
3739 
3741 
3742  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3743  STREAM_TOSERVER | STREAM_START, httpbuf1,
3744  httplen1);
3745  FAIL_IF(r != 0);
3747  STREAM_TOCLIENT | STREAM_START, httpbuf2,
3748  httplen2);
3749  FAIL_IF(r != 0);
3750 
3751  HtpState *http_state = f->alstate;
3752  FAIL_IF_NULL(http_state);
3753 
3754  htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3755  FAIL_IF_NULL(tx);
3756 
3757  FAIL_IF(tx->request_method_number != HTP_M_GET);
3758  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
3759 
3760  FAIL_IF(tx->response_status_number != 200);
3761  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
3762 
3763  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3764  FAIL_IF_NULL(h);
3765 
3768  UTHFreeFlow(f);
3769  PASS;
3770 }
3771 
3772 /** \test
3773  */
3774 static int HTPParserTest07(void)
3775 {
3776  int result = 0;
3777  Flow *f = NULL;
3778  uint8_t httpbuf1[] = "GET /awstats.pl?/migratemigrate%20=%20| HTTP/1.0\r\n\r\n";
3779  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3780  TcpSession ssn;
3781  HtpState *htp_state = NULL;
3782  int r = 0;
3784 
3785  memset(&ssn, 0, sizeof(ssn));
3786 
3787  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3788  if (f == NULL)
3789  goto end;
3790  f->protoctx = &ssn;
3791  f->proto = IPPROTO_TCP;
3792  f->alproto = ALPROTO_HTTP;
3793 
3795 
3796  uint32_t u;
3797  for (u = 0; u < httplen1; u++) {
3798  uint8_t flags = 0;
3799 
3800  if (u == 0)
3802  else if (u == (httplen1 - 1))
3804  else
3806 
3807  FLOWLOCK_WRLOCK(f);
3809  &httpbuf1[u], 1);
3810  if (r != 0) {
3811  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3812  " 0: ", u, r);
3813  FLOWLOCK_UNLOCK(f);
3814  goto end;
3815  }
3816  FLOWLOCK_UNLOCK(f);
3817  }
3818 
3819  htp_state = f->alstate;
3820  if (htp_state == NULL) {
3821  printf("no http state: ");
3822  goto end;
3823  }
3824 
3825  uint8_t ref[] = "/awstats.pl?/migratemigrate = |";
3826  size_t reflen = sizeof(ref) - 1;
3827 
3828  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3829  if (tx == NULL)
3830  goto end;
3831  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3832  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3833  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
3834  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
3835  (uintmax_t)reflen,
3836  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
3837  goto end;
3838  }
3839 
3840  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref,
3841  bstr_len(tx_ud->request_uri_normalized)) != 0)
3842  {
3843  printf("normalized uri \"");
3844  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
3845  printf("\" != \"");
3846  PrintRawUriFp(stdout, ref, reflen);
3847  printf("\": ");
3848  goto end;
3849  }
3850  }
3851 
3852  result = 1;
3853 end:
3854  if (alp_tctx != NULL)
3857  UTHFreeFlow(f);
3858  return result;
3859 }
3860 
3861 #include "conf-yaml-loader.h"
3862 
3863 /** \test Abort
3864  */
3865 static int HTPParserTest08(void)
3866 {
3867  int result = 0;
3868  Flow *f = NULL;
3869  uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3870  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3871  TcpSession ssn;
3873 
3874  char input[] = "\
3875 %YAML 1.1\n\
3876 ---\n\
3877 libhtp:\n\
3878 \n\
3879  default-config:\n\
3880  personality: IDS\n\
3881 ";
3882 
3884  ConfInit();
3886 
3887  ConfYamlLoadString(input, strlen(input));
3888  HTPConfigure();
3889 
3890  HtpState *htp_state = NULL;
3891  int r = 0;
3892  memset(&ssn, 0, sizeof(ssn));
3893 
3894  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3895  if (f == NULL)
3896  goto end;
3897  f->protoctx = &ssn;
3898  f->proto = IPPROTO_TCP;
3899  f->alproto = ALPROTO_HTTP;
3900 
3902 
3903  uint8_t flags = 0;
3905 
3906  FLOWLOCK_WRLOCK(f);
3907  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1,
3908  httplen1);
3909  if (r != 0) {
3910  printf("toserver chunk returned %" PRId32 ", expected"
3911  " 0: ", r);
3912  result = 0;
3913  FLOWLOCK_UNLOCK(f);
3914  goto end;
3915  }
3916  FLOWLOCK_UNLOCK(f);
3917 
3918  htp_state = f->alstate;
3919  if (htp_state == NULL) {
3920  printf("no http state: ");
3921  result = 0;
3922  goto end;
3923  }
3924 
3925  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3926  if (tx == NULL)
3927  goto end;
3928  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3929  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3930  //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3931  PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
3932  bstr_len(tx_ud->request_uri_normalized));
3933  }
3934 
3935  result = 1;
3936 end:
3937  if (alp_tctx != NULL)
3940  HTPFreeConfig();
3941  ConfDeInit();
3944  UTHFreeFlow(f);
3945  return result;
3946 }
3947 
3948 /** \test Abort
3949  */
3950 static int HTPParserTest09(void)
3951 {
3952  int result = 0;
3953  Flow *f = NULL;
3954  uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3955  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3956  TcpSession ssn;
3958 
3959  char input[] = "\
3960 %YAML 1.1\n\
3961 ---\n\
3962 libhtp:\n\
3963 \n\
3964  default-config:\n\
3965  personality: Apache_2_2\n\
3966 ";
3967 
3969  ConfInit();
3971 
3972  ConfYamlLoadString(input, strlen(input));
3973  HTPConfigure();
3974 
3975  HtpState *htp_state = NULL;
3976  int r = 0;
3977 
3978  memset(&ssn, 0, sizeof(ssn));
3979 
3980  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3981  if (f == NULL)
3982  goto end;
3983  f->protoctx = &ssn;
3984  f->proto = IPPROTO_TCP;
3985  f->alproto = ALPROTO_HTTP;
3986 
3988 
3989  uint8_t flags = 0;
3991 
3992  FLOWLOCK_WRLOCK(f);
3993  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1,
3994  httplen1);
3995  if (r != 0) {
3996  printf("toserver chunk returned %" PRId32 ", expected"
3997  " 0: ", r);
3998  FLOWLOCK_UNLOCK(f);
3999  goto end;
4000  }
4001  FLOWLOCK_UNLOCK(f);
4002 
4003  htp_state = f->alstate;
4004  if (htp_state == NULL) {
4005  printf("no http state: ");
4006  goto end;
4007  }
4008 
4009  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4010  if (tx == NULL)
4011  goto end;
4012  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4013  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4014  //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
4015  PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
4016  bstr_len(tx_ud->request_uri_normalized));
4017  }
4018 
4019  result = 1;
4020 end:
4021  if (alp_tctx != NULL)
4024  HTPFreeConfig();
4025  ConfDeInit();
4028  UTHFreeFlow(f);
4029  return result;
4030 }
4031 
4032 /** \test Host:www.google.com <- missing space between name:value (rfc violation)
4033  */
4034 static int HTPParserTest10(void)
4035 {
4036  int result = 0;
4037  Flow *f = NULL;
4038  uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\r\n\r\n";
4039  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4040  TcpSession ssn;
4041  HtpState *htp_state = NULL;
4042  int r = 0;
4044 
4045  memset(&ssn, 0, sizeof(ssn));
4046 
4047  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4048  if (f == NULL)
4049  goto end;
4050  f->protoctx = &ssn;
4051  f->proto = IPPROTO_TCP;
4052  f->alproto = ALPROTO_HTTP;
4053 
4055 
4056  uint32_t u;
4057  for (u = 0; u < httplen1; u++) {
4058  uint8_t flags = 0;
4059 
4060  if (u == 0)
4062  else if (u == (httplen1 - 1))
4064  else
4066 
4067  FLOWLOCK_WRLOCK(f);
4069  &httpbuf1[u], 1);
4070  if (r != 0) {
4071  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4072  " 0: ", u, r);
4073  FLOWLOCK_UNLOCK(f);
4074  goto end;
4075  }
4076  FLOWLOCK_UNLOCK(f);
4077  }
4078 
4079  htp_state = f->alstate;
4080  if (htp_state == NULL) {
4081  printf("no http state: ");
4082  goto end;
4083  }
4084 
4085  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4086  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
4087  if (h == NULL) {
4088  goto end;
4089  }
4090 
4091  char *name = bstr_util_strdup_to_c(h->name);
4092  if (name == NULL) {
4093  goto end;
4094  }
4095 
4096  if (strcmp(name, "Host") != 0) {
4097  printf("header name not \"Host\", instead \"%s\": ", name);
4098  free(name);
4099  goto end;
4100  }
4101  free(name);
4102 
4103  char *value = bstr_util_strdup_to_c(h->value);
4104  if (value == NULL) {
4105  goto end;
4106  }
4107 
4108  if (strcmp(value, "www.google.com") != 0) {
4109  printf("header value not \"www.google.com\", instead \"%s\": ", value);
4110  free(value);
4111  goto end;
4112  }
4113  free(value);
4114 
4115  result = 1;
4116 end:
4117  if (alp_tctx != NULL)
4120  UTHFreeFlow(f);
4121  return result;
4122 }
4123 
4124 /** \test double encoding in path
4125  */
4126 static int HTPParserTest11(void)
4127 {
4128  int result = 0;
4129  Flow *f = NULL;
4130  uint8_t httpbuf1[] = "GET /%2500 HTTP/1.0\r\n\r\n";
4131  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4132  TcpSession ssn;
4133  HtpState *htp_state = NULL;
4134  int r = 0;
4136 
4137  memset(&ssn, 0, sizeof(ssn));
4138 
4139  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4140  if (f == NULL)
4141  goto end;
4142  f->protoctx = &ssn;
4143  f->proto = IPPROTO_TCP;
4144  f->alproto = ALPROTO_HTTP;
4145 
4147 
4148  uint32_t u;
4149  for (u = 0; u < httplen1; u++) {
4150  uint8_t flags = 0;
4151 
4152  if (u == 0)
4154  else if (u == (httplen1 - 1))
4156  else
4158 
4159  FLOWLOCK_WRLOCK(f);
4161  &httpbuf1[u], 1);
4162  if (r != 0) {
4163  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4164  " 0: ", u, r);
4165  FLOWLOCK_UNLOCK(f);
4166  goto end;
4167  }
4168  FLOWLOCK_UNLOCK(f);
4169  }
4170 
4171  htp_state = f->alstate;
4172  if (htp_state == NULL) {
4173  printf("no http state: ");
4174  goto end;
4175  }
4176 
4177  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4178  if (tx == NULL)
4179  goto end;
4180  HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4181  if (tx != NULL && tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4182  if (4 != bstr_len(tx_ud->request_uri_normalized)) {
4183  printf("normalized uri len should be 2, is %"PRIuMAX,
4184  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4185  goto end;
4186  }
4187 
4188  if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
4189  bstr_ptr(tx_ud->request_uri_normalized)[1] != '%' ||
4190  bstr_ptr(tx_ud->request_uri_normalized)[2] != '0' ||
4191  bstr_ptr(tx_ud->request_uri_normalized)[3] != '0')
4192  {
4193  printf("normalized uri \"");
4194  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4195  printf("\": ");
4196  goto end;
4197  }
4198  }
4199 
4200  result = 1;
4201 end:
4202  if (alp_tctx != NULL)
4205  UTHFreeFlow(f);
4206  return result;
4207 }
4208 
4209 /** \test double encoding in query
4210  */
4211 static int HTPParserTest12(void)
4212 {
4213  int result = 0;
4214  Flow *f = NULL;
4215  uint8_t httpbuf1[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
4216  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4217  TcpSession ssn;
4218  HtpState *htp_state = NULL;
4219  int r = 0;
4221 
4222  memset(&ssn, 0, sizeof(ssn));
4223 
4224  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4225  if (f == NULL)
4226  goto end;
4227  f->protoctx = &ssn;
4228  f->proto = IPPROTO_TCP;
4229  f->alproto = ALPROTO_HTTP;
4230 
4232 
4233  uint32_t u;
4234  for (u = 0; u < httplen1; u++) {
4235  uint8_t flags = 0;
4236 
4237  if (u == 0)
4239  else if (u == (httplen1 - 1))
4241  else
4243 
4244  FLOWLOCK_WRLOCK(f);
4246  &httpbuf1[u], 1);
4247  if (r != 0) {
4248  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4249  " 0: ", u, r);
4250  FLOWLOCK_UNLOCK(f);
4251  goto end;
4252  }
4253  FLOWLOCK_UNLOCK(f);
4254  }
4255 
4256  htp_state = f->alstate;
4257  if (htp_state == NULL) {
4258  printf("no http state: ");
4259  goto end;
4260  }
4261 
4262  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4263  if (tx == NULL)
4264  goto end;
4265  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4266  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4267  if (7 != bstr_len(tx_ud->request_uri_normalized)) {
4268  printf("normalized uri len should be 5, is %"PRIuMAX,
4269  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4270  goto end;
4271  }
4272 
4273  if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
4274  bstr_ptr(tx_ud->request_uri_normalized)[1] != '?' ||
4275  bstr_ptr(tx_ud->request_uri_normalized)[2] != 'a' ||
4276  bstr_ptr(tx_ud->request_uri_normalized)[3] != '=' ||
4277  bstr_ptr(tx_ud->request_uri_normalized)[4] != '%' ||
4278  bstr_ptr(tx_ud->request_uri_normalized)[5] != '0' ||
4279  bstr_ptr(tx_ud->request_uri_normalized)[6] != '0')
4280  {
4281  printf("normalized uri \"");
4282  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4283  printf("\": ");
4284  goto end;
4285  }
4286  }
4287 
4288  result = 1;
4289  end:
4290  if (alp_tctx != NULL)
4293  UTHFreeFlow(f);
4294  return result;
4295 }
4296 
4297 /** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
4298  */
4299 static int HTPParserTest13(void)
4300 {
4301  int result = 0;
4302  Flow *f = NULL;
4303  uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\rName: Value\r\n\r\n";
4304  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4305  TcpSession ssn;
4306  HtpState *htp_state = NULL;
4307  int r = 0;
4309 
4310  memset(&ssn, 0, sizeof(ssn));
4311 
4312  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4313  if (f == NULL)
4314  goto end;
4315  f->protoctx = &ssn;
4316  f->proto = IPPROTO_TCP;
4317  f->alproto = ALPROTO_HTTP;
4318 
4320 
4321  uint32_t u;
4322  for (u = 0; u < httplen1; u++) {
4323  uint8_t flags = 0;
4324 
4325  if (u == 0)
4327  else if (u == (httplen1 - 1))
4329  else
4331 
4332  FLOWLOCK_WRLOCK(f);
4334  &httpbuf1[u], 1);
4335  if (r != 0) {
4336  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4337  " 0: ", u, r);
4338  FLOWLOCK_UNLOCK(f);
4339  goto end;
4340  }
4341  FLOWLOCK_UNLOCK(f);
4342  }
4343 
4344  htp_state = f->alstate;
4345  if (htp_state == NULL) {
4346  printf("no http state: ");
4347  goto end;
4348  }
4349 
4350  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4351  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
4352  if (h == NULL) {
4353  goto end;
4354  }
4355 
4356  char *name = bstr_util_strdup_to_c(h->name);
4357  if (name == NULL) {
4358  goto end;
4359  }
4360 
4361  if (strcmp(name, "Host") != 0) {
4362  printf("header name not \"Host\", instead \"%s\": ", name);
4363  free(name);
4364  goto end;
4365  }
4366  free(name);
4367 
4368  char *value = bstr_util_strdup_to_c(h->value);
4369  if (value == NULL) {
4370  goto end;
4371  }
4372 
4373  if (strcmp(value, "www.google.com\rName: Value") != 0) {
4374  printf("header value not \"www.google.com\", instead \"");
4375  PrintRawUriFp(stdout, (uint8_t *)value, strlen(value));
4376  printf("\": ");
4377  free(value);
4378  goto end;
4379  }
4380  free(value);
4381 
4382  result = 1;
4383 end:
4384  if (alp_tctx != NULL)
4387  UTHFreeFlow(f);
4388  return result;
4389 }
4390 
4391 /** \test Test basic config */
4392 static int HTPParserConfigTest01(void)
4393 {
4394  int ret = 0;
4395  char input[] = "\
4396 %YAML 1.1\n\
4397 ---\n\
4398 libhtp:\n\
4399 \n\
4400  default-config:\n\
4401  personality: IDS\n\
4402 \n\
4403  server-config:\n\
4404 \n\
4405  - apache-tomcat:\n\
4406  address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4407  personality: Tomcat_6_0\n\
4408 \n\
4409  - iis7:\n\
4410  address: \n\
4411  - 192.168.0.0/24\n\
4412  - 192.168.10.0/24\n\
4413  personality: IIS_7_0\n\
4414 ";
4415 
4417  ConfInit();
4418 
4419  ConfYamlLoadString(input, strlen(input));
4420 
4421  ConfNode *outputs;
4422  outputs = ConfGetNode("libhtp.default-config.personality");
4423  if (outputs == NULL) {
4424  goto end;
4425  }
4426 
4427  outputs = ConfGetNode("libhtp.server-config");
4428  if (outputs == NULL) {
4429  goto end;
4430  }
4431 
4432  ConfNode *node = TAILQ_FIRST(&outputs->head);
4433  if (node == NULL) {
4434  goto end;
4435  }
4436  if (strcmp(node->name, "0") != 0) {
4437  goto end;
4438  }
4439  node = TAILQ_FIRST(&node->head);
4440  if (node == NULL) {
4441  goto end;
4442  }
4443  if (strcmp(node->name, "apache-tomcat") != 0) {
4444  goto end;
4445  }
4446 
4447  int i = 0;
4448  ConfNode *n;
4449 
4450  ConfNode *node2 = ConfNodeLookupChild(node, "personality");
4451  if (node2 == NULL) {
4452  goto end;
4453  }
4454  if (strcmp(node2->val, "Tomcat_6_0") != 0) {
4455  goto end;
4456  }
4457 
4458  node = ConfNodeLookupChild(node, "address");
4459  if (node == NULL) {
4460  goto end;
4461  }
4462  TAILQ_FOREACH(n, &node->head, next) {
4463  if (n == NULL) {
4464  goto end;
4465  }
4466 
4467  switch(i) {
4468  case 0:
4469  if (strcmp(n->name, "0") != 0) {
4470  goto end;
4471  }
4472  if (strcmp(n->val, "192.168.1.0/24") != 0) {
4473  goto end;
4474  }
4475  break;
4476  case 1:
4477  if (strcmp(n->name, "1") != 0) {
4478  goto end;
4479  }
4480  if (strcmp(n->val, "127.0.0.0/8") != 0) {
4481  goto end;
4482  }
4483  break;
4484  case 2:
4485  if (strcmp(n->name, "2") != 0) {
4486  goto end;
4487  }
4488  if (strcmp(n->val, "::1") != 0) {
4489  goto end;
4490  }
4491  break;
4492  default:
4493  goto end;
4494  }
4495  i++;
4496  }
4497 
4498  outputs = ConfGetNode("libhtp.server-config");
4499  if (outputs == NULL) {
4500  goto end;
4501  }
4502 
4503  node = TAILQ_FIRST(&outputs->head);
4504  node = TAILQ_NEXT(node, next);
4505  if (node == NULL) {
4506  goto end;
4507  }
4508  if (strcmp(node->name, "1") != 0) {
4509  goto end;
4510  }
4511  node = TAILQ_FIRST(&node->head);
4512  if (node == NULL) {
4513  goto end;
4514  }
4515  if (strcmp(node->name, "iis7") != 0) {
4516  goto end;
4517  }
4518 
4519  node2 = ConfNodeLookupChild(node, "personality");
4520  if (node2 == NULL) {
4521  goto end;
4522  }
4523  if (strcmp(node2->val, "IIS_7_0") != 0) {
4524  goto end;
4525  }
4526 
4527  node = ConfNodeLookupChild(node, "address");
4528  if (node == NULL) {
4529  goto end;
4530  }
4531 
4532  i = 0;
4533  TAILQ_FOREACH(n, &node->head, next) {
4534  if (n == NULL) {
4535  goto end;
4536  }
4537 
4538  switch(i) {
4539  case 0:
4540  if (strcmp(n->name, "0") != 0) {
4541  goto end;
4542  }
4543  if (strcmp(n->val, "192.168.0.0/24") != 0) {
4544  goto end;
4545  }
4546  break;
4547  case 1:
4548  if (strcmp(n->name, "1") != 0) {
4549  goto end;
4550  }
4551  if (strcmp(n->val, "192.168.10.0/24") != 0) {
4552  goto end;
4553  }
4554  break;
4555  default:
4556  goto end;
4557  }
4558  i++;
4559  }
4560 
4561  ret = 1;
4562 
4563 end:
4564  ConfDeInit();
4566 
4567  return ret;
4568 }
4569 
4570 /** \test Test config builds radix correctly */
4571 static int HTPParserConfigTest02(void)
4572 {
4573  int ret = 0;
4574  char input[] = "\
4575 %YAML 1.1\n\
4576 ---\n\
4577 libhtp:\n\
4578 \n\
4579  default-config:\n\
4580  personality: IDS\n\
4581 \n\
4582  server-config:\n\
4583 \n\
4584  - apache-tomcat:\n\
4585  address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4586  personality: Tomcat_6_0\n\
4587 \n\
4588  - iis7:\n\
4589  address: \n\
4590  - 192.168.0.0/24\n\
4591  - 192.168.10.0/24\n\
4592  personality: IIS_7_0\n\
4593 ";
4594 
4596  ConfInit();
4598 
4599  ConfYamlLoadString(input, strlen(input));
4600 
4601  HTPConfigure();
4602 
4603  if (cfglist.cfg == NULL) {
4604  printf("No default config created.\n");
4605  goto end;
4606  }
4607 
4608  if (cfgtree == NULL) {
4609  printf("No config tree created.\n");
4610  goto end;
4611  }
4612 
4613  htp_cfg_t *htp = cfglist.cfg;
4614  uint8_t buf[128];
4615  const char *addr;
4616  void *user_data = NULL;
4617 
4618  addr = "192.168.10.42";
4619  if (inet_pton(AF_INET, addr, buf) == 1) {
4620  (void)SCRadixFindKeyIPV4BestMatch(buf, cfgtree, &user_data);
4621  if (user_data != NULL) {
4622  HTPCfgRec *htp_cfg_rec = user_data;
4623  htp = htp_cfg_rec->cfg;
4624  SCLogDebug("LIBHTP using config: %p", htp);
4625  }
4626  if (htp == NULL) {
4627  printf("Could not get config for: %s\n", addr);
4628  goto end;
4629  }
4630  }
4631  else {
4632  printf("Failed to parse address: %s\n", addr);
4633  goto end;
4634  }
4635 
4636  user_data = NULL;
4637  addr = "::1";
4638  if (inet_pton(AF_INET6, addr, buf) == 1) {
4639  (void)SCRadixFindKeyIPV6BestMatch(buf, cfgtree, &user_data);
4640  if (user_data != NULL) {
4641  HTPCfgRec *htp_cfg_rec = user_data;
4642  htp = htp_cfg_rec->cfg;
4643  SCLogDebug("LIBHTP using config: %p", htp);
4644  }
4645  if (htp == NULL) {
4646  printf("Could not get config for: %s\n", addr);
4647  goto end;
4648  }
4649  }
4650  else {
4651  printf("Failed to parse address: %s\n", addr);
4652  goto end;
4653  }
4654 
4655  ret = 1;
4656 
4657 end:
4658  HTPFreeConfig();
4659  ConfDeInit();
4662 
4663  return ret;
4664 }
4665 
4666 /** \test Test traffic is handled by the correct htp config */
4667 static int HTPParserConfigTest03(void)
4668 {
4669  int result = 1;
4670  Flow *f = NULL;
4671  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
4672  " Data is c0oL!";
4673  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4674  TcpSession ssn;
4676 
4677  HtpState *htp_state = NULL;
4678  int r = 0;
4679  char input[] = "\
4680 %YAML 1.1\n\
4681 ---\n\
4682 libhtp:\n\
4683 \n\
4684  default-config:\n\
4685  personality: IDS\n\
4686 \n\
4687  server-config:\n\
4688 \n\
4689  - apache-tomcat:\n\
4690  address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4691  personality: Tomcat_6_0\n\
4692 \n\
4693  - iis7:\n\
4694  address: \n\
4695  - 192.168.0.0/24\n\
4696  - 192.168.10.0/24\n\
4697  personality: IIS_7_0\n\
4698 ";
4699 
4701  ConfInit();
4703 
4704  ConfYamlLoadString(input, strlen(input));
4705 
4706  HTPConfigure();
4707 
4708  const char *addr = "192.168.10.42";
4709 
4710  memset(&ssn, 0, sizeof(ssn));
4711 
4712  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4713  if (f == NULL)
4714  goto end;
4715  f->protoctx = &ssn;
4716  f->proto = IPPROTO_TCP;
4717  f->alproto = ALPROTO_HTTP;
4718 
4719  htp_cfg_t *htp = cfglist.cfg;
4720 
4721  void *user_data = NULL;
4722  (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)f->dst.addr_data32, cfgtree, &user_data);
4723  if (user_data != NULL) {
4724  HTPCfgRec *htp_cfg_rec = user_data;
4725  htp = htp_cfg_rec->cfg;
4726  SCLogDebug("LIBHTP using config: %p", htp);
4727  }
4728  if (htp == NULL) {
4729  printf("Could not get config for: %s\n", addr);
4730  goto end;
4731  }
4732 
4734 
4735  uint32_t u;
4736  for (u = 0; u < httplen1; u++) {
4737  uint8_t flags = 0;
4738 
4739  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4740  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4741  else flags = STREAM_TOSERVER;
4742 
4743  FLOWLOCK_WRLOCK(f);
4745  &httpbuf1[u], 1);
4746  if (r != 0) {
4747  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4748  " 0: ", u, r);
4749  result = 0;
4750  FLOWLOCK_UNLOCK(f);
4751  goto end;
4752  }
4753  FLOWLOCK_UNLOCK(f);
4754  }
4755 
4756  htp_state = f->alstate;
4757  if (htp_state == NULL) {
4758  printf("no http state: ");
4759  result = 0;
4760  goto end;
4761  }
4762 
4763  if (HTPStateGetTxCnt(htp_state) != 2) {
4764  printf("HTPStateGetTxCnt(htp_state) failure\n");
4765  goto end;
4766  }
4767 
4768  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4769  if (tx == NULL)
4770  goto end;
4771  if (tx->cfg != htp) {
4772  printf("wrong HTP config (%p instead of %p - default=%p): ",
4773  tx->cfg, htp, cfglist.cfg);
4774  goto end;
4775  }
4776  tx = HTPStateGetTx(htp_state, 1);
4777  if (tx == NULL)
4778  goto end;
4779  if (tx->cfg != htp) {
4780  printf("wrong HTP config (%p instead of %p - default=%p): ",
4781  tx->cfg, htp, cfglist.cfg);
4782  goto end;
4783  }
4784 
4785 end:
4786  if (alp_tctx != NULL)
4788  HTPFreeConfig();
4789  ConfDeInit();
4792 
4794  UTHFreeFlow(f);
4795  return result;
4796 }
4797 
4798 /* disabled when we upgraded to libhtp 0.5.x */
4799 #if 0
4800 static int HTPParserConfigTest04(void)
4801 {
4802  int result = 0;
4803 
4804  char input[] = "\
4805 %YAML 1.1\n\
4806 ---\n\
4807 libhtp:\n\
4808 \n\
4809  default-config:\n\
4810  personality: IDS\n\
4811  path-control-char-handling: status_400\n\
4812  path-convert-utf8: yes\n\
4813  path-invalid-encoding-handling: remove_percent\n\
4814 \n\
4815  server-config:\n\
4816 \n\
4817  - apache-tomcat:\n\
4818  personality: Tomcat_6_0\n\
4819  path-invalid-utf8-handling: none\n\
4820  path-nul-encoded-handling: status_404\n\
4821  path-nul-raw-handling: status_400\n\
4822 \n\
4823  - iis7:\n\
4824  personality: IIS_7_0\n\
4825  path-replacement-char: o\n\
4826  path-unicode-mapping: status_400\n\
4827 ";
4828 
4830  ConfInit();
4832 
4833  ConfYamlLoadString(input, strlen(input));
4834 
4835  HTPConfigure();
4836 
4837  HTPCfgRec *cfg_rec = &cfglist;
4838  if (cfg_rec->cfg->path_control_char_handling != STATUS_400 ||
4839  cfg_rec->cfg->path_convert_utf8 != 1 ||
4840  cfg_rec->cfg->path_invalid_encoding_handling != URL_DECODER_REMOVE_PERCENT) {
4841  printf("failed 1\n");
4842  goto end;
4843  }
4844 
4845  cfg_rec = cfg_rec->next;
4846  if (cfg_rec->cfg->bestfit_replacement_char != 'o' ||
4847  cfg_rec->cfg->path_unicode_mapping != STATUS_400) {
4848  printf("failed 2\n");
4849  goto end;
4850  }
4851 
4852  cfg_rec = cfg_rec->next;
4853  if (cfg_rec->cfg->path_invalid_utf8_handling != NONE ||
4854  cfg_rec->cfg->path_nul_encoded_handling != STATUS_404 ||
4855  cfg_rec->cfg->path_nul_raw_handling != STATUS_400) {
4856  printf("failed 3\n");
4857  goto end;
4858  }
4859 
4860  result = 1;
4861 
4862 end:
4863  HTPFreeConfig();
4864  ConfDeInit();
4867 
4868  return result;
4869 }
4870 #endif
4871 
4872 /** \test Test %2f decoding in profile Apache_2_2
4873  *
4874  * %2f in path is left untouched
4875  * %2f in query string is normalized to %2F
4876  * %252f in query string is decoded/normalized to %2F
4877  */
4878 static int HTPParserDecodingTest01(void)
4879 {
4880  int result = 0;
4881  Flow *f = NULL;
4882  uint8_t httpbuf1[] =
4883  "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4884  "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4885  "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4886  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4887  TcpSession ssn;
4889 
4890  HtpState *htp_state = NULL;
4891  int r = 0;
4892  char input[] = "\
4893 %YAML 1.1\n\
4894 ---\n\
4895 libhtp:\n\
4896 \n\
4897  default-config:\n\
4898  personality: Apache_2\n\
4899 ";
4900 
4902  ConfInit();
4904  ConfYamlLoadString(input, strlen(input));
4905  HTPConfigure();
4906  const char *addr = "4.3.2.1";
4907  memset(&ssn, 0, sizeof(ssn));
4908 
4909  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4910  if (f == NULL)
4911  goto end;
4912  f->protoctx = &ssn;
4913  f->proto = IPPROTO_TCP;
4914  f->alproto = ALPROTO_HTTP;
4915 
4917 
4918  uint32_t u;
4919  for (u = 0; u < httplen1; u++) {
4920  uint8_t flags = 0;
4921 
4922  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4923  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4924  else flags = STREAM_TOSERVER;
4925 
4926  FLOWLOCK_WRLOCK(f);
4928  &httpbuf1[u], 1);
4929  if (r != 0) {
4930  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4931  " 0: ", u, r);
4932  FLOWLOCK_UNLOCK(f);
4933  goto end;
4934  }
4935  FLOWLOCK_UNLOCK(f);
4936  }
4937 
4938  htp_state = f->alstate;
4939  if (htp_state == NULL) {
4940  printf("no http state: ");
4941  goto end;
4942  }
4943 
4944  uint8_t ref1[] = "/abc%2fdef";
4945  size_t reflen = sizeof(ref1) - 1;
4946 
4947  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4948  if (tx == NULL)
4949  goto end;
4950  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4951  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4952  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4953  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4954  (uintmax_t)reflen,
4955  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4956  goto end;
4957  }
4958 
4959  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
4960  bstr_len(tx_ud->request_uri_normalized)) != 0)
4961  {
4962  printf("normalized uri \"");
4963  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4964  printf("\" != \"");
4965  PrintRawUriFp(stdout, ref1, reflen);
4966  printf("\": ");
4967  goto end;
4968  }
4969  }
4970 
4971  uint8_t ref2[] = "/abc/def?ghi/jkl";
4972  reflen = sizeof(ref2) - 1;
4973 
4974  tx = HTPStateGetTx(htp_state, 1);
4975  if (tx == NULL)
4976  goto end;
4977  tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4978  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4979  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4980  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4981  (uintmax_t)reflen,
4982  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4983  goto end;
4984  }
4985 
4986  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
4987  bstr_len(tx_ud->request_uri_normalized)) != 0)
4988  {
4989  printf("normalized uri \"");
4990  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4991  printf("\" != \"");
4992  PrintRawUriFp(stdout, ref2, reflen);
4993  printf("\": ");
4994  goto end;
4995  }
4996  }
4997 
4998  uint8_t ref3[] = "/abc/def?ghi%2fjkl";
4999  reflen = sizeof(ref3) - 1;
5000  tx = HTPStateGetTx(htp_state, 2);
5001  if (tx == NULL)
5002  goto end;
5003  tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5004  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5005  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5006  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5007  (uintmax_t)reflen,
5008  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5009  goto end;
5010  }
5011 
5012  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
5013  bstr_len(tx_ud->request_uri_normalized)) != 0)
5014  {
5015  printf("normalized uri \"");
5016  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5017  printf("\" != \"");
5018  PrintRawUriFp(stdout, ref3, reflen);
5019  printf("\": ");
5020  goto end;
5021  }
5022  }
5023 
5024  result = 1;
5025 
5026 end:
5027  if (alp_tctx != NULL)
5029  HTPFreeConfig();
5030  ConfDeInit();
5033 
5035  UTHFreeFlow(f);
5036  return result;
5037 }
5038 
5039 /** \test Test %2f decoding in profile IDS
5040  *
5041  * %2f in path decoded to /
5042  * %2f in query string is decoded to /
5043  * %252f in query string is decoded to %2F
5044  */
5045 static int HTPParserDecodingTest02(void)
5046 {
5047  int result = 0;
5048  Flow *f = NULL;
5049  uint8_t httpbuf1[] =
5050  "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5051  "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5052  "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5053  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5054  TcpSession ssn;
5056 
5057  HtpState *htp_state = NULL;
5058  int r = 0;
5059  char input[] = "\
5060 %YAML 1.1\n\
5061 ---\n\
5062 libhtp:\n\
5063 \n\
5064  default-config:\n\
5065  personality: IDS\n\
5066  double-decode-path: no\n\
5067  double-decode-query: no\n\
5068 ";
5069 
5071  ConfInit();
5073  ConfYamlLoadString(input, strlen(input));
5074  HTPConfigure();
5075  const char *addr = "4.3.2.1";
5076  memset(&ssn, 0, sizeof(ssn));
5077 
5078  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5079  if (f == NULL)
5080  goto end;
5081  f->protoctx = &ssn;
5082  f->proto = IPPROTO_TCP;
5083  f->alproto = ALPROTO_HTTP;
5084 
5086 
5087  uint32_t u;
5088  for (u = 0; u < httplen1; u++) {
5089  uint8_t flags = 0;
5090 
5091  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5092  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5093  else flags = STREAM_TOSERVER;
5094 
5095  FLOWLOCK_WRLOCK(f);
5097  &httpbuf1[u], 1);
5098  if (r != 0) {
5099  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5100  " 0: ", u, r);
5101  FLOWLOCK_UNLOCK(f);
5102  goto end;
5103  }
5104  FLOWLOCK_UNLOCK(f);
5105  }
5106 
5107  htp_state = f->alstate;
5108  if (htp_state == NULL) {
5109  printf("no http state: ");
5110  goto end;
5111  }
5112 
5113  uint8_t ref1[] = "/abc/def";
5114  size_t reflen = sizeof(ref1) - 1;
5115 
5116  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5117  if (tx == NULL)
5118  goto end;
5119  HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
5120  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5121  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5122  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5123  (uintmax_t)reflen,
5124  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5125  goto end;
5126  }
5127 
5128  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5129  bstr_len(tx_ud->request_uri_normalized)) != 0)
5130  {
5131  printf("normalized uri \"");
5132  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5133  printf("\" != \"");
5134  PrintRawUriFp(stdout, ref1, reflen);
5135  printf("\": ");
5136  goto end;
5137  }
5138  }
5139 
5140  uint8_t ref2[] = "/abc/def?ghi/jkl";
5141  reflen = sizeof(ref2) - 1;
5142 
5143  tx = HTPStateGetTx(htp_state, 1);
5144  if (tx == NULL)
5145  goto end;
5146  tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
5147  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5148  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5149  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5150  (uintmax_t)reflen,
5151  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5152  goto end;
5153  }
5154 
5155  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
5156  bstr_len(tx_ud->request_uri_normalized)) != 0)
5157  {
5158  printf("normalized uri \"");
5159  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5160  printf("\" != \"");
5161  PrintRawUriFp(stdout, ref2, reflen);
5162  printf("\": ");
5163  goto end;
5164  }
5165  }
5166 
5167  uint8_t ref3[] = "/abc/def?ghi%2fjkl";
5168  reflen = sizeof(ref3) - 1;
5169  tx = HTPStateGetTx(htp_state, 2);
5170  if (tx == NULL)
5171  goto end;
5172  tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5173  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5174  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5175  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX" (3): ",
5176  (uintmax_t)reflen,
5177  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5178  goto end;
5179  }
5180 
5181  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
5182  bstr_len(tx_ud->request_uri_normalized)) != 0)
5183  {
5184  printf("normalized uri \"");
5185  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5186  printf("\" != \"");
5187  PrintRawUriFp(stdout, ref3, reflen);
5188  printf("\": ");
5189  goto end;
5190  }
5191  }
5192 
5193  result = 1;
5194 
5195 end:
5196  if (alp_tctx != NULL)
5198  HTPFreeConfig();
5199  ConfDeInit();
5202 
5204  UTHFreeFlow(f);
5205  return result;
5206 }
5207 
5208 /** \test Test %2f decoding in profile IDS with double-decode-* options
5209  *
5210  * %252f in path decoded to /
5211  * %252f in query string is decoded to /
5212  */
5213 static int HTPParserDecodingTest03(void)
5214 {
5215  int result = 0;
5216  Flow *f = NULL;
5217  uint8_t httpbuf1[] =
5218  "GET /abc%252fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5219  "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5220  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5221  TcpSession ssn;
5223 
5224  HtpState *htp_state = NULL;
5225  int r = 0;
5226  char input[] = "\
5227 %YAML 1.1\n\
5228 ---\n\
5229 libhtp:\n\
5230 \n\
5231  default-config:\n\
5232  personality: IDS\n\
5233  double-decode-path: yes\n\
5234  double-decode-query: yes\n\
5235 ";
5236 
5238  ConfInit();
5240  ConfYamlLoadString(input, strlen(input));
5241  HTPConfigure();
5242  const char *addr = "4.3.2.1";
5243  memset(&ssn, 0, sizeof(ssn));
5244 
5245  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5246  if (f == NULL)
5247  goto end;
5248  f->protoctx = &ssn;
5249  f->proto = IPPROTO_TCP;
5250  f->alproto = ALPROTO_HTTP;
5251 
5253 
5254  uint32_t u;
5255  for (u = 0; u < httplen1; u++) {
5256  uint8_t flags = 0;
5257 
5258  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5259  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5260  else flags = STREAM_TOSERVER;
5261 
5262  FLOWLOCK_WRLOCK(f);
5264  &httpbuf1[u], 1);
5265  if (r != 0) {
5266  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5267  " 0: ", u, r);
5268  FLOWLOCK_UNLOCK(f);
5269  goto end;
5270  }
5271  FLOWLOCK_UNLOCK(f);
5272  }
5273 
5274  htp_state = f->alstate;
5275  if (htp_state == NULL) {
5276  printf("no http state: ");
5277  goto end;
5278  }
5279 
5280  uint8_t ref1[] = "/abc/def";
5281  size_t reflen = sizeof(ref1) - 1;
5282 
5283  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5284  if (tx == NULL)
5285  goto end;
5286  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5287  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5288  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5289  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5290  (uintmax_t)reflen,
5291  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5292  goto end;
5293  }
5294 
5295  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5296  bstr_len(tx_ud->request_uri_normalized)) != 0)
5297  {
5298  printf("normalized uri \"");
5299  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5300  printf("\" != \"");
5301  PrintRawUriFp(stdout, ref1, reflen);
5302  printf("\": ");
5303  goto end;
5304  }
5305  }
5306 
5307  uint8_t ref2[] = "/abc/def?ghi/jkl";
5308  reflen = sizeof(ref2) - 1;
5309 
5310  tx = HTPStateGetTx(htp_state, 1);
5311  if (tx == NULL)
5312  goto end;
5313  tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
5314  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5315  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5316  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5317  (uintmax_t)reflen,
5318  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5319  goto end;
5320  }
5321 
5322  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
5323  bstr_len(tx_ud->request_uri_normalized)) != 0)
5324  {
5325  printf("normalized uri \"");
5326  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5327  printf("\" != \"");
5328  PrintRawUriFp(stdout, ref2, reflen);
5329  printf("\": ");
5330  goto end;
5331  }
5332  }
5333 
5334  result = 1;
5335 
5336 end:
5337  if (alp_tctx != NULL)
5339  HTPFreeConfig();
5340  ConfDeInit();
5343 
5345  UTHFreeFlow(f);
5346  return result;
5347 }
5348 
5349 /** \test Test http:// in query profile IDS
5350  */
5351 static int HTPParserDecodingTest04(void)
5352 {
5353  int result = 0;
5354  Flow *f = NULL;
5355  uint8_t httpbuf1[] =
5356  "GET /abc/def?a=http://www.abc.com/ HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5357  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5358  TcpSession ssn;
5360 
5361  HtpState *htp_state = NULL;
5362  int r = 0;
5363  char input[] = "\
5364 %YAML 1.1\n\
5365 ---\n\
5366 libhtp:\n\
5367 \n\
5368  default-config:\n\
5369  personality: IDS\n\
5370  double-decode-path: yes\n\
5371  double-decode-query: yes\n\
5372 ";
5373 
5375  ConfInit();
5377  ConfYamlLoadString(input, strlen(input));
5378  HTPConfigure();
5379  const char *addr = "4.3.2.1";
5380  memset(&ssn, 0, sizeof(ssn));
5381 
5382  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5383  if (f == NULL)
5384  goto end;
5385  f->protoctx = &ssn;
5386  f->proto = IPPROTO_TCP;
5387  f->alproto = ALPROTO_HTTP;
5388 
5390 
5391  uint32_t u;
5392  for (u = 0; u < httplen1; u++) {
5393  uint8_t flags = 0;
5394 
5395  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5396  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5397  else flags = STREAM_TOSERVER;
5398 
5399  FLOWLOCK_WRLOCK(f);
5401  &httpbuf1[u], 1);
5402  if (r != 0) {
5403  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5404  " 0: ", u, r);
5405  FLOWLOCK_UNLOCK(f);
5406  goto end;
5407  }
5408  FLOWLOCK_UNLOCK(f);
5409  }
5410 
5411  htp_state = f->alstate;
5412  if (htp_state == NULL) {
5413  printf("no http state: ");
5414  goto end;
5415  }
5416 
5417  uint8_t ref1[] = "/abc/def?a=http://www.abc.com/";
5418  size_t reflen = sizeof(ref1) - 1;
5419 
5420  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5421  if (tx == NULL)
5422  goto end;
5423  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5424  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5425  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5426  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5427  (uintmax_t)reflen,
5428  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5429  goto end;
5430  }
5431 
5432  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5433  bstr_len(tx_ud->request_uri_normalized)) != 0)
5434  {
5435  printf("normalized uri \"");
5436  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5437  printf("\" != \"");
5438  PrintRawUriFp(stdout, ref1, reflen);
5439  printf("\": ");
5440  goto end;
5441  }
5442  }
5443 
5444  result = 1;
5445 
5446 end:
5447  if (alp_tctx != NULL)
5449  HTPFreeConfig();
5450  ConfDeInit();
5453 
5455  UTHFreeFlow(f);
5456  return result;
5457 }
5458 
5459 /** \test Test \ char in query profile IDS. Bug 739
5460  */
5461 static int HTPParserDecodingTest05(void)
5462 {
5463  int result = 0;
5464  Flow *f = NULL;
5465  uint8_t httpbuf1[] =
5466  "GET /index?id=\\\"<script>alert(document.cookie)</script> HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5467  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5468  TcpSession ssn;
5470 
5471  HtpState *htp_state = NULL;
5472  int r = 0;
5473  char input[] = "\
5474 %YAML 1.1\n\
5475 ---\n\
5476 libhtp:\n\
5477 \n\
5478  default-config:\n\
5479  personality: IDS\n\
5480  double-decode-path: yes\n\
5481  double-decode-query: yes\n\
5482 ";
5483 
5485  ConfInit();
5487  ConfYamlLoadString(input, strlen(input));
5488  HTPConfigure();
5489  const char *addr = "4.3.2.1";
5490  memset(&ssn, 0, sizeof(ssn));
5491 
5492  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5493  if (f == NULL)
5494  goto end;
5495  f->protoctx = &ssn;
5496  f->proto = IPPROTO_TCP;
5497  f->alproto = ALPROTO_HTTP;
5498 
5500 
5501  uint32_t u;
5502  for (u = 0; u < httplen1; u++) {
5503  uint8_t flags = 0;
5504 
5505  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5506  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5507  else flags = STREAM_TOSERVER;
5508 
5509  FLOWLOCK_WRLOCK(f);
5511  &httpbuf1[u], 1);
5512  if (r != 0) {
5513  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5514  " 0: ", u, r);
5515  FLOWLOCK_UNLOCK(f);
5516  goto end;
5517  }
5518  FLOWLOCK_UNLOCK(f);
5519  }
5520 
5521  htp_state = f->alstate;
5522  if (htp_state == NULL) {
5523  printf("no http state: ");
5524  goto end;
5525  }
5526 
5527  uint8_t ref1[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
5528  size_t reflen = sizeof(ref1) - 1;
5529 
5530  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5531  if (tx == NULL)
5532  goto end;
5533  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5534  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5535  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5536  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5537  (uintmax_t)reflen,
5538  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5539  goto end;
5540  }
5541 
5542  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5543  bstr_len(tx_ud->request_uri_normalized)) != 0)
5544  {
5545  printf("normalized uri \"");
5546  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5547  printf("\" != \"");
5548  PrintRawUriFp(stdout, ref1, reflen);
5549  printf("\": ");
5550  goto end;
5551  }
5552  }
5553 
5554  result = 1;
5555 
5556 end:
5557  if (alp_tctx != NULL)
5559  HTPFreeConfig();
5560  ConfDeInit();
5563 
5565  UTHFreeFlow(f);
5566  return result;
5567 }
5568 
5569 /** \test Test + char in query. Bug 1035
5570  */
5571 static int HTPParserDecodingTest06(void)
5572 {
5573  int result = 0;
5574  Flow *f = NULL;
5575  uint8_t httpbuf1[] =
5576  "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5577  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5578  TcpSession ssn;
5580 
5581  HtpState *htp_state = NULL;
5582  int r = 0;
5583  char input[] = "\
5584 %YAML 1.1\n\
5585 ---\n\
5586 libhtp:\n\
5587 \n\
5588  default-config:\n\
5589  personality: IDS\n\
5590  double-decode-path: yes\n\
5591  double-decode-query: yes\n\
5592 ";
5593 
5595  ConfInit();
5597  ConfYamlLoadString(input, strlen(input));
5598  HTPConfigure();
5599  const char *addr = "4.3.2.1";
5600  memset(&ssn, 0, sizeof(ssn));
5601 
5602  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5603  if (f == NULL)
5604  goto end;
5605  f->protoctx = &ssn;
5606  f->proto = IPPROTO_TCP;
5607  f->alproto = ALPROTO_HTTP;
5608 
5610 
5611  uint32_t u;
5612  for (u = 0; u < httplen1; u++) {
5613  uint8_t flags = 0;
5614 
5615  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5616  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5617  else flags = STREAM_TOSERVER;
5618 
5619  FLOWLOCK_WRLOCK(f);
5621  &httpbuf1[u], 1);
5622  if (r != 0) {
5623  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5624  " 0: ", u, r);
5625  FLOWLOCK_UNLOCK(f);
5626  goto end;
5627  }
5628  FLOWLOCK_UNLOCK(f);
5629  }
5630 
5631  htp_state = f->alstate;
5632  if (htp_state == NULL) {
5633  printf("no http state: ");
5634  goto end;
5635  }
5636 
5637  uint8_t ref1[] = "/put.php?ip=1.2.3.4&port=+6000";
5638  size_t reflen = sizeof(ref1) - 1;
5639 
5640  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5641  if (tx == NULL)
5642  goto end;
5643  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5644  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5645  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5646  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5647  (uintmax_t)reflen,
5648  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5649  goto end;
5650  }
5651 
5652  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5653  bstr_len(tx_ud->request_uri_normalized)) != 0)
5654  {
5655  printf("normalized uri \"");
5656  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5657  printf("\" != \"");
5658  PrintRawUriFp(stdout, ref1, reflen);
5659  printf("\": ");
5660  goto end;
5661  }
5662  }
5663 
5664  result = 1;
5665 
5666 end:
5667  if (alp_tctx != NULL)
5669  HTPFreeConfig();
5670  ConfDeInit();
5673 
5675  UTHFreeFlow(f);
5676  return result;
5677 }
5678 
5679 /** \test Test + char in query. Bug 1035
5680  */
5681 static int HTPParserDecodingTest07(void)
5682 {
5683  int result = 0;
5684  Flow *f = NULL;
5685  uint8_t httpbuf1[] =
5686  "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5687  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5688  TcpSession ssn;
5690 
5691  HtpState *htp_state = NULL;
5692  int r = 0;
5693  char input[] = "\
5694 %YAML 1.1\n\
5695 ---\n\
5696 libhtp:\n\
5697 \n\
5698  default-config:\n\
5699  personality: IDS\n\
5700  double-decode-path: yes\n\
5701  double-decode-query: yes\n\
5702  query-plusspace-decode: yes\n\
5703 ";
5704 
5706  ConfInit();
5708  ConfYamlLoadString(input, strlen(input));
5709  HTPConfigure();
5710  const char *addr = "4.3.2.1";
5711  memset(&ssn, 0, sizeof(ssn));
5712 
5713  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5714  if (f == NULL)
5715  goto end;
5716  f->protoctx = &ssn;
5717  f->proto = IPPROTO_TCP;
5718  f->alproto = ALPROTO_HTTP;
5719 
5721 
5722  uint32_t u;
5723  for (u = 0; u < httplen1; u++) {
5724  uint8_t flags = 0;
5725 
5726  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5727  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5728  else flags = STREAM_TOSERVER;
5729 
5730  FLOWLOCK_WRLOCK(f);
5732  &httpbuf1[u], 1);
5733  if (r != 0) {
5734  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5735  " 0: ", u, r);
5736  FLOWLOCK_UNLOCK(f);
5737  goto end;
5738  }
5739  FLOWLOCK_UNLOCK(f);
5740  }
5741 
5742  htp_state = f->alstate;
5743  if (htp_state == NULL) {
5744  printf("no http state: ");
5745  goto end;
5746  }
5747 
5748  uint8_t ref1[] = "/put.php?ip=1.2.3.4&port= 6000";
5749  size_t reflen = sizeof(ref1) - 1;
5750 
5751  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5752  if (tx == NULL)
5753  goto end;
5754  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5755  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5756  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5757  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5758  (uintmax_t)reflen,
5759  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5760  goto end;
5761  }
5762 
5763  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5764  bstr_len(tx_ud->request_uri_normalized)) != 0)
5765  {
5766  printf("normalized uri \"");
5767  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5768  printf("\" != \"");
5769  PrintRawUriFp(stdout, ref1, reflen);
5770  printf("\": ");
5771  goto end;
5772  }
5773  }
5774 
5775  result = 1;
5776 
5777 end:
5778  if (alp_tctx != NULL)
5779