]>
Commit | Line | Data |
---|---|---|
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 |
64 | typedef 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 | 73 | static SCRadixTree *cfgtree; |
ead13bda | 74 | /** List of HTP configurations. */ |
a9cdd2bb BR |
75 | static HTPCfgRec cfglist; |
76 | ||
07f7ba55 | 77 | #ifdef DEBUG |
e26833be | 78 | static SCMutex htp_state_mem_lock = PTHREAD_MUTEX_INITIALIZER; |
07f7ba55 GS |
79 | static uint64_t htp_state_memuse = 0; |
80 | static uint64_t htp_state_memcnt = 0; | |
81 | #endif | |
97d49d8f AS |
82 | |
83 | static 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 | */ | |
93 | static 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 | */ | |
123 | static 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 | */ | |
147 | static 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 | ||
167 | error: | |
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 | 178 | void 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 | */ | |
225 | void 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 | */ | |
251 | void 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 | */ |
269 | void 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 | */ | |
284 | void 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 |
306 | static 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 | |
454 | error: | |
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 |
470 | static 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 | 550 | int 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 | |
602 | error: | |
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 | */ | |
617 | void 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 | */ | |
641 | void 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 | */ | |
673 | int 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 | */ | |
742 | void 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 */ | |
755 | void 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 | */ | |
778 | static 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 | */ | |
799 | static 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 |
831 | static 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 |
1094 | void 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 | */ | |
1106 | void 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 | */ |
1126 | void 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 |
1141 | static HTPCfgRec cfglist_backup; |
1142 | ||
1143 | static 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 | ||
1152 | static 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. */ | |
1163 | int 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 | ||
1226 | end: | |
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. */ |
1235 | int 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 | ||
1281 | end: | |
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. */ | |
1291 | int 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 | ||
1349 | end: | |
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. */ | |
1359 | int 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 | ||
1403 | end: | |
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. */ | |
1413 | int 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 | } | |
1519 | end: | |
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 | */ | |
1529 | int 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 | } | |
1637 | end: | |
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 */ | |
1648 | int HTPParserConfigTest01(void) | |
1649 | { | |
1650 | int ret = 0; | |
1651 | char input[] = "\ | |
1652 | %YAML 1.1\n\ | |
1653 | ---\n\ | |
1654 | libhtp:\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 | ||
1819 | end: | |
1820 | ConfDeInit(); | |
1821 | ConfRestoreContextBackup(); | |
1822 | ||
1823 | return ret; | |
1824 | } | |
1825 | ||
1826 | /** \test Test config builds radix correctly */ | |
1827 | int HTPParserConfigTest02(void) | |
1828 | { | |
1829 | int ret = 0; | |
1830 | char input[] = "\ | |
1831 | %YAML 1.1\n\ | |
1832 | ---\n\ | |
1833 | libhtp:\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 | ||
1916 | end: | |
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 */ | |
1926 | int 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\ | |
1941 | libhtp:\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 | ||
2025 | end: | |
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 | */ | |
2044 | void 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 |