]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-htp.c
Add C and E flags to flags keyword. We still support 1 and 2 for backward compatibility
[people/ms/suricata.git] / src / app-layer-htp.c
CommitLineData
ce019275
WM
1/* Copyright (C) 2007-2010 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 */
07f7ba55
GS
17
18/**
ce019275 19 * \file
07f7ba55
GS
20 *
21 * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
0165b3f0 22 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
a9cdd2bb 23 * \author Brian Rectanus <brectanu@gmail.com>
07f7ba55 24 *
ce019275 25 * This file provides a HTTP protocol support for the engine using HTP library.
07f7ba55
GS
26 */
27
5c6a65dc 28#include "suricata.h"
ecf86f9c 29#include "suricata-common.h"
07f7ba55
GS
30#include "debug.h"
31#include "decode.h"
32#include "threads.h"
07f7ba55
GS
33
34#include "util-print.h"
35#include "util-pool.h"
a9cdd2bb 36#include "util-radix-tree.h"
07f7ba55
GS
37
38#include "stream-tcp-private.h"
39#include "stream-tcp-reassemble.h"
6a53ab9c 40#include "stream-tcp.h"
07f7ba55
GS
41#include "stream.h"
42
43#include "app-layer-protos.h"
44#include "app-layer-parser.h"
fc2f7f29 45#include "app-layer-htp.h"
07f7ba55 46
705471e4 47#include "util-spm.h"
07f7ba55
GS
48#include "util-debug.h"
49#include "app-layer-htp.h"
fc2f7f29 50#include "util-time.h"
25a3a5c6 51#include <htp/htp.h>
07f7ba55 52
06a65cb4
PR
53#include "util-unittest.h"
54#include "util-unittest-helper.h"
55#include "flow-util.h"
56
57#include "detect-engine.h"
58#include "detect-engine-state.h"
59#include "detect-parse.h"
60
a9cdd2bb
BR
61#include "conf.h"
62
ead13bda 63/** Need a linked list in order to keep track of these */
6ebe7b7c
VJ
64typedef struct HTPCfgRec_ {
65 htp_cfg_t *cfg;
66 struct HTPCfgRec_ *next;
67
68 /** max size of the client body we inspect */
69 uint32_t request_body_limit;
70} HTPCfgRec;
a9cdd2bb 71
ead13bda 72/** Fast lookup tree (radix) for the various HTP configurations */
a9cdd2bb 73static SCRadixTree *cfgtree;
ead13bda 74/** List of HTP configurations. */
a9cdd2bb
BR
75static HTPCfgRec cfglist;
76
07f7ba55 77#ifdef DEBUG
e26833be 78static SCMutex htp_state_mem_lock = PTHREAD_MUTEX_INITIALIZER;
07f7ba55
GS
79static uint64_t htp_state_memuse = 0;
80static uint64_t htp_state_memcnt = 0;
81#endif
97d49d8f
AS
82
83static uint8_t need_htp_request_body = 0;
07f7ba55 84
a9cdd2bb 85
a9cdd2bb
BR
86/**
87 * \internal
88 *
89 * \brief Lookup the HTP personality string from the numeric personality.
90 *
91 * \todo This needs to be a libhtp function.
92 */
93static const char *HTPLookupPersonalityString(int p)
94{
95#define CASE_HTP_PERSONALITY_STRING(p) \
96 case HTP_SERVER_ ## p: return #p
97
98 switch (p) {
99 CASE_HTP_PERSONALITY_STRING(MINIMAL);
100 CASE_HTP_PERSONALITY_STRING(GENERIC);
101 CASE_HTP_PERSONALITY_STRING(IDS);
102 CASE_HTP_PERSONALITY_STRING(IIS_4_0);
103 CASE_HTP_PERSONALITY_STRING(IIS_5_0);
104 CASE_HTP_PERSONALITY_STRING(IIS_5_1);
105 CASE_HTP_PERSONALITY_STRING(IIS_6_0);
106 CASE_HTP_PERSONALITY_STRING(IIS_7_0);
107 CASE_HTP_PERSONALITY_STRING(IIS_7_5);
108 CASE_HTP_PERSONALITY_STRING(TOMCAT_6_0);
109 CASE_HTP_PERSONALITY_STRING(APACHE);
110 CASE_HTP_PERSONALITY_STRING(APACHE_2_2);
111 }
112
113 return NULL;
114}
a9cdd2bb
BR
115
116/**
117 * \internal
118 *
119 * \brief Lookup the numeric HTP personality from a string.
120 *
121 * \todo This needs to be a libhtp function.
122 */
123static int HTPLookupPersonality(const char *str)
124{
125#define IF_HTP_PERSONALITY_NUM(p) \
126 if (strcasecmp(#p, str) == 0) return HTP_SERVER_ ## p
127
128 IF_HTP_PERSONALITY_NUM(MINIMAL);
129 IF_HTP_PERSONALITY_NUM(GENERIC);
130 IF_HTP_PERSONALITY_NUM(IDS);
131 IF_HTP_PERSONALITY_NUM(IIS_4_0);
132 IF_HTP_PERSONALITY_NUM(IIS_5_0);
133 IF_HTP_PERSONALITY_NUM(IIS_5_1);
134 IF_HTP_PERSONALITY_NUM(IIS_6_0);
135 IF_HTP_PERSONALITY_NUM(IIS_7_0);
136 IF_HTP_PERSONALITY_NUM(IIS_7_5);
137 IF_HTP_PERSONALITY_NUM(TOMCAT_6_0);
138 IF_HTP_PERSONALITY_NUM(APACHE);
139 IF_HTP_PERSONALITY_NUM(APACHE_2_2);
140
141 return -1;
142}
143
07f7ba55
GS
144/** \brief Function to allocates the HTTP state memory and also creates the HTTP
145 * connection parser to be used by the HTP library
146 */
147static void *HTPStateAlloc(void)
148{
48248687
VJ
149 SCEnter();
150
25a3a5c6 151 HtpState *s = SCMalloc(sizeof(HtpState));
9f4fae5b 152 if (s == NULL)
48248687 153 goto error;
07f7ba55 154
48248687 155 memset(s, 0x00, sizeof(HtpState));
07f7ba55 156
187949b9
VJ
157#ifdef DEBUG
158 SCMutexLock(&htp_state_mem_lock);
159 htp_state_memcnt++;
160 htp_state_memuse += sizeof(HtpState);
6fca55e0 161 SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
187949b9
VJ
162 SCMutexUnlock(&htp_state_mem_lock);
163#endif
70b32f73 164
48248687
VJ
165 SCReturnPtr((void *)s, "void");
166
167error:
187949b9 168 if (s != NULL) {
25a3a5c6 169 SCFree(s);
187949b9 170 }
48248687
VJ
171
172 SCReturnPtr(NULL, "void");
07f7ba55
GS
173}
174
175/** \brief Function to frees the HTTP state memory and also frees the HTTP
176 * connection parser memory which was used by the HTP library
177 */
25a3a5c6 178void HTPStateFree(void *state)
07f7ba55 179{
48248687
VJ
180 SCEnter();
181
182 HtpState *s = (HtpState *)state;
183
06a65cb4
PR
184 /* Unset the body inspection */
185 s->flags &=~ HTP_FLAG_NEW_BODY_SET;
186
07f7ba55 187 /* free the connection parser memory used by HTP library */
bc55fb27 188 if (s != NULL && s->connp != NULL) {
6fca55e0
VJ
189 SCLogDebug("freeing HTP state");
190
bc55fb27
VJ
191 size_t i;
192 /* free the list of body chunks */
193 if (s->connp->conn != NULL) {
194 for (i = 0; i < list_size(s->connp->conn->transactions); i++) {
195 htp_tx_t *tx = (htp_tx_t *)list_get(s->connp->conn->transactions, i);
196 if (tx != NULL) {
197 SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx);
198 if (htud != NULL) {
199 HtpBodyFree(&htud->body);
200 SCFree(htud);
06a65cb4 201 }
bc55fb27 202 htp_tx_set_user_data(tx, NULL);
06a65cb4
PR
203 }
204 }
48248687 205 }
bc55fb27 206 htp_connp_destroy_all(s->connp);
48248687 207 }
07f7ba55 208
25a3a5c6 209 SCFree(s);
48248687 210
07f7ba55 211#ifdef DEBUG
e26833be 212 SCMutexLock(&htp_state_mem_lock);
07f7ba55 213 htp_state_memcnt--;
187949b9 214 htp_state_memuse -= sizeof(HtpState);
6fca55e0 215 SCLogDebug("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
e26833be 216 SCMutexUnlock(&htp_state_mem_lock);
07f7ba55 217#endif
48248687
VJ
218
219 SCReturn;
07f7ba55
GS
220}
221
70b32f73
VJ
222/**
223 * \brief Update the transaction id based on the http state
224 */
225void HTPStateUpdateTransactionId(void *state, uint16_t *id) {
226 SCEnter();
227
228 HtpState *s = (HtpState *)state;
229
230 SCLogDebug("original id %"PRIu16", s->transaction_cnt+1 %"PRIu16,
231 *id, (s->transaction_cnt+1));
232
233 if ((s->transaction_cnt+1) > (*id)) {
234 SCLogDebug("original id %"PRIu16", updating with s->transaction_cnt+1 %"PRIu16,
235 *id, (s->transaction_cnt+1));
236
237 (*id) = (s->transaction_cnt+1);
238
239 SCLogDebug("updated id %"PRIu16, *id);
240 }
241
242 SCReturn;
243}
244
245/**
246 * \brief HTP transaction cleanup callback
247 *
248 * \warning We cannot actually free the transactions here. It seems that
249 * HTP only accepts freeing of transactions in the response callback.
250 */
251void HTPStateTransactionFree(void *state, uint16_t id) {
252 SCEnter();
253
254 HtpState *s = (HtpState *)state;
255
256 s->transaction_done = id;
257 SCLogDebug("state %p, id %"PRIu16, s, id);
258
259 /* we can't remove the actual transactions here */
260
261 SCReturn;
262}
263
97d49d8f
AS
264/**
265 * \brief Sets a flag that informs the HTP app layer that some module in the
266 * engine needs the http request body data.
bc55fb27 267 * \initonly
97d49d8f
AS
268 */
269void AppLayerHtpEnableRequestBodyCallback(void)
270{
70b32f73 271 SCEnter();
97d49d8f 272 need_htp_request_body = 1;
70b32f73 273 SCReturn;
97d49d8f
AS
274}
275
276
fc2f7f29
GS
277/**
278 * \brief Function to convert the IP addresses in to the string
279 *
280 * \param f pointer to the flow which contains the IP addresses
281 * \param remote_addr pointer the string which will contain the remote address
282 * \param local_addr pointer the string which will contain the local address
283 */
284void HTPGetIPAddr(Flow *f, int family, char *remote_addr, char *local_addr)
285{
a9cdd2bb 286 SCEnter();
fc2f7f29
GS
287 inet_ntop(family, (const void *)&f->src.addr_data32[0], remote_addr,
288 sizeof (remote_addr));
289 inet_ntop(family, (const void *)&f->dst.addr_data32[0], local_addr,
290 sizeof (local_addr));
a9cdd2bb 291 SCReturn;
fc2f7f29
GS
292}
293
07f7ba55
GS
294/**
295 * \brief Function to handle the reassembled data from client and feed it to
296 * the HTP library to process it.
297 *
298 * \param htp_state Pointer the state in which the parsed value to be stored
299 * \param pstate Application layer parser state for this session
300 * \param input Pointer the received HTTP client data
301 * \param input_len Length in bytes of the received data
302 * \param output Pointer to the output (not used in this function)
303 *
2d6cf71d 304 * \retval On success returns 1 or on failure returns -1
07f7ba55 305 */
fc2f7f29
GS
306static int HTPHandleRequestData(Flow *f, void *htp_state,
307 AppLayerParserState *pstate,
07f7ba55 308 uint8_t *input, uint32_t input_len,
18fe3818 309 AppLayerParserResult *output)
07f7ba55 310{
1b39e602 311 SCEnter();
0a85fd67
GS
312 int r = -1;
313 int ret = 1;
70b32f73 314
b8fec77f
VJ
315 //PrintRawDataFp(stdout, input, input_len);
316
07f7ba55 317 HtpState *hstate = (HtpState *)htp_state;
a9cdd2bb 318
70b32f73
VJ
319 /* if the previous run set the new request flag, we unset it here. As
320 * we're here after a new request completed, we know it's a new
321 * transaction. So we set the new transaction flag. */
322 if (hstate->flags & HTP_FLAG_NEW_REQUEST) {
323 hstate->flags &=~ HTP_FLAG_NEW_REQUEST;
324
325 /* new transaction */
326 hstate->transaction_cnt++;
327 SCLogDebug("transaction_cnt %"PRIu16", list_size %"PRIuMAX, hstate->transaction_cnt,
328 (uintmax_t)list_size(hstate->connp->conn->transactions));
329 }
330
a9cdd2bb
BR
331 /* On the first invocation, create the connection parser structure to
332 * be used by HTP library. This is looked up via IP in the radix
333 * tree. Failing that, the default HTP config is used.
334 */
335 if (NULL == hstate->connp ) {
ead13bda
VJ
336 htp_cfg_t *htp = cfglist.cfg; /* Default to the global HTP config */
337 SCRadixNode *cfgnode = NULL;
338
a9cdd2bb
BR
339 if (AF_INET == f->dst.family) {
340 SCLogDebug("Looking up HTP config for ipv4 %08x", *GET_IPV4_DST_ADDR_PTR(f));
341 cfgnode = SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(f), cfgtree);
342 }
78e15ea7 343 else if (AF_INET6 == f->dst.family) {
a9cdd2bb
BR
344 SCLogDebug("Looking up HTP config for ipv6");
345 cfgnode = SCRadixFindKeyIPV6BestMatch((uint8_t *)GET_IPV6_DST_ADDR(f), cfgtree);
346 }
78e15ea7
VJ
347 else {
348 SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown address family, bug!");
349 goto error;
350 }
351
f81fccd6
VJ
352 if (cfgnode != NULL) {
353 HTPCfgRec *htp_cfg_rec = SC_RADIX_NODE_USERDATA(cfgnode, HTPCfgRec);
354 if (htp_cfg_rec != NULL) {
355 htp = htp_cfg_rec->cfg;
356 SCLogDebug("LIBHTP using config: %p", htp);
6ebe7b7c 357
743ed762
VJ
358 hstate->request_body_limit = htp_cfg_rec->request_body_limit;
359 }
a9cdd2bb
BR
360 } else {
361 SCLogDebug("Using default HTP config: %p", htp);
6ebe7b7c
VJ
362
363 hstate->request_body_limit = cfglist.request_body_limit;
a9cdd2bb
BR
364 }
365
366 if (NULL == htp) {
4129146a 367 BUG_ON(htp == NULL);
ead13bda 368 /* should never happen if HTPConfigure is properly invoked */
a9cdd2bb
BR
369 goto error;
370 }
371
372 hstate->connp = htp_connp_create(htp);
373 if (hstate->connp == NULL) {
374 goto error;
375 }
376
377 htp_connp_set_user_data(hstate->connp, (void *)hstate);
378
379 SCLogDebug("New hstate->connp %p", hstate->connp);
380 }
07f7ba55 381
4129146a
VJ
382 /* the code block above should make sure connp is never NULL here */
383 BUG_ON(hstate->connp == NULL);
384
8e444f17
GS
385 if (hstate->connp->in_status == STREAM_STATE_ERROR) {
386 SCLogError(SC_ERR_ALPARSER, "Inbound parser is in error state, no"
387 " need to feed data to libhtp");
388 SCReturnInt(-1);
389 }
390
0165b3f0
PR
391 /* Unset the body inspection (the callback should
392 * reactivate it if necessary) */
70b32f73 393 hstate->flags &=~ HTP_FLAG_NEW_BODY_SET;
0165b3f0 394
fc2f7f29 395 /* Open the HTTP connection on receiving the first request */
ba7e8012 396 if (!(hstate->flags & HTP_FLAG_STATE_OPEN)) {
48248687 397 SCLogDebug("opening htp handle at %p", hstate->connp);
ba7e8012 398
fc2f7f29
GS
399 htp_connp_open(hstate->connp, NULL, f->sp, NULL, f->dp, 0);
400 hstate->flags |= HTP_FLAG_STATE_OPEN;
ba7e8012 401 } else {
48248687 402 SCLogDebug("using existing htp handle at %p", hstate->connp);
fc2f7f29 403 }
07f7ba55 404
70b32f73 405 /* pass the new data to the htp parser */
0a85fd67 406 r = htp_connp_req_data(hstate->connp, 0, input, input_len);
148883ce 407
bf236e45
GS
408 switch(r) {
409 case STREAM_STATE_ERROR:
148883ce 410 if (hstate->connp->last_error != NULL) {
bf236e45
GS
411 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP client "
412 "request: [%"PRId32"] [%s] [%"PRId32"] %s",
413 hstate->connp->last_error->level,
414 hstate->connp->last_error->file,
415 hstate->connp->last_error->line,
416 hstate->connp->last_error->msg);
148883ce 417 } else {
bf236e45
GS
418 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP client "
419 "request");
148883ce 420 }
bf236e45
GS
421 hstate->flags |= HTP_FLAG_STATE_ERROR;
422 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 423 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
424 ret = -1;
425 break;
426 case STREAM_STATE_DATA:
427 hstate->flags |= HTP_FLAG_STATE_DATA;
428 break;
429 case STREAM_STATE_DATA_OTHER:
430 SCLogDebug("CONNECT not supported yet");
431 hstate->flags |= HTP_FLAG_STATE_ERROR;
432 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 433 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
434 ret = -1;
435 break;
436 default:
437 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 438 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
70b32f73 439 }
07f7ba55 440
a9cdd2bb 441 /* if the TCP connection is closed, then close the HTTP connection */
fc2f7f29 442 if ((pstate->flags & APP_LAYER_PARSER_EOF) &&
0a85fd67
GS
443 ! (hstate->flags & HTP_FLAG_STATE_CLOSED) &&
444 ! (hstate->flags & HTP_FLAG_STATE_DATA))
fc2f7f29
GS
445 {
446 htp_connp_close(hstate->connp, 0);
447 hstate->flags |= HTP_FLAG_STATE_CLOSED;
ba7e8012 448 SCLogDebug("stream eof encountered, closing htp handle");
fc2f7f29
GS
449 }
450
48248687 451 SCLogDebug("hstate->connp %p", hstate->connp);
0a85fd67 452 SCReturnInt(ret);
a9cdd2bb
BR
453
454error:
455 SCReturnInt(-1);
07f7ba55
GS
456}
457
458/**
459 * \brief Function to handle the reassembled data from server and feed it to
460 * the HTP library to process it.
461 *
462 * \param htp_state Pointer the state in which the parsed value to be stored
463 * \param pstate Application layer parser state for this session
464 * \param input Pointer the received HTTP server data
465 * \param input_len Length in bytes of the received data
466 * \param output Pointer to the output (not used in this function)
467 *
2d6cf71d 468 * \retval On success returns 1 or on failure returns -1
07f7ba55 469 */
fc2f7f29
GS
470static int HTPHandleResponseData(Flow *f, void *htp_state,
471 AppLayerParserState *pstate,
07f7ba55 472 uint8_t *input, uint32_t input_len,
18fe3818 473 AppLayerParserResult *output)
07f7ba55 474{
1b39e602 475 SCEnter();
0a85fd67
GS
476 int r = -1;
477 int ret = 1;
478
07f7ba55 479 HtpState *hstate = (HtpState *)htp_state;
4129146a
VJ
480 if (hstate->connp == NULL) {
481 SCLogError(SC_ERR_ALPARSER, "HTP state has no connp");
482 SCReturnInt(-1);
483 }
07f7ba55 484
8e444f17
GS
485 if (hstate->connp->out_status == STREAM_STATE_ERROR) {
486 SCLogError(SC_ERR_ALPARSER, "Outbound parser is in error state, no"
487 " need to feed data to libhtp");
488 SCReturnInt(-1);
489 }
490
0165b3f0
PR
491 /* Unset the body inspection (the callback should
492 * reactivate it if necessary) */
70b32f73 493 hstate->flags &=~ HTP_FLAG_NEW_BODY_SET;
0165b3f0 494
0a85fd67 495 r = htp_connp_res_data(hstate->connp, 0, input, input_len);
bf236e45
GS
496 switch(r) {
497 case STREAM_STATE_ERROR:
148883ce 498 if (hstate->connp->last_error != NULL) {
bf236e45
GS
499 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP server "
500 "response: [%"PRId32"] [%s] [%"PRId32"] %s",
501 hstate->connp->last_error->level,
502 hstate->connp->last_error->file,
503 hstate->connp->last_error->line,
504 hstate->connp->last_error->msg);
148883ce 505 } else {
bf236e45
GS
506 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP server "
507 "response");
148883ce 508 }
bf236e45
GS
509 hstate->flags = HTP_FLAG_STATE_ERROR;
510 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 511 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
512 ret = -1;
513 break;
514 case STREAM_STATE_DATA:
515 hstate->flags |= HTP_FLAG_STATE_DATA;
516 break;
517 case STREAM_STATE_DATA_OTHER:
518 SCLogDebug("CONNECT not supported yet");
519 hstate->flags = HTP_FLAG_STATE_ERROR;
520 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 521 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
522 ret = -1;
523 break;
524 default:
525 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 526 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45 527 }
07f7ba55 528
fc2f7f29
GS
529 /* if we the TCP connection is closed, then close the HTTP connection */
530 if ((pstate->flags & APP_LAYER_PARSER_EOF) &&
0a85fd67
GS
531 ! (hstate->flags & HTP_FLAG_STATE_CLOSED) &&
532 ! (hstate->flags & HTP_FLAG_STATE_DATA))
fc2f7f29
GS
533 {
534 htp_connp_close(hstate->connp, 0);
535 hstate->flags |= HTP_FLAG_STATE_CLOSED;
536 }
537
48248687 538 SCLogDebug("hstate->connp %p", hstate->connp);
0a85fd67 539 SCReturnInt(ret);
07f7ba55
GS
540}
541
0165b3f0
PR
542/**
543 * \brief Append a chunk of body to the HtpBody struct
544 * \param body pointer to the HtpBody holding the list
545 * \param data pointer to the data of the chunk
546 * \param len length of the chunk pointed by data
6ebe7b7c
VJ
547 * \retval 0 ok
548 * \retval -1 error
0165b3f0 549 */
6ebe7b7c 550int HtpBodyAppendChunk(SCHtpTxUserData *htud, HtpBody *body, uint8_t *data, uint32_t len)
0165b3f0
PR
551{
552 SCEnter();
7a8cd61f
VJ
553
554 HtpBodyChunk *bd = NULL;
555
6ebe7b7c
VJ
556 if (len == 0 || data == NULL) {
557 SCReturnInt(0);
558 }
44b6380a 559
0165b3f0
PR
560 if (body->nchunks == 0) {
561 /* New chunk */
7a8cd61f 562 bd = (HtpBodyChunk *)SCMalloc(sizeof(HtpBodyChunk));
9f4fae5b 563 if (bd == NULL)
6ebe7b7c 564 goto error;
7a8cd61f 565
0165b3f0 566 bd->len = len;
fe7948a7
PR
567 bd->data = SCMalloc(len);
568 if (bd->data == NULL) {
44b6380a 569 goto error;
fe7948a7 570 }
44b6380a 571
fe7948a7 572 memcpy(bd->data, data, len);
5c6a65dc 573 htud->content_len_so_far = len;
0165b3f0
PR
574 body->first = body->last = bd;
575 body->nchunks++;
576 bd->next = NULL;
577 bd->id = body->nchunks;
578 } else {
6ebe7b7c
VJ
579 bd = (HtpBodyChunk *)SCMalloc(sizeof(HtpBodyChunk));
580 if (bd == NULL)
581 goto error;
44b6380a 582
6ebe7b7c
VJ
583 bd->len = len;
584 bd->data = SCMalloc(len);
585 if (bd->data == NULL) {
586 goto error;
0165b3f0 587 }
6ebe7b7c
VJ
588
589 memcpy(bd->data, data, len);
590 htud->content_len_so_far += len;
591 body->last->next = bd;
592 body->last = bd;
593 body->nchunks++;
594 bd->next = NULL;
595 bd->id = body->nchunks;
0165b3f0 596 }
44b6380a 597 SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"", body,
0165b3f0 598 bd->id, bd->data, (uint32_t)bd->len);
44b6380a 599
6ebe7b7c 600 SCReturnInt(0);
44b6380a
VJ
601
602error:
603 if (bd != NULL) {
604 if (bd->data != NULL) {
605 SCFree(bd->data);
606 }
607 SCFree(bd->data);
608 }
6ebe7b7c 609 SCReturnInt(-1);
0165b3f0
PR
610}
611
612/**
613 * \brief Print the information and chunks of a Body
614 * \param body pointer to the HtpBody holding the list
615 * \retval none
616 */
617void HtpBodyPrint(HtpBody *body)
618{
619 if (SCLogDebugEnabled()) {
620 SCEnter();
621
622 if (body->nchunks == 0)
623 return;
624
7a8cd61f 625 HtpBodyChunk *cur = NULL;
0165b3f0
PR
626 SCLogDebug("--- Start body chunks at %p ---", body);
627 for (cur = body->first; cur != NULL; cur = cur->next) {
628 SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n",
629 body, cur->id, cur->data, (uint32_t)cur->len);
630 PrintRawDataFp(stdout, (uint8_t*)cur->data, cur->len);
631 }
632 SCLogDebug("--- End body chunks at %p ---", body);
633 }
634}
635
636/**
a9cdd2bb 637 * \brief Free the information held in the request body
0165b3f0
PR
638 * \param body pointer to the HtpBody holding the list
639 * \retval none
640 */
641void HtpBodyFree(HtpBody *body)
642{
643 SCEnter();
7a8cd61f 644
0165b3f0
PR
645 if (body->nchunks == 0)
646 return;
647
648 SCLogDebug("Removing chunks of Body %p; Last Chunk id: %"PRIu32", data %p,"
649 " len %"PRIu32"\n", body, body->last->id, body->last->data,
650 (uint32_t)body->last->len);
651 body->nchunks = 0;
652
7a8cd61f
VJ
653 HtpBodyChunk *cur = NULL;
654 HtpBodyChunk *prev = NULL;
655
0165b3f0
PR
656 prev = body->first;
657 while (prev != NULL) {
658 cur = prev->next;
a0fa924c
GS
659 if (prev->data != NULL)
660 SCFree(prev->data);
25a3a5c6 661 SCFree(prev);
0165b3f0
PR
662 prev = cur;
663 }
664 body->first = body->last = NULL;
0165b3f0
PR
665 body->operation = HTP_BODY_NONE;
666}
667
668/**
a9cdd2bb 669 * \brief Function callback to append chunks for Requests
0165b3f0
PR
670 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
671 * \retval int HOOK_OK if all goes well
672 */
673int HTPCallbackRequestBodyData(htp_tx_data_t *d)
674{
675 SCEnter();
676 HtpState *hstate = (HtpState *)d->tx->connp->user_data;
677 SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
678 "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
679
680 //PrintRawDataFp(stdout, d->data, d->len);
06a65cb4
PR
681 SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(d->tx);
682 if (htud == NULL) {
683 htud = SCMalloc(sizeof(SCHtpTxUserData));
684 if (htud == NULL) {
06a65cb4
PR
685 SCReturnInt(HOOK_OK);
686 }
687 memset(htud, 0, sizeof(SCHtpTxUserData));
688 htud->body.operation = HTP_BODY_NONE;
0165b3f0 689
5c6a65dc
AS
690 htp_header_t *cl = table_getc(d->tx->request_headers, "content-length");
691 if (cl != NULL)
692 htud->content_len = htp_parse_content_length(cl->value);
693
06a65cb4
PR
694 /* Set the user data for handling body chunks on this transaction */
695 htp_tx_set_user_data(d->tx, htud);
0165b3f0 696 }
0165b3f0 697
06a65cb4 698 htud->body.operation = HTP_BODY_REQUEST;
0165b3f0 699
6ebe7b7c
VJ
700 SCLogDebug("htud->content_len_so_far %u", htud->content_len_so_far);
701 SCLogDebug("hstate->request_body_limit %u", hstate->request_body_limit);
702
703 /* within limits, add the body chunk to the state. */
dbe291bc 704 if (hstate->request_body_limit == 0 || htud->content_len_so_far < hstate->request_body_limit)
6ebe7b7c
VJ
705 {
706 uint32_t len = (uint32_t)d->len;
707
dbe291bc
VJ
708 if (hstate->request_body_limit > 0 &&
709 (htud->content_len_so_far + len) > hstate->request_body_limit)
710 {
6ebe7b7c
VJ
711 len = hstate->request_body_limit - htud->content_len_so_far;
712 BUG_ON(len > (uint32_t)d->len);
713 }
0165b3f0 714
6ebe7b7c
VJ
715 SCLogDebug("len %u", len);
716
717 int r = HtpBodyAppendChunk(htud, &htud->body, (uint8_t*)d->data, len);
718 if (r < 0) {
719 htud->flags |= HTP_BODY_COMPLETE;
dbe291bc
VJ
720 } else if (hstate->request_body_limit > 0 &&
721 htud->content_len_so_far >= hstate->request_body_limit)
722 {
6ebe7b7c
VJ
723 htud->flags |= HTP_BODY_COMPLETE;
724 } else if (htud->content_len_so_far == htud->content_len) {
725 htud->flags |= HTP_BODY_COMPLETE;
726 }
727
6ebe7b7c
VJ
728 /* set the new chunk flag */
729 hstate->flags |= HTP_FLAG_NEW_BODY_SET;
730
731 //if (SCLogDebugEnabled()) {
732 // HtpBodyPrint(&htud->body);
733 //}
734 }
0165b3f0
PR
735
736 SCReturnInt(HOOK_OK);
737}
738
fc2f7f29
GS
739/**
740 * \brief Print the stats of the HTTP requests
741 */
742void HTPAtExitPrintStats(void)
743{
744#ifdef DEBUG
a9cdd2bb 745 SCEnter();
fc2f7f29
GS
746 SCMutexLock(&htp_state_mem_lock);
747 SCLogDebug("http_state_memcnt %"PRIu64", http_state_memuse %"PRIu64"",
748 htp_state_memcnt, htp_state_memuse);
749 SCMutexUnlock(&htp_state_mem_lock);
a9cdd2bb 750 SCReturn;
fc2f7f29
GS
751#endif
752}
753
754/** \brief Clears the HTTP server configuration memory used by HTP library */
755void HTPFreeConfig(void)
756{
a9cdd2bb
BR
757 SCEnter();
758
759 HTPCfgRec *nextrec = cfglist.next;
760 SCRadixReleaseRadixTree(cfgtree);
a0fa924c 761 htp_config_destroy(cfglist.cfg);
a9cdd2bb
BR
762 while (nextrec != NULL) {
763 HTPCfgRec *htprec = nextrec;
47a47e8a 764 nextrec = nextrec->next;
a9cdd2bb 765
a0fa924c 766 htp_config_destroy(htprec->cfg);
a9cdd2bb 767 SCFree(htprec);
a9cdd2bb
BR
768 }
769 SCReturn;
fc2f7f29
GS
770}
771
356a8bf3
GS
772/**
773 * \brief callback for request to store the recent incoming request
774 in to the recent_in_tx for the given htp state
775 * \param connp pointer to the current connection parser which has the htp
776 * state in it as user data
777 */
778static int HTPCallbackRequest(htp_connp_t *connp) {
779 SCEnter();
187949b9 780
356a8bf3 781 HtpState *hstate = (HtpState *)connp->user_data;
187949b9 782 if (hstate == NULL) {
50f7d0a8 783 SCReturnInt(HOOK_ERROR);
187949b9 784 }
70b32f73
VJ
785
786 hstate->flags |= HTP_FLAG_NEW_REQUEST;
787
788 SCLogDebug("HTTP request completed");
789
50f7d0a8 790 SCReturnInt(HOOK_OK);
356a8bf3
GS
791}
792
793/**
794 * \brief callback for response to remove the recent received requests
795 from the recent_in_tx for the given htp state
796 * \param connp pointer to the current connection parser which has the htp
797 * state in it as user data
798 */
799static int HTPCallbackResponse(htp_connp_t *connp) {
800 SCEnter();
187949b9 801
356a8bf3 802 HtpState *hstate = (HtpState *)connp->user_data;
187949b9 803 if (hstate == NULL) {
50f7d0a8 804 SCReturnInt(HOOK_ERROR);
187949b9 805 }
356a8bf3 806
06a65cb4
PR
807 /* Unset the body inspection (if any) */
808 hstate->flags &=~ HTP_FLAG_NEW_BODY_SET;
0165b3f0 809
70b32f73
VJ
810 /* remove obsolete transactions */
811 size_t idx;
812 for (idx = 0; idx < hstate->transaction_done; idx++) {
ffd85ac4 813 htp_tx_t *tx = list_get(hstate->connp->conn->transactions, idx);
70b32f73
VJ
814 if (tx == NULL)
815 continue;
816
06a65cb4
PR
817 /* This will remove obsolete body chunks */
818 SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx);
819 if (htud != NULL) {
820 HtpBodyFree(&htud->body);
821 htp_tx_set_user_data(tx, NULL);
822 SCFree(htud);
823 }
824
70b32f73 825 htp_tx_destroy(tx);
bf236e45 826 }
50f7d0a8
GS
827
828 SCReturnInt(HOOK_OK);
356a8bf3
GS
829}
830
a9cdd2bb
BR
831static void HTPConfigure(void)
832{
833 SCEnter();
834 ConfNode *default_config;
835 ConfNode *server_config;
836
837 AppLayerRegisterStateFuncs(ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
70b32f73 838 AppLayerRegisterTransactionIdFuncs(ALPROTO_HTTP, HTPStateUpdateTransactionId, HTPStateTransactionFree);
a9cdd2bb
BR
839
840 cfglist.next = NULL;
841
842 cfgtree = SCRadixCreateRadixTree(NULL, NULL);
843 if (NULL == cfgtree) {
844 SCLogError(SC_ERR_MEM_ALLOC, "Error initializing HTP config tree");
845
846 if (SCLogDebugEnabled()) {
847 abort();
848 }
849 else {
850 exit(EXIT_FAILURE);
851 }
852 }
853
854 /* Default Config */
855 cfglist.cfg = htp_config_create();
856 if (NULL == cfglist.cfg) {
857 SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP default config");
858
859 if (SCLogDebugEnabled()) {
860 abort();
861 }
862 else {
863 exit(EXIT_FAILURE);
864 }
865 }
866
867 SCLogDebug("LIBHTP default config: %p", cfglist.cfg);
868
6ebe7b7c 869 cfglist.request_body_limit = HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT;
a9cdd2bb
BR
870 htp_config_register_request(cfglist.cfg, HTPCallbackRequest);
871 htp_config_register_response(cfglist.cfg, HTPCallbackResponse);
872 htp_config_set_generate_request_uri_normalized(cfglist.cfg, 1);
873
874 default_config = ConfGetNode("libhtp.default-config");
875 if (NULL != default_config) {
876 ConfNode *p = NULL;
877
878 /* Default Parameters */
879 TAILQ_FOREACH(p, &default_config->head, next) {
c8863063 880 //ConfNode *pval;
a9cdd2bb
BR
881
882 if (strcasecmp("personality", p->name) == 0) {
883 /* Personalities */
c8863063 884 int personality = HTPLookupPersonality(p->val);
a9cdd2bb 885
c8863063
VJ
886 SCLogDebug("LIBHTP default: %s=%s",
887 p->name, p->val);
a9cdd2bb
BR
888
889
c8863063
VJ
890 if (personality >= 0) {
891 SCLogDebug("LIBHTP default: %s=%s (%d)",
892 p->name, p->val,
893 personality);
894 if (htp_config_set_server_personality(cfglist.cfg,
a9cdd2bb 895 personality) == HTP_ERROR)
c8863063
VJ
896 {
897 SCLogWarning(SC_ERR_INVALID_VALUE,
898 "LIBHTP Failed adding personality "
899 "\"%s\", ignoring", p->val);
900 } else {
901 SCLogDebug("LIBHTP personality set to %s",
902 HTPLookupPersonalityString(personality));
a9cdd2bb 903 }
c8863063
VJ
904 }
905 else {
906 SCLogWarning(SC_ERR_UNKNOWN_VALUE,
907 "LIBHTP Unknown personality "
908 "\"%s\", ignoring", p->val);
909 continue;
6ebe7b7c 910 }
edeec290
VJ
911 } else if (strcasecmp("request-body-limit", p->name) == 0 ||
912 strcasecmp("request_body_limit", p->name) == 0) {
6ebe7b7c 913
dbe291bc
VJ
914 /* limit */
915 int limit = atoi(p->val);
6ebe7b7c 916
dbe291bc
VJ
917 if (limit >= 0) {
918 SCLogDebug("LIBHTP default: %s=%s (%d)",
919 p->name, p->val, limit);
6ebe7b7c 920
dbe291bc
VJ
921 cfglist.request_body_limit = (uint32_t)limit;
922 }
923 else {
924 SCLogWarning(SC_ERR_UNKNOWN_VALUE,
925 "LIBHTP malformed request_body_limit "
926 "\"%s\", using default %u", p->val,
927 HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT);
928 cfglist.request_body_limit = HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT;
929 continue;
a9cdd2bb 930 }
dbe291bc 931
a9cdd2bb
BR
932 } else {
933 SCLogWarning(SC_ERR_UNKNOWN_VALUE,
6ebe7b7c
VJ
934 "LIBHTP Ignoring unknown default config: %s",
935 p->name);
a9cdd2bb
BR
936 }
937 }
938 }
939
940 /* Read server config and create a parser for each IP in radix tree */
941 server_config = ConfGetNode("libhtp.server-config");
942 SCLogDebug("LIBHTP Configuring %p", server_config);
943 if (server_config != NULL) {
944 ConfNode *si;
945 ConfNode *s;
946 HTPCfgRec *htprec;
947 HTPCfgRec *nextrec;
948 htp_cfg_t *htp;
949
950 /* Server Nodes */
951 TAILQ_FOREACH(si, &server_config->head, next) {
952 ConfNode *p = NULL;
953
954 /* Need the named node, not the index */
955 s = TAILQ_FIRST(&si->head);
956 if (NULL == s) {
957 SCLogDebug("LIBHTP s NULL");
958 continue;
959 }
960
961 SCLogDebug("LIBHTP server %s", s->name);
962
963 nextrec = cfglist.next;
964 htprec = cfglist.next = SCMalloc(sizeof(HTPCfgRec));
965 if (NULL == htprec) {
966 SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP server config rec");
967 if (SCLogDebugEnabled()) {
968 abort();
969 }
970 else {
971 exit(EXIT_FAILURE);
972 }
973 }
974
975 cfglist.next->next = nextrec;
976 htp = cfglist.next->cfg = htp_config_create();
977 if (NULL == htp) {
978 SCLogError(SC_ERR_MEM_ALLOC, "Failed to create HTP server config");
979 if (SCLogDebugEnabled()) {
980 abort();
981 }
982 else {
983 exit(EXIT_FAILURE);
984 }
985 }
986
6ebe7b7c 987 htprec->request_body_limit = HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT;
a9cdd2bb
BR
988 htp_config_register_request(htp, HTPCallbackRequest);
989 htp_config_register_response(htp, HTPCallbackResponse);
990 htp_config_set_generate_request_uri_normalized(htp, 1);
991
992 /* Server Parameters */
993 TAILQ_FOREACH(p, &s->head, next) {
994 ConfNode *pval;
995
996 if (strcasecmp("address", p->name) == 0) {
997
998 /* Addresses */
999 TAILQ_FOREACH(pval, &p->head, next) {
1000 SCLogDebug("LIBHTP server %s: %s=%s",
1001 s->name, p->name, pval->val);
1002
1003 /* IPV6 or IPV4? */
1004 if (strchr(pval->val, ':') != NULL) {
1005 SCLogDebug("LIBHTP adding ipv6 server %s at %s: %p",
1006 s->name, pval->val, htp);
1007 if (SCRadixAddKeyIPV6String(pval->val,
1008 cfgtree, htprec) == NULL)
1009 {
1010 SCLogWarning(SC_ERR_INVALID_VALUE,
1011 "LIBHTP failed to add "
1012 "ipv6 server %s, ignoring",
1013 pval->val);
1014 }
1015 } else {
1016 SCLogDebug("LIBHTP adding ipv4 server %s at %s: %p",
1017 s->name, pval->val, htp);
1018 if (SCRadixAddKeyIPV4String(pval->val,
1019 cfgtree, htprec) == NULL)
1020 {
1021 SCLogWarning(SC_ERR_INVALID_VALUE,
1022 "LIBHTP failed to add "
1023 "ipv4 server %s, ignoring",
1024 pval->val);
1025 }
1026 }
1027 }
1028 } else if (strcasecmp("personality", p->name) == 0) {
c8863063
VJ
1029 /* Personalitie */
1030 int personality = HTPLookupPersonality(p->val);
a9cdd2bb 1031
c8863063
VJ
1032 SCLogDebug("LIBHTP server %s: %s=%s",
1033 s->name, p->name, p->val);
a9cdd2bb
BR
1034
1035
c8863063
VJ
1036 if (personality >= 0) {
1037 SCLogDebug("LIBHTP %s: %s=%s (%d)",
1038 s->name, p->name, p->val,
1039 personality);
1040 if (htp_config_set_server_personality(htp,
a9cdd2bb 1041 personality) == HTP_ERROR)
c8863063
VJ
1042 {
1043 SCLogWarning(SC_ERR_INVALID_VALUE,
1044 "LIBHTP Failed adding personality "
1045 "\"%s\", ignoring", p->val);
1046 } else {
1047 SCLogDebug("LIBHTP personality set to %s",
1048 HTPLookupPersonalityString(personality));
a9cdd2bb 1049 }
6ebe7b7c 1050 }
c8863063
VJ
1051 else {
1052 SCLogWarning(SC_ERR_UNKNOWN_VALUE,
1053 "LIBHTP Unknown personality "
1054 "\"%s\", ignoring", p->val);
1055 continue;
1056 }
1057
a3303fcf
VJ
1058 /* VJ the non underscore version was a typo but keeping it for
1059 * compatibility with existing installs */
1060 } else if (strcasecmp("request-body-limit", p->name) == 0 ||
1061 strcasecmp("request_body_limit", p->name) == 0) {
6ebe7b7c 1062 /* limit */
dbe291bc
VJ
1063 SCLogDebug("LIBHTP default: %s=%s",
1064 p->name, p->val);
6ebe7b7c 1065
dbe291bc 1066 int limit = atoi(p->val);
6ebe7b7c 1067
dbe291bc
VJ
1068 if (limit >= 0) {
1069 SCLogDebug("LIBHTP default: %s=%s (%d)",
1070 p->name, p->val, limit);
6ebe7b7c 1071
dbe291bc
VJ
1072 htprec->request_body_limit = (uint32_t)limit;
1073 }
1074 else {
1075 SCLogWarning(SC_ERR_UNKNOWN_VALUE,
1076 "LIBHTP malformed request_body_limit "
1077 "\"%s\", using default %u", p->val,
1078 HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT);
1079 htprec->request_body_limit = HTP_CONFIG_DEFAULT_REQUEST_BODY_LIMIT;
1080 continue;
a9cdd2bb
BR
1081 }
1082 } else {
1083 SCLogWarning(SC_ERR_UNKNOWN_VALUE,
1084 "LIBHTP Ignoring unknown server config: %s",
1085 p->name);
1086 }
1087 }
1088 }
1089 }
1090
1091 SCReturn;
1092}
1093
6fca55e0
VJ
1094void AppLayerHtpPrintStats(void) {
1095#ifdef DEBUG
1096 SCMutexLock(&htp_state_mem_lock);
1097 SCLogInfo("htp memory %"PRIu64" (%"PRIu64")", htp_state_memuse, htp_state_memcnt);
1098 SCMutexUnlock(&htp_state_mem_lock);
1099#endif
1100}
1101
07f7ba55
GS
1102/**
1103 * \brief Register the HTTP protocol and state handling functions to APP layer
1104 * of the engine.
1105 */
1106void RegisterHTPParsers(void)
1107{
a9cdd2bb 1108 SCEnter();
07f7ba55
GS
1109 AppLayerRegisterStateFuncs(ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
1110
1111 AppLayerRegisterProto("http", ALPROTO_HTTP, STREAM_TOSERVER,
1112 HTPHandleRequestData);
1113 AppLayerRegisterProto("http", ALPROTO_HTTP, STREAM_TOCLIENT,
1114 HTPHandleResponseData);
1115
a9cdd2bb
BR
1116 HTPConfigure();
1117 SCReturn;
07f7ba55
GS
1118}
1119
0165b3f0 1120/**
97d49d8f
AS
1121 * \brief This function is called at the end of SigLoadSignatures. This function
1122 * enables the htp layer to register a callback for the http request body.
1123 * need_htp_request_body is a flag that informs the htp app layer that
1124 * a module in the engine needs the http request body.
0165b3f0
PR
1125 */
1126void AppLayerHtpRegisterExtraCallbacks(void) {
a9cdd2bb 1127 SCEnter();
0165b3f0 1128 SCLogDebug("Registering extra htp callbacks");
97d49d8f 1129 if (need_htp_request_body == 1) {
0165b3f0 1130 SCLogDebug("Registering callback htp_config_register_request_body_data on htp");
a9cdd2bb
BR
1131 htp_config_register_request_body_data(cfglist.cfg,
1132 HTPCallbackRequestBodyData);
0165b3f0
PR
1133 } else {
1134 SCLogDebug("No htp extra callback needed");
1135 }
a9cdd2bb 1136 SCReturn;
0165b3f0
PR
1137}
1138
c22d4269 1139
c352bff6 1140#ifdef UNITTESTS
99fca038
VJ
1141static HTPCfgRec cfglist_backup;
1142
1143static void HtpConfigCreateBackup(void)
1144{
1145 cfglist_backup.cfg = cfglist.cfg;
1146 cfglist_backup.next = cfglist.next;
1147 cfglist_backup.request_body_limit = cfglist.request_body_limit;
1148
1149 return;
1150}
1151
1152static void HtpConfigRestoreBackup(void)
1153{
1154 cfglist.cfg = cfglist_backup.cfg;
1155 cfglist.next = cfglist_backup.next;
1156 cfglist.request_body_limit = cfglist_backup.request_body_limit;
1157
1158 return;
1159}
a9cdd2bb 1160
07f7ba55
GS
1161/** \test Test case where chunks are sent in smaller chunks and check the
1162 * response of the parser from HTP library. */
1163int HTPParserTest01(void) {
1164 int result = 1;
1165 Flow f;
fc2f7f29 1166 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
07f7ba55
GS
1167 " Data is c0oL!";
1168 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1169 TcpSession ssn;
25a3a5c6
PR
1170
1171 HtpState *htp_state = NULL;
07f7ba55
GS
1172 int r = 0;
1173 memset(&f, 0, sizeof(f));
1174 memset(&ssn, 0, sizeof(ssn));
07f7ba55 1175 f.protoctx = (void *)&ssn;
78e15ea7
VJ
1176 f.src.family = AF_INET;
1177 f.dst.family = AF_INET;
07f7ba55 1178
6a53ab9c 1179 StreamTcpInitConfig(TRUE);
8cc525c9 1180 FlowL7DataPtrInit(&f);
6a53ab9c 1181
07f7ba55
GS
1182 uint32_t u;
1183 for (u = 0; u < httplen1; u++) {
1184 uint8_t flags = 0;
1185
1186 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
1187 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
1188 else flags = STREAM_TOSERVER;
1189
c352bff6 1190 r = AppLayerParse(&f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
07f7ba55
GS
1191 if (r != 0) {
1192 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
1193 " 0: ", u, r);
1194 result = 0;
1195 goto end;
1196 }
1197 }
1198
8cc525c9 1199 htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
07f7ba55
GS
1200 if (htp_state == NULL) {
1201 printf("no http state: ");
1202 result = 0;
1203 goto end;
1204 }
1205
1206 htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0);
1207
1208 bstr *key = NULL;
1209 htp_header_t *h = NULL;
1210 table_iterator_reset(tx->request_headers);
1211 key = table_iterator_next(tx->request_headers, (void **) & h);
1212
2d6cf71d
GS
1213 if (htp_state->connp == NULL || strcmp(bstr_tocstr(h->value), "Victor/1.0")
1214 || tx->request_method_number != M_POST ||
fc2f7f29 1215 tx->request_protocol_number != HTTP_1_0)
07f7ba55 1216 {
2d6cf71d 1217 printf("expected header value: Victor/1.0 and got %s: and expected"
fc2f7f29 1218 " method: POST and got %s, expected protocol number HTTP/1.0"
2d6cf71d
GS
1219 " and got: %s \n", bstr_tocstr(h->value),
1220 bstr_tocstr(tx->request_method),
1221 bstr_tocstr(tx->request_protocol));
07f7ba55
GS
1222 result = 0;
1223 goto end;
1224 }
1225
1226end:
8cc525c9 1227 FlowL7DataPtrFree(&f);
6a53ab9c 1228 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1229 if (htp_state != NULL)
1230 HTPStateFree(htp_state);
07f7ba55
GS
1231 return result;
1232}
1233
2d6cf71d
GS
1234/** \test See how it deals with an incomplete request. */
1235int HTPParserTest02(void) {
1236 int result = 1;
1237 Flow f;
1238 uint8_t httpbuf1[] = "POST";
1239 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1240 TcpSession ssn;
25a3a5c6 1241 HtpState *http_state = NULL;
2d6cf71d
GS
1242
1243 memset(&f, 0, sizeof(f));
1244 memset(&ssn, 0, sizeof(ssn));
2d6cf71d 1245 f.protoctx = (void *)&ssn;
78e15ea7
VJ
1246 f.src.family = AF_INET;
1247 f.dst.family = AF_INET;
2d6cf71d 1248
6a53ab9c 1249 StreamTcpInitConfig(TRUE);
8cc525c9 1250 FlowL7DataPtrInit(&f);
6a53ab9c 1251
2d6cf71d 1252 int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
c352bff6 1253 STREAM_EOF, httpbuf1, httplen1);
2d6cf71d
GS
1254 if (r != 0) {
1255 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1256 result = 0;
1257 goto end;
1258 }
1259
8cc525c9 1260 http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
1261 if (http_state == NULL) {
1262 printf("no http state: ");
1263 result = 0;
1264 goto end;
1265 }
1266
1267 htp_tx_t *tx = list_get(http_state->connp->conn->transactions, 0);
1268
1269 bstr *key = NULL;
1270 htp_header_t *h = NULL;
1271 table_iterator_reset(tx->request_headers);
1272 key = table_iterator_next(tx->request_headers, (void **) & h);
1273
1274 if ((tx->request_method) != NULL || h != NULL)
1275 {
1276 printf("expected method NULL, got %s \n", bstr_tocstr(tx->request_method));
1277 result = 0;
1278 goto end;
1279 }
1280
1281end:
8cc525c9 1282 FlowL7DataPtrFree(&f);
6a53ab9c 1283 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1284 if (http_state != NULL)
1285 HTPStateFree(http_state);
2d6cf71d
GS
1286 return result;
1287}
1288
1289/** \test Test case where method is invalid and data is sent in smaller chunks
1290 * and check the response of the parser from HTP library. */
1291int HTPParserTest03(void) {
1292 int result = 1;
1293 Flow f;
1294 uint8_t httpbuf1[] = "HELLO / HTTP/1.0\r\n";
1295 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1296 TcpSession ssn;
25a3a5c6
PR
1297
1298 HtpState *htp_state = NULL;
2d6cf71d
GS
1299 int r = 0;
1300 memset(&f, 0, sizeof(f));
1301 memset(&ssn, 0, sizeof(ssn));
2d6cf71d 1302 f.protoctx = (void *)&ssn;
78e15ea7
VJ
1303 f.src.family = AF_INET;
1304 f.dst.family = AF_INET;
2d6cf71d 1305
6a53ab9c 1306 StreamTcpInitConfig(TRUE);
8cc525c9 1307 FlowL7DataPtrInit(&f);
6a53ab9c 1308
2d6cf71d
GS
1309 uint32_t u;
1310 for (u = 0; u < httplen1; u++) {
1311 uint8_t flags = 0;
1312
1313 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
1314 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
1315 else flags = STREAM_TOSERVER;
1316
c352bff6 1317 r = AppLayerParse(&f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
2d6cf71d
GS
1318 if (r != 0) {
1319 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
1320 " 0: ", u, r);
1321 result = 0;
1322 goto end;
1323 }
1324 }
8cc525c9 1325 htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
1326 if (htp_state == NULL) {
1327 printf("no http state: ");
1328 result = 0;
1329 goto end;
1330 }
1331
1332 htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0);
1333
1334 bstr *key = NULL;
1335 htp_header_t *h = NULL;
1336 table_iterator_reset(tx->request_headers);
1337 key = table_iterator_next(tx->request_headers, (void **) & h);
1338
1339 if (htp_state->connp == NULL || tx->request_method_number != M_UNKNOWN ||
1340 h != NULL || tx->request_protocol_number != HTTP_1_0)
1341 {
1342 printf("expected method M_UNKNOWN and got %s: , expected protocol "
1343 "HTTP/1.0 and got %s \n", bstr_tocstr(tx->request_method),
1344 bstr_tocstr(tx->request_protocol));
1345 result = 0;
1346 goto end;
1347 }
1348
1349end:
8cc525c9 1350 FlowL7DataPtrFree(&f);
6a53ab9c 1351 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1352 if (htp_state != NULL)
1353 HTPStateFree(htp_state);
2d6cf71d
GS
1354 return result;
1355}
1356
1357/** \test Test case where invalid data is sent and check the response of the
1358 * parser from HTP library. */
1359int HTPParserTest04(void) {
1360 int result = 1;
1361 Flow f;
25a3a5c6 1362 HtpState *htp_state = NULL;
2d6cf71d
GS
1363 uint8_t httpbuf1[] = "World!\r\n";
1364 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1365 TcpSession ssn;
1366 int r = 0;
1367 memset(&f, 0, sizeof(f));
1368 memset(&ssn, 0, sizeof(ssn));
2d6cf71d 1369 f.protoctx = (void *)&ssn;
78e15ea7
VJ
1370 f.src.family = AF_INET;
1371 f.dst.family = AF_INET;
2d6cf71d 1372
6a53ab9c 1373 StreamTcpInitConfig(TRUE);
8cc525c9 1374 FlowL7DataPtrInit(&f);
6a53ab9c 1375
2d6cf71d 1376 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
c352bff6 1377 STREAM_EOF, httpbuf1, httplen1);
2d6cf71d 1378
8cc525c9 1379 htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
1380 if (htp_state == NULL) {
1381 printf("no http state: ");
1382 result = 0;
1383 goto end;
1384 }
1385
1386 htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0);
1387
1388 bstr *key = NULL;
1389 htp_header_t *h = NULL;
1390 table_iterator_reset(tx->request_headers);
1391 key = table_iterator_next(tx->request_headers, (void **) & h);
1392
1393 if (htp_state->connp == NULL || tx->request_method_number != M_UNKNOWN ||
1394 h != NULL || tx->request_protocol_number != PROTOCOL_UNKNOWN)
1395 {
1396 printf("expected method M_UNKNOWN and got %s: , expected protocol "
1397 "NULL and got %s \n", bstr_tocstr(tx->request_method),
1398 bstr_tocstr(tx->request_protocol));
1399 result = 0;
1400 goto end;
1401 }
1402
1403end:
8cc525c9 1404 FlowL7DataPtrFree(&f);
6a53ab9c 1405 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1406 if (htp_state != NULL)
1407 HTPStateFree(htp_state);
2d6cf71d
GS
1408 return result;
1409}
1410
1411/** \test Test both sides of a http stream mixed up to see if the HTP parser
1412 * properly parsed them and also keeps them separated. */
1413int HTPParserTest05(void) {
1414 int result = 1;
1415 Flow f;
25a3a5c6 1416 HtpState *http_state = NULL;
fc2f7f29 1417 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\n";
2d6cf71d
GS
1418 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1419 uint8_t httpbuf2[] = "Post D";
1420 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
1421 uint8_t httpbuf3[] = "ata is c0oL!";
1422 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
1423
fc2f7f29 1424 uint8_t httpbuf4[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
2d6cf71d
GS
1425 uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
1426 uint8_t httpbuf5[] = "post R";
1427 uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
1428 uint8_t httpbuf6[] = "esults are tha bomb!";
1429 uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
1430 TcpSession ssn;
1431
1432 memset(&f, 0, sizeof(f));
1433 memset(&ssn, 0, sizeof(ssn));
2d6cf71d 1434 f.protoctx = (void *)&ssn;
78e15ea7
VJ
1435 f.src.family = AF_INET;
1436 f.dst.family = AF_INET;
2d6cf71d 1437
6a53ab9c 1438 StreamTcpInitConfig(TRUE);
8cc525c9 1439 FlowL7DataPtrInit(&f);
6a53ab9c 1440
2d6cf71d 1441 int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START,
c352bff6 1442 httpbuf1, httplen1);
2d6cf71d
GS
1443 if (r != 0) {
1444 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1445 result = 0;
1446 goto end;
1447 }
1448
1449 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, httpbuf4,
c352bff6 1450 httplen4);
2d6cf71d
GS
1451 if (r != 0) {
1452 printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
1453 result = 0;
1454 goto end;
1455 }
1456
c352bff6 1457 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf5, httplen5);
2d6cf71d
GS
1458 if (r != 0) {
1459 printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
1460 result = 0;
1461 goto end;
1462 }
1463
c352bff6 1464 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
2d6cf71d
GS
1465 if (r != 0) {
1466 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
1467 result = 0;
1468 goto end;
1469 }
1470
1471 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3,
c352bff6 1472 httplen3);
2d6cf71d
GS
1473 if (r != 0) {
1474 printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
1475 result = 0;
1476 goto end;
1477 }
1478
1479 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf6,
c352bff6 1480 httplen6);
2d6cf71d
GS
1481 if (r != 0) {
1482 printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
1483 result = 0;
1484 goto end;
1485 }
1486
8cc525c9 1487 http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
1488 if (http_state == NULL) {
1489 printf("no http state: ");
1490 result = 0;
1491 goto end;
1492 }
1493
1494 htp_tx_t *tx = list_get(http_state->connp->conn->transactions, 0);
1495
1496 bstr *key = NULL;
1497 htp_header_t *h = NULL;
1498 table_iterator_reset(tx->request_headers);
1499 key = table_iterator_next(tx->request_headers, (void **) & h);
1500
1501 if (http_state->connp == NULL || tx->request_method_number != M_POST ||
fc2f7f29 1502 h == NULL || tx->request_protocol_number != HTTP_1_0)
2d6cf71d
GS
1503 {
1504 printf("expected method M_POST and got %s: , expected protocol "
fc2f7f29 1505 "HTTP/1.0 and got %s \n", bstr_tocstr(tx->request_method),
2d6cf71d
GS
1506 bstr_tocstr(tx->request_protocol));
1507 result = 0;
1508 goto end;
1509 }
1510
f862de2e 1511 if (tx->response_status_number != 200) {
2d6cf71d 1512 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
fc2f7f29 1513 "HTTP/1.0 and got %s \n", tx->response_status_number,
2d6cf71d
GS
1514 bstr_tocstr(tx->response_message),
1515 bstr_tocstr(tx->response_protocol));
1516 result = 0;
1517 goto end;
1518 }
1519end:
8cc525c9 1520 FlowL7DataPtrFree(&f);
6a53ab9c 1521 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1522 if (http_state != NULL)
1523 HTPStateFree(http_state);
2d6cf71d
GS
1524 return result;
1525}
1526
fc2f7f29
GS
1527/** \test Test proper chunked encoded response body
1528 */
1529int HTPParserTest06(void) {
1530 int result = 1;
1531 Flow f;
1532 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
1533 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
1534 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
1535 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1536 uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
1537 "GMT\r\n"
1538 "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
1539 "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
1540 "FrontPage/5.0.2.2510\r\n"
1541 "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
1542 "chunked\r\n"
1543 "Content-Type: text/html\r\n\r\n"
1544 "1408\r\n"
1545 "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
1546 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
1547 "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
1548 "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
1549 "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
1550 "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
1551 "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
1552 "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
1553 "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
1554 "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
1555 "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
1556 "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
1557 "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
1558 "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
1559 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
1560 "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
1561 "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
1562 "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
1563 "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
1564 "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
1565 "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
1566 "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
1567 "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
1568 "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
1569 "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
1570 "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
1571 "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
1572 "aHA=0\r\n\r\n";
1573 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
1574 TcpSession ssn;
25a3a5c6 1575 HtpState *http_state = NULL;
fc2f7f29
GS
1576
1577 memset(&f, 0, sizeof(f));
1578 memset(&ssn, 0, sizeof(ssn));
78e15ea7 1579
fc2f7f29 1580 f.protoctx = (void *)&ssn;
78e15ea7
VJ
1581 f.src.family = AF_INET;
1582 f.dst.family = AF_INET;
fc2f7f29 1583
6a53ab9c 1584 StreamTcpInitConfig(TRUE);
8cc525c9 1585 FlowL7DataPtrInit(&f);
6a53ab9c 1586
fc2f7f29 1587 int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START,
c352bff6 1588 httpbuf1, httplen1);
fc2f7f29
GS
1589 if (r != 0) {
1590 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1591 result = 0;
1592 goto end;
1593 }
1594
1595 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, httpbuf2,
c352bff6 1596 httplen2);
fc2f7f29
GS
1597 if (r != 0) {
1598 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
1599 result = 0;
1600 goto end;
1601 }
1602
8cc525c9 1603 http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
fc2f7f29
GS
1604 if (http_state == NULL) {
1605 printf("no http state: ");
1606 result = 0;
1607 goto end;
1608 }
1609
1610 htp_tx_t *tx = list_get(http_state->connp->conn->transactions, 0);
1611
1612 bstr *key = NULL;
1613 htp_header_t *h = NULL;
1614 table_iterator_reset(tx->request_headers);
1615 key = table_iterator_next(tx->request_headers, (void **) & h);
1616
1617 if (http_state->connp == NULL || tx->request_method_number != M_GET ||
1618 h == NULL || tx->request_protocol_number != HTTP_1_1)
1619 {
1620 printf("expected method M_GET and got %s: , expected protocol "
1621 "HTTP/1.1 and got %s \n", bstr_tocstr(tx->request_method),
1622 bstr_tocstr(tx->request_protocol));
1623 result = 0;
1624 goto end;
1625 }
1626
1627 if (tx->response_status_number != 200 ||
1628 h == NULL || tx->request_protocol_number != HTTP_1_1)
1629 {
1630 printf("expected response 200 OK and got %"PRId32" %s: , expected proto"
1631 "col HTTP/1.1 and got %s \n", tx->response_status_number,
1632 bstr_tocstr(tx->response_message),
1633 bstr_tocstr(tx->response_protocol));
1634 result = 0;
1635 goto end;
1636 }
1637end:
8cc525c9 1638 FlowL7DataPtrFree(&f);
6a53ab9c 1639 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1640 if (http_state != NULL)
1641 HTPStateFree(http_state);
fc2f7f29
GS
1642 return result;
1643}
a9cdd2bb
BR
1644
1645#include "conf-yaml-loader.h"
1646
1647/** \test Test basic config */
1648int HTPParserConfigTest01(void)
1649{
1650 int ret = 0;
1651 char input[] = "\
1652%YAML 1.1\n\
1653---\n\
1654libhtp:\n\
1655\n\
1656 default-config:\n\
1657 personality: IDS\n\
1658\n\
1659 server-config:\n\
1660\n\
1661 - apache-tomcat:\n\
1662 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
1663 personality: Tomcat_6_0\n\
1664\n\
1665 - iis7:\n\
1666 address: \n\
1667 - 192.168.0.0/24\n\
1668 - 192.168.10.0/24\n\
1669 personality: IIS_7_0\n\
1670";
1671
1672 ConfCreateContextBackup();
1673 ConfInit();
1674
1675 ConfYamlLoadString(input, strlen(input));
1676
1677 ConfNode *outputs;
1678 outputs = ConfGetNode("libhtp.default-config.personality");
1679 if (outputs == NULL) {
1680 goto end;
1681 }
1682
1683 outputs = ConfGetNode("libhtp.server-config");
1684 if (outputs == NULL) {
1685 goto end;
1686 }
1687
1688 ConfNode *node = TAILQ_FIRST(&outputs->head);
1689 if (node == NULL) {
1690 goto end;
1691 }
1692 if (strcmp(node->name, "0") != 0) {
1693 goto end;
1694 }
1695 node = TAILQ_FIRST(&node->head);
1696 if (node == NULL) {
1697 goto end;
1698 }
1699 if (strcmp(node->name, "apache-tomcat") != 0) {
1700 goto end;
1701 }
1702
1703 int i = 0;
1704 ConfNode *n;
1705
1706 ConfNode *node2 = ConfNodeLookupChild(node, "personality");
1707 if (node2 == NULL) {
1708 goto end;
1709 }
1710 if (strcmp(node2->val, "Tomcat_6_0") != 0) {
1711 goto end;
1712 }
1713
1714 node = ConfNodeLookupChild(node, "address");
1715 if (node == NULL) {
1716 goto end;
1717 }
1718 TAILQ_FOREACH(n, &node->head, next) {
1719 if (n == NULL) {
1720 goto end;
1721 }
1722
1723 switch(i) {
1724 case 0:
1725 if (strcmp(n->name, "0") != 0) {
1726 goto end;
1727 }
1728 if (strcmp(n->val, "192.168.1.0/24") != 0) {
1729 goto end;
1730 }
1731 break;
1732 case 1:
1733 if (strcmp(n->name, "1") != 0) {
1734 goto end;
1735 }
1736 if (strcmp(n->val, "127.0.0.0/8") != 0) {
1737 goto end;
1738 }
1739 break;
1740 case 2:
1741 if (strcmp(n->name, "2") != 0) {
1742 goto end;
1743 }
1744 if (strcmp(n->val, "::1") != 0) {
1745 goto end;
1746 }
1747 break;
1748 default:
1749 goto end;
1750 }
1751 i++;
1752 }
1753
1754 outputs = ConfGetNode("libhtp.server-config");
1755 if (outputs == NULL) {
1756 goto end;
1757 }
1758
1759 node = TAILQ_FIRST(&outputs->head);
1760 node = TAILQ_NEXT(node, next);
1761 if (node == NULL) {
1762 goto end;
1763 }
1764 if (strcmp(node->name, "1") != 0) {
1765 goto end;
1766 }
1767 node = TAILQ_FIRST(&node->head);
1768 if (node == NULL) {
1769 goto end;
1770 }
1771 if (strcmp(node->name, "iis7") != 0) {
1772 goto end;
1773 }
1774
1775 node2 = ConfNodeLookupChild(node, "personality");
1776 if (node2 == NULL) {
1777 goto end;
1778 }
1779 if (strcmp(node2->val, "IIS_7_0") != 0) {
1780 goto end;
1781 }
1782
1783 node = ConfNodeLookupChild(node, "address");
1784 if (node == NULL) {
1785 goto end;
1786 }
1787
1788 i = 0;
1789 TAILQ_FOREACH(n, &node->head, next) {
1790 if (n == NULL) {
1791 goto end;
1792 }
1793
1794 switch(i) {
1795 case 0:
1796 if (strcmp(n->name, "0") != 0) {
1797 goto end;
1798 }
1799 if (strcmp(n->val, "192.168.0.0/24") != 0) {
1800 goto end;
1801 }
1802 break;
1803 case 1:
1804 if (strcmp(n->name, "1") != 0) {
1805 goto end;
1806 }
1807 if (strcmp(n->val, "192.168.10.0/24") != 0) {
1808 goto end;
1809 }
1810 break;
1811 default:
1812 goto end;
1813 }
1814 i++;
1815 }
1816
1817 ret = 1;
1818
1819end:
1820 ConfDeInit();
1821 ConfRestoreContextBackup();
1822
1823 return ret;
1824}
1825
1826/** \test Test config builds radix correctly */
1827int HTPParserConfigTest02(void)
1828{
1829 int ret = 0;
1830 char input[] = "\
1831%YAML 1.1\n\
1832---\n\
1833libhtp:\n\
1834\n\
1835 default-config:\n\
1836 personality: IDS\n\
1837\n\
1838 server-config:\n\
1839\n\
1840 - apache-tomcat:\n\
1841 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
1842 personality: Tomcat_6_0\n\
1843\n\
1844 - iis7:\n\
1845 address: \n\
1846 - 192.168.0.0/24\n\
1847 - 192.168.10.0/24\n\
1848 personality: IIS_7_0\n\
1849";
1850
1851 ConfCreateContextBackup();
1852 ConfInit();
5c6a65dc 1853 HtpConfigCreateBackup();
a9cdd2bb
BR
1854
1855 ConfYamlLoadString(input, strlen(input));
1856
1857 HTPConfigure();
1858
1859 if (cfglist.cfg == NULL) {
1860 printf("No default config created.\n");
1861 goto end;
1862 }
1863
1864 if (cfgtree == NULL) {
1865 printf("No config tree created.\n");
1866 goto end;
1867 }
1868
1869 SCRadixNode *cfgnode = NULL;
1870 htp_cfg_t *htp = cfglist.cfg;
1871 uint8_t buf[128];
1872 const char *addr;
1873
1874 addr = "192.168.10.42";
1875 if (inet_pton(AF_INET, addr, buf) == 1) {
1876 cfgnode = SCRadixFindKeyIPV4BestMatch(buf, cfgtree);
f81fccd6
VJ
1877 if (cfgnode != NULL) {
1878 HTPCfgRec *htp_cfg_rec = SC_RADIX_NODE_USERDATA(cfgnode, HTPCfgRec);
1879 if (htp_cfg_rec != NULL) {
1880 htp = htp_cfg_rec->cfg;
1881 SCLogDebug("LIBHTP using config: %p", htp);
1882 }
69a4fee7 1883 }
a9cdd2bb
BR
1884 if (htp == NULL) {
1885 printf("Could not get config for: %s\n", addr);
1886 goto end;
1887 }
1888 }
1889 else {
1890 printf("Failed to parse address: %s\n", addr);
1891 goto end;
1892 }
1893
1894 addr = "::1";
1895 if (inet_pton(AF_INET6, addr, buf) == 1) {
1896 cfgnode = SCRadixFindKeyIPV6BestMatch(buf, cfgtree);
f81fccd6
VJ
1897 if (cfgnode != NULL) {
1898 HTPCfgRec *htp_cfg_rec = SC_RADIX_NODE_USERDATA(cfgnode, HTPCfgRec);
1899 if (htp_cfg_rec != NULL) {
1900 htp = htp_cfg_rec->cfg;
1901 SCLogDebug("LIBHTP using config: %p", htp);
1902 }
69a4fee7 1903 }
a9cdd2bb
BR
1904 if (htp == NULL) {
1905 printf("Could not get config for: %s\n", addr);
1906 goto end;
1907 }
1908 }
1909 else {
1910 printf("Failed to parse address: %s\n", addr);
1911 goto end;
1912 }
1913
1914 ret = 1;
1915
1916end:
5c6a65dc 1917 HTPFreeConfig();
a9cdd2bb
BR
1918 ConfDeInit();
1919 ConfRestoreContextBackup();
5c6a65dc 1920 HtpConfigRestoreBackup();
a9cdd2bb
BR
1921
1922 return ret;
1923}
1924
1925/** \test Test traffic is handled by the correct htp config */
1926int HTPParserConfigTest03(void)
1927{
1928 int result = 1;
1929 Flow f;
06a65cb4 1930 FLOW_INITIALIZE(&f);
a9cdd2bb
BR
1931 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
1932 " Data is c0oL!";
1933 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
1934 TcpSession ssn;
1935
1936 HtpState *htp_state = NULL;
1937 int r = 0;
1938 char input[] = "\
1939%YAML 1.1\n\
1940---\n\
1941libhtp:\n\
1942\n\
1943 default-config:\n\
1944 personality: IDS\n\
1945\n\
1946 server-config:\n\
1947\n\
1948 - apache-tomcat:\n\
1949 address: [192.168.1.0/24, 127.0.0.0/8, \"::1\"]\n\
1950 personality: Tomcat_6_0\n\
1951\n\
1952 - iis7:\n\
1953 address: \n\
1954 - 192.168.0.0/24\n\
1955 - 192.168.10.0/24\n\
1956 personality: IIS_7_0\n\
1957";
1958
1959 ConfCreateContextBackup();
1960 ConfInit();
5c6a65dc 1961 HtpConfigCreateBackup();
a9cdd2bb
BR
1962
1963 ConfYamlLoadString(input, strlen(input));
1964
1965 HTPConfigure();
1966
1967 const char *addr = "192.168.10.42";
1968
1969 memset(&f, 0, sizeof(f));
1970 memset(&ssn, 0, sizeof(ssn));
1971 f.protoctx = (void *)&ssn;
1972 f.dst.family = AF_INET;
1973 inet_pton(f.dst.family, addr, f.dst.addr_data32);
1974
1975 SCRadixNode *cfgnode = NULL;
1976 htp_cfg_t *htp = cfglist.cfg;
1977 cfgnode = SCRadixFindKeyIPV4BestMatch((uint8_t *)GET_IPV4_DST_ADDR_PTR(&f), cfgtree);
f81fccd6
VJ
1978 if (cfgnode != NULL) {
1979 HTPCfgRec *htp_cfg_rec = SC_RADIX_NODE_USERDATA(cfgnode, HTPCfgRec);
1980 if (htp_cfg_rec != NULL) {
1981 htp = htp_cfg_rec->cfg;
1982 SCLogDebug("LIBHTP using config: %p", htp);
1983 }
69a4fee7 1984 }
a9cdd2bb
BR
1985 if (htp == NULL) {
1986 printf("Could not get config for: %s\n", addr);
1987 goto end;
1988 }
1989
1990 StreamTcpInitConfig(TRUE);
8cc525c9 1991 FlowL7DataPtrInit(&f);
a9cdd2bb
BR
1992
1993 uint32_t u;
1994 for (u = 0; u < httplen1; u++) {
1995 uint8_t flags = 0;
1996
1997 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
1998 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
1999 else flags = STREAM_TOSERVER;
2000
2001 r = AppLayerParse(&f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
2002 if (r != 0) {
2003 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
2004 " 0: ", u, r);
2005 result = 0;
2006 goto end;
2007 }
2008 }
2009
8cc525c9 2010 htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
a9cdd2bb
BR
2011 if (htp_state == NULL) {
2012 printf("no http state: ");
2013 result = 0;
2014 goto end;
2015 }
2016
2017 /* Check that the HTP state config matches the correct one */
2018 if (htp_state->connp->cfg != htp) {
2019 printf("wrong HTP config (%p instead of %p - default=%p): ",
2020 htp_state->connp->cfg, htp, cfglist.cfg);
2021 result = 0;
2022 goto end;
2023 }
2024
2025end:
5c6a65dc 2026 HTPFreeConfig();
a9cdd2bb
BR
2027 ConfDeInit();
2028 ConfRestoreContextBackup();
5c6a65dc 2029 HtpConfigRestoreBackup();
a9cdd2bb 2030
8cc525c9 2031 FlowL7DataPtrFree(&f);
a9cdd2bb
BR
2032 StreamTcpFreeConfig(TRUE);
2033 if (htp_state != NULL)
2034 HTPStateFree(htp_state);
06a65cb4 2035 FLOW_DESTROY(&f);
a9cdd2bb
BR
2036 return result;
2037}
06a65cb4 2038
c352bff6
VJ
2039#endif /* UNITTESTS */
2040
07f7ba55
GS
2041/**
2042 * \brief Register the Unit tests for the HTTP protocol
2043 */
2044void HTPParserRegisterTests(void) {
2045#ifdef UNITTESTS
2046 UtRegisterTest("HTPParserTest01", HTPParserTest01, 1);
2d6cf71d
GS
2047 UtRegisterTest("HTPParserTest02", HTPParserTest02, 1);
2048 UtRegisterTest("HTPParserTest03", HTPParserTest03, 1);
2049 UtRegisterTest("HTPParserTest04", HTPParserTest04, 1);
2050 UtRegisterTest("HTPParserTest05", HTPParserTest05, 1);
fc2f7f29 2051 UtRegisterTest("HTPParserTest06", HTPParserTest06, 1);
a9cdd2bb
BR
2052 UtRegisterTest("HTPParserConfigTest01", HTPParserConfigTest01, 1);
2053 UtRegisterTest("HTPParserConfigTest02", HTPParserConfigTest02, 1);
2054 UtRegisterTest("HTPParserConfigTest03", HTPParserConfigTest03, 1);
07f7ba55
GS
2055#endif /* UNITTESTS */
2056}
2057