use base64::{self, Engine};
use std::ffi::{c_char, CStr};
use std::fs::{File, OpenOptions};
use std::io::{self, BufRead};
use std::mem::transmute;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::Path;
use std::str::FromStr;
#[derive(Copy, Clone)]
pub enum Dataset {}
#[derive(Debug, PartialEq)]
#[repr(C)]
pub struct DataRepType {
pub value: u16,
}
#[derive(Debug)]
#[repr(C)]
pub enum DatasetType {
DSString = 0,
DSMd5,
DSSha256,
DSIpv4,
DSIpv6,
}
extern "C" {
pub fn DatasetAdd(set: &Dataset, data: *const u8, len: u32) -> i32;
pub fn DatasetAddwRep(set: &Dataset, data: *const u8, len: u32, rep: *const DataRepType)
-> i32;
}
#[no_mangle]
pub unsafe extern "C" fn ParseDatasets(
set: &Dataset, name: *const c_char, fname: *const c_char, fmode: *const c_char,
dstype: DatasetType,
) -> i32 {
let file_string = unwrap_or_return!(CStr::from_ptr(fname).to_str(), -2);
let mode = unwrap_or_return!(CStr::from_ptr(fmode).to_str(), -2);
let set_name = unwrap_or_return!(CStr::from_ptr(name).to_str(), -2);
let filename = Path::new(file_string);
let mut no_rep = false;
let mut with_rep = false;
let lines = match read_or_create_file(filename, mode) {
Ok(fp) => fp,
Err(_) => return -1,
};
for line in lines.map_while(Result::ok) {
let v: Vec<&str> = line.split(',').collect();
if v.is_empty() || v.len() > 2 {
continue;
}
if v.len() == 1 {
if with_rep {
SCLogError!(
"Cannot mix dataset and datarep values for set {} in {}",
set_name,
filename.display()
);
return -2;
}
no_rep = true;
} else {
if no_rep {
SCLogError!(
"Cannot mix dataset and datarep values for set {} in {}",
set_name,
filename.display()
);
return -2;
}
with_rep = true;
}
match dstype {
DatasetType::DSString => {
if process_string_set(set, v, set_name, filename, no_rep) == -1 {
continue;
}
}
DatasetType::DSMd5 => {
if process_md5_set(set, v, set_name, filename, no_rep) == -1 {
continue;
}
}
DatasetType::DSSha256 => {
if process_sha256_set(set, v, set_name, filename, no_rep) == -1 {
continue;
}
}
DatasetType::DSIpv4 => {
if process_ipv4_set(set, v, set_name, filename, no_rep) == -1 {
continue;
}
}
DatasetType::DSIpv6 => {
if process_ipv6_set(set, v, set_name, filename, no_rep) == -1 {
continue;
}
}
}
}
0
}
unsafe fn process_string_set(
set: &Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
) -> i32 {
let mut decoded: Vec<u8> = vec![];
if base64::engine::general_purpose::STANDARD
.decode_vec(v[0], &mut decoded)
.is_err()
{
SCFatalErrorOnInit!("bad base64 encoding {} in {}", set_name, filename.display());
return -1;
}
if no_rep {
DatasetAdd(set, decoded.as_ptr(), decoded.len() as u32);
} else if let Ok(val) = v[1].to_string().parse::<u16>() {
let rep: DataRepType = DataRepType { value: val };
DatasetAddwRep(set, decoded.as_ptr(), decoded.len() as u32, &rep);
} else {
SCFatalErrorOnInit!(
"invalid datarep value {} in {}",
set_name,
filename.display()
);
return -1;
}
0
}
unsafe fn process_md5_set(
set: &Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
) -> i32 {
let md5_string = match hex::decode(v[0]) {
Ok(rs) => rs,
Err(_) => return -1,
};
if no_rep {
DatasetAdd(set, md5_string.as_ptr(), 16);
} else if let Ok(val) = v[1].to_string().parse::<u16>() {
let rep: DataRepType = DataRepType { value: val };
DatasetAddwRep(set, md5_string.as_ptr(), 16, &rep);
} else {
SCFatalErrorOnInit!(
"invalid datarep value {} in {}",
set_name,
filename.display()
);
return -1;
}
0
}
unsafe fn process_sha256_set(
set: &Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
) -> i32 {
let sha256_string = match hex::decode(v[0]) {
Ok(rs) => rs,
Err(_) => return -1,
};
if no_rep {
DatasetAdd(set, sha256_string.as_ptr(), 32);
} else if let Ok(val) = v[1].to_string().parse::<u16>() {
let rep: DataRepType = DataRepType { value: val };
DatasetAddwRep(set, sha256_string.as_ptr(), 32, &rep);
} else {
SCFatalErrorOnInit!(
"invalid datarep value {} in {}",
set_name,
filename.display()
);
return -1;
}
0
}
unsafe fn process_ipv4_set(
set: &Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
) -> i32 {
let ipv4 = match Ipv4Addr::from_str(v[0]) {
Ok(a) => a,
Err(_) => {
SCFatalErrorOnInit!("invalid Ipv4 value {} in {}", set_name, filename.display());
return -1;
}
};
if no_rep {
DatasetAdd(set, ipv4.octets().as_ptr(), 4);
} else if let Ok(val) = v[1].to_string().parse::<u16>() {
let rep: DataRepType = DataRepType { value: val };
DatasetAddwRep(set, ipv4.octets().as_ptr(), 4, &rep);
} else {
SCFatalErrorOnInit!(
"invalid datarep value {} in {}",
set_name,
filename.display()
);
return -1;
}
0
}
unsafe fn process_ipv6_set(
set: &Dataset, v: Vec<&str>, set_name: &str, filename: &Path, no_rep: bool,
) -> i32 {
let ipv6 = match Ipv6Addr::from_str(v[0]) {
Ok(a) => a,
Err(_) => {
SCFatalErrorOnInit!("invalid Ipv6 value {} in {}", set_name, filename.display());
return -1;
}
};
let mut fin_ipv6 = ipv6;
if ipv6.to_ipv4_mapped().is_some() {
let ipv6_octets = ipv6.octets();
let mut internal_ipv6: [u8; 16] = [0; 16];
internal_ipv6[0] = ipv6_octets[12];
internal_ipv6[1] = ipv6_octets[13];
internal_ipv6[2] = ipv6_octets[14];
internal_ipv6[3] = ipv6_octets[15];
let [s0, s1, s2, s3, s4, s5, s6, s7] =
unsafe { transmute::<[u8; 16], [u16; 8]>(internal_ipv6) };
fin_ipv6 = [
u16::from_be(s0),
u16::from_be(s1),
u16::from_be(s2),
u16::from_be(s3),
u16::from_be(s4),
u16::from_be(s5),
u16::from_be(s6),
u16::from_be(s7),
]
.into();
}
if no_rep {
DatasetAdd(set, fin_ipv6.octets().as_ptr(), 16);
} else if let Ok(val) = v[1].to_string().parse::<u16>() {
let rep: DataRepType = DataRepType { value: val };
DatasetAddwRep(set, fin_ipv6.octets().as_ptr(), 16, &rep);
} else {
SCFatalErrorOnInit!(
"invalid datarep value {} in {}",
set_name,
filename.display()
);
return -1;
}
0
}
fn read_or_create_file<P>(filename: P, fmode: &str) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file: File = if fmode == "r" {
File::open(filename)?
} else {
OpenOptions::new()
.append(true)
.create(true)
.read(true)
.open(filename)?
};
Ok(io::BufReader::new(file).lines())
}