Coverage Report

Created: 2026-06-30 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}