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