Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/core.rs
Line
Count
Source
1
/* Copyright (C) 2017 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
//! This module exposes items from the core "C" code to Rust.
19
20
use std;
21
use crate::filecontainer::*;
22
use crate::debug_validate_fail;
23
24
/// Opaque C types.
25
pub enum DetectEngineState {}
26
pub enum AppLayerDecoderEvents {}
27
28
#[repr(C)]
29
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
30
#[allow(non_camel_case_types)]
31
pub enum AppLayerEventType {
32
    APP_LAYER_EVENT_TYPE_TRANSACTION = 1,
33
    APP_LAYER_EVENT_TYPE_PACKET = 2,
34
}
35
36
pub const STREAM_START:    u8 = 0x01;
37
pub const STREAM_EOF:      u8 = 0x02;
38
pub const STREAM_TOSERVER: u8 = 0x04;
39
pub const STREAM_TOCLIENT: u8 = 0x08;
40
pub const STREAM_GAP:      u8 = 0x10;
41
pub const STREAM_DEPTH:    u8 = 0x20;
42
pub const STREAM_MIDSTREAM:u8 = 0x40;
43
pub const DIR_BOTH:        u8 = 0b0000_1100;
44
const DIR_TOSERVER:        u8 = 0b0000_0100;
45
const DIR_TOCLIENT:        u8 = 0b0000_1000;
46
47
#[repr(C)]
48
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
49
pub enum Direction {
50
    ToServer = 0x04,
51
    ToClient = 0x08,
52
}
53
54
impl Direction {
55
    /// Return true if the direction is to server.
56
32.7M
    pub fn is_to_server(&self) -> bool {
57
32.7M
  matches!(self, Self::ToServer)
58
32.7M
    }
59
60
    /// Return true if the direction is to client.
61
32.7M
    pub fn is_to_client(&self) -> bool {
62
32.7M
  matches!(self, Self::ToClient)
63
32.7M
    }
64
}
65
66
impl Default for Direction {
67
405k
    fn default() -> Self { Direction::ToServer }
68
}
69
70
impl std::fmt::Display for Direction {
71
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72
0
        match self {
73
0
            Self::ToServer => write!(f, "toserver"),
74
0
            Self::ToClient => write!(f, "toclient"),
75
        }
76
0
    }
77
}
78
79
impl From<u8> for Direction {
80
31.3M
    fn from(d: u8) -> Self {
81
31.3M
        if d & (DIR_TOSERVER | DIR_TOCLIENT) == (DIR_TOSERVER | DIR_TOCLIENT) {
82
0
            debug_validate_fail!("Both directions are set");
83
0
            Direction::ToServer
84
31.3M
        } else if d & DIR_TOSERVER != 0 {
85
14.9M
            Direction::ToServer
86
16.4M
        } else if d & DIR_TOCLIENT != 0 {
87
16.4M
            Direction::ToClient
88
        } else {
89
0
            debug_validate_fail!("Unknown direction!!");
90
0
            Direction::ToServer
91
        }
92
31.3M
    }
93
}
94
95
impl From<Direction> for u8 {
96
1.57G
    fn from(d: Direction) -> u8 {
97
1.57G
        d as u8
98
1.57G
    }
99
}
100
101
// Application layer protocol identifiers (app-layer-protos.h)
102
pub type AppProto = u16;
103
104
pub const ALPROTO_UNKNOWN : AppProto = 0;
105
pub static mut ALPROTO_FAILED : AppProto = 0; // updated during init
106
107
pub const IPPROTO_TCP : u8 = 6;
108
pub const IPPROTO_UDP : u8 = 17;
109
110
/*
111
macro_rules!BIT_U8 {
112
    ($x:expr) => (1 << $x);
113
}
114
*/
115
macro_rules!BIT_U16 {
116
    ($x:expr) => (1 << $x);
117
}
118
119
macro_rules!BIT_U32 {
120
    ($x:expr) => (1 << $x);
121
}
122
123
macro_rules!BIT_U64 {
124
    ($x:expr) => (1 << $x);
125
}
126
127
// Flow flags
128
pub const FLOW_DIR_REVERSED: u32 = BIT_U32!(26);
129
130
// Defined in app-layer-protos.h
131
/// cbindgen:ignore
132
extern "C" {
133
    pub fn StringToAppProto(proto_name: *const u8) -> AppProto;
134
}
135
136
//
137
// Function types for calls into C.
138
//
139
140
#[allow(non_snake_case)]
141
pub type SCLogMessageFunc =
142
    extern "C" fn(level: std::os::raw::c_int,
143
                  filename: *const std::os::raw::c_char,
144
                  line: std::os::raw::c_uint,
145
                  function: *const std::os::raw::c_char,
146
                  subsystem: *const std::os::raw::c_char,
147
                  message: *const std::os::raw::c_char) -> std::os::raw::c_int;
