diff options
Diffstat (limited to '')
-rw-r--r-- | rust/src/asn1/mod.rs | 6 | ||||
-rw-r--r-- | rust/src/dhcp/logger.rs | 2 | ||||
-rw-r--r-- | rust/src/http2/detect.rs | 19 | ||||
-rw-r--r-- | rust/src/http2/http2.rs | 2 | ||||
-rw-r--r-- | rust/src/http2/logger.rs | 17 | ||||
-rw-r--r-- | rust/src/http2/parser.rs | 61 | ||||
-rw-r--r-- | rust/src/jsonbuilder.rs | 57 | ||||
-rw-r--r-- | rust/src/mqtt/mqtt.rs | 3 | ||||
-rw-r--r-- | rust/src/smb/smb.rs | 2 | ||||
-rw-r--r-- | rust/src/smb/smb1.rs | 2 |
10 files changed, 119 insertions, 52 deletions
diff --git a/rust/src/asn1/mod.rs b/rust/src/asn1/mod.rs index 4b77b0c..7496d44 100644 --- a/rust/src/asn1/mod.rs +++ b/rust/src/asn1/mod.rs @@ -33,7 +33,7 @@ pub struct Asn1<'a>(Vec<BerObject<'a>>); enum Asn1DecodeError { InvalidKeywordParameter, MaxFrames, - BerError(Err<der_parser::error::BerError>), + BerError, } /// Enumeration of Asn1 checks @@ -282,8 +282,8 @@ impl From<std::num::TryFromIntError> for Asn1DecodeError { } impl From<Err<der_parser::error::BerError>> for Asn1DecodeError { - fn from(e: Err<der_parser::error::BerError>) -> Asn1DecodeError { - Asn1DecodeError::BerError(e) + fn from(_e: Err<der_parser::error::BerError>) -> Asn1DecodeError { + Asn1DecodeError::BerError } } diff --git a/rust/src/dhcp/logger.rs b/rust/src/dhcp/logger.rs index 3c86b1b..a9d8d98 100644 --- a/rust/src/dhcp/logger.rs +++ b/rust/src/dhcp/logger.rs @@ -89,7 +89,7 @@ impl DHCPLogger { js.set_uint("id", header.txid as u64)?; js.set_string("client_mac", - &format_addr_hex(&header.clienthw.to_vec()))?; + &format_addr_hex(&header.clienthw))?; js.set_string("assigned_ip", &dns_print_addr(&header.yourip))?; if self.extended { diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index 52b4119..67933b6 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -23,6 +23,7 @@ use crate::core::Direction; use crate::detect::uint::{detect_match_uint, DetectUintData}; use std::ffi::CStr; use std::str::FromStr; +use std::rc::Rc; fn http2_tx_has_frametype( tx: &mut HTTP2Transaction, direction: Direction, value: u8, @@ -404,7 +405,7 @@ fn http2_frames_get_header_firstvalue<'a>( for frame in frames { if let Some(blocks) = http2_header_blocks(frame) { for block in blocks.iter() { - if block.name == name.as_bytes() { + if block.name.as_ref() == name.as_bytes() { return Ok(&block.value); } } @@ -428,7 +429,7 @@ pub fn http2_frames_get_header_value_vec( for frame in frames { if let Some(blocks) = http2_header_blocks(frame) { for block in blocks.iter() { - if block.name == name.as_bytes() { + if block.name.as_ref() == name.as_bytes() { if found == 0 { vec.extend_from_slice(&block.value); found = 1; @@ -465,7 +466,7 @@ fn http2_frames_get_header_value<'a>( for frame in frames { if let Some(blocks) = http2_header_blocks(frame) { for block in blocks.iter() { - if block.name == name.as_bytes() { + if block.name.as_ref() == name.as_bytes() { if found == 0 { single = Ok(&block.value); found = 1; @@ -920,8 +921,8 @@ fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) { }; let mut blocks = Vec::new(); let b = parser::HTTP2FrameHeaderBlock { - name: name.to_vec(), - value: input.to_vec(), + name: Rc::new(name.to_vec()), + value: Rc::new(input.to_vec()), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; @@ -1063,15 +1064,15 @@ mod tests { }; let mut blocks = Vec::new(); let b = parser::HTTP2FrameHeaderBlock { - name: "Host".as_bytes().to_vec(), - value: "abc.com".as_bytes().to_vec(), + name: "Host".as_bytes().to_vec().into(), + value: "abc.com".as_bytes().to_vec().into(), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; blocks.push(b); let b2 = parser::HTTP2FrameHeaderBlock { - name: "Host".as_bytes().to_vec(), - value: "efg.net".as_bytes().to_vec(), + name: "Host".as_bytes().to_vec().into(), + value: "efg.net".as_bytes().to_vec().into(), error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index b62ccb9..23aaf26 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -208,7 +208,7 @@ impl HTTP2Transaction { let mut authority = None; let mut host = None; for block in blocks { - if block.name == b"content-encoding" { + if block.name.as_ref() == b"content-encoding" { self.decoder.http2_encoding_fromvec(&block.value, dir); } else if block.name.eq_ignore_ascii_case(b":authority") { authority = Some(&block.value); diff --git a/rust/src/http2/logger.rs b/rust/src/http2/logger.rs index d25f852..a117a54 100644 --- a/rust/src/http2/logger.rs +++ b/rust/src/http2/logger.rs @@ -19,7 +19,8 @@ use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction}; use super::parser; use crate::jsonbuilder::{JsonBuilder, JsonError}; use std; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::rc::Rc; #[derive(Hash, PartialEq, Eq, Debug)] enum HeaderName { @@ -35,10 +36,20 @@ fn log_http2_headers<'a>( blocks: &'a [parser::HTTP2FrameHeaderBlock], js: &mut JsonBuilder, common: &mut HashMap<HeaderName, &'a Vec<u8>>, ) -> Result<(), JsonError> { + let mut logged_headers = HashSet::new(); for block in blocks { - js.start_object()?; + // delay js.start_object() because we skip suplicate headers match block.error { parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => { + if Rc::strong_count(&block.name) > 2 { + // more than one reference in headers table + current headers + let ptr = Rc::as_ptr(&block.name) as usize; + if !logged_headers.insert(ptr) { + // only log once + continue; + } + } + js.start_object()?; 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) { @@ -66,9 +77,11 @@ fn log_http2_headers<'a>( } } parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => { + js.start_object()?; js.set_uint("table_size_update", block.sizeupdate)?; } _ => { + js.start_object()?; js.set_string("error", &block.error.to_string())?; } } diff --git a/rust/src/http2/parser.rs b/rust/src/http2/parser.rs index adabeb2..1a46437 100644 --- a/rust/src/http2/parser.rs +++ b/rust/src/http2/parser.rs @@ -30,6 +30,7 @@ use nom7::sequence::tuple; use nom7::{Err, IResult}; use std::fmt; use std::str::FromStr; +use std::rc::Rc; #[repr(u8)] #[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)] @@ -295,8 +296,8 @@ fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP }; if !name.is_empty() { return Some(HTTP2FrameHeaderBlock { - name: name.as_bytes().to_vec(), - value: value.as_bytes().to_vec(), + name: Rc::new(name.as_bytes().to_vec()), + value: Rc::new(value.as_bytes().to_vec()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }); @@ -304,23 +305,23 @@ fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP //use dynamic table if n == 0 { return Some(HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0, sizeupdate: 0, }); } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize { return Some(HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed, sizeupdate: 0, }); } else { let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER); let headcopy = HTTP2FrameHeaderBlock { - name: dyn_headers.table[indyn].name.to_vec(), - value: dyn_headers.table[indyn].value.to_vec(), + name: dyn_headers.table[indyn].name.clone(), + value: dyn_headers.table[indyn].value.clone(), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess, sizeupdate: 0, }; @@ -348,8 +349,10 @@ impl fmt::Display for HTTP2HeaderDecodeStatus { #[derive(Clone, Debug)] pub struct HTTP2FrameHeaderBlock { - pub name: Vec<u8>, - pub value: Vec<u8>, + // Use Rc reference counted so that indexed headers do not get copied. + // Otherwise, this leads to quadratic complexity in memory occupation. + pub name: Rc<Vec<u8>>, + pub value: Rc<Vec<u8>>, pub error: HTTP2HeaderDecodeStatus, pub sizeupdate: u64, } @@ -391,7 +394,7 @@ fn http2_parse_headers_block_literal_common<'a>( ) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> { let (i3, name, error) = if index == 0 { match http2_parse_headers_block_string(input) { - Ok((r, n)) => Ok((r, n, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)), + Ok((r, n)) => Ok((r, Rc::new(n), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)), Err(e) => Err(e), } } else { @@ -403,7 +406,7 @@ fn http2_parse_headers_block_literal_common<'a>( )), None => Ok(( input, - Vec::new(), + Rc::new(Vec::new()), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed, )), } @@ -413,7 +416,7 @@ fn http2_parse_headers_block_literal_common<'a>( i4, HTTP2FrameHeaderBlock { name, - value, + value: Rc::new(value), error, sizeupdate: 0, }, @@ -435,8 +438,8 @@ fn http2_parse_headers_block_literal_incindex<'a>( match r { Ok((r, head)) => { let headcopy = HTTP2FrameHeaderBlock { - name: head.name.to_vec(), - value: head.value.to_vec(), + name: head.name.clone(), + value: head.value.clone(), error: head.error, sizeupdate: 0, }; @@ -556,8 +559,8 @@ fn http2_parse_headers_block_dynamic_size<'a>( return Ok(( i3, HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate, sizeupdate: maxsize2, }, @@ -614,8 +617,8 @@ fn http2_parse_headers_blocks<'a>( // if we error from http2_parse_var_uint, we keep the first parsed headers if err.code == ErrorKind::LengthValue { blocks.push(HTTP2FrameHeaderBlock { - name: Vec::new(), - value: Vec::new(), + name: Rc::new(Vec::new()), + value: Rc::new(Vec::new()), error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow, sizeupdate: 0, }); @@ -765,8 +768,8 @@ mod tests { match r0 { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, ":method".as_bytes().to_vec()); - assert_eq!(hd.value, "GET".as_bytes().to_vec()); + assert_eq!(hd.name, ":method".as_bytes().to_vec().into()); + assert_eq!(hd.value, "GET".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); } @@ -782,8 +785,8 @@ mod tests { match r1 { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, "accept".as_bytes().to_vec()); - assert_eq!(hd.value, "*/*".as_bytes().to_vec()); + assert_eq!(hd.name, "accept".as_bytes().to_vec().into()); + assert_eq!(hd.value, "*/*".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 1); @@ -802,8 +805,8 @@ mod tests { match result { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, ":authority".as_bytes().to_vec()); - assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec()); + assert_eq!(hd.name, ":authority".as_bytes().to_vec().into()); + assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 2); @@ -820,8 +823,8 @@ mod tests { match r3 { Ok((remainder, hd)) => { // same as before - assert_eq!(hd.name, ":authority".as_bytes().to_vec()); - assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec()); + assert_eq!(hd.name, ":authority".as_bytes().to_vec().into()); + assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 2); @@ -856,8 +859,8 @@ mod tests { match r2 { Ok((remainder, hd)) => { // Check the first message. - assert_eq!(hd.name, ":path".as_bytes().to_vec()); - assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec()); + assert_eq!(hd.name, ":path".as_bytes().to_vec().into()); + assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec().into()); // And we should have no bytes left. assert_eq!(remainder.len(), 0); assert_eq!(dynh.table.len(), 2); diff --git a/rust/src/jsonbuilder.rs b/rust/src/jsonbuilder.rs index 7264be5..8b42966 100644 --- a/rust/src/jsonbuilder.rs +++ b/rust/src/jsonbuilder.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Open Information Security Foundation +/* Copyright (C) 2020-2024 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 @@ -435,7 +435,7 @@ impl JsonBuilder { return Err(JsonError::InvalidState); } } - self.push_str(&val.to_string())?; + self.push_float(val)?; Ok(self) } @@ -650,7 +650,7 @@ impl JsonBuilder { self.push('"')?; self.push_str(key)?; self.push_str("\":")?; - self.push_str(&val.to_string())?; + self.push_float(val)?; Ok(self) } @@ -681,6 +681,15 @@ impl JsonBuilder { self.buf.capacity() } + fn push_float(&mut self, val: f64) -> Result<(), JsonError> { + if val.is_nan() || val.is_infinite() { + self.push_str("null")?; + } else { + self.push_str(&val.to_string())?; + } + Ok(()) + } + /// Encode a string into the buffer, escaping as needed. /// /// The string is encoded into an intermediate vector as its faster @@ -1339,6 +1348,48 @@ mod test { jb.close().unwrap(); assert_eq!(jb.buf, r#"[1.1,2.2]"#); } + + #[test] + fn test_set_nan() { + let mut jb = JsonBuilder::try_new_object().unwrap(); + jb.set_float("nan", f64::NAN).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"{"nan":null}"#); + } + + #[test] + fn test_append_nan() { + let mut jb = JsonBuilder::try_new_array().unwrap(); + jb.append_float(f64::NAN).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"[null]"#); + } + + #[test] + fn test_set_inf() { + let mut jb = JsonBuilder::try_new_object().unwrap(); + jb.set_float("inf", f64::INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"{"inf":null}"#); + + let mut jb = JsonBuilder::try_new_object().unwrap(); + jb.set_float("inf", f64::NEG_INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"{"inf":null}"#); + } + + #[test] + fn test_append_inf() { + let mut jb = JsonBuilder::try_new_array().unwrap(); + jb.append_float(f64::INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"[null]"#); + + let mut jb = JsonBuilder::try_new_array().unwrap(); + jb.append_float(f64::NEG_INFINITY).unwrap(); + jb.close().unwrap(); + assert_eq!(jb.buf, r#"[null]"#); + } } // Escape table as seen in serde-json (MIT/Apache license) diff --git a/rust/src/mqtt/mqtt.rs b/rust/src/mqtt/mqtt.rs index 8260251..35f3c6d 100644 --- a/rust/src/mqtt/mqtt.rs +++ b/rust/src/mqtt/mqtt.rs @@ -749,8 +749,7 @@ export_state_data_get!(rs_mqtt_get_state_data, MQTTState); #[no_mangle] pub unsafe extern "C" fn rs_mqtt_register_parser(cfg_max_msg_len: u32) { let default_port = CString::new("[1883]").unwrap(); - let max_msg_len = &mut MAX_MSG_LEN; - *max_msg_len = cfg_max_msg_len; + MAX_MSG_LEN = cfg_max_msg_len; let parser = RustParser { name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, default_port: default_port.as_ptr(), diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index d6b0a56..45d2cdc 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -1045,7 +1045,7 @@ impl SMBState { pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool) { - let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) { + let (name, is_dcerpc) = match self.guid2name_map.get(guid) { Some(n) => { let mut s = n.as_slice(); // skip leading \ if we have it diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index 9d7d47e..3a04c72 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -482,7 +482,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and state.ssn2vec_map.insert(name_key, name_val); let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_create_tx(&cr.file_name.to_vec(), + let tx = state.new_create_tx(&cr.file_name, cr.disposition, del, dir, tx_hdr); tx.vercmd.set_smb1_cmd(command); SCLogDebug!("TS CREATE TX {} created", tx.id); |