/src/suricata7/rust/src/http2/logger.rs
Line | Count | Source |
1 | | /* Copyright (C) 2020 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction}; |
19 | | use super::parser; |
20 | | use crate::jsonbuilder::{JsonBuilder, JsonError}; |
21 | | use std; |
22 | | use std::collections::{HashMap, HashSet}; |
23 | | use std::rc::Rc; |
24 | | |
25 | | #[derive(Hash, PartialEq, Eq, Debug)] |
26 | | enum HeaderName { |
27 | | Method, |
28 | | Path, |
29 | | Host, |
30 | | UserAgent, |
31 | | Status, |
32 | | ContentLength, |
33 | | } |
34 | | |
35 | 12.0k | fn log_http2_headers<'a>( |
36 | 12.0k | blocks: &'a [parser::HTTP2FrameHeaderBlock], js: &mut JsonBuilder, |
37 | 12.0k | common: &mut HashMap<HeaderName, &'a Vec<u8>>, |
38 | 12.0k | ) -> Result<(), JsonError> { |
39 | 12.0k | let mut logged_headers = HashSet::new(); |
40 | 6.86M | for block in blocks { |
41 | | // delay js.start_object() because we skip suplicate headers |
42 | 6.85M | match block.error { |
43 | | parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => { |
44 | 1.62M | if Rc::strong_count(&block.name) > 2 { |
45 | | // more than one reference in headers table + current headers |
46 | 62.7k | let ptr = Rc::as_ptr(&block.name) as usize; |
47 | 62.7k | if !logged_headers.insert(ptr) { |
48 | | // only log once |
49 | 21.2k | continue; |
50 | 41.5k | } |
51 | 1.56M | } |
52 | 1.60M | js.start_object()?; |
53 | 1.60M | js.set_string_from_bytes("name", &block.name)?; |
54 | 1.60M | js.set_string_from_bytes("value", &block.value)?; |
55 | 1.60M | if let Ok(name) = std::str::from_utf8(&block.name) { |
56 | 1.57M | match name.to_lowercase().as_ref() { |
57 | 1.57M | ":method" => { |
58 | 54.1k | common.insert(HeaderName::Method, &block.value); |
59 | 54.1k | } |
60 | 1.51M | ":path" => { |
61 | 642k | common.insert(HeaderName::Path, &block.value); |
62 | 642k | } |
63 | 874k | ":status" => { |
64 | 62.0k | common.insert(HeaderName::Status, &block.value); |
65 | 62.0k | } |
66 | 812k | "user-agent" => { |
67 | 5.97k | common.insert(HeaderName::UserAgent, &block.value); |
68 | 5.97k | } |
69 | 806k | "host" => { |
70 | 12.4k | common.insert(HeaderName::Host, &block.value); |
71 | 12.4k | } |
72 | 794k | "content-length" => { |
73 | 14.5k | common.insert(HeaderName::ContentLength, &block.value); |
74 | 14.5k | } |
75 | 779k | _ => {} |
76 | | } |
77 | 36.2k | } |
78 | | } |
79 | | parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => { |
80 | 62.8k | js.start_object()?; |
81 | 62.8k | js.set_uint("table_size_update", block.sizeupdate)?; |
82 | | } |
83 | | _ => { |
84 | 5.16M | js.start_object()?; |
85 | 5.16M | js.set_string("error", &block.error.to_string())?; |
86 | | } |
87 | | } |
88 | 6.83M | js.close()?; |
89 | | } |
90 | 12.0k | return Ok(()); |
91 | 12.0k | } |
92 | | |
93 | 17.8k | fn log_headers<'a>( |
94 | 17.8k | frames: &'a Vec<HTTP2Frame>, js: &mut JsonBuilder, |
95 | 17.8k | common: &mut HashMap<HeaderName, &'a Vec<u8>>, |
96 | 17.8k | ) -> Result<bool, JsonError> { |
97 | 17.8k | let mut has_headers = false; |
98 | 48.5k | for frame in frames { |
99 | 30.7k | match &frame.data { |
100 | 9.76k | HTTP2FrameTypeData::HEADERS(hd) => { |
101 | 9.76k | log_http2_headers(&hd.blocks, js, common)?; |
102 | 9.76k | has_headers = true; |
103 | | } |
104 | 2.22k | HTTP2FrameTypeData::PUSHPROMISE(hd) => { |
105 | 2.22k | log_http2_headers(&hd.blocks, js, common)?; |
106 | 2.22k | has_headers = true; |
107 | | } |
108 | 18 | HTTP2FrameTypeData::CONTINUATION(hd) => { |
109 | 18 | log_http2_headers(&hd.blocks, js, common)?; |
110 | 18 | has_headers = true; |
111 | | } |
112 | 18.7k | _ => {} |
113 | | } |
114 | | } |
115 | 17.8k | Ok(has_headers) |
116 | 17.8k | } |
117 | | |
118 | 17.8k | fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result<bool, JsonError> { |
119 | 17.8k | let mut has_settings = false; |
120 | 48.5k | for frame in frames { |
121 | 30.7k | if let HTTP2FrameTypeData::SETTINGS(set) = &frame.data { |
122 | 141 | if !has_settings { |
123 | 141 | js.open_array("settings")?; |
124 | 141 | has_settings = true; |
125 | 0 | } |
126 | 495 | for e in set { |
127 | 354 | js.start_object()?; |
128 | 354 | js.set_string("settings_id", &format!("SETTINGS{}", &e.id.to_string().to_uppercase()))?; |
129 | 354 | js.set_uint("settings_value", e.value as u64)?; |
130 | 354 | js.close()?; |
131 | | } |
132 | 30.6k | } |
133 | | } |
134 | 17.8k | if has_settings { |
135 | 141 | js.close()?; |
136 | 17.6k | } |
137 | | |
138 | 17.8k | let mut has_error_code = false; |
139 | 17.8k | let mut has_priority = false; |
140 | 17.8k | let mut has_multiple = false; |
141 | 48.5k | for frame in frames { |
142 | 30.7k | match &frame.data { |
143 | 0 | HTTP2FrameTypeData::GOAWAY(goaway) => { |
144 | 0 | if !has_error_code { |
145 | 0 | let errcode: Option<parser::HTTP2ErrorCode> = |
146 | 0 | num::FromPrimitive::from_u32(goaway.errorcode); |
147 | 0 | match errcode { |
148 | 0 | Some(errstr) => { |
149 | 0 | js.set_string("error_code", &errstr.to_string().to_uppercase())?; |
150 | | } |
151 | | None => { |
152 | | //use uint32 |
153 | 0 | js.set_string("error_code", &goaway.errorcode.to_string())?; |
154 | | } |
155 | | } |
156 | 0 | has_error_code = true; |
157 | 0 | } else if !has_multiple { |
158 | 0 | js.set_string("has_multiple", "error_code")?; |
159 | 0 | has_multiple = true; |
160 | 0 | } |
161 | | } |
162 | 40 | HTTP2FrameTypeData::RSTSTREAM(rst) => { |
163 | 40 | if !has_error_code { |
164 | 40 | let errcode: Option<parser::HTTP2ErrorCode> = |
165 | 40 | num::FromPrimitive::from_u32(rst.errorcode); |
166 | 40 | match errcode { |
167 | 39 | Some(errstr) => { |
168 | 39 | js.set_string("error_code", &errstr.to_string())?; |
169 | | } |
170 | | None => { |
171 | | //use uint32 |
172 | 1 | js.set_string("error_code", &rst.errorcode.to_string())?; |
173 | | } |
174 | | } |
175 | 40 | has_error_code = true; |
176 | 0 | } else if !has_multiple { |
177 | 0 | js.set_string("has_multiple", "error_code")?; |
178 | 0 | has_multiple = true; |
179 | 0 | } |
180 | | } |
181 | 7 | HTTP2FrameTypeData::PRIORITY(priority) => { |
182 | 7 | if !has_priority { |
183 | 6 | js.set_uint("priority", priority.weight as u64)?; |
184 | 6 | has_priority = true; |
185 | 1 | } else if !has_multiple { |
186 | 1 | js.set_string("has_multiple", "priority")?; |
187 | 1 | has_multiple = true; |
188 | 0 | } |
189 | | } |
190 | 9.76k | HTTP2FrameTypeData::HEADERS(hd) => { |
191 | 9.76k | if let Some(ref priority) = hd.priority { |
192 | 2.19k | if !has_priority { |
193 | 2.19k | js.set_uint("priority", priority.weight as u64)?; |
194 | 2.19k | has_priority = true; |
195 | 7 | } else if !has_multiple { |
196 | 3 | js.set_string("has_multiple", "priority")?; |
197 | 3 | has_multiple = true; |
198 | 4 | } |
199 | 7.56k | } |
200 | | } |
201 | 20.9k | _ => {} |
202 | | } |
203 | | } |
204 | 17.8k | return Ok(has_settings || has_error_code || has_priority); |
205 | 17.8k | } |
206 | | |
207 | 8.92k | fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonError> { |
208 | 8.92k | js.set_string("version", "2")?; |
209 | | |
210 | 8.92k | let mut common: HashMap<HeaderName, &Vec<u8>> = HashMap::new(); |
211 | | |
212 | 8.92k | let mut has_headers = false; |
213 | | |
214 | | // Request headers. |
215 | 8.92k | let mark = js.get_mark(); |
216 | 8.92k | js.open_array("request_headers")?; |
217 | 8.92k | if log_headers(&tx.frames_ts, js, &mut common)? { |
218 | 2.63k | js.close()?; |
219 | 2.63k | has_headers = true; |
220 | | } else { |
221 | 6.28k | js.restore_mark(&mark)?; |
222 | | } |
223 | | |
224 | | // Response headers. |
225 | 8.92k | let mark = js.get_mark(); |
226 | 8.92k | js.open_array("response_headers")?; |
227 | 8.92k | if log_headers(&tx.frames_tc, js, &mut common)? { |
228 | 6.66k | js.close()?; |
229 | 6.66k | has_headers = true; |
230 | | } else { |
231 | 2.26k | js.restore_mark(&mark)?; |
232 | | } |
233 | | |
234 | 32.5k | for (name, value) in common { |
235 | 23.6k | match name { |
236 | | HeaderName::Method => { |
237 | 3.85k | js.set_string_from_bytes("http_method", value)?; |
238 | | } |
239 | | HeaderName::Path => { |
240 | 3.83k | js.set_string_from_bytes("url", value)?; |
241 | | } |
242 | | HeaderName::Host => { |
243 | 1.05k | js.set_string_from_bytes("hostname", value)?; |
244 | | } |
245 | | HeaderName::UserAgent => { |
246 | 2.56k | js.set_string_from_bytes("http_user_agent", value)?; |
247 | | } |
248 | | HeaderName::ContentLength => { |
249 | 5.65k | if let Ok(value) = std::str::from_utf8(value) { |
250 | 5.36k | if let Ok(value) = value.parse::<u64>() { |
251 | 4.65k | js.set_uint("length", value)?; |
252 | 707 | } |
253 | 297 | } |
254 | | } |
255 | | HeaderName::Status => { |
256 | 6.66k | if let Ok(value) = std::str::from_utf8(value) { |
257 | 6.35k | if let Ok(value) = value.parse::<u64>() { |
258 | 5.24k | js.set_uint("status", value)?; |
259 | 1.11k | } |
260 | 310 | } |
261 | | } |
262 | | } |
263 | | } |
264 | | |
265 | | // The rest of http2 logging is placed in an "http2" object. |
266 | 8.92k | js.open_object("http2")?; |
267 | | |
268 | 8.92k | js.set_uint("stream_id", tx.stream_id as u64)?; |
269 | 8.92k | js.open_object("request")?; |
270 | 8.92k | let has_request = log_http2_frames(&tx.frames_ts, js)?; |
271 | 8.92k | js.close()?; |
272 | | |
273 | 8.92k | js.open_object("response")?; |
274 | 8.92k | let has_response = log_http2_frames(&tx.frames_tc, js)?; |
275 | 8.92k | js.close()?; |
276 | | |
277 | | // Close http2. |
278 | 8.92k | js.close()?; |
279 | | |
280 | 8.92k | return Ok(has_request || has_response || has_headers); |
281 | 8.92k | } |
282 | | |
283 | | #[no_mangle] |
284 | 8.92k | pub unsafe extern "C" fn rs_http2_log_json( |
285 | 8.92k | tx: *mut std::os::raw::c_void, js: &mut JsonBuilder, |
286 | 8.92k | ) -> bool { |
287 | 8.92k | let tx = cast_pointer!(tx, HTTP2Transaction); |
288 | 8.92k | if let Ok(x) = log_http2(tx, js) { |
289 | 8.92k | return x; |
290 | 0 | } |
291 | 0 | return false; |
292 | 8.92k | } |