iris_core/protocols/stream/
mod.rs1#[doc(hidden)]
8pub mod conn;
9pub mod dns;
10pub mod http;
11pub mod quic;
12pub mod ssh;
13pub mod tls;
14
15use self::conn::ConnField;
16use self::conn::{Ipv4CData, Ipv6CData, TcpCData, UdpCData};
17use self::dns::{parser::DnsParser, Dns};
18use self::http::{parser::HttpParser, Http};
19use self::quic::parser::QuicParser;
20use self::ssh::{parser::SshParser, Ssh};
21use self::tls::{parser::TlsParser, Tls};
22use crate::conntrack::conn_id::FiveTuple;
23use crate::conntrack::pdu::L4Pdu;
24
25use std::collections::HashSet;
26use std::str::FromStr;
27
28use anyhow::Result;
29use quic::QuicConn;
30use strum_macros::EnumString;
31
32pub const IMPLEMENTED_PROTOCOLS: [&str; 5] = ["tls", "dns", "http", "quic", "ssh"];
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub(crate) enum ParseResult {
37 HeadersDone(usize),
40 Done(usize),
42 Continue(usize),
45 Skipped,
47 None,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub(crate) enum ProbeResult {
54 Certain,
56 Unsure,
58 NotForUs,
60 Error,
62}
63
64#[derive(Debug)]
66pub(crate) enum ProbeRegistryResult {
67 Some(ConnParser),
69 None,
71 Unsure,
73}
74
75#[derive(Debug)]
77pub struct ParserRegistry(Vec<ConnParser>);
78
79impl ParserRegistry {
80 pub fn from_strings(input: Vec<&'static str>) -> ParserRegistry {
82 let stream_protocols: HashSet<&'static str> = input.into_iter().collect();
84 let mut parsers = vec![];
85 for stream_protocol in stream_protocols {
86 let parser = ConnParser::from_str(stream_protocol)
87 .unwrap_or_else(|_| panic!("Invalid stream protocol: {}", stream_protocol));
88 parsers.push(parser);
89 }
90 ParserRegistry(parsers)
91 }
92
93 pub(crate) fn probe_all(&self, pdu: &L4Pdu) -> ProbeRegistryResult {
95 if self.0.is_empty() {
96 return ProbeRegistryResult::None;
97 }
98 if pdu.length() == 0 {
99 return ProbeRegistryResult::Unsure;
100 }
101
102 let mut num_notmatched = 0;
103 for parser in self.0.iter() {
104 match parser.probe(pdu) {
105 ProbeResult::Certain => {
106 return ProbeRegistryResult::Some(parser.reset_new());
107 }
108 ProbeResult::NotForUs => {
109 num_notmatched += 1;
110 }
111 _ => (), }
113 }
114 if num_notmatched == self.0.len() {
115 ProbeRegistryResult::None
116 } else {
117 ProbeRegistryResult::Unsure
118 }
119 }
120}
121
122pub(crate) trait ConnParsable {
124 fn parse(&mut self, pdu: &L4Pdu) -> ParseResult;
126
127 fn probe(&self, pdu: &L4Pdu) -> ProbeResult;
129
130 fn remove_session(&mut self, session_id: usize) -> Option<Session>;
132
133 fn drain_sessions(&mut self) -> Vec<Session>;
135
136 fn session_parsed_state(&self) -> ParsingState;
138
139 fn body_offset(&mut self) -> Option<usize>;
143}
144
145#[doc(hidden)]
153#[derive(Debug)]
154pub struct ConnData {
155 pub five_tuple: FiveTuple,
157}
158
159impl ConnData {
161 pub(crate) fn supported_fields() -> Vec<&'static str> {
162 let mut v: Vec<_> = TcpCData::supported_fields()
163 .into_iter()
164 .chain(UdpCData::supported_fields())
165 .chain(Ipv4CData::supported_fields())
166 .chain(Ipv6CData::supported_fields())
167 .collect();
168 v.dedup();
169 v
170 }
171
172 pub(crate) fn supported_protocols() -> Vec<&'static str> {
173 vec!["ipv4", "ipv6", "tcp", "udp"]
174 }
175
176 pub(crate) fn new(five_tuple: FiveTuple) -> Self {
179 ConnData { five_tuple }
180 }
181
182 pub fn parse_to<T: ConnField>(&self) -> Result<T>
184 where
185 Self: Sized,
186 {
187 T::parse_from(self)
188 }
189}
190
191#[doc(hidden)]
199#[derive(Debug)]
200pub enum SessionData {
201 Tls(Box<Tls>),
203 Dns(Box<Dns>),
204 Http(Box<Http>),
205 Quic(Box<QuicConn>),
206 Ssh(Box<Ssh>),
207 Null,
208}
209
210#[derive(Debug, Clone)]
213pub enum SessionProto {
214 Tls,
215 Dns,
216 Http,
217 Quic,
218 Ssh,
219 Ipv4,
220 Ipv6,
221 Tcp,
222 Udp,
223 Null, Probing, }
226
227#[doc(hidden)]
235#[derive(Debug)]
236pub struct Session {
237 pub data: SessionData,
239 pub id: usize,
241}
242
243impl Default for Session {
244 fn default() -> Self {
245 Session {
246 data: SessionData::Null,
247 id: 0,
248 }
249 }
250}
251
252#[doc(hidden)]
260#[derive(Debug, EnumString)]
261#[strum(serialize_all = "snake_case")]
262pub enum ConnParser {
263 Tls(TlsParser),
265 Dns(DnsParser),
266 Http(HttpParser),
267 Quic(QuicParser),
268 Ssh(SshParser),
269 Unknown,
270}
271
272impl ConnParser {
273 pub(crate) fn reset_new(&self) -> ConnParser {
275 match self {
276 ConnParser::Tls(_) => ConnParser::Tls(TlsParser::default()),
277 ConnParser::Dns(_) => ConnParser::Dns(DnsParser::default()),
278 ConnParser::Http(_) => ConnParser::Http(HttpParser::default()),
279 ConnParser::Quic(_) => ConnParser::Quic(QuicParser::default()),
280 ConnParser::Ssh(_) => ConnParser::Ssh(SshParser::default()),
281 ConnParser::Unknown => ConnParser::Unknown,
282 }
283 }
284
285 pub(crate) fn parse(&mut self, pdu: &L4Pdu) -> ParseResult {
287 match self {
288 ConnParser::Tls(parser) => parser.parse(pdu),
289 ConnParser::Dns(parser) => parser.parse(pdu),
290 ConnParser::Http(parser) => parser.parse(pdu),
291 ConnParser::Quic(parser) => parser.parse(pdu),
292 ConnParser::Ssh(parser) => parser.parse(pdu),
293 ConnParser::Unknown => ParseResult::None,
294 }
295 }
296
297 pub(crate) fn probe(&self, pdu: &L4Pdu) -> ProbeResult {
299 match self {
300 ConnParser::Tls(parser) => parser.probe(pdu),
301 ConnParser::Dns(parser) => parser.probe(pdu),
302 ConnParser::Http(parser) => parser.probe(pdu),
303 ConnParser::Quic(parser) => parser.probe(pdu),
304 ConnParser::Ssh(parser) => parser.probe(pdu),
305 ConnParser::Unknown => ProbeResult::Error,
306 }
307 }
308
309 pub(crate) fn remove_session(&mut self, session_id: usize) -> Option<Session> {
312 match self {
313 ConnParser::Tls(parser) => parser.remove_session(session_id),
314 ConnParser::Dns(parser) => parser.remove_session(session_id),
315 ConnParser::Http(parser) => parser.remove_session(session_id),
316 ConnParser::Quic(parser) => parser.remove_session(session_id),
317 ConnParser::Ssh(parser) => parser.remove_session(session_id),
318 ConnParser::Unknown => None,
319 }
320 }
321
322 pub(crate) fn drain_sessions(&mut self) -> Vec<Session> {
324 match self {
325 ConnParser::Tls(parser) => parser.drain_sessions(),
326 ConnParser::Dns(parser) => parser.drain_sessions(),
327 ConnParser::Http(parser) => parser.drain_sessions(),
328 ConnParser::Quic(parser) => parser.drain_sessions(),
329 ConnParser::Ssh(parser) => parser.drain_sessions(),
330 ConnParser::Unknown => vec![],
331 }
332 }
333
334 pub(crate) fn session_parsed_state(&self) -> ParsingState {
335 match self {
336 ConnParser::Tls(parser) => parser.session_parsed_state(),
337 ConnParser::Dns(parser) => parser.session_parsed_state(),
338 ConnParser::Http(parser) => parser.session_parsed_state(),
339 ConnParser::Quic(parser) => parser.session_parsed_state(),
340 ConnParser::Ssh(parser) => parser.session_parsed_state(),
341 ConnParser::Unknown => ParsingState::Stop,
342 }
343 }
344
345 pub(crate) fn body_offset(&mut self) -> Option<usize> {
346 match self {
347 ConnParser::Tls(parser) => parser.body_offset(),
348 ConnParser::Dns(parser) => parser.body_offset(),
349 ConnParser::Http(parser) => parser.body_offset(),
350 ConnParser::Quic(parser) => parser.body_offset(),
351 ConnParser::Ssh(parser) => parser.body_offset(),
352 ConnParser::Unknown => None,
353 }
354 }
355
356 pub fn protocol_name(&self) -> Option<String> {
359 match self {
360 ConnParser::Tls(_parser) => Some("tls".into()),
361 ConnParser::Dns(_parser) => Some("dns".into()),
362 ConnParser::Http(_parser) => Some("http".into()),
363 ConnParser::Quic(_parser) => Some("quic".into()),
364 ConnParser::Ssh(_parser) => Some("ssh".into()),
365 ConnParser::Unknown => None,
366 }
367 }
368
369 pub fn protocol(&self) -> SessionProto {
370 match self {
371 ConnParser::Tls(_) => SessionProto::Tls,
372 ConnParser::Dns(_) => SessionProto::Dns,
373 ConnParser::Http(_) => SessionProto::Http,
374 ConnParser::Quic(_) => SessionProto::Quic,
375 ConnParser::Ssh(_) => SessionProto::Ssh,
376 ConnParser::Unknown => SessionProto::Null,
377 }
378 }
379
380 pub fn requires_parsing(filter_str: &str) -> HashSet<&'static str> {
381 let mut out = hashset! {};
382
383 for s in IMPLEMENTED_PROTOCOLS {
384 if filter_str.contains(s) {
385 out.insert(s);
386 }
387 }
388 out
389 }
390}
391
392#[derive(Debug)]
393pub enum ParsingState {
394 Probing,
396 Parsing,
398 Stop,
400}