]> git.ipfire.org Git - people/ms/suricata.git/blame - src/app-layer-htp.c
Add functions to radix to add ip/netblocks as string. Add macro to get node user...
[people/ms/suricata.git] / src / app-layer-htp.c
CommitLineData
07f7ba55
GS
1/* Copyright (c) 2009 Open Information Security Foundation */
2
3/**
4 * \file This file provides a HTTP protocol support for the engine using
5 * HTP library.
6 *
7 * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
0165b3f0 8 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
07f7ba55
GS
9 *
10 */
11
ecf86f9c 12#include "suricata-common.h"
07f7ba55
GS
13#include "debug.h"
14#include "decode.h"
15#include "threads.h"
07f7ba55
GS
16
17#include "util-print.h"
18#include "util-pool.h"
19
20#include "stream-tcp-private.h"
21#include "stream-tcp-reassemble.h"
6a53ab9c 22#include "stream-tcp.h"
07f7ba55
GS
23#include "stream.h"
24
25#include "app-layer-protos.h"
26#include "app-layer-parser.h"
fc2f7f29 27#include "app-layer-htp.h"
07f7ba55 28
705471e4 29#include "util-spm.h"
07f7ba55
GS
30#include "util-unittest.h"
31#include "util-debug.h"
32#include "app-layer-htp.h"
fc2f7f29 33#include "util-time.h"
25a3a5c6 34#include <htp/htp.h>
07f7ba55
GS
35
36#ifdef DEBUG
e26833be 37static SCMutex htp_state_mem_lock = PTHREAD_MUTEX_INITIALIZER;
07f7ba55
GS
38static uint64_t htp_state_memuse = 0;
39static uint64_t htp_state_memcnt = 0;
40#endif
97d49d8f
AS
41
42static uint8_t need_htp_request_body = 0;
07f7ba55
GS
43
44/** \brief Function to allocates the HTTP state memory and also creates the HTTP
45 * connection parser to be used by the HTP library
46 */
47static void *HTPStateAlloc(void)
48{
48248687
VJ
49 SCEnter();
50
25a3a5c6 51 HtpState *s = SCMalloc(sizeof(HtpState));
48248687
VJ
52 if (s == NULL) {
53 goto error;
54 }
07f7ba55 55
48248687 56 memset(s, 0x00, sizeof(HtpState));
07f7ba55
GS
57
58 /* create the connection parser structure to be used by HTP library */
48248687
VJ
59 s->connp = htp_connp_create(cfg);
60 if (s->connp == NULL) {
61 goto error;
62 }
48248687 63 SCLogDebug("s->connp %p", s->connp);
07f7ba55 64
0165b3f0
PR
65 s->body.nchunks = 0;
66 s->body.operation = HTP_BODY_NONE;
67 s->body.pcre_flags = HTP_PCRE_NONE;
68
356a8bf3 69 htp_connp_set_user_data(s->connp, (void *)s);
187949b9
VJ
70
71#ifdef DEBUG
72 SCMutexLock(&htp_state_mem_lock);
73 htp_state_memcnt++;
74 htp_state_memuse += sizeof(HtpState);
75 SCMutexUnlock(&htp_state_mem_lock);
76#endif
48248687
VJ
77 SCReturnPtr((void *)s, "void");
78
79error:
187949b9
VJ
80 if (s != NULL) {
81 if (s->connp != NULL)
82 htp_connp_destroy(s->connp);
83
25a3a5c6 84 SCFree(s);
187949b9 85 }
48248687
VJ
86
87 SCReturnPtr(NULL, "void");
07f7ba55
GS
88}
89
90/** \brief Function to frees the HTTP state memory and also frees the HTTP
91 * connection parser memory which was used by the HTP library
92 */
25a3a5c6 93void HTPStateFree(void *state)
07f7ba55 94{
48248687
VJ
95 SCEnter();
96
97 HtpState *s = (HtpState *)state;
98
07f7ba55 99 /* free the connection parser memory used by HTP library */
48248687
VJ
100 if (s != NULL) {
101 if (s->connp != NULL) {
102 htp_connp_destroy_all(s->connp);
103 }
0165b3f0
PR
104 /* free the list of body chunks */
105 if (s->body.nchunks > 0) {
106 HtpBodyFree(&s->body);
107 }
48248687 108 }
07f7ba55 109
25a3a5c6 110 SCFree(s);
48248687 111
07f7ba55 112#ifdef DEBUG
e26833be 113 SCMutexLock(&htp_state_mem_lock);
07f7ba55 114 htp_state_memcnt--;
187949b9 115 htp_state_memuse -= sizeof(HtpState);
e26833be 116 SCMutexUnlock(&htp_state_mem_lock);
07f7ba55 117#endif
48248687
VJ
118
119 SCReturn;
07f7ba55
GS
120}
121
97d49d8f
AS
122/**
123 * \brief Sets a flag that informs the HTP app layer that some module in the
124 * engine needs the http request body data.
125 */
126void AppLayerHtpEnableRequestBodyCallback(void)
127{
128 need_htp_request_body = 1;
129
130 return;
131}
132
133
fc2f7f29
GS
134/**
135 * \brief Function to convert the IP addresses in to the string
136 *
137 * \param f pointer to the flow which contains the IP addresses
138 * \param remote_addr pointer the string which will contain the remote address
139 * \param local_addr pointer the string which will contain the local address
140 */
141void HTPGetIPAddr(Flow *f, int family, char *remote_addr, char *local_addr)
142{
143 inet_ntop(family, (const void *)&f->src.addr_data32[0], remote_addr,
144 sizeof (remote_addr));
145 inet_ntop(family, (const void *)&f->dst.addr_data32[0], local_addr,
146 sizeof (local_addr));
147}
148
07f7ba55
GS
149/**
150 * \brief Function to handle the reassembled data from client and feed it to
151 * the HTP library to process it.
152 *
153 * \param htp_state Pointer the state in which the parsed value to be stored
154 * \param pstate Application layer parser state for this session
155 * \param input Pointer the received HTTP client data
156 * \param input_len Length in bytes of the received data
157 * \param output Pointer to the output (not used in this function)
158 *
2d6cf71d 159 * \retval On success returns 1 or on failure returns -1
07f7ba55 160 */
fc2f7f29
GS
161static int HTPHandleRequestData(Flow *f, void *htp_state,
162 AppLayerParserState *pstate,
07f7ba55 163 uint8_t *input, uint32_t input_len,
18fe3818 164 AppLayerParserResult *output)
07f7ba55 165{
1b39e602 166 SCEnter();
0a85fd67
GS
167 int r = -1;
168 int ret = 1;
169
07f7ba55 170 HtpState *hstate = (HtpState *)htp_state;
07f7ba55 171
8e444f17
GS
172 if (hstate->connp->in_status == STREAM_STATE_ERROR) {
173 SCLogError(SC_ERR_ALPARSER, "Inbound parser is in error state, no"
174 " need to feed data to libhtp");
175 SCReturnInt(-1);
176 }
177
0165b3f0
PR
178 /* Unset the body inspection (the callback should
179 * reactivate it if necessary) */
50f7d0a8 180 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
0165b3f0 181
fc2f7f29 182 /* Open the HTTP connection on receiving the first request */
ba7e8012 183 if (!(hstate->flags & HTP_FLAG_STATE_OPEN)) {
48248687 184 SCLogDebug("opening htp handle at %p", hstate->connp);
ba7e8012 185
fc2f7f29
GS
186 htp_connp_open(hstate->connp, NULL, f->sp, NULL, f->dp, 0);
187 hstate->flags |= HTP_FLAG_STATE_OPEN;
ba7e8012 188 } else {
48248687 189 SCLogDebug("using existing htp handle at %p", hstate->connp);
fc2f7f29 190 }
07f7ba55 191
0a85fd67 192 r = htp_connp_req_data(hstate->connp, 0, input, input_len);
148883ce 193
bf236e45
GS
194 switch(r) {
195 case STREAM_STATE_ERROR:
148883ce 196 if (hstate->connp->last_error != NULL) {
bf236e45
GS
197 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP client "
198 "request: [%"PRId32"] [%s] [%"PRId32"] %s",
199 hstate->connp->last_error->level,
200 hstate->connp->last_error->file,
201 hstate->connp->last_error->line,
202 hstate->connp->last_error->msg);
148883ce 203 } else {
bf236e45
GS
204 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP client "
205 "request");
148883ce 206 }
bf236e45
GS
207 hstate->flags |= HTP_FLAG_STATE_ERROR;
208 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 209 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
210 ret = -1;
211 break;
212 case STREAM_STATE_DATA:
213 hstate->flags |= HTP_FLAG_STATE_DATA;
214 break;
215 case STREAM_STATE_DATA_OTHER:
216 SCLogDebug("CONNECT not supported yet");
217 hstate->flags |= HTP_FLAG_STATE_ERROR;
218 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 219 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
220 ret = -1;
221 break;
222 default:
223 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 224 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45 225 }
07f7ba55 226
fc2f7f29
GS
227 /* if we the TCP connection is closed, then close the HTTP connection */
228 if ((pstate->flags & APP_LAYER_PARSER_EOF) &&
0a85fd67
GS
229 ! (hstate->flags & HTP_FLAG_STATE_CLOSED) &&
230 ! (hstate->flags & HTP_FLAG_STATE_DATA))
fc2f7f29
GS
231 {
232 htp_connp_close(hstate->connp, 0);
233 hstate->flags |= HTP_FLAG_STATE_CLOSED;
ba7e8012 234 SCLogDebug("stream eof encountered, closing htp handle");
fc2f7f29
GS
235 }
236
48248687 237 SCLogDebug("hstate->connp %p", hstate->connp);
0a85fd67 238 SCReturnInt(ret);
07f7ba55
GS
239}
240
241/**
242 * \brief Function to handle the reassembled data from server and feed it to
243 * the HTP library to process it.
244 *
245 * \param htp_state Pointer the state in which the parsed value to be stored
246 * \param pstate Application layer parser state for this session
247 * \param input Pointer the received HTTP server data
248 * \param input_len Length in bytes of the received data
249 * \param output Pointer to the output (not used in this function)
250 *
2d6cf71d 251 * \retval On success returns 1 or on failure returns -1
07f7ba55 252 */
fc2f7f29
GS
253static int HTPHandleResponseData(Flow *f, void *htp_state,
254 AppLayerParserState *pstate,
07f7ba55 255 uint8_t *input, uint32_t input_len,
18fe3818 256 AppLayerParserResult *output)
07f7ba55 257{
1b39e602 258 SCEnter();
0a85fd67
GS
259 int r = -1;
260 int ret = 1;
261
07f7ba55 262 HtpState *hstate = (HtpState *)htp_state;
07f7ba55 263
8e444f17
GS
264 if (hstate->connp->out_status == STREAM_STATE_ERROR) {
265 SCLogError(SC_ERR_ALPARSER, "Outbound parser is in error state, no"
266 " need to feed data to libhtp");
267 SCReturnInt(-1);
268 }
269
0165b3f0
PR
270 /* Unset the body inspection (the callback should
271 * reactivate it if necessary) */
50f7d0a8 272 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
0165b3f0 273
0a85fd67 274 r = htp_connp_res_data(hstate->connp, 0, input, input_len);
bf236e45
GS
275 switch(r) {
276 case STREAM_STATE_ERROR:
148883ce 277 if (hstate->connp->last_error != NULL) {
bf236e45
GS
278 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP server "
279 "response: [%"PRId32"] [%s] [%"PRId32"] %s",
280 hstate->connp->last_error->level,
281 hstate->connp->last_error->file,
282 hstate->connp->last_error->line,
283 hstate->connp->last_error->msg);
148883ce 284 } else {
bf236e45
GS
285 SCLogError(SC_ERR_ALPARSER, "Error in parsing HTTP server "
286 "response");
148883ce 287 }
bf236e45
GS
288 hstate->flags = HTP_FLAG_STATE_ERROR;
289 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 290 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
291 ret = -1;
292 break;
293 case STREAM_STATE_DATA:
294 hstate->flags |= HTP_FLAG_STATE_DATA;
295 break;
296 case STREAM_STATE_DATA_OTHER:
297 SCLogDebug("CONNECT not supported yet");
298 hstate->flags = HTP_FLAG_STATE_ERROR;
299 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 300 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45
GS
301 ret = -1;
302 break;
303 default:
304 hstate->flags &= ~HTP_FLAG_STATE_DATA;
50f7d0a8 305 hstate->flags &= ~HTP_FLAG_NEW_BODY_SET;
bf236e45 306 }
07f7ba55 307
fc2f7f29
GS
308 /* if we the TCP connection is closed, then close the HTTP connection */
309 if ((pstate->flags & APP_LAYER_PARSER_EOF) &&
0a85fd67
GS
310 ! (hstate->flags & HTP_FLAG_STATE_CLOSED) &&
311 ! (hstate->flags & HTP_FLAG_STATE_DATA))
fc2f7f29
GS
312 {
313 htp_connp_close(hstate->connp, 0);
314 hstate->flags |= HTP_FLAG_STATE_CLOSED;
315 }
316
48248687 317 SCLogDebug("hstate->connp %p", hstate->connp);
0a85fd67 318 SCReturnInt(ret);
07f7ba55
GS
319}
320
0165b3f0
PR
321/**
322 * \brief Append a chunk of body to the HtpBody struct
323 * \param body pointer to the HtpBody holding the list
324 * \param data pointer to the data of the chunk
325 * \param len length of the chunk pointed by data
326 * \retval none
327 */
328void HtpBodyAppendChunk(HtpBody *body, uint8_t *data, uint32_t len)
329{
330 SCEnter();
7a8cd61f
VJ
331
332 HtpBodyChunk *bd = NULL;
333
0165b3f0
PR
334 if (body->nchunks == 0) {
335 /* New chunk */
7a8cd61f 336 bd = (HtpBodyChunk *)SCMalloc(sizeof(HtpBodyChunk));
0165b3f0
PR
337 if (bd == NULL) {
338 SCLogError(SC_ERR_MEM_ALLOC, "Fatal error, error allocationg memory");
339 exit(EXIT_FAILURE);
340 }
7a8cd61f 341
0165b3f0
PR
342 bd->len = len;
343 bd->data = data;
344 body->first = body->last = bd;
345 body->nchunks++;
346 bd->next = NULL;
347 bd->id = body->nchunks;
348 } else {
349 /* New or old, we have to check it.. */
350 if (body->last->data == data) {
351 /* Weird, but sometimes htp lib calls the callback
352 * more than once for the same chunk, with more
353 * len, so updating the len */
354 body->last->len = len;
355 bd = body->last;
356 } else {
7a8cd61f
VJ
357 bd = (HtpBodyChunk *)SCMalloc(sizeof(HtpBodyChunk));
358 if (bd == NULL) {
359 SCLogError(SC_ERR_MEM_ALLOC, "Fatal error, error allocationg memory");
360 exit(EXIT_FAILURE);
361 }
362
0165b3f0
PR
363 bd->len = len;
364 bd->data = data;
365 body->last->next = bd;
366 body->last = bd;
367 body->nchunks++;
368 bd->next = NULL;
369 bd->id = body->nchunks;
370 }
371 }
372 SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n", body,
373 bd->id, bd->data, (uint32_t)bd->len);
374}
375
376/**
377 * \brief Print the information and chunks of a Body
378 * \param body pointer to the HtpBody holding the list
379 * \retval none
380 */
381void HtpBodyPrint(HtpBody *body)
382{
383 if (SCLogDebugEnabled()) {
384 SCEnter();
385
386 if (body->nchunks == 0)
387 return;
388
7a8cd61f 389 HtpBodyChunk *cur = NULL;
0165b3f0
PR
390 SCLogDebug("--- Start body chunks at %p ---", body);
391 for (cur = body->first; cur != NULL; cur = cur->next) {
392 SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n",
393 body, cur->id, cur->data, (uint32_t)cur->len);
394 PrintRawDataFp(stdout, (uint8_t*)cur->data, cur->len);
395 }
396 SCLogDebug("--- End body chunks at %p ---", body);
397 }
398}
399
400/**
401 * \brief Free the information holded of the body request
402 * \param body pointer to the HtpBody holding the list
403 * \retval none
404 */
405void HtpBodyFree(HtpBody *body)
406{
407 SCEnter();
7a8cd61f 408
0165b3f0
PR
409 if (body->nchunks == 0)
410 return;
411
412 SCLogDebug("Removing chunks of Body %p; Last Chunk id: %"PRIu32", data %p,"
413 " len %"PRIu32"\n", body, body->last->id, body->last->data,
414 (uint32_t)body->last->len);
415 body->nchunks = 0;
416
7a8cd61f
VJ
417 HtpBodyChunk *cur = NULL;
418 HtpBodyChunk *prev = NULL;
419
0165b3f0
PR
420 prev = body->first;
421 while (prev != NULL) {
422 cur = prev->next;
25a3a5c6 423 SCFree(prev);
0165b3f0
PR
424 prev = cur;
425 }
426 body->first = body->last = NULL;
427 body->pcre_flags = HTP_PCRE_NONE;
428 body->operation = HTP_BODY_NONE;
429}
430
431/**
432 * \brief Function callback to append chunks for Resquests
433 * \param d pointer to the htp_tx_data_t structure (a chunk from htp lib)
434 * \retval int HOOK_OK if all goes well
435 */
436int HTPCallbackRequestBodyData(htp_tx_data_t *d)
437{
438 SCEnter();
439 HtpState *hstate = (HtpState *)d->tx->connp->user_data;
440 SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
441 "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
442
443 //PrintRawDataFp(stdout, d->data, d->len);
444
445 /* If it has been inspected by pcre and there's no match,
446 * remove this chunks */
447 if ( !(hstate->body.pcre_flags & HTP_PCRE_HAS_MATCH) &&
448 (hstate->body.pcre_flags & HTP_PCRE_DONE))
449 {
450 HtpBodyFree(&hstate->body);
451 }
452
453 /* If its a new operation, remove the old data */
454 if (hstate->body.operation == HTP_BODY_RESPONSE) {
455 HtpBodyFree(&hstate->body);
456 hstate->body.pcre_flags = HTP_PCRE_NONE;
457 }
458 hstate->body.operation = HTP_BODY_REQUEST;
459
460
461 HtpBodyAppendChunk(&hstate->body, (uint8_t*)d->data, (uint32_t)d->len);
462 hstate->body.pcre_flags = HTP_PCRE_NONE;
463 if (SCLogDebugEnabled()) {
464 HtpBodyPrint(&hstate->body);
465 }
466
467 /* set the new chunk flag */
50f7d0a8 468 hstate->flags |= HTP_FLAG_NEW_BODY_SET;
0165b3f0
PR
469
470 SCReturnInt(HOOK_OK);
471}
472
fc2f7f29
GS
473/**
474 * \brief Print the stats of the HTTP requests
475 */
476void HTPAtExitPrintStats(void)
477{
478#ifdef DEBUG
479 SCMutexLock(&htp_state_mem_lock);
480 SCLogDebug("http_state_memcnt %"PRIu64", http_state_memuse %"PRIu64"",
481 htp_state_memcnt, htp_state_memuse);
482 SCMutexUnlock(&htp_state_mem_lock);
483#endif
484}
485
486/** \brief Clears the HTTP server configuration memory used by HTP library */
487void HTPFreeConfig(void)
488{
489 htp_config_destroy(cfg);
490}
491
356a8bf3
GS
492/**
493 * \brief callback for request to store the recent incoming request
494 in to the recent_in_tx for the given htp state
495 * \param connp pointer to the current connection parser which has the htp
496 * state in it as user data
497 */
498static int HTPCallbackRequest(htp_connp_t *connp) {
499 SCEnter();
187949b9 500
356a8bf3 501 HtpState *hstate = (HtpState *)connp->user_data;
187949b9 502 if (hstate == NULL) {
50f7d0a8 503 SCReturnInt(HOOK_ERROR);
187949b9 504 }
50f7d0a8
GS
505 if (! (hstate->flags & HTP_FLAG_NEW_REQUEST)) {
506 hstate->flags |= HTP_FLAG_NEW_REQUEST;
507 hstate->new_in_tx_index = list_size(hstate->connp->conn->transactions) - 1;
508 }
509 SCReturnInt(HOOK_OK);
356a8bf3
GS
510}
511
512/**
513 * \brief callback for response to remove the recent received requests
514 from the recent_in_tx for the given htp state
515 * \param connp pointer to the current connection parser which has the htp
516 * state in it as user data
517 */
518static int HTPCallbackResponse(htp_connp_t *connp) {
519 SCEnter();
187949b9 520
ffd85ac4
VJ
521 size_t idx;
522
356a8bf3 523 HtpState *hstate = (HtpState *)connp->user_data;
187949b9 524 if (hstate == NULL) {
50f7d0a8 525 SCReturnInt(HOOK_ERROR);
187949b9 526 }
356a8bf3 527
0165b3f0
PR
528 /* Free data when we have a response */
529 if (hstate->body.nchunks > 0)
530 HtpBodyFree(&hstate->body);
ffd85ac4 531
0165b3f0
PR
532 hstate->body.operation = HTP_BODY_RESPONSE;
533 hstate->body.pcre_flags = HTP_PCRE_NONE;
534
bf236e45
GS
535 /* Clear the trasactions which are processed by the engine from libhtp.
536 This helps in reducing the meory consumptions of libhtp */
ffd85ac4
VJ
537 for (idx = 0; idx < hstate->new_in_tx_index; idx++) {
538 htp_tx_t *tx = list_get(hstate->connp->conn->transactions, idx);
bf236e45
GS
539 if (tx != NULL)
540 htp_tx_destroy(tx);
541 }
50f7d0a8
GS
542
543 SCReturnInt(HOOK_OK);
356a8bf3
GS
544}
545
07f7ba55
GS
546/**
547 * \brief Register the HTTP protocol and state handling functions to APP layer
548 * of the engine.
549 */
550void RegisterHTPParsers(void)
551{
07f7ba55
GS
552 AppLayerRegisterStateFuncs(ALPROTO_HTTP, HTPStateAlloc, HTPStateFree);
553
554 AppLayerRegisterProto("http", ALPROTO_HTTP, STREAM_TOSERVER,
555 HTPHandleRequestData);
556 AppLayerRegisterProto("http", ALPROTO_HTTP, STREAM_TOCLIENT,
557 HTPHandleResponseData);
558
559 cfg = htp_config_create();
356a8bf3
GS
560 /* Register the callback for request to store the recent incoming request
561 in to the recent_in_tx for the given htp state */
562 htp_config_register_request(cfg, HTPCallbackRequest);
563 /* Register the callback for response to remove the recently received request
564 from the recent_in_tx for the given htp state */
565 htp_config_register_response(cfg, HTPCallbackResponse);
566 /* set the normalized request parsing to be used in uricontent matching */
567 htp_config_set_generate_request_uri_normalized(cfg, 1);
07f7ba55
GS
568}
569
0165b3f0 570/**
97d49d8f
AS
571 * \brief This function is called at the end of SigLoadSignatures. This function
572 * enables the htp layer to register a callback for the http request body.
573 * need_htp_request_body is a flag that informs the htp app layer that
574 * a module in the engine needs the http request body.
0165b3f0
PR
575 */
576void AppLayerHtpRegisterExtraCallbacks(void) {
577 SCLogDebug("Registering extra htp callbacks");
97d49d8f 578 if (need_htp_request_body == 1) {
0165b3f0
PR
579 SCLogDebug("Registering callback htp_config_register_request_body_data on htp");
580 htp_config_register_request_body_data(cfg, HTPCallbackRequestBodyData);
581 } else {
582 SCLogDebug("No htp extra callback needed");
583 }
584}
585
c22d4269 586
c352bff6 587#ifdef UNITTESTS
07f7ba55
GS
588/** \test Test case where chunks are sent in smaller chunks and check the
589 * response of the parser from HTP library. */
590int HTPParserTest01(void) {
591 int result = 1;
592 Flow f;
fc2f7f29 593 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost"
07f7ba55
GS
594 " Data is c0oL!";
595 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
596 TcpSession ssn;
25a3a5c6
PR
597
598 HtpState *htp_state = NULL;
07f7ba55
GS
599 int r = 0;
600 memset(&f, 0, sizeof(f));
601 memset(&ssn, 0, sizeof(ssn));
07f7ba55
GS
602 f.protoctx = (void *)&ssn;
603
6a53ab9c
VJ
604 StreamTcpInitConfig(TRUE);
605 StreamL7DataPtrInit(&ssn);
606
07f7ba55
GS
607 uint32_t u;
608 for (u = 0; u < httplen1; u++) {
609 uint8_t flags = 0;
610
611 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
612 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
613 else flags = STREAM_TOSERVER;
614
c352bff6 615 r = AppLayerParse(&f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
07f7ba55
GS
616 if (r != 0) {
617 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
618 " 0: ", u, r);
619 result = 0;
620 goto end;
621 }
622 }
623
25a3a5c6 624 htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
07f7ba55
GS
625 if (htp_state == NULL) {
626 printf("no http state: ");
627 result = 0;
628 goto end;
629 }
630
631 htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0);
632
633 bstr *key = NULL;
634 htp_header_t *h = NULL;
635 table_iterator_reset(tx->request_headers);
636 key = table_iterator_next(tx->request_headers, (void **) & h);
637
2d6cf71d
GS
638 if (htp_state->connp == NULL || strcmp(bstr_tocstr(h->value), "Victor/1.0")
639 || tx->request_method_number != M_POST ||
fc2f7f29 640 tx->request_protocol_number != HTTP_1_0)
07f7ba55 641 {
2d6cf71d 642 printf("expected header value: Victor/1.0 and got %s: and expected"
fc2f7f29 643 " method: POST and got %s, expected protocol number HTTP/1.0"
2d6cf71d
GS
644 " and got: %s \n", bstr_tocstr(h->value),
645 bstr_tocstr(tx->request_method),
646 bstr_tocstr(tx->request_protocol));
07f7ba55
GS
647 result = 0;
648 goto end;
649 }
650
651end:
6a53ab9c
VJ
652 StreamL7DataPtrFree(&ssn);
653 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
654 if (htp_state != NULL)
655 HTPStateFree(htp_state);
07f7ba55
GS
656 return result;
657}
658
2d6cf71d
GS
659/** \test See how it deals with an incomplete request. */
660int HTPParserTest02(void) {
661 int result = 1;
662 Flow f;
663 uint8_t httpbuf1[] = "POST";
664 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
665 TcpSession ssn;
25a3a5c6 666 HtpState *http_state = NULL;
2d6cf71d
GS
667
668 memset(&f, 0, sizeof(f));
669 memset(&ssn, 0, sizeof(ssn));
2d6cf71d
GS
670 f.protoctx = (void *)&ssn;
671
6a53ab9c
VJ
672 StreamTcpInitConfig(TRUE);
673 StreamL7DataPtrInit(&ssn);
674
2d6cf71d 675 int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
c352bff6 676 STREAM_EOF, httpbuf1, httplen1);
2d6cf71d
GS
677 if (r != 0) {
678 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
679 result = 0;
680 goto end;
681 }
682
25a3a5c6 683 http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
684 if (http_state == NULL) {
685 printf("no http state: ");
686 result = 0;
687 goto end;
688 }
689
690 htp_tx_t *tx = list_get(http_state->connp->conn->transactions, 0);
691
692 bstr *key = NULL;
693 htp_header_t *h = NULL;
694 table_iterator_reset(tx->request_headers);
695 key = table_iterator_next(tx->request_headers, (void **) & h);
696
697 if ((tx->request_method) != NULL || h != NULL)
698 {
699 printf("expected method NULL, got %s \n", bstr_tocstr(tx->request_method));
700 result = 0;
701 goto end;
702 }
703
704end:
6a53ab9c
VJ
705 StreamL7DataPtrFree(&ssn);
706 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
707 if (http_state != NULL)
708 HTPStateFree(http_state);
2d6cf71d
GS
709 return result;
710}
711
712/** \test Test case where method is invalid and data is sent in smaller chunks
713 * and check the response of the parser from HTP library. */
714int HTPParserTest03(void) {
715 int result = 1;
716 Flow f;
717 uint8_t httpbuf1[] = "HELLO / HTTP/1.0\r\n";
718 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
719 TcpSession ssn;
25a3a5c6
PR
720
721 HtpState *htp_state = NULL;
2d6cf71d
GS
722 int r = 0;
723 memset(&f, 0, sizeof(f));
724 memset(&ssn, 0, sizeof(ssn));
2d6cf71d
GS
725 f.protoctx = (void *)&ssn;
726
6a53ab9c
VJ
727 StreamTcpInitConfig(TRUE);
728 StreamL7DataPtrInit(&ssn);
729
2d6cf71d
GS
730 uint32_t u;
731 for (u = 0; u < httplen1; u++) {
732 uint8_t flags = 0;
733
734 if (u == 0) flags = STREAM_TOSERVER|STREAM_START;
735 else if (u == (httplen1 - 1)) flags = STREAM_TOSERVER|STREAM_EOF;
736 else flags = STREAM_TOSERVER;
737
c352bff6 738 r = AppLayerParse(&f, ALPROTO_HTTP, flags, &httpbuf1[u], 1);
2d6cf71d
GS
739 if (r != 0) {
740 printf("toserver chunk %" PRIu32 " returned %" PRId32 ", expected"
741 " 0: ", u, r);
742 result = 0;
743 goto end;
744 }
745 }
25a3a5c6 746 htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
747 if (htp_state == NULL) {
748 printf("no http state: ");
749 result = 0;
750 goto end;
751 }
752
753 htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0);
754
755 bstr *key = NULL;
756 htp_header_t *h = NULL;
757 table_iterator_reset(tx->request_headers);
758 key = table_iterator_next(tx->request_headers, (void **) & h);
759
760 if (htp_state->connp == NULL || tx->request_method_number != M_UNKNOWN ||
761 h != NULL || tx->request_protocol_number != HTTP_1_0)
762 {
763 printf("expected method M_UNKNOWN and got %s: , expected protocol "
764 "HTTP/1.0 and got %s \n", bstr_tocstr(tx->request_method),
765 bstr_tocstr(tx->request_protocol));
766 result = 0;
767 goto end;
768 }
769
770end:
6a53ab9c
VJ
771 StreamL7DataPtrFree(&ssn);
772 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
773 if (htp_state != NULL)
774 HTPStateFree(htp_state);
2d6cf71d
GS
775 return result;
776}
777
778/** \test Test case where invalid data is sent and check the response of the
779 * parser from HTP library. */
780int HTPParserTest04(void) {
781 int result = 1;
782 Flow f;
25a3a5c6 783 HtpState *htp_state = NULL;
2d6cf71d
GS
784 uint8_t httpbuf1[] = "World!\r\n";
785 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
786 TcpSession ssn;
787 int r = 0;
788 memset(&f, 0, sizeof(f));
789 memset(&ssn, 0, sizeof(ssn));
2d6cf71d
GS
790 f.protoctx = (void *)&ssn;
791
6a53ab9c
VJ
792 StreamTcpInitConfig(TRUE);
793 StreamL7DataPtrInit(&ssn);
794
2d6cf71d 795 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|
c352bff6 796 STREAM_EOF, httpbuf1, httplen1);
2d6cf71d 797
25a3a5c6 798 htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
799 if (htp_state == NULL) {
800 printf("no http state: ");
801 result = 0;
802 goto end;
803 }
804
805 htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0);
806
807 bstr *key = NULL;
808 htp_header_t *h = NULL;
809 table_iterator_reset(tx->request_headers);
810 key = table_iterator_next(tx->request_headers, (void **) & h);
811
812 if (htp_state->connp == NULL || tx->request_method_number != M_UNKNOWN ||
813 h != NULL || tx->request_protocol_number != PROTOCOL_UNKNOWN)
814 {
815 printf("expected method M_UNKNOWN and got %s: , expected protocol "
816 "NULL and got %s \n", bstr_tocstr(tx->request_method),
817 bstr_tocstr(tx->request_protocol));
818 result = 0;
819 goto end;
820 }
821
822end:
6a53ab9c
VJ
823 StreamL7DataPtrFree(&ssn);
824 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
825 if (htp_state != NULL)
826 HTPStateFree(htp_state);
2d6cf71d
GS
827 return result;
828}
829
830/** \test Test both sides of a http stream mixed up to see if the HTP parser
831 * properly parsed them and also keeps them separated. */
832int HTPParserTest05(void) {
833 int result = 1;
834 Flow f;
25a3a5c6 835 HtpState *http_state = NULL;
fc2f7f29 836 uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\n";
2d6cf71d
GS
837 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
838 uint8_t httpbuf2[] = "Post D";
839 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
840 uint8_t httpbuf3[] = "ata is c0oL!";
841 uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
842
fc2f7f29 843 uint8_t httpbuf4[] = "HTTP/1.0 200 OK\r\nServer: VictorServer/1.0\r\n\r\n";
2d6cf71d
GS
844 uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
845 uint8_t httpbuf5[] = "post R";
846 uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */
847 uint8_t httpbuf6[] = "esults are tha bomb!";
848 uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */
849 TcpSession ssn;
850
851 memset(&f, 0, sizeof(f));
852 memset(&ssn, 0, sizeof(ssn));
2d6cf71d
GS
853 f.protoctx = (void *)&ssn;
854
6a53ab9c
VJ
855 StreamTcpInitConfig(TRUE);
856 StreamL7DataPtrInit(&ssn);
857
2d6cf71d 858 int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START,
c352bff6 859 httpbuf1, httplen1);
2d6cf71d
GS
860 if (r != 0) {
861 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
862 result = 0;
863 goto end;
864 }
865
866 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, httpbuf4,
c352bff6 867 httplen4);
2d6cf71d
GS
868 if (r != 0) {
869 printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
870 result = 0;
871 goto end;
872 }
873
c352bff6 874 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT, httpbuf5, httplen5);
2d6cf71d
GS
875 if (r != 0) {
876 printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
877 result = 0;
878 goto end;
879 }
880
c352bff6 881 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
2d6cf71d
GS
882 if (r != 0) {
883 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
884 result = 0;
885 goto end;
886 }
887
888 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf3,
c352bff6 889 httplen3);
2d6cf71d
GS
890 if (r != 0) {
891 printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
892 result = 0;
893 goto end;
894 }
895
896 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_EOF, httpbuf6,
c352bff6 897 httplen6);
2d6cf71d
GS
898 if (r != 0) {
899 printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
900 result = 0;
901 goto end;
902 }
903
25a3a5c6 904 http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
2d6cf71d
GS
905 if (http_state == NULL) {
906 printf("no http state: ");
907 result = 0;
908 goto end;
909 }
910
911 htp_tx_t *tx = list_get(http_state->connp->conn->transactions, 0);
912
913 bstr *key = NULL;
914 htp_header_t *h = NULL;
915 table_iterator_reset(tx->request_headers);
916 key = table_iterator_next(tx->request_headers, (void **) & h);
917
918 if (http_state->connp == NULL || tx->request_method_number != M_POST ||
fc2f7f29 919 h == NULL || tx->request_protocol_number != HTTP_1_0)
2d6cf71d
GS
920 {
921 printf("expected method M_POST and got %s: , expected protocol "
fc2f7f29 922 "HTTP/1.0 and got %s \n", bstr_tocstr(tx->request_method),
2d6cf71d
GS
923 bstr_tocstr(tx->request_protocol));
924 result = 0;
925 goto end;
926 }
927
928 if (tx->response_status_number != 200 ||
fc2f7f29 929 h == NULL || tx->request_protocol_number != HTTP_1_0)
2d6cf71d
GS
930 {
931 printf("expected response 200 OK and got %"PRId32" %s: , expected protocol "
fc2f7f29 932 "HTTP/1.0 and got %s \n", tx->response_status_number,
2d6cf71d
GS
933 bstr_tocstr(tx->response_message),
934 bstr_tocstr(tx->response_protocol));
935 result = 0;
936 goto end;
937 }
938end:
6a53ab9c
VJ
939 StreamL7DataPtrFree(&ssn);
940 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
941 if (http_state != NULL)
942 HTPStateFree(http_state);
2d6cf71d
GS
943 return result;
944}
945
fc2f7f29
GS
946/** \test Test proper chunked encoded response body
947 */
948int HTPParserTest06(void) {
949 int result = 1;
950 Flow f;
951 uint8_t httpbuf1[] = "GET /ld/index.php?id=412784631&cid=0064&version=4&"
952 "name=try HTTP/1.1\r\nAccept: */*\r\nUser-Agent: "
953 "LD-agent\r\nHost: 209.205.196.16\r\n\r\n";
954 uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
955 uint8_t httpbuf2[] = "HTTP/1.1 200 OK\r\nDate: Sat, 03 Oct 2009 10:16:02 "
956 "GMT\r\n"
957 "Server: Apache/1.3.37 (Unix) mod_ssl/2.8.28 "
958 "OpenSSL/0.9.7a PHP/4.4.7 mod_perl/1.29 "
959 "FrontPage/5.0.2.2510\r\n"
960 "X-Powered-By: PHP/4.4.7\r\nTransfer-Encoding: "
961 "chunked\r\n"
962 "Content-Type: text/html\r\n\r\n"
963 "1408\r\n"
964 "W2dyb3VwMV0NCnBob25lMT1wMDB3ODgyMTMxMzAyMTINCmxvZ2lu"
965 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj1wMDB3ODgyMTMxMzAyMTIN"
966 "CmxvZ2luMj0NCnBhc3N3b3JkMj0NCnBob25lMz0NCmxvZ2luMz0N"
967 "CnBhc3N3b3JkMz0NCnBob25lND0NCmxvZ2luND0NCnBhc3N3b3Jk"
968 "ND0NCnBob25lNT0NCmxvZ2luNT0NCnBhc3N3b3JkNT0NCnBob25l"
969 "Nj0NCmxvZ2luNj0NCnBhc3N3b3JkNj0NCmNhbGxfdGltZTE9MzIN"
970 "CmNhbGxfdGltZTI9MjMyDQpkYXlfbGltaXQ9NQ0KbW9udGhfbGlt"
971 "aXQ9MTUNCltncm91cDJdDQpwaG9uZTE9DQpsb2dpbjE9DQpwYXNz"
972 "d29yZDE9DQpwaG9uZTI9DQpsb2dpbjI9DQpwYXNzd29yZDI9DQpw"
973 "aG9uZTM9DQpsb2dpbjM9DQpwYXNzd29yZDM9DQpwaG9uZTQ9DQps"
974 "b2dpbjQ9DQpwYXNzd29yZDQ9DQpwaG9uZTU9DQpsb2dpbjU9DQpw"
975 "YXNzd29yZDU9DQpwaG9uZTY9DQpsb2dpbjY9DQpwYXNzd29yZDY9"
976 "DQpjYWxsX3RpbWUxPQ0KY2FsbF90aW1lMj0NCmRheV9saW1pdD0N"
977 "Cm1vbnRoX2xpbWl0PQ0KW2dyb3VwM10NCnBob25lMT0NCmxvZ2lu"
978 "MT0NCnBhc3N3b3JkMT0NCnBob25lMj0NCmxvZ2luMj0NCnBhc3N3"
979 "b3JkMj0NCnBob25lMz0NCmxvZ2luMz0NCnBhc3N3b3JkMz0NCnBo"
980 "b25lND0NCmxvZ2luND0NCnBhc3N3b3JkND0NCnBob25lNT0NCmxv"
981 "Z2luNT0NCnBhc3N3b3JkNT0NCnBob25lNj0NCmxvZ2luNj0NCnBh"
982 "c3N3b3JkNj0NCmNhbGxfdGltZTE9DQpjYWxsX3RpbWUyPQ0KZGF5"
983 "X2xpbWl0PQ0KbW9udGhfbGltaXQ9DQpbZ3JvdXA0XQ0KcGhvbmUx"
984 "PQ0KbG9naW4xPQ0KcGFzc3dvcmQxPQ0KcGhvbmUyPQ0KbG9naW4y"
985 "PQ0KcGFzc3dvcmQyPQ0KcGhvbmUzPQ0KbG9naW4zPQ0KcGFzc3dv"
986 "cmQzPQ0KcGhvbmU0PQ0KbG9naW40PQ0KcGFzc3dvcmQ0PQ0KcGhv"
987 "bmU1PQ0KbG9naW41PQ0KcGFzc3dvcmQ1PQ0KcGhvbmU2PQ0KbG9n"
988 "aW42PQ0KcGFzc3dvcmQ2PQ0KY2FsbF90aW1lMT0NCmNhbGxfdGlt"
989 "ZTI9DQpkYXlfbGltaXQ9DQptb250aF9saW1pdD0NCltmaWxlc10N"
990 "Cmxpbms9aHR0cDovLzIwOS4yMDUuMTk2LjE2L2xkL2dldGJvdC5w"
991 "aHA=0\r\n\r\n";
992 uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
993 TcpSession ssn;
25a3a5c6 994 HtpState *http_state = NULL;
fc2f7f29
GS
995
996 memset(&f, 0, sizeof(f));
997 memset(&ssn, 0, sizeof(ssn));
fc2f7f29
GS
998 f.protoctx = (void *)&ssn;
999
6a53ab9c
VJ
1000 StreamTcpInitConfig(TRUE);
1001 StreamL7DataPtrInit(&ssn);
1002
fc2f7f29 1003 int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START,
c352bff6 1004 httpbuf1, httplen1);
fc2f7f29
GS
1005 if (r != 0) {
1006 printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
1007 result = 0;
1008 goto end;
1009 }
1010
1011 r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOCLIENT|STREAM_START, httpbuf2,
c352bff6 1012 httplen2);
fc2f7f29
GS
1013 if (r != 0) {
1014 printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
1015 result = 0;
1016 goto end;
1017 }
1018
25a3a5c6 1019 http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)];
fc2f7f29
GS
1020 if (http_state == NULL) {
1021 printf("no http state: ");
1022 result = 0;
1023 goto end;
1024 }
1025
1026 htp_tx_t *tx = list_get(http_state->connp->conn->transactions, 0);
1027
1028 bstr *key = NULL;
1029 htp_header_t *h = NULL;
1030 table_iterator_reset(tx->request_headers);
1031 key = table_iterator_next(tx->request_headers, (void **) & h);
1032
1033 if (http_state->connp == NULL || tx->request_method_number != M_GET ||
1034 h == NULL || tx->request_protocol_number != HTTP_1_1)
1035 {
1036 printf("expected method M_GET and got %s: , expected protocol "
1037 "HTTP/1.1 and got %s \n", bstr_tocstr(tx->request_method),
1038 bstr_tocstr(tx->request_protocol));
1039 result = 0;
1040 goto end;
1041 }
1042
1043 if (tx->response_status_number != 200 ||
1044 h == NULL || tx->request_protocol_number != HTTP_1_1)
1045 {
1046 printf("expected response 200 OK and got %"PRId32" %s: , expected proto"
1047 "col HTTP/1.1 and got %s \n", tx->response_status_number,
1048 bstr_tocstr(tx->response_message),
1049 bstr_tocstr(tx->response_protocol));
1050 result = 0;
1051 goto end;
1052 }
1053end:
6a53ab9c
VJ
1054 StreamL7DataPtrFree(&ssn);
1055 StreamTcpFreeConfig(TRUE);
25a3a5c6
PR
1056 if (http_state != NULL)
1057 HTPStateFree(http_state);
fc2f7f29
GS
1058 return result;
1059}
c352bff6
VJ
1060#endif /* UNITTESTS */
1061
07f7ba55
GS
1062/**
1063 * \brief Register the Unit tests for the HTTP protocol
1064 */
1065void HTPParserRegisterTests(void) {
1066#ifdef UNITTESTS
1067 UtRegisterTest("HTPParserTest01", HTPParserTest01, 1);
2d6cf71d
GS
1068 UtRegisterTest("HTPParserTest02", HTPParserTest02, 1);
1069 UtRegisterTest("HTPParserTest03", HTPParserTest03, 1);
1070 UtRegisterTest("HTPParserTest04", HTPParserTest04, 1);
1071 UtRegisterTest("HTPParserTest05", HTPParserTest05, 1);
fc2f7f29 1072 UtRegisterTest("HTPParserTest06", HTPParserTest06, 1);
07f7ba55
GS
1073#endif /* UNITTESTS */
1074}
1075