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  //set range if present
1637  htp_header_t *h_content_range = htp_table_get_c(tx->response_headers, "content-range");
1638  if (h_content_range != NULL) {
1639  HTPFileSetRange(hstate, h_content_range->value);
1640  }
1641  }
1642  }
1643  else
1644  {
1645  /* otherwise, just store the data */
1646 
1647  if (!(htud->tcflags & HTP_DONTSTORE)) {
1648  result = HTPFileStoreChunk(hstate, data, data_len, STREAM_TOCLIENT);
1649  SCLogDebug("result %d", result);
1650  if (result == -1) {
1651  goto end;
1652  } else if (result == -2) {
1653  /* we know for sure we're not storing the file */
1654  htud->tcflags |= HTP_DONTSTORE;
1655  }
1656  }
1657  }
1658 
1659  htud->response_body.body_parsed += data_len;
1660  return 0;
1661 end:
1662  return -1;
1663 }
1664 
1665 /**
1666  * \brief Function callback to append chunks for Requests
1667  * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1668  * \retval int HTP_OK if all goes well
1669  */
1670 static int HTPCallbackRequestBodyData(htp_tx_data_t *d)
1671 {
1672  SCEnter();
1673 
1674  if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_REQUEST_BODY))
1675  SCReturnInt(HTP_OK);
1676 
1677  if (d->data == NULL || d->len == 0)
1678  SCReturnInt(HTP_OK);
1679 
1680 #ifdef PRINT
1681  printf("HTPBODY START: \n");
1682  PrintRawDataFp(stdout, (uint8_t *)d->data, d->len);
1683  printf("HTPBODY END: \n");
1684 #endif
1685 
1686  HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
1687  if (hstate == NULL) {
1688  SCReturnInt(HTP_ERROR);
1689  }
1690 
1691  SCLogDebug("New request body data available at %p -> %p -> %p, bodylen "
1692  "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
1693 
1694  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
1695  if (tx_ud == NULL) {
1696  tx_ud = HTPMalloc(sizeof(HtpTxUserData));
1697  if (unlikely(tx_ud == NULL)) {
1698  SCReturnInt(HTP_OK);
1699  }
1700  memset(tx_ud, 0, sizeof(HtpTxUserData));
1701 
1702  /* Set the user data for handling body chunks on this transaction */
1703  htp_tx_set_user_data(d->tx, tx_ud);
1704  }
1705  if (!tx_ud->response_body_init) {
1706  tx_ud->response_body_init = 1;
1707 
1708  if (d->tx->request_method_number == HTP_M_POST) {
1709  SCLogDebug("POST");
1710  int r = HtpRequestBodySetupMultipart(d, tx_ud);
1711  if (r == 1) {
1713  } else if (r == 0) {
1715  SCLogDebug("not multipart");
1716  }
1717  } else if (d->tx->request_method_number == HTP_M_PUT) {
1718  if (HtpRequestBodySetupPUT(d, tx_ud) == 0) {
1720  }
1721  }
1722  }
1723 
1724  /* see if we can get rid of htp body chunks */
1725  HtpBodyPrune(hstate, &tx_ud->request_body, STREAM_TOSERVER);
1726 
1727  SCLogDebug("tx_ud->request_body.content_len_so_far %"PRIu64, tx_ud->request_body.content_len_so_far);
1728  SCLogDebug("hstate->cfg->request.body_limit %u", hstate->cfg->request.body_limit);
1729 
1730  /* within limits, add the body chunk to the state. */
1731  if (hstate->cfg->request.body_limit == 0 || tx_ud->request_body.content_len_so_far < hstate->cfg->request.body_limit)
1732  {
1733  uint32_t len = (uint32_t)d->len;
1734 
1735  if (hstate->cfg->request.body_limit > 0 &&
1736  (tx_ud->request_body.content_len_so_far + len) > hstate->cfg->request.body_limit)
1737  {
1738  len = hstate->cfg->request.body_limit - tx_ud->request_body.content_len_so_far;
1739  BUG_ON(len > (uint32_t)d->len);
1740  }
1741  SCLogDebug("len %u", len);
1742 
1743  HtpBodyAppendChunk(&hstate->cfg->request, &tx_ud->request_body, d->data, len);
1744 
1745  const uint8_t *chunks_buffer = NULL;
1746  uint32_t chunks_buffer_len = 0;
1747 
1749  /* multi-part body handling starts here */
1750  if (!(tx_ud->tsflags & HTP_BOUNDARY_SET)) {
1751  goto end;
1752  }
1753 
1754  HtpRequestBodyReassemble(tx_ud, &chunks_buffer, &chunks_buffer_len);
1755  if (chunks_buffer == NULL) {
1756  goto end;
1757  }
1758 #ifdef PRINT
1759  printf("REASSCHUNK START: \n");
1760  PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
1761  printf("REASSCHUNK END: \n");
1762 #endif
1763 
1764  HtpRequestBodyHandleMultipart(hstate, tx_ud, d->tx, chunks_buffer, chunks_buffer_len);
1765 
1766  } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_POST) {
1767  HtpRequestBodyHandlePOST(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
1768  } else if (tx_ud->request_body_type == HTP_BODY_REQUEST_PUT) {
1769  HtpRequestBodyHandlePUT(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
1770  }
1771 
1772  } else {
1773  if (tx_ud->tsflags & HTP_FILENAME_SET) {
1774  SCLogDebug("closing file that was being stored");
1775  (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOSERVER);
1776  tx_ud->tsflags &= ~HTP_FILENAME_SET;
1777  }
1778  }
1779 
1780 end:
1781  if (hstate->conn != NULL) {
1782  SCLogDebug("checking body size %"PRIu64" against inspect limit %u (cur %"PRIu64", last %"PRIu64")",
1784  hstate->cfg->request.inspect_min_size,
1785  (uint64_t)hstate->conn->in_data_counter, hstate->last_request_data_stamp);
1786 
1787  /* if we reach the inspect_min_size we'll trigger inspection,
1788  * so make sure that raw stream is also inspected. Set the
1789  * data to be used to the amount of raw bytes we've seen to
1790  * get here. */
1791  if (tx_ud->request_body.body_inspected == 0 &&
1793  if ((uint64_t)hstate->conn->in_data_counter > hstate->last_request_data_stamp &&
1794  (uint64_t)hstate->conn->in_data_counter - hstate->last_request_data_stamp < (uint64_t)UINT_MAX)
1795  {
1796  uint32_t x = (uint32_t)((uint64_t)hstate->conn->in_data_counter - hstate->last_request_data_stamp);
1797 
1798  /* body still in progress, but due to min inspect size we need to inspect now */
1801  }
1802  /* after the start of the body, disable the depth logic */
1803  } else if (tx_ud->request_body.body_inspected > 0) {
1805  }
1806  }
1807  SCReturnInt(HTP_OK);
1808 }
1809 
1810 /**
1811  * \brief Function callback to append chunks for Responses
1812  * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
1813  * \retval int HTP_OK if all goes well
1814  */
1815 static int HTPCallbackResponseBodyData(htp_tx_data_t *d)
1816 {
1817  SCEnter();
1818 
1819  if (!(SC_ATOMIC_GET(htp_config_flags) & HTP_REQUIRE_RESPONSE_BODY))
1820  SCReturnInt(HTP_OK);
1821 
1822  if (d->data == NULL || d->len == 0)
1823  SCReturnInt(HTP_OK);
1824 
1825  HtpState *hstate = htp_connp_get_user_data(d->tx->connp);
1826  if (hstate == NULL) {
1827  SCReturnInt(HTP_ERROR);
1828  }
1829 
1830  SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
1831  "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
1832 
1833  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(d->tx);
1834  if (tx_ud == NULL) {
1835  tx_ud = HTPMalloc(sizeof(HtpTxUserData));
1836  if (unlikely(tx_ud == NULL)) {
1837  SCReturnInt(HTP_OK);
1838  }
1839  memset(tx_ud, 0, sizeof(HtpTxUserData));
1840 
1841  /* Set the user data for handling body chunks on this transaction */
1842  htp_tx_set_user_data(d->tx, tx_ud);
1843  }
1844  if (!tx_ud->request_body_init) {
1845  tx_ud->request_body_init = 1;
1846  }
1847 
1848  /* see if we can get rid of htp body chunks */
1849  HtpBodyPrune(hstate, &tx_ud->response_body, STREAM_TOCLIENT);
1850 
1851  SCLogDebug("tx_ud->response_body.content_len_so_far %"PRIu64, tx_ud->response_body.content_len_so_far);
1852  SCLogDebug("hstate->cfg->response.body_limit %u", hstate->cfg->response.body_limit);
1853 
1854  /* within limits, add the body chunk to the state. */
1855  if (hstate->cfg->response.body_limit == 0 || tx_ud->response_body.content_len_so_far < hstate->cfg->response.body_limit)
1856  {
1857  uint32_t len = (uint32_t)d->len;
1858 
1859  if (hstate->cfg->response.body_limit > 0 &&
1860  (tx_ud->response_body.content_len_so_far + len) > hstate->cfg->response.body_limit)
1861  {
1862  len = hstate->cfg->response.body_limit - tx_ud->response_body.content_len_so_far;
1863  BUG_ON(len > (uint32_t)d->len);
1864  }
1865  SCLogDebug("len %u", len);
1866 
1867  HtpBodyAppendChunk(&hstate->cfg->response, &tx_ud->response_body, d->data, len);
1868 
1869  HtpResponseBodyHandle(hstate, tx_ud, d->tx, (uint8_t *)d->data, (uint32_t)d->len);
1870  } else {
1871  if (tx_ud->tcflags & HTP_FILENAME_SET) {
1872  SCLogDebug("closing file that was being stored");
1873  (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOCLIENT);
1874  tx_ud->tcflags &= ~HTP_FILENAME_SET;
1875  }
1876  }
1877 
1878  if (hstate->conn != NULL) {
1879  SCLogDebug("checking body size %"PRIu64" against inspect limit %u (cur %"PRIu64", last %"PRIu64")",
1881  hstate->cfg->response.inspect_min_size,
1882  (uint64_t)hstate->conn->in_data_counter, hstate->last_response_data_stamp);
1883  /* if we reach the inspect_min_size we'll trigger inspection,
1884  * so make sure that raw stream is also inspected. Set the
1885  * data to be used to the amount of raw bytes we've seen to
1886  * get here. */
1887  if (tx_ud->response_body.body_inspected == 0 &&
1889  if ((uint64_t)hstate->conn->out_data_counter > hstate->last_response_data_stamp &&
1890  (uint64_t)hstate->conn->out_data_counter - hstate->last_response_data_stamp < (uint64_t)UINT_MAX)
1891  {
1892  uint32_t x = (uint32_t)((uint64_t)hstate->conn->out_data_counter - hstate->last_response_data_stamp);
1893 
1894  /* body still in progress, but due to min inspect size we need to inspect now */
1897  }
1898  /* after the start of the body, disable the depth logic */
1899  } else if (tx_ud->response_body.body_inspected > 0) {
1901  }
1902  }
1903  SCReturnInt(HTP_OK);
1904 }
1905 
1906 /**
1907  * \brief Print the stats of the HTTP requests
1908  */
1910 {
1911 #ifdef DEBUG
1912  SCEnter();
1913  SCMutexLock(&htp_state_mem_lock);
1914  SCLogDebug("http_state_memcnt %"PRIu64", http_state_memuse %"PRIu64"",
1915  htp_state_memcnt, htp_state_memuse);
1916  SCMutexUnlock(&htp_state_mem_lock);
1917  SCReturn;
1918 #endif
1919 }
1920 
1921 /** \brief Clears the HTTP server configuration memory used by HTP library */
1922 void HTPFreeConfig(void)
1923 {
1924  SCEnter();
1925 
1926  if (!AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "http") ||
1927  !AppLayerParserConfParserEnabled("tcp", "http"))
1928  {
1929  SCReturn;
1930  }
1931 
1932  HTPCfgRec *nextrec = cfglist.next;
1933  SCRadixReleaseRadixTree(cfgtree);
1934  cfgtree = NULL;
1935  htp_config_destroy(cfglist.cfg);
1936  while (nextrec != NULL) {
1937  HTPCfgRec *htprec = nextrec;
1938  nextrec = nextrec->next;
1939 
1940  htp_config_destroy(htprec->cfg);
1941  SCFree(htprec);
1942  }
1943  HTPDestroyMemcap();
1944  SCReturn;
1945 }
1946 
1947 static int HTPCallbackRequestHasTrailer(htp_tx_t *tx)
1948 {
1949  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
1950  if (htud != NULL) {
1951  htud->request_has_trailers = 1;
1952  }
1953  return HTP_OK;
1954 }
1955 
1956 static int HTPCallbackResponseHasTrailer(htp_tx_t *tx)
1957 {
1958  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
1959  if (htud != NULL) {
1960  htud->response_has_trailers = 1;
1961  }
1962  return HTP_OK;
1963 }
1964 
1965 /**\internal
1966  * \brief called at start of request
1967  * Set min inspect size.
1968  */
1969 static int HTPCallbackRequestStart(htp_tx_t *tx)
1970 {
1971  HtpState *hstate = htp_connp_get_user_data(tx->connp);
1972  if (hstate == NULL) {
1973  SCReturnInt(HTP_ERROR);
1974  }
1975 
1976  if (hstate->cfg)
1978  hstate->cfg->request.inspect_min_size);
1979  SCReturnInt(HTP_OK);
1980 }
1981 
1982 /**\internal
1983  * \brief called at start of response
1984  * Set min inspect size.
1985  */
1986 static int HTPCallbackResponseStart(htp_tx_t *tx)
1987 {
1988  HtpState *hstate = htp_connp_get_user_data(tx->connp);
1989  if (hstate == NULL) {
1990  SCReturnInt(HTP_ERROR);
1991  }
1992 
1993  if (hstate->cfg)
1995  hstate->cfg->response.inspect_min_size);
1996  SCReturnInt(HTP_OK);
1997 }
1998 
1999 
2000 /**
2001  * \brief callback for request to store the recent incoming request
2002  in to the recent_in_tx for the given htp state
2003  * \param connp pointer to the current connection parser which has the htp
2004  * state in it as user data
2005  */
2006 static int HTPCallbackRequest(htp_tx_t *tx)
2007 {
2008  SCEnter();
2009 
2010  if (tx == NULL) {
2011  SCReturnInt(HTP_ERROR);
2012  }
2013 
2014  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2015  if (hstate == NULL) {
2016  SCReturnInt(HTP_ERROR);
2017  }
2018 
2019  SCLogDebug("transaction_cnt %"PRIu64", list_size %"PRIu64,
2020  hstate->transaction_cnt, HTPStateGetTxCnt(hstate));
2021 
2022  SCLogDebug("HTTP request completed");
2023 
2024  HTPErrorCheckTxRequestFlags(hstate, tx);
2025 
2026  HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
2027  if (htud != NULL) {
2028  if (htud->tsflags & HTP_FILENAME_SET) {
2029  SCLogDebug("closing file that was being stored");
2030  (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER);
2031  htud->tsflags &= ~HTP_FILENAME_SET;
2033  (uint32_t)hstate->conn->in_data_counter);
2034  }
2035  }
2036 
2037  hstate->last_request_data_stamp = (uint64_t)hstate->conn->in_data_counter;
2038  /* request done, do raw reassembly now to inspect state and stream
2039  * at the same time. */
2041  SCReturnInt(HTP_OK);
2042 }
2043 
2044 /**
2045  * \brief callback for response to remove the recent received requests
2046  from the recent_in_tx for the given htp state
2047  * \param connp pointer to the current connection parser which has the htp
2048  * state in it as user data
2049  */
2050 static int HTPCallbackResponse(htp_tx_t *tx)
2051 {
2052  SCEnter();
2053 
2054  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2055  if (hstate == NULL) {
2056  SCReturnInt(HTP_ERROR);
2057  }
2058 
2059  /* we have one whole transaction now */
2060  hstate->transaction_cnt++;
2061 
2062  HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2063  if (htud != NULL) {
2064  if (htud->tcflags & HTP_FILENAME_SET) {
2065  SCLogDebug("closing file that was being stored");
2066  (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOCLIENT);
2067  htud->tcflags &= ~HTP_FILENAME_SET;
2068  }
2069  }
2070 
2071  /* response done, do raw reassembly now to inspect state and stream
2072  * at the same time. */
2074 
2075  /* handle HTTP CONNECT */
2076  if (tx->request_method_number == HTP_M_CONNECT) {
2077  /* any 2XX status response implies that the connection will become
2078  a tunnel immediately after this packet (RFC 7230, 3.3.3). */
2079  if ((tx->response_status_number >= 200) &&
2080  (tx->response_status_number < 300) &&
2081  (hstate->transaction_cnt == 1)) {
2082  uint16_t dp = 0;
2083  if (tx->request_port_number != -1) {
2084  dp = (uint16_t)tx->request_port_number;
2085  }
2086  // both ALPROTO_HTTP and ALPROTO_TLS are normal options
2088  tx->request_progress = HTP_REQUEST_COMPLETE;
2089  tx->response_progress = HTP_RESPONSE_COMPLETE;
2090  }
2091  }
2092 
2093  hstate->last_response_data_stamp = (uint64_t)hstate->conn->out_data_counter;
2094  SCReturnInt(HTP_OK);
2095 }
2096 
2097 static int HTPCallbackRequestLine(htp_tx_t *tx)
2098 {
2099  HtpTxUserData *tx_ud;
2100  bstr *request_uri_normalized;
2101  HtpState *hstate = htp_connp_get_user_data(tx->connp);
2102  const HTPCfgRec *cfg = hstate->cfg;
2103 
2104  request_uri_normalized = SCHTPGenerateNormalizedUri(tx, tx->parsed_uri, cfg->uri_include_all);
2105  if (request_uri_normalized == NULL)
2106  return HTP_OK;
2107 
2108  tx_ud = htp_tx_get_user_data(tx);
2109  if (likely(tx_ud == NULL)) {
2110  tx_ud = HTPMalloc(sizeof(*tx_ud));
2111  if (unlikely(tx_ud == NULL)) {
2112  bstr_free(request_uri_normalized);
2113  return HTP_OK;
2114  }
2115  memset(tx_ud, 0, sizeof(*tx_ud));
2116  htp_tx_set_user_data(tx, tx_ud);
2117  }
2118  if (unlikely(tx_ud->request_uri_normalized != NULL))
2119  bstr_free(tx_ud->request_uri_normalized);
2120  tx_ud->request_uri_normalized = request_uri_normalized;
2121 
2122  if (tx->flags) {
2123  HTPErrorCheckTxRequestFlags(hstate, tx);
2124  }
2125  return HTP_OK;
2126 }
2127 
2128 static int HTPCallbackDoubleDecodeQuery(htp_tx_t *tx)
2129 {
2130  if (tx->parsed_uri == NULL || tx->parsed_uri->query == NULL)
2131  return HTP_OK;
2132 
2133  uint64_t flags = 0;
2134  htp_urldecode_inplace(tx->cfg, HTP_DECODER_URLENCODED, tx->parsed_uri->query, &flags);
2135 
2136  return HTP_OK;
2137 }
2138 
2139 static int HTPCallbackDoubleDecodePath(htp_tx_t *tx)
2140 {
2141  if (tx->parsed_uri == NULL || tx->parsed_uri->path == NULL)
2142  return HTP_OK;
2143 
2144  uint64_t flags = 0;
2145  htp_urldecode_inplace(tx->cfg, HTP_DECODER_URL_PATH, tx->parsed_uri->path, &flags);
2146 
2147  return HTP_OK;
2148 }
2149 
2150 static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data)
2151 {
2152  void *ptmp;
2153  if (tx_data->len == 0 || tx_data->tx == NULL)
2154  return HTP_OK;
2155 
2156  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
2157  if (tx_ud == NULL) {
2158  tx_ud = HTPMalloc(sizeof(*tx_ud));
2159  if (unlikely(tx_ud == NULL))
2160  return HTP_OK;
2161  memset(tx_ud, 0, sizeof(*tx_ud));
2162  htp_tx_set_user_data(tx_data->tx, tx_ud);
2163  }
2164  ptmp = HTPRealloc(tx_ud->request_headers_raw,
2165  tx_ud->request_headers_raw_len,
2166  tx_ud->request_headers_raw_len + tx_data->len);
2167  if (ptmp == NULL) {
2168  /* error: we're freeing the entire user data */
2169  HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2170  HtpTxUserDataFree(hstate, tx_ud);
2171  htp_tx_set_user_data(tx_data->tx, NULL);
2172  return HTP_OK;
2173  }
2174  tx_ud->request_headers_raw = ptmp;
2175 
2176  memcpy(tx_ud->request_headers_raw + tx_ud->request_headers_raw_len,
2177  tx_data->data, tx_data->len);
2178  tx_ud->request_headers_raw_len += tx_data->len;
2179 
2180  if (tx_data->tx && tx_data->tx->flags) {
2181  HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2182  HTPErrorCheckTxRequestFlags(hstate, tx_data->tx);
2183  }
2184  return HTP_OK;
2185 }
2186 
2187 static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data)
2188 {
2189  void *ptmp;
2190  if (tx_data->len == 0 || tx_data->tx == NULL)
2191  return HTP_OK;
2192 
2193  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx_data->tx);
2194  if (tx_ud == NULL) {
2195  tx_ud = HTPMalloc(sizeof(*tx_ud));
2196  if (unlikely(tx_ud == NULL))
2197  return HTP_OK;
2198  memset(tx_ud, 0, sizeof(*tx_ud));
2199  htp_tx_set_user_data(tx_data->tx, tx_ud);
2200  }
2201  ptmp = HTPRealloc(tx_ud->response_headers_raw,
2202  tx_ud->response_headers_raw_len,
2203  tx_ud->response_headers_raw_len + tx_data->len);
2204  if (ptmp == NULL) {
2205  /* error: we're freeing the entire user data */
2206  HtpState *hstate = htp_connp_get_user_data(tx_data->tx->connp);
2207  HtpTxUserDataFree(hstate, tx_ud);
2208  htp_tx_set_user_data(tx_data->tx, NULL);
2209  return HTP_OK;
2210  }
2211  tx_ud->response_headers_raw = ptmp;
2212 
2213  memcpy(tx_ud->response_headers_raw + tx_ud->response_headers_raw_len,
2214  tx_data->data, tx_data->len);
2215  tx_ud->response_headers_raw_len += tx_data->len;
2216 
2217  return HTP_OK;
2218 }
2219 
2220 /*
2221  * We have a similar set function called HTPConfigSetDefaultsPhase1.
2222  */
2223 static void HTPConfigSetDefaultsPhase1(HTPCfgRec *cfg_prec)
2224 {
2225  cfg_prec->uri_include_all = FALSE;
2232 
2233  if (!g_disable_randomness) {
2235  } else {
2236  cfg_prec->randomize = 0;
2237  }
2239 
2240  htp_config_register_request_header_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
2241  htp_config_register_request_trailer_data(cfg_prec->cfg, HTPCallbackRequestHeaderData);
2242  htp_config_register_response_header_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
2243  htp_config_register_response_trailer_data(cfg_prec->cfg, HTPCallbackResponseHeaderData);
2244 
2245  htp_config_register_request_trailer(cfg_prec->cfg, HTPCallbackRequestHasTrailer);
2246  htp_config_register_response_trailer(cfg_prec->cfg, HTPCallbackResponseHasTrailer);
2247 
2248  htp_config_register_request_body_data(cfg_prec->cfg, HTPCallbackRequestBodyData);
2249  htp_config_register_response_body_data(cfg_prec->cfg, HTPCallbackResponseBodyData);
2250 
2251  htp_config_register_request_start(cfg_prec->cfg, HTPCallbackRequestStart);
2252  htp_config_register_request_complete(cfg_prec->cfg, HTPCallbackRequest);
2253 
2254  htp_config_register_response_start(cfg_prec->cfg, HTPCallbackResponseStart);
2255  htp_config_register_response_complete(cfg_prec->cfg, HTPCallbackResponse);
2256 
2257  htp_config_set_parse_request_cookies(cfg_prec->cfg, 0);
2258  htp_config_set_parse_request_auth(cfg_prec->cfg, 0);
2259 
2260  /* don't convert + to space by default */
2261  htp_config_set_plusspace_decode(cfg_prec->cfg, HTP_DECODER_URLENCODED, 0);
2262 
2263  /* libhtp <= 0.5.9 doesn't use soft limit, but it's impossible to set
2264  * only the hard limit. So we set both here to the (current) htp defaults.
2265  * The reason we do this is that if the user sets the hard limit in the
2266  * config, we have to set the soft limit as well. If libhtp starts using
2267  * the soft limit in the future, we at least make sure we control what
2268  * it's value is. */
2269  htp_config_set_field_limits(cfg_prec->cfg,
2272  return;
2273 }
2274 
2275 /* hack: htp random range code expects random values in range of 0-RAND_MAX,
2276  * but we can get both <0 and >RAND_MAX values from RandomGet
2277  */
2278 static int RandomGetWrap(void)
2279 {
2280  unsigned long r;
2281 
2282  do {
2283  r = RandomGet();
2284  } while(r >= ULONG_MAX - (ULONG_MAX % RAND_MAX));
2285 
2286  return r % RAND_MAX;
2287 }
2288 
2289 /*
2290  * We have this splitup so that in case double decoding has been enabled
2291  * for query and path, they would be called first on the callback queue,
2292  * before the callback set by Phase2() is called. We need this, since
2293  * the callback in Phase2() generates the normalized uri which utilizes
2294  * the query and path. */
2295 static void HTPConfigSetDefaultsPhase2(const char *name, HTPCfgRec *cfg_prec)
2296 {
2297  /* randomize inspection size if needed */
2298  if (cfg_prec->randomize) {
2299  int rdrange = cfg_prec->randomize_range;
2300 
2301  long int r = RandomGetWrap();
2302  cfg_prec->request.inspect_min_size +=
2303  (int) (cfg_prec->request.inspect_min_size *
2304  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2305 
2306  r = RandomGetWrap();
2307  cfg_prec->request.inspect_window +=
2308  (int) (cfg_prec->request.inspect_window *
2309  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2310  SCLogConfig("'%s' server has 'request-body-minimal-inspect-size' set to"
2311  " %d and 'request-body-inspect-window' set to %d after"
2312  " randomization.",
2313  name,
2314  cfg_prec->request.inspect_min_size,
2315  cfg_prec->request.inspect_window);
2316 
2317 
2318  r = RandomGetWrap();
2319  cfg_prec->response.inspect_min_size +=
2320  (int) (cfg_prec->response.inspect_min_size *
2321  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2322 
2323  r = RandomGetWrap();
2324  cfg_prec->response.inspect_window +=
2325  (int) (cfg_prec->response.inspect_window *
2326  (r * 1.0 / RAND_MAX - 0.5) * rdrange / 100);
2327 
2328  SCLogConfig("'%s' server has 'response-body-minimal-inspect-size' set to"
2329  " %d and 'response-body-inspect-window' set to %d after"
2330  " randomization.",
2331  name,
2332  cfg_prec->response.inspect_min_size,
2333  cfg_prec->response.inspect_window);
2334  }
2335 
2336  htp_config_register_request_line(cfg_prec->cfg, HTPCallbackRequestLine);
2337 
2338  cfg_prec->request.sbcfg.flags = 0;
2339  cfg_prec->request.sbcfg.buf_size = cfg_prec->request.inspect_window ?
2340  cfg_prec->request.inspect_window : 256;
2341  cfg_prec->request.sbcfg.buf_slide = 0;
2342  cfg_prec->request.sbcfg.Malloc = HTPMalloc;
2343  cfg_prec->request.sbcfg.Calloc = HTPCalloc;
2344  cfg_prec->request.sbcfg.Realloc = HTPRealloc;
2345  cfg_prec->request.sbcfg.Free = HTPFree;
2346 
2347  cfg_prec->response.sbcfg.flags = 0;
2348  cfg_prec->response.sbcfg.buf_size = cfg_prec->response.inspect_window ?
2349  cfg_prec->response.inspect_window : 256;
2350  cfg_prec->response.sbcfg.buf_slide = 0;
2351  cfg_prec->response.sbcfg.Malloc = HTPMalloc;
2352  cfg_prec->response.sbcfg.Calloc = HTPCalloc;
2353  cfg_prec->response.sbcfg.Realloc = HTPRealloc;
2354  cfg_prec->response.sbcfg.Free = HTPFree;
2355  return;
2356 }
2357 
2358 static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, ConfNode *s,
2359  SCRadixTree *tree)
2360 {
2361  if (cfg_prec == NULL || s == NULL || tree == NULL)
2362  return;
2363 
2364  ConfNode *p = NULL;
2365 
2366  /* Default Parameters */
2367  TAILQ_FOREACH(p, &s->head, next) {
2368 
2369  if (strcasecmp("address", p->name) == 0) {
2370  ConfNode *pval;
2371  /* Addresses */
2372  TAILQ_FOREACH(pval, &p->head, next) {
2373  SCLogDebug("LIBHTP server %s: %s=%s", s->name, p->name,
2374  pval->val);
2375 
2376  /* IPV6 or IPV4? */
2377  if (strchr(pval->val, ':') != NULL) {
2378  SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
2379  s->name, pval->val, cfg_prec->cfg);
2380  if (SCRadixAddKeyIPV6String(pval->val, tree, cfg_prec) == NULL) {
2381  SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed to "
2382  "add ipv6 server %s, ignoring", pval->val);
2383  }
2384  } else {
2385  SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
2386  s->name, pval->val, cfg_prec->cfg);
2387  if (SCRadixAddKeyIPV4String(pval->val, tree, cfg_prec) == NULL) {
2388  SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP failed "
2389  "to add ipv4 server %s, ignoring",
2390  pval->val);
2391  }
2392  } /* else - if (strchr(pval->val, ':') != NULL) */
2393  } /* TAILQ_FOREACH(pval, &p->head, next) */
2394 
2395  } else if (strcasecmp("personality", p->name) == 0) {
2396  /* Personalities */
2397  int personality = HTPLookupPersonality(p->val);
2398  SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
2399  SCLogDebug("LIBHTP default: %s = %s", p->name, p->val);
2400 
2401  if (personality >= 0) {
2402  SCLogDebug("LIBHTP default: %s=%s (%d)", p->name, p->val,
2403  personality);
2404  if (htp_config_set_server_personality(cfg_prec->cfg, personality) == HTP_ERROR){
2405  SCLogWarning(SC_ERR_INVALID_VALUE, "LIBHTP Failed adding "
2406  "personality \"%s\", ignoring", p->val);
2407  } else {
2408  SCLogDebug("LIBHTP personality set to %s",
2409  HTPLookupPersonalityString(personality));
2410  }
2411 
2412  /* The IDS personality by default converts the path (and due to
2413  * our query string callback also the query string) to lowercase.
2414  * Signatures do not expect this, so override it. */
2415  htp_config_set_convert_lowercase(cfg_prec->cfg, HTP_DECODER_URL_PATH, 0);
2416  } else {
2417  SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Unknown personality "
2418  "\"%s\", ignoring", p->val);
2419  continue;
2420  }
2421 
2422  } else if (strcasecmp("request-body-limit", p->name) == 0 ||
2423  strcasecmp("request_body_limit", p->name) == 0) {
2424  if (ParseSizeStringU32(p->val, &cfg_prec->request.body_limit) < 0) {
2425  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-limit "
2426  "from conf file - %s. Killing engine", p->val);
2427  exit(EXIT_FAILURE);
2428  }
2429 
2430  } else if (strcasecmp("response-body-limit", p->name) == 0) {
2431  if (ParseSizeStringU32(p->val, &cfg_prec->response.body_limit) < 0) {
2432  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-limit "
2433  "from conf file - %s. Killing engine", p->val);
2434  exit(EXIT_FAILURE);
2435  }
2436 
2437  } else if (strcasecmp("request-body-minimal-inspect-size", p->name) == 0) {
2438  if (ParseSizeStringU32(p->val, &cfg_prec->request.inspect_min_size) < 0) {
2439  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-minimal-inspect-size "
2440  "from conf file - %s. Killing engine", p->val);
2441  exit(EXIT_FAILURE);
2442  }
2443 
2444  } else if (strcasecmp("request-body-inspect-window", p->name) == 0) {
2445  if (ParseSizeStringU32(p->val, &cfg_prec->request.inspect_window) < 0) {
2446  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing request-body-inspect-window "
2447  "from conf file - %s. Killing engine", p->val);
2448  exit(EXIT_FAILURE);
2449  }
2450 
2451  } else if (strcasecmp("double-decode-path", p->name) == 0) {
2452  if (ConfValIsTrue(p->val)) {
2453  htp_config_register_request_line(cfg_prec->cfg,
2454  HTPCallbackDoubleDecodeQuery);
2455  }
2456 
2457  } else if (strcasecmp("double-decode-query", p->name) == 0) {
2458  if (ConfValIsTrue(p->val)) {
2459  htp_config_register_request_line(cfg_prec->cfg,
2460  HTPCallbackDoubleDecodePath);
2461  }
2462 
2463  } else if (strcasecmp("response-body-minimal-inspect-size", p->name) == 0) {
2464  if (ParseSizeStringU32(p->val, &cfg_prec->response.inspect_min_size) < 0) {
2465  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-minimal-inspect-size "
2466  "from conf file - %s. Killing engine", p->val);
2467  exit(EXIT_FAILURE);
2468  }
2469 
2470  } else if (strcasecmp("response-body-inspect-window", p->name) == 0) {
2471  if (ParseSizeStringU32(p->val, &cfg_prec->response.inspect_window) < 0) {
2472  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
2473  "from conf file - %s. Killing engine", p->val);
2474  exit(EXIT_FAILURE);
2475  }
2476 
2477  } else if (strcasecmp("response-body-decompress-layer-limit", p->name) == 0) {
2478  uint32_t value = 2;
2479  if (ParseSizeStringU32(p->val, &value) < 0) {
2480  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing response-body-inspect-window "
2481  "from conf file - %s. Killing engine", p->val);
2482  exit(EXIT_FAILURE);
2483  }
2484 #ifdef HAVE_HTP_CONFIG_SET_RESPONSE_DECOMPRESSION_LAYER_LIMIT
2485  htp_config_set_response_decompression_layer_limit(cfg_prec->cfg, value);
2486 #else
2487  SCLogWarning(SC_WARN_OUTDATED_LIBHTP, "can't set response-body-decompress-layer-limit "
2488  "to %u, libhtp version too old", value);
2489 #endif
2490  } else if (strcasecmp("path-convert-backslash-separators", p->name) == 0) {
2491  htp_config_set_backslash_convert_slashes(cfg_prec->cfg,
2492  HTP_DECODER_URL_PATH,
2493  ConfValIsTrue(p->val));
2494  } else if (strcasecmp("path-bestfit-replacement-char", p->name) == 0) {
2495  if (strlen(p->val) == 1) {
2496  htp_config_set_bestfit_replacement_byte(cfg_prec->cfg,
2497  HTP_DECODER_URL_PATH,
2498  p->val[0]);
2499  } else {
2500  SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
2501  "for libhtp param path-bestfit-replacement-char");
2502  }
2503  } else if (strcasecmp("path-convert-lowercase", p->name) == 0) {
2504  htp_config_set_convert_lowercase(cfg_prec->cfg,
2505  HTP_DECODER_URL_PATH,
2506  ConfValIsTrue(p->val));
2507  } else if (strcasecmp("path-nul-encoded-terminates", p->name) == 0) {
2508  htp_config_set_nul_encoded_terminates(cfg_prec->cfg,
2509  HTP_DECODER_URL_PATH,
2510  ConfValIsTrue(p->val));
2511  } else if (strcasecmp("path-nul-raw-terminates", p->name) == 0) {
2512  htp_config_set_nul_raw_terminates(cfg_prec->cfg,
2513  HTP_DECODER_URL_PATH,
2514  ConfValIsTrue(p->val));
2515  } else if (strcasecmp("path-separators-compress", p->name) == 0) {
2516  htp_config_set_path_separators_compress(cfg_prec->cfg,
2517  HTP_DECODER_URL_PATH,
2518  ConfValIsTrue(p->val));
2519  } else if (strcasecmp("path-separators-decode", p->name) == 0) {
2520  htp_config_set_path_separators_decode(cfg_prec->cfg,
2521  HTP_DECODER_URL_PATH,
2522  ConfValIsTrue(p->val));
2523  } else if (strcasecmp("path-u-encoding-decode", p->name) == 0) {
2524  htp_config_set_u_encoding_decode(cfg_prec->cfg,
2525  HTP_DECODER_URL_PATH,
2526  ConfValIsTrue(p->val));
2527  } else if (strcasecmp("path-url-encoding-invalid-handling", p->name) == 0) {
2528  enum htp_url_encoding_handling_t handling;
2529  if (strcasecmp(p->val, "preserve_percent") == 0) {
2530  handling = HTP_URL_DECODE_PRESERVE_PERCENT;
2531  } else if (strcasecmp(p->val, "remove_percent") == 0) {
2532  handling = HTP_URL_DECODE_REMOVE_PERCENT;
2533  } else if (strcasecmp(p->val, "decode_invalid") == 0) {
2534  handling = HTP_URL_DECODE_PROCESS_INVALID;
2535  } else {
2536  SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY, "Invalid entry "
2537  "for libhtp param path-url-encoding-invalid-handling");
2538  return;
2539  }
2540  htp_config_set_url_encoding_invalid_handling(cfg_prec->cfg,
2541  HTP_DECODER_URL_PATH,
2542  handling);
2543  } else if (strcasecmp("path-utf8-convert-bestfit", p->name) == 0) {
2544  htp_config_set_utf8_convert_bestfit(cfg_prec->cfg,
2545  HTP_DECODER_URL_PATH,
2546  ConfValIsTrue(p->val));
2547  } else if (strcasecmp("uri-include-all", p->name) == 0) {
2548  cfg_prec->uri_include_all = ConfValIsTrue(p->val);
2549  SCLogDebug("uri-include-all %s",
2550  cfg_prec->uri_include_all ? "enabled" : "disabled");
2551  } else if (strcasecmp("query-plusspace-decode", p->name) == 0) {
2552  htp_config_set_plusspace_decode(cfg_prec->cfg,
2553  HTP_DECODER_URLENCODED,
2554  ConfValIsTrue(p->val));
2555  } else if (strcasecmp("meta-field-limit", p->name) == 0) {
2556  uint32_t limit = 0;
2557  if (ParseSizeStringU32(p->val, &limit) < 0) {
2558  SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
2559  "from conf file - %s. Killing engine", p->val);
2560  exit(EXIT_FAILURE);
2561  }
2562  if (limit == 0) {
2563  SCLogError(SC_ERR_SIZE_PARSE, "Error meta-field-limit "
2564  "from conf file cannot be 0. Killing engine");
2565  exit(EXIT_FAILURE);
2566  }
2567  /* set default soft-limit with our new hard limit */
2568  htp_config_set_field_limits(cfg_prec->cfg,
2570  (size_t)limit);
2571  } else if (strcasecmp("randomize-inspection-sizes", p->name) == 0) {
2572  if (!g_disable_randomness) {
2573  cfg_prec->randomize = ConfValIsTrue(p->val);
2574  }
2575  } else if (strcasecmp("randomize-inspection-range", p->name) == 0) {
2576  uint32_t range = atoi(p->val);
2577  if (range > 100) {
2578  SCLogError(SC_ERR_SIZE_PARSE, "Invalid value for randomize"
2579  " inspection range setting from conf file - %s."
2580  " It should be inferior to 100."
2581  " Killing engine",
2582  p->val);
2583  exit(EXIT_FAILURE);
2584  }
2585  cfg_prec->randomize_range = range;
2586  } else if (strcasecmp("http-body-inline", p->name) == 0) {
2587  if (ConfValIsTrue(p->val)) {
2588  cfg_prec->http_body_inline = 1;
2589  } else if (ConfValIsFalse(p->val)) {
2590  cfg_prec->http_body_inline = 0;
2591  } else {
2592  if (strcmp("auto", p->val) != 0) {
2593  WarnInvalidConfEntry("http_body_inline", "%s", "auto");
2594  }
2595  if (EngineModeIsIPS()) {
2596  cfg_prec->http_body_inline = 1;
2597  } else {
2598  cfg_prec->http_body_inline = 0;
2599  }
2600  }
2601  } else if (strcasecmp("swf-decompression", p->name) == 0) {
2602  ConfNode *pval;
2603 
2604  TAILQ_FOREACH(pval, &p->head, next) {
2605  if (strcasecmp("enabled", pval->name) == 0) {
2606  if (ConfValIsTrue(pval->val)) {
2607  cfg_prec->swf_decompression_enabled = 1;
2608  } else if (ConfValIsFalse(pval->val)) {
2609  cfg_prec->swf_decompression_enabled = 0;
2610  } else {
2611  WarnInvalidConfEntry("swf-decompression.enabled", "%s", "no");
2612  }
2613  } else if (strcasecmp("type", pval->name) == 0) {
2614  if (strcasecmp("no", pval->val) == 0) {
2616  } else if (strcasecmp("deflate", pval->val) == 0) {
2618  } else if (strcasecmp("lzma", pval->val) == 0) {
2620  } else if (strcasecmp("both", pval->val) == 0) {
2622  } else {
2624  "Invalid entry for "
2625  "swf-decompression.type: %s - "
2626  "Killing engine", pval->val);
2627  exit(EXIT_FAILURE);
2628  }
2629  } else if (strcasecmp("compress-depth", pval->name) == 0) {
2630  if (ParseSizeStringU32(pval->val, &cfg_prec->swf_compress_depth) < 0) {
2632  "Error parsing swf-decompression.compression-depth "
2633  "from conf file - %s. Killing engine", p->val);
2634  exit(EXIT_FAILURE);
2635  }
2636  } else if (strcasecmp("decompress-depth", pval->name) == 0) {
2637  if (ParseSizeStringU32(pval->val, &cfg_prec->swf_decompress_depth) < 0) {
2639  "Error parsing swf-decompression.decompression-depth "
2640  "from conf file - %s. Killing engine", p->val);
2641  exit(EXIT_FAILURE);
2642  }
2643  } else {
2644  SCLogWarning(SC_ERR_UNKNOWN_VALUE, "Ignoring unknown param %s", pval->name);
2645  }
2646  }
2647  } else {
2648  SCLogWarning(SC_ERR_UNKNOWN_VALUE, "LIBHTP Ignoring unknown "
2649  "default config: %s", p->name);
2650  }
2651  } /* TAILQ_FOREACH(p, &default_config->head, next) */
2652 
2653  return;
2654 }
2655 
2656 void HTPConfigure(void)
2657 {
2658  SCEnter();
2659 
2660  cfglist.next = NULL;
2661 
2662  cfgtree = SCRadixCreateRadixTree(NULL, NULL);
2663  if (NULL == cfgtree)
2664  exit(EXIT_FAILURE);
2665 
2666  /* Default Config */
2667  cfglist.cfg = htp_config_create();
2668  if (NULL == cfglist.cfg) {
2669  SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP default config");
2670  exit(EXIT_FAILURE);
2671  }
2672  SCLogDebug("LIBHTP default config: %p", cfglist.cfg);
2673  HTPConfigSetDefaultsPhase1(&cfglist);
2674  if (ConfGetNode("app-layer.protocols.http.libhtp") == NULL) {
2675  HTPConfigParseParameters(&cfglist, ConfGetNode("libhtp.default-config"),
2676  cfgtree);
2677  } else {
2678  HTPConfigParseParameters(&cfglist, ConfGetNode("app-layer.protocols.http.libhtp.default-config"), cfgtree);
2679  }
2680  HTPConfigSetDefaultsPhase2("default", &cfglist);
2681 
2682  HTPParseMemcap();
2683 
2684  /* Read server config and create a parser for each IP in radix tree */
2685  ConfNode *server_config = ConfGetNode("app-layer.protocols.http.libhtp.server-config");
2686  if (server_config == NULL) {
2687  server_config = ConfGetNode("libhtp.server-config");
2688  if (server_config == NULL) {
2689  SCLogDebug("LIBHTP Configuring %p", server_config);
2690  SCReturn;
2691  }
2692  }
2693  SCLogDebug("LIBHTP Configuring %p", server_config);
2694 
2695  ConfNode *si;
2696  /* Server Nodes */
2697  TAILQ_FOREACH(si, &server_config->head, next) {
2698  /* Need the named node, not the index */
2699  ConfNode *s = TAILQ_FIRST(&si->head);
2700  if (NULL == s) {
2701  SCLogDebug("LIBHTP s NULL");
2702  continue;
2703  }
2704 
2705  SCLogDebug("LIBHTP server %s", s->name);
2706 
2707  HTPCfgRec *nextrec = cfglist.next;
2708  HTPCfgRec *htprec = SCMalloc(sizeof(HTPCfgRec));
2709  if (NULL == htprec)
2710  exit(EXIT_FAILURE);
2711  memset(htprec, 0x00, sizeof(*htprec));
2712 
2713  cfglist.next = htprec;
2714 
2715  cfglist.next->next = nextrec;
2716  cfglist.next->cfg = htp_config_create();
2717  if (NULL == cfglist.next->cfg) {
2718  SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP server config");
2719  exit(EXIT_FAILURE);
2720  }
2721 
2722  HTPConfigSetDefaultsPhase1(htprec);
2723  HTPConfigParseParameters(htprec, s, cfgtree);
2724  HTPConfigSetDefaultsPhase2(s->name, htprec);
2725  }
2726 
2727  SCReturn;
2728 }
2729 
2731 {
2732 #ifdef DEBUG
2733  SCMutexLock(&htp_state_mem_lock);
2734  SCLogPerf("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
2735  SCMutexUnlock(&htp_state_mem_lock);
2736 #endif
2737 }
2738 
2739 /** \internal
2740  * \brief get files callback
2741  * \param state state ptr
2742  * \param direction flow direction
2743  * \retval files files ptr
2744  */
2745 static FileContainer *HTPStateGetFiles(void *state, uint8_t direction)
2746 {
2747  if (state == NULL)
2748  return NULL;
2749 
2750  HtpState *http_state = (HtpState *)state;
2751 
2752  if (direction & STREAM_TOCLIENT) {
2753  SCReturnPtr(http_state->files_tc, "FileContainer");
2754  } else {
2755  SCReturnPtr(http_state->files_ts, "FileContainer");
2756  }
2757 }
2758 
2759 static int HTPStateGetAlstateProgress(void *tx, uint8_t direction)
2760 {
2761  if (direction & STREAM_TOSERVER)
2762  return ((htp_tx_t *)tx)->request_progress;
2763  else
2764  return ((htp_tx_t *)tx)->response_progress;
2765 }
2766 
2767 static uint64_t HTPStateGetTxCnt(void *alstate)
2768 {
2769  HtpState *http_state = (HtpState *)alstate;
2770 
2771  if (http_state != NULL && http_state->conn != NULL) {
2772  const uint64_t size = (uint64_t)htp_list_size(http_state->conn->transactions);
2773  SCLogDebug("size %"PRIu64, size);
2774  return size;
2775  } else {
2776  return 0ULL;
2777  }
2778 }
2779 
2780 static void *HTPStateGetTx(void *alstate, uint64_t tx_id)
2781 {
2782  HtpState *http_state = (HtpState *)alstate;
2783 
2784  if (http_state != NULL && http_state->conn != NULL)
2785  return htp_list_get(http_state->conn->transactions, tx_id);
2786  else
2787  return NULL;
2788 }
2789 
2790 static void HTPStateSetTxLogged(void *alstate, void *vtx, LoggerId bits)
2791 {
2792  htp_tx_t *tx = (htp_tx_t *)vtx;
2793  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2794  if (tx_ud)
2795  tx_ud->logged = bits;
2796 }
2797 
2798 static LoggerId HTPStateGetTxLogged(void *alstate, void *vtx)
2799 {
2800  htp_tx_t *tx = (htp_tx_t *)vtx;
2801  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
2802  if (tx_ud != NULL)
2803  return tx_ud->logged;
2804 
2805  return 0;
2806 }
2807 
2808 static int HTPStateGetAlstateProgressCompletionStatus(uint8_t direction)
2809 {
2810  return (direction & STREAM_TOSERVER) ? HTP_REQUEST_COMPLETE : HTP_RESPONSE_COMPLETE;
2811 }
2812 
2813 static int HTPStateGetEventInfo(const char *event_name,
2814  int *event_id, AppLayerEventType *event_type)
2815 {
2816  *event_id = SCMapEnumNameToValue(event_name, http_decoder_event_table);
2817  if (*event_id == -1) {
2818  SCLogError(SC_ERR_INVALID_ENUM_MAP, "event \"%s\" not present in "
2819  "http's enum map table.", event_name);
2820  /* this should be treated as fatal */
2821  return -1;
2822  }
2823 
2824  *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
2825 
2826  return 0;
2827 }
2828 
2829 static void HTPStateTruncate(void *state, uint8_t direction)
2830 {
2831  FileContainer *fc = HTPStateGetFiles(state, direction);
2832  if (fc != NULL) {
2834  }
2835 }
2836 
2837 static DetectEngineState *HTPGetTxDetectState(void *vtx)
2838 {
2839  htp_tx_t *tx = (htp_tx_t *)vtx;
2840  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2841  return tx_ud ? tx_ud->de_state : NULL;
2842 }
2843 
2844 static int HTPSetTxDetectState(void *vtx, DetectEngineState *s)
2845 {
2846  htp_tx_t *tx = (htp_tx_t *)vtx;
2847  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2848  if (tx_ud == NULL) {
2849  tx_ud = HTPMalloc(sizeof(*tx_ud));
2850  if (unlikely(tx_ud == NULL))
2851  return -ENOMEM;
2852  memset(tx_ud, 0, sizeof(*tx_ud));
2853  htp_tx_set_user_data(tx, tx_ud);
2854  }
2855  tx_ud->de_state = s;
2856  return 0;
2857 }
2858 
2859 static uint64_t HTPGetTxDetectFlags(void *vtx, uint8_t dir)
2860 {
2861  htp_tx_t *tx = (htp_tx_t *)vtx;
2862  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2863  if (tx_ud) {
2864  if (dir & STREAM_TOSERVER) {
2865  return tx_ud->detect_flags_ts;
2866  } else {
2867  return tx_ud->detect_flags_tc;
2868  }
2869  }
2870  return 0;
2871 }
2872 
2873 static void HTPSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t detect_flags)
2874 {
2875  htp_tx_t *tx = (htp_tx_t *)vtx;
2876  HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
2877  if (tx_ud == NULL) {
2878  tx_ud = HTPMalloc(sizeof(*tx_ud));
2879  if (unlikely(tx_ud == NULL))
2880  return;
2881  memset(tx_ud, 0, sizeof(*tx_ud));
2882  htp_tx_set_user_data(tx, tx_ud);
2883  }
2884  if (dir & STREAM_TOSERVER) {
2885  tx_ud->detect_flags_ts = detect_flags;
2886  } else {
2887  tx_ud->detect_flags_tc = detect_flags;
2888  }
2889  return;
2890 }
2891 
2892 static int HTPRegisterPatternsForProtocolDetection(void)
2893 {
2894  const char *methods[] = { "GET", "PUT", "POST", "HEAD", "TRACE", "OPTIONS",
2895  "CONNECT", "DELETE", "PATCH", "PROPFIND", "PROPPATCH", "MKCOL",
2896  "COPY", "MOVE", "LOCK", "UNLOCK", "CHECKOUT", "UNCHECKOUT", "CHECKIN",
2897  "UPDATE", "LABEL", "REPORT", "MKWORKSPACE", "MKACTIVITY", "MERGE",
2898  "INVALID", "VERSION-CONTROL", "BASELINE-CONTROL", NULL};
2899  const char *spacings[] = { "|20|", "|09|", NULL };
2900  const char *versions[] = { "HTTP/0.9", "HTTP/1.0", "HTTP/1.1", NULL };
2901 
2902  int methods_pos;
2903  int spacings_pos;
2904  int versions_pos;
2905  int register_result;
2906  char method_buffer[32] = "";
2907 
2908  /* Loop through all the methods ands spacings and register the patterns */
2909  for (methods_pos = 0; methods[methods_pos]; methods_pos++) {
2910  for (spacings_pos = 0; spacings[spacings_pos]; spacings_pos++) {
2911 
2912  /* Combine the method name and the spacing */
2913  snprintf(method_buffer, sizeof(method_buffer), "%s%s", methods[methods_pos], spacings[spacings_pos]);
2914 
2915  /* Register the new method+spacing pattern
2916  * 3 is subtracted from the length since the spacing is hex typed as |xx|
2917  * but the pattern matching should only be one char
2918  */
2919  register_result = AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP,
2920  ALPROTO_HTTP, method_buffer, strlen(method_buffer)-3, 0, STREAM_TOSERVER);
2921  if (register_result < 0) {
2922  return -1;
2923  }
2924  }
2925  }
2926 
2927  /* Loop through all the http verions patterns that are TO_CLIENT */
2928  for (versions_pos = 0; versions[versions_pos]; versions_pos++) {
2929  register_result = AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP,
2930  ALPROTO_HTTP, versions[versions_pos], strlen(versions[versions_pos]),
2931  0, STREAM_TOCLIENT);
2932  if (register_result < 0) {
2933  return -1;
2934  }
2935  }
2936 
2937  return 0;
2938 }
2939 
2940 /**
2941  * \brief Register the HTTP protocol and state handling functions to APP layer
2942  * of the engine.
2943  */
2945 {
2946  SCEnter();
2947 
2948  const char *proto_name = "http";
2949 
2950  /** HTTP */
2951  if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
2953  if (HTPRegisterPatternsForProtocolDetection() < 0)
2954  return;
2955  } else {
2956  SCLogInfo("Protocol detection and parser disabled for %s protocol",
2957  proto_name);
2958  return;
2959  }
2960 
2961  if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
2962  AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
2963  AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTransactionFree);
2964  AppLayerParserRegisterGetFilesFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetFiles);
2965  AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetAlstateProgress);
2966  AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTxCnt);
2967  AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTx);
2968  AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetTxLogged,
2969  HTPStateSetTxLogged);
2971  HTPStateGetAlstateProgressCompletionStatus);
2972  AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPGetEvents);
2973  AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_HTTP, HTPStateGetEventInfo);
2974 
2975  AppLayerParserRegisterTruncateFunc(IPPROTO_TCP, ALPROTO_HTTP, HTPStateTruncate);
2977  HTPGetTxDetectState, HTPSetTxDetectState);
2979  HTPGetTxDetectFlags, HTPSetTxDetectFlags);
2980 
2982  HTPHandleRequestData);
2984  HTPHandleResponseData);
2985  SC_ATOMIC_INIT(htp_config_flags);
2988  HTPConfigure();
2989  } else {
2990  SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
2991  "still on.", proto_name);
2992  }
2993 #ifdef UNITTESTS
2995 #endif
2996 
2997  SCReturn;
2998 }
2999 
3000 #ifdef UNITTESTS
3001 static HTPCfgRec cfglist_backup;
3002 
3004 {
3005  cfglist_backup = cfglist;
3006 
3007  return;
3008 }
3009 
3011 {
3012  cfglist = cfglist_backup;
3013 
3014  return;
3015 }
3016 
3017 /** \test Test case where chunks are sent in smaller chunks and check the
3018  * response of the parser from HTP library. */
3019 static int HTPParserTest01(void)
3020 {
3021  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3022  " Data is c0oL!";
3023  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3024 
3025  TcpSession ssn;
3026  memset(&ssn, 0, sizeof(ssn));
3027 
3029  FAIL_IF_NULL(alp_tctx);
3030 
3031  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3032  FAIL_IF_NULL(f);
3033  f->protoctx = &ssn;
3034  f->proto = IPPROTO_TCP;
3035  f->alproto = ALPROTO_HTTP;
3036 
3038 
3039  uint32_t u;
3040  for (u = 0; u < httplen1; u++) {
3041  uint8_t flags = 0;
3042 
3043  if (u == 0)
3044  flags = STREAM_TOSERVER|STREAM_START;
3045  else if (u == (httplen1 - 1))
3046  flags = STREAM_TOSERVER|STREAM_EOF;
3047  else
3048  flags = STREAM_TOSERVER;
3049 
3050  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3051  &httpbuf1[u], 1);
3052  FAIL_IF(r != 0);
3053  }
3054 
3055  HtpState *htp_state = f->alstate;
3056  FAIL_IF_NULL(htp_state);
3057 
3058  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3059  FAIL_IF_NULL(tx);
3060 
3061  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3062  FAIL_IF_NULL(h);
3063 
3064  FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3065  FAIL_IF(tx->request_method_number != HTP_M_POST);
3066  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3067 
3068  AppLayerParserThreadCtxFree(alp_tctx);
3070  UTHFreeFlow(f);
3071  PASS;
3072 }
3073 
3074 /** \test Test folding in 1 read case */
3075 static int HTPParserTest01b(void)
3076 {
3077  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3078  " Data is c0oL!";
3079  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3080 
3081  TcpSession ssn;
3082  memset(&ssn, 0, sizeof(ssn));
3083 
3085  FAIL_IF_NULL(alp_tctx);
3086 
3087  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3088  FAIL_IF_NULL(f);
3089  f->protoctx = &ssn;
3090  f->proto = IPPROTO_TCP;
3091  f->alproto = ALPROTO_HTTP;
3092 
3094 
3096  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3097  httpbuf1, httplen1);
3098  FAIL_IF(r != 0);
3099 
3100  HtpState *htp_state = f->alstate;
3101  FAIL_IF_NULL(htp_state);
3102 
3103  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3104  FAIL_IF_NULL(tx);
3105 
3106  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3107  FAIL_IF_NULL(h);
3108 
3109  FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3110  FAIL_IF(tx->request_method_number != HTP_M_POST);
3111  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3112 
3113  AppLayerParserThreadCtxFree(alp_tctx);
3115  UTHFreeFlow(f);
3116  PASS;
3117 }
3118 
3119 /** \test Test folding in 1byte per read case */
3120 static int HTPParserTest01c(void)
3121 {
3122  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent:\r\n Victor/1.0\r\n\r\nPost"
3123  " Data is c0oL!";
3124  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3125 
3126  TcpSession ssn;
3127  memset(&ssn, 0, sizeof(ssn));
3128 
3130  FAIL_IF_NULL(alp_tctx);
3131 
3132  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3133  FAIL_IF_NULL(f);
3134  f->protoctx = &ssn;
3135  f->proto = IPPROTO_TCP;
3136  f->alproto = ALPROTO_HTTP;
3137 
3139 
3140  uint32_t u;
3141  for (u = 0; u < httplen1; u++) {
3142  uint8_t flags = 0;
3143 
3144  if (u == 0)
3145  flags = STREAM_TOSERVER|STREAM_START;
3146  else if (u == (httplen1 - 1))
3147  flags = STREAM_TOSERVER|STREAM_EOF;
3148  else
3149  flags = STREAM_TOSERVER;
3150 
3151  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3152  &httpbuf1[u], 1);
3153  FAIL_IF(r != 0);
3154  }
3155 
3156  HtpState *htp_state = f->alstate;
3157  FAIL_IF_NULL(htp_state);
3158 
3159  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3160  FAIL_IF_NULL(tx);
3161 
3162  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3163  FAIL_IF_NULL(h);
3164 
3165  FAIL_IF(strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0"));
3166  FAIL_IF(tx->request_method_number != HTP_M_POST);
3167  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_0);
3168 
3169  AppLayerParserThreadCtxFree(alp_tctx);
3171  UTHFreeFlow(f);
3172  PASS;
3173 }
3174 
3175 /** \test Test case where chunks are sent in smaller chunks and check the
3176  * response of the parser from HTP library. */
3177 static int HTPParserTest01a(void)
3178 {
3179  int result = 0;
3180  Flow *f = NULL;
3181  uint8_t httpbuf1[] = " POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
3182  " Data is c0oL!";
3183  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3184  TcpSession ssn;
3185  HtpState *htp_state = NULL;
3186  int r = 0;
3188 
3189  memset(&ssn, 0, sizeof(ssn));
3190 
3191  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3192  if (f == NULL)
3193  goto end;
3194  f->protoctx = &ssn;
3195  f->proto = IPPROTO_TCP;
3196  f->alproto = ALPROTO_HTTP;
3197 
3199 
3200  uint32_t u;
3201  for (u = 0; u < httplen1; u++) {
3202  uint8_t flags = 0;
3203 
3204  if (u == 0)
3205  flags = STREAM_TOSERVER|STREAM_START;
3206  else if (u == (httplen1 - 1))
3207  flags = STREAM_TOSERVER|STREAM_EOF;
3208  else
3209  flags = STREAM_TOSERVER;
3210 
3211  FLOWLOCK_WRLOCK(f);
3212  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3213  &httpbuf1[u], 1);
3214  if (r != 0) {
3215  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3216  " 0: ", u, r);
3217  FLOWLOCK_UNLOCK(f);
3218  goto end;
3219  }
3220  FLOWLOCK_UNLOCK(f);
3221  }
3222 
3223  htp_state = f->alstate;
3224  if (htp_state == NULL) {
3225  printf("no http state: ");
3226  goto end;
3227  }
3228 
3229  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3230  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3231  if (strcmp(bstr_util_strdup_to_c(h->value), "Victor/1.0")
3232  || tx->request_method_number != HTP_M_POST ||
3233  tx->request_protocol_number != HTP_PROTOCOL_1_0)
3234  {
3235  printf("expected header value: Victor/1.0 and got %s: and expected"
3236  " method: POST and got %s, expected protocol number HTTP/1.0"
3237  " and got: %s \n", bstr_util_strdup_to_c(h->value),
3238  bstr_util_strdup_to_c(tx->request_method),
3239  bstr_util_strdup_to_c(tx->request_protocol));
3240  goto end;
3241  }
3242  result = 1;
3243 end:
3244  if (alp_tctx != NULL)
3245  AppLayerParserThreadCtxFree(alp_tctx);
3247  UTHFreeFlow(f);
3248  return result;
3249 }
3250 
3251 /** \test See how it deals with an incomplete request. */
3252 static int HTPParserTest02(void)
3253 {
3254  int result = 0;
3255  Flow *f = NULL;
3256  uint8_t httpbuf1[] = "POST";
3257  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3258  TcpSession ssn;
3259  HtpState *http_state = NULL;
3261 
3262  memset(&ssn, 0, sizeof(ssn));
3263 
3264  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3265  if (f == NULL)
3266  goto end;
3267  f->protoctx = &ssn;
3268  f->proto = IPPROTO_TCP;
3269  f->alproto = ALPROTO_HTTP;
3270 
3272 
3273  FLOWLOCK_WRLOCK(f);
3274  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3276  httpbuf1,
3277  httplen1);
3278  if (r != 0) {
3279  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
3280  FLOWLOCK_UNLOCK(f);
3281  goto end;
3282  }
3283  FLOWLOCK_UNLOCK(f);
3284 
3285  http_state = f->alstate;
3286  if (http_state == NULL) {
3287  printf("no http state: ");
3288  goto end;
3289  }
3290 
3291  htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3292  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3293  if ((tx->request_method) != NULL || h != NULL)
3294  {
3295  printf("expected method NULL, got %s \n", bstr_util_strdup_to_c(tx->request_method));
3296  goto end;
3297  }
3298  result = 1;
3299 end:
3300  if (alp_tctx != NULL)
3301  AppLayerParserThreadCtxFree(alp_tctx);
3303  UTHFreeFlow(f);
3304  return result;
3305 }
3306 
3307 /** \test Test case where method is invalid and data is sent in smaller chunks
3308  * and check the response of the parser from HTP library. */
3309 static int HTPParserTest03(void)
3310 {
3311  int result = 0;
3312  Flow *f = NULL;
3313  uint8_t httpbuf1[] = "HELLO / HTTP/1.0\r\n";
3314  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3315  TcpSession ssn;
3316  HtpState *htp_state = NULL;
3317  int r = 0;
3319 
3320  memset(&ssn, 0, sizeof(ssn));
3321 
3322  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3323  if (f == NULL)
3324  goto end;
3325  f->protoctx = &ssn;
3326  f->proto = IPPROTO_TCP;
3327  f->alproto = ALPROTO_HTTP;
3328 
3330 
3331  uint32_t u;
3332  for (u = 0; u < httplen1; u++) {
3333  uint8_t flags = 0;
3334 
3335  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
3336  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
3337  else flags = STREAM_TOSERVER;
3338 
3339  FLOWLOCK_WRLOCK(f);
3340  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3341  &httpbuf1[u], 1);
3342  if (r != 0) {
3343  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3344  " 0: ", u, r);
3345  FLOWLOCK_UNLOCK(f);
3346  goto end;
3347  }
3348  FLOWLOCK_UNLOCK(f);
3349  }
3350  htp_state = f->alstate;
3351  if (htp_state == NULL) {
3352  printf("no http state: ");
3353  goto end;
3354  }
3355 
3356  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3357 
3358  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3359  if (tx->request_method_number != HTP_M_UNKNOWN ||
3360  h != NULL || tx->request_protocol_number != HTP_PROTOCOL_1_0)
3361  {
3362  printf("expected method M_UNKNOWN and got %s: , expected protocol "
3363  "HTTP/1.0 and got %s \n", bstr_util_strdup_to_c(tx->request_method),
3364  bstr_util_strdup_to_c(tx->request_protocol));
3365  goto end;
3366  }
3367  result = 1;
3368 end:
3369  if (alp_tctx != NULL)
3370  AppLayerParserThreadCtxFree(alp_tctx);
3372  UTHFreeFlow(f);
3373  return result;
3374 }
3375 
3376 /** \test Test case where invalid data is sent and check the response of the
3377  * parser from HTP library. */
3378 static int HTPParserTest04(void)
3379 {
3380  int result = 0;
3381  Flow *f = NULL;
3382  HtpState *htp_state = NULL;
3383  uint8_t httpbuf1[] = "World!\r\n";
3384  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3385  TcpSession ssn;
3386  int r = 0;
3388 
3389  memset(&ssn, 0, sizeof(ssn));
3390 
3391  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3392  if (f == NULL)
3393  goto end;
3394  f->protoctx = &ssn;
3395  f->proto = IPPROTO_TCP;
3396  f->alproto = ALPROTO_HTTP;
3397 
3399 
3400  FLOWLOCK_WRLOCK(f);
3401  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3403  httpbuf1,
3404  httplen1);
3405  if (r != 0) {
3406  FLOWLOCK_UNLOCK(f);
3407  goto end;
3408  }
3409  FLOWLOCK_UNLOCK(f);
3410 
3411  htp_state = f->alstate;
3412  if (htp_state == NULL) {
3413  printf("no http state: ");
3414  goto end;
3415  }
3416 
3417  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3418  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3419  if (tx->request_method_number != HTP_M_UNKNOWN ||
3420  h != NULL || tx->request_protocol_number != HTP_PROTOCOL_0_9)
3421  {
3422  printf("expected method M_UNKNOWN and got %s: , expected protocol "
3423  "NULL and got %s \n", bstr_util_strdup_to_c(tx->request_method),
3424  bstr_util_strdup_to_c(tx->request_protocol));
3425  goto end;
3426  }
3427  result = 1;
3428 end:
3429  if (alp_tctx != NULL)
3430  AppLayerParserThreadCtxFree(alp_tctx);
3432  UTHFreeFlow(f);
3433  return result;
3434 }
3435 
3436 /** \test Test both sides of a http stream mixed up to see if the HTP parser
3437  * properly parsed them and also keeps them separated. */
3438 static int HTPParserTest05(void)
3439 {
3440  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\nContent-Length: 17\r\n\r\n";
3441  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3442  uint8_t httpbuf2[] = "Post D";
3443  uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
3444  uint8_t httpbuf3[] = "ata is c0oL!";
3445  uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
3446 
3447  uint8_t httpbuf4[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
3448  uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
3449  uint8_t httpbuf5[] = "post R";
3450  uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
3451  uint8_t httpbuf6[] = "esults are tha bomb!";
3452  uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
3453 
3454  TcpSession ssn;
3455  memset(&ssn, 0, sizeof(ssn));
3456 
3458  FAIL_IF_NULL(alp_tctx);
3459 
3460  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3461  FAIL_IF_NULL(f);
3462  f->protoctx = &ssn;
3463  f->proto = IPPROTO_TCP;
3464  f->alproto = ALPROTO_HTTP;
3465 
3467 
3468  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3469  STREAM_TOSERVER | STREAM_START, httpbuf1,
3470  httplen1);
3471  FAIL_IF(r != 0);
3472 
3473  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3474  STREAM_TOCLIENT | STREAM_START, httpbuf4,
3475  httplen4);
3476  FAIL_IF(r != 0);
3477 
3478  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOCLIENT,
3479  httpbuf5, httplen5);
3480  FAIL_IF(r != 0);
3481 
3482  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
3483  httpbuf2, httplen2);
3484  FAIL_IF(r != 0);
3485 
3486  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3487  STREAM_TOSERVER | STREAM_EOF, httpbuf3, httplen3);
3488  FAIL_IF(r != 0);
3489 
3490  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3491  STREAM_TOCLIENT | STREAM_EOF, httpbuf6, httplen6);
3492  FAIL_IF(r != 0);
3493 
3494  HtpState *http_state = f->alstate;
3495  FAIL_IF_NULL(http_state);
3496 
3497  htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3498  FAIL_IF_NULL(tx);
3499  FAIL_IF_NOT(tx->request_method_number == HTP_M_POST);
3500  FAIL_IF_NOT(tx->request_protocol_number == HTP_PROTOCOL_1_0);
3501 
3502  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3503  FAIL_IF_NULL(h);
3504 
3505  FAIL_IF_NOT(tx->response_status_number == 200);
3506 
3507  AppLayerParserThreadCtxFree(alp_tctx);
3509  UTHFreeFlow(f);
3510  PASS;
3511 }
3512 
3513 /** \test Test proper chunked encoded response body
3514  */
3515 static int HTPParserTest06(void)
3516 {
3517  uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
3518  "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
3519  "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
3520  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3521  uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
3522  "GMT\r\n"
3523  "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
3524  "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
3525  "FrontPage/5.0.2.2510\r\n"
3526  "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
3527  "chunked\r\n"
3528  "Content-Type: text/html\r\n\r\n"
3529  "580\r\n"
3530  "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
3531  "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
3532  "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
3533  "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
3534  "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
3535  "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
3536  "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
3537  "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
3538  "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
3539  "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
3540  "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
3541  "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
3542  "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
3543  "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
3544  "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
3545  "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
3546  "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
3547  "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
3548  "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
3549  "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
3550  "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
3551  "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
3552  "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
3553  "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
3554  "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
3555  "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
3556  "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
3557  "aHA=\r\n0\r\n\r\n";
3558  uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
3559  TcpSession ssn;
3560 
3562  FAIL_IF_NULL(alp_tctx);
3563 
3564  memset(&ssn, 0, sizeof(ssn));
3565 
3566  Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3567  FAIL_IF_NULL(f);
3568  f->protoctx = &ssn;
3569  f->proto = IPPROTO_TCP;
3570  f->alproto = ALPROTO_HTTP;
3571 
3573 
3574  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3575  STREAM_TOSERVER | STREAM_START, httpbuf1,
3576  httplen1);
3577  FAIL_IF(r != 0);
3578  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
3579  STREAM_TOCLIENT | STREAM_START, httpbuf2,
3580  httplen2);
3581  FAIL_IF(r != 0);
3582 
3583  HtpState *http_state = f->alstate;
3584  FAIL_IF_NULL(http_state);
3585 
3586  htp_tx_t *tx = HTPStateGetTx(http_state, 0);
3587  FAIL_IF_NULL(tx);
3588 
3589  FAIL_IF(tx->request_method_number != HTP_M_GET);
3590  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
3591 
3592  FAIL_IF(tx->response_status_number != 200);
3593  FAIL_IF(tx->request_protocol_number != HTP_PROTOCOL_1_1);
3594 
3595  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3596  FAIL_IF_NULL(h);
3597 
3598  AppLayerParserThreadCtxFree(alp_tctx);
3600  UTHFreeFlow(f);
3601  PASS;
3602 }
3603 
3604 /** \test
3605  */
3606 static int HTPParserTest07(void)
3607 {
3608  int result = 0;
3609  Flow *f = NULL;
3610  uint8_t httpbuf1[] = "GET /awstats.pl?/migratemigrate%20=%20| HTTP/1.0\r\n\r\n";
3611  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3612  TcpSession ssn;
3613  HtpState *htp_state = NULL;
3614  int r = 0;
3616 
3617  memset(&ssn, 0, sizeof(ssn));
3618 
3619  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3620  if (f == NULL)
3621  goto end;
3622  f->protoctx = &ssn;
3623  f->proto = IPPROTO_TCP;
3624  f->alproto = ALPROTO_HTTP;
3625 
3627 
3628  uint32_t u;
3629  for (u = 0; u < httplen1; u++) {
3630  uint8_t flags = 0;
3631 
3632  if (u == 0)
3633  flags = STREAM_TOSERVER|STREAM_START;
3634  else if (u == (httplen1 - 1))
3635  flags = STREAM_TOSERVER|STREAM_EOF;
3636  else
3637  flags = STREAM_TOSERVER;
3638 
3639  FLOWLOCK_WRLOCK(f);
3640  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3641  &httpbuf1[u], 1);
3642  if (r != 0) {
3643  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3644  " 0: ", u, r);
3645  FLOWLOCK_UNLOCK(f);
3646  goto end;
3647  }
3648  FLOWLOCK_UNLOCK(f);
3649  }
3650 
3651  htp_state = f->alstate;
3652  if (htp_state == NULL) {
3653  printf("no http state: ");
3654  goto end;
3655  }
3656 
3657  uint8_t ref[] = "/awstats.pl?/migratemigrate = |";
3658  size_t reflen = sizeof(ref) - 1;
3659 
3660  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3661  if (tx == NULL)
3662  goto end;
3663  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3664  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3665  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
3666  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
3667  (uintmax_t)reflen,
3668  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
3669  goto end;
3670  }
3671 
3672  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref,
3673  bstr_len(tx_ud->request_uri_normalized)) != 0)
3674  {
3675  printf("normalized uri \"");
3676  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
3677  printf("\" != \"");
3678  PrintRawUriFp(stdout, ref, reflen);
3679  printf("\": ");
3680  goto end;
3681  }
3682  }
3683 
3684  result = 1;
3685 end:
3686  if (alp_tctx != NULL)
3687  AppLayerParserThreadCtxFree(alp_tctx);
3689  UTHFreeFlow(f);
3690  return result;
3691 }
3692 
3693 #include "conf-yaml-loader.h"
3694 
3695 /** \test Abort
3696  */
3697 static int HTPParserTest08(void)
3698 {
3699  int result = 0;
3700  Flow *f = NULL;
3701  uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3702  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3703  TcpSession ssn;
3705 
3706  char input[] = "\
3707 %YAML 1.1\n\
3708 ---\n\
3709 libhtp:\n\
3710 \n\
3711  default-config:\n\
3712  personality: IDS\n\
3713 ";
3714 
3716  ConfInit();
3718 
3719  ConfYamlLoadString(input, strlen(input));
3720  HTPConfigure();
3721 
3722  HtpState *htp_state = NULL;
3723  int r = 0;
3724  memset(&ssn, 0, sizeof(ssn));
3725 
3726  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3727  if (f == NULL)
3728  goto end;
3729  f->protoctx = &ssn;
3730  f->proto = IPPROTO_TCP;
3731  f->alproto = ALPROTO_HTTP;
3732 
3734 
3735  uint8_t flags = 0;
3737 
3738  FLOWLOCK_WRLOCK(f);
3739  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1,
3740  httplen1);
3741  if (r != 0) {
3742  printf("toserver chunk returned %" PRId32 ", expected"
3743  " 0: ", r);
3744  result = 0;
3745  FLOWLOCK_UNLOCK(f);
3746  goto end;
3747  }
3748  FLOWLOCK_UNLOCK(f);
3749 
3750  htp_state = f->alstate;
3751  if (htp_state == NULL) {
3752  printf("no http state: ");
3753  result = 0;
3754  goto end;
3755  }
3756 
3757  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3758  if (tx == NULL)
3759  goto end;
3760  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3761  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3762  //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3763  PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
3764  bstr_len(tx_ud->request_uri_normalized));
3765  }
3766 
3767  result = 1;
3768 end:
3769  if (alp_tctx != NULL)
3770  AppLayerParserThreadCtxFree(alp_tctx);
3772  HTPFreeConfig();
3773  ConfDeInit();
3776  UTHFreeFlow(f);
3777  return result;
3778 }
3779 
3780 /** \test Abort
3781  */
3782 static int HTPParserTest09(void)
3783 {
3784  int result = 0;
3785  Flow *f = NULL;
3786  uint8_t httpbuf1[] = "GET /secondhouse/image/js/\%ce\%de\%ce\%fd_RentCity.js?v=2011.05.02 HTTP/1.0\r\n\r\n";
3787  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3788  TcpSession ssn;
3790 
3791  char input[] = "\
3792 %YAML 1.1\n\
3793 ---\n\
3794 libhtp:\n\
3795 \n\
3796  default-config:\n\
3797  personality: Apache_2_2\n\
3798 ";
3799 
3801  ConfInit();
3803 
3804  ConfYamlLoadString(input, strlen(input));
3805  HTPConfigure();
3806 
3807  HtpState *htp_state = NULL;
3808  int r = 0;
3809 
3810  memset(&ssn, 0, sizeof(ssn));
3811 
3812  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3813  if (f == NULL)
3814  goto end;
3815  f->protoctx = &ssn;
3816  f->proto = IPPROTO_TCP;
3817  f->alproto = ALPROTO_HTTP;
3818 
3820 
3821  uint8_t flags = 0;
3823 
3824  FLOWLOCK_WRLOCK(f);
3825  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags, httpbuf1,
3826  httplen1);
3827  if (r != 0) {
3828  printf("toserver chunk returned %" PRId32 ", expected"
3829  " 0: ", r);
3830  FLOWLOCK_UNLOCK(f);
3831  goto end;
3832  }
3833  FLOWLOCK_UNLOCK(f);
3834 
3835  htp_state = f->alstate;
3836  if (htp_state == NULL) {
3837  printf("no http state: ");
3838  goto end;
3839  }
3840 
3841  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3842  if (tx == NULL)
3843  goto end;
3844  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
3845  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
3846  //printf("uri %s\n", bstr_util_strdup_to_c(tx->request_uri_normalized));
3847  PrintRawDataFp(stdout, bstr_ptr(tx_ud->request_uri_normalized),
3848  bstr_len(tx_ud->request_uri_normalized));
3849  }
3850 
3851  result = 1;
3852 end:
3853  if (alp_tctx != NULL)
3854  AppLayerParserThreadCtxFree(alp_tctx);
3856  HTPFreeConfig();
3857  ConfDeInit();
3860  UTHFreeFlow(f);
3861  return result;
3862 }
3863 
3864 /** \test Host:www.google.com <- missing space between name:value (rfc violation)
3865  */
3866 static int HTPParserTest10(void)
3867 {
3868  int result = 0;
3869  Flow *f = NULL;
3870  uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\r\n\r\n";
3871  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3872  TcpSession ssn;
3873  HtpState *htp_state = NULL;
3874  int r = 0;
3876 
3877  memset(&ssn, 0, sizeof(ssn));
3878 
3879  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3880  if (f == NULL)
3881  goto end;
3882  f->protoctx = &ssn;
3883  f->proto = IPPROTO_TCP;
3884  f->alproto = ALPROTO_HTTP;
3885 
3887 
3888  uint32_t u;
3889  for (u = 0; u < httplen1; u++) {
3890  uint8_t flags = 0;
3891 
3892  if (u == 0)
3893  flags = STREAM_TOSERVER|STREAM_START;
3894  else if (u == (httplen1 - 1))
3895  flags = STREAM_TOSERVER|STREAM_EOF;
3896  else
3897  flags = STREAM_TOSERVER;
3898 
3899  FLOWLOCK_WRLOCK(f);
3900  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3901  &httpbuf1[u], 1);
3902  if (r != 0) {
3903  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3904  " 0: ", u, r);
3905  FLOWLOCK_UNLOCK(f);
3906  goto end;
3907  }
3908  FLOWLOCK_UNLOCK(f);
3909  }
3910 
3911  htp_state = f->alstate;
3912  if (htp_state == NULL) {
3913  printf("no http state: ");
3914  goto end;
3915  }
3916 
3917  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
3918  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
3919  if (h == NULL) {
3920  goto end;
3921  }
3922 
3923  char *name = bstr_util_strdup_to_c(h->name);
3924  if (name == NULL) {
3925  goto end;
3926  }
3927 
3928  if (strcmp(name, "Host") != 0) {
3929  printf("header name not \"Host\", instead \"%s\": ", name);
3930  free(name);
3931  goto end;
3932  }
3933  free(name);
3934 
3935  char *value = bstr_util_strdup_to_c(h->value);
3936  if (value == NULL) {
3937  goto end;
3938  }
3939 
3940  if (strcmp(value, "www.google.com") != 0) {
3941  printf("header value not \"www.google.com\", instead \"%s\": ", value);
3942  free(value);
3943  goto end;
3944  }
3945  free(value);
3946 
3947  result = 1;
3948 end:
3949  if (alp_tctx != NULL)
3950  AppLayerParserThreadCtxFree(alp_tctx);
3952  UTHFreeFlow(f);
3953  return result;
3954 }
3955 
3956 /** \test double encoding in path
3957  */
3958 static int HTPParserTest11(void)
3959 {
3960  int result = 0;
3961  Flow *f = NULL;
3962  uint8_t httpbuf1[] = "GET /%2500 HTTP/1.0\r\n\r\n";
3963  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
3964  TcpSession ssn;
3965  HtpState *htp_state = NULL;
3966  int r = 0;
3968 
3969  memset(&ssn, 0, sizeof(ssn));
3970 
3971  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
3972  if (f == NULL)
3973  goto end;
3974  f->protoctx = &ssn;
3975  f->proto = IPPROTO_TCP;
3976  f->alproto = ALPROTO_HTTP;
3977 
3979 
3980  uint32_t u;
3981  for (u = 0; u < httplen1; u++) {
3982  uint8_t flags = 0;
3983 
3984  if (u == 0)
3985  flags = STREAM_TOSERVER|STREAM_START;
3986  else if (u == (httplen1 - 1))
3987  flags = STREAM_TOSERVER|STREAM_EOF;
3988  else
3989  flags = STREAM_TOSERVER;
3990 
3991  FLOWLOCK_WRLOCK(f);
3992  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
3993  &httpbuf1[u], 1);
3994  if (r != 0) {
3995  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
3996  " 0: ", u, r);
3997  FLOWLOCK_UNLOCK(f);
3998  goto end;
3999  }
4000  FLOWLOCK_UNLOCK(f);
4001  }
4002 
4003  htp_state = f->alstate;
4004  if (htp_state == NULL) {
4005  printf("no http state: ");
4006  goto end;
4007  }
4008 
4009  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4010  if (tx == NULL)
4011  goto end;
4012  HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4013  if (tx != NULL && tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4014  if (4 != bstr_len(tx_ud->request_uri_normalized)) {
4015  printf("normalized uri len should be 2, is %"PRIuMAX,
4016  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4017  goto end;
4018  }
4019 
4020  if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
4021  bstr_ptr(tx_ud->request_uri_normalized)[1] != '%' ||
4022  bstr_ptr(tx_ud->request_uri_normalized)[2] != '0' ||
4023  bstr_ptr(tx_ud->request_uri_normalized)[3] != '0')
4024  {
4025  printf("normalized uri \"");
4026  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4027  printf("\": ");
4028  goto end;
4029  }
4030  }
4031 
4032  result = 1;
4033 end:
4034  if (alp_tctx != NULL)
4035  AppLayerParserThreadCtxFree(alp_tctx);
4037  UTHFreeFlow(f);
4038  return result;
4039 }
4040 
4041 /** \test double encoding in query
4042  */
4043 static int HTPParserTest12(void)
4044 {
4045  int result = 0;
4046  Flow *f = NULL;
4047  uint8_t httpbuf1[] = "GET /?a=%2500 HTTP/1.0\r\n\r\n";
4048  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4049  TcpSession ssn;
4050  HtpState *htp_state = NULL;
4051  int r = 0;
4053 
4054  memset(&ssn, 0, sizeof(ssn));
4055 
4056  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4057  if (f == NULL)
4058  goto end;
4059  f->protoctx = &ssn;
4060  f->proto = IPPROTO_TCP;
4061  f->alproto = ALPROTO_HTTP;
4062 
4064 
4065  uint32_t u;
4066  for (u = 0; u < httplen1; u++) {
4067  uint8_t flags = 0;
4068 
4069  if (u == 0)
4070  flags = STREAM_TOSERVER|STREAM_START;
4071  else if (u == (httplen1 - 1))
4072  flags = STREAM_TOSERVER|STREAM_EOF;
4073  else
4074  flags = STREAM_TOSERVER;
4075 
4076  FLOWLOCK_WRLOCK(f);
4077  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4078  &httpbuf1[u], 1);
4079  if (r != 0) {
4080  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4081  " 0: ", u, r);
4082  FLOWLOCK_UNLOCK(f);
4083  goto end;
4084  }
4085  FLOWLOCK_UNLOCK(f);
4086  }
4087 
4088  htp_state = f->alstate;
4089  if (htp_state == NULL) {
4090  printf("no http state: ");
4091  goto end;
4092  }
4093 
4094  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4095  if (tx == NULL)
4096  goto end;
4097  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4098  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4099  if (7 != bstr_len(tx_ud->request_uri_normalized)) {
4100  printf("normalized uri len should be 5, is %"PRIuMAX,
4101  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4102  goto end;
4103  }
4104 
4105  if (bstr_ptr(tx_ud->request_uri_normalized)[0] != '/' ||
4106  bstr_ptr(tx_ud->request_uri_normalized)[1] != '?' ||
4107  bstr_ptr(tx_ud->request_uri_normalized)[2] != 'a' ||
4108  bstr_ptr(tx_ud->request_uri_normalized)[3] != '=' ||
4109  bstr_ptr(tx_ud->request_uri_normalized)[4] != '%' ||
4110  bstr_ptr(tx_ud->request_uri_normalized)[5] != '0' ||
4111  bstr_ptr(tx_ud->request_uri_normalized)[6] != '0')
4112  {
4113  printf("normalized uri \"");
4114  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4115  printf("\": ");
4116  goto end;
4117  }
4118  }
4119 
4120  result = 1;
4121  end:
4122  if (alp_tctx != NULL)
4123  AppLayerParserThreadCtxFree(alp_tctx);
4125  UTHFreeFlow(f);
4126  return result;
4127 }
4128 
4129 /** \test Host:www.google.com0dName: Value0d0a <- missing space between name:value (rfc violation)
4130  */
4131 static int HTPParserTest13(void)
4132 {
4133  int result = 0;
4134  Flow *f = NULL;
4135  uint8_t httpbuf1[] = "GET / HTTP/1.0\r\nHost:www.google.com\rName: Value\r\n\r\n";
4136  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4137  TcpSession ssn;
4138  HtpState *htp_state = NULL;
4139  int r = 0;
4141 
4142  memset(&ssn, 0, sizeof(ssn));
4143 
4144  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
4145  if (f == NULL)
4146  goto end;
4147  f->protoctx = &ssn;
4148  f->proto = IPPROTO_TCP;
4149  f->alproto = ALPROTO_HTTP;
4150 
4152 
4153  uint32_t u;
4154  for (u = 0; u < httplen1; u++) {
4155  uint8_t flags = 0;
4156 
4157  if (u == 0)
4158  flags = STREAM_TOSERVER|STREAM_START;
4159  else if (u == (httplen1 - 1))
4160  flags = STREAM_TOSERVER|STREAM_EOF;
4161  else
4162  flags = STREAM_TOSERVER;
4163 
4164  FLOWLOCK_WRLOCK(f);
4165  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4166  &httpbuf1[u], 1);
4167  if (r != 0) {
4168  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4169  " 0: ", u, r);
4170  FLOWLOCK_UNLOCK(f);
4171  goto end;
4172  }
4173  FLOWLOCK_UNLOCK(f);
4174  }
4175 
4176  htp_state = f->alstate;
4177  if (htp_state == NULL) {
4178  printf("no http state: ");
4179  goto end;
4180  }
4181 
4182  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4183  htp_header_t *h = htp_table_get_index(tx->request_headers, 0, NULL);
4184  if (h == NULL) {
4185  goto end;
4186  }
4187 
4188  char *name = bstr_util_strdup_to_c(h->name);
4189  if (name == NULL) {
4190  goto end;
4191  }
4192 
4193  if (strcmp(name, "Host") != 0) {
4194  printf("header name not \"Host\", instead \"%s\": ", name);
4195  free(name);
4196  goto end;
4197  }
4198  free(name);
4199 
4200  char *value = bstr_util_strdup_to_c(h->value);
4201  if (value == NULL) {
4202  goto end;
4203  }
4204 
4205  if (strcmp(value, "www.google.com\rName: Value") != 0) {
4206  printf("header value not \"www.google.com\", instead \"");
4207  PrintRawUriFp(stdout, (uint8_t *)value, strlen(value));
4208  printf("\": ");
4209  free(value);
4210  goto end;
4211  }
4212  free(value);
4213 
4214  result = 1;
4215 end:
4216  if (alp_tctx != NULL)
4217  AppLayerParserThreadCtxFree(alp_tctx);
4219  UTHFreeFlow(f);
4220  return result;
4221 }
4222 
4223 /** \test Test basic config */
4224 static int HTPParserConfigTest01(void)
4225 {
4226  int ret = 0;
4227  char input[] = "\
4228 %YAML 1.1\n\
4229 ---\n\
4230 libhtp:\n\
4231 \n\
4232  default-config:\n\
4233  personality: IDS\n\
4234 \n\
4235  server-config:\n\
4236 \n\
4237  - apache-tomcat:\n\
4238  address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4239  personality: Tomcat_6_0\n\
4240 \n\
4241  - iis7:\n\
4242  address: \n\
4243  - 192.168.0.0/24\n\
4244  - 192.168.10.0/24\n\
4245  personality: IIS_7_0\n\
4246 ";
4247 
4249  ConfInit();
4250 
4251  ConfYamlLoadString(input, strlen(input));
4252 
4253  ConfNode *outputs;
4254  outputs = ConfGetNode("libhtp.default-config.personality");
4255  if (outputs == NULL) {
4256  goto end;
4257  }
4258 
4259  outputs = ConfGetNode("libhtp.server-config");
4260  if (outputs == NULL) {
4261  goto end;
4262  }
4263 
4264  ConfNode *node = TAILQ_FIRST(&outputs->head);
4265  if (node == NULL) {
4266  goto end;
4267  }
4268  if (strcmp(node->name, "0") != 0) {
4269  goto end;
4270  }
4271  node = TAILQ_FIRST(&node->head);
4272  if (node == NULL) {
4273  goto end;
4274  }
4275  if (strcmp(node->name, "apache-tomcat") != 0) {
4276  goto end;
4277  }
4278 
4279  int i = 0;
4280  ConfNode *n;
4281 
4282  ConfNode *node2 = ConfNodeLookupChild(node, "personality");
4283  if (node2 == NULL) {
4284  goto end;
4285  }
4286  if (strcmp(node2->val, "Tomcat_6_0") != 0) {
4287  goto end;
4288  }
4289 
4290  node = ConfNodeLookupChild(node, "address");
4291  if (node == NULL) {
4292  goto end;
4293  }
4294  TAILQ_FOREACH(n, &node->head, next) {
4295  if (n == NULL) {
4296  goto end;
4297  }
4298 
4299  switch(i) {
4300  case 0:
4301  if (strcmp(n->name, "0") != 0) {
4302  goto end;
4303  }
4304  if (strcmp(n->val, "192.168.1.0/24") != 0) {
4305  goto end;
4306  }
4307  break;
4308  case 1:
4309  if (strcmp(n->name, "1") != 0) {
4310  goto end;
4311  }
4312  if (strcmp(n->val, "127.0.0.0/8") != 0) {
4313  goto end;
4314  }
4315  break;
4316  case 2:
4317  if (strcmp(n->name, "2") != 0) {
4318  goto end;
4319  }
4320  if (strcmp(n->val, "::1") != 0) {
4321  goto end;
4322  }
4323  break;
4324  default:
4325  goto end;
4326  }
4327  i++;
4328  }
4329 
4330  outputs = ConfGetNode("libhtp.server-config");
4331  if (outputs == NULL) {
4332  goto end;
4333  }
4334 
4335  node = TAILQ_FIRST(&outputs->head);
4336  node = TAILQ_NEXT(node, next);
4337  if (node == NULL) {
4338  goto end;
4339  }
4340  if (strcmp(node->name, "1") != 0) {
4341  goto end;
4342  }
4343  node = TAILQ_FIRST(&node->head);
4344  if (node == NULL) {
4345  goto end;
4346  }
4347  if (strcmp(node->name, "iis7") != 0) {
4348  goto end;
4349  }
4350 
4351  node2 = ConfNodeLookupChild(node, "personality");
4352  if (node2 == NULL) {
4353  goto end;
4354  }
4355  if (strcmp(node2->val, "IIS_7_0") != 0) {
4356  goto end;
4357  }
4358 
4359  node = ConfNodeLookupChild(node, "address");
4360  if (node == NULL) {
4361  goto end;
4362  }
4363 
4364  i = 0;
4365  TAILQ_FOREACH(n, &node->head, next) {
4366  if (n == NULL) {
4367  goto end;
4368  }
4369 
4370  switch(i) {
4371  case 0:
4372  if (strcmp(n->name, "0") != 0) {
4373  goto end;
4374  }
4375  if (strcmp(n->val, "192.168.0.0/24") != 0) {
4376  goto end;
4377  }
4378  break;
4379  case 1:
4380  if (strcmp(n->name, "1") != 0) {
4381  goto end;
4382  }
4383  if (strcmp(n->val, "192.168.10.0/24") != 0) {
4384  goto end;
4385  }
4386  break;
4387  default:
4388  goto end;
4389  }
4390  i++;
4391  }
4392 
4393  ret = 1;
4394 
4395 end:
4396  ConfDeInit();
4398 
4399  return ret;
4400 }
4401 
4402 /** \test Test config builds radix correctly */
4403 static int HTPParserConfigTest02(void)
4404 {
4405  int ret = 0;
4406  char input[] = "\
4407 %YAML 1.1\n\
4408 ---\n\
4409 libhtp:\n\
4410 \n\
4411  default-config:\n\
4412  personality: IDS\n\
4413 \n\
4414  server-config:\n\
4415 \n\
4416  - apache-tomcat:\n\
4417  address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4418  personality: Tomcat_6_0\n\
4419 \n\
4420  - iis7:\n\
4421  address: \n\
4422  - 192.168.0.0/24\n\
4423  - 192.168.10.0/24\n\
4424  personality: IIS_7_0\n\
4425 ";
4426 
4428  ConfInit();
4430 
4431  ConfYamlLoadString(input, strlen(input));
4432 
4433  HTPConfigure();
4434 
4435  if (cfglist.cfg == NULL) {
4436  printf("No default config created.\n");
4437  goto end;
4438  }
4439 
4440  if (cfgtree == NULL) {
4441  printf("No config tree created.\n");
4442  goto end;
4443  }
4444 
4445  htp_cfg_t *htp = cfglist.cfg;
4446  uint8_t buf[128];
4447  const char *addr;
4448  void *user_data = NULL;
4449 
4450  addr = "192.168.10.42";
4451  if (inet_pton(AF_INET, addr, buf) == 1) {
4452  (void)SCRadixFindKeyIPV4BestMatch(buf, cfgtree, &user_data);
4453  if (user_data != NULL) {
4454  HTPCfgRec *htp_cfg_rec = user_data;
4455  htp = htp_cfg_rec->cfg;
4456  SCLogDebug("LIBHTP using config: %p", htp);
4457  }
4458  if (htp == NULL) {
4459  printf("Could not get config for: %s\n", addr);
4460  goto end;
4461  }
4462  }
4463  else {
4464  printf("Failed to parse address: %s\n", addr);
4465  goto end;
4466  }
4467 
4468  user_data = NULL;
4469  addr = "::1";
4470  if (inet_pton(AF_INET6, addr, buf) == 1) {
4471  (void)SCRadixFindKeyIPV6BestMatch(buf, cfgtree, &user_data);
4472  if (user_data != NULL) {
4473  HTPCfgRec *htp_cfg_rec = user_data;
4474  htp = htp_cfg_rec->cfg;
4475  SCLogDebug("LIBHTP using config: %p", htp);
4476  }
4477  if (htp == NULL) {
4478  printf("Could not get config for: %s\n", addr);
4479  goto end;
4480  }
4481  }
4482  else {
4483  printf("Failed to parse address: %s\n", addr);
4484  goto end;
4485  }
4486 
4487  ret = 1;
4488 
4489 end:
4490  HTPFreeConfig();
4491  ConfDeInit();
4494 
4495  return ret;
4496 }
4497 
4498 /** \test Test traffic is handled by the correct htp config */
4499 static int HTPParserConfigTest03(void)
4500 {
4501  int result = 1;
4502  Flow *f = NULL;
4503  uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
4504  " Data is c0oL!";
4505  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4506  TcpSession ssn;
4508 
4509  HtpState *htp_state = NULL;
4510  int r = 0;
4511  char input[] = "\
4512 %YAML 1.1\n\
4513 ---\n\
4514 libhtp:\n\
4515 \n\
4516  default-config:\n\
4517  personality: IDS\n\
4518 \n\
4519  server-config:\n\
4520 \n\
4521  - apache-tomcat:\n\
4522  address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
4523  personality: Tomcat_6_0\n\
4524 \n\
4525  - iis7:\n\
4526  address: \n\
4527  - 192.168.0.0/24\n\
4528  - 192.168.10.0/24\n\
4529  personality: IIS_7_0\n\
4530 ";
4531 
4533  ConfInit();
4535 
4536  ConfYamlLoadString(input, strlen(input));
4537 
4538  HTPConfigure();
4539 
4540  const char *addr = "192.168.10.42";
4541 
4542  memset(&ssn, 0, sizeof(ssn));
4543 
4544  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4545  if (f == NULL)
4546  goto end;
4547  f->protoctx = &ssn;
4548  f->proto = IPPROTO_TCP;
4549  f->alproto = ALPROTO_HTTP;
4550 
4551  htp_cfg_t *htp = cfglist.cfg;
4552 
4553  void *user_data = NULL;
4554  (void)SCRadixFindKeyIPV4BestMatch((uint8_t *)f->dst.addr_data32, cfgtree, &user_data);
4555  if (user_data != NULL) {
4556  HTPCfgRec *htp_cfg_rec = user_data;
4557  htp = htp_cfg_rec->cfg;
4558  SCLogDebug("LIBHTP using config: %p", htp);
4559  }
4560  if (htp == NULL) {
4561  printf("Could not get config for: %s\n", addr);
4562  goto end;
4563  }
4564 
4566 
4567  uint32_t u;
4568  for (u = 0; u < httplen1; u++) {
4569  uint8_t flags = 0;
4570 
4571  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4572  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4573  else flags = STREAM_TOSERVER;
4574 
4575  FLOWLOCK_WRLOCK(f);
4576  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4577  &httpbuf1[u], 1);
4578  if (r != 0) {
4579  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4580  " 0: ", u, r);
4581  result = 0;
4582  FLOWLOCK_UNLOCK(f);
4583  goto end;
4584  }
4585  FLOWLOCK_UNLOCK(f);
4586  }
4587 
4588  htp_state = f->alstate;
4589  if (htp_state == NULL) {
4590  printf("no http state: ");
4591  result = 0;
4592  goto end;
4593  }
4594 
4595  if (HTPStateGetTxCnt(htp_state) != 2) {
4596  printf("HTPStateGetTxCnt(htp_state) failure\n");
4597  goto end;
4598  }
4599 
4600  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4601  if (tx == NULL)
4602  goto end;
4603  if (tx->cfg != htp) {
4604  printf("wrong HTP config (%p instead of %p - default=%p): ",
4605  tx->cfg, htp, cfglist.cfg);
4606  goto end;
4607  }
4608  tx = HTPStateGetTx(htp_state, 1);
4609  if (tx == NULL)
4610  goto end;
4611  if (tx->cfg != htp) {
4612  printf("wrong HTP config (%p instead of %p - default=%p): ",
4613  tx->cfg, htp, cfglist.cfg);
4614  goto end;
4615  }
4616 
4617 end:
4618  if (alp_tctx != NULL)
4619  AppLayerParserThreadCtxFree(alp_tctx);
4620  HTPFreeConfig();
4621  ConfDeInit();
4624 
4626  UTHFreeFlow(f);
4627  return result;
4628 }
4629 
4630 /* disabled when we upgraded to libhtp 0.5.x */
4631 #if 0
4632 static int HTPParserConfigTest04(void)
4633 {
4634  int result = 0;
4635 
4636  char input[] = "\
4637 %YAML 1.1\n\
4638 ---\n\
4639 libhtp:\n\
4640 \n\
4641  default-config:\n\
4642  personality: IDS\n\
4643  path-control-char-handling: status_400\n\
4644  path-convert-utf8: yes\n\
4645  path-invalid-encoding-handling: remove_percent\n\
4646 \n\
4647  server-config:\n\
4648 \n\
4649  - apache-tomcat:\n\
4650  personality: Tomcat_6_0\n\
4651  path-invalid-utf8-handling: none\n\
4652  path-nul-encoded-handling: status_404\n\
4653  path-nul-raw-handling: status_400\n\
4654 \n\
4655  - iis7:\n\
4656  personality: IIS_7_0\n\
4657  path-replacement-char: o\n\
4658  path-unicode-mapping: status_400\n\
4659 ";
4660 
4662  ConfInit();
4664 
4665  ConfYamlLoadString(input, strlen(input));
4666 
4667  HTPConfigure();
4668 
4669  HTPCfgRec *cfg_rec = &cfglist;
4670  if (cfg_rec->cfg->path_control_char_handling != STATUS_400 ||
4671  cfg_rec->cfg->path_convert_utf8 != 1 ||
4672  cfg_rec->cfg->path_invalid_encoding_handling != URL_DECODER_REMOVE_PERCENT) {
4673  printf("failed 1\n");
4674  goto end;
4675  }
4676 
4677  cfg_rec = cfg_rec->next;
4678  if (cfg_rec->cfg->bestfit_replacement_char != 'o' ||
4679  cfg_rec->cfg->path_unicode_mapping != STATUS_400) {
4680  printf("failed 2\n");
4681  goto end;
4682  }
4683 
4684  cfg_rec = cfg_rec->next;
4685  if (cfg_rec->cfg->path_invalid_utf8_handling != NONE ||
4686  cfg_rec->cfg->path_nul_encoded_handling != STATUS_404 ||
4687  cfg_rec->cfg->path_nul_raw_handling != STATUS_400) {
4688  printf("failed 3\n");
4689  goto end;
4690  }
4691 
4692  result = 1;
4693 
4694 end:
4695  HTPFreeConfig();
4696  ConfDeInit();
4699 
4700  return result;
4701 }
4702 #endif
4703 
4704 /** \test Test %2f decoding in profile Apache_2_2
4705  *
4706  * %2f in path is left untouched
4707  * %2f in query string is normalized to %2F
4708  * %252f in query string is decoded/normalized to %2F
4709  */
4710 static int HTPParserDecodingTest01(void)
4711 {
4712  int result = 0;
4713  Flow *f = NULL;
4714  uint8_t httpbuf1[] =
4715  "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4716  "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4717  "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4718  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4719  TcpSession ssn;
4721 
4722  HtpState *htp_state = NULL;
4723  int r = 0;
4724  char input[] = "\
4725 %YAML 1.1\n\
4726 ---\n\
4727 libhtp:\n\
4728 \n\
4729  default-config:\n\
4730  personality: Apache_2\n\
4731 ";
4732 
4734  ConfInit();
4736  ConfYamlLoadString(input, strlen(input));
4737  HTPConfigure();
4738  const char *addr = "4.3.2.1";
4739  memset(&ssn, 0, sizeof(ssn));
4740 
4741  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4742  if (f == NULL)
4743  goto end;
4744  f->protoctx = &ssn;
4745  f->proto = IPPROTO_TCP;
4746  f->alproto = ALPROTO_HTTP;
4747 
4749 
4750  uint32_t u;
4751  for (u = 0; u < httplen1; u++) {
4752  uint8_t flags = 0;
4753 
4754  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4755  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4756  else flags = STREAM_TOSERVER;
4757 
4758  FLOWLOCK_WRLOCK(f);
4759  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4760  &httpbuf1[u], 1);
4761  if (r != 0) {
4762  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4763  " 0: ", u, r);
4764  FLOWLOCK_UNLOCK(f);
4765  goto end;
4766  }
4767  FLOWLOCK_UNLOCK(f);
4768  }
4769 
4770  htp_state = f->alstate;
4771  if (htp_state == NULL) {
4772  printf("no http state: ");
4773  goto end;
4774  }
4775 
4776  uint8_t ref1[] = "/abc%2fdef";
4777  size_t reflen = sizeof(ref1) - 1;
4778 
4779  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4780  if (tx == NULL)
4781  goto end;
4782  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4783  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4784  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4785  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4786  (uintmax_t)reflen,
4787  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4788  goto end;
4789  }
4790 
4791  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
4792  bstr_len(tx_ud->request_uri_normalized)) != 0)
4793  {
4794  printf("normalized uri \"");
4795  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4796  printf("\" != \"");
4797  PrintRawUriFp(stdout, ref1, reflen);
4798  printf("\": ");
4799  goto end;
4800  }
4801  }
4802 
4803  uint8_t ref2[] = "/abc/def?ghi/jkl";
4804  reflen = sizeof(ref2) - 1;
4805 
4806  tx = HTPStateGetTx(htp_state, 1);
4807  if (tx == NULL)
4808  goto end;
4809  tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4810  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4811  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4812  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4813  (uintmax_t)reflen,
4814  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4815  goto end;
4816  }
4817 
4818  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
4819  bstr_len(tx_ud->request_uri_normalized)) != 0)
4820  {
4821  printf("normalized uri \"");
4822  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4823  printf("\" != \"");
4824  PrintRawUriFp(stdout, ref2, reflen);
4825  printf("\": ");
4826  goto end;
4827  }
4828  }
4829 
4830  uint8_t ref3[] = "/abc/def?ghi%2fjkl";
4831  reflen = sizeof(ref3) - 1;
4832  tx = HTPStateGetTx(htp_state, 2);
4833  if (tx == NULL)
4834  goto end;
4835  tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
4836  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4837  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4838  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4839  (uintmax_t)reflen,
4840  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4841  goto end;
4842  }
4843 
4844  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
4845  bstr_len(tx_ud->request_uri_normalized)) != 0)
4846  {
4847  printf("normalized uri \"");
4848  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4849  printf("\" != \"");
4850  PrintRawUriFp(stdout, ref3, reflen);
4851  printf("\": ");
4852  goto end;
4853  }
4854  }
4855 
4856  result = 1;
4857 
4858 end:
4859  if (alp_tctx != NULL)
4860  AppLayerParserThreadCtxFree(alp_tctx);
4861  HTPFreeConfig();
4862  ConfDeInit();
4865 
4867  UTHFreeFlow(f);
4868  return result;
4869 }
4870 
4871 /** \test Test %2f decoding in profile IDS
4872  *
4873  * %2f in path decoded to /
4874  * %2f in query string is decoded to /
4875  * %252f in query string is decoded to %2F
4876  */
4877 static int HTPParserDecodingTest02(void)
4878 {
4879  int result = 0;
4880  Flow *f = NULL;
4881  uint8_t httpbuf1[] =
4882  "GET /abc%2fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4883  "GET /abc/def?ghi%2fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
4884  "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
4885  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
4886  TcpSession ssn;
4888 
4889  HtpState *htp_state = NULL;
4890  int r = 0;
4891  char input[] = "\
4892 %YAML 1.1\n\
4893 ---\n\
4894 libhtp:\n\
4895 \n\
4896  default-config:\n\
4897  personality: IDS\n\
4898  double-decode-path: no\n\
4899  double-decode-query: no\n\
4900 ";
4901 
4903  ConfInit();
4905  ConfYamlLoadString(input, strlen(input));
4906  HTPConfigure();
4907  const char *addr = "4.3.2.1";
4908  memset(&ssn, 0, sizeof(ssn));
4909 
4910  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
4911  if (f == NULL)
4912  goto end;
4913  f->protoctx = &ssn;
4914  f->proto = IPPROTO_TCP;
4915  f->alproto = ALPROTO_HTTP;
4916 
4918 
4919  uint32_t u;
4920  for (u = 0; u < httplen1; u++) {
4921  uint8_t flags = 0;
4922 
4923  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
4924  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
4925  else flags = STREAM_TOSERVER;
4926 
4927  FLOWLOCK_WRLOCK(f);
4928  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
4929  &httpbuf1[u], 1);
4930  if (r != 0) {
4931  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
4932  " 0: ", u, r);
4933  FLOWLOCK_UNLOCK(f);
4934  goto end;
4935  }
4936  FLOWLOCK_UNLOCK(f);
4937  }
4938 
4939  htp_state = f->alstate;
4940  if (htp_state == NULL) {
4941  printf("no http state: ");
4942  goto end;
4943  }
4944 
4945  uint8_t ref1[] = "/abc/def";
4946  size_t reflen = sizeof(ref1) - 1;
4947 
4948  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
4949  if (tx == NULL)
4950  goto end;
4951  HtpTxUserData *tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4952  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4953  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4954  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4955  (uintmax_t)reflen,
4956  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4957  goto end;
4958  }
4959 
4960  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
4961  bstr_len(tx_ud->request_uri_normalized)) != 0)
4962  {
4963  printf("normalized uri \"");
4964  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4965  printf("\" != \"");
4966  PrintRawUriFp(stdout, ref1, reflen);
4967  printf("\": ");
4968  goto end;
4969  }
4970  }
4971 
4972  uint8_t ref2[] = "/abc/def?ghi/jkl";
4973  reflen = sizeof(ref2) - 1;
4974 
4975  tx = HTPStateGetTx(htp_state, 1);
4976  if (tx == NULL)
4977  goto end;
4978  tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
4979  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
4980  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
4981  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
4982  (uintmax_t)reflen,
4983  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
4984  goto end;
4985  }
4986 
4987  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
4988  bstr_len(tx_ud->request_uri_normalized)) != 0)
4989  {
4990  printf("normalized uri \"");
4991  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
4992  printf("\" != \"");
4993  PrintRawUriFp(stdout, ref2, reflen);
4994  printf("\": ");
4995  goto end;
4996  }
4997  }
4998 
4999  uint8_t ref3[] = "/abc/def?ghi%2fjkl";
5000  reflen = sizeof(ref3) - 1;
5001  tx = HTPStateGetTx(htp_state, 2);
5002  if (tx == NULL)
5003  goto end;
5004  tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5005  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5006  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5007  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX" (3): ",
5008  (uintmax_t)reflen,
5009  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5010  goto end;
5011  }
5012 
5013  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref3,
5014  bstr_len(tx_ud->request_uri_normalized)) != 0)
5015  {
5016  printf("normalized uri \"");
5017  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5018  printf("\" != \"");
5019  PrintRawUriFp(stdout, ref3, reflen);
5020  printf("\": ");
5021  goto end;
5022  }
5023  }
5024 
5025  result = 1;
5026 
5027 end:
5028  if (alp_tctx != NULL)
5029  AppLayerParserThreadCtxFree(alp_tctx);
5030  HTPFreeConfig();
5031  ConfDeInit();
5034 
5036  UTHFreeFlow(f);
5037  return result;
5038 }
5039 
5040 /** \test Test %2f decoding in profile IDS with double-decode-* options
5041  *
5042  * %252f in path decoded to /
5043  * %252f in query string is decoded to /
5044  */
5045 static int HTPParserDecodingTest03(void)
5046 {
5047  int result = 0;
5048  Flow *f = NULL;
5049  uint8_t httpbuf1[] =
5050  "GET /abc%252fdef HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n"
5051  "GET /abc/def?ghi%252fjkl HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5052  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5053  TcpSession ssn;
5055 
5056  HtpState *htp_state = NULL;
5057  int r = 0;
5058  char input[] = "\
5059 %YAML 1.1\n\
5060 ---\n\
5061 libhtp:\n\
5062 \n\
5063  default-config:\n\
5064  personality: IDS\n\
5065  double-decode-path: yes\n\
5066  double-decode-query: yes\n\
5067 ";
5068 
5070  ConfInit();
5072  ConfYamlLoadString(input, strlen(input));
5073  HTPConfigure();
5074  const char *addr = "4.3.2.1";
5075  memset(&ssn, 0, sizeof(ssn));
5076 
5077  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5078  if (f == NULL)
5079  goto end;
5080  f->protoctx = &ssn;
5081  f->proto = IPPROTO_TCP;
5082  f->alproto = ALPROTO_HTTP;
5083 
5085 
5086  uint32_t u;
5087  for (u = 0; u < httplen1; u++) {
5088  uint8_t flags = 0;
5089 
5090  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5091  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5092  else flags = STREAM_TOSERVER;
5093 
5094  FLOWLOCK_WRLOCK(f);
5095  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5096  &httpbuf1[u], 1);
5097  if (r != 0) {
5098  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5099  " 0: ", u, r);
5100  FLOWLOCK_UNLOCK(f);
5101  goto end;
5102  }
5103  FLOWLOCK_UNLOCK(f);
5104  }
5105 
5106  htp_state = f->alstate;
5107  if (htp_state == NULL) {
5108  printf("no http state: ");
5109  goto end;
5110  }
5111 
5112  uint8_t ref1[] = "/abc/def";
5113  size_t reflen = sizeof(ref1) - 1;
5114 
5115  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5116  if (tx == NULL)
5117  goto end;
5118  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5119  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5120  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5121  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5122  (uintmax_t)reflen,
5123  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5124  goto end;
5125  }
5126 
5127  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5128  bstr_len(tx_ud->request_uri_normalized)) != 0)
5129  {
5130  printf("normalized uri \"");
5131  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5132  printf("\" != \"");
5133  PrintRawUriFp(stdout, ref1, reflen);
5134  printf("\": ");
5135  goto end;
5136  }
5137  }
5138 
5139  uint8_t ref2[] = "/abc/def?ghi/jkl";
5140  reflen = sizeof(ref2) - 1;
5141 
5142  tx = HTPStateGetTx(htp_state, 1);
5143  if (tx == NULL)
5144  goto end;
5145  tx_ud = (HtpTxUserData *)htp_tx_get_user_data(tx);
5146  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5147  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5148  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5149  (uintmax_t)reflen,
5150  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5151  goto end;
5152  }
5153 
5154  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref2,
5155  bstr_len(tx_ud->request_uri_normalized)) != 0)
5156  {
5157  printf("normalized uri \"");
5158  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5159  printf("\" != \"");
5160  PrintRawUriFp(stdout, ref2, reflen);
5161  printf("\": ");
5162  goto end;
5163  }
5164  }
5165 
5166  result = 1;
5167 
5168 end:
5169  if (alp_tctx != NULL)
5170  AppLayerParserThreadCtxFree(alp_tctx);
5171  HTPFreeConfig();
5172  ConfDeInit();
5175 
5177  UTHFreeFlow(f);
5178  return result;
5179 }
5180 
5181 /** \test Test http:// in query profile IDS
5182  */
5183 static int HTPParserDecodingTest04(void)
5184 {
5185  int result = 0;
5186  Flow *f = NULL;
5187  uint8_t httpbuf1[] =
5188  "GET /abc/def?a=http://www.abc.com/ HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5189  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5190  TcpSession ssn;
5192 
5193  HtpState *htp_state = NULL;
5194  int r = 0;
5195  char input[] = "\
5196 %YAML 1.1\n\
5197 ---\n\
5198 libhtp:\n\
5199 \n\
5200  default-config:\n\
5201  personality: IDS\n\
5202  double-decode-path: yes\n\
5203  double-decode-query: yes\n\
5204 ";
5205 
5207  ConfInit();
5209  ConfYamlLoadString(input, strlen(input));
5210  HTPConfigure();
5211  const char *addr = "4.3.2.1";
5212  memset(&ssn, 0, sizeof(ssn));
5213 
5214  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5215  if (f == NULL)
5216  goto end;
5217  f->protoctx = &ssn;
5218  f->proto = IPPROTO_TCP;
5219  f->alproto = ALPROTO_HTTP;
5220 
5222 
5223  uint32_t u;
5224  for (u = 0; u < httplen1; u++) {
5225  uint8_t flags = 0;
5226 
5227  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5228  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5229  else flags = STREAM_TOSERVER;
5230 
5231  FLOWLOCK_WRLOCK(f);
5232  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5233  &httpbuf1[u], 1);
5234  if (r != 0) {
5235  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5236  " 0: ", u, r);
5237  FLOWLOCK_UNLOCK(f);
5238  goto end;
5239  }
5240  FLOWLOCK_UNLOCK(f);
5241  }
5242 
5243  htp_state = f->alstate;
5244  if (htp_state == NULL) {
5245  printf("no http state: ");
5246  goto end;
5247  }
5248 
5249  uint8_t ref1[] = "/abc/def?a=http://www.abc.com/";
5250  size_t reflen = sizeof(ref1) - 1;
5251 
5252  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5253  if (tx == NULL)
5254  goto end;
5255  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5256  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5257  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5258  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5259  (uintmax_t)reflen,
5260  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5261  goto end;
5262  }
5263 
5264  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5265  bstr_len(tx_ud->request_uri_normalized)) != 0)
5266  {
5267  printf("normalized uri \"");
5268  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5269  printf("\" != \"");
5270  PrintRawUriFp(stdout, ref1, reflen);
5271  printf("\": ");
5272  goto end;
5273  }
5274  }
5275 
5276  result = 1;
5277 
5278 end:
5279  if (alp_tctx != NULL)
5280  AppLayerParserThreadCtxFree(alp_tctx);
5281  HTPFreeConfig();
5282  ConfDeInit();
5285 
5287  UTHFreeFlow(f);
5288  return result;
5289 }
5290 
5291 /** \test Test \ char in query profile IDS. Bug 739
5292  */
5293 static int HTPParserDecodingTest05(void)
5294 {
5295  int result = 0;
5296  Flow *f = NULL;
5297  uint8_t httpbuf1[] =
5298  "GET /index?id=\\\"<script>alert(document.cookie)</script> HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5299  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5300  TcpSession ssn;
5302 
5303  HtpState *htp_state = NULL;
5304  int r = 0;
5305  char input[] = "\
5306 %YAML 1.1\n\
5307 ---\n\
5308 libhtp:\n\
5309 \n\
5310  default-config:\n\
5311  personality: IDS\n\
5312  double-decode-path: yes\n\
5313  double-decode-query: yes\n\
5314 ";
5315 
5317  ConfInit();
5319  ConfYamlLoadString(input, strlen(input));
5320  HTPConfigure();
5321  const char *addr = "4.3.2.1";
5322  memset(&ssn, 0, sizeof(ssn));
5323 
5324  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5325  if (f == NULL)
5326  goto end;
5327  f->protoctx = &ssn;
5328  f->proto = IPPROTO_TCP;
5329  f->alproto = ALPROTO_HTTP;
5330 
5332 
5333  uint32_t u;
5334  for (u = 0; u < httplen1; u++) {
5335  uint8_t flags = 0;
5336 
5337  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5338  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5339  else flags = STREAM_TOSERVER;
5340 
5341  FLOWLOCK_WRLOCK(f);
5342  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5343  &httpbuf1[u], 1);
5344  if (r != 0) {
5345  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5346  " 0: ", u, r);
5347  FLOWLOCK_UNLOCK(f);
5348  goto end;
5349  }
5350  FLOWLOCK_UNLOCK(f);
5351  }
5352 
5353  htp_state = f->alstate;
5354  if (htp_state == NULL) {
5355  printf("no http state: ");
5356  goto end;
5357  }
5358 
5359  uint8_t ref1[] = "/index?id=\\\"<script>alert(document.cookie)</script>";
5360  size_t reflen = sizeof(ref1) - 1;
5361 
5362  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5363  if (tx == NULL)
5364  goto end;
5365  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5366  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5367  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5368  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5369  (uintmax_t)reflen,
5370  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5371  goto end;
5372  }
5373 
5374  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5375  bstr_len(tx_ud->request_uri_normalized)) != 0)
5376  {
5377  printf("normalized uri \"");
5378  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5379  printf("\" != \"");
5380  PrintRawUriFp(stdout, ref1, reflen);
5381  printf("\": ");
5382  goto end;
5383  }
5384  }
5385 
5386  result = 1;
5387 
5388 end:
5389  if (alp_tctx != NULL)
5390  AppLayerParserThreadCtxFree(alp_tctx);
5391  HTPFreeConfig();
5392  ConfDeInit();
5395 
5397  UTHFreeFlow(f);
5398  return result;
5399 }
5400 
5401 /** \test Test + char in query. Bug 1035
5402  */
5403 static int HTPParserDecodingTest06(void)
5404 {
5405  int result = 0;
5406  Flow *f = NULL;
5407  uint8_t httpbuf1[] =
5408  "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5409  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5410  TcpSession ssn;
5412 
5413  HtpState *htp_state = NULL;
5414  int r = 0;
5415  char input[] = "\
5416 %YAML 1.1\n\
5417 ---\n\
5418 libhtp:\n\
5419 \n\
5420  default-config:\n\
5421  personality: IDS\n\
5422  double-decode-path: yes\n\
5423  double-decode-query: yes\n\
5424 ";
5425 
5427  ConfInit();
5429  ConfYamlLoadString(input, strlen(input));
5430  HTPConfigure();
5431  const char *addr = "4.3.2.1";
5432  memset(&ssn, 0, sizeof(ssn));
5433 
5434  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5435  if (f == NULL)
5436  goto end;
5437  f->protoctx = &ssn;
5438  f->proto = IPPROTO_TCP;
5439  f->alproto = ALPROTO_HTTP;
5440 
5442 
5443  uint32_t u;
5444  for (u = 0; u < httplen1; u++) {
5445  uint8_t flags = 0;
5446 
5447  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5448  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5449  else flags = STREAM_TOSERVER;
5450 
5451  FLOWLOCK_WRLOCK(f);
5452  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5453  &httpbuf1[u], 1);
5454  if (r != 0) {
5455  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5456  " 0: ", u, r);
5457  FLOWLOCK_UNLOCK(f);
5458  goto end;
5459  }
5460  FLOWLOCK_UNLOCK(f);
5461  }
5462 
5463  htp_state = f->alstate;
5464  if (htp_state == NULL) {
5465  printf("no http state: ");
5466  goto end;
5467  }
5468 
5469  uint8_t ref1[] = "/put.php?ip=1.2.3.4&port=+6000";
5470  size_t reflen = sizeof(ref1) - 1;
5471 
5472  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5473  if (tx == NULL)
5474  goto end;
5475  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5476  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5477  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5478  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5479  (uintmax_t)reflen,
5480  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5481  goto end;
5482  }
5483 
5484  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5485  bstr_len(tx_ud->request_uri_normalized)) != 0)
5486  {
5487  printf("normalized uri \"");
5488  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5489  printf("\" != \"");
5490  PrintRawUriFp(stdout, ref1, reflen);
5491  printf("\": ");
5492  goto end;
5493  }
5494  }
5495 
5496  result = 1;
5497 
5498 end:
5499  if (alp_tctx != NULL)
5500  AppLayerParserThreadCtxFree(alp_tctx);
5501  HTPFreeConfig();
5502  ConfDeInit();
5505 
5507  UTHFreeFlow(f);
5508  return result;
5509 }
5510 
5511 /** \test Test + char in query. Bug 1035
5512  */
5513 static int HTPParserDecodingTest07(void)
5514 {
5515  int result = 0;
5516  Flow *f = NULL;
5517  uint8_t httpbuf1[] =
5518  "GET /put.php?ip=1.2.3.4&port=+6000 HTTP/1.1\r\nHost: www.domain.ltd\r\n\r\n";
5519  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5520  TcpSession ssn;
5522 
5523  HtpState *htp_state = NULL;
5524  int r = 0;
5525  char input[] = "\
5526 %YAML 1.1\n\
5527 ---\n\
5528 libhtp:\n\
5529 \n\
5530  default-config:\n\
5531  personality: IDS\n\
5532  double-decode-path: yes\n\
5533  double-decode-query: yes\n\
5534  query-plusspace-decode: yes\n\
5535 ";
5536 
5538  ConfInit();
5540  ConfYamlLoadString(input, strlen(input));
5541  HTPConfigure();
5542  const char *addr = "4.3.2.1";
5543  memset(&ssn, 0, sizeof(ssn));
5544 
5545  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5546  if (f == NULL)
5547  goto end;
5548  f->protoctx = &ssn;
5549  f->proto = IPPROTO_TCP;
5550  f->alproto = ALPROTO_HTTP;
5551 
5553 
5554  uint32_t u;
5555  for (u = 0; u < httplen1; u++) {
5556  uint8_t flags = 0;
5557 
5558  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5559  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5560  else flags = STREAM_TOSERVER;
5561 
5562  FLOWLOCK_WRLOCK(f);
5563  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5564  &httpbuf1[u], 1);
5565  if (r != 0) {
5566  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5567  " 0: ", u, r);
5568  FLOWLOCK_UNLOCK(f);
5569  goto end;
5570  }
5571  FLOWLOCK_UNLOCK(f);
5572  }
5573 
5574  htp_state = f->alstate;
5575  if (htp_state == NULL) {
5576  printf("no http state: ");
5577  goto end;
5578  }
5579 
5580  uint8_t ref1[] = "/put.php?ip=1.2.3.4&port= 6000";
5581  size_t reflen = sizeof(ref1) - 1;
5582 
5583  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5584  if (tx == NULL)
5585  goto end;
5586  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5587  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5588  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5589  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5590  (uintmax_t)reflen,
5591  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5592  goto end;
5593  }
5594 
5595  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5596  bstr_len(tx_ud->request_uri_normalized)) != 0)
5597  {
5598  printf("normalized uri \"");
5599  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5600  printf("\" != \"");
5601  PrintRawUriFp(stdout, ref1, reflen);
5602  printf("\": ");
5603  goto end;
5604  }
5605  }
5606 
5607  result = 1;
5608 
5609 end:
5610  if (alp_tctx != NULL)
5611  AppLayerParserThreadCtxFree(alp_tctx);
5612  HTPFreeConfig();
5613  ConfDeInit();
5616 
5618  UTHFreeFlow(f);
5619  return result;
5620 }
5621 
5622 /** \test Test 'proxy' URI normalization. Ticket 1008
5623  */
5624 static int HTPParserDecodingTest08(void)
5625 {
5626  int result = 0;
5627  Flow *f = NULL;
5628  uint8_t httpbuf1[] =
5629  "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5630  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5631  TcpSession ssn;
5633 
5634  HtpState *htp_state = NULL;
5635  int r = 0;
5636  char input[] = "\
5637 %YAML 1.1\n\
5638 ---\n\
5639 libhtp:\n\
5640 \n\
5641  default-config:\n\
5642  personality: IDS\n\
5643 ";
5644 
5646  ConfInit();
5648  ConfYamlLoadString(input, strlen(input));
5649  HTPConfigure();
5650  const char *addr = "4.3.2.1";
5651  memset(&ssn, 0, sizeof(ssn));
5652 
5653  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5654  if (f == NULL)
5655  goto end;
5656  f->protoctx = &ssn;
5657  f->proto = IPPROTO_TCP;
5658  f->alproto = ALPROTO_HTTP;
5659 
5661 
5662  uint32_t u;
5663  for (u = 0; u < httplen1; u++) {
5664  uint8_t flags = 0;
5665 
5666  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5667  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5668  else flags = STREAM_TOSERVER;
5669 
5670  FLOWLOCK_WRLOCK(f);
5671  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5672  &httpbuf1[u], 1);
5673  if (r != 0) {
5674  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5675  " 0: ", u, r);
5676  FLOWLOCK_UNLOCK(f);
5677  goto end;
5678  }
5679  FLOWLOCK_UNLOCK(f);
5680  }
5681 
5682  htp_state = f->alstate;
5683  if (htp_state == NULL) {
5684  printf("no http state: ");
5685  goto end;
5686  }
5687 
5688  uint8_t ref1[] = "/blah/";
5689  size_t reflen = sizeof(ref1) - 1;
5690 
5691  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5692  if (tx == NULL)
5693  goto end;
5694  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5695  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5696  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5697  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5698  (uintmax_t)reflen,
5699  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5700  goto end;
5701  }
5702 
5703  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5704  bstr_len(tx_ud->request_uri_normalized)) != 0)
5705  {
5706  printf("normalized uri \"");
5707  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5708  printf("\" != \"");
5709  PrintRawUriFp(stdout, ref1, reflen);
5710  printf("\": ");
5711  goto end;
5712  }
5713  }
5714 
5715  result = 1;
5716 
5717 end:
5718  if (alp_tctx != NULL)
5719  AppLayerParserThreadCtxFree(alp_tctx);
5720  HTPFreeConfig();
5721  ConfDeInit();
5724 
5726  UTHFreeFlow(f);
5727  return result;
5728 }
5729 
5730 /** \test Test 'proxy' URI normalization. Ticket 1008
5731  */
5732 static int HTPParserDecodingTest09(void)
5733 {
5734  int result = 0;
5735  Flow *f = NULL;
5736  uint8_t httpbuf1[] =
5737  "GET http://suricata-ids.org/blah/ HTTP/1.1\r\nHost: suricata-ids.org\r\n\r\n";
5738  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5739  TcpSession ssn;
5741 
5742  HtpState *htp_state = NULL;
5743  int r = 0;
5744  char input[] = "\
5745 %YAML 1.1\n\
5746 ---\n\
5747 libhtp:\n\
5748 \n\
5749  default-config:\n\
5750  personality: IDS\n\
5751  uri-include-all: true\n\
5752 ";
5753 
5755  ConfInit();
5757  ConfYamlLoadString(input, strlen(input));
5758  HTPConfigure();
5759  const char *addr = "4.3.2.1";
5760  memset(&ssn, 0, sizeof(ssn));
5761 
5762  f = UTHBuildFlow(AF_INET, "1.2.3.4", addr, 1024, 80);
5763  if (f == NULL)
5764  goto end;
5765  f->protoctx = &ssn;
5766  f->proto = IPPROTO_TCP;
5767  f->alproto = ALPROTO_HTTP;
5768 
5770 
5771  uint32_t u;
5772  for (u = 0; u < httplen1; u++) {
5773  uint8_t flags = 0;
5774 
5775  if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
5776  else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
5777  else flags = STREAM_TOSERVER;
5778 
5779  FLOWLOCK_WRLOCK(f);
5780  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, flags,
5781  &httpbuf1[u], 1);
5782  if (r != 0) {
5783  printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
5784  " 0: ", u, r);
5785  FLOWLOCK_UNLOCK(f);
5786  goto end;
5787  }
5788  FLOWLOCK_UNLOCK(f);
5789  }
5790 
5791  htp_state = f->alstate;
5792  if (htp_state == NULL) {
5793  printf("no http state: ");
5794  goto end;
5795  }
5796 
5797  uint8_t ref1[] = "http://suricata-ids.org/blah/";
5798  size_t reflen = sizeof(ref1) - 1;
5799 
5800  htp_tx_t *tx = HTPStateGetTx(htp_state, 0);
5801  if (tx == NULL)
5802  goto end;
5803  HtpTxUserData *tx_ud = (HtpTxUserData *) htp_tx_get_user_data(tx);
5804  if (tx_ud != NULL && tx_ud->request_uri_normalized != NULL) {
5805  if (reflen != bstr_len(tx_ud->request_uri_normalized)) {
5806  printf("normalized uri len should be %"PRIuMAX", is %"PRIuMAX,
5807  (uintmax_t)reflen,
5808  (uintmax_t)bstr_len(tx_ud->request_uri_normalized));
5809  goto end;
5810  }
5811 
5812  if (memcmp(bstr_ptr(tx_ud->request_uri_normalized), ref1,
5813  bstr_len(tx_ud->request_uri_normalized)) != 0)
5814  {
5815  printf("normalized uri \"");
5816  PrintRawUriFp(stdout, bstr_ptr(tx_ud->request_uri_normalized), bstr_len(tx_ud->request_uri_normalized));
5817  printf("\" != \"");
5818  PrintRawUriFp(stdout, ref1, reflen);
5819  printf("\": ");
5820  goto end;
5821  }
5822  }
5823 
5824  result = 1;
5825 
5826 end:
5827  if (alp_tctx != NULL)
5828  AppLayerParserThreadCtxFree(alp_tctx);
5829  HTPFreeConfig();
5830  ConfDeInit();
5833 
5835  UTHFreeFlow(f);
5836  return result;
5837 }
5838 
5839 /** \test BG box crash -- chunks are messed up. Observed for real. */
5840 static int HTPBodyReassemblyTest01(void)
5841 {
5842  int result = 0;
5843  HtpTxUserData htud;
5844  memset(&htud, 0x00, sizeof(htud));
5845  HtpState hstate;
5846  memset(&hstate, 0x00, sizeof(hstate));
5847  Flow flow;
5848  memset(&flow, 0x00, sizeof(flow));
5850  htp_tx_t tx;
5851  memset(&tx, 0, sizeof(tx));
5852 
5853  hstate.f = &flow;
5854  flow.alparser = parser;
5855 
5856  uint8_t chunk1[] = "--e5a320f21416a02493a0a6f561b1c494\r\nContent-Disposition: form-data; name=\"uploadfile\"; filename=\"D2GUef.jpg\"\r";
5857  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";
5858 
5859  int r = HtpBodyAppendChunk(NULL, &htud.request_body, chunk1, sizeof(chunk1)-1);
5860  BUG_ON(r != 0);
5861  r = HtpBodyAppendChunk(NULL, &htud.request_body, chunk2, sizeof(chunk2)-1);
5862  BUG_ON(r != 0);
5863 
5864  const uint8_t *chunks_buffer = NULL;
5865  uint32_t chunks_buffer_len = 0;
5866 
5867  HtpRequestBodyReassemble(&htud, &chunks_buffer, &chunks_buffer_len);
5868  if (chunks_buffer == NULL) {
5869  goto end;
5870  }
5871 #ifdef PRINT
5872  printf("REASSCHUNK START: \n");
5873  PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
5874  printf("REASSCHUNK END: \n");
5875 #endif
5876 
5877  HtpRequestBodyHandleMultipart(&hstate, &htud, &tx, chunks_buffer, chunks_buffer_len);
5878 
5879  if (htud.request_body.content_len_so_far != 669) {
5880  printf("htud.request_body.content_len_so_far %"PRIu64": ", htud.request_body.content_len_so_far);
5881  goto end;
5882  }
5883 
5884  if (hstate.files_ts != NULL)
5885  goto end;
5886 
5887  result = 1;
5888 end:
5889  return result;
5890 }
5891 
5892 /** \test BG crash */
5893 static int HTPSegvTest01(void)
5894 {
5895  int result = 0;
5896  Flow *f = NULL;
5897  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";
5898  uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
5899  char input[] = "\
5900 %YAML 1.1\n\
5901 ---\n\
5902 libhtp:\n\
5903 \n\
5904  default-config:\n\
5905  personality: IDS\n\
5906  double-decode-path: no\n\
5907  double-decode-query: no\n\
5908  request-body-limit: 0\n\
5909  response-body-limit: 0\n\
5910 ";
5911 
5913  ConfInit();
5915  ConfYamlLoadString(input, strlen(input));
5916  HTPConfigure();
5917 
5918  TcpSession ssn;
5919  HtpState *http_state = NULL;
5921 
5922  memset(&ssn, 0, sizeof(ssn));
5923 
5924  f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
5925  if (f == NULL)
5926  goto end;
5927  f->protoctx = &ssn;
5928  f->proto = IPPROTO_TCP;
5929  f->alproto = ALPROTO_HTTP;
5930 
5932 
5933  SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
5934  FLOWLOCK_WRLOCK(f);
5935  int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP,
5936  STREAM_TOSERVER | STREAM_START, httpbuf1,
5937  httplen1);
5938  if (r != 0) {
5939  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
5940  FLOWLOCK_UNLOCK(f);
5941  goto end;
5942  }
5943  FLOWLOCK_UNLOCK(f);
5944  SCLogDebug("\n>>>> processing chunk 1 again <<<<\n");
5945  FLOWLOCK_WRLOCK(f);
5946  r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER,
5947  httpbuf1, httplen1);
5948  if (r != 0) {
5949  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
5950  FLOWLOCK_UNLOCK(f);
5951  goto end;
5952  }
5953  FLOWLOCK_UNLOCK(f);
5954 
5955  http_state = f->alstate;
5956  if (http_state == NULL) {
5957  printf("no http state: ");
5958  goto end;
5959  }
5960 
5961  FLOWLOCK_WRLOCK(f);
5963  if (decoder_events != NULL) {
5964  printf("app events: ");
5965  FLOWLOCK_UNLOCK(f);
5966  goto end;
5967  }
5968  FLOWLOCK_UNLOCK(f);
5969  result = 1;
5970 end:
5971  if (alp_tctx != NULL)
5972  AppLayerParserThreadCtxFree(alp_tctx);
5973  HTPFreeConfig();
5974  ConfDeInit();
5978