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