1 //! Handles stream processing of commands and events.
2 
3 use chrono::NaiveDateTime;
4 use std::collections::BTreeMap;
5 use std::io::Write;
6 
7 use crate::parser::Packet;
8 
9 /// Signals are pre-defined indicators that are seen in a packet stream.
10 pub struct Signal {
11     /// Where in the packet stream we see this signal.
12     pub index: usize,
13 
14     /// Timestamp where this signal is seen.
15     pub ts: NaiveDateTime,
16 
17     /// Tag identifying the signal. Signals must be pre-defined so we're going
18     /// to enforce a static lifetime here.
19     pub tag: &'static str,
20 }
21 
22 /// Trait that describes a single rule processor. A rule should be used to represent a certain type
23 /// of analysis (for example: ACL Connections rule may keep track of all ACL connections and report
24 /// on failed connections).
25 pub trait Rule {
26     /// Process a single packet.
process(&mut self, packet: &Packet)27     fn process(&mut self, packet: &Packet);
28 
29     /// Generate a report for this rule based on the input stream so far. Usually, this should
30     /// report on the instances of this rule that were discovered or any error conditions that are
31     /// relevant to this rule.
report(&self, writer: &mut dyn Write)32     fn report(&self, writer: &mut dyn Write);
33 
34     /// Report on any signals seen by this rule on the input stream so far. Signals are
35     /// structured indicators that specify a specific type of condition that are pre-defined and
36     /// used to bucket interesting behavior. Not all reportable events are signals but all signals
37     /// are reportable events.
report_signals(&self) -> &[Signal]38     fn report_signals(&self) -> &[Signal];
39 }
40 
41 /// Grouping of rules. This is used to make it easier to enable/disable certain rules for
42 /// processing a file.
43 pub struct RuleGroup {
44     rules: Vec<Box<dyn Rule>>,
45 }
46 
47 impl RuleGroup {
new() -> Self48     pub fn new() -> Self {
49         RuleGroup { rules: vec![] }
50     }
51 
add_rule(&mut self, rule: Box<dyn Rule>)52     pub fn add_rule(&mut self, rule: Box<dyn Rule>) {
53         self.rules.push(rule);
54     }
55 
process(&mut self, packet: &Packet)56     pub fn process(&mut self, packet: &Packet) {
57         for rule in &mut self.rules {
58             rule.process(packet);
59         }
60     }
61 
report(&self, writer: &mut dyn Write)62     pub fn report(&self, writer: &mut dyn Write) {
63         for rule in &self.rules {
64             rule.report(writer);
65         }
66     }
67 
report_signals(&self, writer: &mut dyn Write)68     pub fn report_signals(&self, writer: &mut dyn Write) {
69         for rule in &self.rules {
70             for signal in rule.report_signals() {
71                 let _ = writeln!(writer, "({}, {}, {})", signal.index, signal.ts, signal.tag);
72             }
73         }
74     }
75 }
76 /// Main entry point to process input data and run rules on them.
77 pub struct RuleEngine {
78     groups: BTreeMap<String, RuleGroup>,
79 }
80 
81 impl RuleEngine {
new() -> Self82     pub fn new() -> Self {
83         RuleEngine { groups: BTreeMap::new() }
84     }
85 
add_rule_group(&mut self, name: String, group: RuleGroup)86     pub fn add_rule_group(&mut self, name: String, group: RuleGroup) {
87         self.groups.insert(name, group);
88     }
89 
90     /// Consume a packet and run it through the various rules processors.
process(&mut self, packet: Packet)91     pub fn process(&mut self, packet: Packet) {
92         for group in self.groups.values_mut() {
93             group.process(&packet);
94         }
95     }
96 
report(&self, writer: &mut dyn Write)97     pub fn report(&self, writer: &mut dyn Write) {
98         for group in self.groups.values() {
99             group.report(writer);
100         }
101     }
102 
report_signals(&self, writer: &mut dyn Write)103     pub fn report_signals(&self, writer: &mut dyn Write) {
104         for group in self.groups.values() {
105             group.report_signals(writer);
106         }
107     }
108 }
109