マルチスレッド対応
This commit is contained in:
189
Cargo.lock
generated
189
Cargo.lock
generated
@@ -63,7 +63,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
@@ -112,6 +112,12 @@ version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.6.4"
|
||||
@@ -137,6 +143,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
@@ -198,7 +210,7 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -229,7 +241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"maybe-uninit",
|
||||
@@ -244,7 +256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
@@ -412,7 +424,7 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
@@ -436,7 +448,7 @@ version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
@@ -494,6 +506,15 @@ dependencies = [
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
@@ -529,9 +550,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.77"
|
||||
version = "0.2.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||
checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
@@ -539,13 +560,22 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -579,6 +609,38 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2182a122f3b7f3f5329cb1972cee089ba2459a0a80a56935e6e674f096f8d839"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.2"
|
||||
@@ -625,6 +687,43 @@ version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.5",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.18"
|
||||
@@ -804,6 +903,15 @@ version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.9"
|
||||
@@ -934,6 +1042,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
version = "0.8.0"
|
||||
@@ -961,6 +1078,23 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
@@ -994,10 +1128,10 @@ version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.1.57",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
@@ -1079,6 +1213,37 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d56477f6ed99e10225f38f9f75f872f29b8b8bd8c0b946f63345bb144e9eeda"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
@@ -1193,11 +1358,13 @@ dependencies = [
|
||||
"flate2",
|
||||
"lazy_static",
|
||||
"linked-hash-map",
|
||||
"num_cpus",
|
||||
"quick-xml 0.17.2",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ lazy_static = "1.4.0"
|
||||
chrono = "0.4.19"
|
||||
yaml-rust = "0.4"
|
||||
linked-hash-map = "0.5.3"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
num_cpus = "1.13.0"
|
||||
|
||||
[target.x86_64-pc-windows-gnu]
|
||||
linker = "x86_64-w64-mingw32-gcc"
|
||||
|
||||
@@ -4,10 +4,15 @@ use crate::detections::print::MESSAGES;
|
||||
use crate::detections::rule;
|
||||
use crate::detections::rule::RuleNode;
|
||||
use crate::yaml::ParseYaml;
|
||||
|
||||
use evtx::err;
|
||||
use evtx::{EvtxParser, ParserSettings, SerializedEvtxRecord};
|
||||
use serde_json::{Error, Value};
|
||||
use std::path::PathBuf;
|
||||
use tokio::runtime;
|
||||
use tokio::{spawn, task::JoinHandle};
|
||||
|
||||
use std::{fs::File, sync::Arc};
|
||||
use std::{path::PathBuf, time::Instant};
|
||||
|
||||
const DIRPATH_RULES: &str = "rules";
|
||||
|
||||
@@ -26,74 +31,17 @@ impl Detection {
|
||||
}
|
||||
|
||||
// parse rule files
|
||||
let mut selection_rules = self.parse_rule_files();
|
||||
if selection_rules.is_empty() {
|
||||
let rules = self.parse_rule_files();
|
||||
if rules.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// serialize from evtx files to json
|
||||
let evtx_records = self.serialize_evtx_to_jsons(evtx_files);
|
||||
// transform from evtx files into json
|
||||
let records = self.evtx_to_jsons(evtx_files);
|
||||
|
||||
// select rule files and collect message
|
||||
let mut message = MESSAGES.lock().unwrap();
|
||||
selection_rules.iter_mut().for_each(|rule| {
|
||||
evtx_records.iter().for_each(|event_record| {
|
||||
if !rule.select(event_record) {
|
||||
return;
|
||||
}
|
||||
|
||||
message.insert(
|
||||
event_record,
|
||||
rule.yaml["title"].as_str().unwrap_or("").to_string(),
|
||||
rule.yaml["output"].as_str().unwrap_or("").to_string(),
|
||||
)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// serialize evtx files to json
|
||||
fn serialize_evtx_to_jsons(&self, evtx_files: Vec<PathBuf>) -> Vec<Value> {
|
||||
return evtx_files
|
||||
.iter()
|
||||
.filter_map(|evtx_file| {
|
||||
// convert to evtx parser
|
||||
match EvtxParser::from_path(evtx_file) {
|
||||
Ok(parser) => Option::Some(parser),
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
return Option::None;
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(|mut cur| {
|
||||
let mut parse_config = ParserSettings::default();
|
||||
parse_config = parse_config.separate_json_attributes(true);
|
||||
cur = cur.with_configuration(parse_config);
|
||||
let ret: Vec<err::Result<SerializedEvtxRecord<String>>> =
|
||||
cur.records_json().collect();
|
||||
return ret;
|
||||
})
|
||||
.flatten()
|
||||
.filter_map(|json_record| {
|
||||
// convert from evtx parser to evtx json string records
|
||||
if json_record.is_ok() {
|
||||
return Option::Some(json_record.unwrap());
|
||||
} else {
|
||||
eprintln!("{}", json_record.unwrap_err());
|
||||
return Option::None;
|
||||
}
|
||||
})
|
||||
.filter_map(|json_record| {
|
||||
// serialize json from json string
|
||||
let result_json: Result<Value, Error> = serde_json::from_str(&json_record.data); //// https://rust-lang-nursery.github.io/rust-cookbook/encoding/complex.html
|
||||
if result_json.is_err() {
|
||||
eprintln!("{}", result_json.unwrap_err());
|
||||
return Option::None;
|
||||
} else {
|
||||
return result_json.ok();
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(self.execute_rule(rules, records));
|
||||
}
|
||||
|
||||
fn parse_rule_files(&self) -> Vec<RuleNode> {
|
||||
@@ -135,4 +83,187 @@ impl Detection {
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
// evtxファイルをjsonに変換する。
|
||||
fn evtx_to_jsons(&mut self, evtx_files: Vec<PathBuf>) -> Vec<Value> {
|
||||
// EvtxParserを生成する。
|
||||
let evtx_parsers: Vec<EvtxParser<File>> = evtx_files
|
||||
.iter()
|
||||
.filter_map(|evtx_file| {
|
||||
// convert to evtx parser
|
||||
match EvtxParser::from_path(evtx_file) {
|
||||
Ok(parser) => Option::Some(parser),
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
return Option::None;
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let xml_records = runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(self.evtx_to_xml(evtx_parsers));
|
||||
|
||||
return runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(self.xml_to_json(xml_records));
|
||||
}
|
||||
|
||||
// evtxファイルからxmlを生成する。
|
||||
async fn evtx_to_xml(
|
||||
&mut self,
|
||||
evtx_parsers: Vec<EvtxParser<File>>,
|
||||
) -> Vec<SerializedEvtxRecord<String>> {
|
||||
// evtx_parser.records_json()でevtxをxmlに変換するJobを作成
|
||||
let handles: Vec<JoinHandle<Vec<err::Result<SerializedEvtxRecord<String>>>>> = evtx_parsers
|
||||
.into_iter()
|
||||
.map(|mut evtx_parser| {
|
||||
return spawn(async move {
|
||||
let mut parse_config = ParserSettings::default();
|
||||
parse_config = parse_config.separate_json_attributes(true);
|
||||
evtx_parser = evtx_parser.with_configuration(parse_config);
|
||||
let values = evtx_parser.records_json().collect();
|
||||
return values;
|
||||
});
|
||||
})
|
||||
.collect();
|
||||
|
||||
// 作成したjobを実行し(handle.awaitの部分)、スレッドの実行時にエラーが発生した場合、標準エラー出力に出しておく
|
||||
let mut ret = vec![];
|
||||
for handle in handles {
|
||||
let future_result = handle.await;
|
||||
if future_result.is_err() {
|
||||
eprintln!("{}", future_result.unwrap_err());
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.push(future_result.unwrap());
|
||||
}
|
||||
|
||||
// xmlの変換でエラーが出た場合、標準エラー出力に出しておく
|
||||
return ret
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|parse_result| {
|
||||
if parse_result.is_err() {
|
||||
eprintln!("{}", parse_result.unwrap_err());
|
||||
return Option::None;
|
||||
}
|
||||
|
||||
return Option::Some(parse_result.unwrap());
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
async fn xml_to_json(&mut self, xml_records: Vec<SerializedEvtxRecord<String>>) -> Vec<Value> {
|
||||
// xmlからjsonに変換するJobを作成
|
||||
let handles: Vec<JoinHandle<Result<Value, Error>>> = xml_records
|
||||
.into_iter()
|
||||
.map(|xml_record| {
|
||||
return spawn(async move {
|
||||
return serde_json::from_str(&xml_record.data);
|
||||
});
|
||||
})
|
||||
.collect();
|
||||
|
||||
// 作成したjobを実行し(handle.awaitの部分)、スレッドの実行時にエラーが発生した場合、標準エラー出力に出しておく
|
||||
let mut ret = vec![];
|
||||
for handle in handles {
|
||||
let future_result = handle.await;
|
||||
if future_result.is_err() {
|
||||
eprintln!("{}", future_result.unwrap_err());
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.push(future_result.unwrap());
|
||||
}
|
||||
|
||||
// xmlの変換でエラーが出た場合、標準エラー出力に出しておく
|
||||
return ret
|
||||
.into_iter()
|
||||
.filter_map(|parse_result| {
|
||||
if parse_result.is_err() {
|
||||
eprintln!("{}", parse_result.unwrap_err());
|
||||
return Option::None;
|
||||
}
|
||||
|
||||
return Option::Some(parse_result.unwrap());
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
async fn execute_rule(&mut self, rules: Vec<RuleNode>, records: Vec<Value>) {
|
||||
// 排他制御と所有権共有のため、recordをRwLockとArcで囲む
|
||||
// recordは不変参照(mutが不要)なので、不変参照なら複数スレッドが同時にロックを取得できるようにRwLockを用いている。
|
||||
// RwLockの代わりにMutexを使うこともできるが、これは不変参照であっても同時に1スレッドしかロックを取得できず、パフォーマンスが良くないと思う。
|
||||
let mut records_arcs = vec![];
|
||||
for record_chunk in Detection::chunks(records, num_cpus::get() * 4) {
|
||||
let record_chunk_arc = Arc::new(record_chunk);
|
||||
records_arcs.push(record_chunk_arc);
|
||||
}
|
||||
|
||||
// 所有権共有のため、ruleをArcで囲む
|
||||
let rules_arc = Arc::new(rules);
|
||||
|
||||
// ルール実行するスレッドを作成。
|
||||
let mut handles = vec![];
|
||||
for record_chunk_arc in &records_arcs {
|
||||
let records_arc_clone = Arc::clone(&record_chunk_arc);
|
||||
let rules_clones = Arc::clone(&rules_arc);
|
||||
|
||||
let handle: JoinHandle<Vec<bool>> = spawn(async move {
|
||||
let mut ret = vec![];
|
||||
for record in records_arc_clone.iter() {
|
||||
for rule in rules_clones.iter() {
|
||||
if rule.select(record) {
|
||||
// TODO ここはtrue/falseじゃなくて、ruleとrecordのタプルをretにpushする実装に変更したい。
|
||||
ret.push(true);
|
||||
} else {
|
||||
ret.push(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// メッセージを追加する。これを上記のspawnの中でやると、ロックの取得で逆に時間がかかるので、外に出す
|
||||
let mut message = MESSAGES.lock().unwrap();
|
||||
let mut handles_ite = handles.into_iter();
|
||||
for record_chunk_arc in &records_arcs {
|
||||
let mut handles_ret_ite = handles_ite.next().unwrap().await.unwrap().into_iter();
|
||||
for rule in rules_arc.iter() {
|
||||
for record_arc in record_chunk_arc.iter() {
|
||||
if handles_ret_ite.next().unwrap() == true {
|
||||
// TODO メッセージが多いと、rule.select()よりもこの処理の方が時間かかる。
|
||||
message.insert(
|
||||
record_arc,
|
||||
rule.yaml["title"].as_str().unwrap_or("").to_string(),
|
||||
rule.yaml["output"].as_str().unwrap_or("").to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 配列を指定したサイズで分割する。Vector.chunksと同じ動作をするが、Vectorの関数だとinto的なことができないので自作
|
||||
fn chunks(ary: Vec<Value>, size: usize) -> Vec<Vec<Value>> {
|
||||
let arylen = ary.len();
|
||||
let mut ite = ary.into_iter();
|
||||
|
||||
let mut ret = vec![];
|
||||
for i in 0..arylen {
|
||||
if i % size == 0 {
|
||||
ret.push(vec![]);
|
||||
ret.iter_mut().last().unwrap().push(ite.next().unwrap());
|
||||
} else {
|
||||
ret.iter_mut().last().unwrap().push(ite.next().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ fn concat_selection_key(key_list: &Vec<String>) -> String {
|
||||
});
|
||||
}
|
||||
|
||||
fn parse_selection(yaml: &Yaml) -> Option<Box<dyn SelectionNode>> {
|
||||
fn parse_selection(yaml: &Yaml) -> Option<Box<dyn SelectionNode + Send>> {
|
||||
// TODO detection-selectionが存在しない場合のチェック
|
||||
let selection_yaml = &yaml["detection"]["selection"];
|
||||
if selection_yaml.is_badvalue() {
|
||||
@@ -45,7 +45,10 @@ fn parse_selection(yaml: &Yaml) -> Option<Box<dyn SelectionNode>> {
|
||||
return Option::Some(parse_selection_recursively(vec![], &selection_yaml));
|
||||
}
|
||||
|
||||
fn parse_selection_recursively(key_list: Vec<String>, yaml: &Yaml) -> Box<dyn SelectionNode> {
|
||||
fn parse_selection_recursively(
|
||||
key_list: Vec<String>,
|
||||
yaml: &Yaml,
|
||||
) -> Box<dyn SelectionNode + Send> {
|
||||
if yaml.as_hash().is_some() {
|
||||
// 連想配列はAND条件と解釈する
|
||||
let yaml_hash = yaml.as_hash().unwrap();
|
||||
@@ -80,6 +83,8 @@ pub struct RuleNode {
|
||||
detection: Option<DetectionNode>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for RuleNode {}
|
||||
|
||||
impl RuleNode {
|
||||
pub fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
let mut errmsgs: Vec<String> = vec![];
|
||||
@@ -105,11 +110,11 @@ impl RuleNode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select(&mut self, event_record: &Value) -> bool {
|
||||
pub fn select(&self, event_record: &Value) -> bool {
|
||||
let selection = self
|
||||
.detection
|
||||
.as_mut()
|
||||
.and_then(|detect_node| detect_node.selection.as_mut());
|
||||
.as_ref()
|
||||
.and_then(|detect_node| detect_node.selection.as_ref());
|
||||
if selection.is_none() {
|
||||
return false;
|
||||
}
|
||||
@@ -120,7 +125,7 @@ impl RuleNode {
|
||||
|
||||
// Ruleファイルのdetectionを表すノード
|
||||
struct DetectionNode {
|
||||
pub selection: Option<Box<dyn SelectionNode>>,
|
||||
pub selection: Option<Box<dyn SelectionNode + Send>>,
|
||||
}
|
||||
|
||||
impl DetectionNode {
|
||||
@@ -135,7 +140,7 @@ impl DetectionNode {
|
||||
|
||||
// Ruleファイルの detection- selection配下のノードはこのtraitを実装する。
|
||||
trait SelectionNode {
|
||||
fn select(&mut self, event_record: &Value) -> bool;
|
||||
fn select(&self, event_record: &Value) -> bool;
|
||||
fn init(&mut self) -> Result<(), Vec<String>>;
|
||||
}
|
||||
|
||||
@@ -144,6 +149,8 @@ struct AndSelectionNode {
|
||||
pub child_nodes: Vec<Box<dyn SelectionNode>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AndSelectionNode {}
|
||||
|
||||
impl AndSelectionNode {
|
||||
pub fn new() -> AndSelectionNode {
|
||||
return AndSelectionNode {
|
||||
@@ -153,8 +160,8 @@ impl AndSelectionNode {
|
||||
}
|
||||
|
||||
impl SelectionNode for AndSelectionNode {
|
||||
fn select(&mut self, event_record: &Value) -> bool {
|
||||
return self.child_nodes.iter_mut().all(|child_node| {
|
||||
fn select(&self, event_record: &Value) -> bool {
|
||||
return self.child_nodes.iter().all(|child_node| {
|
||||
return child_node.select(event_record);
|
||||
});
|
||||
}
|
||||
@@ -192,6 +199,8 @@ struct OrSelectionNode {
|
||||
pub child_nodes: Vec<Box<dyn SelectionNode>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for OrSelectionNode {}
|
||||
|
||||
impl OrSelectionNode {
|
||||
pub fn new() -> OrSelectionNode {
|
||||
return OrSelectionNode {
|
||||
@@ -201,8 +210,8 @@ impl OrSelectionNode {
|
||||
}
|
||||
|
||||
impl SelectionNode for OrSelectionNode {
|
||||
fn select(&mut self, event_record: &Value) -> bool {
|
||||
return self.child_nodes.iter_mut().any(|child_node| {
|
||||
fn select(&self, event_record: &Value) -> bool {
|
||||
return self.child_nodes.iter().any(|child_node| {
|
||||
return child_node.select(event_record);
|
||||
});
|
||||
}
|
||||
@@ -242,6 +251,8 @@ struct LeafSelectionNode {
|
||||
matcher: Option<Box<dyn LeafMatcher>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for LeafSelectionNode {}
|
||||
|
||||
impl LeafSelectionNode {
|
||||
fn new(key_list: Vec<String>, value_yaml: Yaml) -> LeafSelectionNode {
|
||||
return LeafSelectionNode {
|
||||
@@ -272,7 +283,7 @@ impl LeafSelectionNode {
|
||||
}
|
||||
|
||||
impl SelectionNode for LeafSelectionNode {
|
||||
fn select(&mut self, event_record: &Value) -> bool {
|
||||
fn select(&self, event_record: &Value) -> bool {
|
||||
if self.matcher.is_none() {
|
||||
return false;
|
||||
}
|
||||
@@ -300,7 +311,7 @@ impl SelectionNode for LeafSelectionNode {
|
||||
if self.key_list.len() > 0 && self.key_list[0].to_string() == "EventData" {
|
||||
let values = utils::get_event_value(&"Event.EventData.Data".to_string(), event_record);
|
||||
if values.is_none() {
|
||||
return self.matcher.as_mut().unwrap().is_match(Option::None);
|
||||
return self.matcher.as_ref().unwrap().is_match(Option::None);
|
||||
}
|
||||
|
||||
// 配列じゃなくて、文字列や数値等の場合は普通通りに比較する。
|
||||
@@ -309,7 +320,7 @@ impl SelectionNode for LeafSelectionNode {
|
||||
{
|
||||
return self
|
||||
.matcher
|
||||
.as_mut()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(Option::Some(eventdata_data));
|
||||
}
|
||||
@@ -323,17 +334,17 @@ impl SelectionNode for LeafSelectionNode {
|
||||
.any(|ary_element| {
|
||||
return self
|
||||
.matcher
|
||||
.as_mut()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_match(Option::Some(ary_element));
|
||||
});
|
||||
} else {
|
||||
return self.matcher.as_mut().unwrap().is_match(Option::None);
|
||||
return self.matcher.as_ref().unwrap().is_match(Option::None);
|
||||
}
|
||||
}
|
||||
|
||||
let event_value = self.get_event_value(event_record);
|
||||
return self.matcher.as_mut().unwrap().is_match(event_value);
|
||||
return self.matcher.as_ref().unwrap().is_match(event_value);
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<(), Vec<String>> {
|
||||
@@ -374,7 +385,7 @@ impl SelectionNode for LeafSelectionNode {
|
||||
trait LeafMatcher {
|
||||
fn is_target_key(&self, key_list: &Vec<String>) -> bool;
|
||||
|
||||
fn is_match(&mut self, event_value: Option<&Value>) -> bool;
|
||||
fn is_match(&self, event_value: Option<&Value>) -> bool;
|
||||
|
||||
fn init(&mut self, key_list: &Vec<String>, select_value: &Yaml) -> Result<(), Vec<String>>;
|
||||
}
|
||||
@@ -449,7 +460,7 @@ impl LeafMatcher for RegexMatcher {
|
||||
return Result::Ok(());
|
||||
}
|
||||
|
||||
fn is_match(&mut self, event_value: Option<&Value>) -> bool {
|
||||
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||
// unwrap_orの引数に""ではなく" "を指定しているのは、
|
||||
// event_valueが文字列じゃない場合にis_event_value_nullの値がfalseになるように、len() == 0とならない値を指定している。
|
||||
let is_event_value_null = event_value.is_none()
|
||||
@@ -504,7 +515,7 @@ impl LeafMatcher for MinlengthMatcher {
|
||||
return Result::Ok(());
|
||||
}
|
||||
|
||||
fn is_match(&mut self, event_value: Option<&Value>) -> bool {
|
||||
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||
return match event_value.unwrap_or(&Value::Null) {
|
||||
Value::String(s) => s.len() as i64 >= self.min_len,
|
||||
Value::Number(n) => n.to_string().len() as i64 >= self.min_len,
|
||||
@@ -564,7 +575,7 @@ impl LeafMatcher for RegexesFileMatcher {
|
||||
return Result::Ok(());
|
||||
}
|
||||
|
||||
fn is_match(&mut self, event_value: Option<&Value>) -> bool {
|
||||
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||
return match event_value.unwrap_or(&Value::Null) {
|
||||
Value::String(s) => !utils::check_regex(s, 0, &self.regexes_csv_content).is_empty(),
|
||||
Value::Number(n) => {
|
||||
@@ -626,7 +637,7 @@ impl LeafMatcher for WhitelistFileMatcher {
|
||||
return Result::Ok(());
|
||||
}
|
||||
|
||||
fn is_match(&mut self, event_value: Option<&Value>) -> bool {
|
||||
fn is_match(&self, event_value: Option<&Value>) -> bool {
|
||||
return match event_value.unwrap_or(&Value::Null) {
|
||||
Value::String(s) => utils::check_whitelist(s, &self.whitelist_csv_content),
|
||||
Value::Number(n) => utils::check_whitelist(&n.to_string(), &self.whitelist_csv_content),
|
||||
|
||||
Reference in New Issue
Block a user