148
149
pub type DetectEngineStateFreeFunc =
150
    extern "C" fn(state: *mut DetectEngineState);
151
152
pub type AppLayerParserTriggerRawStreamReassemblyFunc =
153
    extern "C" fn (flow: *const Flow, direction: i32);
154
pub type AppLayerDecoderEventsSetEventRawFunc =
155
    extern "C" fn (events: *mut *mut AppLayerDecoderEvents,
156
                   event: u8);
157
158
pub type AppLayerDecoderEventsFreeEventsFunc =
159
    extern "C" fn (events: *mut *mut AppLayerDecoderEvents);
160
161
pub enum StreamingBufferConfig {}
162
163
// Opaque flow type (defined in C)
164
pub enum HttpRangeContainerBlock {}
165
166
pub type SCHttpRangeFreeBlock = extern "C" fn (
167
        c: *mut HttpRangeContainerBlock);
168
pub type SCHTPFileCloseHandleRange = extern "C" fn (
169
        sbcfg: &StreamingBufferConfig,
170
        fc: *mut FileContainer,
171
        flags: u16,
172
        c: *mut HttpRangeContainerBlock,
173
        data: *const u8,
174
        data_len: u32) -> bool;
175
pub type SCFileOpenFileWithId = extern "C" fn (
176
        file_container: &FileContainer,
177
        sbcfg: &StreamingBufferConfig,
178
        track_id: u32,
179
        name: *const u8, name_len: u16,
180
        data: *const u8, data_len: u32,
181
        flags: u16) -> i32;
182
pub type SCFileCloseFileById = extern "C" fn (
183
        file_container: &FileContainer,
184
        sbcfg: &StreamingBufferConfig,
185
        track_id: u32,
186
        data: *const u8, data_len: u32,
187
        flags: u16) -> i32;
188
pub type SCFileAppendDataById = extern "C" fn (
189
        file_container: &FileContainer,
190
        sbcfg: &StreamingBufferConfig,
191
        track_id: u32,
192
        data: *const u8, data_len: u32) -> i32;
193
pub type SCFileAppendGAPById = extern "C" fn (
194
        file_container: &FileContainer,
195
        sbcfg: &StreamingBufferConfig,
196
        track_id: u32,
197
        data: *const u8, data_len: u32) -> i32;
198
pub type SCFileContainerRecycle = extern "C" fn (
199
        file_container: &FileContainer,
200
        sbcfg: &StreamingBufferConfig);
