summaryrefslogtreecommitdiffstats
path: root/rust/src/http2/logger.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
commita0aa2307322cd47bbf416810ac0292925e03be87 (patch)
tree37076262a026c4b48c8a0e84f44ff9187556ca35 /rust/src/http2/logger.rs
parentInitial commit. (diff)
downloadsuricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz
suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'rust/src/http2/logger.rs')
-rw-r--r--rust/src/http2/logger.rs279
1 files changed, 279 insertions, 0 deletions
diff --git a/rust/src/http2/logger.rs b/rust/src/http2/logger.rs
new file mode 100644
index 0000000..d25f852
--- /dev/null
+++ b/rust/src/http2/logger.rs
@@ -0,0 +1,279 @@
+/* Copyright (C) 2020 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction};
+use super::parser;
+use crate::jsonbuilder::{JsonBuilder, JsonError};
+use std;
+use std::collections::HashMap;
+
+#[derive(Hash, PartialEq, Eq, Debug)]
+enum HeaderName {
+ Method,
+ Path,
+ Host,
+ UserAgent,
+ Status,
+ ContentLength,
+}
+
+fn log_http2_headers<'a>(
+ blocks: &'a [parser::HTTP2FrameHeaderBlock], js: &mut JsonBuilder,
+ common: &mut HashMap<HeaderName, &'a Vec<u8>>,
+) -> Result<(), JsonError> {
+ for block in blocks {
+ js.start_object()?;
+ match block.error {
+ parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => {
+ js.set_string_from_bytes("name", &block.name)?;
+ js.set_string_from_bytes("value", &block.value)?;
+ if let Ok(name) = std::str::from_utf8(&block.name) {
+ match name.to_lowercase().as_ref() {
+ ":method" => {
+ common.insert(HeaderName::Method, &block.value);
+ }
+ ":path" => {
+ common.insert(HeaderName::Path, &block.value);
+ }
+ ":status" => {
+ common.insert(HeaderName::Status, &block.value);
+ }
+ "user-agent" => {
+ common.insert(HeaderName::UserAgent, &block.value);
+ }
+ "host" => {
+ common.insert(HeaderName::Host, &block.value);
+ }
+ "content-length" => {
+ common.insert(HeaderName::ContentLength, &block.value);
+ }
+ _ => {}
+ }
+ }
+ }
+ parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => {
+ js.set_uint("table_size_update", block.sizeupdate)?;
+ }
+ _ => {
+ js.set_string("error", &block.error.to_string())?;
+ }
+ }
+ js.close()?;
+ }
+ return Ok(());
+}
+
+fn log_headers<'a>(
+ frames: &'a Vec<HTTP2Frame>, js: &mut JsonBuilder,
+ common: &mut HashMap<HeaderName, &'a Vec<u8>>,
+) -> Result<bool, JsonError> {
+ let mut has_headers = false;
+ for frame in frames {
+ match &frame.data {
+ HTTP2FrameTypeData::HEADERS(hd) => {
+ log_http2_headers(&hd.blocks, js, common)?;
+ has_headers = true;
+ }
+ HTTP2FrameTypeData::PUSHPROMISE(hd) => {
+ log_http2_headers(&hd.blocks, js, common)?;
+ has_headers = true;
+ }
+ HTTP2FrameTypeData::CONTINUATION(hd) => {
+ log_http2_headers(&hd.blocks, js, common)?;
+ has_headers = true;
+ }
+ _ => {}
+ }
+ }
+ Ok(has_headers)
+}
+
+fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result<bool, JsonError> {
+ let mut has_settings = false;
+ for frame in frames {
+ if let HTTP2FrameTypeData::SETTINGS(set) = &frame.data {
+ if !has_settings {
+ js.open_array("settings")?;
+ has_settings = true;
+ }
+ for e in set {
+ js.start_object()?;
+ js.set_string("settings_id", &format!("SETTINGS{}", &e.id.to_string().to_uppercase()))?;
+ js.set_uint("settings_value", e.value as u64)?;
+ js.close()?;
+ }
+ }
+ }
+ if has_settings {
+ js.close()?;
+ }
+
+ let mut has_error_code = false;
+ let mut has_priority = false;
+ let mut has_multiple = false;
+ for frame in frames {
+ match &frame.data {
+ HTTP2FrameTypeData::GOAWAY(goaway) => {
+ if !has_error_code {
+ let errcode: Option<parser::HTTP2ErrorCode> =
+ num::FromPrimitive::from_u32(goaway.errorcode);
+ match errcode {
+ Some(errstr) => {
+ js.set_string("error_code", &errstr.to_string().to_uppercase())?;
+ }
+ None => {
+ //use uint32
+ js.set_string("error_code", &goaway.errorcode.to_string())?;
+ }
+ }
+ has_error_code = true;
+ } else if !has_multiple {
+ js.set_string("has_multiple", "error_code")?;
+ has_multiple = true;
+ }
+ }
+ HTTP2FrameTypeData::RSTSTREAM(rst) => {
+ if !has_error_code {
+ let errcode: Option<parser::HTTP2ErrorCode> =
+ num::FromPrimitive::from_u32(rst.errorcode);
+ match errcode {
+ Some(errstr) => {
+ js.set_string("error_code", &errstr.to_string())?;
+ }
+ None => {
+ //use uint32
+ js.set_string("error_code", &rst.errorcode.to_string())?;
+ }
+ }
+ has_error_code = true;
+ } else if !has_multiple {
+ js.set_string("has_multiple", "error_code")?;
+ has_multiple = true;
+ }
+ }
+ HTTP2FrameTypeData::PRIORITY(priority) => {
+ if !has_priority {
+ js.set_uint("priority", priority.weight as u64)?;
+ has_priority = true;
+ } else if !has_multiple {
+ js.set_string("has_multiple", "priority")?;
+ has_multiple = true;
+ }
+ }
+ HTTP2FrameTypeData::HEADERS(hd) => {
+ if let Some(ref priority) = hd.priority {
+ if !has_priority {
+ js.set_uint("priority", priority.weight as u64)?;
+ has_priority = true;
+ } else if !has_multiple {
+ js.set_string("has_multiple", "priority")?;
+ has_multiple = true;
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ return Ok(has_settings || has_error_code || has_priority);
+}
+
+fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonError> {
+ js.set_string("version", "2")?;
+
+ let mut common: HashMap<HeaderName, &Vec<u8>> = HashMap::new();
+
+ let mut has_headers = false;
+
+ // Request headers.
+ let mark = js.get_mark();
+ js.open_array("request_headers")?;
+ if log_headers(&tx.frames_ts, js, &mut common)? {
+ js.close()?;
+ has_headers = true;
+ } else {
+ js.restore_mark(&mark)?;
+ }
+
+ // Response headers.
+ let mark = js.get_mark();
+ js.open_array("response_headers")?;
+ if log_headers(&tx.frames_tc, js, &mut common)? {
+ js.close()?;
+ has_headers = true;
+ } else {
+ js.restore_mark(&mark)?;
+ }
+
+ for (name, value) in common {
+ match name {
+ HeaderName::Method => {
+ js.set_string_from_bytes("http_method", value)?;
+ }
+ HeaderName::Path => {
+ js.set_string_from_bytes("url", value)?;
+ }
+ HeaderName::Host => {
+ js.set_string_from_bytes("hostname", value)?;
+ }
+ HeaderName::UserAgent => {
+ js.set_string_from_bytes("http_user_agent", value)?;
+ }
+ HeaderName::ContentLength => {
+ if let Ok(value) = std::str::from_utf8(value) {
+ if let Ok(value) = value.parse::<u64>() {
+ js.set_uint("length", value)?;
+ }
+ }
+ }
+ HeaderName::Status => {
+ if let Ok(value) = std::str::from_utf8(value) {
+ if let Ok(value) = value.parse::<u64>() {
+ js.set_uint("status", value)?;
+ }
+ }
+ }
+ }
+ }
+
+ // The rest of http2 logging is placed in an "http2" object.
+ js.open_object("http2")?;
+
+ js.set_uint("stream_id", tx.stream_id as u64)?;
+ js.open_object("request")?;
+ let has_request = log_http2_frames(&tx.frames_ts, js)?;
+ js.close()?;
+
+ js.open_object("response")?;
+ let has_response = log_http2_frames(&tx.frames_tc, js)?;
+ js.close()?;
+
+ // Close http2.
+ js.close()?;
+
+ return Ok(has_request || has_response || has_headers);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_http2_log_json(
+ tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
+) -> bool {
+ let tx = cast_pointer!(tx, HTTP2Transaction);
+ if let Ok(x) = log_http2(tx, js) {
+ return x;
+ }
+ return false;
+}