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