/src/suricata7/libhtp/htp/htp_request.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*************************************************************************** |
2 | | * Copyright (c) 2009-2010 Open Information Security Foundation |
3 | | * Copyright (c) 2010-2013 Qualys, Inc. |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions are |
8 | | * met: |
9 | | * |
10 | | * - Redistributions of source code must retain the above copyright |
11 | | * notice, this list of conditions and the following disclaimer. |
12 | | |
13 | | * - Redistributions in binary form must reproduce the above copyright |
14 | | * notice, this list of conditions and the following disclaimer in the |
15 | | * documentation and/or other materials provided with the distribution. |
16 | | |
17 | | * - Neither the name of the Qualys, Inc. nor the names of its |
18 | | * contributors may be used to endorse or promote products derived from |
19 | | * this software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31 | | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | | ***************************************************************************/ |
33 | | |
34 | | /** |
35 | | * @file |
36 | | * @author Ivan Ristic <ivanr@webkreator.com> |
37 | | */ |
38 | | |
39 | | #include "htp_config_auto.h" |
40 | | |
41 | | #include "htp_private.h" |
42 | | |
43 | 234k | #define IN_TEST_NEXT_BYTE_OR_RETURN(X) \ |
44 | 234k | if ((X)->in_current_read_offset >= (X)->in_current_len) { \ |
45 | 40.8k | return HTP_DATA; \ |
46 | 40.8k | } |
47 | | |
48 | 50.9M | #define IN_PEEK_NEXT(X) \ |
49 | 50.9M | if ((X)->in_current_read_offset >= (X)->in_current_len) { \ |
50 | 157k | (X)->in_next_byte = -1; \ |
51 | 50.7M | } else { \ |
52 | 50.7M | (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ |
53 | 50.7M | } |
54 | | |
55 | | #define IN_NEXT_BYTE(X) \ |
56 | | if ((X)->in_current_read_offset < (X)->in_current_len) { \ |
57 | | (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ |
58 | | (X)->in_current_read_offset++; \ |
59 | | (X)->in_current_consume_offset++; \ |
60 | | (X)->in_stream_offset++; \ |
61 | | } else { \ |
62 | | (X)->in_next_byte = -1; \ |
63 | | } |
64 | | |
65 | 43.5k | #define IN_NEXT_BYTE_OR_RETURN(X) \ |
66 | 43.5k | if ((X)->in_current_read_offset < (X)->in_current_len) { \ |
67 | 42.5k | (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ |
68 | 42.5k | (X)->in_current_read_offset++; \ |
69 | 42.5k | (X)->in_current_consume_offset++; \ |
70 | 42.5k | (X)->in_stream_offset++; \ |
71 | 42.5k | } else { \ |
72 | 1.02k | return HTP_DATA; \ |
73 | 1.02k | } |
74 | | |
75 | 84.3M | #define IN_COPY_BYTE_OR_RETURN(X) \ |
76 | 84.3M | if ((X)->in_current_read_offset < (X)->in_current_len) { \ |
77 | 84.1M | (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ |
78 | 84.1M | (X)->in_current_read_offset++; \ |
79 | 84.1M | (X)->in_stream_offset++; \ |
80 | 84.1M | } else { \ |
81 | 191k | return HTP_DATA_BUFFER; \ |
82 | 191k | } |
83 | | |
84 | | /** |
85 | | * Sends outstanding connection data to the currently active data receiver hook. |
86 | | * |
87 | | * @param[in] connp |
88 | | * @param[in] is_last |
89 | | * @return HTP_OK, or a value returned from a callback. |
90 | | */ |
91 | 434k | static htp_status_t htp_connp_req_receiver_send_data(htp_connp_t *connp, int is_last) { |
92 | 434k | if (connp->in_data_receiver_hook == NULL) return HTP_OK; |
93 | | |
94 | 274k | htp_tx_data_t d; |
95 | 274k | d.tx = connp->in_tx; |
96 | 274k | d.data = connp->in_current_data + connp->in_current_receiver_offset; |
97 | 274k | d.len = connp->in_current_read_offset - connp->in_current_receiver_offset; |
98 | 274k | d.is_last = is_last; |
99 | | |
100 | 274k | htp_status_t rc = htp_hook_run_all(connp->in_data_receiver_hook, &d); |
101 | 274k | if (rc != HTP_OK) return rc; |
102 | | |
103 | 274k | connp->in_current_receiver_offset = connp->in_current_read_offset; |
104 | | |
105 | 274k | return HTP_OK; |
106 | 274k | } |
107 | | |
108 | | /** |
109 | | * Configures the data receiver hook. If there is a previous hook, it will be finalized and cleared. |
110 | | * |
111 | | * @param[in] connp |
112 | | * @param[in] data_receiver_hook |
113 | | * @return HTP_OK, or a value returned from a callback. |
114 | | */ |
115 | 185k | static htp_status_t htp_connp_req_receiver_set(htp_connp_t *connp, htp_hook_t *data_receiver_hook) { |
116 | 185k | htp_status_t rc = htp_connp_req_receiver_finalize_clear(connp); |
117 | | |
118 | 185k | connp->in_data_receiver_hook = data_receiver_hook; |
119 | 185k | connp->in_current_receiver_offset = connp->in_current_read_offset; |
120 | | |
121 | 185k | return rc; |
122 | 185k | } |
123 | | |
124 | | /** |
125 | | * Finalizes an existing data receiver hook by sending any outstanding data to it. The |
126 | | * hook is then removed so that it receives no more data. |
127 | | * |
128 | | * @param[in] connp |
129 | | * @return HTP_OK, or a value returned from a callback. |
130 | | */ |
131 | 594k | htp_status_t htp_connp_req_receiver_finalize_clear(htp_connp_t *connp) { |
132 | 594k | if (connp->in_data_receiver_hook == NULL) return HTP_OK; |
133 | | |
134 | 183k | htp_status_t rc = htp_connp_req_receiver_send_data(connp, 1 /* last */); |
135 | | |
136 | 183k | connp->in_data_receiver_hook = NULL; |
137 | | |
138 | 183k | return rc; |
139 | 594k | } |
140 | | |
141 | | /** |
142 | | * Handles request parser state changes. At the moment, this function is used only |
143 | | * to configure data receivers, which are sent raw connection data. |
144 | | * |
145 | | * @param[in] connp |
146 | | * @return HTP_OK, or a value returned from a callback. |
147 | | */ |
148 | 2.51M | static htp_status_t htp_req_handle_state_change(htp_connp_t *connp) { |
149 | 2.51M | if (connp->in_state_previous == connp->in_state) return HTP_OK; |
150 | | |
151 | 1.33M | if (connp->in_state == htp_connp_REQ_HEADERS) { |
152 | 185k | htp_status_t rc = HTP_OK; |
153 | | |
154 | 185k | switch (connp->in_tx->request_progress) { |
155 | 184k | case HTP_REQUEST_HEADERS: |
156 | 184k | rc = htp_connp_req_receiver_set(connp, connp->in_tx->cfg->hook_request_header_data); |
157 | 184k | break; |
158 | | |
159 | 1.22k | case HTP_REQUEST_TRAILER: |
160 | 1.22k | rc = htp_connp_req_receiver_set(connp, connp->in_tx->cfg->hook_request_trailer_data); |
161 | 1.22k | break; |
162 | | |
163 | 0 | default: |
164 | | // Do nothing; receivers are currently used only for header blocks. |
165 | 0 | break; |
166 | 185k | } |
167 | | |
168 | 185k | if (rc != HTP_OK) return rc; |
169 | 185k | } |
170 | | |
171 | | // Initially, I had the finalization of raw data sending here, but that |
172 | | // caused the last REQUEST_HEADER_DATA hook to be invoked after the |
173 | | // REQUEST_HEADERS hook -- which I thought made no sense. For that reason, |
174 | | // the finalization is now initiated from the request header processing code, |
175 | | // which is less elegant but provides a better user experience. Having some |
176 | | // (or all) hooks to be invoked on state change might work better. |
177 | | |
178 | 1.33M | connp->in_state_previous = connp->in_state; |
179 | | |
180 | 1.33M | return HTP_OK; |
181 | 1.33M | } |
182 | | |
183 | | /** |
184 | | * If there is any data left in the inbound data chunk, this function will preserve |
185 | | * it for later consumption. The maximum amount accepted for buffering is controlled |
186 | | * by htp_config_t::field_limit_hard. |
187 | | * |
188 | | * @param[in] connp |
189 | | * @return HTP_OK, or HTP_ERROR on fatal failure. |
190 | | */ |
191 | 278k | static htp_status_t htp_connp_req_buffer(htp_connp_t *connp) { |
192 | 278k | if (connp->in_current_data == NULL) return HTP_OK; |
193 | | |
194 | 275k | unsigned char *data = connp->in_current_data + connp->in_current_consume_offset; |
195 | 275k | size_t len = connp->in_current_read_offset - connp->in_current_consume_offset; |
196 | | |
197 | 275k | if (len == 0) |
198 | 32.8k | return HTP_OK; |
199 | | |
200 | | // Check the hard (buffering) limit. |
201 | | |
202 | 242k | size_t newlen = connp->in_buf_size + len; |
203 | | |
204 | | // When calculating the size of the buffer, take into account the |
205 | | // space we're using for the request header buffer. |
206 | 242k | if (connp->in_header != NULL) { |
207 | 52.6k | newlen += bstr_len(connp->in_header); |
208 | 52.6k | } |
209 | | |
210 | 242k | if (newlen > connp->in_tx->cfg->field_limit_hard) { |
211 | 42 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request buffer over the limit: size %zd limit %zd.", |
212 | 42 | newlen, connp->in_tx->cfg->field_limit_hard); |
213 | 42 | return HTP_ERROR; |
214 | 42 | } |
215 | | |
216 | | // Copy the data remaining in the buffer. |
217 | | |
218 | 242k | if (connp->in_buf == NULL) { |
219 | 71.9k | connp->in_buf = malloc(len); |
220 | 71.9k | if (connp->in_buf == NULL) return HTP_ERROR; |
221 | 71.9k | memcpy(connp->in_buf, data, len); |
222 | 71.9k | connp->in_buf_size = len; |
223 | 170k | } else { |
224 | 170k | size_t newsize = connp->in_buf_size + len; |
225 | 170k | unsigned char *newbuf = realloc(connp->in_buf, newsize); |
226 | 170k | if (newbuf == NULL) return HTP_ERROR; |
227 | 170k | connp->in_buf = newbuf; |
228 | 170k | memcpy(connp->in_buf + connp->in_buf_size, data, len); |
229 | 170k | connp->in_buf_size = newsize; |
230 | 170k | } |
231 | | |
232 | | // Reset the consumer position. |
233 | 242k | connp->in_current_consume_offset = connp->in_current_read_offset; |
234 | | |
235 | 242k | return HTP_OK; |
236 | 242k | } |
237 | | |
238 | | /** |
239 | | * Returns to the caller the memory region that should be processed next. This function |
240 | | * hides away the buffering process from the rest of the code, allowing it to work with |
241 | | * non-buffered data that's in the inbound chunk, or buffered data that's in our structures. |
242 | | * |
243 | | * @param[in] connp |
244 | | * @param[out] data |
245 | | * @param[out] len |
246 | | * @return HTP_OK |
247 | | */ |
248 | 3.42M | static htp_status_t htp_connp_req_consolidate_data(htp_connp_t *connp, unsigned char **data, size_t *len) { |
249 | 3.42M | if (connp->in_buf == NULL) { |
250 | | // We do not have any data buffered; point to the current data chunk. |
251 | 3.33M | *data = connp->in_current_data + connp->in_current_consume_offset; |
252 | 3.33M | *len = connp->in_current_read_offset - connp->in_current_consume_offset; |
253 | 3.33M | } else { |
254 | | // We already have some data in the buffer. Add the data from the current |
255 | | // chunk to it, and point to the consolidated buffer. |
256 | 86.7k | if (htp_connp_req_buffer(connp) != HTP_OK) { |
257 | 23 | return HTP_ERROR; |
258 | 23 | } |
259 | | |
260 | 86.7k | *data = connp->in_buf; |
261 | 86.7k | *len = connp->in_buf_size; |
262 | 86.7k | } |
263 | | |
264 | 3.42M | return HTP_OK; |
265 | 3.42M | } |
266 | | |
267 | | /** |
268 | | * Clears buffered inbound data and resets the consumer position to the reader position. |
269 | | * |
270 | | * @param[in] connp |
271 | | */ |
272 | 2.49M | static void htp_connp_req_clear_buffer(htp_connp_t *connp) { |
273 | 2.49M | connp->in_current_consume_offset = connp->in_current_read_offset; |
274 | | |
275 | 2.49M | if (connp->in_buf != NULL) { |
276 | 68.6k | free(connp->in_buf); |
277 | 68.6k | connp->in_buf = NULL; |
278 | 68.6k | connp->in_buf_size = 0; |
279 | 68.6k | } |
280 | 2.49M | } |
281 | | |
282 | | /** |
283 | | * Performs a check for a CONNECT transaction to decide whether inbound |
284 | | * parsing needs to be suspended. |
285 | | * |
286 | | * @param[in] connp |
287 | | * @return HTP_OK if the request does not use CONNECT, HTP_DATA_OTHER if |
288 | | * inbound parsing needs to be suspended until we hear from the |
289 | | * other side |
290 | | */ |
291 | 177k | htp_status_t htp_connp_REQ_CONNECT_CHECK(htp_connp_t *connp) { |
292 | | // If the request uses the CONNECT method, then there will |
293 | | // not be a request body, but first we need to wait to see the |
294 | | // response in order to determine if the tunneling request |
295 | | // was a success. |
296 | 177k | if (connp->in_tx->request_method_number == HTP_M_CONNECT) { |
297 | 3.22k | connp->in_state = htp_connp_REQ_CONNECT_WAIT_RESPONSE; |
298 | 3.22k | connp->in_status = HTP_STREAM_DATA_OTHER; |
299 | 3.22k | return HTP_DATA_OTHER; |
300 | 3.22k | } |
301 | | |
302 | | // Continue to the next step to determine |
303 | | // the presence of request body |
304 | 174k | connp->in_state = htp_connp_REQ_BODY_DETERMINE; |
305 | | |
306 | 174k | return HTP_OK; |
307 | 177k | } |
308 | | |
309 | | /** |
310 | | * Determines whether inbound parsing needs to continue or stop. In |
311 | | * case the data appears to be plain text HTTP, we try to continue. |
312 | | * |
313 | | * @param[in] connp |
314 | | * @return HTP_OK if the parser can resume parsing, HTP_DATA_BUFFER if |
315 | | * we need more data. |
316 | | */ |
317 | 436 | htp_status_t htp_connp_REQ_CONNECT_PROBE_DATA(htp_connp_t *connp) { |
318 | 25.4k | for (;;) {//;i < max_read; i++) { |
319 | 25.4k | IN_PEEK_NEXT(connp); |
320 | | // Have we reached the end of the line? For some reason |
321 | | // we can't test after IN_COPY_BYTE_OR_RETURN */ |
322 | 25.4k | if (connp->in_next_byte == LF || connp->in_next_byte == 0x00) |
323 | 54 | break; |
324 | | |
325 | 25.3k | IN_COPY_BYTE_OR_RETURN(connp); |
326 | | |
327 | 24.9k | } |
328 | | |
329 | 54 | unsigned char *data; |
330 | 54 | size_t len; |
331 | 54 | if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { |
332 | 0 | return HTP_ERROR; |
333 | 0 | } |
334 | | #ifdef HTP_DEBUG |
335 | | fprint_raw_data(stderr, "PROBING", data, len); |
336 | | #endif |
337 | | |
338 | 54 | size_t pos = 0; |
339 | 54 | size_t mstart = 0; |
340 | | // skip past leading whitespace. IIS allows this |
341 | 75 | while ((pos < len) && htp_is_space(data[pos])) |
342 | 21 | pos++; |
343 | 54 | if (pos) |
344 | 6 | mstart = pos; |
345 | | // The request method starts at the beginning of the |
346 | | // line and ends with the first whitespace character. |
347 | 837 | while ((pos < len) && (!htp_is_space(data[pos]))) |
348 | 783 | pos++; |
349 | | |
350 | 54 | int methodi = HTP_M_UNKNOWN; |
351 | 54 | bstr *method = bstr_dup_mem(data + mstart, pos - mstart); |
352 | 54 | if (method) { |
353 | 54 | methodi = htp_convert_method_to_number(method); |
354 | 54 | bstr_free(method); |
355 | 54 | } |
356 | 54 | if (methodi != HTP_M_UNKNOWN) { |
357 | | #ifdef HTP_DEBUG |
358 | | fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel contains plain text HTTP", data, len); |
359 | | #endif |
360 | 16 | return htp_tx_state_request_complete(connp->in_tx); |
361 | 38 | } else { |
362 | | #ifdef HTP_DEBUG |
363 | | fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel is not HTTP", data, len); |
364 | | #endif |
365 | 38 | connp->in_status = HTP_STREAM_TUNNEL; |
366 | 38 | connp->out_status = HTP_STREAM_TUNNEL; |
367 | 38 | } |
368 | | |
369 | | // not calling htp_connp_req_clear_buffer, we're not consuming the data |
370 | | |
371 | 38 | return HTP_OK; |
372 | 54 | } |
373 | | |
374 | | /** |
375 | | * Determines whether inbound parsing, which was suspended after |
376 | | * encountering a CONNECT transaction, can proceed (after receiving |
377 | | * the response). |
378 | | * |
379 | | * @param[in] connp |
380 | | * @return HTP_OK if the parser can resume parsing, HTP_DATA_OTHER if |
381 | | * it needs to continue waiting. |
382 | | */ |
383 | 1.20k | htp_status_t htp_connp_REQ_CONNECT_WAIT_RESPONSE(htp_connp_t *connp) { |
384 | | // Check that we saw the response line of the current inbound transaction. |
385 | 1.20k | if (connp->in_tx->response_progress <= HTP_RESPONSE_LINE) { |
386 | 1.00k | return HTP_DATA_OTHER; |
387 | 1.00k | } |
388 | | |
389 | | // A 2xx response means a tunnel was established. Anything |
390 | | // else means we continue to follow the HTTP stream. |
391 | 199 | if ((connp->in_tx->response_status_number >= 200) && (connp->in_tx->response_status_number <= 299)) { |
392 | | // TODO Check that the server did not accept a connection to itself. |
393 | | |
394 | | // The requested tunnel was established: we are going |
395 | | // to probe the remaining data on this stream to see |
396 | | // if we need to ignore it or parse it |
397 | 75 | connp->in_state = htp_connp_REQ_CONNECT_PROBE_DATA; |
398 | 124 | } else { |
399 | | // No tunnel; continue to the next transaction |
400 | 124 | connp->in_state = htp_connp_REQ_FINALIZE; |
401 | 124 | } |
402 | | |
403 | 199 | return HTP_OK; |
404 | 1.20k | } |
405 | | |
406 | | /** |
407 | | * Consumes bytes until the end of the current line. |
408 | | * |
409 | | * @param[in] connp |
410 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
411 | | */ |
412 | 5.30k | htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA_END(htp_connp_t *connp) { |
413 | | // TODO We shouldn't really see anything apart from CR and LF, |
414 | | // so we should warn about anything else. |
415 | | |
416 | 43.5k | for (;;) { |
417 | 43.5k | IN_NEXT_BYTE_OR_RETURN(connp); |
418 | | |
419 | 42.5k | connp->in_tx->request_message_len++; |
420 | | |
421 | 42.5k | if (connp->in_next_byte == LF) { |
422 | 4.27k | connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH; |
423 | 4.27k | return HTP_OK; |
424 | 4.27k | } |
425 | 42.5k | } |
426 | | |
427 | 0 | return HTP_ERROR; |
428 | 5.30k | } |
429 | | |
430 | | /** |
431 | | * Processes a chunk of data. |
432 | | * |
433 | | * @param[in] connp |
434 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
435 | | */ |
436 | 7.18k | htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA(htp_connp_t *connp) { |
437 | | // Determine how many bytes we can consume. |
438 | 7.18k | size_t bytes_to_consume; |
439 | 7.18k | if (connp->in_current_len - connp->in_current_read_offset >= connp->in_chunked_length) { |
440 | | // Entire chunk available in the buffer; read all of it. |
441 | 4.30k | bytes_to_consume = connp->in_chunked_length; |
442 | 4.30k | } else { |
443 | | // Partial chunk available in the buffer; read as much as we can. |
444 | 2.88k | bytes_to_consume = connp->in_current_len - connp->in_current_read_offset; |
445 | 2.88k | } |
446 | | |
447 | | #ifdef HTP_DEBUG |
448 | | fprintf(stderr, "htp_connp_REQ_BODY_CHUNKED_DATA Consuming %zd bytes\n", bytes_to_consume); |
449 | | #endif |
450 | | |
451 | | // If the input buffer is empty, ask for more data. |
452 | 7.18k | if (bytes_to_consume == 0) return HTP_DATA; |
453 | | |
454 | | // Consume the data. |
455 | 6.89k | htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, connp->in_current_data + connp->in_current_read_offset, bytes_to_consume); |
456 | 6.89k | if (rc != HTP_OK) return rc; |
457 | | |
458 | | // Adjust counters. |
459 | 6.89k | connp->in_current_read_offset += bytes_to_consume; |
460 | 6.89k | connp->in_current_consume_offset += bytes_to_consume; |
461 | 6.89k | connp->in_stream_offset += bytes_to_consume; |
462 | 6.89k | connp->in_tx->request_message_len += bytes_to_consume; |
463 | 6.89k | connp->in_chunked_length -= bytes_to_consume; |
464 | | |
465 | 6.89k | if (connp->in_chunked_length == 0) { |
466 | | // End of the chunk. |
467 | 4.30k | connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA_END; |
468 | 4.30k | return HTP_OK; |
469 | 4.30k | } |
470 | | |
471 | | // Ask for more data. |
472 | 2.59k | return HTP_DATA; |
473 | 6.89k | } |
474 | | |
475 | | /** |
476 | | * Extracts chunk length. |
477 | | * |
478 | | * @param[in] connp |
479 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
480 | | */ |
481 | 6.68k | htp_status_t htp_connp_REQ_BODY_CHUNKED_LENGTH(htp_connp_t *connp) { |
482 | 131k | for (;;) { |
483 | 131k | IN_COPY_BYTE_OR_RETURN(connp); |
484 | | |
485 | | // Have we reached the end of the line? |
486 | 130k | if (connp->in_next_byte == LF) { |
487 | 5.73k | unsigned char *data; |
488 | 5.73k | size_t len; |
489 | | |
490 | 5.73k | if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { |
491 | 2 | return HTP_ERROR; |
492 | 2 | } |
493 | | |
494 | 5.73k | connp->in_tx->request_message_len += len; |
495 | | |
496 | | #ifdef HTP_DEBUG |
497 | | fprint_raw_data(stderr, "Chunk length line", data, len); |
498 | | #endif |
499 | | |
500 | 5.73k | htp_chomp(data, &len); |
501 | | |
502 | 5.73k | int chunk_ext = 0; |
503 | 5.73k | connp->in_chunked_length = htp_parse_chunked_length(data, len, &chunk_ext); |
504 | 5.73k | if (chunk_ext == 1) { |
505 | 460 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request chunk extension"); |
506 | 460 | } |
507 | | |
508 | 5.73k | htp_connp_req_clear_buffer(connp); |
509 | | |
510 | | // Handle chunk length. |
511 | 5.73k | if (connp->in_chunked_length > 0) { |
512 | | // More data available. |
513 | 4.42k | connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA; |
514 | 4.42k | } else if (connp->in_chunked_length == 0) { |
515 | | // End of data. |
516 | 1.22k | connp->in_state = htp_connp_REQ_HEADERS; |
517 | 1.22k | connp->in_tx->request_progress = HTP_REQUEST_TRAILER; |
518 | 1.22k | } else { |
519 | | // Invalid chunk length. |
520 | 90 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request chunk encoding: Invalid chunk length"); |
521 | 90 | return HTP_ERROR; |
522 | 90 | } |
523 | | |
524 | 5.64k | return HTP_OK; |
525 | 5.73k | } |
526 | 130k | } |
527 | | |
528 | 0 | return HTP_ERROR; |
529 | 6.68k | } |
530 | | |
531 | | /** |
532 | | * Processes identity request body. |
533 | | * |
534 | | * @param[in] connp |
535 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
536 | | */ |
537 | 9.94k | htp_status_t htp_connp_REQ_BODY_IDENTITY(htp_connp_t *connp) { |
538 | | // Determine how many bytes we can consume. |
539 | 9.94k | size_t bytes_to_consume; |
540 | 9.94k | if (connp->in_current_len - connp->in_current_read_offset >= connp->in_body_data_left) { |
541 | 5.04k | bytes_to_consume = connp->in_body_data_left; |
542 | 5.04k | } else { |
543 | 4.89k | bytes_to_consume = connp->in_current_len - connp->in_current_read_offset; |
544 | 4.89k | } |
545 | | |
546 | | // If the input buffer is empty, ask for more data. |
547 | 9.94k | if (bytes_to_consume == 0) return HTP_DATA; |
548 | | |
549 | | // Consume data. |
550 | 9.61k | int rc = htp_tx_req_process_body_data_ex(connp->in_tx, connp->in_current_data + connp->in_current_read_offset, bytes_to_consume); |
551 | 9.61k | if (rc != HTP_OK) return rc; |
552 | | |
553 | | // Adjust counters. |
554 | 9.61k | connp->in_current_read_offset += bytes_to_consume; |
555 | 9.61k | connp->in_current_consume_offset += bytes_to_consume; |
556 | 9.61k | connp->in_stream_offset += bytes_to_consume; |
557 | 9.61k | connp->in_tx->request_message_len += bytes_to_consume; |
558 | 9.61k | connp->in_body_data_left -= bytes_to_consume; |
559 | | |
560 | 9.61k | if (connp->in_body_data_left == 0) { |
561 | | // End of request body. |
562 | 5.04k | connp->in_state = htp_connp_REQ_FINALIZE; |
563 | 5.04k | return HTP_OK; |
564 | 5.04k | } |
565 | | |
566 | | // Ask for more data. |
567 | 4.56k | return HTP_DATA; |
568 | 9.61k | } |
569 | | |
570 | | /** |
571 | | * Determines presence (and encoding) of a request body. |
572 | | * |
573 | | * @param[in] connp |
574 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
575 | | */ |
576 | 174k | htp_status_t htp_connp_REQ_BODY_DETERMINE(htp_connp_t *connp) { |
577 | | // Determine the next state based on the presence of the request |
578 | | // body, and the coding used. |
579 | 174k | switch (connp->in_tx->request_transfer_coding) { |
580 | | |
581 | 1.50k | case HTP_CODING_CHUNKED: |
582 | 1.50k | connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH; |
583 | 1.50k | connp->in_tx->request_progress = HTP_REQUEST_BODY; |
584 | 1.50k | break; |
585 | | |
586 | 11.4k | case HTP_CODING_IDENTITY: |
587 | 11.4k | connp->in_content_length = connp->in_tx->request_content_length; |
588 | 11.4k | connp->in_body_data_left = connp->in_content_length; |
589 | | |
590 | 11.4k | if (connp->in_content_length != 0) { |
591 | 5.48k | connp->in_state = htp_connp_REQ_BODY_IDENTITY; |
592 | 5.48k | connp->in_tx->request_progress = HTP_REQUEST_BODY; |
593 | 5.96k | } else { |
594 | 5.96k | connp->in_tx->connp->in_state = htp_connp_REQ_FINALIZE; |
595 | 5.96k | } |
596 | 11.4k | break; |
597 | | |
598 | 161k | case HTP_CODING_NO_BODY: |
599 | | // This request does not have a body, which |
600 | | // means that we're done with it |
601 | 161k | connp->in_state = htp_connp_REQ_FINALIZE; |
602 | 161k | break; |
603 | | |
604 | 198 | default: |
605 | | // Should not be here |
606 | 198 | return HTP_ERROR; |
607 | 0 | break; |
608 | 174k | } |
609 | | |
610 | 174k | return HTP_OK; |
611 | 174k | } |
612 | | |
613 | | /** |
614 | | * Parses request headers. |
615 | | * |
616 | | * @param[in] connp |
617 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
618 | | */ |
619 | 267k | htp_status_t htp_connp_REQ_HEADERS(htp_connp_t *connp) { |
620 | 35.1M | for (;;) { |
621 | 35.1M | if (connp->in_status == HTP_STREAM_CLOSED) { |
622 | | // Parse previous header, if any. |
623 | 1.74k | if (connp->in_header != NULL) { |
624 | 474 | if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), |
625 | 474 | bstr_len(connp->in_header)) != HTP_OK) |
626 | 0 | return HTP_ERROR; |
627 | 474 | bstr_free(connp->in_header); |
628 | 474 | connp->in_header = NULL; |
629 | 474 | } |
630 | | |
631 | 1.74k | htp_connp_req_clear_buffer(connp); |
632 | | |
633 | 1.74k | connp->in_tx->request_progress = HTP_REQUEST_TRAILER; |
634 | | |
635 | | // We've seen all the request headers. |
636 | 1.74k | return htp_tx_state_request_headers(connp->in_tx); |
637 | 1.74k | } |
638 | 35.1M | IN_COPY_BYTE_OR_RETURN(connp); |
639 | | |
640 | | // Have we reached the end of the line? |
641 | 35.0M | if (connp->in_next_byte == LF) { |
642 | 1.10M | unsigned char *data; |
643 | 1.10M | size_t len; |
644 | | |
645 | 1.10M | if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { |
646 | 6 | return HTP_ERROR; |
647 | 6 | } |
648 | | |
649 | | #ifdef HTP_DEBUG |
650 | | fprint_raw_data(stderr, __func__, data, len); |
651 | | #endif |
652 | | |
653 | | // Should we terminate headers? |
654 | 1.10M | if (htp_connp_is_line_terminator(connp, data, len, 0)) { |
655 | | // Parse previous header, if any. |
656 | 179k | if (connp->in_header != NULL) { |
657 | 20.7k | if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), |
658 | 20.7k | bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR; |
659 | | |
660 | 20.7k | bstr_free(connp->in_header); |
661 | 20.7k | connp->in_header = NULL; |
662 | 20.7k | } |
663 | | |
664 | 179k | htp_connp_req_clear_buffer(connp); |
665 | | |
666 | | // We've seen all the request headers. |
667 | 179k | return htp_tx_state_request_headers(connp->in_tx); |
668 | 179k | } |
669 | | |
670 | 923k | htp_chomp(data, &len); |
671 | | |
672 | | // Check for header folding. |
673 | 923k | if (htp_connp_is_line_folded(data, len) == 0) { |
674 | | // New header line. |
675 | | |
676 | | // Parse previous header, if any. |
677 | 662k | if (connp->in_header != NULL) { |
678 | 195k | if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), |
679 | 195k | bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR; |
680 | | |
681 | 195k | bstr_free(connp->in_header); |
682 | 195k | connp->in_header = NULL; |
683 | 195k | } |
684 | | |
685 | 662k | IN_PEEK_NEXT(connp); |
686 | | |
687 | 662k | if (connp->in_next_byte != -1 && htp_is_folding_char(connp->in_next_byte) == 0) { |
688 | | // Because we know this header is not folded, we can process the buffer straight away. |
689 | 467k | if (connp->cfg->process_request_header(connp, data, len) != HTP_OK) return HTP_ERROR; |
690 | 467k | } else { |
691 | | // Keep the partial header data for parsing later. |
692 | 195k | connp->in_header = bstr_dup_mem(data, len); |
693 | 195k | if (connp->in_header == NULL) return HTP_ERROR; |
694 | 195k | } |
695 | 662k | } else { |
696 | | // Folding; check that there's a previous header line to add to. |
697 | 261k | if (connp->in_header == NULL) { |
698 | | // Invalid folding. |
699 | | |
700 | | // Warn only once per transaction. |
701 | 22.7k | if (!(connp->in_tx->flags & HTP_INVALID_FOLDING)) { |
702 | 20.6k | connp->in_tx->flags |= HTP_INVALID_FOLDING; |
703 | 20.6k | htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid request field folding"); |
704 | 20.6k | } |
705 | | |
706 | | // Keep the header data for parsing later. |
707 | 22.7k | size_t trim = 0; |
708 | 76.6k | while(trim < len) { |
709 | 70.5k | if (!htp_is_folding_char(data[trim])) { |
710 | 16.6k | break; |
711 | 16.6k | } |
712 | 53.9k | trim++; |
713 | 53.9k | } |
714 | 22.7k | connp->in_header = bstr_dup_mem(data + trim, len - trim); |
715 | 22.7k | if (connp->in_header == NULL) return HTP_ERROR; |
716 | 238k | } else { |
717 | | // Add to the existing header. |
718 | 238k | if (bstr_len(connp->in_header) < HTP_MAX_HEADER_FOLDED) { |
719 | 238k | bstr *new_in_header = bstr_add_mem(connp->in_header, data, len); |
720 | 238k | if (new_in_header == NULL) return HTP_ERROR; |
721 | 238k | connp->in_header = new_in_header; |
722 | 238k | } else { |
723 | 264 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field length exceeds folded maximum"); |
724 | 264 | } |
725 | 238k | } |
726 | 261k | } |
727 | | |
728 | 923k | htp_connp_req_clear_buffer(connp); |
729 | 923k | } |
730 | 35.0M | } |
731 | | |
732 | 0 | return HTP_ERROR; |
733 | 267k | } |
734 | | |
735 | | // HTTP/0.9 is supposed to be only a request line without protocol. |
736 | | // Libhtp will still consider the request to be HTTP/0.9 if there |
737 | | // are some junk whitespaces after that request line. |
738 | | // Libhtp allows the small value of 16 extra bytes/whitespaces, |
739 | | // otherwise we consider it to be a HTTP/1.x request with missing protocol. |
740 | | // It is unlikely to meet HTTP/0.9, and we want to limit probing. |
741 | 113k | #define HTTP09_MAX_JUNK_LEN 16 |
742 | | |
743 | | /** |
744 | | * Determines request protocol. |
745 | | * |
746 | | * @param[in] connp |
747 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
748 | | */ |
749 | 187k | htp_status_t htp_connp_REQ_PROTOCOL(htp_connp_t *connp) { |
750 | | // Is this a short-style HTTP/0.9 request? If it is, |
751 | | // we will not want to parse request headers. |
752 | 187k | if (connp->in_tx->is_protocol_0_9 == 0) { |
753 | | // Switch to request header parsing. |
754 | 74.1k | connp->in_state = htp_connp_REQ_HEADERS; |
755 | 74.1k | connp->in_tx->request_progress = HTP_REQUEST_HEADERS; |
756 | 113k | } else { |
757 | | // Let's check if the protocol was simply missing |
758 | 113k | int64_t pos = connp->in_current_read_offset; |
759 | | // Probe if data looks like a header line |
760 | 113k | if (connp->in_current_len > connp->in_current_read_offset + HTTP09_MAX_JUNK_LEN) { |
761 | 104k | htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: missing protocol"); |
762 | 104k | connp->in_tx->is_protocol_0_9 = 0; |
763 | | // Switch to request header parsing. |
764 | 104k | connp->in_state = htp_connp_REQ_HEADERS; |
765 | 104k | connp->in_tx->request_progress = HTP_REQUEST_HEADERS; |
766 | 104k | return HTP_OK; |
767 | 104k | } |
768 | 21.2k | while (pos < connp->in_current_len) { |
769 | 18.3k | if (!htp_is_space(connp->in_current_data[pos])) { |
770 | 5.99k | htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: missing protocol"); |
771 | 5.99k | connp->in_tx->is_protocol_0_9 = 0; |
772 | | // Switch to request header parsing. |
773 | 5.99k | connp->in_state = htp_connp_REQ_HEADERS; |
774 | 5.99k | connp->in_tx->request_progress = HTP_REQUEST_HEADERS; |
775 | 5.99k | return HTP_OK; |
776 | 5.99k | } |
777 | 12.3k | pos++; |
778 | 12.3k | } |
779 | | // We're done with this request. |
780 | 2.90k | connp->in_state = htp_connp_REQ_FINALIZE; |
781 | 2.90k | } |
782 | | |
783 | 77.0k | return HTP_OK; |
784 | 187k | } |
785 | | |
786 | | /** |
787 | | * Parse the request line. |
788 | | * |
789 | | * @param[in] connp |
790 | | * @returns HTP_OK on succesful parse, HTP_ERROR on error. |
791 | | */ |
792 | 614k | htp_status_t htp_connp_REQ_LINE_complete(htp_connp_t *connp) { |
793 | 614k | unsigned char *data; |
794 | 614k | size_t len; |
795 | | |
796 | 614k | if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { |
797 | 3 | return HTP_ERROR; |
798 | 3 | } |
799 | | |
800 | | #ifdef HTP_DEBUG |
801 | | fprint_raw_data(stderr, __func__, data, len); |
802 | | #endif |
803 | 614k | if (len == 0) { |
804 | 51 | htp_connp_req_clear_buffer(connp); |
805 | 51 | return HTP_DATA; |
806 | 51 | } |
807 | | |
808 | | // Is this a line that should be ignored? |
809 | 614k | if (htp_connp_is_line_ignorable(connp, data, len)) { |
810 | | // We have an empty/whitespace line, which we'll note, ignore and move on. |
811 | 427k | connp->in_tx->request_ignored_lines++; |
812 | | |
813 | 427k | htp_connp_req_clear_buffer(connp); |
814 | | |
815 | 427k | return HTP_OK; |
816 | 427k | } |
817 | | |
818 | | // Process request line. |
819 | | |
820 | 187k | htp_chomp(data, &len); |
821 | | |
822 | 187k | connp->in_tx->request_line = bstr_dup_mem(data, len); |
823 | 187k | if (connp->in_tx->request_line == NULL) |
824 | 0 | return HTP_ERROR; |
825 | | |
826 | 187k | if (connp->cfg->parse_request_line(connp) != HTP_OK) |
827 | 0 | return HTP_ERROR; |
828 | | |
829 | | // Finalize request line parsing. |
830 | | |
831 | 187k | if (htp_tx_state_request_line(connp->in_tx) != HTP_OK) |
832 | 1 | return HTP_ERROR; |
833 | | |
834 | 187k | htp_connp_req_clear_buffer(connp); |
835 | | |
836 | 187k | return HTP_OK; |
837 | 187k | } |
838 | | |
839 | | /** |
840 | | * Parses request line. |
841 | | * |
842 | | * @param[in] connp |
843 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
844 | | */ |
845 | 665k | htp_status_t htp_connp_REQ_LINE(htp_connp_t *connp) { |
846 | 21.2M | for (;;) { |
847 | | // Get one byte |
848 | 21.2M | IN_PEEK_NEXT(connp); |
849 | 21.2M | if (connp->in_status == HTP_STREAM_CLOSED && connp->in_next_byte == -1) { |
850 | 1.74k | return htp_connp_REQ_LINE_complete(connp); |
851 | 1.74k | } |
852 | 21.2M | IN_COPY_BYTE_OR_RETURN(connp); |
853 | | |
854 | | // Have we reached the end of the line? |
855 | 21.2M | if (connp->in_next_byte == LF) { |
856 | 613k | return htp_connp_REQ_LINE_complete(connp); |
857 | 613k | } |
858 | 21.2M | } |
859 | | |
860 | 0 | return HTP_ERROR; |
861 | 665k | } |
862 | | |
863 | 1.02M | htp_status_t htp_connp_REQ_FINALIZE(htp_connp_t *connp) { |
864 | 1.02M | if (connp->in_status != HTP_STREAM_CLOSED) { |
865 | 1.01M | IN_PEEK_NEXT(connp); |
866 | 1.01M | if (connp->in_next_byte == -1) { |
867 | 36.9k | return htp_tx_state_request_complete(connp->in_tx); |
868 | 36.9k | } |
869 | 978k | if (connp->in_next_byte != LF || connp->in_current_consume_offset >= connp->in_current_read_offset) { |
870 | 27.9M | for (;;) {//;i < max_read; i++) { |
871 | | // peek until LF but do not mark it read so that REQ_LINE works |
872 | 27.9M | IN_PEEK_NEXT(connp); |
873 | 27.9M | if (connp->in_next_byte == LF) |
874 | 926k | break; |
875 | 27.0M | IN_COPY_BYTE_OR_RETURN(connp); |
876 | 26.9M | } |
877 | 978k | } |
878 | 978k | } |
879 | | |
880 | 932k | unsigned char *data; |
881 | 932k | size_t len; |
882 | 932k | if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { |
883 | 2 | return HTP_ERROR; |
884 | 2 | } |
885 | | #ifdef HTP_DEBUG |
886 | | fprint_raw_data(stderr, "PROBING request finalize", data, len); |
887 | | #endif |
888 | 932k | if (len == 0) { |
889 | | //closing |
890 | 145k | return htp_tx_state_request_complete(connp->in_tx); |
891 | 145k | } |
892 | | |
893 | 787k | size_t pos = 0; |
894 | 787k | size_t mstart = 0; |
895 | | // skip past leading whitespace. IIS allows this |
896 | 1.03M | while ((pos < len) && htp_is_space(data[pos])) |
897 | 246k | pos++; |
898 | 787k | if (pos) |
899 | 189k | mstart = pos; |
900 | | // The request method starts at the beginning of the |
901 | | // line and ends with the first whitespace character. |
902 | 8.92M | while ((pos < len) && (!htp_is_space(data[pos]))) |
903 | 8.13M | pos++; |
904 | | |
905 | 787k | if (pos > mstart) { |
906 | | //non empty whitespace line |
907 | 627k | int methodi = HTP_M_UNKNOWN; |
908 | 627k | bstr *method = bstr_dup_mem(data + mstart, pos - mstart); |
909 | 627k | if (method) { |
910 | 627k | methodi = htp_convert_method_to_number(method); |
911 | 627k | bstr_free(method); |
912 | 627k | } |
913 | 627k | if (methodi != HTP_M_UNKNOWN) { |
914 | 18.1k | connp->in_body_data_left = -1; |
915 | 18.1k | return htp_tx_state_request_complete(connp->in_tx); |
916 | 18.1k | } // else continue |
917 | 609k | if (connp->in_body_data_left <= 0) { |
918 | | // log only once per transaction |
919 | 609k | htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Unexpected request body"); |
920 | 609k | } else { |
921 | 0 | connp->in_body_data_left = 1; |
922 | 0 | } |
923 | 609k | } |
924 | | //Adds linefeed to the buffer if there was one |
925 | 769k | if (connp->in_next_byte == LF) { |
926 | 767k | IN_COPY_BYTE_OR_RETURN(connp); |
927 | 767k | htp_connp_req_consolidate_data(connp, &data, &len); |
928 | 767k | } |
929 | | // Interpret remaining bytes as body data |
930 | 769k | htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, data, len); |
931 | 769k | htp_connp_req_clear_buffer(connp); |
932 | 769k | return rc; |
933 | 769k | } |
934 | | |
935 | 9.39k | htp_status_t htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9(htp_connp_t *connp) { |
936 | | // Consume whatever is left in the buffer. |
937 | | |
938 | 9.39k | size_t bytes_left = connp->in_current_len - connp->in_current_read_offset; |
939 | | |
940 | 9.39k | if (bytes_left > 0) { |
941 | 6.89k | connp->conn->flags |= HTP_CONN_HTTP_0_9_EXTRA; |
942 | 6.89k | } |
943 | | |
944 | 9.39k | connp->in_current_read_offset += bytes_left; |
945 | 9.39k | connp->in_current_consume_offset += bytes_left; |
946 | 9.39k | connp->in_stream_offset += bytes_left; |
947 | | |
948 | 9.39k | return HTP_DATA; |
949 | 9.39k | } |
950 | | |
951 | | /** |
952 | | * The idle state is where the parser will end up after a transaction is processed. |
953 | | * If there is more data available, a new request will be started. |
954 | | * |
955 | | * @param[in] connp |
956 | | * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. |
957 | | */ |
958 | 234k | htp_status_t htp_connp_REQ_IDLE(htp_connp_t * connp) { |
959 | | // We want to start parsing the next request (and change |
960 | | // the state from IDLE) only if there's at least one |
961 | | // byte of data available. Otherwise we could be creating |
962 | | // new structures even if there's no more data on the |
963 | | // connection. |
964 | 234k | IN_TEST_NEXT_BYTE_OR_RETURN(connp); |
965 | | |
966 | 193k | connp->in_tx = htp_connp_tx_create(connp); |
967 | 193k | if (connp->in_tx == NULL) return HTP_ERROR; |
968 | | |
969 | | // Change state to TRANSACTION_START |
970 | 193k | htp_tx_state_request_start(connp->in_tx); |
971 | | |
972 | 193k | return HTP_OK; |
973 | 193k | } |
974 | | |
975 | | /** |
976 | | * Returns how many bytes from the current data chunks were consumed so far. |
977 | | * |
978 | | * @param[in] connp |
979 | | * @return The number of bytes consumed. |
980 | | */ |
981 | 421k | size_t htp_connp_req_data_consumed(htp_connp_t *connp) { |
982 | 421k | return connp->in_current_read_offset; |
983 | 421k | } |
984 | | |
985 | 256k | int htp_connp_req_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len) { |
986 | | #ifdef HTP_DEBUG |
987 | | fprintf(stderr, "htp_connp_req_data(connp->in_status %x)\n", connp->in_status); |
988 | | fprint_raw_data(stderr, __func__, data, len); |
989 | | #endif |
990 | | |
991 | | // Return if the connection is in stop state. |
992 | 256k | if (connp->in_status == HTP_STREAM_STOP) { |
993 | 0 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_INFO, 0, "Inbound parser is in HTP_STREAM_STOP"); |
994 | 0 | return HTP_STREAM_STOP; |
995 | 0 | } |
996 | | |
997 | | // Return if the connection had a fatal error earlier |
998 | 256k | if (connp->in_status == HTP_STREAM_ERROR) { |
999 | 246 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Inbound parser is in HTP_STREAM_ERROR"); |
1000 | | |
1001 | | #ifdef HTP_DEBUG |
1002 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (previous error)\n"); |
1003 | | #endif |
1004 | | |
1005 | 246 | return HTP_STREAM_ERROR; |
1006 | 246 | } |
1007 | | |
1008 | | // Sanity check: we must have a transaction pointer if the state is not IDLE (no inbound transaction) |
1009 | 256k | if ((connp->in_tx == NULL)&& |
1010 | 256k | (connp->in_state != htp_connp_REQ_IDLE && connp->in_state != htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9)) { |
1011 | 66 | connp->in_status = HTP_STREAM_ERROR; |
1012 | | |
1013 | 66 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Missing inbound transaction data"); |
1014 | | |
1015 | 66 | return HTP_STREAM_ERROR; |
1016 | 66 | } |
1017 | | |
1018 | | // If the length of the supplied data chunk is zero, proceed |
1019 | | // only if the stream has been closed. We do not allow zero-sized |
1020 | | // chunks in the API, but we use them internally to force the parsers |
1021 | | // to finalize parsing. |
1022 | 256k | if (len == 0 && connp->in_status != HTP_STREAM_CLOSED) { |
1023 | 0 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Zero-length data chunks are not allowed"); |
1024 | |
|
1025 | | #ifdef HTP_DEBUG |
1026 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (zero-length chunk)\n"); |
1027 | | #endif |
1028 | |
|
1029 | 0 | return HTP_STREAM_CLOSED; |
1030 | 0 | } |
1031 | | |
1032 | | // Remember the timestamp of the current request data chunk |
1033 | 256k | if (timestamp != NULL) { |
1034 | 256k | memcpy(&connp->in_timestamp, timestamp, sizeof (*timestamp)); |
1035 | 256k | } |
1036 | | |
1037 | | // Store the current chunk information |
1038 | 256k | connp->in_current_data = (unsigned char *) data; |
1039 | 256k | connp->in_current_len = len; |
1040 | 256k | connp->in_current_read_offset = 0; |
1041 | 256k | connp->in_current_consume_offset = 0; |
1042 | 256k | connp->in_current_receiver_offset = 0; |
1043 | 256k | connp->in_chunk_count++; |
1044 | | |
1045 | 256k | htp_conn_track_inbound_data(connp->conn, len, timestamp); |
1046 | | |
1047 | | |
1048 | | // Return without processing any data if the stream is in tunneling |
1049 | | // mode (which it would be after an initial CONNECT transaction). |
1050 | 256k | if (connp->in_status == HTP_STREAM_TUNNEL) { |
1051 | | #ifdef HTP_DEBUG |
1052 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n"); |
1053 | | #endif |
1054 | | |
1055 | 689 | return HTP_STREAM_TUNNEL; |
1056 | 689 | } |
1057 | | |
1058 | 255k | if (connp->out_status == HTP_STREAM_DATA_OTHER) { |
1059 | 21 | connp->out_status = HTP_STREAM_DATA; |
1060 | 21 | } |
1061 | | |
1062 | | // Invoke a processor, in a loop, until an error |
1063 | | // occurs or until we run out of data. Many processors |
1064 | | // will process a request, each pointing to the next |
1065 | | // processor that needs to run. |
1066 | 2.77M | for (;;) { |
1067 | | #ifdef HTP_DEBUG |
1068 | | fprintf(stderr, "htp_connp_req_data: in state=%s, progress=%s\n", |
1069 | | htp_connp_in_state_as_string(connp), |
1070 | | htp_tx_request_progress_as_string(connp->in_tx)); |
1071 | | #endif |
1072 | | |
1073 | | // Return if there's been an error or if we've run out of data. We are relying |
1074 | | // on processors to supply error messages, so we'll keep quiet here. |
1075 | | |
1076 | 2.77M | htp_status_t rc; |
1077 | | //handle gap |
1078 | 2.77M | if (data == NULL && len > 0) { |
1079 | | //cannot switch over a function pointer in C |
1080 | 692 | if (connp->in_state == htp_connp_REQ_BODY_IDENTITY || |
1081 | 692 | connp->in_state == htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9) { |
1082 | 8 | rc = connp->in_state(connp); |
1083 | 684 | } else if (connp->in_state == htp_connp_REQ_FINALIZE) { |
1084 | | //simple version without probing |
1085 | 196 | rc = htp_tx_state_request_complete(connp->in_tx); |
1086 | 488 | } else { |
1087 | | // go to htp_connp_REQ_CONNECT_PROBE_DATA ? |
1088 | 488 | htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Gaps are not allowed during this state"); |
1089 | 488 | return HTP_STREAM_CLOSED; |
1090 | 488 | } |
1091 | 2.77M | } else { |
1092 | 2.77M | rc = connp->in_state(connp); |
1093 | 2.77M | } |
1094 | 2.77M | if (rc == HTP_OK) { |
1095 | 2.51M | if (connp->in_status == HTP_STREAM_TUNNEL) { |
1096 | | #ifdef HTP_DEBUG |
1097 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n"); |
1098 | | #endif |
1099 | | |
1100 | 38 | return HTP_STREAM_TUNNEL; |
1101 | 38 | } |
1102 | | |
1103 | 2.51M | rc = htp_req_handle_state_change(connp); |
1104 | 2.51M | } |
1105 | | |
1106 | 2.77M | if (rc != HTP_OK) { |
1107 | | // Do we need more data? |
1108 | 254k | if ((rc == HTP_DATA) || (rc == HTP_DATA_BUFFER)) { |
1109 | 250k | htp_connp_req_receiver_send_data(connp, 0 /* not last */); |
1110 | | |
1111 | 250k | if (rc == HTP_DATA_BUFFER) { |
1112 | 191k | if (htp_connp_req_buffer(connp) != HTP_OK) { |
1113 | 19 | connp->in_status = HTP_STREAM_ERROR; |
1114 | 19 | return HTP_STREAM_ERROR; |
1115 | 19 | } |
1116 | 191k | } |
1117 | | |
1118 | | #ifdef HTP_DEBUG |
1119 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA\n"); |
1120 | | #endif |
1121 | | |
1122 | 250k | connp->in_status = HTP_STREAM_DATA; |
1123 | | |
1124 | 250k | return HTP_STREAM_DATA; |
1125 | 250k | } |
1126 | | |
1127 | | // Check for suspended parsing. |
1128 | 4.57k | if (rc == HTP_DATA_OTHER) { |
1129 | | // We might have actually consumed the entire data chunk? |
1130 | 4.23k | if (connp->in_current_read_offset >= connp->in_current_len) { |
1131 | | // Do not send STREAM_DATE_DATA_OTHER if we've consumed the entire chunk. |
1132 | | |
1133 | | #ifdef HTP_DEBUG |
1134 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (suspended parsing)\n"); |
1135 | | #endif |
1136 | | |
1137 | 3.14k | connp->in_status = HTP_STREAM_DATA; |
1138 | | |
1139 | 3.14k | return HTP_STREAM_DATA; |
1140 | 3.14k | } else { |
1141 | | // Partial chunk consumption. |
1142 | | |
1143 | | #ifdef HTP_DEBUG |
1144 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA_OTHER\n"); |
1145 | | #endif |
1146 | | |
1147 | 1.09k | connp->in_status = HTP_STREAM_DATA_OTHER; |
1148 | | |
1149 | 1.09k | return HTP_STREAM_DATA_OTHER; |
1150 | 1.09k | } |
1151 | 4.23k | } |
1152 | | |
1153 | | // Check for the stop signal. |
1154 | 335 | if (rc == HTP_STOP) { |
1155 | | #ifdef HTP_DEBUG |
1156 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_STOP\n"); |
1157 | | #endif |
1158 | |
|
1159 | 0 | connp->in_status = HTP_STREAM_STOP; |
1160 | |
|
1161 | 0 | return HTP_STREAM_STOP; |
1162 | 0 | } |
1163 | | |
1164 | | #ifdef HTP_DEBUG |
1165 | | fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_ERROR\n"); |
1166 | | #endif |
1167 | | |
1168 | | // Permanent stream error. |
1169 | 335 | connp->in_status = HTP_STREAM_ERROR; |
1170 | | |
1171 | 335 | return HTP_STREAM_ERROR; |
1172 | 335 | } |
1173 | 2.77M | } |
1174 | 255k | } |