• 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

14.8
/rust/src/detect/datasets.rs
1
/* Copyright (C) 2025 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: Shivani Bhardwaj <shivani@oisf.net>
19

20
//! This module exposes items from the datasets C code to Rust.
21

22
use base64::{self, Engine};
23
use std::ffi::{c_char, CStr};
24
use std::fs::{File, OpenOptions};
25
use std::io::{self, BufRead};
26
use std::mem::transmute;
27
use std::net::{Ipv4Addr, Ipv6Addr};
28
use std::path::Path;
29
use std::str::FromStr;
30

31
#[derive(Debug)]
32
#[repr(C)]
33
pub enum DatasetType {
34
    DSString = 0,
35
    DSMd5,
36
    DSSha256,
37
    DSIpv4,
38
    DSIpv6,
39
}
40

41
use suricata_sys::sys::{Dataset, SCDatasetAdd, SCDatasetAddwRep};
42

43
#[no_mangle]
44
pub unsafe extern "C" fn ParseDatasets(
10,375✔
45
    set: &mut Dataset, name: *const c_char, fname: *const c_char, fmode: *const c_char,
10,375✔
46
    dstype: DatasetType,
10,375✔
47
) -> i32 {
10,375✔
48
    let file_string = unwrap_or_return!(CStr::from_ptr(fname).to_str(), -2);
10,375✔
49
    let mode = unwrap_or_return!(CStr::from_ptr(fmode).to_str(), -2);
10,375✔
50
    let set_name = unwrap_or_return!(CStr::from_ptr(name).to_str(), -2);
10,375✔
51
    let filename = Path::new(file_string);
10,354✔
52
    let mut no_rep = false;
10,354✔
53
    let mut with_rep = false;
10,354✔
54
    let lines = match read_or_create_file(filename, mode) {
10,354✔
55
        Ok(fp) => fp,
129✔
56
        Err(_) => return -1,
10,225✔
57
    };
58
    for line in lines.map_while(Result::ok) {
129✔
UNCOV
59
        let v: Vec<&str> = line.split(',').collect();
×
UNCOV
60
        // Ignore empty and invalid lines in dataset/rep file
×
UNCOV
61
        if v.is_empty() || v.len() > 2 {
×
62
            continue;
×
UNCOV
63
        }
×
UNCOV
64

×
UNCOV
65
        if v.len() == 1 {
×
UNCOV
66
            if with_rep {
×
UNCOV
67
                SCLogError!(
×
UNCOV
68
                    "Cannot mix dataset and datarep values for set {} in {}",
×
UNCOV
69
                    set_name,
×
UNCOV
70
                    filename.display()
×
71
                );
UNCOV
72
                return -2;
×
UNCOV
73
            }
×
UNCOV
74
            // Dataset
×
UNCOV
75
            no_rep = true;
×
76
        } else {
UNCOV
77
            if no_rep {
×
UNCOV
78
                SCLogError!(
×
UNCOV
79
                    "Cannot mix dataset and datarep values for set {} in {}",
×
UNCOV
80
                    set_name,
×
UNCOV
81
                    filename.display()
×
82
                );
UNCOV
83
                return -2;
×
UNCOV
84
            }
×
UNCOV
85
            // Datarep
×
UNCOV
86
            with_rep = true;
×
87
        }
UNCOV
88
        match dstype {
×
89
            DatasetType::DSString => {
UNCOV
90
                if process_string_set(set, v, set_name, filename, no_rep) == -1 {
×
91
                    continue;
×
UNCOV
92
                }
×
93
            }
94
            DatasetType::DSMd5 => {
UNCOV
95
                if process_md5_set(set, v, set_name, filename, no_rep) == -1 {
×
96
                    continue;
×
UNCOV
97
                }
×
98
            }
99
            DatasetType::DSSha256 => {
UNCOV
100
                if process_sha256_set(set, v, set_name, filename, no_rep) == -1 {
×
101
                    continue;
×
UNCOV
102
                }
×
103
            }
104
            DatasetType::DSIpv4 => {
UNCOV
105
                if process_ipv4_set(set, v, set_name, filename, no_rep) == -1 {
×
106
                    continue;
×
UNCOV
107
                }
×
108
            }
109
            DatasetType::DSIpv6 => {
UNCOV
110
                if process_ipv6_set(set, v, set_name, filename, no_rep) == -1 {
×
111
                    continue;
×
UNCOV
112
                }
×
113
            }
114
        }
115
    }
116

117
    0
129✔
118
}
10,375✔
119

UNCOV
120
unsafe fn process_string_set(
×
UNCOV
121
    set: &mut Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
×
UNCOV
122
) -> i32 {
×
UNCOV
123
    let mut decoded: Vec<u8> = vec![];
×
UNCOV
124
    if base64::engine::general_purpose::STANDARD
×
UNCOV
125
        .decode_vec(v[0], &mut decoded)
×
UNCOV
126
        .is_err()
×
127
    {
UNCOV
128
        SCFatalErrorOnInit!("bad base64 encoding {} in {}", set_name, filename.display());
×
UNCOV
129
        return -1;
×
UNCOV
130
    }
×
UNCOV
131
    if no_rep {
×
UNCOV
132
        SCDatasetAdd(set, decoded.as_ptr(), decoded.len() as u32);
×
UNCOV
133
    } else if let Ok(val) = v[1].to_string().parse::<u16>() {
×
UNCOV
134
        SCDatasetAddwRep(set, decoded.as_ptr(), decoded.len() as u32, &val);
×
UNCOV
135
    } else {
×
UNCOV
136
        SCFatalErrorOnInit!(
×
UNCOV
137
            "invalid datarep value {} in {}",
×
UNCOV
138
            set_name,
×
UNCOV
139
            filename.display()
×
UNCOV
140
        );
×
UNCOV
141
        return -1;
×
142
    }
UNCOV
143
    0
×
UNCOV
144
}
×
145

UNCOV
146
unsafe fn process_md5_set(
×
UNCOV
147
    set: &mut Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
×
UNCOV
148
) -> i32 {
×
UNCOV
149
    let md5_string = match hex::decode(v[0]) {
×
UNCOV
150
        Ok(rs) => rs,
×
151
        Err(_) => return -1,
×
152
    };
153

UNCOV
154
    if no_rep {
×
155
        SCDatasetAdd(set, md5_string.as_ptr(), 16);
×
UNCOV
156
    } else if let Ok(val) = v[1].to_string().parse::<u16>() {
×
UNCOV
157
        SCDatasetAddwRep(set, md5_string.as_ptr(), 16, &val);
×
UNCOV
158
    } else {
×
UNCOV
159
        SCFatalErrorOnInit!(
×
UNCOV
160
            "invalid datarep value {} in {}",
×
UNCOV
161
            set_name,
×
UNCOV
162
            filename.display()
×
UNCOV
163
        );
×
UNCOV
164
        return -1;
×
165
    }
UNCOV
166
    0
×
UNCOV
167
}
×
168

UNCOV
169
unsafe fn process_sha256_set(
×
UNCOV
170
    set: &mut Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
×
UNCOV
171
) -> i32 {
×
UNCOV
172
    let sha256_string = match hex::decode(v[0]) {
×
UNCOV
173
        Ok(rs) => rs,
×
174
        Err(_) => return -1,
×
175
    };
176

UNCOV
177
    if no_rep {
×
178
        SCDatasetAdd(set, sha256_string.as_ptr(), 32);
×
UNCOV
179
    } else if let Ok(val) = v[1].to_string().parse::<u16>() {
×
UNCOV
180
        SCDatasetAddwRep(set, sha256_string.as_ptr(), 32, &val);
×
UNCOV
181
    } else {
×
182
        SCFatalErrorOnInit!(
×
183
            "invalid datarep value {} in {}",
×
184
            set_name,
×
185
            filename.display()
×
186
        );
×
187
        return -1;
×
188
    }
UNCOV
189
    0
×
UNCOV
190
}
×
191

UNCOV
192
unsafe fn process_ipv4_set(
×
UNCOV
193
    set: &mut Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
×
UNCOV
194
) -> i32 {
×
UNCOV
195
    let ipv4 = match Ipv4Addr::from_str(v[0]) {
×
UNCOV
196
        Ok(a) => a,
×
197
        Err(_) => {
198
            SCFatalErrorOnInit!("invalid Ipv4 value {} in {}", set_name, filename.display());
×
199
            return -1;
×
200
        }
201
    };
UNCOV
202
    if no_rep {
×
UNCOV
203
        SCDatasetAdd(set, ipv4.octets().as_ptr(), 4);
×
UNCOV
204
    } else if let Ok(val) = v[1].to_string().parse::<u16>() {
×
205
        SCDatasetAddwRep(set, ipv4.octets().as_ptr(), 4, &val);
×
206
    } else {
×
207
        SCFatalErrorOnInit!(
×
208
            "invalid datarep value {} in {}",
×
209
            set_name,
×
210
            filename.display()
×
211
        );
×
212
        return -1;
×
213
    }
UNCOV
214
    0
×
UNCOV
215
}
×
216

UNCOV
217
unsafe fn process_ipv6_set(
×
UNCOV
218
    set: &mut Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
×
UNCOV
219
) -> i32 {
×
UNCOV
220
    let ipv6 = match Ipv6Addr::from_str(v[0]) {
×
UNCOV
221
        Ok(a) => a,
×
222
        Err(_) => {
UNCOV
223
            SCFatalErrorOnInit!("invalid Ipv6 value {} in {}", set_name, filename.display());
×
UNCOV
224
            return -1;
×
225
        }
226
    };
UNCOV
227
    let mut fin_ipv6 = ipv6;
×
UNCOV
228

×
UNCOV
229
    if ipv6.to_ipv4_mapped().is_some() {
×
UNCOV
230
        let ipv6_octets = ipv6.octets();
×
UNCOV
231
        let mut internal_ipv6: [u8; 16] = [0; 16];
×
UNCOV
232
        internal_ipv6[0] = ipv6_octets[12];
×
UNCOV
233
        internal_ipv6[1] = ipv6_octets[13];
×
UNCOV
234
        internal_ipv6[2] = ipv6_octets[14];
×
UNCOV
235
        internal_ipv6[3] = ipv6_octets[15];
×
UNCOV
236

×
UNCOV
237
        // [u8; 16] is always safe to transmute to [u16; 8]
×
UNCOV
238
        let [s0, s1, s2, s3, s4, s5, s6, s7] =
×
UNCOV
239
            unsafe { transmute::<[u8; 16], [u16; 8]>(internal_ipv6) };
×
UNCOV
240
        fin_ipv6 = [
×
UNCOV
241
            u16::from_be(s0),
×
UNCOV
242
            u16::from_be(s1),
×
UNCOV
243
            u16::from_be(s2),
×
UNCOV
244
            u16::from_be(s3),
×
UNCOV
245
            u16::from_be(s4),
×
UNCOV
246
            u16::from_be(s5),
×
UNCOV
247
            u16::from_be(s6),
×
UNCOV
248
            u16::from_be(s7),
×
UNCOV
249
        ]
×
UNCOV
250
        .into();
×
UNCOV
251
    }
×
UNCOV
252
    if no_rep {
×
UNCOV
253
        SCDatasetAdd(set, fin_ipv6.octets().as_ptr(), 16);
×
UNCOV
254
    } else if let Ok(val) = v[1].to_string().parse::<u16>() {
×
255
        SCDatasetAddwRep(set, fin_ipv6.octets().as_ptr(), 16, &val);
×
256
    } else {
×
257
        SCFatalErrorOnInit!(
×
258
            "invalid datarep value {} in {}",
×
259
            set_name,
×
260
            filename.display()
×
261
        );
×
262
        return -1;
×
263
    }
UNCOV
264
    0
×
UNCOV
265
}
×
266

267
fn read_or_create_file<P>(filename: P, fmode: &str) -> io::Result<io::Lines<io::BufReader<File>>>
10,354✔
268
where
10,354✔
269
    P: AsRef<Path>,
10,354✔
270
{
10,354✔
271
    let file: File = if fmode == "r" {
10,354✔
272
        File::open(filename)?
9,638✔
273
    } else {
274
        OpenOptions::new()
716✔
275
            .append(true)
716✔
276
            .create(true)
716✔
277
            .read(true)
716✔
278
            .open(filename)?
716✔
279
    };
280
    Ok(io::BufReader::new(file).lines())
129✔
281
}
10,354✔
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