retina_core/protocols/stream/http/
parser.rs1use super::transaction::{HttpRequest, HttpResponse};
8use super::Http;
9use crate::conntrack::conn::conn_info::ConnState;
10use crate::conntrack::pdu::L4Pdu;
11use crate::protocols::stream::{ConnParsable, ParseResult, ProbeResult, Session, SessionData};
12
13use httparse::{Request, EMPTY_HEADER};
14use std::collections::HashMap;
15
16#[derive(Default, Debug)]
17pub struct HttpParser {
18 pending: HashMap<usize, Http>,
20 current_trans: usize,
22 cnt: usize,
24}
25
26impl HttpParser {
27 pub(crate) fn process_ctos(&mut self, data: &[u8]) -> ParseResult {
29 if let Ok(request) = HttpRequest::parse_from(data) {
30 let session_id = self.cnt;
31 let http = Http {
32 request,
33 response: HttpResponse::default(),
34 trans_depth: session_id,
35 };
36 self.cnt += 1;
37 self.pending.insert(session_id, http);
38 ParseResult::Continue(session_id)
39 } else {
40 ParseResult::Skipped
43 }
44 }
45
46 pub(crate) fn process_stoc(&mut self, data: &[u8], pdu: &L4Pdu) -> ParseResult {
48 if let Ok(response) = HttpResponse::parse_from(data) {
49 if let Some(http) = self.pending.get_mut(&self.current_trans) {
50 http.response = response;
51 ParseResult::Done(self.current_trans)
54 } else {
55 log::warn!("HTTP response without oustanding request: {:?}", pdu.ctxt);
56 ParseResult::Skipped
57 }
58 } else {
59 ParseResult::Skipped
62 }
63 }
64}
65
66impl ConnParsable for HttpParser {
67 fn parse(&mut self, pdu: &L4Pdu) -> ParseResult {
68 let offset = pdu.offset();
69 let length = pdu.length();
70 if length == 0 {
71 return ParseResult::Skipped;
72 }
73
74 if let Ok(data) = (pdu.mbuf_ref()).get_data_slice(offset, length) {
75 if pdu.dir {
76 self.process_ctos(data)
77 } else {
78 self.process_stoc(data, pdu)
79 }
80 } else {
81 log::warn!("Malformed packet on parse");
82 ParseResult::Skipped
83 }
84 }
85
86 fn probe(&self, pdu: &L4Pdu) -> ProbeResult {
87 const NUM_OF_HEADERS: usize = 4;
91
92 if pdu.length() < 6 {
93 return ProbeResult::Unsure;
94 }
95 let offset = pdu.offset();
96 let length = pdu.length();
97 if let Ok(data) = (pdu.mbuf_ref()).get_data_slice(offset, length) {
98 match &data[..4] {
100 b"OPTI" | b"GET " | b"HEAD" | b"POST" | b"PUT " | b"PATC" | b"COPY" | b"MOVE"
101 | b"DELE" | b"LINK" | b"UNLI" | b"TRAC" | b"WRAP" => (),
102 _ => return ProbeResult::NotForUs,
103 }
104 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
106 let mut req = Request::new(&mut headers[..]);
107 let status = req.parse(data);
108 if let Err(e) = status {
109 if e != httparse::Error::TooManyHeaders {
110 log::trace!(
111 "data could be HTTP, but got error {:?} while parsing",
112 status
113 );
114 return ProbeResult::Unsure;
115 }
116 }
117 ProbeResult::Certain
118 } else {
119 log::warn!("Malformed packet");
120 ProbeResult::Error
121 }
122 }
123
124 fn remove_session(&mut self, session_id: usize) -> Option<Session> {
125 self.current_trans = session_id + 1;
127 self.pending.remove(&session_id).map(|http| Session {
128 data: SessionData::Http(Box::new(http)),
129 id: session_id,
130 })
131 }
132
133 fn drain_sessions(&mut self) -> Vec<Session> {
134 self.pending
135 .drain()
136 .map(|(session_id, http)| Session {
137 data: SessionData::Http(Box::new(http)),
138 id: session_id,
139 })
140 .collect()
141 }
142
143 fn session_match_state(&self) -> ConnState {
144 ConnState::Parsing
145 }
146
147 fn session_nomatch_state(&self) -> ConnState {
148 ConnState::Parsing
149 }
150}