/src/suricata7/rust/src/applayertemplate/template.rs
Line | Count | Source |
1 | | /* Copyright (C) 2018-2022 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | use super::parser; |
19 | | use crate::applayer::{self, *}; |
20 | | use crate::conf::conf_get; |
21 | | use crate::core::{AppProto, Flow, ALPROTO_UNKNOWN, IPPROTO_TCP}; |
22 | | use nom7 as nom; |
23 | | use std; |
24 | | use std::collections::VecDeque; |
25 | | use std::ffi::CString; |
26 | | use std::os::raw::{c_char, c_int, c_void}; |
27 | | |
28 | | static mut TEMPLATE_MAX_TX: usize = 256; |
29 | | |
30 | | static mut ALPROTO_TEMPLATE: AppProto = ALPROTO_UNKNOWN; |
31 | | |
32 | | #[derive(AppLayerEvent)] |
33 | | enum TemplateEvent { |
34 | | TooManyTransactions, |
35 | | } |
36 | | |
37 | | pub struct TemplateTransaction { |
38 | | tx_id: u64, |
39 | | pub request: Option<String>, |
40 | | pub response: Option<String>, |
41 | | |
42 | | tx_data: AppLayerTxData, |
43 | | } |
44 | | |
45 | | impl Default for TemplateTransaction { |
46 | 0 | fn default() -> Self { |
47 | 0 | Self::new() |
48 | 0 | } |
49 | | } |
50 | | |
51 | | impl TemplateTransaction { |
52 | 41.0k | pub fn new() -> TemplateTransaction { |
53 | 41.0k | Self { |
54 | 41.0k | tx_id: 0, |
55 | 41.0k | request: None, |
56 | 41.0k | response: None, |
57 | 41.0k | tx_data: AppLayerTxData::new(), |
58 | 41.0k | } |
59 | 41.0k | } |
60 | | } |
61 | | |
62 | | impl Transaction for TemplateTransaction { |
63 | 6.01M | fn id(&self) -> u64 { |
64 | 6.01M | self.tx_id |
65 | 6.01M | } |
66 | | } |
67 | | |
68 | | #[derive(Default)] |
69 | | pub struct TemplateState { |
70 | | state_data: AppLayerStateData, |
71 | | tx_id: u64, |
72 | | transactions: VecDeque<TemplateTransaction>, |
73 | | request_gap: bool, |
74 | | response_gap: bool, |
75 | | } |
76 | | |
77 | | impl State<TemplateTransaction> for TemplateState { |
78 | 2.04M | fn get_transaction_count(&self) -> usize { |
79 | 2.04M | self.transactions.len() |
80 | 2.04M | } |
81 | | |
82 | 3.97M | fn get_transaction_by_index(&self, index: usize) -> Option<&TemplateTransaction> { |
83 | 3.97M | self.transactions.get(index) |
84 | 3.97M | } |
85 | | } |
86 | | |
87 | | impl TemplateState { |
88 | 2.87k | pub fn new() -> Self { |
89 | 2.87k | Default::default() |
90 | 2.87k | } |
91 | | |
92 | | // Free a transaction by ID. |
93 | 37.4k | fn free_tx(&mut self, tx_id: u64) { |
94 | 37.4k | let len = self.transactions.len(); |
95 | 37.4k | let mut found = false; |
96 | 37.4k | let mut index = 0; |
97 | 37.4k | for i in 0..len { |
98 | 37.4k | let tx = &self.transactions[i]; |
99 | 37.4k | if tx.tx_id == tx_id + 1 { |
100 | 37.4k | found = true; |
101 | 37.4k | index = i; |
102 | 37.4k | break; |
103 | 0 | } |
104 | | } |
105 | 37.4k | if found { |
106 | 37.4k | self.transactions.remove(index); |
107 | 37.4k | } |
108 | 37.4k | } |
109 | | |
110 | 327 | pub fn get_tx(&mut self, tx_id: u64) -> Option<&TemplateTransaction> { |
111 | 327 | self.transactions.iter().find(|tx| tx.tx_id == tx_id + 1) |
112 | 327 | } |
113 | | |
114 | 41.0k | fn new_tx(&mut self) -> TemplateTransaction { |
115 | 41.0k | let mut tx = TemplateTransaction::new(); |
116 | 41.0k | self.tx_id += 1; |
117 | 41.0k | tx.tx_id = self.tx_id; |
118 | 41.0k | return tx; |
119 | 41.0k | } |
120 | | |
121 | 39.4k | fn find_request(&mut self) -> Option<&mut TemplateTransaction> { |
122 | 60.7k | self.transactions.iter_mut().find(|tx| tx.response.is_none()) |
123 | 39.4k | } |
124 | | |
125 | 38.7k | fn parse_request(&mut self, input: &[u8]) -> AppLayerResult { |
126 | | // We're not interested in empty requests. |
127 | 38.7k | if input.is_empty() { |
128 | 0 | return AppLayerResult::ok(); |
129 | 38.7k | } |
130 | | |
131 | | // If there was gap, check we can sync up again. |
132 | 38.7k | if self.request_gap { |
133 | 108 | if probe(input).is_err() { |
134 | | // The parser now needs to decide what to do as we are not in sync. |
135 | | // For this template, we'll just try again next time. |
136 | 102 | return AppLayerResult::ok(); |
137 | 6 | } |
138 | | |
139 | | // It looks like we're in sync with a message header, clear gap |
140 | | // state and keep parsing. |
141 | 6 | self.request_gap = false; |
142 | 38.6k | } |
143 | | |
144 | 38.6k | let mut start = input; |
145 | 79.6k | while !start.is_empty() { |
146 | 77.1k | match parser::parse_message(start) { |
147 | 41.0k | Ok((rem, request)) => { |
148 | 41.0k | start = rem; |
149 | | |
150 | 41.0k | SCLogNotice!("Request: {}", request); |
151 | 41.0k | let mut tx = self.new_tx(); |
152 | 41.0k | tx.request = Some(request); |
153 | 41.0k | if self.transactions.len() >= unsafe {TEMPLATE_MAX_TX} { |
154 | 0 | tx.tx_data.set_event(TemplateEvent::TooManyTransactions as u8); |
155 | 41.0k | } |
156 | 41.0k | self.transactions.push_back(tx); |
157 | 41.0k | if self.transactions.len() >= unsafe {TEMPLATE_MAX_TX} { |
158 | 2 | return AppLayerResult::err(); |
159 | 41.0k | } |
160 | | } |
161 | | Err(nom::Err::Incomplete(_)) => { |
162 | | // Not enough data. This parser doesn't give us a good indication |
163 | | // of how much data is missing so just ask for one more byte so the |
164 | | // parse is called as soon as more data is received. |
165 | 36.1k | let consumed = input.len() - start.len(); |
166 | 36.1k | let needed = start.len() + 1; |
167 | 36.1k | return AppLayerResult::incomplete(consumed as u32, needed as u32); |
168 | | } |
169 | | Err(_) => { |
170 | 48 | return AppLayerResult::err(); |
171 | | } |
172 | | } |
173 | | } |
174 | | |
175 | | // Input was fully consumed. |
176 | 2.50k | return AppLayerResult::ok(); |
177 | 38.7k | } |
178 | | |
179 | 37.4k | fn parse_response(&mut self, input: &[u8]) -> AppLayerResult { |
180 | | // We're not interested in empty responses. |
181 | 37.4k | if input.is_empty() { |
182 | 215 | return AppLayerResult::ok(); |
183 | 37.1k | } |
184 | | |
185 | 37.1k | if self.response_gap { |
186 | 102 | if probe(input).is_err() { |
187 | | // The parser now needs to decide what to do as we are not in sync. |
188 | | // For this template, we'll just try again next time. |
189 | 102 | return AppLayerResult::ok(); |
190 | 0 | } |
191 | | |
192 | | // It looks like we're in sync with a message header, clear gap |
193 | | // state and keep parsing. |
194 | 0 | self.response_gap = false; |
195 | 37.0k | } |
196 | 37.0k | let mut start = input; |
197 | 76.5k | while !start.is_empty() { |
198 | 75.7k | match parser::parse_message(start) { |
199 | 39.4k | Ok((rem, response)) => { |
200 | 39.4k | start = rem; |
201 | | |
202 | 39.4k | if let Some(tx) = self.find_request() { |
203 | 37.6k | tx.tx_data.updated_tc = true; |
204 | 37.6k | tx.response = Some(response); |
205 | 37.6k | SCLogNotice!("Found response for request:"); |
206 | 37.6k | SCLogNotice!("- Request: {:?}", tx.request); |
207 | 37.6k | SCLogNotice!("- Response: {:?}", tx.response); |
208 | 1.75k | } |
209 | | } |
210 | | Err(nom::Err::Incomplete(_)) => { |
211 | 36.2k | let consumed = input.len() - start.len(); |
212 | 36.2k | let needed = start.len() + 1; |
213 | 36.2k | return AppLayerResult::incomplete(consumed as u32, needed as u32); |
214 | | } |
215 | | Err(_) => { |
216 | 59 | return AppLayerResult::err(); |
217 | | } |
218 | | } |
219 | | } |
220 | | |
221 | | // All input was fully consumed. |
222 | 780 | return AppLayerResult::ok(); |
223 | 37.4k | } |
224 | | |
225 | 108 | fn on_request_gap(&mut self, _size: u32) { |
226 | 108 | self.request_gap = true; |
227 | 108 | } |
228 | | |
229 | 3 | fn on_response_gap(&mut self, _size: u32) { |
230 | 3 | self.response_gap = true; |
231 | 3 | } |
232 | | } |
233 | | |
234 | | /// Probe for a valid header. |
235 | | /// |
236 | | /// As this template protocol uses messages prefixed with the size |
237 | | /// as a string followed by a ':', we look at up to the first 10 |
238 | | /// characters for that pattern. |
239 | 5.08k | fn probe(input: &[u8]) -> nom::IResult<&[u8], ()> { |
240 | 5.08k | let size = std::cmp::min(10, input.len()); |
241 | 5.08k | let (rem, prefix) = nom::bytes::complete::take(size)(input)?; |
242 | 5.08k | nom::sequence::terminated( |
243 | 5.08k | nom::bytes::complete::take_while1(nom::character::is_digit), |
244 | 5.08k | nom::bytes::complete::tag(":"), |
245 | 5.08k | )(prefix)?; |
246 | 3.87k | Ok((rem, ())) |
247 | 5.08k | } |
248 | | |
249 | | // C exports. |
250 | | |
251 | | /// C entry point for a probing parser. |
252 | 16.7k | unsafe extern "C" fn rs_template_probing_parser( |
253 | 16.7k | _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, |
254 | 16.7k | ) -> AppProto { |
255 | | // Need at least 2 bytes. |
256 | 16.7k | if input_len > 1 && !input.is_null() { |
257 | 4.87k | let slice = build_slice!(input, input_len as usize); |
258 | 4.87k | if probe(slice).is_ok() { |
259 | 3.86k | return ALPROTO_TEMPLATE; |
260 | 1.00k | } |
261 | 11.9k | } |
262 | 12.9k | return ALPROTO_UNKNOWN; |
263 | 16.7k | } |
264 | | |
265 | 2.87k | extern "C" fn rs_template_state_new( |
266 | 2.87k | _orig_state: *mut c_void, _orig_proto: AppProto, |
267 | 2.87k | ) -> *mut c_void { |
268 | 2.87k | let state = TemplateState::new(); |
269 | 2.87k | let boxed = Box::new(state); |
270 | 2.87k | return Box::into_raw(boxed) as *mut c_void; |
271 | 2.87k | } |
272 | | |
273 | 2.87k | unsafe extern "C" fn rs_template_state_free(state: *mut c_void) { |
274 | 2.87k | std::mem::drop(Box::from_raw(state as *mut TemplateState)); |
275 | 2.87k | } |
276 | | |
277 | 37.4k | unsafe extern "C" fn rs_template_state_tx_free(state: *mut c_void, tx_id: u64) { |
278 | 37.4k | let state = cast_pointer!(state, TemplateState); |
279 | 37.4k | state.free_tx(tx_id); |
280 | 37.4k | } |
281 | | |
282 | 43.0k | unsafe extern "C" fn rs_template_parse_request( |
283 | 43.0k | _flow: *const Flow, state: *mut c_void, pstate: *mut c_void, stream_slice: StreamSlice, |
284 | 43.0k | _data: *const c_void, |
285 | 43.0k | ) -> AppLayerResult { |
286 | 43.0k | let eof = AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0; |
287 | | |
288 | 43.0k | if eof { |
289 | | // If needed, handle EOF, or pass it into the parser. |
290 | 4.20k | return AppLayerResult::ok(); |
291 | 38.8k | } |
292 | | |
293 | 38.8k | let state = cast_pointer!(state, TemplateState); |
294 | | |
295 | 38.8k | if stream_slice.is_gap() { |
296 | | // Here we have a gap signaled by the input being null, but a greater |
297 | | // than 0 input_len which provides the size of the gap. |
298 | 108 | state.on_request_gap(stream_slice.gap_size()); |
299 | 108 | AppLayerResult::ok() |
300 | | } else { |
301 | 38.7k | let buf = stream_slice.as_slice(); |
302 | 38.7k | state.parse_request(buf) |
303 | | } |
304 | 43.0k | } |
305 | | |
306 | 37.4k | unsafe extern "C" fn rs_template_parse_response( |
307 | 37.4k | _flow: *const Flow, state: *mut c_void, pstate: *mut c_void, stream_slice: StreamSlice, |
308 | 37.4k | _data: *const c_void, |
309 | 37.4k | ) -> AppLayerResult { |
310 | 37.4k | let _eof = AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0; |
311 | 37.4k | let state = cast_pointer!(state, TemplateState); |
312 | | |
313 | 37.4k | if stream_slice.is_gap() { |
314 | | // Here we have a gap signaled by the input being null, but a greater |
315 | | // than 0 input_len which provides the size of the gap. |
316 | 3 | state.on_response_gap(stream_slice.gap_size()); |
317 | 3 | AppLayerResult::ok() |
318 | | } else { |
319 | 37.4k | let buf = stream_slice.as_slice(); |
320 | 37.4k | state.parse_response(buf) |
321 | | } |
322 | 37.4k | } |
323 | | |
324 | 327 | unsafe extern "C" fn rs_template_state_get_tx(state: *mut c_void, tx_id: u64) -> *mut c_void { |
325 | 327 | let state = cast_pointer!(state, TemplateState); |
326 | 327 | match state.get_tx(tx_id) { |
327 | 265 | Some(tx) => { |
328 | 265 | return tx as *const _ as *mut _; |
329 | | } |
330 | | None => { |
331 | 62 | return std::ptr::null_mut(); |
332 | | } |
333 | | } |
334 | 327 | } |
335 | | |
336 | 250k | unsafe extern "C" fn rs_template_state_get_tx_count(state: *mut c_void) -> u64 { |
337 | 250k | let state = cast_pointer!(state, TemplateState); |
338 | 250k | return state.tx_id; |
339 | 250k | } |
340 | | |
341 | 2.08M | unsafe extern "C" fn rs_template_tx_get_alstate_progress(tx: *mut c_void, _direction: u8) -> c_int { |
342 | 2.08M | let tx = cast_pointer!(tx, TemplateTransaction); |
343 | | |
344 | | // Transaction is done if we have a response. |
345 | 2.08M | if tx.response.is_some() { |
346 | 74.9k | return 1; |
347 | 2.00M | } |
348 | 2.00M | return 0; |
349 | 2.08M | } |
350 | | |
351 | | /// Get the request buffer for a transaction from C. |
352 | | /// |
353 | | /// No required for parsing, but an example function for retrieving a |
354 | | /// pointer to the request buffer from C for detection. |
355 | | #[no_mangle] |
356 | 1 | pub unsafe extern "C" fn rs_template_get_request_buffer( |
357 | 1 | tx: *mut c_void, buf: *mut *const u8, len: *mut u32, |
358 | 1 | ) -> u8 { |
359 | 1 | let tx = cast_pointer!(tx, TemplateTransaction); |
360 | 1 | if let Some(ref request) = tx.request { |
361 | 1 | if !request.is_empty() { |
362 | 1 | *len = request.len() as u32; |
363 | 1 | *buf = request.as_ptr(); |
364 | 1 | return 1; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | return 0; |
368 | 1 | } |
369 | | |
370 | | /// Get the response buffer for a transaction from C. |
371 | | #[no_mangle] |
372 | 5 | pub unsafe extern "C" fn rs_template_get_response_buffer( |
373 | 5 | tx: *mut c_void, buf: *mut *const u8, len: *mut u32, |
374 | 5 | ) -> u8 { |
375 | 5 | let tx = cast_pointer!(tx, TemplateTransaction); |
376 | 5 | if let Some(ref response) = tx.response { |
377 | 0 | if !response.is_empty() { |
378 | 0 | *len = response.len() as u32; |
379 | 0 | *buf = response.as_ptr(); |
380 | 0 | return 1; |
381 | 0 | } |
382 | 5 | } |
383 | 5 | return 0; |
384 | 5 | } |
385 | | |
386 | | export_tx_data_get!(rs_template_get_tx_data, TemplateTransaction); |
387 | | export_state_data_get!(rs_template_get_state_data, TemplateState); |
388 | | |
389 | | // Parser name as a C style string. |
390 | | const PARSER_NAME: &[u8] = b"template\0"; |
391 | | |
392 | | #[no_mangle] |
393 | 34 | pub unsafe extern "C" fn rs_template_register_parser() { |
394 | | /* TEMPLATE_START_REMOVE */ |
395 | 34 | if crate::conf::conf_get_node("app-layer.protocols.template").is_none() { |
396 | 1 | return; |
397 | 33 | } |
398 | | /* TEMPLATE_END_REMOVE */ |
399 | | |
400 | 33 | let default_port = CString::new("[7000]").unwrap(); |
401 | 33 | let parser = RustParser { |
402 | 33 | name: PARSER_NAME.as_ptr() as *const c_char, |
403 | 33 | default_port: default_port.as_ptr(), |
404 | 33 | ipproto: IPPROTO_TCP, |
405 | 33 | probe_ts: Some(rs_template_probing_parser), |
406 | 33 | probe_tc: Some(rs_template_probing_parser), |
407 | 33 | min_depth: 0, |
408 | 33 | max_depth: 16, |
409 | 33 | state_new: rs_template_state_new, |
410 | 33 | state_free: rs_template_state_free, |
411 | 33 | tx_free: rs_template_state_tx_free, |
412 | 33 | parse_ts: rs_template_parse_request, |
413 | 33 | parse_tc: rs_template_parse_response, |
414 | 33 | get_tx_count: rs_template_state_get_tx_count, |
415 | 33 | get_tx: rs_template_state_get_tx, |
416 | 33 | tx_comp_st_ts: 1, |
417 | 33 | tx_comp_st_tc: 1, |
418 | 33 | tx_get_progress: rs_template_tx_get_alstate_progress, |
419 | 33 | get_eventinfo: Some(TemplateEvent::get_event_info), |
420 | 33 | get_eventinfo_byid: Some(TemplateEvent::get_event_info_by_id), |
421 | 33 | localstorage_new: None, |
422 | 33 | localstorage_free: None, |
423 | 33 | get_tx_files: None, |
424 | 33 | get_tx_iterator: Some( |
425 | 33 | applayer::state_get_tx_iterator::<TemplateState, TemplateTransaction>, |
426 | 33 | ), |
427 | 33 | get_tx_data: rs_template_get_tx_data, |
428 | 33 | get_state_data: rs_template_get_state_data, |
429 | 33 | apply_tx_config: None, |
430 | 33 | flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS, |
431 | 33 | truncate: None, |
432 | 33 | get_frame_id_by_name: None, |
433 | 33 | get_frame_name_by_id: None, |
434 | 33 | }; |
435 | | |
436 | 33 | let ip_proto_str = CString::new("tcp").unwrap(); |
437 | | |
438 | 33 | if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
439 | 33 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
440 | 33 | ALPROTO_TEMPLATE = alproto; |
441 | 33 | if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
442 | 33 | let _ = AppLayerRegisterParser(&parser, alproto); |
443 | 33 | } |
444 | 33 | if let Some(val) = conf_get("app-layer.protocols.template.max-tx") { |
445 | 0 | if let Ok(v) = val.parse::<usize>() { |
446 | 0 | TEMPLATE_MAX_TX = v; |
447 | 0 | } else { |
448 | 0 | SCLogError!("Invalid value for template.max-tx"); |
449 | | } |
450 | 33 | } |
451 | 33 | SCLogNotice!("Rust template parser registered."); |
452 | | } else { |
453 | 0 | SCLogNotice!("Protocol detector and parser disabled for TEMPLATE."); |
454 | | } |
455 | 34 | } |
456 | | |
457 | | #[cfg(test)] |
458 | | mod test { |
459 | | use super::*; |
460 | | |
461 | | #[test] |
462 | | fn test_probe() { |
463 | | assert!(probe(b"1").is_err()); |
464 | | assert!(probe(b"1:").is_ok()); |
465 | | assert!(probe(b"123456789:").is_ok()); |
466 | | assert!(probe(b"0123456789:").is_err()); |
467 | | } |
468 | | |
469 | | #[test] |
470 | | fn test_incomplete() { |
471 | | let mut state = TemplateState::new(); |
472 | | let buf = b"5:Hello3:bye"; |
473 | | |
474 | | let r = state.parse_request(&buf[0..0]); |
475 | | assert_eq!( |
476 | | r, |
477 | | AppLayerResult { |
478 | | status: 0, |
479 | | consumed: 0, |
480 | | needed: 0 |
481 | | } |
482 | | ); |
483 | | |
484 | | let r = state.parse_request(&buf[0..1]); |
485 | | assert_eq!( |
486 | | r, |
487 | | AppLayerResult { |
488 | | status: 1, |
489 | | consumed: 0, |
490 | | needed: 2 |
491 | | } |
492 | | ); |
493 | | |
494 | | let r = state.parse_request(&buf[0..2]); |
495 | | assert_eq!( |
496 | | r, |
497 | | AppLayerResult { |
498 | | status: 1, |
499 | | consumed: 0, |
500 | | needed: 3 |
501 | | } |
502 | | ); |
503 | | |
504 | | // This is the first message and only the first message. |
505 | | let r = state.parse_request(&buf[0..7]); |
506 | | assert_eq!( |
507 | | r, |
508 | | AppLayerResult { |
509 | | status: 0, |
510 | | consumed: 0, |
511 | | needed: 0 |
512 | | } |
513 | | ); |
514 | | |
515 | | // The first message and a portion of the second. |
516 | | let r = state.parse_request(&buf[0..9]); |
517 | | assert_eq!( |
518 | | r, |
519 | | AppLayerResult { |
520 | | status: 1, |
521 | | consumed: 7, |
522 | | needed: 3 |
523 | | } |
524 | | ); |
525 | | } |
526 | | } |