Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/detect/byte_math.rs
Line
Count
Source
1
/* Copyright (C) 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
// Author: Jeff Lucovsky <jlucovsky@oisf.net>
19
20
use crate::detect::error::RuleParseError;
21
use crate::detect::parser::{parse_token, take_until_whitespace};
22
use std::ffi::{CStr, CString};
23
use std::os::raw::c_char;
24
25
use nom7::bytes::complete::tag;
26
use nom7::character::complete::multispace0;
27
use nom7::sequence::preceded;
28
use nom7::{Err, IResult};
29
use std::str;
30
31
pub const DETECT_BYTEMATH_FLAG_RELATIVE: u8 = 0x01;
32
pub const DETECT_BYTEMATH_FLAG_STRING: u8 = 0x02;
33
pub const DETECT_BYTEMATH_FLAG_BITMASK: u8 = 0x04;
34
pub const DETECT_BYTEMATH_FLAG_ENDIAN: u8 = 0x08;
35
pub const DETECT_BYTEMATH_FLAG_RVALUE_VAR: u8 = 0x10;
36
pub const DETECT_BYTEMATH_FLAG_NBYTES_VAR: u8 = 0x20;
37
38
// Ensure required values are provided
39
const DETECT_BYTEMATH_FLAG_NBYTES: u8 = 0x1;
40
const DETECT_BYTEMATH_FLAG_OFFSET: u8 = 0x2;
41
const DETECT_BYTEMATH_FLAG_OPER: u8 = 0x4;
42
const DETECT_BYTEMATH_FLAG_RVALUE: u8 = 0x8;
43
const DETECT_BYTEMATH_FLAG_RESULT: u8 = 0x10;
44
const DETECT_BYTEMATH_FLAG_REQUIRED: u8 = DETECT_BYTEMATH_FLAG_RESULT
45
    | DETECT_BYTEMATH_FLAG_RVALUE
46
    | DETECT_BYTEMATH_FLAG_NBYTES
47
    | DETECT_BYTEMATH_FLAG_OFFSET
48
    | DETECT_BYTEMATH_FLAG_OPER;
49
50
#[repr(u8)]
51
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
52
// operators: +, -, /, *, <<, >>
53
pub enum ByteMathOperator {
54
    OperatorNone = 1,
55
    Addition = 2,
56
    Subtraction = 3,
57
    Division = 4,
58
    Multiplication = 5,
59
    LeftShift = 6,
60
    RightShift = 7,
61
}
62
63
#[repr(u8)]
64
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
65
// endian <big|little|dce>
66
pub enum ByteMathEndian {
67
    _EndianNone = 0,
68
    BigEndian = 1,
69
    LittleEndian = 2,
70
    EndianDCE = 3,
71
}
72
pub const DETECT_BYTEMATH_ENDIAN_DEFAULT: ByteMathEndian = ByteMathEndian::BigEndian;
73
74
#[repr(u8)]
75
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
76
pub enum ByteMathBase {
77
    _BaseNone = 0,
78
    BaseOct = 8,
79
    BaseDec = 10,
80
    BaseHex = 16,
81
}
82
const BASE_DEFAULT: ByteMathBase = ByteMathBase::BaseDec;
83
84
// Fixed position parameter count: bytes, offset, oper, rvalue, result
85
// result is not parsed with the fixed position parameters as it's
86
// often swapped with optional parameters
87
pub const DETECT_BYTEMATH_FIXED_PARAM_COUNT: usize = 5;
88
// Optional parameters: endian, relative, string, dce, bitmask
89
pub const DETECT_BYTEMATH_MAX_PARAM_COUNT: usize = 10;
90
91
#[derive(Debug)]
92
enum ResultValue {
93
    Numeric(u64),
94
    String(String),
95
}
96
97
#[repr(C)]
98
#[derive(Debug)]
99
pub struct DetectByteMathData {
100
    rvalue_str: *const c_char,
101
    result: *const c_char,
102
    nbytes_str: *const c_char,
103
    rvalue: u32,
104
    offset: i32,
105
    bitmask_val: u32,
106
    bitmask_shift_count: u16,
107
    id: u16,
108
    flags: u8,
109
    local_id: u8,
110
    nbytes: u8,
111
    oper: ByteMathOperator,
112
    endian: ByteMathEndian, // big, little, dce
113
    base: ByteMathBase,     // From string or dce
114
}
115
116
impl Drop for DetectByteMathData {
117
17.0k
    fn drop(&mut self) {
118
        unsafe {
119
17.0k
            if !self.result.is_null() {
120
5.88k
                let _ = CString::from_raw(self.result as *mut c_char);
121
11.1k
            }
122
17.0k
            if !self.rvalue_str.is_null() {
123
3.30k
                let _ = CString::from_raw(self.rvalue_str as *mut c_char);
124
13.7k
            }
125
17.0k
            if !self.nbytes_str.is_null() {
126
530
                let _ = CString::from_raw(self.nbytes_str as *mut c_char);
127
16.5k
            }
128
        }
129
17.0k
    }
130
}
131
132
impl Default for DetectByteMathData {
133
8.51k
    fn default() -> Self {
134
8.51k
        DetectByteMathData {
135
8.51k
            local_id: 0,
136
8.51k
            flags: 0,
137
8.51k
            nbytes: 0,
138
8.51k
            offset: 0,
139
8.51k
            oper: ByteMathOperator::OperatorNone,
140
8.51k
            rvalue_str: std::ptr::null_mut(),
141
8.51k
            nbytes_str: std::ptr::null_mut(),
142
8.51k
            rvalue: 0,
143
8.51k
            result: std::ptr::null_mut(),
144
8.51k
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
145
8.51k
            base: BASE_DEFAULT,
146
8.51k
            bitmask_val: 0,
147
8.51k
            bitmask_shift_count: 0,
148
8.51k
            id: 0,
149
8.51k
        }
150
8.51k
    }
151
}
152
153
impl DetectByteMathData {
154
8.51k
    pub fn new() -> Self {
155
8.51k
        Self {
156
8.51k
            ..Default::default()
157
8.51k
        }
158
8.51k
    }
159
}
160
161
16
fn get_string_value(value: &str) -> Result<ByteMathBase, ()> {
162
16
    let res = match value {
163
16
        "hex" => ByteMathBase::BaseHex,
164
15
        "oct" => ByteMathBase::BaseOct,
165
14
        "dec" => ByteMathBase::BaseDec,
166
13
        _ => return Err(()),
167
    };
168
169
3
    Ok(res)
170
16
}
171
172
6.69k
fn get_oper_value(value: &str) -> Result<ByteMathOperator, ()> {
173
6.69k
    let res = match value {
174
6.69k
        "+" => ByteMathOperator::Addition,
175
5.48k
        "-" => ByteMathOperator::Subtraction,
176
3.42k
        "/" => ByteMathOperator::Division,
177
1.62k
        "*" => ByteMathOperator::Multiplication,
178
1.61k
        "<<" => ByteMathOperator::LeftShift,
179
943
        ">>" => ByteMathOperator::RightShift,
180
39
        _ => return Err(()),
181
    };
182
183
6.65k
    Ok(res)
184
6.69k
}
185
186
11
fn get_endian_value(value: &str) -> Result<ByteMathEndian, ()> {
187
11
    let res = match value {
188
11
        "big" => ByteMathEndian::BigEndian,
189
10
        "little" => ByteMathEndian::LittleEndian,
190
10
        "dce" => ByteMathEndian::EndianDCE,
191
9
        _ => return Err(()),
192
    };
193
194
2
    Ok(res)
195
11
}
196
197
// Parsed as a u64 for validation with u32 {min,max} so values greater than uint32
198
// are not treated as a string value.
199
13.5k
fn parse_var(input: &str) -> IResult<&str, ResultValue, RuleParseError<&str>> {
200
13.5k
    let (input, value) = parse_token(input)?;
201
13.5k
    if let Ok(val) = value.parse::<u64>() {
202
9.73k
        Ok((input, ResultValue::Numeric(val)))
203
    } else {
204
3.83k
        Ok((input, ResultValue::String(value.to_string())))
205
    }
206
13.5k
}
207
208
10.0k
fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseError<&str>> {
209
    // Inner utility function for easy error creation.
210
4.19k
    fn make_error(reason: String) -> nom7::Err<RuleParseError<&'static str>> {
211
4.19k
        Err::Error(RuleParseError::InvalidByteMath(reason))
212
4.19k
    }
213
10.0k
    let (_, values) = nom7::multi::separated_list1(
214
10.0k
        tag(","),
215
10.0k
        preceded(multispace0, nom7::bytes::complete::is_not(",")),
216
10.0k
    )(input)?;
217
218
10.0k
    if values.len() < DETECT_BYTEMATH_FIXED_PARAM_COUNT
219
8.62k
        || values.len() > DETECT_BYTEMATH_MAX_PARAM_COUNT
220
    {
221
1.50k
        return Err(make_error(format!("Incorrect argument string; at least {} values must be specified but no more than {}: {:?}",
222
1.50k
            DETECT_BYTEMATH_FIXED_PARAM_COUNT, DETECT_BYTEMATH_MAX_PARAM_COUNT, input)));
223
8.51k
    }
224
225
8.51k
    let mut required_flags: u8 = 0;
226
8.51k
    let mut byte_math = DetectByteMathData::new();
227
    //for value in &values[0..] {
228
43.0k
    for value in values {
229
37.2k
        let (mut val, mut name) = take_until_whitespace(value)?;
230
37.2k
        val = val.trim();
231
37.2k
        name = name.trim();
232
37.2k
        match name {
233
37.2k
            "oper" => {
234
6.70k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_OPER) {
235
11
                    return Err(make_error("operator already set".to_string()));
236
6.69k
                }
237
6.69k
                byte_math.oper = match get_oper_value(val) {
238
6.65k
                    Ok(val) => val,
239
                    Err(_) => {
240
39
                        return Err(make_error(format!("unknown oper value {}", val)));
241
                    }
242
                };
243
6.65k
                required_flags |= DETECT_BYTEMATH_FLAG_OPER;
244
            }
245
30.5k
            "result" => {
246
5.89k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_RESULT) {
247
6
                    return Err(make_error("result already set".to_string()));
248
5.88k
                }
249
5.88k
                let tmp: String = val
250
5.88k
                    .parse()
251
5.88k
                    .map_err(|_| make_error(format!("invalid result: {}", val)))?;
252
5.88k
                match CString::new(tmp) {
253
5.88k
                    Ok(strval) => {
254
5.88k
                        byte_math.result = strval.into_raw();
255
5.88k
                        required_flags |= DETECT_BYTEMATH_FLAG_RESULT;
256
5.88k
                    }
257
                    _ => {
258
0
                        return Err(make_error(
259
0
                            "parse string not safely convertible to C".to_string(),
260
0
                        ));
261
                    }
262
                }
263
            }
264
24.6k
            "rvalue" => {
265
6.56k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_RVALUE) {
266
1
                    return Err(make_error("rvalue already set".to_string()));
267
6.56k
                }
268
6.56k
                let (_, res) = parse_var(val)?;
269
6.56k
                match res {
270
3.25k
                    ResultValue::Numeric(val) => {
271
3.25k
                        if val >= u32::MIN.into() && val <= u32::MAX.into() {
272
3.22k
                            byte_math.rvalue = val as u32
273
                        } else {
274
33
                            return Err(make_error(format!(
275
33
                                "invalid rvalue value: must be between {} and {}: {}",
276
33
                                1,
277
33
                                u32::MAX,
278
33
                                val
279
33
                            )));
280
                        }
281
                    }
282
3.30k
                    ResultValue::String(val) => match CString::new(val) {
283
3.30k
                        Ok(newval) => {
284
3.30k
                            byte_math.rvalue_str = newval.into_raw();
285
3.30k
                            byte_math.flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR;
286
3.30k
                        }
287
                        _ => {
288
0
                            return Err(make_error(
289
0
                                "parse string not safely convertible to C".to_string(),
290
0
                            ))
291
                        }
292
                    },
293
                }
294
6.52k
                required_flags |= DETECT_BYTEMATH_FLAG_RVALUE;
295
            }
296
18.0k
            "endian" => {
297
11
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) {
298
0
                    return Err(make_error("endianess already set".to_string()));
299
11
                }
300
11
                byte_math.endian = match get_endian_value(val) {
301
2
                    Ok(val) => val,
302
                    Err(_) => {
303
9
                        return Err(make_error(format!("invalid endian value: {}", val)));
304
                    }
305
                };
306
2
                byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
307
            }
308
18.0k
            "dce" => {
309
18
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) {
310
2
                    return Err(make_error("endianess already set".to_string()));
311
16
                }
312
16
                byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
313
16
                byte_math.endian = ByteMathEndian::EndianDCE;
314
            }
315
18.0k
            "string" => {
316
17
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_STRING) {
317
1
                    return Err(make_error("string already set".to_string()));
318
16
                }
319
16
                byte_math.base = match get_string_value(val) {
320
3
                    Ok(val) => val,
321
                    Err(_) => {
322
13
                        return Err(make_error(format!("invalid string value: {}", val)));
323
                    }
324
                };
325
3
                byte_math.flags |= DETECT_BYTEMATH_FLAG_STRING;
326
            }
327
18.0k
            "relative" => {
328
1.26k
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
329
1
                    return Err(make_error("relative already set".to_string()));
330
1.26k
                }
331
1.26k
                byte_math.flags |= DETECT_BYTEMATH_FLAG_RELATIVE;
332
            }
333
16.7k
            "bitmask" => {
334
581
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_BITMASK) {
335
2
                    return Err(make_error("bitmask already set".to_string()));
336
579
                }
337
579
                let trimmed = if val.starts_with("0x") || val.starts_with("0X") {
338
323
                    &val[2..]
339
                } else {
340
256
                    val
341
                };
342
343
579
                let val = u32::from_str_radix(trimmed, 16)
344
579
                    .map_err(|_| make_error(format!("invalid bitmask value: {}", value)))?;
345
535
                byte_math.bitmask_val = val;
346
535
                byte_math.flags |= DETECT_BYTEMATH_FLAG_BITMASK;
347
            }
348
16.1k
            "offset" => {
349
7.17k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_OFFSET) {
350
370
                    return Err(make_error("offset already set".to_string()));
351
6.80k
                }
352
6.80k
                byte_math.offset = val
353
6.80k
                    .parse::<i32>()
354
6.80k
                    .map_err(|_| make_error(format!("invalid offset value: {}", val)))?;
355
6.76k
                if byte_math.offset > 65535 || byte_math.offset < -65535 {
356
29
                    return Err(make_error(format!(
357
29
                        "invalid offset value: must be between -65535 and 65535: {}",
358
29
                        val
359
29
                    )));
360
6.74k
                }
361
6.74k
                required_flags |= DETECT_BYTEMATH_FLAG_OFFSET;
362
            }
363
8.99k
            "bytes" => {
364
7.01k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_NBYTES) {
365
1
                    return Err(make_error("nbytes already set".to_string()));
366
7.01k
                }
367
7.01k
                let (_, res) = parse_var(val)?;
368
7.00k
                match res {
369
6.47k
                    ResultValue::Numeric(val) => {
370
6.47k
                        if (1..=10).contains(&val) {
371
6.38k
                            byte_math.nbytes = val as u8
372
                        } else {
373
94
                            return Err(make_error(format!(
374
94
                                "invalid nbytes value: must be between 1 and 10: {}",
375
94
                                val
376
94
                            )));
377
                        }
378
                    }
379
530
                    ResultValue::String(val) => match CString::new(val) {
380
530
                        Ok(newval) => {
381
530
                            byte_math.nbytes_str = newval.into_raw();
382
530
                            byte_math.flags |= DETECT_BYTEMATH_FLAG_NBYTES_VAR;
383
530
                        }
384
                        _ => {
385
0
                            return Err(make_error(
386
0
                                "parse string not safely convertible to C".to_string(),
387
0
                            ))
388
                        }
389
                    },
390
                }
391
6.91k
                required_flags |= DETECT_BYTEMATH_FLAG_NBYTES;
392
            }
393
            _ => {
394
1.98k
                return Err(make_error(format!("unknown byte_math keyword: {}", name)));
395
            }
396
        };
397
    }
398
399
    // Ensure required values are present
400
5.83k
    if (required_flags & DETECT_BYTEMATH_FLAG_REQUIRED) != DETECT_BYTEMATH_FLAG_REQUIRED {
401
16
        return Err(make_error(format!(
402
16
            "required byte_math parameters missing: \"{:?}\"",
403
16
            input
404
16
        )));
405
5.82k
    }
406
407
    // Using left/right shift further restricts the value of nbytes. Note that
408
    // validation has already ensured nbytes is in [1..10]
409
5.82k
    match byte_math.oper {
410
        ByteMathOperator::LeftShift | ByteMathOperator::RightShift => {
411
1.41k
            if byte_math.nbytes > 4 {
412
6
                return Err(make_error(format!("nbytes must be 1 through 4 (inclusive) when used with \"<<\" or \">>\"; {} is not valid", byte_math.nbytes)));
413
1.40k
            }
414
        }
415
4.40k
        _ => {}
416
    };
417
418
5.81k
    Ok((input, byte_math))
419
10.0k
}
420
421
/// Intermediary function between the C code and the parsing functions.
422
#[no_mangle]
423
10.0k
pub unsafe extern "C" fn ScByteMathParse(c_arg: *const c_char) -> *mut DetectByteMathData {
424
10.0k
    if c_arg.is_null() {
425
0
        return std::ptr::null_mut();
426
10.0k
    }
427
428
10.0k
    let arg = match CStr::from_ptr(c_arg).to_str() {
429
10.0k
        Ok(arg) => arg,
430
        Err(_) => {
431
0
            return std::ptr::null_mut();
432
        }
433
    };
434
10.0k
    match parse_bytemath(arg) {
435
5.81k
        Ok((_, detect)) => return Box::into_raw(Box::new(detect)),
436
4.27k
        Err(_) => return std::ptr::null_mut(),
437
    }
438
10.0k
}
439
440
#[no_mangle]
441
10.0k
pub unsafe extern "C" fn ScByteMathFree(ptr: *mut DetectByteMathData) {
442
10.0k
    if !ptr.is_null() {
443
5.81k
        let _ = Box::from_raw(ptr);
444
5.81k
    }
445
10.0k
}
446
447
#[cfg(test)]
448
mod tests {
449
    use super::*;
450
    // structure equality only used by test cases
451
    impl PartialEq for DetectByteMathData {
452
        fn eq(&self, other: &Self) -> bool {
453
            let mut res: bool = false;
454
455
            if !self.rvalue_str.is_null() && !other.rvalue_str.is_null() {
456
                let s_val = unsafe { CStr::from_ptr(self.rvalue_str) };
457
                let o_val = unsafe { CStr::from_ptr(other.rvalue_str) };
458
                res = s_val == o_val;
459
            } else if !self.rvalue_str.is_null() || !other.rvalue_str.is_null() {
460
                return false;
461
            }
462
463
            if !self.nbytes_str.is_null() && !other.nbytes_str.is_null() {
464
                let s_val = unsafe { CStr::from_ptr(self.nbytes_str) };
465
                let o_val = unsafe { CStr::from_ptr(other.nbytes_str) };
466
                res = s_val == o_val;
467
            } else if !self.nbytes_str.is_null() || !other.nbytes_str.is_null() {
468
                return false;
469
            }
470
471
            if !self.result.is_null() && !self.result.is_null() {
472
                let s_val = unsafe { CStr::from_ptr(self.result) };
473
                let o_val = unsafe { CStr::from_ptr(other.result) };
474
                res = s_val == o_val;
475
            } else if !self.result.is_null() || !self.result.is_null() {
476
                return false;
477
            }
478
479
            res && self.local_id == other.local_id
480
                && self.flags == other.flags
481
                && self.nbytes == other.nbytes
482
                && self.offset == other.offset
483
                && self.oper == other.oper
484
                && self.rvalue == other.rvalue
485
                && self.endian == other.endian
486
                && self.base == other.base
487
                && self.bitmask_val == other.bitmask_val
488
                && self.bitmask_shift_count == other.bitmask_shift_count
489
                && self.id == other.id
490
        }
491
    }
492
493
    fn valid_test(
494
        args: &str, nbytes: u8, offset: i32, oper: ByteMathOperator, rvalue_str: &str, nbytes_str: &str, rvalue: u32,
495
        result: &str, base: ByteMathBase, endian: ByteMathEndian, bitmask_val: u32, flags: u8,
496
    ) {
497
        let bmd = DetectByteMathData {
498
            nbytes,
499
            offset,
500
            oper,
501
            rvalue_str: if !rvalue_str.is_empty() {
502
                CString::new(rvalue_str).unwrap().into_raw()
503
            } else {
504
                std::ptr::null_mut()
505
            },
506
            nbytes_str: if !nbytes_str.is_empty() {
507
                CString::new(nbytes_str).unwrap().into_raw()
508
            } else {
509
                std::ptr::null_mut()
510
            },
511
            rvalue,
512
            result: CString::new(result).unwrap().into_raw(),
513
            base,
514
            endian,
515
            bitmask_val,
516
            flags,
517
            ..Default::default()
518
        };
519
520
        let (_, val) = parse_bytemath(args).unwrap();
521
        assert_eq!(val, bmd);
522
    }
523
524
    #[test]
525
    fn test_parser_valid() {
526
        valid_test(
527
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result myresult, dce, string dec",
528
            4,
529
            3933,
530
            ByteMathOperator::Addition,
531
            "myrvalue",
532
            "",
533
            0,
534
            "myresult",
535
            ByteMathBase::BaseDec,
536
            ByteMathEndian::EndianDCE,
537
            0,
538
            DETECT_BYTEMATH_FLAG_RVALUE_VAR
539
                | DETECT_BYTEMATH_FLAG_STRING
540
                | DETECT_BYTEMATH_FLAG_ENDIAN,
541
        );
542
543
        valid_test(
544
            "bytes 4, offset 3933, oper +, rvalue 99, result other, dce, string dec",
545
            4,
546
            3933,
547
            ByteMathOperator::Addition,
548
            "",
549
            "",
550
            99,
551
            "other",
552
            ByteMathBase::BaseDec,
553
            ByteMathEndian::EndianDCE,
554
            0,
555
            DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN,
556
        );
557
558
        valid_test(
559
            "bytes 4, offset -3933, oper +, rvalue myrvalue, result foo",
560
            4,
561
            -3933,
562
            ByteMathOperator::Addition,
563
            "rvalue",
564
            "",
565
            0,
566
            "foo",
567
            BASE_DEFAULT,
568
            ByteMathEndian::BigEndian,
569
            0,
570
            DETECT_BYTEMATH_FLAG_RVALUE_VAR,
571
        );
572
573
        valid_test(
574
            "bytes nbytes_var, offset -3933, oper +, rvalue myrvalue, result foo",
575
            0,
576
            -3933,
577
            ByteMathOperator::Addition,
578
            "rvalue",
579
            "nbytes_var",
580
            0,
581
            "foo",
582
            BASE_DEFAULT,
583
            ByteMathEndian::BigEndian,
584
            0,
585
            DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_NBYTES_VAR,
586
        );
587
588
        // Out of order
589
        valid_test(
590
            "string dec, endian big, result other, rvalue 99, oper +, offset 3933, bytes 4",
591
            4,
592
            3933,
593
            ByteMathOperator::Addition,
594
            "",
595
            "",
596
            99,
597
            "other",
598
            ByteMathBase::BaseDec,
599
            ByteMathEndian::BigEndian,
600
            0,
601
            DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN,
602
        );
603
    }
604
605
    #[test]
606
    fn test_parser_string_valid() {
607
        let mut bmd = DetectByteMathData {
608
            nbytes: 4,
609
            offset: 3933,
610
            oper: ByteMathOperator::Addition,
611
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
612
            rvalue: 0,
613
            result: CString::new("foo").unwrap().into_raw(),
614
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
615
            base: ByteMathBase::BaseDec,
616
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING,
617
            ..Default::default()
618
        };
619
620
        let (_, val) = parse_bytemath(
621
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string dec",
622
        ).unwrap();
623
        assert_eq!(val, bmd);
624
625
        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR;
626
        bmd.base = BASE_DEFAULT;
627
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo").unwrap();
628
        assert_eq!(val, bmd);
629
630
        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING;
631
        bmd.base = ByteMathBase::BaseHex;
632
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hex").unwrap();
633
        assert_eq!(val, bmd);
634
635
        bmd.base = ByteMathBase::BaseOct;
636
        let (_, val) = parse_bytemath(
637
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string oct",
638
        ).unwrap();
639
        assert_eq!(val, bmd);
640
    }
641
642
    #[test]
643
    fn test_parser_string_invalid() {
644
        assert!(
645
            parse_bytemath(
646
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string decimal"
647
            )
648
            .is_err()
649
        );
650
        assert!(
651
            parse_bytemath(
652
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hexadecimal"
653
            )
654
            .is_err()
655
        );
656
        assert!(
657
            parse_bytemath(
658
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string octal"
659
            )
660
            .is_err()
661
        );
662
    }
663
664
    #[test]
665
    // bytes must be between 1 and 10; when combined with rshift/lshift, must be 4 or less
666
    fn test_parser_bytes_invalid() {
667
        assert!(
668
            parse_bytemath("bytes 0, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
669
        );
670
        assert!(
671
            parse_bytemath("bytes 11, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
672
        );
673
        assert!(
674
            parse_bytemath("bytes 5, offset 3933, oper >>, rvalue myrvalue, result foo").is_err()
675
        );
676
        assert!(
677
            parse_bytemath("bytes 5, offset 3933, oper <<, rvalue myrvalue, result foo").is_err()
678
        );
679
    }
680
681
    #[test]
682
    fn test_parser_bitmask_invalid() {
683
        assert!(
684
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x")
685
                .is_err()
686
        );
687
        assert!(
688
            parse_bytemath(
689
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask x12345678"
690
            )
691
            .is_err()
692
        );
693
        assert!(
694
            parse_bytemath(
695
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask X12345678"
696
            )
697
            .is_err()
698
        );
699
        assert!(
700
            parse_bytemath(
701
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x123456789012"
702
            )
703
            .is_err()
704
        );
705
        assert!(
706
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0q")
707
                .is_err()
708
        );
709
        assert!(
710
            parse_bytemath(
711
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask maple"
712
            )
713
            .is_err()
714
        );
715
        assert!(
716
            parse_bytemath(
717
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0xGHIJKLMN"
718
            )
719
            .is_err()
720
        );
721
        assert!(
722
            parse_bytemath(
723
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask #*#*@-"
724
            )
725
            .is_err()
726
        );
727
    }
728
729
    #[test]
730
    fn test_parser_bitmask_valid() {
731
        let mut bmd = DetectByteMathData {
732
            nbytes: 4,
733
            offset: 3933,
734
            oper: ByteMathOperator::Addition,
735
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
736
            rvalue: 0,
737
            result: CString::new("foo").unwrap().into_raw(),
738
            endian: ByteMathEndian::BigEndian,
739
            base: BASE_DEFAULT,
740
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_BITMASK,
741
            ..Default::default()
742
        };
743
744
        bmd.bitmask_val = 0x12345678;
745
        let (_, val) = parse_bytemath(
746
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x12345678",
747
        ).unwrap();
748
        assert_eq!(val, bmd);
749
750
751
        bmd.bitmask_val = 0xffff1234;
752
        let (_, val) = parse_bytemath(
753
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask ffff1234",
754
        ).unwrap();
755
        assert_eq!(val, bmd);
756
757
758
        bmd.bitmask_val = 0xffff1234;
759
        let (_, val) = parse_bytemath(
760
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0Xffff1234",
761
        ).unwrap();
762
        assert_eq!(val, bmd);
763
764
    }
765
    #[test]
766
    fn test_parser_endian_valid() {
767
        let mut bmd = DetectByteMathData {
768
            nbytes: 4,
769
            offset: 3933,
770
            oper: ByteMathOperator::Addition,
771
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
772
            rvalue: 0,
773
            result: CString::new("foo").unwrap().into_raw(),
774
            endian: ByteMathEndian::BigEndian,
775
            base: BASE_DEFAULT,
776
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_ENDIAN,
777
            ..Default::default()
778
        };
779
780
        let (_, val) = parse_bytemath(
781
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big",
782
        ).unwrap();
783
        assert_eq!(val, bmd);
784
785
786
        bmd.endian = ByteMathEndian::LittleEndian;
787
        let (_, val) = parse_bytemath(
788
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian little",
789
        ).unwrap();
790
        assert_eq!(val, bmd);
791
792
793
        bmd.endian = ByteMathEndian::EndianDCE;
794
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, dce").unwrap();
795
        assert_eq!(val, bmd);
796
797
798
        bmd.endian = DETECT_BYTEMATH_ENDIAN_DEFAULT;
799
        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR;
800
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo").unwrap();
801
        assert_eq!(val, bmd);
802
803
    }
804
805
    #[test]
806
    fn test_parser_endian_invalid() {
807
        assert!(
808
            parse_bytemath(
809
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian bigger"
810
            )
811
            .is_err()
812
        );
813
        assert!(
814
            parse_bytemath(
815
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian smaller"
816
            )
817
            .is_err()
818
        );
819
820
        // endianess can only be specified once
821
        assert!(
822
            parse_bytemath(
823
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big, dce"
824
            )
825
            .is_err()
826
        );
827
        assert!(
828
            parse_bytemath(
829
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, endian big"
830
            )
831
            .is_err()
832
        );
833
        assert!(
834
            parse_bytemath(
835
                "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, dce"
836
            )
837
            .is_err()
838
        );
839
    }
840
841
    #[test]
842
    fn test_parser_oper_valid() {
843
        let mut bmd = DetectByteMathData {
844
            nbytes: 4,
845
            offset: 3933,
846
            oper: ByteMathOperator::Addition,
847
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
848
            rvalue: 0,
849
            result: CString::new("foo").unwrap().into_raw(),
850
            endian: ByteMathEndian::BigEndian,
851
            base: BASE_DEFAULT,
852
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR,
853
            ..Default::default()
854
        };
855
856
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo").unwrap();
857
        assert_eq!(val, bmd);
858
859
860
        bmd.oper = ByteMathOperator::Subtraction;
861
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper -, rvalue myrvalue, result foo").unwrap();
862
        assert_eq!(val, bmd);
863
864
865
        bmd.oper = ByteMathOperator::Multiplication;
866
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper *, rvalue myrvalue, result foo").unwrap();
867
        assert_eq!(val, bmd);
868
869
        bmd.oper = ByteMathOperator::Division;
870
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper /, rvalue myrvalue, result foo").unwrap();
871
        assert_eq!(val, bmd);
872
873
        bmd.oper = ByteMathOperator::RightShift;
874
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper >>, rvalue myrvalue, result foo").unwrap();
875
        assert_eq!(val, bmd);
876
877
        bmd.oper = ByteMathOperator::LeftShift;
878
        let (_, val) = parse_bytemath("bytes 4, offset 3933, oper <<, rvalue myrvalue, result foo").unwrap();
879
        assert_eq!(val, bmd);
880
881
    }
882
883
    #[test]
884
    fn test_parser_oper_invalid() {
885
        assert!(
886
            parse_bytemath("bytes 4, offset 0, oper !, rvalue myvalue, result foo").is_err()
887
        );
888
        assert!(
889
            parse_bytemath("bytes 4, offset 0, oper ^, rvalue myvalue, result foo").is_err()
890
        );
891
        assert!(
892
            parse_bytemath("bytes 4, offset 0, oper <>, rvalue myvalue, result foo").is_err()
893
        );
894
        assert!(
895
            parse_bytemath("bytes 4, offset 0, oper ><, rvalue myvalue, result foo").is_err()
896
        );
897
        assert!(
898
            parse_bytemath("bytes 4, offset 0, oper <, rvalue myvalue, result foo").is_err()
899
        );
900
        assert!(
901
            parse_bytemath("bytes 4, offset 0, oper >, rvalue myvalue, result foo").is_err()
902
        );
903
    }
904
905
    #[test]
906
    fn test_parser_rvalue_valid() {
907
        let mut bmd = DetectByteMathData {
908
            nbytes: 4,
909
            offset: 47303,
910
            oper: ByteMathOperator::Multiplication,
911
            rvalue_str: std::ptr::null_mut(),
912
            rvalue: 4294967295,
913
            result: CString::new("foo").unwrap().into_raw(),
914
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
915
            base: BASE_DEFAULT,
916
            ..Default::default()
917
        };
918
919
        let (_, val) = parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967295      , result foo").unwrap();
920
        assert_eq!(val, bmd);
921
922
923
        bmd.rvalue = 1;
924
        let (_, val) = parse_bytemath("bytes 4, offset 47303, oper *, rvalue 1, result foo").unwrap();
925
        assert_eq!(val, bmd);
926
927
        bmd.rvalue = 0;
928
        let (_, val) = parse_bytemath("bytes 4, offset 47303, oper *, rvalue 0, result foo").unwrap();
929
        assert_eq!(val, bmd);
930
931
    }
932
933
    #[test]
934
    fn test_parser_rvalue_invalid() {
935
        assert!(
936
            parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967296, result foo").is_err()
937
        );
938
    }
939
940
    #[test]
941
    fn test_parser_offset_valid() {
942
        let mut bmd = DetectByteMathData {
943
            nbytes: 4,
944
            offset: -65535,
945
            oper: ByteMathOperator::Multiplication,
946
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
947
            rvalue: 0,
948
            result: CString::new("foo").unwrap().into_raw(),
949
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
950
            base: BASE_DEFAULT,
951
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR,
952
            ..Default::default()
953
        };
954
955
        let (_, val) = parse_bytemath("bytes 4, offset -65535, oper *, rvalue myrvalue, result foo").unwrap();
956
        assert_eq!(val, bmd);
957
958
959
        bmd.offset = 65535;
960
        let (_, val) = parse_bytemath("bytes 4, offset 65535, oper *, rvalue myrvalue, result foo").unwrap();
961
        assert_eq!(val, bmd);
962
963
    }
964
965
    #[test]
966
    // offset: numeric values must be between -65535 and 65535
967
    fn test_parser_offset_invalid() {
968
        assert!(
969
            parse_bytemath("bytes 4, offset -70000, oper *, rvalue myvalue, result foo").is_err()
970
        );
971
        assert!(
972
            parse_bytemath("bytes 4, offset 70000, oper +, rvalue myvalue, result foo").is_err()
973
        );
974
    }
975
976
    #[test]
977
    fn test_parser_incomplete_args() {
978
        assert!(parse_bytemath("").is_err());
979
        assert!(parse_bytemath("bytes 4").is_err());
980
        assert!(parse_bytemath("bytes 4, offset 0").is_err());
981
        assert!(parse_bytemath("bytes 4, offset 0, oper <<").is_err());
982
    }
983
984
    #[test]
985
    fn test_parser_missing_required() {
986
        assert!(
987
            parse_bytemath("endian big, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
988
        );
989
        assert!(
990
            parse_bytemath("bytes 4, endian big, oper +, rvalue myrvalue, result foo,").is_err()
991
        );
992
        assert!(
993
            parse_bytemath("bytes 4, offset 3933, endian big, rvalue myrvalue, result foo")
994
                .is_err()
995
        );
996
        assert!(
997
            parse_bytemath("bytes 4, offset 3933, oper +, endian big, result foo").is_err()
998
        );
999
        assert!(
1000
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, endian big").is_err()
1001
        );
1002
    }
1003
1004
    #[test]
1005
    fn test_parser_invalid_args() {
1006
        assert!(parse_bytemath("monkey banana").is_err());
1007
        assert!(parse_bytemath("bytes nan").is_err());
1008
        assert!(parse_bytemath("bytes 4, offset nan").is_err());
1009
        assert!(parse_bytemath("bytes 4, offset 0, three 3, four 4, five 5, six 6, seven 7, eight 8, nine 9, ten 10, eleven 11").is_err());
1010
        assert!(
1011
            parse_bytemath("bytes 4, offset 0, oper ><, rvalue myrvalue").is_err()
1012
        );
1013
        assert!(
1014
            parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, endian endian").is_err()
1015
        );
1016
    }
1017
    #[test]
1018
    fn test_parser_multiple() {
1019
        assert!(
1020
            parse_bytemath(
1021
                "bytes 4, bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, endian big"
1022
            )
1023
            .is_err()
1024
        );
1025
        assert!(
1026
            parse_bytemath(
1027
                "bytes 4, offset 0, offset 0, oper +, rvalue myrvalue, result myresult, endian big"
1028
            )
1029
            .is_err()
1030
        );
1031
        assert!(
1032
            parse_bytemath(
1033
                "bytes 4, offset 0, oper +, oper +, rvalue myrvalue, result myresult, endian big"
1034
            )
1035
            .is_err()
1036
        );
1037
        assert!(parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, rvalue myrvalue, result myresult, endian big").is_err());
1038
        assert!(parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, result myresult, endian big").is_err());
1039
        assert!(parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, endian big, endian big").is_err());
1040
    }
1041
}