Use `lru` crate. Rename to reflect this.
Add `app-layer.protocols.smb.max-guid-cache-size` to control the max
size of the LRU cache.
Ticket: #5672.
"alloc-no-stdlib",
]
+[[package]]
+name = "allocator-api2"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+
[[package]]
name = "asn1-rs"
version = "0.6.2"
"num-traits 0.1.43",
]
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
[[package]]
name = "failure"
version = "0.1.8"
"miniz_oxide",
]
+[[package]]
+name = "foldhash"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
+
[[package]]
name = "fs_extra"
version = "1.3.0"
"polyval",
]
+[[package]]
+name = "hashbrown"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
+
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
+[[package]]
+name = "lru"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
+dependencies = [
+ "hashbrown",
+]
+
[[package]]
name = "lzma-rs"
version = "0.2.0"
"lazy_static",
"ldap-parser",
"libc",
+ "lru",
"lzma-rs",
"md-5",
"memchr",
hkdf = "~0.12.3"
aes = "~0.7.5"
aes-gcm = "~0.9.4"
+lru = "~0.12.5"
der-parser = { version = "~9.0.0", default-features = false }
kerberos-parser = { version = "~0.8.0", default-features = false }
extern crate byteorder;
extern crate crc;
extern crate memchr;
+extern crate lru;
#[macro_use]
extern crate num_derive;
extern crate widestring;
#[cfg(feature = "debug")]
pub fn _debug_state_stats(&self) {
- SCLogDebug!("ssn2vec_map {} guid2name_map {} ssn2vecoffset_map {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", self.ssn2vec_map.len(), self.guid2name_map.len(), self.ssn2vecoffset_map.len(), self.ssn2tree_map.len(), self.ssnguid2vec_map.len(), self.file_ts_guid.len(), self.file_tc_guid.len(), self.transactions.len());
+ SCLogDebug!("ssn2vec_map {} guid2name_cache {} ssn2vecoffset_map {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}",
+ self.ssn2vec_map.len(),
+ self.guid2name_cache.len(),
+ self.ssn2vecoffset_map.len(),
+ self.ssn2tree_map.len(),
+ self.ssnguid2vec_map.len(),
+ self.file_ts_guid.len(),
+ self.file_tc_guid.len(),
+ self.transactions.len());
}
}
use nom7::{Err, Needed};
use nom7::error::{make_error, ErrorKind};
+use lru::LruCache;
+use std::num::NonZeroUsize;
+
use crate::core::*;
use crate::applayer;
use crate::applayer::*;
pub static mut SMB_CFG_MAX_WRITE_SIZE: u32 = 16777216;
pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 67108864;
pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64;
+/// max size of the per state guid2name cache
+pub static mut SMB_CFG_MAX_GUID_CACHE_SIZE: usize = 1024;
static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN;
return [o1, o2, o3, o4]
}
-#[derive(Default, Debug)]
+#[derive(Debug)]
pub struct SMBState<> {
pub state_data: AppLayerStateData,
/// map ssn/tree/msgid to vec (guid/name/share)
pub ssn2vec_map: HashMap<SMBCommonHdr, Vec<u8>>,
+
/// map guid to filename
- pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
+ ///
+ /// Lifecycle of the members:
+ /// - Added by CREATE responses
+ /// - Removed by CLOSE requests
+ /// - Post GAP logic removes based on timestamp, as the CLOSE
+ /// commands may have been missed.
+ ///
+ pub guid2name_cache: LruCache<Vec<u8>, Vec<u8>>,
+
/// map ssn key to read offset
pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
}
}
+impl Default for SMBState {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl SMBState {
/// Allocation function for a new TLS parser instance
pub fn new() -> Self {
Self {
state_data:AppLayerStateData::new(),
ssn2vec_map:HashMap::new(),
- guid2name_map:HashMap::new(),
+ guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()),
ssn2vecoffset_map:HashMap::new(),
ssn2tree_map:HashMap::new(),
ssnguid2vec_map:HashMap::new(),
dialect:0,
dialect_vec: None,
dcerpc_ifaces: None,
+ max_read_size: 0,
+ max_write_size: 0,
ts: 0,
- ..Default::default()
}
}
return tx_ref.unwrap();
}
- pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)
+ pub fn get_service_for_guid(&mut self, guid: &[u8]) -> (&'static str, bool)
{
- let (name, is_dcerpc) = match self.guid2name_map.get(guid) {
+ let (name, is_dcerpc) = match self.guid2name_cache.get(guid) {
Some(n) => {
let mut s = n.as_slice();
// skip leading \ if we have it
SCLogError!("Invalid value for smb.max-tx");
}
}
+ let retval = conf_get("app-layer.protocols.smb.max-guid-cache-size");
+ if let Some(val) = retval {
+ if let Ok(v) = val.parse::<usize>() {
+ if v > 0 {
+ SMB_CFG_MAX_GUID_CACHE_SIZE = v;
+ } else {
+ SCLogError!("Invalid max-guid-cache-size value");
+ }
+ } else {
+ SCLogError!("Invalid max-guid-cache-size value");
+ }
+ }
SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}",
SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE);
SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}",
SMB_CFG_MAX_WRITE_SIZE, SMB_CFG_MAX_WRITE_QUEUE_CNT, SMB_CFG_MAX_WRITE_QUEUE_SIZE);
+ SCLogConfig!("guid: max cache size: {}", SMB_CFG_MAX_GUID_CACHE_SIZE);
} else {
SCLogDebug!("Protocol detector and parser disabled for SMB.");
}
let mut frankenfid = pd.fid.to_vec();
frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
- let filename = match state.guid2name_map.get(&frankenfid) {
+ let filename = match state.guid2name_cache.get(&frankenfid) {
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let mut frankenfid = pd.fid.to_vec();
frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
- let oldname = match state.guid2name_map.get(&frankenfid) {
+ let oldname = match state.guid2name_cache.get(&frankenfid) {
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let mut fid = cd.fid.to_vec();
fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
- let _name = state.guid2name_map.remove(&fid);
+ let _name = state.guid2name_cache.pop(&fid);
state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec());
SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid);
fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX fid {:?}", fid);
SCLogDebug!("fid {:?} name {:?}", fid, p);
- state.guid2name_map.insert(fid, p);
+ _ = state.guid2name_cache.put(fid, p);
} else {
SCLogDebug!("SMBv1 response: GUID NOT FOUND");
}
SCLogDebug!("SMBv1 WRITE: FID {:?} offset {}",
file_fid, rd.offset);
- let file_name = match state.guid2name_map.get(&file_fid) {
+ let file_name = match state.guid2name_cache.get(&file_fid) {
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
state.set_file_left(Direction::ToServer, rd.len, rd.data.len() as u32, file_fid.to_vec());
if command == SMB1_COMMAND_WRITE_AND_CLOSE {
- let _name = state.guid2name_map.remove(&file_fid);
+ let _name = state.guid2name_cache.pop(&file_fid);
SCLogDebug!("closing FID {:?}", file_fid);
smb1_close_file(state, &file_fid, Direction::ToServer);
}
_ => { (false, Vec::new()) },
};
if !is_pipe {
- let file_name = match state.guid2name_map.get(&file_fid) {
+ let file_name = match state.guid2name_cache.get(&file_fid) {
Some(n) => n.to_vec(),
None => Vec::new(),
};
let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
state.ssn2tree_map.insert(tree_key, tree);
if !is_dcerpc {
- state.guid2name_map.insert(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
+ _ = state.guid2name_cache.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec());
}
is_pipe = true;
is_dcerpc = true;
SCLogDebug!("non-DCERPC pipe");
state.set_skip(Direction::ToClient, nbss_remaining);
} else {
- let file_name = match state.guid2name_map.get(&file_guid) {
+ let file_name = match state.guid2name_cache.get(&file_guid) {
Some(n) => { n.to_vec() }
None => { b"<unknown>".to_vec() }
};
state.ssn2vec_map.insert(guid_key, wr.guid.to_vec());
let file_guid = wr.guid.to_vec();
- let file_name = match state.guid2name_map.get(&file_guid) {
+ let file_name = match state.guid2name_cache.get(&file_guid) {
Some(n) => n.to_vec(),
None => Vec::new(),
};
let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true);
state.ssn2tree_map.insert(tree_key, tree);
if !is_dcerpc {
- state.guid2name_map.insert(file_guid.to_vec(),
- b"suricata::dcerpc".to_vec());
+ _ = state.guid2name_cache.put(file_guid.to_vec(),
+ b"suricata::dcerpc".to_vec());
}
is_pipe = true;
is_dcerpc = true;
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
let mut newname = ren.name.to_vec();
newname.retain(|&i|i != 0x00);
- let oldname = match state.guid2name_map.get(rd.guid) {
+ let oldname = match state.guid2name_cache.get(rd.guid) {
Some(n) => { n.to_vec() },
None => { b"<unknown>".to_vec() },
};
}
Smb2SetInfoRequestData::DISPOSITION(ref dis) => {
let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
- let fname = match state.guid2name_map.get(rd.guid) {
+ let fname = match state.guid2name_cache.get(rd.guid) {
Some(n) => { n.to_vec() },
None => {
// try to find latest created file in case of chained commands
},
SMB2_COMMAND_CLOSE => {
if let Ok((_, cd)) = parse_smb2_request_close(r.data) {
- let _name = state.guid2name_map.remove(cd.guid);
+ let _name = state.guid2name_cache.pop(cd.guid);
let found_ts = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) {
if !tx.request_done {
let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME);
if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) {
p.retain(|&i|i != 0x00);
- state.guid2name_map.insert(cr.guid.to_vec(), p);
+ _ = state.guid2name_cache.put(cr.guid.to_vec(), p);
} else {
SCLogDebug!("SMBv2 response: GUID NOT FOUND");
}