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