201
202
// A Suricata context that is passed in from C. This is alternative to
203
// using functions from Suricata directly, so they can be wrapped so
204
// Rust unit tests will still compile when they are not linked
205
// directly to the real function.
206
//
207
// This might add a little too much complexity to keep pure Rust test
208
// cases working.
209
#[allow(non_snake_case)]
210
#[repr(C)]
211
pub struct SuricataContext {
212
    pub SCLogMessage: SCLogMessageFunc,
213
    DetectEngineStateFree: DetectEngineStateFreeFunc,
214
    AppLayerDecoderEventsSetEventRaw: AppLayerDecoderEventsSetEventRawFunc,
215
    AppLayerDecoderEventsFreeEvents: AppLayerDecoderEventsFreeEventsFunc,
216
    pub AppLayerParserTriggerRawStreamReassembly: AppLayerParserTriggerRawStreamReassemblyFunc,
217
218
    pub HttpRangeFreeBlock: SCHttpRangeFreeBlock,
219
    pub HTPFileCloseHandleRange: SCHTPFileCloseHandleRange,
220
221
    pub FileOpenFile: SCFileOpenFileWithId,
222
    pub FileCloseFile: SCFileCloseFileById,
223
    pub FileAppendData: SCFileAppendDataById,
224
    pub FileAppendGAP: SCFileAppendGAPById,
225
    pub FileContainerRecycle: SCFileContainerRecycle,
226
227
    pub AppLayerRegisterParser: extern "C" fn(parser: *const crate::applayer::RustParser, alproto: AppProto) -> std::os::raw::c_int,
228
}
229
230
#[allow(non_snake_case)]
231
#[repr(C)]
232
pub struct SuricataFileContext {
233
    pub files_sbcfg: &'static StreamingBufferConfig,
234
}
235
236
/// cbindgen:ignore
237
extern "C" {
238
    pub fn SCGetContext() -> &'static mut SuricataContext;
239
    pub fn SCLogGetLogLevel() -> i32;
240
}
241
242
pub static mut SC: Option<&'static SuricataContext> = None;
243
244
37
pub fn init_ffi(context: &'static SuricataContext)
245
{
246
37
    unsafe {
247
37
        SC = Some(context);
248
37
        ALPROTO_FAILED = StringToAppProto("failed\0".as_ptr());
249
37
    }
250
37
}
251
252
#[no_mangle]
253
37
pub extern "C" fn rs_init(context: &'static SuricataContext)
254
{
255
37
    init_ffi(context);
256
37
}
257
258
/// DetectEngineStateFree wrapper.
259
20.4k
pub fn sc_detect_engine_state_free(state: *mut DetectEngineState)
260
{
261
    unsafe {
262
20.4k
        if let Some(c) = SC {
263
20.4k
            (c.DetectEngineStateFree)(state);
264
20.4k
        }
265
    }
266
20.4k
}
267
268
/// AppLayerParserTriggerRawStreamReassembly wrapper
269
8.13M
pub fn sc_app_layer_parser_trigger_raw_stream_reassembly(flow: *const Flow, direction: i32) {
270
    unsafe {
271
8.13M
        if let Some(c) = SC {
272
8.13M
            (c.AppLayerParserTriggerRawStreamReassembly)(flow, direction);
273
8.13M
        }
274
    }
275
8.13M
}
276
277
/// AppLayerDecoderEventsSetEventRaw wrapper.
278
47.2M
pub fn sc_app_layer_decoder_events_set_event_raw(
279
47.2M
    events: *mut *mut AppLayerDecoderEvents, event: u8)
280
{
281
    unsafe {
282
47.2M
        if let Some(c) = SC {
283
47.2M
            (c.AppLayerDecoderEventsSetEventRaw)(events, event);
284
47.2M
        }
285
    }
286
47.2M
}
287
288
/// AppLayerDecoderEventsFreeEvents wrapper.
289
38.4M
pub fn sc_app_layer_decoder_events_free_events(
290
38.4M
    events: *mut *mut AppLayerDecoderEvents)
291
{
292
    unsafe {
293
38.4M
        if let Some(c) = SC {
294
38.4M
            (c.AppLayerDecoderEventsFreeEvents)(events);
295
38.4M
        }
296
    }
297
38.4M
}
298
299
/// Opaque flow type (defined in C)
300
pub enum Flow {}
301
302
// Extern functions operating on Flow.
303
/// cbindgen:ignore
304
extern "C" {
305
    pub fn FlowGetLastTimeAsParts(flow: &Flow, secs: *mut u64, usecs: *mut u64);
306
    pub fn FlowGetFlags(flow: &Flow) -> u32;
307
    pub fn FlowGetSourcePort(flow: &Flow) -> u16;
308
    pub fn FlowGetDestinationPort(flow: &Flow) -> u16;
309
}
310
311
/// Rust implementation of Flow.
312
impl Flow {
313
314
    /// Return the time of the last flow update as a `Duration`
315
    /// since the epoch.
316
6.38M
    pub fn get_last_time(&mut self) -> std::time::Duration {
317
        unsafe {
318
6.38M
            let mut secs: u64 = 0;
319
6.38M
            let mut usecs: u64 = 0;
320
6.38M
            FlowGetLastTimeAsParts(self, &mut secs, &mut usecs);
321
6.38M
            std::time::Duration::new(secs, usecs as u32 * 1000)
322
        }
323
6.38M
    }
324
325
    /// Return the flow flags.
326
11
    pub fn get_flags(&self) -> u32 {
327
11
        unsafe { FlowGetFlags(self) }
328
11
    }
329
330
    /// Return flow ports
331
11
    pub fn get_ports(&self) -> (u16, u16) {
332
11
        unsafe { (FlowGetSourcePort(self), FlowGetDestinationPort(self)) }
333
11
    }
334
}
335
336
#[cfg(test)]
337
mod test {
338
    use super::*;
339
340
    #[test]
341
    fn test_direction() {
342
  assert!(Direction::ToServer.is_to_server());
343
  assert!(!Direction::ToServer.is_to_client());
344
345
  assert!(Direction::ToClient.is_to_client());
346
  assert!(!Direction::ToClient.is_to_server());
347
    }
348
}