summaryrefslogtreecommitdiffstats
path: root/rust/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/asn1/mod.rs6
-rw-r--r--rust/src/dhcp/logger.rs2
-rw-r--r--rust/src/http2/detect.rs19
-rw-r--r--rust/src/http2/http2.rs2
-rw-r--r--rust/src/http2/logger.rs17
-rw-r--r--rust/src/http2/parser.rs61
-rw-r--r--rust/src/jsonbuilder.rs57
-rw-r--r--rust/src/mqtt/mqtt.rs3
-rw-r--r--rust/src/smb/smb.rs2
-rw-r--r--rust/src/smb/smb1.rs2
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);