/src/suricata8/rust/htp/src/connection_parser.rs
Line | Count | Source |
1 | | use crate::{ |
2 | | bstr::Bstr, |
3 | | config::Config, |
4 | | connection::{Connection, ConnectionFlags}, |
5 | | decompressors::HtpContentEncoding, |
6 | | error::Result, |
7 | | hook::DataHook, |
8 | | log::Logger, |
9 | | transaction::{HtpRequestProgress, HtpResponseProgress, HtpTransferCoding, Transaction}, |
10 | | transactions::Transactions, |
11 | | util::{FlagOperations, HtpFlags}, |
12 | | HtpStatus, |
13 | | }; |
14 | | use std::{any::Any, borrow::Cow, cell::Cell, net::IpAddr, time::SystemTime}; |
15 | | use time::OffsetDateTime; |
16 | | |
17 | | /// Enumerates parsing state. |
18 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
19 | | pub(crate) enum State { |
20 | | /// Default state. |
21 | | None, |
22 | | /// State once a transaction is processed or about to be processed. |
23 | | Idle, |
24 | | /// State for request/response line parsing. |
25 | | Line, |
26 | | /// State for header parsing. |
27 | | Headers, |
28 | | /// State for finalizing chunked body data parsing. |
29 | | BodyChunkedDataEnd, |
30 | | /// State for chunked body data. |
31 | | BodyChunkedData, |
32 | | /// Parse the chunked length state. |
33 | | BodyChunkedLength, |
34 | | /// State to determine encoding of body data. |
35 | | BodyDetermine, |
36 | | /// State for finalizing transaction side. |
37 | | Finalize, |
38 | | // Used by request_state only |
39 | | /// State for determining the request protocol. |
40 | | Protocol, |
41 | | /// State to determine if there is a CONNECT request. |
42 | | ConnectCheck, |
43 | | /// State to determine if inbound parsing needs to be suspended. |
44 | | ConnectProbeData, |
45 | | /// State to determine if inbound parsing can continue if it was suspended. |
46 | | ConnectWaitResponse, |
47 | | /// State to process request body data. |
48 | | BodyIdentity, |
49 | | /// State to consume remaining data in request buffer for the HTTP 0.9 case. |
50 | | IgnoreDataAfterHTTP09, |
51 | | // Used by response_state only |
52 | | /// State to consume response remaining body data when content-length is unknown. |
53 | | BodyIdentityStreamClose, |
54 | | /// State to consume response body data when content-length is known. |
55 | | BodyIdentityCLKnown, |
56 | | } |
57 | | |
58 | | /// Enumerates all stream states. Each connection has two streams, one |
59 | | /// inbound and one outbound. Their states are tracked separately. |
60 | | #[repr(C)] |
61 | | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
62 | | pub enum HtpStreamState { |
63 | | /// Default stream state. |
64 | | NEW, |
65 | | /// State when connection is open. |
66 | | OPEN, |
67 | | /// State when connection is closed. |
68 | | CLOSED, |
69 | | /// State when stream produces a fatal error. |
70 | | ERROR, |
71 | | /// State for a tunnelled stream. |
72 | | TUNNEL, |
73 | | /// State when parsing is suspended and not consumed in order. This is to |
74 | | /// allow processing on another stream. |
75 | | DATA_OTHER, |
76 | | /// State when we should stop parsing the associated connection. |
77 | | STOP, |
78 | | /// State when all current data in the stream has been processed. |
79 | | DATA, |
80 | | } |
81 | | |
82 | | #[derive(Debug, Default, Clone)] |
83 | | /// This structure is used to pass data (for example |
84 | | /// request and response body buffers or gaps) to parsers. |
85 | | pub(crate) struct ParserData<'a> { |
86 | | /// Ref to the data buffer. |
87 | | data: Option<Cow<'a, [u8]>>, |
88 | | // Length of data gap. Only set if is a gap. |
89 | | gap_len: Option<usize>, |
90 | | // Current position offset of the data to parse |
91 | | position: Cell<usize>, |
92 | | // Current callback data position |
93 | | callback_position: usize, |
94 | | } |
95 | | |
96 | | impl ParserData<'_> { |
97 | | /// Returns a pointer to the raw data associated with Data. |
98 | | /// This returns a pointer to the entire data chunk. |
99 | 7.10M | pub(crate) fn data_ptr(&self) -> *const u8 { |
100 | 7.10M | self.data() |
101 | 7.10M | .as_ref() |
102 | 7.10M | .map(|data| data.as_ptr()) |
103 | 7.10M | .unwrap_or(std::ptr::null()) |
104 | 7.10M | } |
105 | | |
106 | | /// Returns the unconsumed data |
107 | 7.27M | pub(crate) fn data(&self) -> Option<&[u8]> { |
108 | 7.27M | let data = self.data.as_ref()?; |
109 | 7.27M | if self.position.get() <= data.len() { |
110 | 7.27M | Some(&data[self.position.get()..]) |
111 | | } else { |
112 | 0 | None |
113 | | } |
114 | 7.27M | } |
115 | | |
116 | | /// Returns the length of the unconsumed data. |
117 | 30.8M | pub(crate) fn len(&self) -> usize { |
118 | 30.8M | if let Some(gap_len) = self.gap_len { |
119 | 268 | if self.position.get() >= gap_len { |
120 | 0 | 0 |
121 | | } else { |
122 | 268 | gap_len - self.position.get() |
123 | | } |
124 | | } else { |
125 | 30.8M | self.as_slice().len() |
126 | | } |
127 | 30.8M | } |
128 | | |
129 | | /// Returns how much data has been consumed so far |
130 | 13.8M | fn consumed_len(&self) -> usize { |
131 | 13.8M | self.position.get() |
132 | 13.8M | } |
133 | | |
134 | | /// Return an immutable slice view of the unconsumed data. |
135 | 48.6M | pub(crate) fn as_slice(&self) -> &[u8] { |
136 | 48.6M | if let Some(data) = self.data.as_ref() { |
137 | 47.8M | if self.position.get() <= data.len() { |
138 | 47.8M | return &data[self.position.get()..]; |
139 | 0 | } |
140 | 834k | } |
141 | 834k | b"" |
142 | 48.6M | } |
143 | | |
144 | | /// Determines if this chunk is a gap or not |
145 | 20.0M | pub(crate) fn is_gap(&self) -> bool { |
146 | 20.0M | self.gap_len.is_some() |
147 | 20.0M | } |
148 | | |
149 | | /// Determine whether there is no more data to consume. |
150 | 4.51M | pub(crate) fn is_empty(&self) -> bool { |
151 | 4.51M | self.len() == 0 |
152 | 4.51M | } |
153 | | |
154 | | /// Set the position offset into the data for parsing |
155 | 13.8M | fn set_position(&self, position: usize) { |
156 | 13.8M | self.position.set(position); |
157 | 13.8M | } |
158 | | |
159 | | /// Advances the internal position where we are parsing |
160 | 13.8M | pub(crate) fn consume(&self, consumed: usize) { |
161 | 13.8M | self.set_position(self.position.get() + consumed); |
162 | 13.8M | } |
163 | | |
164 | | /// Decrements the internal position where we are parsing |
165 | 20.4k | fn unconsume(&self, unconsume: usize) { |
166 | 20.4k | if unconsume < self.position.get() { |
167 | 17.3k | self.set_position(self.position.get() - unconsume); |
168 | 17.3k | } else { |
169 | 3.07k | self.set_position(0); |
170 | 3.07k | } |
171 | 20.4k | } |
172 | | |
173 | | /// Make an owned version of this data. |
174 | | #[cfg(test)] |
175 | | pub(crate) fn into_owned(self) -> ParserData<'static> { |
176 | | ParserData { |
177 | | data: self.data.map(|d| Cow::Owned(d.into_owned())), |
178 | | gap_len: self.gap_len, |
179 | | position: self.position, |
180 | | callback_position: self.callback_position, |
181 | | } |
182 | | } |
183 | | |
184 | | /// Callback data is raw data buffer content that is passed to the |
185 | | /// application via the header and trailer data hooks. |
186 | | /// |
187 | | /// This function will return any data that has been consumed but not |
188 | | /// yet returned from this function. |
189 | 2.19M | pub(crate) fn callback_data(&mut self) -> &[u8] { |
190 | 2.19M | if let Some(data) = self.data.as_ref() { |
191 | 2.17M | if self.position.get() <= data.len() && self.callback_position <= self.position.get() { |
192 | 2.17M | let d = &data[self.callback_position..self.position.get()]; |
193 | 2.17M | self.callback_position = self.position.get(); |
194 | 2.17M | return d; |
195 | 0 | } |
196 | 23.2k | } |
197 | 23.2k | b"" |
198 | 2.19M | } |
199 | | |
200 | | /// Sets the callback start location to the current parsing location |
201 | 451k | pub(crate) fn reset_callback_start(&mut self) { |
202 | 451k | self.callback_position = self.position.get(); |
203 | 451k | } |
204 | | } |
205 | | |
206 | | impl<'a> From<Option<&'a [u8]>> for ParserData<'a> { |
207 | 5.76M | fn from(data: Option<&'a [u8]>) -> Self { |
208 | 5.76M | ParserData { |
209 | 5.76M | data: data.map(Cow::Borrowed), |
210 | 5.76M | gap_len: None, |
211 | 5.76M | position: Cell::new(0), |
212 | 5.76M | callback_position: 0, |
213 | 5.76M | } |
214 | 5.76M | } |
215 | | } |
216 | | |
217 | | impl<'a> From<&'a [u8]> for ParserData<'a> { |
218 | 3.94M | fn from(data: &'a [u8]) -> Self { |
219 | 3.94M | ParserData { |
220 | 3.94M | data: Some(Cow::Borrowed(data)), |
221 | 3.94M | gap_len: None, |
222 | 3.94M | position: Cell::new(0), |
223 | 3.94M | callback_position: 0, |
224 | 3.94M | } |
225 | 3.94M | } |
226 | | } |
227 | | |
228 | | impl From<Vec<u8>> for ParserData<'static> { |
229 | 0 | fn from(data: Vec<u8>) -> Self { |
230 | 0 | ParserData { |
231 | 0 | data: Some(Cow::Owned(data)), |
232 | 0 | gap_len: None, |
233 | 0 | position: Cell::new(0), |
234 | 0 | callback_position: 0, |
235 | 0 | } |
236 | 0 | } |
237 | | } |
238 | | |
239 | | impl<'a> From<&'a Vec<u8>> for ParserData<'a> { |
240 | 0 | fn from(data: &'a Vec<u8>) -> Self { |
241 | 0 | ParserData { |
242 | 0 | data: Some(Cow::Borrowed(data.as_slice())), |
243 | 0 | gap_len: None, |
244 | 0 | position: Cell::new(0), |
245 | 0 | callback_position: 0, |
246 | 0 | } |
247 | 0 | } |
248 | | } |
249 | | |
250 | | impl From<usize> for ParserData<'_> { |
251 | 105 | fn from(gap_len: usize) -> Self { |
252 | 105 | ParserData { |
253 | 105 | data: None, |
254 | 105 | gap_len: Some(gap_len), |
255 | 105 | position: Cell::new(0), |
256 | 105 | callback_position: 0, |
257 | 105 | } |
258 | 105 | } |
259 | | } |
260 | | |
261 | | impl From<(*const u8, usize)> for ParserData<'_> { |
262 | 1.74M | fn from((data, len): (*const u8, usize)) -> Self { |
263 | 1.74M | if data.is_null() { |
264 | 105 | if len > 0 { |
265 | 105 | ParserData::from(len) |
266 | | } else { |
267 | 0 | ParserData::from(b"".as_ref()) |
268 | | } |
269 | | } else { |
270 | 1.74M | unsafe { ParserData::from(std::slice::from_raw_parts(data, len)) } |
271 | | } |
272 | 1.74M | } |
273 | | } |
274 | | |
275 | | /// Stores information about the parsing process and associated transactions. |
276 | | pub struct ConnectionParser { |
277 | | // General fields |
278 | | /// The logger structure associated with this parser |
279 | | pub(crate) logger: Logger, |
280 | | /// A reference to the current parser configuration structure. |
281 | | pub(crate) cfg: &'static Config, |
282 | | /// The connection structure associated with this parser. |
283 | | pub(crate) conn: Connection, |
284 | | /// Opaque user data associated with this parser. |
285 | | pub(crate) user_data: Option<Box<dyn Any>>, |
286 | | // Request parser fields |
287 | | /// Parser inbound status. Starts as OK, but may turn into ERROR. |
288 | | pub(crate) request_status: HtpStreamState, |
289 | | /// Parser outbound status. Starts as OK, but may turn into ERROR. |
290 | | pub(crate) response_status: HtpStreamState, |
291 | | /// When true, this field indicates that there is unprocessed inbound data, and |
292 | | /// that the response parsing code should stop at the end of the current request |
293 | | /// in order to allow more requests to be produced. |
294 | | pub(crate) response_data_other_at_tx_end: bool, |
295 | | /// The time when the last request data chunk was received. |
296 | | pub(crate) request_timestamp: OffsetDateTime, |
297 | | /// How many bytes from the last input chunk have we consumed |
298 | | /// This is mostly used from callbacks, where the caller |
299 | | /// wants to know how far into the last chunk the parser is. |
300 | | pub(crate) request_bytes_consumed: usize, |
301 | | /// How many data chunks does the inbound connection stream consist of? |
302 | | pub(crate) request_chunk_count: usize, |
303 | | /// The index of the first chunk used in the current request. |
304 | | pub(crate) request_chunk_request_index: usize, |
305 | | /// Used to buffer a line of inbound data when buffering cannot be avoided. |
306 | | pub(crate) request_buf: Bstr, |
307 | | /// Stores the current value of a folded request header. Such headers span |
308 | | /// multiple lines, and are processed only when all data is available. |
309 | | pub(crate) request_header: Option<Bstr>, |
310 | | /// The request body length declared in a valid request header. The key here |
311 | | /// is "valid". This field will not be populated if the request contains both |
312 | | /// a Transfer-Encoding header and a Content-Length header. |
313 | | pub(crate) request_content_length: Option<u64>, |
314 | | /// Holds the remaining request body length that we expect to read. This |
315 | | /// field will be available only when the length of a request body is known |
316 | | /// in advance, i.e. when request headers contain a Content-Length header. |
317 | | pub(crate) request_body_data_left: Option<u64>, |
318 | | /// Holds the amount of data that needs to be read from the |
319 | | /// current data chunk. Only used with chunked request bodies. |
320 | | pub(crate) request_chunked_length: Option<u64>, |
321 | | /// Current request parser state. |
322 | | pub(crate) request_state: State, |
323 | | /// Previous request parser state. Used to detect state changes. |
324 | | pub(crate) request_state_previous: State, |
325 | | /// The hook that should be receiving raw connection data. |
326 | | pub(crate) request_data_receiver_hook: Option<DataHook>, |
327 | | |
328 | | // Response parser fields |
329 | | /// The time when the last response data chunk was received. |
330 | | pub(crate) response_timestamp: OffsetDateTime, |
331 | | /// How many bytes from the last input chunk have we consumed |
332 | | /// This is mostly used from callbacks, where the caller |
333 | | /// wants to know how far into the last chunk the parser is. |
334 | | pub(crate) response_bytes_consumed: usize, |
335 | | /// Used to buffer a line of outbound data when buffering cannot be avoided. |
336 | | pub(crate) response_buf: Bstr, |
337 | | /// Stores the current value of a folded response header. Such headers span |
338 | | /// multiple lines, and are processed only when all data is available. |
339 | | pub(crate) response_header: Option<Bstr>, |
340 | | /// The length of the current response body as presented in the |
341 | | /// Content-Length response header. |
342 | | pub(crate) response_content_length: Option<u64>, |
343 | | /// The remaining length of the current response body, if known. Set to None otherwise. |
344 | | pub(crate) response_body_data_left: Option<u64>, |
345 | | /// Holds the amount of data that needs to be read from the |
346 | | /// current response data chunk. Only used with chunked response bodies. |
347 | | pub(crate) response_chunked_length: Option<u64>, |
348 | | /// Current response parser state. |
349 | | pub(crate) response_state: State, |
350 | | /// Previous response parser state. |
351 | | pub(crate) response_state_previous: State, |
352 | | /// The hook that should be receiving raw connection data. |
353 | | pub(crate) response_data_receiver_hook: Option<DataHook>, |
354 | | |
355 | | /// Number of compression bombs seen. |
356 | | pub(crate) bombs: u8, |
357 | | |
358 | | /// Transactions processed by this parser |
359 | | transactions: Transactions, |
360 | | } |
361 | | |
362 | | impl std::fmt::Debug for ConnectionParser { |
363 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
364 | 0 | f.debug_struct("ConnectionParser") |
365 | 0 | .field("request_status", &self.request_status) |
366 | 0 | .field("response_status", &self.response_status) |
367 | 0 | .field("request_index", &self.request_index()) |
368 | 0 | .field("response_index", &self.response_index()) |
369 | 0 | .finish() |
370 | 0 | } |
371 | | } |
372 | | |
373 | | impl ConnectionParser { |
374 | | /// Creates a new ConnectionParser with a preconfigured `Config` struct. |
375 | 14.8k | pub(crate) fn new(cfg: &'static Config) -> Self { |
376 | 14.8k | let conn = Connection::default(); |
377 | 14.8k | let logger = Logger::new(conn.get_sender()); |
378 | 14.8k | Self { |
379 | 14.8k | logger: logger.clone(), |
380 | 14.8k | cfg, |
381 | 14.8k | conn, |
382 | 14.8k | user_data: None, |
383 | 14.8k | request_status: HtpStreamState::NEW, |
384 | 14.8k | response_status: HtpStreamState::NEW, |
385 | 14.8k | response_data_other_at_tx_end: false, |
386 | 14.8k | request_timestamp: OffsetDateTime::from(SystemTime::now()), |
387 | 14.8k | request_bytes_consumed: 0, |
388 | 14.8k | request_chunk_count: 0, |
389 | 14.8k | request_chunk_request_index: 0, |
390 | 14.8k | request_buf: Bstr::new(), |
391 | 14.8k | request_header: None, |
392 | 14.8k | request_content_length: None, |
393 | 14.8k | request_body_data_left: None, |
394 | 14.8k | request_chunked_length: None, |
395 | 14.8k | request_state: State::Idle, |
396 | 14.8k | request_state_previous: State::None, |
397 | 14.8k | request_data_receiver_hook: None, |
398 | 14.8k | response_timestamp: OffsetDateTime::from(SystemTime::now()), |
399 | 14.8k | response_bytes_consumed: 0, |
400 | 14.8k | response_buf: Bstr::new(), |
401 | 14.8k | response_header: None, |
402 | 14.8k | response_content_length: None, |
403 | 14.8k | response_body_data_left: None, |
404 | 14.8k | response_chunked_length: None, |
405 | 14.8k | response_state: State::Idle, |
406 | 14.8k | response_state_previous: State::None, |
407 | 14.8k | response_data_receiver_hook: None, |
408 | 14.8k | bombs: 0, |
409 | 14.8k | transactions: Transactions::new(cfg, &logger), |
410 | 14.8k | } |
411 | 14.8k | } |
412 | | |
413 | | /// Get the current request transaction |
414 | 2.40M | pub(crate) fn request(&mut self) -> Option<&Transaction> { |
415 | 2.40M | self.transactions.request() |
416 | 2.40M | } |
417 | | |
418 | | /// Get the current request transaction |
419 | 25.9M | pub(crate) fn request_mut(&mut self) -> Option<&mut Transaction> { |
420 | 25.9M | self.transactions.request_mut() |
421 | 25.9M | } |
422 | | |
423 | | /// Get the current response transaction |
424 | 2.31M | pub(crate) fn response(&mut self) -> Option<&Transaction> { |
425 | 2.31M | self.transactions.response() |
426 | 2.31M | } |
427 | | |
428 | | /// Get the current response transaction |
429 | 12.2M | pub(crate) fn response_mut(&mut self) -> Option<&mut Transaction> { |
430 | 12.2M | self.transactions.response_mut() |
431 | 12.2M | } |
432 | | |
433 | | /// Advance to the next request |
434 | | /// Returns the next request transaction id |
435 | 776k | pub(crate) fn request_next(&mut self) -> usize { |
436 | | // Detect pipelining. |
437 | 776k | if self.transactions.request_index() > self.transactions.response_index() { |
438 | 403k | self.conn.flags.set(ConnectionFlags::PIPELINED) |
439 | 372k | } |
440 | 776k | self.transactions.request_next() |
441 | 776k | } |
442 | | |
443 | | /// Advance to the next response |
444 | | /// Returns the next response transaction id |
445 | 379k | pub(crate) fn response_next(&mut self) -> usize { |
446 | 379k | self.transactions.response_next() |
447 | 379k | } |
448 | | |
449 | | /// Get the index of the request transaction |
450 | 1.74M | pub(crate) fn request_index(&self) -> usize { |
451 | 1.74M | self.transactions.request_index() |
452 | 1.74M | } |
453 | | |
454 | | /// Get the index of the response transaction |
455 | 868k | pub(crate) fn response_index(&self) -> usize { |
456 | 868k | self.transactions.response_index() |
457 | 868k | } |
458 | | |
459 | | /// Get the number of transactions processed up to now |
460 | 76.0M | pub(crate) fn tx_size(&self) -> usize { |
461 | 76.0M | self.transactions.size() |
462 | 76.0M | } |
463 | | |
464 | | /// Get a specific transaction |
465 | 6.35M | pub(crate) fn tx(&self, index: usize) -> Option<&Transaction> { |
466 | 6.35M | self.transactions.get(index) |
467 | 6.35M | } |
468 | | |
469 | | /// Get a specific transaction by its index |
470 | 132M | pub(crate) fn tx_index(&mut self, index: usize) -> Option<&mut Transaction> { |
471 | 132M | self.transactions.get_index(index) |
472 | 132M | } |
473 | | |
474 | | /// Get a specific transaction |
475 | 2.07M | pub(crate) fn tx_mut(&mut self, index: usize) -> Option<&mut Transaction> { |
476 | 2.07M | self.transactions.get_mut(index) |
477 | 2.07M | } |
478 | | |
479 | | /// Handle the current state to be processed. |
480 | 8.16M | pub(crate) fn handle_request_state(&mut self, data: &mut ParserData) -> Result<()> { |
481 | 8.16M | match self.request_state { |
482 | 0 | State::None => Err(HtpStatus::ERROR), |
483 | 466k | State::Idle => self.request_idle(data), |
484 | 189k | State::IgnoreDataAfterHTTP09 => self.request_ignore_data_after_http_0_9(data), |
485 | 1.33M | State::Line => self.request_line(data), |
486 | 434k | State::Protocol => self.request_protocol(data), |
487 | 839k | State::Headers => self.request_headers(data), |
488 | 10.4k | State::ConnectWaitResponse => self.request_connect_wait_response(), |
489 | 422k | State::ConnectCheck => self.request_connect_check(), |
490 | 1.53k | State::ConnectProbeData => self.request_connect_probe_data(data), |
491 | 418k | State::BodyDetermine => self.request_body_determine(), |
492 | 0 | State::BodyChunkedData => self.request_body_chunked_data(data), |
493 | 0 | State::BodyChunkedLength => self.request_body_chunked_length(data), |
494 | 0 | State::BodyChunkedDataEnd => self.request_body_chunked_data_end(data), |
495 | 0 | State::BodyIdentity => self.request_body_identity(data), |
496 | 4.04M | State::Finalize => self.request_finalize(data), |
497 | | // These are only used by response_state |
498 | 0 | _ => Err(HtpStatus::ERROR), |
499 | | } |
500 | 8.16M | } |
501 | | |
502 | | /// Handle the current state to be processed. |
503 | 5.15M | pub(crate) fn handle_response_state(&mut self, data: &mut ParserData) -> Result<()> { |
504 | 5.15M | match self.response_state { |
505 | 0 | State::None => Err(HtpStatus::ERROR), |
506 | 754k | State::Idle => self.response_idle(data), |
507 | 3.13M | State::Line => self.response_line(data), |
508 | 102k | State::Headers => self.response_headers(data), |
509 | 18.4k | State::BodyDetermine => self.response_body_determine(data), |
510 | 0 | State::BodyChunkedData => self.response_body_chunked_data(data), |
511 | 0 | State::BodyChunkedLength => self.response_body_chunked_length(data), |
512 | 0 | State::BodyChunkedDataEnd => self.response_body_chunked_data_end(data), |
513 | 967k | State::Finalize => self.response_finalize(data), |
514 | 172k | State::BodyIdentityStreamClose => self.response_body_identity_stream_close(data), |
515 | 16 | State::BodyIdentityCLKnown => self.response_body_identity_cl_known(data), |
516 | | // These are only used by request_state |
517 | 0 | _ => Err(HtpStatus::ERROR), |
518 | | } |
519 | 5.15M | } |
520 | | |
521 | | /// Closes the connection associated with the supplied parser. |
522 | 9.57k | pub(crate) fn request_close(&mut self, timestamp: Option<OffsetDateTime>) { |
523 | | // Update internal flags |
524 | 9.57k | if self.request_status != HtpStreamState::ERROR { |
525 | 9.47k | self.request_status = HtpStreamState::CLOSED |
526 | 95 | } |
527 | | // Call the parsers one last time, which will allow them |
528 | | // to process the events that depend on stream closure |
529 | 9.57k | self.request_data(ParserData::default(), timestamp); |
530 | 9.57k | } |
531 | | |
532 | | /// Closes the connection associated with the supplied parser. |
533 | 3.64k | pub(crate) fn close(&mut self, timestamp: Option<OffsetDateTime>) { |
534 | | // Close the underlying connection. |
535 | 3.64k | self.conn.close(timestamp); |
536 | | // Update internal flags |
537 | 3.64k | if self.request_status != HtpStreamState::ERROR { |
538 | 3.63k | self.request_status = HtpStreamState::CLOSED |
539 | 14 | } |
540 | 3.64k | if self.response_status != HtpStreamState::ERROR { |
541 | 3.61k | self.response_status = HtpStreamState::CLOSED |
542 | 28 | } |
543 | | // Call the parsers one last time, which will allow them |
544 | | // to process the events that depend on stream closure |
545 | 3.64k | self.request_data(ParserData::default(), timestamp); |
546 | 3.64k | self.response_data(ParserData::default(), timestamp); |
547 | 3.64k | } |
548 | | |
549 | | /// This function is most likely not used and/or not needed. |
550 | 435k | pub(crate) fn request_reset(&mut self) { |
551 | 435k | self.request_content_length = None; |
552 | 435k | self.request_body_data_left = None; |
553 | 435k | self.request_chunk_request_index = self.request_chunk_count; |
554 | 435k | } |
555 | | |
556 | | /// Returns the number of bytes consumed from the current data chunks so far. |
557 | 866k | pub(crate) fn request_data_consumed(&self) -> usize { |
558 | 866k | self.request_bytes_consumed |
559 | 866k | } |
560 | | |
561 | | /// Consume the given number of bytes from the ParserData and update |
562 | | /// the internal counter for how many bytes consumed so far. |
563 | 9.86M | pub(crate) fn request_data_consume(&mut self, input: &ParserData, consumed: usize) { |
564 | 9.86M | input.consume(consumed); |
565 | 9.86M | self.request_bytes_consumed = input.consumed_len(); |
566 | 9.86M | } |
567 | | |
568 | | /// Unconsume the given number of bytes from the ParserData and update the |
569 | | /// the internal counter for how many bytes are consumed. |
570 | | /// If the requested number of bytes is larger than the number of bytes |
571 | | /// already consumed then the parser will be unwound to the beginning. |
572 | 12.8k | pub(crate) fn request_data_unconsume(&mut self, input: &mut ParserData, unconsume: usize) { |
573 | 12.8k | input.unconsume(unconsume); |
574 | 12.8k | self.request_bytes_consumed = input.consumed_len(); |
575 | 12.8k | } |
576 | | |
577 | | /// Consume the given number of bytes from the ParserData and update |
578 | | /// the internal counter for how many bytes consumed so far. |
579 | 4.00M | pub(crate) fn response_data_consume(&mut self, input: &ParserData, consumed: usize) { |
580 | 4.00M | input.consume(consumed); |
581 | 4.00M | self.response_bytes_consumed = input.consumed_len(); |
582 | 4.00M | } |
583 | | |
584 | | /// Unconsume the given number of bytes from the ParserData and update the |
585 | | /// the internal counter for how many bytes are consumed. |
586 | | /// If the requested number of bytes is larger than the number of bytes |
587 | | /// already consumed then the parser will be unwound to the beginning. |
588 | 7.56k | pub(crate) fn response_data_unconsume(&mut self, input: &mut ParserData, unconsume: usize) { |
589 | 7.56k | input.unconsume(unconsume); |
590 | 7.56k | self.response_bytes_consumed = input.consumed_len(); |
591 | 7.56k | } |
592 | | |
593 | | /// Returns the number of bytes consumed from the most recent outbound data chunk. Normally, an invocation |
594 | | /// of response_data() will consume all data from the supplied buffer, but there are circumstances |
595 | | /// where only partial consumption is possible. In such cases DATA_OTHER will be returned. |
596 | | /// Consumed bytes are no longer necessary, but the remainder of the buffer will be saved |
597 | | /// for later. |
598 | 763k | pub(crate) fn response_data_consumed(&self) -> usize { |
599 | 763k | self.response_bytes_consumed |
600 | 763k | } |
601 | | |
602 | | /// Opens connection. |
603 | 14.8k | pub(crate) fn open( |
604 | 14.8k | &mut self, client_addr: Option<IpAddr>, client_port: Option<u16>, |
605 | 14.8k | server_addr: Option<IpAddr>, server_port: Option<u16>, timestamp: Option<OffsetDateTime>, |
606 | 14.8k | ) { |
607 | | // Check connection parser state first. |
608 | 14.8k | if self.request_status != HtpStreamState::NEW || self.response_status != HtpStreamState::NEW |
609 | | { |
610 | 0 | htp_error!( |
611 | 0 | self.logger, |
612 | 0 | HtpLogCode::CONNECTION_ALREADY_OPEN, |
613 | 0 | "Connection is already open" |
614 | 0 | ); |
615 | 0 | return; |
616 | 14.8k | } |
617 | 14.8k | self.conn.open( |
618 | 14.8k | client_addr, |
619 | 14.8k | client_port, |
620 | 14.8k | server_addr, |
621 | 14.8k | server_port, |
622 | 14.8k | timestamp, |
623 | | ); |
624 | 14.8k | self.request_status = HtpStreamState::OPEN; |
625 | 14.8k | self.response_status = HtpStreamState::OPEN; |
626 | 14.8k | } |
627 | | |
628 | | /// Set the user data. |
629 | 14.8k | pub(crate) fn set_user_data(&mut self, data: Box<dyn Any + 'static>) { |
630 | 14.8k | self.user_data = Some(data); |
631 | 14.8k | } |
632 | | |
633 | | /// Get a reference to the user data. |
634 | 8.24M | pub(crate) fn user_data<T: 'static>(&self) -> Option<&T> { |
635 | 8.24M | self.user_data |
636 | 8.24M | .as_ref() |
637 | 8.24M | .and_then(|ud| ud.downcast_ref::<T>()) |
638 | 8.24M | } |
639 | | |
640 | | /// Initialize request parsing, change state to LINE, |
641 | | /// and invoke all registered callbacks. |
642 | | /// |
643 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP if one of the |
644 | | /// callbacks does not want to follow the transaction any more. |
645 | 435k | pub(crate) fn state_request_start(&mut self) -> Result<()> { |
646 | | // Change state into request line parsing. |
647 | 435k | self.request_state = State::Line; |
648 | 435k | let req = self.request_mut(); |
649 | 435k | if req.is_none() { |
650 | 125 | return Err(HtpStatus::ERROR); |
651 | 435k | } |
652 | 435k | req.unwrap().request_progress = HtpRequestProgress::LINE; |
653 | | // Run hook REQUEST_START. |
654 | 435k | self.cfg |
655 | 435k | .hook_request_start |
656 | 435k | .clone() |
657 | 435k | .run_all(self, self.request_index())?; |
658 | 435k | Ok(()) |
659 | 435k | } |
660 | | |
661 | | /// Change transaction state to HEADERS and invoke all |
662 | | /// registered callbacks. |
663 | | /// |
664 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP if one of the |
665 | | /// callbacks does not want to follow the transaction any more. |
666 | 430k | pub(crate) fn state_request_headers(&mut self, input: &mut ParserData) -> Result<()> { |
667 | | // Finalize sending raw header data |
668 | 430k | self.request_receiver_finalize_clear(input)?; |
669 | | // If we're in HTP_REQ_HEADERS that means that this is the |
670 | | // first time we're processing headers in a request. Otherwise, |
671 | | // we're dealing with trailing headers. |
672 | 430k | let req = self.request(); |
673 | 430k | if req.is_none() { |
674 | 0 | return Err(HtpStatus::ERROR); |
675 | 430k | } |
676 | 430k | let request_progress = req.unwrap().request_progress; |
677 | 430k | if request_progress > HtpRequestProgress::HEADERS { |
678 | | // Request trailers. |
679 | | // Run hook HTP_REQUEST_TRAILER. |
680 | 8.17k | self.cfg |
681 | 8.17k | .hook_request_trailer |
682 | 8.17k | .clone() |
683 | 8.17k | .run_all(self, self.request_index())?; |
684 | | // Completed parsing this request; finalize it now. |
685 | 8.17k | self.request_state = State::Finalize; |
686 | 422k | } else if request_progress >= HtpRequestProgress::LINE { |
687 | | // Request headers. |
688 | | // Did this request arrive in multiple data chunks? |
689 | 422k | let req = self.transactions.request_mut().unwrap(); |
690 | 422k | if self.request_chunk_count != self.request_chunk_request_index { |
691 | 43.1k | req.flags.set(HtpFlags::MULTI_PACKET_HEAD) |
692 | 379k | } |
693 | 422k | req.process_request_headers()?; |
694 | | // Run hook REQUEST_HEADERS. |
695 | | #[cfg(test)] |
696 | | self.cfg |
697 | | .hook_request_headers |
698 | | .clone() |
699 | | .run_all(self, self.request_index())?; |
700 | 422k | self.request_initialize_decompressors()?; |
701 | | |
702 | | // We still proceed if the request is invalid. |
703 | 422k | self.request_state = State::ConnectCheck; |
704 | | } else { |
705 | 29 | htp_warn!( |
706 | 29 | self.logger, |
707 | 29 | HtpLogCode::RESPONSE_BODY_INTERNAL_ERROR, |
708 | 29 | format!( |
709 | 29 | "[Internal Error] Invalid tx progress: {:?}", |
710 | 29 | request_progress |
711 | 29 | ) |
712 | 29 | ); |
713 | 29 | return Err(HtpStatus::ERROR); |
714 | | } |
715 | 430k | Ok(()) |
716 | 430k | } |
717 | | |
718 | | /// Change transaction state to PROTOCOL and invoke all |
719 | | /// registered callbacks. |
720 | | /// |
721 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP if one of the |
722 | | /// callbacks does not want to follow the transaction any more. |
723 | 434k | pub(crate) fn state_request_line(&mut self) -> Result<()> { |
724 | 434k | let req = self.request_mut(); |
725 | 434k | if req.is_none() { |
726 | 0 | return Err(HtpStatus::ERROR); |
727 | 434k | } |
728 | 434k | req.unwrap().parse_request_line()?; |
729 | | // Run hook REQUEST_LINE. |
730 | 434k | self.cfg |
731 | 434k | .hook_request_line |
732 | 434k | .clone() |
733 | 434k | .run_all(self, self.request_index())?; |
734 | 434k | let logger = self.logger.clone(); |
735 | 434k | let req = self.request_mut().unwrap(); |
736 | 434k | if let Some(parsed_uri) = req.parsed_uri.as_mut() { |
737 | 434k | let (partial_normalized_uri, complete_normalized_uri) = |
738 | 434k | parsed_uri.generate_normalized_uri(Some(logger)); |
739 | 434k | req.partial_normalized_uri = partial_normalized_uri; |
740 | 434k | req.complete_normalized_uri = complete_normalized_uri; |
741 | 434k | } |
742 | | // Move on to the next phase. |
743 | 434k | self.request_state = State::Protocol; |
744 | 434k | Ok(()) |
745 | 434k | } |
746 | | |
747 | | /// Advance state after processing request headers. |
748 | | /// |
749 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP |
750 | | /// if one of the callbacks does not want to follow the transaction any more. |
751 | 433k | pub(crate) fn state_request_complete(&mut self, input: &mut ParserData) -> Result<()> { |
752 | 433k | let req = self.request_mut(); |
753 | 433k | if req.is_none() { |
754 | 0 | return Err(HtpStatus::ERROR); |
755 | 433k | } |
756 | 433k | let req = req.unwrap(); |
757 | 433k | if req.request_progress != HtpRequestProgress::COMPLETE { |
758 | | // Finalize request body. |
759 | 431k | if req.request_has_body() { |
760 | 0 | self.request_body_data(None)?; |
761 | 431k | } |
762 | 431k | self.request_mut().unwrap().request_progress = HtpRequestProgress::COMPLETE; |
763 | | // Run hook REQUEST_COMPLETE. |
764 | 431k | self.cfg |
765 | 431k | .hook_request_complete |
766 | 431k | .clone() |
767 | 431k | .run_all(self, self.request_index())?; |
768 | | |
769 | | // Clear request data |
770 | 431k | self.request_receiver_finalize_clear(input)?; |
771 | 2.37k | } |
772 | | // Determine what happens next, and remove this transaction from the parser. |
773 | 433k | self.request_state = if self.request().unwrap().is_protocol_0_9 { |
774 | 3.70k | State::IgnoreDataAfterHTTP09 |
775 | | } else { |
776 | 430k | State::Idle |
777 | | }; |
778 | | // Check if the entire transaction is complete. |
779 | 433k | self.finalize(self.request_index())?; |
780 | 433k | self.request_next(); |
781 | 433k | Ok(()) |
782 | 433k | } |
783 | | |
784 | | /// Determine if the transaction is complete and run any hooks. |
785 | 813k | fn finalize(&mut self, _tx_index: usize) -> Result<()> { |
786 | | #[cfg(test)] |
787 | | if let Some(tx) = self.tx(_tx_index) { |
788 | | if !tx.is_complete() { |
789 | | return Ok(()); |
790 | | } |
791 | | // Disconnect transaction from the parser. |
792 | | // Run hook TRANSACTION_COMPLETE. |
793 | | self.cfg |
794 | | .hook_transaction_complete |
795 | | .clone() |
796 | | .run_all(self, _tx_index)?; |
797 | | } |
798 | 813k | Ok(()) |
799 | 813k | } |
800 | | |
801 | | /// Advance state to LINE, or BODY if http version is 0.9. |
802 | | /// |
803 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP |
804 | | /// if one of the callbacks does not want to follow the transaction any more. |
805 | 382k | pub(crate) fn state_response_start(&mut self) -> Result<()> { |
806 | | // Change state into response line parsing, except if we're following |
807 | | // a HTTP/0.9 request (no status line or response headers). |
808 | 382k | let tx = self.response_mut(); |
809 | 382k | if tx.is_none() { |
810 | 0 | return Err(HtpStatus::ERROR); |
811 | 382k | } |
812 | 382k | let tx = tx.unwrap(); |
813 | | |
814 | 382k | if tx.is_protocol_0_9 { |
815 | 103 | tx.response_transfer_coding = HtpTransferCoding::Identity; |
816 | 103 | tx.response_content_encoding_processing = HtpContentEncoding::None; |
817 | 103 | tx.response_progress = HtpResponseProgress::BODY; |
818 | 103 | self.response_state = State::BodyIdentityStreamClose; |
819 | 103 | self.response_body_data_left = None |
820 | | } else { |
821 | 382k | tx.response_progress = HtpResponseProgress::LINE; |
822 | 382k | self.response_state = State::Line |
823 | | } |
824 | | // Run hook RESPONSE_START. |
825 | 382k | self.cfg |
826 | 382k | .hook_response_start |
827 | 382k | .clone() |
828 | 382k | .run_all(self, self.response_index())?; |
829 | | // If at this point we have no method and no uri and our status |
830 | | // is still REQ_LINE, we likely have timed out request |
831 | | // or a overly long request |
832 | 382k | let tx = self.response_mut().unwrap(); |
833 | 382k | if tx.request_method.is_none() |
834 | 346k | && tx.request_uri.is_none() |
835 | 3.48k | && self.request_state == State::Line |
836 | 3.48k | { |
837 | 3.48k | htp_warn!( |
838 | 3.48k | self.logger, |
839 | 3.48k | HtpLogCode::REQUEST_LINE_INCOMPLETE, |
840 | 3.48k | "Request line incomplete" |
841 | 3.48k | ); |
842 | 379k | } |
843 | 382k | Ok(()) |
844 | 382k | } |
845 | | |
846 | | /// Advance state after processing response headers. |
847 | | /// |
848 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP |
849 | | /// if one of the callbacks does not want to follow the transaction any more. |
850 | 16.6k | pub(crate) fn state_response_headers(&mut self, input: &mut ParserData) -> Result<()> { |
851 | | // Finalize sending raw header data. |
852 | 16.6k | self.response_receiver_finalize_clear(input)?; |
853 | | // Run hook RESPONSE_HEADERS. |
854 | | #[cfg(test)] |
855 | | self.cfg |
856 | | .hook_response_headers |
857 | | .clone() |
858 | | .run_all(self, self.response_index())?; |
859 | 16.6k | self.response_initialize_decompressors() |
860 | 16.6k | } |
861 | | |
862 | | /// Change transaction state to RESPONSE_LINE and invoke registered callbacks. |
863 | | /// |
864 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP |
865 | | /// if one of the callbacks does not want to follow the transaction any more. |
866 | 19.9k | pub(crate) fn state_response_line(&mut self) -> Result<()> { |
867 | | // Is the response line valid? |
868 | 19.9k | let tx = self.response_mut(); |
869 | 19.9k | if tx.is_none() { |
870 | 0 | return Err(HtpStatus::ERROR); |
871 | 19.9k | } |
872 | 19.9k | let tx = tx.unwrap(); |
873 | | |
874 | 19.9k | tx.validate_response_line(); |
875 | | #[cfg(test)] |
876 | | let index = tx.index; |
877 | | // Run hook HTP_RESPONSE_LINE |
878 | | #[cfg(test)] |
879 | | return self.cfg.hook_response_line.clone().run_all(self, index); |
880 | | #[cfg(not(test))] |
881 | 19.9k | return Ok(()); |
882 | 19.9k | } |
883 | | |
884 | | /// Change transaction state to COMPLETE and invoke registered callbacks. |
885 | | /// |
886 | | /// Returns HtpStatus::OK on success; HtpStatus::ERROR on error, HtpStatus::STOP |
887 | | /// if one of the callbacks does not want to follow the transaction any more. |
888 | 381k | pub(crate) fn state_response_complete(&mut self, input: &mut ParserData) -> Result<()> { |
889 | 381k | let response_index = self.response_index(); |
890 | 381k | let tx = self.response_mut(); |
891 | 381k | if tx.is_none() { |
892 | 0 | return Err(HtpStatus::ERROR); |
893 | 381k | } |
894 | 381k | let tx = tx.unwrap(); |
895 | 381k | if tx.response_progress != HtpResponseProgress::COMPLETE { |
896 | 380k | tx.response_progress = HtpResponseProgress::COMPLETE; |
897 | | // Run the last RESPONSE_BODY_DATA HOOK, but only if there was a response body present. |
898 | 380k | if tx.response_transfer_coding != HtpTransferCoding::NoBody { |
899 | 366k | let _ = self.response_body_data(None); |
900 | 366k | } |
901 | | // Run hook RESPONSE_COMPLETE. |
902 | 380k | self.cfg |
903 | 380k | .hook_response_complete |
904 | 380k | .clone() |
905 | 380k | .run_all(self, response_index)?; |
906 | | |
907 | | // Clear the data receivers hook if any |
908 | 380k | self.response_receiver_finalize_clear(input)?; |
909 | 694 | } |
910 | | // Check if we want to signal the caller to send request data |
911 | 381k | self.request_parser_check_waiting()?; |
912 | | // Otherwise finalize the transaction |
913 | 379k | self.finalize(response_index)?; |
914 | 379k | self.response_next(); |
915 | 379k | self.response_state = State::Idle; |
916 | 379k | Ok(()) |
917 | 381k | } |
918 | | |
919 | | /// Check if we had previously signalled the caller to give us response |
920 | | /// data, and now we are ready to receive it |
921 | 381k | fn request_parser_check_waiting(&mut self) -> Result<()> { |
922 | | // Check if the inbound parser is waiting on us. If it is, that means that |
923 | | // there might be request data that the inbound parser hasn't consumed yet. |
924 | | // If we don't stop parsing we might encounter a response without a request, |
925 | | // which is why we want to return straight away before processing any data. |
926 | | // |
927 | | // This situation will occur any time the parser needs to see the server |
928 | | // respond to a particular situation before it can decide how to proceed. For |
929 | | // example, when a CONNECT is sent, different paths are used when it is accepted |
930 | | // and when it is not accepted. |
931 | | // |
932 | | // It is not enough to check only in_status here. Because of pipelining, it's possible |
933 | | // that many inbound transactions have been processed, and that the parser is |
934 | | // waiting on a response that we have not seen yet. |
935 | 381k | if self.response_status == HtpStreamState::DATA_OTHER |
936 | 5 | && self.response_index() == self.request_index() |
937 | | { |
938 | 5 | return Err(HtpStatus::DATA_OTHER); |
939 | 381k | } |
940 | | |
941 | | // Do we have a signal to yield to inbound processing at |
942 | | // the end of the next transaction? |
943 | 381k | if self.response_data_other_at_tx_end { |
944 | | // We do. Let's yield then. |
945 | 2.05k | self.response_data_other_at_tx_end = false; |
946 | 2.05k | if self.response_index() == self.request_index() { |
947 | 1.72k | return Err(HtpStatus::DATA_OTHER); |
948 | 331 | } |
949 | 379k | } |
950 | 379k | Ok(()) |
951 | 381k | } |
952 | | |
953 | | /// Remove the given transaction from the parser |
954 | 354k | pub(crate) fn remove_tx(&mut self, tx_id: usize) { |
955 | 354k | self.transactions.remove(tx_id); |
956 | 354k | } |
957 | | } |