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