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