• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jasonish / suricata / 22802647571

07 Mar 2026 04:23PM UTC coverage: 75.827% (-3.4%) from 79.233%
22802647571

push

github

jasonish
github-ci: add schema ordering check for yaml schema

253365 of 334137 relevant lines covered (75.83%)

3384729.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.54
/rust/src/applayer.rs
1
/* Copyright (C) 2017-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
//! Parser registration functions and common interface module.
19

20
use std;
21
use crate::core::{self,AppLayerEventType, STREAM_TOSERVER};
22
use crate::direction::Direction;
23
use crate::flow::Flow;
24
use std::os::raw::{c_void,c_char,c_int};
25
use std::ffi::CStr;
26

27
// Make the AppLayerEvent derive macro available to users importing
28
// AppLayerEvent from this module.
29
pub use suricata_derive::AppLayerEvent;
30
use suricata_sys::sys::{
31
    AppLayerGetTxIterState, AppLayerParserState, AppProto,
32
};
33

34
pub use suricata_sys::sys::{
35
    AppLayerGetFileState, AppLayerGetTxIterTuple, AppLayerResult, AppLayerStateData,
36
    AppLayerTxConfig, StreamSlice,
37
};
38

39
#[cfg(not(test))]
40
use suricata_sys::sys::SCAppLayerDecoderEventsSetEventRaw;
41

42
pub use suricata_ffi::cast_pointer;
43

44
pub trait StreamSliceRust {
45
    #[cfg(test)]
46
    fn from_slice(slice: &[u8], flags: u8, offset: u64) -> Self;
47
    fn is_gap(&self) -> bool;
48
    fn gap_size(&self) -> u32;
49
    fn as_slice(&self) -> &[u8];
50
    fn is_empty(&self) -> bool;
51
    fn len(&self) -> u32;
52
    fn offset_from(&self, slice: &[u8]) -> u32;
53
    fn flags(&self) -> u8;
54
}
55

56
impl StreamSliceRust for StreamSlice {
57
    /// Create a StreamSlice from a Rust slice. Useful in unit tests.
58
    #[cfg(test)]
59
    fn from_slice(slice: &[u8], flags: u8, offset: u64) -> Self {
32✔
60
        Self {
32✔
61
            input: slice.as_ptr(),
32✔
62
            input_len: slice.len() as u32,
32✔
63
            flags,
32✔
64
            offset
32✔
65
        }
32✔
66
    }
32✔
67

68
    fn is_gap(&self) -> bool {
49,075✔
69
        self.input.is_null() && self.input_len > 0
49,075✔
70
    }
49,075✔
71
    fn gap_size(&self) -> u32 {
6✔
72
        self.input_len
6✔
73
    }
6✔
74
    fn as_slice(&self) -> &[u8] {
379,682✔
75
        if self.input.is_null() && self.input_len == 0 {
379,682✔
76
            return &[];
787✔
77
        }
378,895✔
78
        unsafe { std::slice::from_raw_parts(self.input, self.input_len as usize) }
378,895✔
79
    }
379,682✔
80
    fn is_empty(&self) -> bool {
2,471✔
81
        self.input_len == 0
2,471✔
82
    }
2,471✔
83
    fn len(&self) -> u32 {
597✔
84
        self.input_len
597✔
85
    }
597✔
86
    fn offset_from(&self, slice: &[u8]) -> u32 {
597✔
87
        self.len() - slice.len() as u32
597✔
88
    }
597✔
89
    fn flags(&self) -> u8 {
338,651✔
90
        self.flags
338,651✔
91
    }
338,651✔
92
}
93

94
#[derive(Debug, Default, Eq, PartialEq)]
95
pub struct AppLayerTxData(pub suricata_sys::sys::AppLayerTxData);
96

97
impl AppLayerTxData {
98
    /// Create new AppLayerTxData for a transaction that covers both
99
    /// directions.
100
    pub fn new() -> Self {
42,089✔
101
        Self (suricata_sys::sys::AppLayerTxData {
42,089✔
102
            updated_tc: true,
42,089✔
103
            updated_ts: true,
42,089✔
104
            ..Default::default()
42,089✔
105
        })
42,089✔
106
    }
42,089✔
107

108
    /// Create new AppLayerTxData for a transaction in a single
109
    /// direction.
110
    pub fn for_direction(direction: Direction) -> Self {
14,814✔
111
        let (flags, updated_ts, updated_tc) = match direction {
14,814✔
112
            Direction::ToServer => (APP_LAYER_TX_SKIP_INSPECT_TC, true, false),
11,039✔
113
            Direction::ToClient => (APP_LAYER_TX_SKIP_INSPECT_TS, false, true),
3,775✔
114
        };
115
        Self (suricata_sys::sys::AppLayerTxData{
14,814✔
116
            updated_tc,
14,814✔
117
            updated_ts,
14,814✔
118
            flags,
14,814✔
119
            ..Default::default()
14,814✔
120
        })
14,814✔
121
    }
14,814✔
122

123
    pub fn init_files_opened(&mut self) {
714✔
124
        self.0.files_opened = 1;
714✔
125
    }
714✔
126

127
    pub fn incr_files_opened(&mut self) {
260✔
128
        self.0.files_opened += 1;
260✔
129
    }
260✔
130

131
    pub fn set_event(&mut self, _event: u8) {
289✔
132
        #[cfg(not(test))]
289✔
133
        unsafe {
289✔
134
            SCAppLayerDecoderEventsSetEventRaw(&mut self.0.events, _event);
289✔
135
        }
289✔
136
    }
289✔
137

138
    pub fn update_file_flags(&mut self, state_flags: u16) {
8,318✔
139
        unsafe {
8,318✔
140
            SCTxDataUpdateFileFlags(&mut self.0, state_flags);
8,318✔
141
        }
8,318✔
142
    }
8,318✔
143
}
144

145
#[cfg(not(test))]
146
use suricata_sys::sys::SCAppLayerTxDataCleanup;
147

148
impl Drop for AppLayerTxData {
149
    fn drop(&mut self) {
75,436✔
150
        #[cfg(not(test))]
75,436✔
151
        unsafe {
75,436✔
152
            SCAppLayerTxDataCleanup(&mut self.0);
75,436✔
153
        }
75,436✔
154
    }
75,436✔
155
}
156

157

158
// need to keep in sync with C flow.h
159
pub const FLOWFILE_NO_STORE_TS: u16 = BIT_U16!(2);
160
pub const FLOWFILE_NO_STORE_TC: u16 = BIT_U16!(3);
161
pub const FLOWFILE_STORE_TS: u16 = BIT_U16!(12);
162
pub const FLOWFILE_STORE_TC: u16 = BIT_U16!(13);
163

164
#[no_mangle]
165
pub unsafe extern "C" fn SCTxDataUpdateFileFlags(txd: &mut suricata_sys::sys::AppLayerTxData, state_flags: u16) {
218,751✔
166
    if (txd.file_flags & state_flags) != state_flags {
218,751✔
167
        SCLogDebug!("updating tx file_flags {:04x} with state flags {:04x}", txd.file_flags, state_flags);
168
        let mut nf = state_flags;
5,385✔
169
        // With keyword filestore:both,flow :
5,385✔
170
        // There may be some opened unclosed file in one direction without filestore
5,385✔
171
        // As such it has tx file_flags had FLOWFILE_NO_STORE_TS or TC
5,385✔
172
        // But a new file in the other direction may trigger filestore:both,flow
5,385✔
173
        // And thus set state_flags FLOWFILE_STORE_TS
5,385✔
174
        // If the file was opened without storing it, do not try to store just the end of it
5,385✔
175
        if (txd.file_flags & FLOWFILE_NO_STORE_TS) != 0 && (state_flags & FLOWFILE_STORE_TS) != 0 {
5,385✔
176
            nf &= !FLOWFILE_STORE_TS;
×
177
        }
5,385✔
178
        if (txd.file_flags & FLOWFILE_NO_STORE_TC) != 0 && (state_flags & FLOWFILE_STORE_TC) != 0 {
5,385✔
179
            nf &= !FLOWFILE_STORE_TC;
×
180
        }
5,385✔
181
        txd.file_flags |= nf;
5,385✔
182
    }
213,366✔
183
}
218,751✔
184

185
#[macro_export]
186
macro_rules!export_tx_data_get {
187
    ($name:ident, $type:ty) => {
188
        unsafe extern "C" fn $name(tx: *mut std::os::raw::c_void)
100,137,733✔
189
            -> *mut suricata_sys::sys::AppLayerTxData
100,137,733✔
190
        {
100,137,733✔
191
            let tx = &mut *(tx as *mut $type);
100,137,733✔
192
            &mut tx.tx_data.0
100,137,733✔
193
        }
100,137,733✔
194
    }
195
}
196

197
#[macro_export]
198
macro_rules!export_state_data_get {
199
    ($name:ident, $type:ty) => {
200
        unsafe extern "C" fn $name(state: *mut std::os::raw::c_void)
1,395✔
201
            -> *mut $crate::applayer::AppLayerStateData
1,395✔
202
        {
1,395✔
203
            let state = &mut *(state as *mut $type);
1,395✔
204
            &mut state.state_data
1,395✔
205
        }
1,395✔
206
    }
207
}
208

209
pub trait AppLayerResultRust {
210
    fn ok() -> Self;
211
    fn err() -> Self;
212
    fn incomplete(consumed: u32, needed: u32) -> Self;
213
    fn is_ok(&self) -> bool;
214
    fn is_err(&self) -> bool;
215
    fn is_incomplete(&self) -> bool;
216
}
217

218
impl AppLayerResultRust for AppLayerResult {
219
    /// parser has successfully processed in the input, and has consumed all of it
220
    fn ok() -> Self {
89,838✔
221
        Default::default()
89,838✔
222
    }
89,838✔
223
    /// parser has hit an unrecoverable error. Returning this to the API
224
    /// leads to no further calls to the parser.
225
    fn err() -> Self {
479✔
226
        return AppLayerResult{
479✔
227
            status: -1,
479✔
228
            ..Default::default()
479✔
229
        };
479✔
230
    }
479✔
231

232
    /// parser needs more data. Through 'consumed' it will indicate how many
233
    /// of the input bytes it has consumed. Through 'needed' it will indicate
234
    /// how many more bytes it needs before getting called again.
235
    /// Note: consumed should never be more than the input len
236
    ///       needed + consumed should be more than the input len
237
    fn incomplete(consumed: u32, needed: u32) -> Self {
1,307✔
238
        return Self {
1,307✔
239
            status: 1,
1,307✔
240
            consumed,
1,307✔
241
            needed,
1,307✔
242
        };
1,307✔
243
    }
1,307✔
244

245
    fn is_ok(&self) -> bool {
125✔
246
        self.status == 0
125✔
247
    }
125✔
248

249
    fn is_err(&self) -> bool {
×
250
        self.status == -1
×
251
    }
×
252

253
    fn is_incomplete(&self) -> bool {
126✔
254
        self.status == 1
126✔
255
    }
126✔
256
}
257

258
/// Rust parser declaration
259
#[repr(C)]
260
pub struct RustParser {
261
    /// Parser name.
262
    pub name:               *const c_char,
263
    /// Default port
264
    pub default_port:       *const c_char,
265

266
    /// IP Protocol (core::IPPROTO_UDP, core::IPPROTO_TCP, etc.)
267
    pub ipproto:            u8,
268

269
    /// Probing function, for packets going to server
270
    pub probe_ts:           Option<ProbeFn>,
271
    /// Probing function, for packets going to client
272
    pub probe_tc:           Option<ProbeFn>,
273

274
    /// Minimum frame depth for probing
275
    pub min_depth:          u16,
276
    /// Maximum frame depth for probing
277
    pub max_depth:          u16,
278

279
    /// Allocation function for a new state
280
    pub state_new:          StateAllocFn,
281
    /// Function called to free a state
282
    pub state_free:         StateFreeFn,
283

284
    /// Parsing function, for packets going to server
285
    pub parse_ts:           ParseFn,
286
    /// Parsing function, for packets going to client
287
    pub parse_tc:           ParseFn,
288

289
    /// Get the current transaction count
290
    pub get_tx_count:       StateGetTxCntFn,
291
    /// Get a transaction
292
    pub get_tx:             StateGetTxFn,
293
    /// Function called to free a transaction
294
    pub tx_free:            StateTxFreeFn,
295
    /// Progress values at which the tx is considered complete in a direction
296
    pub tx_comp_st_ts:      c_int,
297
    pub tx_comp_st_tc:      c_int,
298
    /// Function returning the current transaction progress
299
    pub tx_get_progress:    StateGetProgressFn,
300

301
    /// Function to get an event id from a description
302
    pub get_eventinfo:      Option<GetEventInfoFn>,
303
    /// Function to get an event description from an event id
304
    pub get_eventinfo_byid: Option<GetEventInfoByIdFn>,
305

306
    /// Function to allocate local storage
307
    pub localstorage_new:   Option<LocalStorageNewFn>,
308
    /// Function to free local storage
309
    pub localstorage_free:  Option<LocalStorageFreeFn>,
310

311
    /// Function to get files
312
    pub get_tx_files:       Option<GetTxFilesFn>,
313

314
    /// Function to get the TX iterator
315
    pub get_tx_iterator:    Option<GetTxIteratorFn>,
316

317
    pub get_state_data: GetStateDataFn,
318
    pub get_tx_data: GetTxDataFn,
319

320
    // Function to apply config to a TX. Optional. Normal (bidirectional)
321
    // transactions don't need to set this. It is meant for cases where
322
    // the requests and responses are not sharing tx. It is then up to
323
    // the implementation to make sure the config is applied correctly.
324
    pub apply_tx_config: Option<ApplyTxConfigFn>,
325

326
    pub flags: u32,
327

328
    pub get_frame_id_by_name: Option<GetFrameIdByName>,
329
    pub get_frame_name_by_id: Option<GetFrameNameById>,
330

331
    pub get_state_id_by_name: Option<GetStateIdByName>,
332
    pub get_state_name_by_id: Option<GetStateNameById>,
333
}
334

335
pub trait AppLayerGetFileStateRust {
336
    fn err() -> Self;
337
}
338

339
impl AppLayerGetFileStateRust for AppLayerGetFileState {
340
    fn err() -> AppLayerGetFileState {
180,856✔
341
        AppLayerGetFileState { fc: std::ptr::null_mut(), cfg: std::ptr::null() }
180,856✔
342
    }
180,856✔
343
}
344

345
pub type ParseFn      = unsafe extern "C" fn (flow: *mut Flow,
346
                                       state: *mut c_void,
347
                                       pstate: *mut AppLayerParserState,
348
                                       stream_slice: StreamSlice,
349
                                       data: *mut c_void) -> AppLayerResult;
350
pub type ProbeFn      = unsafe extern "C" fn (flow: *const Flow, flags: u8, input:*const u8, input_len: u32, rdir: *mut u8) -> AppProto;
351
pub type StateAllocFn = unsafe extern "C" fn (*mut c_void, AppProto) -> *mut c_void;
352
pub type StateFreeFn  = unsafe extern "C" fn (*mut c_void);
353
pub type StateTxFreeFn  = unsafe extern "C" fn (*mut c_void, u64);
354
pub type StateGetTxFn            = unsafe extern "C" fn (*mut c_void, u64) -> *mut c_void;
355
pub type StateGetTxCntFn         = unsafe extern "C" fn (*mut c_void) -> u64;
356
pub type StateGetProgressFn = unsafe extern "C" fn (*mut c_void, u8) -> c_int;
357
pub type GetEventInfoFn     = unsafe extern "C" fn (*const c_char, event_id: *mut u8, *mut AppLayerEventType) -> c_int;
358
pub type GetEventInfoByIdFn = unsafe extern "C" fn (event_id: u8, *mut *const c_char, *mut AppLayerEventType) -> c_int;
359
pub type LocalStorageNewFn  = unsafe extern "C" fn () -> *mut c_void;
360
pub type LocalStorageFreeFn = unsafe extern "C" fn (*mut c_void);
361
pub type GetTxFilesFn       = unsafe extern "C" fn (*mut c_void, u8) -> AppLayerGetFileState;
362
pub type GetTxIteratorFn    = unsafe extern "C" fn (ipproto: u8, alproto: AppProto,
363
                                             state: *mut c_void,
364
                                             min_tx_id: u64,
365
                                             max_tx_id: u64,
366
                                             istate: *mut AppLayerGetTxIterState)
367
                                             -> AppLayerGetTxIterTuple;
368
pub type GetTxDataFn = unsafe extern "C" fn(*mut c_void) -> *mut suricata_sys::sys::AppLayerTxData;
369
pub type GetStateDataFn = unsafe extern "C" fn(*mut c_void) -> *mut AppLayerStateData;
370
pub type ApplyTxConfigFn = unsafe extern "C" fn (*mut c_void, *mut c_void, c_int, AppLayerTxConfig);
371
pub type GetFrameIdByName = unsafe extern "C" fn(*const c_char) -> c_int;
372
pub type GetFrameNameById = unsafe extern "C" fn(u8) -> *const c_char;
373
pub type GetStateIdByName = unsafe extern "C" fn(*const c_char, u8) -> c_int;
374
pub type GetStateNameById = unsafe extern "C" fn(c_int, u8) -> *const c_char;
375

376
use suricata_sys::sys::{AppLayerParser, SCAppLayerRegisterParser};
377

378
#[allow(non_snake_case)]
379
pub fn AppLayerRegisterParser(parser: &RustParser, alproto: AppProto) -> c_int {
68,014✔
380
    let det = AppLayerParser{
68,014✔
381
        name: parser.name,
68,014✔
382
        default_port: parser.default_port,
68,014✔
383
        ip_proto: parser.ipproto,
68,014✔
384
        ProbeTS: parser.probe_ts,
68,014✔
385
        ProbeTC: parser.probe_tc,
68,014✔
386
        min_depth: parser.min_depth,
68,014✔
387
        max_depth: parser.max_depth,
68,014✔
388

68,014✔
389
        StateAlloc: Some(parser.state_new),
68,014✔
390
        StateFree: Some(parser.state_free),
68,014✔
391

68,014✔
392
        ParseTS: Some(parser.parse_ts),
68,014✔
393
        ParseTC: Some(parser.parse_tc),
68,014✔
394

68,014✔
395
        StateGetTxCnt: Some(parser.get_tx_count),
68,014✔
396
        StateGetTx: Some(parser.get_tx),
68,014✔
397
        StateTransactionFree: Some(parser.tx_free),
68,014✔
398

68,014✔
399
        complete_ts: parser.tx_comp_st_ts,
68,014✔
400
        complete_tc: parser.tx_comp_st_tc,
68,014✔
401
        StateGetProgress: Some(parser.tx_get_progress),
68,014✔
402

68,014✔
403
        StateGetEventInfo: parser.get_eventinfo,
68,014✔
404
        StateGetEventInfoById: parser.get_eventinfo_byid,
68,014✔
405
        LocalStorageAlloc: parser.localstorage_new,
68,014✔
406
        LocalStorageFree: parser.localstorage_free,
68,014✔
407

68,014✔
408
        GetTxFiles: parser.get_tx_files,
68,014✔
409
        GetTxIterator: parser.get_tx_iterator,
68,014✔
410
        GetStateData: Some(parser.get_state_data),
68,014✔
411
        GetTxData: Some(parser.get_tx_data),
68,014✔
412
        ApplyTxConfig: parser.apply_tx_config,
68,014✔
413

68,014✔
414
        flags: parser.flags,
68,014✔
415

68,014✔
416
        GetFrameIdByName: parser.get_frame_id_by_name,
68,014✔
417
        GetFrameNameById: parser.get_frame_name_by_id,
68,014✔
418
        GetStateIdByName: parser.get_state_id_by_name,
68,014✔
419
        GetStateNameById: parser.get_state_name_by_id,
68,014✔
420
    };
68,014✔
421
    unsafe {SCAppLayerRegisterParser(&det, alproto) }
68,014✔
422
}
68,014✔
423

424
use suricata_sys::sys::{AppLayerProtocolDetect, SCAppLayerRegisterProtocolDetection};
425

426
pub fn applayer_register_protocol_detection(parser: &RustParser, enable_default: c_int) -> AppProto {
68,018✔
427
    let det = AppLayerProtocolDetect{
68,018✔
428
        name: parser.name,
68,018✔
429
        default_port: parser.default_port,
68,018✔
430
        ip_proto: parser.ipproto,
68,018✔
431
        ProbeTS: parser.probe_ts,
68,018✔
432
        ProbeTC: parser.probe_tc,
68,018✔
433
        min_depth: parser.min_depth,
68,018✔
434
        max_depth: parser.max_depth,
68,018✔
435
    };
68,018✔
436
    unsafe {SCAppLayerRegisterProtocolDetection(&det, enable_default) }
68,018✔
437
}
68,018✔
438

439

440
// Defined in app-layer-parser.h
441
pub const APP_LAYER_PARSER_NO_INSPECTION : u16 = BIT_U16!(1);
442
pub const APP_LAYER_PARSER_NO_REASSEMBLY : u16 = BIT_U16!(2);
443
pub const APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD : u16 = BIT_U16!(3);
444
pub const APP_LAYER_PARSER_BYPASS_READY : u16 = BIT_U16!(4);
445
pub const APP_LAYER_PARSER_EOF_TS : u16 = BIT_U16!(5);
446
pub const APP_LAYER_PARSER_EOF_TC : u16 = BIT_U16!(6);
447

448
pub const APP_LAYER_PARSER_OPT_ACCEPT_GAPS: u32 = BIT_U32!(0);
449

450
pub const APP_LAYER_TX_SKIP_INSPECT_TS: u8 = BIT_U8!(0);
451
pub const APP_LAYER_TX_SKIP_INSPECT_TC: u8 = BIT_U8!(1);
452
pub const _APP_LAYER_TX_INSPECTED_TS: u8 = BIT_U8!(2);
453
pub const _APP_LAYER_TX_INSPECTED_TC: u8 = BIT_U8!(3);
454
pub const APP_LAYER_TX_ACCEPT: u8 = BIT_U8!(4);
455

456
pub trait AppLayerGetTxIterTupleRust {
457
    fn with_values(tx_ptr: *mut std::os::raw::c_void, tx_id: u64, has_next: bool) -> Self;
458
    fn not_found() -> Self;
459
}
460

461
impl AppLayerGetTxIterTupleRust for AppLayerGetTxIterTuple {
462
    fn with_values(tx_ptr: *mut std::os::raw::c_void, tx_id: u64, has_next: bool) -> AppLayerGetTxIterTuple {
109,931,235✔
463
        AppLayerGetTxIterTuple {
109,931,235✔
464
            tx_ptr, tx_id, has_next,
109,931,235✔
465
        }
109,931,235✔
466
    }
109,931,235✔
467
    fn not_found() -> AppLayerGetTxIterTuple {
45,828✔
468
        AppLayerGetTxIterTuple {
45,828✔
469
            tx_ptr: std::ptr::null_mut(), tx_id: 0, has_next: false,
45,828✔
470
        }
45,828✔
471
    }
45,828✔
472
}
473

474
/// AppLayerEvent trait that will be implemented on enums that
475
/// derive AppLayerEvent.
476
pub trait AppLayerEvent {
477
    /// Return the enum variant of the given ID.
478
    fn from_id(id: u8) -> Option<Self> where Self: std::marker::Sized;
479

480
    /// Convert the enum variant to a C-style string (suffixed with \0).
481
    fn to_cstring(&self) -> &str;
482

483
    /// Return the enum variant for the given name.
484
    fn from_string(s: &str) -> Option<Self> where Self: std::marker::Sized;
485

486
    /// Return the ID value of the enum variant.
487
    fn as_u8(&self) -> u8;
488

489
    unsafe extern "C" fn get_event_info(
490
        event_name: *const std::os::raw::c_char,
491
        event_id: *mut u8,
492
        event_type: *mut core::AppLayerEventType,
493
    ) -> std::os::raw::c_int;
494

495
    unsafe extern "C" fn get_event_info_by_id(
496
        event_id: u8,
497
        event_name: *mut *const std::os::raw::c_char,
498
        event_type: *mut core::AppLayerEventType,
499
    ) -> std::os::raw::c_int;
500
}
501

502
/// Generic `get_info_info` implementation for enums implementing
503
/// AppLayerEvent.
504
///
505
/// Normally usage of this function will be generated by
506
/// derive(AppLayerEvent), for example:
507
///
508
/// ```rust,ignore
509
/// #[derive(AppLayerEvent)]
510
/// enum AppEvent {
511
///     EventOne,
512
///     EventTwo,
513
/// }
514
///
515
/// get_event_info::<AppEvent>(...)
516
/// ```
517
#[inline(always)]
518
pub unsafe fn get_event_info<T: AppLayerEvent>(
1,138✔
519
    event_name: *const std::os::raw::c_char,
1,138✔
520
    event_id: *mut u8,
1,138✔
521
    event_type: *mut core::AppLayerEventType,
1,138✔
522
) -> std::os::raw::c_int {
1,138✔
523
    if event_name.is_null() {
1,138✔
524
        return -1;
×
525
    }
1,138✔
526

527
    let event = match CStr::from_ptr(event_name).to_str().map(T::from_string) {
1,138✔
528
        Ok(Some(event)) => event.as_u8(),
1,083✔
529
        _ => {
530
            return -1;
55✔
531
        }
532
    };
533
    *event_type = core::AppLayerEventType::APP_LAYER_EVENT_TYPE_TRANSACTION;
1,083✔
534
    *event_id = event;
1,083✔
535
    return 0;
1,083✔
536
}
1,138✔
537

538
/// Generic `get_info_info_by_id` implementation for enums implementing
539
/// AppLayerEvent.
540
#[inline(always)]
541
pub unsafe fn get_event_info_by_id<T: AppLayerEvent>(
213✔
542
    event_id: u8,
213✔
543
    event_name: *mut *const std::os::raw::c_char,
213✔
544
    event_type: *mut core::AppLayerEventType,
213✔
545
) -> std::os::raw::c_int {
213✔
546
    if let Some(e) = T::from_id(event_id) {
213✔
547
        *event_name = e.to_cstring().as_ptr() as *const std::os::raw::c_char;
213✔
548
        *event_type = core::AppLayerEventType::APP_LAYER_EVENT_TYPE_TRANSACTION;
213✔
549
        return 0;
213✔
550
    }
×
551
    return -1;
×
552
}
213✔
553

554
/// Transaction trait.
555
///
556
/// This trait defines methods that a Transaction struct must implement
557
/// in order to define some generic helper functions.
558
pub trait Transaction {
559
    fn id(&self) -> u64;
560
}
561

562
pub trait State<Tx: Transaction> {
563
    /// Return the number of transactions in the state's transaction collection.
564
    fn get_transaction_count(&self) -> usize;
565

566
    /// Return a transaction by its index in the container.
567
    fn get_transaction_by_index(&self, index: usize) -> Option<&Tx>;
568

569
    fn get_transaction_iterator(&self, min_tx_id: u64, state: &mut u64) -> AppLayerGetTxIterTuple {
109,977,063✔
570
        let mut index = *state as usize;
109,977,063✔
571
        let len = self.get_transaction_count();
109,977,063✔
572
        while index < len {
511,226,162✔
573
            let tx = self.get_transaction_by_index(index).unwrap();
511,180,334✔
574
            if tx.id() < min_tx_id + 1 {
511,180,334✔
575
                index += 1;
401,249,099✔
576
                continue;
401,249,099✔
577
            }
109,931,235✔
578
            *state = index as u64;
109,931,235✔
579
            return AppLayerGetTxIterTuple::with_values(
109,931,235✔
580
                tx as *const _ as *mut _,
109,931,235✔
581
                tx.id() - 1,
109,931,235✔
582
                len - index > 1,
109,931,235✔
583
            );
109,931,235✔
584
        }
585
        return AppLayerGetTxIterTuple::not_found();
45,828✔
586
    }
109,977,063✔
587
}
588

589
pub unsafe extern "C" fn state_get_tx_iterator<S: State<Tx>, Tx: Transaction>(
109,977,063✔
590
    _ipproto: u8, _alproto: AppProto, state: *mut std::os::raw::c_void, min_tx_id: u64,
109,977,063✔
591
    _max_tx_id: u64, istate: *mut AppLayerGetTxIterState,
109,977,063✔
592
) -> AppLayerGetTxIterTuple {
109,977,063✔
593
    let state = cast_pointer!(state, S);
109,977,063✔
594
    state.get_transaction_iterator(min_tx_id, &mut (*istate).un.u64_)
109,977,063✔
595
}
109,977,063✔
596

597
/// AppLayerFrameType trait.
598
///
599
/// This is the behavior expected from an enum of frame types. For most instances
600
/// this behavior can be derived.
601
///
602
/// Example:
603
///
604
/// #[derive(AppLayerFrameType)]
605
/// enum SomeProtoFrameType {
606
///     PDU,
607
///     Data,
608
/// }
609
pub trait AppLayerFrameType {
610
    /// Create a frame type variant from a u8.
611
    ///
612
    /// None will be returned if there is no matching enum variant.
613
    fn from_u8(value: u8) -> Option<Self> where Self: std::marker::Sized;
614

615
    /// Return the u8 value of the enum where the first entry has the value of 0.
616
    fn as_u8(&self) -> u8;
617

618
    /// Create a frame type variant from a &str.
619
    ///
620
    /// None will be returned if there is no matching enum variant.
621
    fn from_str(s: &str) -> Option<Self> where Self: std::marker::Sized;
622

623
    /// Return a pointer to a C string of the enum variant suitable as-is for
624
    /// FFI.
625
    fn to_cstring(&self) -> *const std::os::raw::c_char;
626

627
    /// Converts a C string formatted name to a frame type ID.
628
    unsafe extern "C" fn ffi_id_from_name(name: *const std::os::raw::c_char) -> i32 where Self: Sized {
80✔
629
        if name.is_null() {
80✔
630
            return -1;
×
631
        }
80✔
632
        let frame_id = if let Ok(s) = std::ffi::CStr::from_ptr(name).to_str() {
80✔
633
            Self::from_str(s).map(|t| t.as_u8() as i32).unwrap_or(-1)
80✔
634
        } else {
635
            -1
×
636
        };
637
        frame_id
80✔
638
    }
80✔
639

640
    /// Converts a variant ID to an FFI safe name.
641
    extern "C" fn ffi_name_from_id(id: u8) -> *const std::os::raw::c_char where Self: Sized {
1,336✔
642
        Self::from_u8(id).map(|s| s.to_cstring()).unwrap_or_else(std::ptr::null)
1,336✔
643
    }
1,336✔
644
}
645

646
/// AppLayerState trait.
647
///
648
/// This is the behavior expected from an enum of state progress. For most instances
649
/// this behavior can be derived. This is for protocols which do not need direction,
650
/// like SSH (which is symmetric).
651
///
652
/// Example:
653
///
654
/// #[derive(AppLayerState)]
655
/// enum SomeProtoState {
656
///     Start,
657
///     Complete,
658
/// }
659
pub trait AppLayerState {
660
    /// Create a state progress variant from a u8.
661
    ///
662
    /// None will be returned if there is no matching enum variant.
663
    fn from_u8(value: u8) -> Option<Self>
664
    where
665
        Self: Sized;
666

667
    /// Return the u8 value of the enum where the first entry has the value of 0.
668
    fn as_u8(&self) -> u8;
669

670
    /// Create a state progress variant from a &str.
671
    ///
672
    /// None will be returned if there is no matching enum variant.
673
    fn from_str(s: &str) -> Option<Self>
674
    where
675
        Self: Sized;
676

677
    /// Return a pointer to a C string of the enum variant suitable as-is for
678
    /// FFI.
679
    fn to_cstring(&self, to_server: bool) -> *const c_char;
680

681
    /// Converts a C string formatted name to a state progress.
682
    unsafe extern "C" fn ffi_id_from_name(name: *const c_char, dir: u8) -> c_int
20✔
683
    where
20✔
684
        Self: Sized,
20✔
685
    {
20✔
686
        if name.is_null() {
20✔
687
            return -1;
×
688
        }
20✔
689
        if let Ok(s) = std::ffi::CStr::from_ptr(name).to_str() {
20✔
690
            let dir = Direction::from(dir);
20✔
691
            let s2 = match dir {
20✔
692
                Direction::ToServer => {
693
                    if !s.starts_with("request_") {
14✔
694
                        return -1;
6✔
695
                    }
8✔
696
                    &s["request_".len()..]
8✔
697
                }
698
                Direction::ToClient => {
699
                    if !s.starts_with("response_") {
6✔
700
                        return -1;
×
701
                    }
6✔
702
                    &s["response_".len()..]
6✔
703
                }
704
            };
705
            Self::from_str(s2).map(|t| t.as_u8() as c_int).unwrap_or(-1)
14✔
706
        } else {
707
            -1
×
708
        }
709
    }
20✔
710

711
    /// Converts a variant ID to an FFI name.
712
    unsafe extern "C" fn ffi_name_from_id(id: c_int, dir: u8) -> *const c_char
18,006✔
713
    where
18,006✔
714
        Self: Sized,
18,006✔
715
    {
18,006✔
716
        if id < 0 || id > c_int::from(u8::MAX) {
18,006✔
717
            return std::ptr::null();
×
718
        }
18,006✔
719
        if let Some(v) = Self::from_u8(id as u8) {
18,006✔
720
            return v.to_cstring(dir == STREAM_TOSERVER);
18,006✔
721
        }
×
722
        return std::ptr::null();
×
723
    }
18,006✔
724
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc