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

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

48.33
/rust/src/util.rs
1
/* Copyright (C) 2020 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
//! Utility module.
19

20
use std::borrow::Cow;
21
use std::ffi::CStr;
22
use std::os::raw::c_char;
23

24
use nom8::bytes::complete::take_while1;
25
use nom8::character::complete::char;
26
use nom8::combinator::verify;
27
use nom8::multi::many1_count;
28
use nom8::{AsChar, IResult, Parser};
29

30
use humantime::parse_duration;
31

32
#[no_mangle]
33
pub unsafe extern "C" fn SCCheckUtf8(val: *const c_char) -> bool {
7,363,146✔
34
    CStr::from_ptr(val).to_str().is_ok()
7,363,146✔
35
}
7,363,146✔
36

37
fn is_alphanumeric_or_hyphen(chr: u8) -> bool {
375,165✔
38
    return chr.is_alphanum() || chr == b'-';
375,165✔
39
}
375,165✔
40

41
fn parse_domain_label(i: &[u8]) -> IResult<&[u8], ()> {
11,162✔
42
    let (i, _) = verify(take_while1(is_alphanumeric_or_hyphen), |x: &[u8]| {
11,162✔
43
        x[0].is_alpha() && x[x.len() - 1] != b'-'
11,100✔
44
    }).parse(i)?;
11,162✔
45
    return Ok((i, ()));
11,084✔
46
}
11,162✔
47

48
fn parse_subdomain(input: &[u8]) -> IResult<&[u8], ()> {
10,490✔
49
    let (input, _) = parse_domain_label(input)?;
10,490✔
50
    let (input, _) = char('.').parse(input)?;
10,444✔
51
    return Ok((input, ()));
9,701✔
52
}
10,490✔
53

54
fn parse_domain(input: &[u8]) -> IResult<&[u8], ()> {
789✔
55
    let (input, _) = many1_count(parse_subdomain).parse(input)?;
789✔
56
    let (input, _) = parse_domain_label(input)?;
672✔
57
    return Ok((input, ()));
640✔
58
}
789✔
59

60
#[no_mangle]
61
pub unsafe extern "C" fn SCValidateDomain(input: *const u8, in_len: u32) -> u32 {
789✔
62
    let islice = build_slice!(input, in_len as usize);
789✔
63
    if let Ok((rem, _)) = parse_domain(islice) {
789✔
64
        return (islice.len() - rem.len()) as u32;
640✔
65
    }
149✔
66
    return 0;
149✔
67
}
789✔
68

69
/// Add 's' suffix if input is only digits, and convert to lowercase if needed.
UNCOV
70
fn duration_unit_normalize(input: &str) -> Cow<'_, str> {
×
UNCOV
71
    if input.bytes().all(|b| b.is_ascii_digit()) {
×
UNCOV
72
        let mut owned = String::with_capacity(input.len() + 1);
×
UNCOV
73
        owned.push_str(input);
×
UNCOV
74
        owned.push('s');
×
UNCOV
75
        return Cow::Owned(owned);
×
UNCOV
76
    }
×
UNCOV
77

×
UNCOV
78
    if input.bytes().any(|b| b.is_ascii_uppercase()) {
×
UNCOV
79
        Cow::Owned(input.to_ascii_lowercase())
×
80
    } else {
UNCOV
81
        Cow::Borrowed(input)
×
82
    }
UNCOV
83
}
×
84

85
/// Reads a C string from `input`, parses it, and writes the result to `*res`.
86
/// Returns 0 on success (result written to *res), -1 otherwise.
87
#[no_mangle]
UNCOV
88
pub unsafe extern "C" fn SCParseTimeDuration(input: *const c_char, res: *mut u64) -> i32 {
×
UNCOV
89
    if input.is_null() || res.is_null() {
×
UNCOV
90
        return -1;
×
UNCOV
91
    }
×
92

UNCOV
93
    let input_str = match CStr::from_ptr(input).to_str() {
×
UNCOV
94
        Ok(s) => s,
×
95
        Err(_) => return -1,
×
96
    };
97

UNCOV
98
    let trimmed = input_str.trim();
×
UNCOV
99
    if trimmed.is_empty() {
×
100
        return -1;
×
UNCOV
101
    }
×
UNCOV
102

×
UNCOV
103
    let normalized = duration_unit_normalize(trimmed);
×
UNCOV
104
    match parse_duration(normalized.as_ref()) {
×
UNCOV
105
        Ok(duration) => {
×
UNCOV
106
            *res = duration.as_secs();
×
UNCOV
107
            0
×
108
        }
UNCOV
109
        Err(_) => -1,
×
110
    }
UNCOV
111
}
×
112

113
#[cfg(test)]
114
mod tests {
115

116
    use super::*;
117
    use std::ffi::CString;
118
    use std::ptr::{null, null_mut};
119

120
    #[test]
121
    fn test_parse_domain() {
122
        let buf0: &[u8] = "a-1.oisf.net more".as_bytes();
123
        let (rem, _) = parse_domain(buf0).unwrap();
124
        // And we should have 5 bytes left.
125
        assert_eq!(rem.len(), 5);
126
        let buf1: &[u8] = "justatext".as_bytes();
127
        assert!(parse_domain(buf1).is_err());
128
        let buf1: &[u8] = "1.com".as_bytes();
129
        assert!(parse_domain(buf1).is_err());
130
        let buf1: &[u8] = "a-.com".as_bytes();
131
        assert!(parse_domain(buf1).is_err());
132
        let buf1: &[u8] = "a(x)y.com".as_bytes();
133
        assert!(parse_domain(buf1).is_err());
134
    }
135

136
    #[test]
137
    fn test_parse_time_valid() {
138
        unsafe {
139
            let mut v: u64 = 0;
140

141
            let s = CString::new("10").unwrap();
142
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
143
            assert_eq!(v, 10);
144

145
            let s = CString::new("0").unwrap();
146
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
147
            assert_eq!(v, 0);
148

149
            let s = CString::new("2H").unwrap();
150
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
151
            assert_eq!(v, 7200);
152

153
            let s = CString::new("1 day").unwrap();
154
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
155
            assert_eq!(v, 86400);
156

157
            let s = CString::new("1w").unwrap();
158
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
159
            assert_eq!(v, 604800);
160

161
            let s = CString::new("1 week").unwrap();
162
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
163
            assert_eq!(v, 604800);
164

165
            let s = CString::new("1y").unwrap();
166
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
167
            assert_eq!(v, 31557600);
168

169
            let s = CString::new("1 year").unwrap();
170
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
171
            assert_eq!(v, 31557600);
172

173
            // max
174
            let s = CString::new("18446744073709551615").unwrap();
175
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), 0);
176
            assert_eq!(v, u64::MAX);
177
        }
178
    }
179

180
    #[test]
181
    fn test_parse_time_duration_invalid() {
182
        unsafe {
183
            let mut v: u64 = 0;
184
            let s = CString::new("10q").unwrap();
185
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), -1);
186

187
            let s = CString::new("abc").unwrap();
188
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), -1);
189

190
            let s = CString::new("-300s").unwrap();
191
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), -1);
192

193
            let s = CString::new("1h -600s").unwrap();
194
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), -1);
195

196
            assert_eq!(SCParseTimeDuration(null(), &mut v), -1);
197
            assert_eq!(SCParseTimeDuration(s.as_ptr(), null_mut()), -1);
198

199
            let overflow_years = (u64::MAX / 31557600) + 1;
200
            let s = CString::new(format!("{}y", overflow_years)).unwrap();
201
            assert_eq!(SCParseTimeDuration(s.as_ptr(), &mut v), -1);
202
        }
203
    }
204
}
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